SDK API reference
This page documents the TypeScript API exported by @anturno/nod/sdk. Unlike the CLI, every interface below is a real exported type. For setup and examples, see the Bun SDK guide.
API index
| Function or method | Returns | Purpose |
|---|---|---|
createAgent(options) |
Promise<Agent> |
Create one conversation. |
agent.prompt(input, options?) |
Turn |
Run a prompt and stream events. |
agent.checkpoint() |
Promise<Uint8Array> |
Export conversation history and usage. |
agent.close() |
Promise<void> |
Stop the agent and release its runtime. |
| Function or method | Returns | Purpose |
|---|---|---|
turn.cancel() |
void |
Cancel the active turn. |
| Function or method | Returns | Purpose |
|---|---|---|
createTerminal(options) |
Promise<TerminalRuntime> |
Start the interactive terminal. |
terminal.write(data) |
void |
Send terminal input. |
terminal.resize() |
void |
Notify nod of a size change. |
terminal.abort() |
void |
Stop the terminal and release listeners. |
| Function or method | Returns | Purpose |
|---|---|---|
createMcpTools(client, options?) |
Promise<McpTools> |
Convert an MCP client's tools and context. |
mcp.close() |
Promise<void> |
Close the adapter and its client. |
| Function or method | Returns | Purpose |
|---|---|---|
createSkillsAdapter(records) |
SkillsAdapter |
Convert loaded skill records. |
loadSkillFile(path, options?) |
Promise<SkillRecord> |
Read one skill file. |
| Function or method | Returns | Purpose |
|---|---|---|
listModels(options) |
Promise<string[]> |
List available model IDs. |
xtermAdapter(term) |
TerminalAdapter |
Connect an xterm.js instance. |
encodeXtermKeyEvent(event) |
string | null |
Encode special terminal keys. |
Imports
import {
createAgent,
createTerminal,
createMcpTools,
createSkillsAdapter,
loadSkillFile,
listModels,
sdkApiVersion,
} from "@anturno/nod/sdk";
import { xtermAdapter, encodeXtermKeyEvent } from "@anturno/nod/sdk/xterm";
@anturno/nod/sdk runs on Bun. @anturno/nod/sdk/xterm has no Bun dependency and is meant for a browser bundle. sdkApiVersion is currently 1; it identifies the API revision, not the package version.
Interfaces
| Interface | Describes |
|---|---|
AgentOptions |
Credentials, model, instructions, tools, permissions, workspace, and checkpoint input. |
Agent |
The conversation's methods. |
PromptInput and PromptOptions |
Text, resources, images, and cancellation. |
Turn |
An event stream, result promise, and cancel method. |
TurnEvent |
Text, reasoning, and tool events. |
TurnResult and Usage |
Stop reason and token counts. |
HostTool |
A tool's schema and execution callback. |
ApprovalRequest and ApprovalDecision |
What the permission callback receives and returns. |
DiagnosticEvent |
Runtime diagnostics sent to onEvent. |
TerminalOptions |
Terminal adapter, configuration, and stores. |
TerminalRuntime |
Terminal readiness, exit, and control methods. |
TerminalAdapter |
Input, output, and geometry supplied by your UI. |
McpTools |
Adapted tools, instructions, and cleanup. |
SkillRecord and SkillsAdapter |
Loaded skill text, resources, and tools. |
Shared notation used below:
type MaybePromise<T> = T | Promise<T>;
type Bytes = ArrayBuffer | ArrayBufferView;
type Fetch = typeof globalThis.fetch;
type Provider = "codex" | "grok";
type Auth = { provider: Provider } | { provider: Provider; token: string };
{ provider } reads the session saved by nod login under ~/.nod (or NOD_HOME) and refreshes it when needed. { provider, token } uses a subscription access token the host obtained itself and never touches disk.
Agent
interface Agent {
prompt(input: PromptInput, options?: PromptOptions): Turn;
checkpoint(): Promise<Uint8Array>;
close(): Promise<void>;
}
An agent has no createSession(), setModel(), or history property. To change creation options, save a checkpoint and restore it into a new agent with those options.
createAgent
createAgent(options: AgentOptions): Promise<Agent>
Creates one in-memory conversation. The promise resolves after credential resolution and any checkpoint restore finish. No model request is made until the first prompt.
AgentOptions
interface AgentOptions {
auth: Auth;
model?: string;
effort?: "auto" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
instructions?: string | string[];
tools?: HostTool[];
checkpoint?: Bytes;
fetch?: Fetch;
onEvent?: (event: DiagnosticEvent) => void;
permissions?: (request: ApprovalRequest, signal: AbortSignal) => MaybePromise<ApprovalDecision>;
workspace?: { cwd: string; shell?: ShellRunner };
}
| Option | Required | Default | Behavior |
|---|---|---|---|
auth |
Yes | None | Provider and, optionally, a token. See Auth. |
model |
No | The provider default | Model ID, at most 1 KiB of UTF-8. |
effort |
No | "auto" |
Reasoning effort, when the model supports it. |
instructions |
No | No system message | Complete system instructions. An array joins non-empty entries with blank lines. At most 64 KiB of UTF-8 after joining. |
tools |
No | [] |
Up to 64 explicit host tools. No CLI tools are enabled automatically. |
checkpoint |
No | New conversation | Opaque bytes from agent.checkpoint(). The input is copied. |
fetch |
No | globalThis.fetch |
Host-controlled HTTP transport. Preserve the supplied AbortSignal. |
onEvent |
No | None | Synchronous diagnostic callback. Model output is on the Turn stream instead. |
permissions |
No | Deny everything | Called for every sensitive built-in call when workspace is set. See Permissions. |
workspace |
No | None | Enables nod's built-in file and shell tools rooted at cwd. shell replaces the command runner. |
Do not pass env: the agent rejects it. Terminal settings, session stores, and CLI permission modes are not agent configuration.
Invalid options reject creation. A missing saved session for auth.provider and invalid checkpoints also reject the promise. See Errors.
agent.prompt
agent.prompt(input: PromptInput, options?: PromptOptions): Turn
Starts a turn in the conversation and returns a Turn immediately, not a promise. Read events with for await, then await turn.result.
import { createAgent } from "@anturno/nod/sdk";
const agent = await createAgent({ auth: { provider: "codex" } });
try {
const turn = agent.prompt("Explain this schema.");
for await (const event of turn) {
if (event.type === "text_delta") process.stdout.write(event.delta);
}
const result = await turn.result;
console.log(result.stopReason, result.usage);
} finally {
await agent.close();
}
PromptInput and PromptOptions
type PromptInput = string | PromptBlock[];
type PromptBlock =
| { type: "text"; text: string }
| { type: "resource"; resource: { uri: string; text?: string } }
| { type: "image"; mimeType: string; data: string };
interface PromptOptions {
signal?: AbortSignal;
}
A string is equivalent to one text block. For a resource, pass its text explicitly; the URI identifies the resource and does not grant file access. Image blocks carry base64 data in PNG, JPEG, GIF, or WebP, up to eight per prompt and 5 MiB each. Audio blocks are not supported.
const turn = agent.prompt(
[
{ type: "text", text: "Summarize this file." },
{
type: "resource",
resource: {
uri: "file:///workspace/schema.sql",
text: "CREATE TABLE users (id INTEGER PRIMARY KEY);",
},
},
],
{ signal: controller.signal },
);
prompt() throws synchronously for malformed input, a closed agent, or another active prompt. An already-aborted signal returns a cancelled turn without making a model request or changing history. Network and runtime failures during execution reject the stream or result promise.
agent.checkpoint
agent.checkpoint(): Promise<Uint8Array>
Returns opaque, versioned conversation bytes while the agent is idle. It rejects while a prompt is active or after the agent closes. Store the bytes without editing them and restore them only through createAgent({ checkpoint, ...options }). The format is the same recovery checkpoint the CLI writes for a session.
A checkpoint contains history and usage, not credentials, model selection, instructions, tools, MCP clients, or skills. Resupply those options on restoration. Protect stored checkpoints as conversation data.
const checkpoint = await agent.checkpoint();
await agent.close();
const restored = await createAgent({
auth: { provider: "codex" },
checkpoint,
});
// Continue with restored.prompt(...), then await restored.close().
agent.close
agent.close(): Promise<void>
Cancels an active turn, releases blocked output, and waits for the runtime to exit. Repeated calls are safe. The agent cannot be prompted or checkpointed afterward. Closing an agent does not close host-owned database connections or MCP clients.
Turn
interface Turn extends AsyncIterable<TurnEvent> {
result: Promise<TurnResult>;
cancel(): void;
}
A turn permits one event consumer. Read the stream even when you only need the result:
const turn = agent.prompt("Summarize the discussion.");
for await (const _ of turn) {
}
const result = await turn.result;
A slow reader pauses output production instead of growing an unlimited queue. Awaiting only turn.result can stall while unread events wait to be consumed. Breaking out of the iterator cancels the turn. Do not start another prompt until the current one settles.
turn.cancel
turn.cancel(): void
Requests cancellation without waiting for completion. It aborts model requests and the signals passed to host tools. Continue draining the stream and await turn.result to observe completion. Calling cancel() again, or after the turn finishes, has no effect.
const controller = new AbortController();
const turn = agent.prompt("Review the schema.", { signal: controller.signal });
controller.abort(); // Equivalent to requesting cancellation with turn.cancel().
for await (const _ of turn) {
}
console.log((await turn.result).stopReason);
Cancellation stops waiting for tool callbacks; it cannot stop JavaScript work that ignores its signal. Late tool results and rejections are ignored.
TurnEvent
type TurnEvent =
| { type: "text_delta"; delta: string }
| { type: "reasoning_delta"; delta: string }
| { type: "tool_start"; id: string; name: string }
| {
type: "tool_end";
id: string;
name: string;
content?: string;
isError: boolean;
};
| Event | Fields | Meaning |
|---|---|---|
text_delta |
delta |
Append this text to the answer. |
reasoning_delta |
delta |
Reasoning text when the provider supplies it. |
tool_start |
id, name |
A tool call started. |
tool_end |
id, name, content?, isError |
A tool call completed or failed. Match it to its start by id. |
tool_end.content is the available text result, not the original JavaScript return value. There is no separate final-result event; use turn.result after reading the stream.
TurnResult and Usage
interface TurnResult {
stopReason: StopReason;
usage: Usage;
}
type StopReason = "end_turn" | "max_output_tokens" | "max_model_turns" | "refused" | "cancelled";
interface Usage {
inputTokens?: number;
outputTokens?: number;
cacheReadTokens?: number;
cacheWriteTokens?: number;
reasoningTokens?: number;
}
| Stop reason | Meaning |
|---|---|
end_turn |
The turn ended normally. |
max_output_tokens |
The response reached its output-token limit. |
max_model_turns |
The turn reached its model-step limit. |
refused |
The model refused the request. |
cancelled |
The turn was cancelled. |
usage is always an object. Its fields are optional: missing counts are omitted, not replaced with zero. A prompt cancelled before it starts returns { stopReason: "cancelled", usage: {} }. Transport or decoding failures reject rather than returning a successful result with missing output.
HostTool
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
interface HostTool {
name: string;
description: string;
inputSchema: Record<string, JsonValue>;
execute(input: unknown, context: { signal: AbortSignal }): MaybePromise<JsonValue | undefined | RichToolResult>;
}
interface RichToolResult {
type: "nod.tool-result";
text: string;
images: Array<{ type: "image"; mimeType: string; data: string }>;
isError?: boolean;
}
Tool names must be unique, contain 1–64 letters, digits, underscores, or hyphens, and have a JSON-serializable object schema. Your callback must validate and authorize actions before executing them.
Strings return as text. Other ordinary results are JSON-encoded; undefined becomes "null". A thrown error becomes a failed tool result with its message. Use RichToolResult for image results: data is base64, with PNG, JPEG, GIF, and WebP supported. Ordinary objects are not interpreted as images.
Up to eight images are allowed, each with at most 5 MiB of base64 data and an 8 MiB serialized rich-result limit. Keep tool results small and honor context.signal.
Permissions
interface ApprovalRequest {
toolName: string;
label: string;
kind: "command" | "file" | "mcp" | "other" | "confirm";
detail?: string;
preparation?: string;
targets: string[];
suggestedGrants: string[];
}
interface ApprovalDecision {
outcome: "once" | "always" | "deny";
note?: string;
}
The callback runs only for built-in tools enabled by workspace, and only for calls the CLI would also stop on: file changes, commands, and external paths. once runs the call; always runs it and grants suggestedGrants for the rest of the agent's life; deny refuses it, and note is returned to the model with the refusal. The callback receives the turn's AbortSignal; a cancelled turn aborts a pending decision. Without a callback every request is denied.
TerminalRuntime
interface TerminalRuntime {
interactive: Promise<void>;
exited: Promise<number>;
write(data: string | Uint8Array): void;
resize(): void;
abort(): void;
}
| Member | Behavior |
|---|---|
interactive |
Resolves after the terminal reaches its input loop and optional adapter drain() finishes. Rejects if nod exits before reaching that loop or draining fails. |
exited |
Resolves with the exit code. An explicit abort uses 130. |
write(data) |
Sends text or bytes as input. Ctrl+C string input also cancels host effects; it does not immediately destroy the runtime. |
resize() |
Wakes nod to read the adapter's current cols and rows. It takes no dimensions. |
abort() |
Stops the runtime and releases data, key, and resize subscriptions. Returns immediately; await exited for the exit code. |
createTerminal
createTerminal(options: TerminalOptions): Promise<TerminalRuntime>
Starts the interactive terminal, not a headless Agent. It runs the same Ink interface as the nod command, in-process, drawing to your adapter instead of a TTY. Await runtime.interactive before sending input.
TerminalOptions
interface TerminalOptions {
terminal: TerminalAdapter;
env?: Record<string, string>;
args?: string[];
fetch?: Fetch;
onEvent?: (event: DiagnosticEvent) => void;
interruptKey?: string;
configStore?: ConfigStore;
promptHistoryStore?: PromptHistoryStore;
sessionStore?: SessionStore;
oauthSessionStore?: OAuthSessionStore;
openUrl?: (url: string) => MaybePromise<boolean>;
workspace?: WorkspaceAdapter;
}
| Option | Default | Behavior |
|---|---|---|
terminal |
Required | UI adapter for input, output, and size. |
env |
process.env |
Terminal environment, including NOD_HOME, NOD_MODEL, or NOD_PERMISSION_MODE. |
args |
[] |
Terminal CLI arguments, such as ["--resume", "last"]. |
fetch |
globalThis.fetch |
Host HTTP transport. |
onEvent |
None | Diagnostic callback. |
interruptKey |
"\x03" (Ctrl+C) |
String input containing this key also cancels active host effects. "" disables this detection. |
Stores, openUrl, workspace |
Files under ~/.nod |
Optional terminal host integrations listed below. |
Store methods can return their value directly or in a promise. See Terminal embedding for method signatures and revision rules:
| Interface | Contract |
|---|---|
ConfigStore |
get(id) and set(id, value) |
PromptHistoryStore |
load, append, and clear |
SessionStore |
load, commit, list, and remove |
OAuthSessionStore |
load, commit, and remove |
WorkspaceAdapter |
info, permission, and exec |
When a store is omitted, the terminal uses the same files the CLI uses under ~/.nod. These stores do not configure a headless agent. Use agent creation options and checkpoints instead.
TerminalAdapter
interface TerminalAdapter {
readonly cols: number;
readonly rows: number;
write(bytes: Uint8Array): void;
onData(callback: (data: string) => void): () => void;
onResize(callback: () => void): () => void;
onKeyData?(callback: (data: string) => void): () => void;
drain?(): MaybePromise<void>;
}
Subscription methods return unsubscribe functions. Keep dimensions current before emitting a resize. Use drain() when your UI needs to flush pending output before the terminal is considered interactive. The standard xterm adapter handles the subscriptions for you.
McpTools
createMcpTools
createMcpTools(client: McpClient, options?: McpOptions): Promise<McpTools>
interface McpOptions {
prefix?: string;
resources?: string[];
prompts?: Array<string | { name: string; arguments?: Record<string, string> }>;
}
interface McpTools {
tools: HostTool[];
instructions: string;
close(): Promise<void>;
}
McpClient is your already-connected client: a nod McpRuntime, or any object implementing listTools() and callTool(params, resultSchema?, options?), such as an MCP TypeScript SDK v1 client. Resource options also require readResource({ uri }); prompt options require getPrompt({ name, arguments? }).
| Option | Default | Behavior |
|---|---|---|
prefix |
"" |
Prefix for model-facing tool names; letters, digits, underscores, and hyphens only. |
resources |
[] |
Resource URIs whose text is added to instructions. |
prompts |
[] |
Prompt names or name/arguments objects whose text is added to instructions. |
Creation lists tools, follows pagination, and fetches the requested context. More than 64 tools, invalid or repeated cursors, duplicate original tool names, or instructions larger than 64 KiB fail creation. Tool names are normalized for model APIs; calls to the client retain the original names. Non-text prompt/resource context is replaced with an omission notice.
Pass mcp.tools and mcp.instructions into createAgent(). Tool cancellation reaches the client's third callTool argument as { signal }. mcp.close() calls client.close() if supplied, once. Close the agent first. The host chooses and authenticates the client and remains responsible for transport setup.
SkillsAdapter
createSkillsAdapter
createSkillsAdapter(records: SkillRecord[]): SkillsAdapter
interface SkillRecord {
name: string;
description?: string;
instructions: string;
resources?: Array<{ uri: string; text: string }>;
tools?: HostTool[];
}
interface SkillsAdapter {
instructions: string;
tools: HostTool[];
}
Combines up to 64 loaded records into instructions and a tool array. Skill names must be unique. Resource text is included directly; this function does not fetch resources or read files. Invalid records, duplicate names, or combined instructions larger than 64 KiB throw. Pass the returned fields into createAgent({ auth, ...skills }).
loadSkillFile
loadSkillFile(path: string, options?: LoadSkillOptions): Promise<SkillRecord>
interface LoadSkillOptions {
readFile?: (path: string, encoding: "utf8") => MaybePromise<string>;
resources?: Array<{ uri: string; text: string }>;
tools?: HostTool[];
}
Reads one file as UTF-8 using Bun's file API, or the supplied function. It uses the same SKILL.md frontmatter parser as the CLI: name and description, with the remaining text as instructions. Without a name, it uses the filename without .md. It does not scan directories or load referenced files. File errors and unterminated frontmatter reject the promise.
Helpers
listModels
listModels(options: ListModelsOptions): Promise<string[]>
interface ListModelsOptions {
auth: Auth;
fetch?: Fetch;
}
Returns sorted, unique model IDs for the provider in auth. fetch defaults to the global implementation. This performs one catalog request to the provider without creating an agent. The promise rejects on invalid options, a missing session, or a failed HTTP response. Agent creation does not call this function automatically.
xtermAdapter
xtermAdapter(term: import("@xterm/xterm").Terminal): TerminalAdapter
Wraps an xterm.js terminal and forwards input, output, geometry, and resize notifications. It installs a custom key handler for nod-specific key encodings when xterm supports that hook. The host still owns opening and disposing the xterm.js instance.
encodeXtermKeyEvent
encodeXtermKeyEvent(event: KeyboardEvent): string | null
Returns an escape sequence for Shift+Enter (\x1b[13;2u) and supported Meta+Backspace (\x1b\x7f) or Meta+arrow (\x1bb, \x1bf) key combinations. Returns null for keys it does not handle, non-keydown events, or Alt/Ctrl combinations. null means the terminal should use its normal handling.
DiagnosticEvent
onEvent receives runtime diagnostics, not the TurnEvent stream:
interface DiagnosticEvent {
type: string;
timestamp: number;
[detail: string]: unknown;
}
timestamp is milliseconds from performance.now(), not a Unix timestamp. The callback runs synchronously; exceptions thrown by it are ignored. Treat event-specific fields as diagnostics rather than the answer/result API.
| Event | Additional fields |
|---|---|
runtime.start, runtime.ready |
Terminal events include surface: "terminal". |
runtime.exit |
code; terminal events also include surface. |
transport.start |
attempt, method, endpoint, model when selected. |
transport.response |
attempt, status, elapsedMs, requestId, model, provider. Header-derived values can be null or absent. |
transport.error |
attempt, elapsedMs, error (error name). |
transport.retry |
attempt, nextAttempt, elapsedMs, error. |
output.backpressure |
bufferedBytes, bufferedEvents. |
permission.request, permission.decision |
toolName, kind, and the decision outcome. |
terminal.resize, terminal.size |
cols, rows. |
Transport metadata omits credentials and raw headers, but events can name tools and models. Do not log the entire diagnostic stream as if it were free of sensitive data.
Errors
| Operation | Failure behavior |
|---|---|
createAgent, createTerminal |
Reject on invalid options, a missing saved session for the provider, or initialization failure. |
agent.prompt |
Throws for invalid input, a closed agent, or a concurrent prompt. |
Turn iteration and turn.result |
Reject on transport, decoding, or runtime failure. Normal cancellation resolves with stopReason: "cancelled". |
agent.checkpoint |
Rejects during an active turn, after close, or if export fails. |
listModels |
Rejects invalid options, a missing session, or HTTP errors. |
| Host tool callback | A thrown error is sent to the model as a failed tool result. |
| Session stores | A stale revision throws an error whose code is NOD_SESSION_REVISION_CONFLICT or NOD_OAUTH_SESSION_REVISION_CONFLICT. |