Skip to content

[codex] add Rust SDK parity for OpenHuman#224

Merged
senamakel merged 8 commits into
mainfrom
feat/wk-rust-sdk-parity
Jul 5, 2026
Merged

[codex] add Rust SDK parity for OpenHuman#224
senamakel merged 8 commits into
mainfrom
feat/wk-rust-sdk-parity

Conversation

@senamakel

@senamakel senamakel commented Jul 5, 2026

Copy link
Copy Markdown
Member

Summary

  • add Rust SDK onboard handoff parity with the TypeScript SDK
  • add Rust SDK compatibility namespaces used by OpenHuman: artifacts, jobs, marketplace, directory identity listing, and GraphQL helpers
  • keep the SDK serde-tolerant for broader OpenHuman integration while preserving existing request construction patterns

Validation

  • cargo test --manifest-path sdk/rust/Cargo.toml --test api_parity
  • cargo test --manifest-path sdk/rust/Cargo.toml --no-run
  • validated through OpenHuman root and Tauri cargo checks after vendoring

Summary by CodeRabbit

  • New Features

    • Expanded the Rust SDK with broader support for artifacts, jobs, marketplace, and onboarding flows.
    • Added a deterministic mock harness and end-to-end orchestration test for more reliable local validation.
  • Bug Fixes

    • Improved encrypted messaging routing and key-bundle lookup handling.
    • Strengthened message-field sanitization for safer tool-call argument handling.
  • Documentation

    • Updated Rust SDK guidance and added orchestration test usage instructions.

senamakel added 8 commits July 5, 2026 00:18
…orchestration tests

Adds a `mock` harness adapter (adapters/mock.mjs) — a deterministic stand-in for
a Claude Code / Codex TUI so the full daemon → route → autoresponder → outbox →
send pipeline can be exercised end to end without a real model or terminal.

- Registered in mcp/harness.mjs but NEVER auto-detected: detectHarness only
  selects it via the explicit TINYPLACE_HARNESS=mock override, so the real
  Claude/Codex paths are untouched. Passes adapter-contract-test.
- The LLM is mocked at the one seam the core exposes: adapter.responder. Instead
  of `claude -p` / `codex exec`, it spawns test/mock-responder.mjs, which parses
  the injected prompt and writes a deterministic canned reply to the agent outbox
  — the same side-effecting path the real `auto_reply` MCP tool takes — so the
  per-agent daemon then encrypts + sends it over Signal. No network, no model.
- test/mock-tui.mjs is a trivial launch stand-in for the launcher recipe.

Full offline plugin suite green (mcp-smoke needs @modelcontextprotocol/sdk
installed; unrelated).

Claude-Session: https://claude.ai/code/session_01EJDqbVAWGNdmx9njh4hME8
… key

Both sides of the Signal key exchange addressed the relay key routes
(/keys/:cryptoId/{signed-prekey,prekeys,bundle}) with the base64 Ed25519 key.
A base64 key contains `/`, which breaks the single-segment `:cryptoId` route
match and 404s for ~half of all identities (any key whose base64 contains a
slash):

- publishKeyBundle (enableEncryption/publishKeys) intermittently failed to
  publish, so peers could never open an X3DH session with the agent;
- encryptEnvelope's getBundle intermittently failed to fetch a peer's bundle.

Address both by the base58 cryptoId (deriveCryptoId(pubkey) == signer.agentId);
the Signal identity key still travels in the body as `identityKey`. Mirrors the
manual publish/fetch flow that already used agentId.

Verified: 8/8 fresh identities publish + fetch a bundle including 6 whose base64
key contains `/` (previously 404); full SDK vitest suite green (354 passed).

Claude-Session: https://claude.ai/code/session_01EJDqbVAWGNdmx9njh4hME8
respond-batch's safeArg sanitized msg.from (the reply target) with
/[^\w:.-]+/g, on the assumption it was always a base58 agent id. But a messaging
address is the base64 Ed25519 key, which contains `+ / =` — safeArg stripped
those, mangling the address into an unresolvable string, so the headless
autoresponder's auto_reply could never be delivered back to a base64-addressed
sender.

Allow `+ / = @` in the charset (base64 keys, cryptoIds, and @Handles). None can
terminate a double-quoted tool-call argument (only `"`/newline can, and both stay
excluded), so the prompt-injection guard is unchanged.

Surfaced by the new orchestration-live-e2e: the mock agent's reply sat
undeliverable in the outbox until the target address survived intact.

Claude-Session: https://claude.ai/code/session_01EJDqbVAWGNdmx9njh4hME8
orchestration-live-e2e.mjs drives the WHOLE coding-agent pipe against a real
backend with no model and no terminal: a "human" peer (SDK) sets up a contact
connection and sends a Signal DM; bob's real per-agent daemon under the `mock`
harness drains + decrypts it, routes it headless to the autoresponder, which
spawns the mock responder (canned reply) in place of claude -p / codex exec; the
daemon encrypts + sends the reply back and the human decrypts it. Asserts the
full round trip, that the mock LLM saw the message, and that the relay only ever
held ciphertext.

