API, SDK, JSON-RPC, and ACP
How browsers, automation, and external clients drive the same Agent runtime
Conclusion: this is not “one API,” but four protocols with different ownership models
DeepSeek Harness exposes a browser Host API, generated Typert Remote APIs, a private SDK JSON-RPC protocol, and a standard ACP adapter. They share some Agent and Session capabilities, but they do not share the same stability, authentication, cancellation, reconnection, or lifecycle guarantees. The most dangerous reading is to treat them as interchangeable remote APIs merely because they all use JSON.
The shortest useful distinction is this: the browser API serves a UI shipped with the Host; Remote projects compile-time descriptors onto that same browser carrier; the SDK owns a local subprocess on behalf of its caller; and ACP serves automation clients that understand the standard protocol. When the wrong boundary is chosen, the first thing to become ambiguous is usually not the type system, but who owns the process, who may cancel work, and who restores state after a disconnect.
1. Four protocol surfaces: separate them by consumer and lifecycle first
| Surface | Typical consumer | Physical carrier | Session owner | Role |
|---|---|---|---|---|
| Browser ApiProxy | Web client shipped with the Host | HTTP POST uplink + two WebSocket downlinks | Host process | Full product UI contract |
| Typert Remote | Client plugins with compile-time selected contributions | Reuses /api HTTP and the Host event stream | Host Service / Context | Strict generated BFF surface |
| SDK JSON-RPC | TypeScript and Python automation callers | Subprocess stdio, JSONL | SDK instance | Minimal private runtime wire |
| ACP | Standard ACP clients or out-of-process child backends | Standard JSON-RPC over stdio | ACP connection | Minimal automation adapter |
“They can all send a prompt” is not compatibility. Compatibility also includes handshake ordering, session recovery, error classification, cancellation propagation, credential boundaries, and state reconstruction after a disconnect.
2. Browser API grammar: a four-quadrant message model, not a loose set of fetch calls
ApiProxy separates messages from carriers into four forms: client-request, server-response, server-request, and client-response. The initiator creates an rpcId, and the responder echoes it unchanged. A server request may represent an approval or question that requires an answer, or a pure push; whether a reply is required is statically determined by the method. Client answers return through POST /api/respond, while late or duplicate responses receive only a carrier receipt.API-RPC-MODEL
Parsing follows a two-stage discipline. The first stage recognizes only the four envelope forms, rpcId, and the error union, leaving payload/value as unknown. The second stage parses the method-specific business schema. This lets the transport report a malformed envelope with correlation while preserving a precise business contract instead of diluting everything into one broad JSON schema.API-RPC-SCHEMAS
A business failure is not an exception; it is the error branch of RpcResult<T>. A transport exception may collapse to internal. HTTP status, RPC envelope, and business result therefore represent three distinct semantic layers.
3. Browser API version posture: ship together instead of negotiating the protocol
HostApi explicitly states that client and Host are shipped together, so there is currently no protocolVersion. host.describe.version is defined as the Host application version, not a wire version. Protocol version negotiation is deferred until independently shipped clients exist.API-HOST-VERSION-CONTRACT
The implementation does not currently return the real application version: a source TODO still returns the fixed value 0.0.1. It therefore cannot negotiate the protocol and, for now, cannot reliably identify the Host build either.API-HOST-VERSION-PLACEHOLDER
4. Browser carriers: HTTP uplink and WebSocket downlink have asymmetric error layers
The generic Remote/Connection RPC handler accepts only POST with application/json, and requires the URL endpoint to match the envelope method. Invalid envelopes and mismatched methods return typed bad-request results. An unexpected business-handler exception becomes a direct HTTP 500, which the browser carrier then treats as a transport failure. This scope excludes the event WebSocket upgrades and session-export route on the same /api surface.API-HTTP-HANDLERAPI-HTTP-CLIENT
Every /api request first passes Host authority, Fetch Metadata, and Origin fences. These defend against DNS rebinding and cross-site browser requests, but the source explicitly says they are not an authentication layer. Configuring a trusted host only permits requests bearing that authority through this fence; it proves neither network reachability nor requester identity.API-TRUST-FENCE
The trusted-host LAN surface does not give every method equal authority either. Privileged methods for settings, credentials, native dialogs, agent-preset management, and model discovery with draft credentials are separately pinned to loopback authority through an empty trusted-host set and still pass the shared Fetch-Metadata and Origin-authority checks; HTTP and both WebSocket upgrades pass the base trust fence. This is not a full scheme/host/port same-origin test: Origin may be absent, and when present only its authority is compared.API-PRIVILEGED-LOOPBACK
The HTTP bridge buffers the whole request body in memory, with a default 160 MiB limit. Browser disconnect is translated through response close into a Fetch AbortSignal, and the response stream observes socket drain for backpressure. The signal can notify downstream work, but it cannot forcibly stop in-process business logic that ignores cancellation.API-HTTP-BRIDGE
5. The two WebSockets are downlink-only streams; malformed frames and disconnects are different failures
The Host opens one downlink-only WebSocket for mux events and another for Host events. Any business message sent by the client closes the socket with a policy violation. If a source throws, the Host attempts to send stream/error first; on close it aborts the source and waits for all pumps to converge.API-WS-HOST
On the browser side, binary frames, invalid JSON, invalid envelopes, and invalid business frames are logged and dropped rather than terminating the generation. Schema corruption therefore does not automatically reconnect; only socket termination ends the stream.API-WS-CLIENT
Dropping one bad frame prevents a local extension mismatch from taking down the connection, but if that frame carried a critical increment, repair depends on later reconnect/resync. The current generation has no per-frame replay.
6. Browser reconnection replaces the entire generation, not one broken socket
Each generation starts mux, Host, and host.describe concurrently. Normal readiness requires both streams to open and describe to succeed; if a proxy never emits open, a three-second guard allows degraded connection readiness. Provided describe/handshake converges, either stream ending aborts the whole generation and rebuilds both streams. Backoff grows exponentially from 500ms to a 10s cap, with delay jittered between half the cap and the cap. A successful handshake resets the attempt counter. A business sink exception is logged without killing the pump.API-CONNECTION-LOOP
The carrier has no resume token or per-frame offset. onConnected is the upper layer’s opportunity to reload lists and snapshots. The connection layer restores reachability, not business state.
7. Typert Remote: compile-time descriptors become live Service calls at runtime
The carrier-independent Remote request shape contains only namespace, method, exact named arguments, and a signal injected only for methods that declare cancellation. The Gateway also declares a stable set of boundary failure categories.REMOTE-GATEWAY-TYPES
For every invocation, the Host reloads the current strict descriptor, validates the field set, resolves lookup or Context identity, binds the current Service, and validates the result before returning it. If a strict endpoint appeared and was later withdrawn, the Gateway fails closed instead of falling back to weaker SRC inference. This prevents a hot-unloaded method name from silently acquiring a broader contract.REMOTE-HOST-DISPATCH
Cancellation is not universal: methods without a cancellation descriptor receive no signal. If a method that declares cancellation throws after its signal has aborted, the wire maps the outcome to cancelled. Existing RpcError values from lookup policy are preserved, while most other Gateway or business exceptions collapse to internal; rich in-process error categories therefore do not all cross the wire.REMOTE-HOST-ERRORS
8. Remote mounting, withdrawal, and events are explicit capabilities
A client contribution accepts only a strict codec. Before publishing methods, it checks for duplicate descriptors, namespaces, and Service conflicts; a partial installation failure rolls back in reverse order. Each mount token owns an AbortController. Withdrawal marks the mount inactive, aborts in-flight calls, and then removes the methods.REMOTE-CLIENT-MOUNT
The caller converts positional arguments into descriptor-named fields and combines the caller signal with mount lifetime. The returned value is parsed again on the client. At invocation, before entering the carrier try, a missing active Connection still throws directly; after a Connection exists, RPC rejection, abort, and malformed responses fold into the error branch of RemoteResult.REMOTE-CLIENT-INVOKE
Events are not forwarded automatically either. The current surface is an 11-item allowlist that preserves the original Cordis event name and argument list without projection, renaming, or redaction.REMOTE-EVENT-ALLOWLIST Before enqueueing, the Host proves only that the arguments are JSON-safe and then wraps them as host/remote-event.REMOTE-EVENT-FORWARD
Agent and Session identity lookup is part of the BFF lifecycle policy. A live Agent is reused directly; concurrent cold resumes of an ordinary Session are coalesced by ID. Before cold resume, subagent ownership is fenced at entry, after durable inspection, and immediately before calling resume. If a publication race makes resume fail, the catch path rechecks the published identity and maps the relevant collision to agent-busy, preventing the general API from taking over a child owned by another router.REMOTE-AGENT-RESOLVE
9. SDK JSONL: JSON-RPC 2.0 outbound, lenient classification inbound
The SDK transport writes "jsonrpc":"2.0" on outbound requests, responses, and notifications, with one compact frame per line. Inbound, it classifies frames only by the presence of id and method; it does not validate the jsonrpc field or a complete JSON-RPC envelope. StringDecoder prevents a chunk boundary from splitting multibyte UTF-8. Invalid JSON lines, non-objects, and unknown response IDs are ignored. A missing handler returns -32601; a handler rejection returns -32603.SDK-JSONL-TRANSPORT
The application vocabulary has only three client requests: initialize, session/prompt, and shutdown; and four server notifications: session.event, session.status, subagent.started, and subagent.finished. A prompt response is only a durable queue receipt containing messageId. The full SessionEvent crosses the wire, making the Session event vocabulary part of the compatibility surface too.SDK-WIRE-METHODS
initialize ───────────────→ serverInfo
session/prompt ───────────→ messageId (queue receipt only)
├─ session.event ──→ durable facts
├─ session.status ─→ whole-agent running / idle
└─ subagent.* ─────→ in-process child lineage / completion
shutdown ─────────────────→ {}
10. SDK compatibility and security posture: no version negotiation, wire auth, or prompt cancellation
The published limitations state directly that serverInfo.version is fixed at 0.0.1, clients do not compare it with an expected version, prompt cancellation and session close do not exist, and server-to-client requests have no current producer. The transport retains that last capability only for a future approval flow.SDK-WIRE-LIMITS
The SDK security boundary is caller ownership of a local subprocess and its stdio, not an online identity protocol. The wire has no authenticate method. Model credentials enter through the child environment or runtime configuration, so environment construction is part of the credential policy.
At the pre-release stage, equal package versions alone do not prove wire compatibility. The safest posture is to ship and upgrade SDK and runtime together, and treat serverInfo as diagnostic information rather than a negotiated result.
11. SDK server: global event sources, with session-tree filtering delegated to clients
At construction, the server subscribes to every Session event and Agent status in the Context, not only Sessions created by the SDK. Child-created events are likewise reported globally. A subagent-finished notification is emitted only when the service snapshot says local is true; locality cannot be inferred from a matching ID or parent lineage.SDK-SERVER-EVENTS
initialize parses cwd, stores provider/model/maxTokens, and automatically mounts the DeepSeek fallback only when no owner exists. The class comment says reinitialization is unsupported, but the implementation has no handshake-state field; handleRequest also does not prevent a pre-initialize prompt.SDK-SERVER-INIT
A Session is created lazily on its first prompt, and concurrent creation for the same ID is coalesced through a promise map. Every prompt verifies that the retained handle still equals the live registry instance. Shutdown closes admission, waits for creation to converge, removes subscriptions, and uses allSettled for every Agent and the fallback adapter, reporting multiple failures together.SDK-SERVER-SESSIONS
After the protocol shutdown response is written, the next event-loop turn flushes the transport, disposes the root fiber, and calls exit(0). The successful plugin-only unmount path shuts down the server, then closes the transport without exiting the process; however, the disposer has no finally, so a server-teardown rejection skips transport close.SDK-SERVER-SHUTDOWN
12. TypeScript SDK: run spans receipt-to-idle, not a one-to-one turn result
The low-level HarnessClient spawns the runtime lazily. Initialize requires only that the returned name and version are strings; it does not validate expected values. A request timeout only abandons the local pending-map entry, while server work continues until the runtime is closed. JSON-RPC errors preserve code/data, and transport failures add the exit code plus at most 400 lines of stderr tail.SDK-TS-CLIENT
High-level run() subscribes to the Session tree before sending the prompt. After receiving messageId, it drops notifications before the matching durable inbox receipt and then collects until the root Agent next becomes idle. finalResponse is the last committed assistant text in that interval. Work admitted by another producer before idle may also contribute, so it is not a response uniquely attributable to that prompt.SDK-TS-RUN
Close is a locally authoritative termination sequence: bounded best-effort shutdown, then stdin EOF → POSIX SIGTERM → SIGKILL, resolving only after process exit is observed. Windows proceeds directly to the forced-termination tier.SDK-TS-DISPOSE
13. The Python SDK speaks the same wire, but is not a mechanical translation of the TypeScript API
Python HarnessClient is synchronous and sends the same three requests. It additionally exposes next_request(), respond(), and respond_error() for future server requests; the current server does not send such a request. Python copies the parent environment and then merges explicit overrides by default, unlike TypeScript, where an explicit env replaces the whole environment. Its initialize-response model also permits serverInfo, name, and version to be absent.SDK-PY-CLIENTSDK-PY-MODELS
The reader thread routes responses by ID into waiter queues, fans notifications out to subscriptions, and places frames with id+method into a server-request queue. Timeout removes the waiter and adds exit/stderr diagnostics, but likewise sends no cancellation to the runtime. A filter exception terminates only its own subscription.SDK-PY-READER
High-level Python Session.run() also uses a receipt-to-idle interval, but additionally extracts the final turn/end.reason.kind as finish_reason. A missing string kind raises SdkProtocolError. This is still an interval summary, not a prompt-specific verdict.SDK-PY-RUN
| Difference | TypeScript | Python | Impact |
|---|---|---|---|
| Explicit env | Replaces the complete child env | Copies parent, then merges | Credential inheritance differs |
| serverInfo validation | Requires name/version strings | Both fields may be absent | Different acceptance of a malformed runtime |
| RunResult | No finish reason | Includes finish_reason and session_root | High-level APIs are not isomorphic |
| Server request | No consumer API | Generic queue/respond API | A dead capability today, with different extension posture |
| Close/reuse | Default six-second configurable EOF grace before termination escalation; low-level client is permanently closed | Terminates immediately after the shutdown response if still alive; the same instance can start again, but close sentinels left in the global-notification and server-request queues are not cleared | Python may TERM while runtime root/persistence disposal is still running; the first low-level queue read after restart may also observe an old sentinel |
The TypeScript public type fixes env replacement and a four-field RunResult.SDK-TS-RESULT Python configuration adds session-root, base-URL, and API-key conveniences, and returns additional fields.SDK-PY-RESULT
14. SDK drift and enforcement gap: documented intent is not a wire state machine
SdkProtocolError JSDoc still uses “prompt response missing accepted:true” as an example, but the current wire and implementation require messageId. This is a stale local comment, not dual-format compatibility.SDK-DOC-STALE-ACCEPTED
Reinitialization being “unsupported” exists only in the class comment. The source rejects neither a second initialize nor a prompt before initialize. Official SDK wrappers handshake in order, so the normal path hides the gap; any raw JSON-RPC client can reach it.
A second initialize rewrites cwd/provider/model/maxTokens for later new Sessions, while existing Sessions retain the old configuration, producing mixed generations within one connection. The fix should be an explicit connection state machine, not a client convention to avoid the sequence.
15. ACP: a standard protocol version with a deliberately narrow automation baseline
The adapter is a public 0.1.0-rc.5 package and pins @agentclientprotocol/sdk 0.25.1.ACP-PACKAGE At runtime it imports PROTOCOL_VERSION, ndJsonStream, and AgentSideConnection directly from that SDK, then constructs the connection over the standard stream. Framing, method machinery, and the protocol constant therefore come from the SDK rather than a custom wire.ACP-SDK-IMPORTSACP-SDK-CONNECTION
Regardless of which version the client requests, initialize returns the PROTOCOL_VERSION supported by this build, advertises only the capabilities needed for a text/resource-link baseline, returns an empty authMethods, and implements authenticate as a no-op. session/new creates only fresh Sessions, requires an absolute cwd, and rejects nonempty additional directories and MCP servers.ACP-HANDSHAKE
ACP “standards compatibility” means a standard envelope plus baseline method compatibility; it does not imply every optional ACP capability. Clients must inspect capabilities and the returned protocol version instead of assuming resume, filesystem, terminal, or MCP support from the protocol name.
16. ACP output and errors: committed answers only, trading token latency for a clean result
The bridge does not forward raw deltas, reasoning, tool traces, plans, or titles. It emits only committed assistant/message text as agent_message_chunk, and turns images into explicit attachment placeholders. A transport/write rejection from sessionUpdate() is caught and warned; because a JSON-RPC notification has no response, a client-handler failure cannot feed back over that notification. For tool-call approvals it owns, the bridge sends a one-shot allow/reject permission request; cancellation or an unknown choice does not grant permission.ACP-OUTPUT-PERMISSION
Each Session permits only one in-flight prompt. The bridge creates a slot keyed by the user messageId and captures its turn on agent/inbox/claimed. turn/end for that captured turn is correlated exactly, while ordinary endings still wait for whole-agent idle. A separate agent/error fallback rejects the current slot only when the turn is not yet captured or differs, with no message-level causality: a prior autonomous-turn failure, or a competing-turn failure after the prompt turn but before idle, may be misattributed to the waiting ACP prompt. Output likewise includes every committed assistant message for that Session through idle, not only the prompt turn. session/cancel is a no-op for an unknown ID; for a known ID it cancels only the corresponding Agent and immediately settles that prompt as cancelled.ACP-PROMPT-CANCEL
The content codec concatenates text unchanged, renders a resource link as a bracketed reference containing its name and URI, and rejects richer blocks. Mapping turn endings to ACP stop reasons is lossy. The codec itself maps max-token to max_tokens, but at whole-agent-idle settlement the prompt wrapper separately lowers it to end_turn because it does not claim a prompt-specific turn outcome.ACP-CODECACP-PROMPT-CANCEL
17. ACP lifecycle: one connection may own multiple Sessions, but there is no per-Session close
Connection close and plugin disposal share one memoized quiesce operation. It synchronously closes admission, clears the map, cancels Agents, and settles every prompt. It then drains only continuable descendants beneath those top-level Agents and disposes handles in parallel. Descendant-drain failure only warns and continues; only handle-disposal failures are aggregated and thrown. Forests owned by other frontends in the same Context are not swept accidentally.ACP-QUIESCE
This provides connection-owned isolation and deterministic ownership/teardown ordering on the successful path. Top-level handle disposal continues even after descendant-drain failure, but complete descendant release is then unproven. There is no session/load, resume, fork, or per-Session close. A long-lived client must treat the connection itself as the resource container instead of treating each Session as an independently reclaimable remote object.
18. ACP is also a child backend: every run is a fresh-process transaction
The out-of-process provider explicitly advertises no output-schema, depth-limit, tool-filter, or persona support, and does not inherit the parent conversation. Child cwd must come from explicit configuration or the delegating parent Session. Missing workspace fails loudly instead of falling back to server-process cwd.ACP-CHILD-CAPABILITIES
Every run starts a fresh process. The parent creates a separate lifecycle ID so identical ACP Session IDs in different children cannot collide. A handle is published only after spawn, initialize, and session/new all succeed; an error or cancellation before that point first reclaims the private process. A shared subprocess policy removes credential-shaped and DSH_* ambient names, after which the local spawn layer merges explicit child env.ACP-CHILD-STARTACP-CHILD-ENV-POLICYACP-CHILD-ENV-MERGE
Caller abort may immediately return the text collected so far with aborted, while sending best-effort ACP cancel; this does not prove the child has stopped. dispose() is the authoritative quiescence path: it ensures best-effort cancellation has been requested if not already, then performs stdin EOF → termination escalation → whole-tree exit proof.ACP-CHILD-CANCEL
19. Version, authentication, cancellation, reconnect, and recovery matrix
| Dimension | Browser API / Remote | SDK JSON-RPC | ACP adapter |
|---|---|---|---|
| Version | Shipped together; no protocolVersion; describe version remains a placeholder | serverInfo 0.0.1; no negotiation or expected-value comparison | Standard SDK PROTOCOL_VERSION; capability subset |
| Authentication | Host/Origin trust fence, not identity authentication | No wire auth; relies on local subprocess ownership | Empty authMethods; trusted stdio |
| Cancellation | HTTP disconnect/caller signal; injected only for declared methods; cooperative | Timeout only abandons locally; closing the runtime is required to terminate | session/cancel routes to the target Agent and settles the prompt |
| Reconnect | After readiness converges, rebuild both streams as one generation + upper-layer snapshot resync; hung describe can wedge convergence | Transport death after handshake is terminal; no automatic reconnect | No connection resume; disconnect releases owned Sessions |
| Session recovery | BFF may cold-resume an ordinary durable Session | Reuses by sessionId inside one runtime; no wire load/resume | Fresh only |
| Process teardown | Owned by the Host | SDK shutdown + EOF/TERM/KILL | Server connection quiesce; child backend separately reaps the whole tree |
| Result semantics | Typed RPC result + event streams | messageId receipt; high-level receipt-to-idle interval | Committed chunks + stopReason, without claiming a prompt-causal turn |
20. Comparison with the public Codex App Server contract
| Dimension | DeepSeek Harness (source reviewed here) | Codex (public documentation only) |
|---|---|---|
| Purpose | Separate browser surface, narrow SDK, and ACP adapter | App Server for rich clients; SDK for automation/CI |
| JSON-RPC framing | SDK writes a 2.0 header outbound but classifies inbound leniently; ACP uses standard SDK framing; browser API has a custom envelope | Bidirectional JSON-RPC 2.0, but omits the jsonrpc field on the wire; stdio is JSONL, with experimental WebSocket and Unix-socket transports |
| Handshake | SDK server lacks an enforced state machine; ACP returns the standard version | Exactly one initialize per connection followed by initialized; requests before handshake and repeated initialize are rejected |
| Sessions | SDK implicit get/create; ACP fresh only; Browser BFF may cold-resume | Public thread start/resume/fork/read/list and turn start/steer/interrupt |
| Cancellation and result | SDK has no wire cancel; ACP has session/cancel; Browser injects signals by descriptor | After turn/interrupt, convergence occurs through turn/completed with final status |
| Remote authentication | The adapters covered here expose no public network identity authentication | Remote WebSocket may require a capability token or signed bearer authentication before initialize |
| Compatibility strategy | Browser ships atomically; SDK pre-release has no negotiation; ACP exposes a capability subset | TypeScript/JSON Schema can be generated for the exact Codex version being run; experimental methods and fields require capability opt-in |
| Backpressure | Browser HTTP/WS and SDK handle it separately; SDK has no overload code | A full WebSocket ingress queue returns -32001, with documented exponential backoff + jitter |
| Server requests | Browser approvals can be answered; ACP permissions exist; SDK capability is currently dead | Public approval and other server-initiated requests receive a client decision |
The Codex information above comes from the official Codex App Server documentation (retrieved 2026-08-13). This comparison covers public protocol capabilities only and makes no inference about private implementation.
Codex App Server resembles a rich-client protocol with handshake state, version-matched schemas, long-lived threads/turns, and explicit approvals. The DeepSeek SDK resembles a minimal subprocess automation pipe, while its ACP adapter trades product breadth for standards compatibility. This is not merely a difference in feature count; it reflects different expected client lifetimes and degrees of independent release.
21. Choosing safely: select by required guarantees, not familiar syntax
| Need | Prefer | You must still supply |
|---|---|---|
| Full Web UI shipped with the product | Browser ApiProxy + Remote | Real identity authentication, cross-version deployment discipline, snapshot resync after reconnect |
| Local scripts, CI, batch automation | TypeScript/Python SDK | Same-version runtime pinning, process close, and a policy for whether timeout terminates the whole runtime |
| Generic agent-client integration | ACP | Depend only on advertised capabilities; accept fresh-only, committed-only, connection-owned lifetime |
| Out-of-process child Agent | ACP child backend | Explicit cwd, explicit credentials, permission policy, and unconditional disposal |
| Independently shipped rich client | No equivalent unified DeepSeek protocol today | Version negotiation, authentication, resume, schema generation, overload/replay contract |
A minimal protocol reduces implementation and token-surface cost, but pushes state ownership, upgrades, and recovery onto the client. A richer protocol supports an independent client lifecycle, but must carry capability negotiation, authentication, backpressure, replay, and a larger compatibility test matrix.
22. Verification scope and reproducible result
This chapter uses commit 47f943859bef60e4160492346772ded9b24f765a as its fixed baseline. Focused Vitest coverage includes SDK transport/server/client, ACP bridge/codec/permission/multi-session/disposal, the ACP child backend, Typert Gateway, Remote identity resolution, and browser trust/HTTP bridge/WebSocket/reconnect. The SDK transport suite pins framing and error semantics.TEST-SDK-PROTOCOL The ACP bridge suite pins version, auth, fresh-session, and input boundaries.TEST-ACP
The connection suite pins two-stream generations and reconnect behavior.TEST-CONNECTION The Remote client suite pins strict mounting, withdrawal cancellation, scope, and error folding.TEST-REMOTE
pnpm exec vitest run \
packages/sdk/protocol/tests/transport.spec.ts \
packages/sdk/server/tests/{server,plugin-apply,plugin-shape}.spec.ts \
packages/sdk/client/tests/{sdk-client,dispose}.spec.ts \
packages/acp/acp/tests/{approval,bridge,codec,dispose,edges,multi-session,turns}.spec.ts \
packages/subagent/subagent-acp/tests/subagent-acp.spec.ts \
packages/api/gateway/tests/{gateway.client,gateway.host}.spec.ts \
packages/api/remotes/tests/agent-lookup.spec.ts \
packages/client/connection/tests/{api-request-trust.host,http-bridge.host,websocket-downlink.host,connection.client}.spec.ts \
--reporter=verbose
Result: all 21 test files passed; 313/313 tests passed.
Duration: 21.01s (reported by Vitest).
My Learning Notes
Autosaved only in this browser. Nothing is uploaded or committed. Export Markdown whenever you want to keep a copy.