API Contract
This page documents only the A3S Code Node SDK behavior covered by
scripts/docs_api_contract_smoke.mjs. The script starts a temporary
OpenAI-compatible local test server, creates real SDK sessions, calls the native
binding, and asserts the returned values. It does not require a docs build.
Run the contract from the repository root:
Agent
Verified entry points:
Agent.create() accepts ACL source text or an .acl file path. JSON config is
not part of the verified contract. The integration check covers both
apiKey/baseUrl and api_key/base_url provider aliases against the local
OpenAI-compatible test server.
sessionForAgent() was verified with the built-in explore agent.
The Node factory names remain synchronous at the JavaScript surface, but their
native implementation delegates resource resolution to the core async session
construction path. Rust embedders should use SessionBuilder::build().await or
the async factories; the synchronous Rust compatibility factory requires an
explicit pre-initialized memory store.
The integration check also covers the ACL field that creates a file-backed session store:
This contract does not cover storage_url. It is not the local file-session
persistence path; use sessions_dir in ACL or pass sessionStore in SDK
options.
Session Options
The integration check covers this option shape:
model is a per-session override. The check verifies that a session created
with model: 'openai/docs-alt' sends docs-alt to the local provider.
Basic session accessors are verified:
workspace is returned as the SDK's canonical workspace path.
planningMode accepts the explicit modes documented elsewhere: 'auto',
'enabled', and 'disabled'.
The check also verifies that this permissionPolicy shape is accepted at
session creation:
Prompt slot options are strings:
Result Shape
session.send() returns AgentResult fields on the result object itself:
Trace events and verification reports are session APIs, not fields on
AgentResult.
Streaming
session.stream() returns EventStream. The verified consumption contract is
.next():
The smoke check starts another send() immediately after this loop. Exhaustion
therefore verifies both event delivery and release of the stream's single-flight
admission lease; no retry delay is required.
Each SDK event is an envelope-v1 projection with version === 1, an open
type string, a complete payload, and optional metadata. Consumers must
retain a default branch for future event types. Node exposes payloadJson and
metadataJson string views; Python exposes payload_json and metadata_json
and retains event_type as an alias for type.
Do not depend on for await unless your installed SDK version has separately
validated async iteration support.
Direct Tools
Full guide: Tools.
The integration check covers these host-driven direct calls:
The verified toolNames() set includes read, write, edit, patch,
grep, glob, ls, bash, task, parallel_task, search_skills, Skill,
program, git, batch, web_fetch, and web_search.
Direct host calls are privileged. Gate them in the host application before exposing them to end users.
AGENTS.md
The script writes an AGENTS.md file in the workspace and asserts that its
instruction token appears in the local provider request body:
Keep project instructions operational and free of secrets.
Programmatic Tool Calling
session.program() runs bounded JavaScript in the embedded QuickJS runtime:
Verified ctx helpers: readFile, read, grep, glob, ls, bash,
git, and generic tool(name, args). allowedTools limits which registered
tools the script may call. The program tool is not included in its own default
tool set.
Verification
Full guide: Verification.
Verification is session-scoped:
Memory
Full guide: Memory.
Node memory was verified with FileMemoryStore:
The verified recent-memory method is memoryRecent(). recallRecent() is not
present on the current Node SDK surface.
Skills
Full guide: Skills.
File-backed and inline skills are verified through search_skills:
The skill-file check uses Markdown with YAML frontmatter and the
allowed-tools key.
Side Questions
Full guide: Sessions.
The current SDK surface has no dedicated ephemeral-question helper. Use explicit history when the call must not mutate the session transcript:
Runs And Cancellation
Full guide: Sessions.
Each send() or stream() records replayable run state:
currentRun() is for the current operation. When idle, it may return
null or a retained snapshot depending on the preceding control flow. Use
runs() for completed history.
Transcript-affecting operations are single-flight per session. An overlapping
send, stream, attachment call, slash command, or resumeRun fails immediately
with SessionBusy; it is not queued. A stream retains admission until its
producer has stopped, even when its public handle is dropped.
Persistence
Full guide: Persistence and Sessions.
File-backed session persistence was verified with stable sessionId,
autoSave, explicit save(), and resumeSession():
Core persistence commits the conversation and its artifacts, traces, run
records, verification reports, and subagent task snapshots together as one
versioned SessionSnapshotV1. File and memory stores publish that aggregate
atomically. Legacy fragmented records remain loadable, while custom stores must
implement aggregate save explicitly.
Use session.close() when a Node process should release session-scoped
background resources promptly. close() is the full graceful-stop entry
point: it flips session.isClosed to true (further send / stream
calls reject with a Session closed error), fires the session-level
CancellationToken so every in-flight run, delegated subagent task, and
HITL confirmation aborts. Subsequent close() calls are no-ops.
For control-plane callers that only know the session ID, the same cleanup is reachable from the agent:
After agent.close(), subsequent agent.session(...) and
agent.resumeSession(...) calls reject with a Session closed error.
Idempotent. Use this in process-shutdown handlers to guarantee no
session-scoped workers outlive the agent.
Delegation
Full guide: Tasks and Orchestration.
The direct helpers for the core delegation tools were verified:
They return ToolResult values from task and parallel_task.
Hooks
Full guide: Hooks.
The verified hook management surface is:
Validate the specific event path you depend on before using hook behavior as a production enforcement gate.
Slash Commands
Full guide: Commands.
Custom slash commands are invoked through session.send():
Lane Queue
Full guide: Lane Queue.
Queue infrastructure is opt-in:
Ordinary sessions are queue-free unless queueConfig is provided.
MCP
Full guide: MCP. Idle disconnect: Cluster Extension Points.
The integration check covers a live stdio MCP server:
Tools from the server are named mcp__<server>__<tool>.
addMcpServer(...) and addMcpServerConfig(...) remain compatibility aliases;
new examples use the compact object-shaped addMcp(...) API.
Live add/remove operations target a private manager owned by this session. Agent-global and host-supplied managers are inherited read-only capability sources, so a session cannot mutate a sibling or global MCP configuration.
Cluster-grade extension points
Full guide: Cluster Extension Points (identity labels, budget guard, cluster events, deterministic IDs/replay, loop checkpoints, retention caps).
These contracts let a cluster control plane wire multi-tenancy, cost governance, and crash-tolerant runs without forking the framework. The framework defines decision points and emits structured events; the host supplies the policy implementations.
Identity labels
Four optional SessionOptions slots are propagated through hooks,
traces, and SessionData but never interpreted by the framework:
apply_persisted_runtime_options restores them on resume; caller-
supplied options on resume take precedence so you can relabel.
Budget / cost guard
BudgetGuard is consulted before every run-owned provider call (and after a
successful response for usage accounting) and before every governed tool call,
including nested and trusted host-direct calls. Deny returns
CodeError::BudgetExhausted { resource, reason }; SoftLimit emits
an AgentEvent::BudgetThresholdHit { kind: "soft", .. } and proceeds.
Rust hosts inject the trait directly. Node.js, Python, and Go expose callback bridges later in this section:
Cluster event vocabulary
AgentEvent (non-exhaustive) carries platform-level events the host
emits via HookExecutor:
BudgetThresholdHit { resource, kind, consumed, limit, message? }PassivationRequested { reason, deadline_ms? }PeerInvocation { from_session_id, from_tenant_id?, correlation_id? }
In-session hooks subscribe to these to react uniformly regardless of how the host's transport delivers them.
Deterministic IDs / time
HostEnv { id_generator, clock } replaces the default
uuid::Uuid::new_v4() + wall-clock pair. Replay tooling configures
SequentialIdGenerator + FixedClock to recreate a run bit-identical
on another node.
Loop checkpoints + run resumption
When a SessionStore is configured, the agent loop persists a
LoopCheckpoint after each completed tool round, keyed by run_id.
Any node holding the same store can rehydrate a run from its last
boundary:
A new run id is allocated for the resumed work — the framework does not pretend the old run continues. Two distinguishable error paths:
"resume_run requires a session_store"— host should fall back to a fresh session."no loop checkpoint found for run 'X'"— host can retry later (race against checkpoint write) or treat the run as lost.
Boundary policy: checkpoints are taken only between tool rounds, never mid-tool. If a process dies mid-tool the work of that round is lost; the LLM re-deliberates from the previous boundary. This trades retry cost for correctness — re-executing a non-idempotent tool across the boundary is worse than re-asking the LLM.
Retention caps for long-running sessions
SessionRetentionLimits lets the host cap the four in-memory stores
that grow with session age: the run records, per-run event buffers,
trace events, and terminal subagent task snapshots. Each cap is
optional (omission keeps the finite framework default; unbounded: true
deliberately restores unlimited retention). Eviction is
strict FIFO; running subagent tasks are never dropped.
The host should pick caps from the same observability budget that caps the rest
of its in-memory state (Prometheus carries history anyway). Node exposes this
as retentionLimits; Python as opts.retention_limits; Go as
SessionOptions.RetentionLimits.
MCP idle disconnect
Agent::disconnect_idle_mcp(threshold_ms) walks the connected MCP
servers and drops any whose last activity is older than
now - threshold_ms. The server's registered config stays — a
later tool call will reconnect on demand. Returns the names of
disconnected servers.
Activity is stamped on connect and on every successful call_tool.
Hosts that route tool traffic through a side channel can call
McpManager.touch(name) to manually keep a server warm.
BudgetGuard SDK bridges
All callback-capable SDKs accept the same decision shape:
Missing methods on the guard object are treated as a permissive default (Allow / no-op). Python callback errors fall back to Allow. Go callback errors, timeouts, and malformed decisions fail closed as Deny.
Node callbacks receive a single ctx object and must not throw; wrap handler
logic in try/catch and return an explicit decision. Hung or unreadable
check* callbacks fail closed as deny. Python callbacks use positional
arguments and catch exceptions as Allow. Go handlers receive typed contexts;
callback errors and timeouts fail closed.
Pass null to setBudgetGuard (Node) or set opts.budget_guard = None and re-create the session (Python) to clear. Call
session.SetBudgetGuard(ctx, nil) in Go.