Commit graph Ghostlight/sidecar
Author SHA1 Message Date
Meta Vens
4f424a5cfa Close the session's unissued-call gate with a test
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
0fa411bd81 Falsify the SDK port: adversarial oracle equality, gates, and the Zod divergence
Tests only. Per lane, an adversarial call sequence — decode failure, duplicate
terminal, call after terminal, unknown tool name, oversized argument — is
answered by the oracle with exactly the strings the evaluator recomputes for the
same transcript, and the transcripts the oracle refuses are the transcripts the
evaluator refuses. Identity is pinned across every request builder and one
checkpoint variant. The wire's closed enums, the fault disposition table, the
lowering gate table, the dispatch-order gate, the receipt's contents, and the
routing refusal each get a test.

Two recorded findings, characterized and not fixed:

- The emitted grammar's top-level object is not strict in Zod, so a property the
  connector's `deny_unknown_fields` decoders record as a gap is silently
  stripped and the call succeeds; and a missing or mistyped required property is
  answered by the SDK itself, leaving a registered `tool_use` block with no
  dispatch, which `assembleEvents` raises as `protocol_violation` and Rust maps
  to an integrity violation. Both are proven against the real in-process MCP
  server with no credential and no network.
- A lend refused by the caller-identity or expiry gate is never reclaimed: those
  gates run before `take_oracle`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
2ff1d435d8 Add the Claude SDK sidecar: framing, the closed schema converter, the query
`sidecar/claude-sdk` is the repo's second TypeScript package, wired to the
root only through `npm --prefix`. It reads length-prefixed MessagePack
frames on stdin, runs one SDK query, forwards every tool call to Rust, and
returns Rust's answer to the model verbatim. It computes no tool result,
reads no credential, logs no prompt, argument, or result, and writes nothing
outside its own stdout.

The query is opened with a custom system prompt, `settingSources: []`,
`tools: []`, `strictMcpConfig`, `persistSession: false`, and `alwaysLoad` on
every tool, so no CLAUDE.md, settings file, output style, built-in tool, or
persisted transcript reaches a caller-derived prompt.

`schema.ts` converts exactly the grammar `tool_schema.rs` emits and throws on
anything else, so an emitter that outgrows it fails a test rather than
registering a looser tool. Two Rust tests publish the fixtures the sidecar
tests read — the frame bytes and every real `parameters_json` — so the halves
cannot drift apart silently.

Dependencies are exact-pinned: @anthropic-ai/claude-agent-sdk 0.3.261,
@msgpack/msgpack 3.1.3, zod 4.5.4, and dev typescript 5.9.3 with
@types/node 24.13.3. `engines.node` is the installed major.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
b150b5e38e Add the SDK inference port, its oracle, and the model-name routing rule
`SdkInferencePort` runs one Claude Agent SDK query per round through a Node
sidecar and returns it as one `InferenceOutput`. It prepares through the
same `prepare_invocation` the connector port uses, so there is one identity
scheme and every `integrity_is_valid` variant is unchanged.

The sidecar computes no tool result. Every call is answered by
`ToolResultOracle`, which is the lane's own evaluator fold replayed over the
calls made so far — the same function that will recompute the string at
re-derivation. A lane lends its oracle keyed by the prepared request id
immediately before `infer`; the connector port ignores the lend.

Rust owns the transcript lowering, the receipt digest, and the fault
disposition. The sidecar names a closed set of reasons and cannot assert an
integrity violation, a request identity, or a tool result.

`open_inference` routes each configured model: a `GHOSTLIGHT_SDK_MODEL_PREFIX`
model reaches the SDK port, anything else the connector, and a model no
built backend claims fails at open rather than at its first tick. Ghostlight
reads no credential for this transport.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00