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 textmaxSteps caps the loop; the default is 50.
Events
stream() emits ten AgentEvent types:
| type | Fields | When |
|---|---|---|
text_delta | delta | model output |
thinking | delta | extended thinking (when enabled) |
step_start / step_end | step | boundaries of each step |
tool_call | tool input id | the model decided to call a tool |
tool_result | tool output id | the tool returned |
tool_error | tool error id | the tool threw or timed out |
permission_request | tool input id | approval needed (see below) |
done | usage stopReason | the run finished |
error | error | runtime 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:
| stopReason | Meaning |
|---|---|
end_turn | the model converged |
max_steps | hit maxSteps |
tool_error | a tool failure ended the run |
aborted | abort() or an external signal |
permission_denied | a tool call was not approved |
Permission modes
permission takes four forms:
| Value | Behaviour |
|---|---|
'auto' (default) | normal tools run; dangerous and high-risk calls are denied |
'readonly' | only tools marked readOnly run |
PermissionHandler | your 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.

