A3S Docs
A3S Code

Sessions

Creating, streaming, resuming, and saving workspace-bound sessions

Sessions

Agent owns configuration and provider state. Session binds that agent to one workspace and one conversation lifecycle.

import { Agent } from '@a3s-lab/code';

const agent = await Agent.create('agent.acl');
const session = agent.session('/repo', {
  model: 'openai/MiniMax-M2.7-highspeed',
  builtinSkills: true,
  planningMode: 'enabled',
  goalTracking: true,
  autoDelegation: { enabled: true, maxTasks: 4 },
  autoParallel: false,
});

planningMode is explicit: use 'auto' for the default structured pre-analysis path, 'enabled' to force planning, and 'disabled' to turn planning off for latency-sensitive calls. The legacy boolean planning option remains available for compatibility.

Planning emits run-scoped state. Hosts can render that state as a TaskList and update each item as the runtime records progress instead of trying to infer progress from text tokens.

Send

const result = await session.send('Review this repository and list release blockers');

console.log(result.text);
console.log(result.totalTokens);
console.log(result.verificationStatus);

Stream

const stream = await session.stream('Run the focused tests and explain failures');

while (true) {
  const { value: event, done } = await stream.next();
  if (done) break;
  if (!event) continue;

  if (event.text) process.stdout.write(event.text);
  if (event.toolName) console.log('tool:', event.toolName);
}

Side Questions

btw() asks an ephemeral read-only side question. It snapshots the current history and does not append the answer back into the session.

const answer = await session.btw('What files has this session already inspected?');
console.log(answer.answer);

Resume

import { Agent, FileSessionStore } from '@a3s-lab/code';

const agent = await Agent.create('agent.acl');
const session = agent.resumeSession('release-review', {
  sessionStore: new FileSessionStore('./.a3s/sessions'),
});

Set autoSave: true or call await session.save() when using a session store.

Lifecycle and Close

session.close() is a full graceful stop. The first call flips the session into the closed state — further send/stream calls fast-fail with CodeError::SessionClosed instead of starting a new run — then cancels the active run, every in-flight delegated subagent task, and all pending human-in-the-loop confirmations. Subsequent calls are no-ops and never panic. Check the closed state with session.isClosed() (Node) / session.is_closed() (Python).

await session.close();
if (session.isClosed()) {
  // send/stream now reject with CodeError::SessionClosed
}
session.close()
if session.is_closed():
    # send/stream now reject with CodeError::SessionClosed
    pass

Cancellation token

Every run derives its per-operation cancellation token from a single session-level parent via child_token(), so close() cascades to all in-flight work in one shot. Embedders that need the raw token — for example to wire it into a host-side select! or to abort the session without the run-store and hook side effects of close() — can clone it through AgentSession::session_cancel_token().

Agent-side registry

The owning Agent tracks its live sessions by Weak reference (pruned lazily) so a control plane can drive lifecycle without holding a session handle:

  • Agent::list_sessions() returns the live session IDs (sorted, stable).
  • Agent::close_session(id) closes one session by ID — the same cleanup as AgentSession::close(), invoked out-of-band.
  • Agent::close() closes every live session and tears down agent-owned background resources (it also disconnects the global MCP connections). After it returns, new session / resumeSession calls fail fast with CodeError::SessionClosed.
  • Agent::is_closed() reports whether the agent itself has been closed.
const ids = await agent.listSessions();
await agent.closeSession(ids[0]);
await agent.close(); // closes all remaining sessions + global MCP
console.log(agent.isClosed());
ids = agent.list_sessions()
agent.close_session(ids[0])
agent.close()  # closes all remaining sessions + global MCP
print(agent.is_closed())

See CHANGELOG [3.3.0] — "Session / Agent lifecycle control".

Host Identity Labels

SessionOptions carries four opaque identity fields that the host can attach at session creation. The framework only transports them — it never interprets or enforces them. They are propagated into SessionData, hooks, and traces, and restored on resume, so the host can drive multi-tenant aggregation, billing, and distributed tracing:

Node (camelCase)Python (snake_case)Meaning
tenantIdtenant_idMulti-tenant label
principalprincipalUser / service that triggered the session
agentTemplateIdagent_template_idAgent template / definition the session was instantiated from
correlationIdcorrelation_idDistributed-trace correlation id
const session = agent.session('/repo', {
  tenantId: 'acme-corp',
  principal: 'user-42',
  agentTemplateId: 'planner-v3',
  correlationId: 'trace-deadbeef',
});
opts = SessionOptions(
    tenant_id='acme-corp',
    principal='user-42',
    agent_template_id='planner-v3',
    correlation_id='trace-deadbeef',
)
session = agent.session('/repo', opts)
print(session.tenant_id, session.principal)

See CHANGELOG [3.3.0] — "Host-provided identity labels".

Run Replay

Each send() or stream() creates a run record. Applications can inspect those records for UI state, audit, replay, cancellation, and tests:

const runs = await session.runs();
const latest = runs.at(-1);

if (latest) {
  console.log(await session.runSnapshot(latest.id));
  console.log(await session.runEvents(latest.id));
}

currentRun() is for the operation that is current at the time of the call. During an active send() or stream(), pass its id to cancelRun(id) to request cancellation. When idle, currentRun() may return null or a retained run snapshot depending on the preceding control flow, so use runs() for completed history and inspect status before treating a snapshot as cancellable:

const current = await session.currentRun();
if (current?.id && current.status === 'running') {
  await session.cancelRun(current.id);
}

Agent Definitions

sessionForAgent() applies a named agent definition from built-in agents, .a3s/agents, or configured agentDirs.

const session = agent.sessionForAgent('/repo', 'explore', ['./agents'], {
  builtinSkills: true,
});

On this page