- test/ORCHESTRATION.md documents the pieces, the data flow, and the human
  connect+send flow (CLI + scripted).
- mock-responder.mjs gains a debug breadcrumb log (respond-batch runs it with
  stdio ignored) for headless troubleshooting.

Green against the docker-compose backend. Run: API_URL=... node
orchestration-live-e2e.mjs (self-links the local SDK).

Claude-Session: https://claude.ai/code/session_01EJDqbVAWGNdmx9njh4hME8
…ish path

Follow-on to the SDK key-routing fix: verifyKeyBundlePublished probed the relay
with the base64 messaging key (`/keys/<base64>/bundle`), but the bundle is now
published under the base58 cryptoId. The probe 404s after a successful publish,
so clearPublishProgress leaves isReady false and browser users can't enable
encrypted messaging. Derive the cryptoId (deriveCryptoId(fromBase64(address)))
for the verification fetch. (Codex P1 on #223.)

Claude-Session: https://claude.ai/code/session_01EJDqbVAWGNdmx9njh4hME8
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tiny-place-website Ready Ready Preview, Comment Jul 5, 2026 8:55am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 73026211-2ba6-4cfe-9e28-263670344ddc

📥 Commits

Reviewing files that changed from the base of the PR and between 0fce176 and 65faa27.

📒 Files selected for processing (26)
  • sdk/plugin-tinyplace/adapters/mock.mjs
  • sdk/plugin-tinyplace/hooks/respond-batch.mjs
  • sdk/plugin-tinyplace/mcp/harness.mjs
  • sdk/plugin-tinyplace/orchestration-live-e2e.mjs
  • sdk/plugin-tinyplace/test/ORCHESTRATION.md
  • sdk/plugin-tinyplace/test/mock-responder.mjs
  • sdk/plugin-tinyplace/test/mock-tui.mjs
  • sdk/rust/PORTING.md
  • sdk/rust/src/api/artifacts.rs
  • sdk/rust/src/api/directory.rs
  • sdk/rust/src/api/graphql.rs
  • sdk/rust/src/api/jobs.rs
  • sdk/rust/src/api/marketplace.rs
  • sdk/rust/src/api/mod.rs
  • sdk/rust/src/api/onboard.rs
  • sdk/rust/src/client.rs
  • sdk/rust/src/lib.rs
  • sdk/rust/src/types/artifacts.rs
  • sdk/rust/src/types/jobs.rs
  • sdk/rust/src/types/marketplace.rs
  • sdk/rust/src/types/mod.rs
  • sdk/rust/src/types/onboard.rs
  • sdk/rust/tests/api_parity.rs
  • sdk/typescript/src/messaging/encryption.ts
  • sdk/typescript/src/version.ts
  • website/src/common/signal-messaging.ts

📝 Walkthrough

Walkthrough

This PR adds a deterministic mock harness (adapter, responder, TUI) and live e2e orchestration test for the tinyplace plugin, expands the Rust SDK with artifacts/jobs/marketplace/onboard API and type modules plus client/GraphQL/directory wiring, and fixes Signal relay key-bundle routing to use base58 cryptoId in the TypeScript SDK and website, alongside an SDK version bump.

Changes

Tinyplace Mock Harness and E2E Test

Layer / File(s) Summary
Mock adapter, harness wiring, and arg sanitization
sdk/plugin-tinyplace/adapters/mock.mjs, sdk/plugin-tinyplace/mcp/harness.mjs, sdk/plugin-tinyplace/hooks/respond-batch.mjs
Adds mockAdapter config with headless pull-only behavior, wires a mock entry into ADAPTERS, and broadens the unsafe-character sanitization regex for quoted tool-call arguments.
Mock responder and TUI scripts
sdk/plugin-tinyplace/test/mock-responder.mjs, sdk/plugin-tinyplace/test/mock-tui.mjs
Adds a deterministic CLI responder that parses prompts, selects a wallet, builds a canned reply, and enqueues an outbox job; adds a minimal headless TUI script.
Live e2e orchestration test and docs
sdk/plugin-tinyplace/orchestration-live-e2e.mjs, sdk/plugin-tinyplace/test/ORCHESTRATION.md
Adds an executable test that spins up isolated agents, sends an encrypted DM through a real Signal/relay path with only the LLM mocked, verifies the reply and ciphertext-only storage, and documents the flow.

Estimated code review effort: 4 (Complex) | ~60 minutes

Rust SDK New API Modules

Layer / File(s) Summary
Artifacts API
sdk/rust/src/types/artifacts.rs, sdk/rust/src/api/artifacts.rs
Adds ArtifactQueryParams and ArtifactsApi with get/list methods and query helpers.
Jobs API
sdk/rust/src/types/jobs.rs, sdk/rust/src/api/jobs.rs
Adds job/proposal request types and JobsApi covering job CRUD, proposals, selection, and disputes.
Marketplace API
sdk/rust/src/types/marketplace.rs, sdk/rust/src/api/marketplace.rs
Adds marketplace types and MarketplaceApi for listings, bids, offers, products, categories, and purchases.
Onboard API and tests
sdk/rust/src/types/onboard.rs, sdk/rust/src/api/onboard.rs, sdk/rust/tests/api_parity.rs
Adds OnboardApi handoff create/redeem flow with response types and parity tests.
Client and module wiring
sdk/rust/src/client.rs, sdk/rust/src/api/mod.rs, sdk/rust/src/types/mod.rs
Registers the new modules and wires new client fields/initializers for artifacts, jobs, marketplace, and onboard.
GraphQL and directory extensions
sdk/rust/src/api/graphql.rs, sdk/rust/src/api/directory.rs
Adds new GraphQL query params/endpoints for jobs, products, and identity listings/bids/offers/sales, and adds DirectoryApi::list_identities.
SDK documentation updates
sdk/rust/PORTING.md, sdk/rust/src/lib.rs
Updates porting guidance and crate docs to reflect inclusion of Signal protocol primitives.

Estimated code review effort: 4 (Complex) | ~70 minutes

Signal Relay CryptoId Routing Fix

Layer / File(s) Summary
TypeScript encryption cryptoId routing and version bump
sdk/typescript/src/messaging/encryption.ts, sdk/typescript/src/version.ts
Uses deriveCryptoId for key bundle publish/fetch routing instead of base64 addresses; bumps SDK_VERSION to 2.0.0.
Website signal-messaging cryptoId derivation
website/src/common/signal-messaging.ts
Derives cryptoId from the base64 address before calling getBundle in verifyKeyBundlePublished.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TestScript as orchestration-live-e2e.mjs
  participant BobDaemon as Bob agent-daemon (mock harness)
  participant Responder as mock-responder.mjs
  participant Relay as Relay/API

  TestScript->>BobDaemon: spawn with TINYPLACE_HARNESS=mock
  TestScript->>Relay: fetch Bob key bundle (Alice)
  TestScript->>Relay: send encrypted DM (Alice->Bob)
  Relay->>BobDaemon: deliver envelope
  BobDaemon->>Responder: invoke with prompt/model
  Responder->>Relay: writeOutboxJob (auto reply)
  Relay->>TestScript: poll mailbox, decrypt reply
  TestScript->>TestScript: assert reply and ciphertext-only storage
Loading
sequenceDiagram
  participant Client as Signal client (TS SDK/website)
  participant KeyDerivation as deriveCryptoId
  participant Relay as Relay API

  Client->>KeyDerivation: derive cryptoId from Ed25519/base64 address
  Client->>Relay: getBundle(cryptoId) / publish keys at keyPathId
  Relay-->>Client: bundle / publish result
Loading

Possibly related PRs

  • tinyhumansai/tiny.place#215: Extends the same harness/adapter selection architecture in mcp/harness.mjs with a new mock adapter and associated respond-batch sanitization flow.

Poem

A rabbit hops through mock and key,
Bob and Alice DM secretly,
Rust gains jobs, marketplace, and art,
CryptoIds now route each part.
🐇✨ Ship it, hop away!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@senamakel senamakel marked this pull request as ready for review July 5, 2026 08:57
@senamakel senamakel merged commit 453c292 into main Jul 5, 2026
11 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 65faa27b84

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +716 to +720
const GENERIC_JOBS_QUERY: &str = "query Jobs { jobs }";
const GENERIC_JOB_QUERY: &str = "query Job($id: ID!) { job(id: $id) }";
const GENERIC_PRODUCTS_QUERY: &str = "query Products { products }";
const GENERIC_PRODUCT_QUERY: &str = "query Product($id: ID!) { product(id: $id) }";
const GENERIC_IDENTITY_LISTINGS_QUERY: &str = "query IdentityListings { identityListings }";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass GraphQL variables into the new queries

These new compatibility helpers build variables from the caller's filters, but the query documents call jobs, products, and identityListings with no declared variables or field arguments. When a caller supplies limit, status, q, seller, etc., the server receives an unfiltered/default query and the SDK returns the wrong page of data. Mirror the existing queries above by declaring the variables and passing them as field arguments.

Useful? React with 👍 / 👎.

const GENERIC_IDENTITY_LISTINGS_QUERY: &str = "query IdentityListings { identityListings }";
const GENERIC_IDENTITY_LISTING_QUERY: &str =
"query IdentityListing($id: ID!) { identityListing(id: $id) }";
const GENERIC_IDENTITY_BIDS_QUERY: &str = "query IdentityBids { identityBids }";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope identity bid queries to the requested listing

identity_bids(listing_id, params) constructs listingId, limit, and offset variables, but this document never declares or passes them, so opening bids for a specific identity listing can only return the gateway's default/global bid set rather than that listing's bids. Pass listingId and the pagination arguments into the identityBids field.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant