Skip to content
Agent SDK

Core concepts

The loop, events, stop reasons, permission modes.

The loop

agent.run(prompt) starts one agent loop: the model emits text or tool calls, tool results are fed back, and it repeats until the model converges or a cap trips.

prompt → [model] → tool call? ──yes──> [run tool] → feed result ─┐
                      │no                                        │
                      ↓                                          └─ next step
                  converge, return text

maxSteps caps the loop; the default is 50.

Events

stream() emits ten AgentEvent types:

typeFieldsWhen
text_deltadeltamodel output
thinkingdeltaextended thinking (when enabled)
step_start / step_endstepboundaries of each step
tool_calltool input idthe model decided to call a tool
tool_resulttool output idthe tool returned
tool_errortool error idthe tool threw or timed out
permission_requesttool input idapproval needed (see below)
doneusage stopReasonthe run finished
errorerrorruntime failure

Getting the result

The object returned by stream() is both iterable and awaitable — no need to reassemble text_delta yourself:

const stream = agent.stream(prompt);
for await (const ev of stream) render(ev);
const { text, usage, messages, stopReason } = await stream.result();

Stop reasons

AgentResult.stopReason matches the done event:

stopReasonMeaning
end_turnthe model converged
max_stepshit maxSteps
tool_errora tool failure ended the run
abortedabort() or an external signal
permission_denieda tool call was not approved

Permission modes

permission takes four forms:

ValueBehaviour
'auto' (default)normal tools run; dangerous and high-risk calls are denied
'readonly'only tools marked readOnly run
PermissionHandleryour decision, may be async
'ask'requires a handler; throws at run time without one
new Agent({
  model: 'claude-sonnet-4-6',
  tools,
  permission: async ({ tool, input, dangerous, risk }) => ({
    approved: !dangerous || (await humanApproves(tool, input)),
    reason: 'needs human confirmation',   // fed back to the model on denial
  }),
});

'ask' without a handler fails loudly instead of silently denying every call — a missing approval path is a configuration error.

Usage

usage is { inputTokens, outputTokens, cacheReadTokens?, cacheWriteTokens? }. The SDK does not convert to currency — apply your own provider pricing.

Thinking

thinking: 'auto' | 'high' | 'off'. When on, thinking events carry reasoning deltas; whether they are billed depends on the provider.

Multi-turn sessions

A Session accumulates history; with a checkpointDir it persists after every turn, and Session.resume() picks it back up across process restarts.

const session = await createSession({
  model: 'claude-sonnet-4-6',
  checkpointDir: './.neox-sessions',
});
await session.send('Take a look at this module');
await session.send('Expand on your second point');      // carries prior context

const revived = await Session.resume(session.id, {
  checkpointDir: './.neox-sessions',
  model: 'claude-sonnet-4-6',
});

Snapshots exclude the provider (and therefore API keys) — supply one on resume or rely on the environment.