A session is its durable conversation and execution state. Sending a message starts a run; the run exposes events and a final result or interruption. Keep the session reference in your application database.
Use session.state() for current activity, pending interactions and outstanding
timers. On restart, reattach to the saved session rather than creating a replacement.
Reconnect/replay does not mean replaying side effects. If an action’s outcome is
unknown, inspect its status before retrying it.
Agent identity and workspace
Agent names identify persisted immutable definitions. Changing instructions, tools, approval mode or other definition fields requires a new name. Identical registration is idempotent; creating another session does not replace a definition. Keep old identities for their existing sessions. A fresh state directory is a separate development environment, not a migration of existing conversations.
An explicit workspace selects the working directory. Omitting it normally
allocates a private scratch workspace. scratch: false requires an explicit
workspace; scratch: true cannot accompany one. Scratch does not promise automatic
deletion. Keep state outside files available to tools, and enforce directory scope
in app callbacks; a workspace is not an OS sandbox.
Continue after an application restart
Save the session reference with your application’s conversation and owner record:
const session = await agent.newSession();
const reference = session.reference(); // Persist in the application's database.
await session.run('Investigate this support request.');After starting the harness with the same persistent state and registering the same agent definition, load that record and reattach:
const session = await agent.resumeSession(reference);
const state = await session.state(); // Inspect pending work or interactions first.
// Submit a new message only when the session is ready for it.
const result = await session.run('What should we do next?');Your app authorizes the user before loading the reference. Recovery uses the saved execution state; it does not rerun old tool calls as a new task. Restore model credentials at startup when using memory-only credential storage.
Context and limits for each turn
Use instructionAddendum in TypeScript, or TurnOptions.instruction_addendum in
Rust, for application-owned preferences that change between turns. The addendum
accompanies every model call and retry in that turn, survives recovery, and is
included in the assembled request inspected by before-model hooks. User-input
guardrails apply to the user message, not this application-owned field. It is stored with the turn rather than
as user transcript text. It expires for the next turn. Keep user-supplied text in
the user message; an addendum does not grant tools, approvals or sandbox access.
const run = await session.send("Suggest names for my app.", {
submissionId: "naming-turn-42",
instructionAddendum: "Prefer .ai domains. The application budget is $40.",
limits: {
maxModelCalls: 8,
maxOutputTokens: 60000,
maxOutputTokensPerCall: 24000,
summaryTokens: 512,
},
});maxOutputTokensPerCall defaults to 8,192 and applies to each working-model call.
The remaining turn budget may reduce it. maxOutputTokens is the total output
budget for the turn, including the final summary reserve; increasing a per-call
limit does not increase that total. Set limits on the agent for defaults or on
send for one turn. The selected model must support the requested per-call limit.
Reusing a submission ID requires the same message, context, limits and safety
options; a changed addendum is a new turn with a new submission ID.
Guide the auto-approver and compactor
Source preview: use the matching updated runtime. For embedded Rust and
managed-subprocess TypeScript sessions, autoApproverContext supplies application
facts and review guidance to the action reviewer. compaction.additionalInstructions
adds summary guidance. Both can be set on an agent, a new session, or one turn.
TypeScript
const session = await agent.newSession({
autoApproverContext: "Staging workspace. Ask before publishing.",
compaction: { additionalInstructions: "Preserve decisions and unfinished tests." },
});
await session.run("Continue.", {
autoApproverContext: "For this turn, changes must remain inside src/.",
compaction: { additionalInstructions: "Keep the migration checklist." },
});Rust
let session = agent.new_session_with(
SessionOptions::default()
.auto_approver_context("Staging workspace. Ask before publishing.")
.compaction(CompactionOptions::additional_instructions(
"Preserve decisions and unfinished tests."
))
).await?;
let run = session.send_with_options("Continue.", TurnOptions {
auto_approver_context: Some("Changes must remain inside src/.".into()),
compaction: Some(CompactionOptions::additional_instructions("Keep the migration checklist.")),
..Default::default()
}).await?;
let result = run.result().await?;Precedence is agent → session → turn. Omitted fields inherit; an empty string clears inherited guidance for that scope. Turn overrides survive recovery and apply to retries and tool follow-ups in that turn. They do not change the next turn’s defaults. Reusing a submission ID with changed guidance fails.
Each string supports up to 32,000 UTF-8 bytes and no NUL. Guidance is stored separately from user history. Reviewer context does not enable auto-approval or grant tool access: configure auto-approval and a reviewer model separately. Hard policy checks still run first; an unavailable or unsure reviewer asks the human. Compaction guidance supplements the required summary structure. These session/turn overrides are not yet exposed by the service-cluster REST client.
Attach a file
The SDK supports images, PDFs and UTF-8 files as ordered user-message content. See Attachments for the convenience and interleaved forms, storage limits and guardrail behavior.
Import a conversation
Use initialHistory only when creating a new session. Ordinary recovery should
resume the existing session reference. The portable import format supports plain
user/assistant text and completed tool calls/results:
const session = await agent.newSession({ initialHistory: [
{ type: "user", text: "Check Atlas." },
{ type: "tool_call", callId: "old-lookup", name: "app__lookup",
arguments: { name: "Atlas" } },
{ type: "tool_result", callId: "old-lookup", output: '{"available":false}' },
{ type: "assistant", text: "Atlas is taken." },
] });Imported calls are historical data and never execute. Import validates the entire transcript before creating a session, and the history and new session commit together. A nonempty transcript must start with a user message. Every tool call needs exactly one matching result; duplicate IDs and unfinished calls are rejected. Limits are 256 items, 256,000 bytes per text/arguments/result, 512,000 serialized bytes total, and 32 levels of argument nesting. Tool arguments must be objects.
This format does not accept system messages, approvals, internal runtime notices, images or provider-private reasoning/signatures. Translate your application’s transcript to the portable fields rather than passing raw provider JSON. If a provider requires signed reasoning metadata to replay an old tool call, resume the original harness session or import a text-only conversation summary instead. An import never restores permission grants or an in-progress operation.
Import validates structure and bounds; it does not retroactively run user-input guardrails or tool-result hooks over the old transcript. The application must sanitize migrated content before import. Before-model hooks can inspect the assembled request, including imported history, before the next model call.
Approvals and questions
Render durable approval/question interruptions in your UI, then send the user’s actual response. Configure the decision path in the agent definition:
| TypeScript mode | Behavior |
|---|---|
always (default) | Requests a human decision unless a valid session grant covers the tool. |
only with a tool list | Requests approval for listed tools; ordinary unlisted tools receive application-policy approval. |
auto with reviewerModel | An explicit LLM reviews eligible calls: allow executes, deny returns a denial, uncertain requests human input. |
never | Bypasses approval review; hard security checks still apply. |
Rust exposes Approvals::Always, Only, auto(model) and Never. Auto review
can include read-only tools. Hard denials and valid session grants are checked
first. Privileged child/type-creation operations have additional requirements;
omission from an only list does not automatically exempt them.
Use once when each mutation requires a new card. A session grant covers the
entire tool, including different valid arguments, survives restart and does not
override hard checks. Questions are separate from approval decisions. See the
complete streamed approval loop.
The typed auto_approved summary currently describes application-policy approval,
not a model review verdict. Raw interaction_required / interaction_resolved
events can include data.review.verdict, a safe reason, origin and nested
call.call_id; handle absent/withheld content. A denied tool result tells the
working model not to retry or evade the denial, but currently omits the reviewer’s
reason. The app can display an available safe journal reason itself.
A tool-free guide can disable optional tools, memory, planning and scheduling. Auto-skip questions is a separate setting, not an authorization mode.
An agent with application tools can also opt out of implicit session tools:
const agent = harness.createAgent({
name: "naming", instructions: "Help the user choose a name.",
tools: ["app.lookup"],
builtinTools: { askUser: false, readHistory: false },
defaultModel: { provider: "OpenAI", name: "YOUR_MODEL_ID" },
});Omitting builtinTools retains the existing behavior: question/history tools
accompany a nonempty selected tool catalog, while a completely tool-free agent
gets neither. Explicit true enables that built-in tool, including on an otherwise
tool-free agent. Disabled tools are absent from the provider request and unexpected
calls are rejected. These settings are part of the immutable agent definition;
changing them requires a new agent identity and new sessions.
These APIs require an updated native runtime as well as the SDK package. Clients check host capabilities before sending the new options. Omitted options retain existing agent definitions and session schema digests across upgrades.
Standalone sub-agents
Enable delegation on a normal agent, then let its model create and message children. See sub-agents and teams for configuration, the model-facing tool and workspace behavior.
Teams
Use an explicit team for named agent types, board communication and configurable sandbox placement. Compare teams with standalone sub-agents.