[codex] add Rust SDK parity for OpenHuman#224
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (26)
📝 WalkthroughWalkthroughThis 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. ChangesTinyplace Mock Harness and E2E Test
Estimated code review effort: 4 (Complex) | ~60 minutes Rust SDK New API Modules
Estimated code review effort: 4 (Complex) | ~70 minutes Signal Relay CryptoId Routing Fix
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
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
Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
💡 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".
| 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 }"; |
There was a problem hiding this comment.
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 }"; |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
Validation
Summary by CodeRabbit
New Features
Bug Fixes
Documentation