# AgentRuntime

The class that runs the nanocode WebAssembly module for one session scope.

One `AgentRuntime` owns one module instance. Reader and log commands such as `submit`, `turn`, and `page` run through the SDK's `SessionLog` without an open session. `open` opens the SDK session on the same instance, once. After that, `drive` runs root turns and `dispose` shuts the session down.

```ts title="host.ts"
import {
  AgentRuntime,
  sessionUuid,
  type RuntimeOptions,
  type SqlHost,
} from 'nanocode-wasm'

declare const module: WebAssembly.Module
declare const sql: SqlHost
declare const options: RuntimeOptions

// 1. Instantiate, then open the session
const runtime = await AgentRuntime.instantiate(module, sql, {
  published: () => {},
})
await runtime.open(
  {
    endpoint: 'https://api.anthropic.com/v1/messages',
    scope: 'owner',
    session_id: sessionUuid(),
    subscription: false,
  },
  options,
)

// 2. Queue a turn and drive the queue until no turn is pending
await runtime.submit('owner', {
  metadata: {},
  prompt: 'Summarize the log.',
  request_id: 'turn-1',
})
for (
  let next = (await runtime.pending('owner'))[0];
  next;
  next = (await runtime.pending('owner'))[0]
) {
  const driven = await runtime.drive(next.report.request_id)
  // A retry result disposed the runtime, so reopen from the checkpoint
  // in a new instance
  if (driven.retry) break
  if (driven.waiting)
    await new Promise((resolve) => setTimeout(resolve, 500))
}

// 3. Shut the session down
await runtime.dispose()
```

## Lifecycle

```mermaid
flowchart LR
  instantiate[AgentRuntime.instantiate] --> open[open]
  instantiate --> readers[submit, turn, page]
  open --> submit[submit]
  submit --> drive[drive]
  drive --> turn[turn and pending]
  turn --> drive
  drive --> dispose[dispose]
```

A runtime cannot be opened twice. If `drive` returns `retry`, the runtime disposes itself, and the host instantiates a new runtime that reopens the session from its checkpoint.

## Methods

| Method                                                   | Purpose                                                               |
| -------------------------------------------------------- | --------------------------------------------------------------------- |
| [`AgentRuntime.instantiate`](/docs/api/wasm/instantiate) | Instantiates the module for one session scope.                        |
| [`open`](/docs/api/wasm/open)                            | Opens the SDK session with a configuration and host options.          |
| [`submit`](/docs/api/wasm/submit)                        | Queues a turn in the session log.                                     |
| [`drive`](/docs/api/wasm/drive)                          | Runs one root turn through the SDK's `TurnInbox`.                     |
| [`turn`](/docs/api/wasm/turn)                            | Reads one turn.                                                       |
| [`pending`](/docs/api/wasm/turn#pending)                 | Reads the queued and running turns.                                   |
| [`page`](/docs/api/wasm/page)                            | Reads logged events after a cursor.                                   |
| [`cancel`](/docs/api/wasm/cancel)                        | Cancels a queued or running turn.                                     |
| [`dispose`](/docs/api/wasm/dispose)                      | Shuts the SDK session down.                                           |
| [`admit`](#admit)                                        | Checks a request ID and a provider change before submission.          |
| [`catalog`](#catalog)                                    | Reads the name that a hand's catalog frame advertises.                |
| [`identity`](#identity)                                  | Reads the name and runtime ID that a hand's catalog frame advertises. |
| [`interrupted`](#interrupted)                            | Records a note on a running turn.                                     |
| [`retry`](#retry)                                        | Records why a turn waits on its host.                                 |
| [`finish`](#finish)                                      | Ends a turn as failed or cancelled.                                   |
| [`import`](#import)                                      | Imports turns into the session log.                                   |
| [`attach`](#attach)                                      | Registers a hand connection.                                          |
| [`detach`](#detach)                                      | Removes a hand connection.                                            |
| [`settle`](#settle)                                      | Reports whether the session is quiescent.                             |
| [`preconnect`](#preconnect)                              | Opens a Codex socket before the first turn.                           |
| [`changed`](#changed)                                    | Resolves after the next command reply or logged batch.                |
| [`releaseConnections`](#releaseconnections)              | Closes open model sockets.                                            |

## Reader Methods

### admit

```ts
// Rejects a reserved request ID or a change of provider
admit(
  request_id: string,
  recorded: string | null,
  requested: string,
): Promise<'reserved_request_id' | 'provider_changed' | undefined>
```

Runs the SDK's prompt admission check. `recorded` is the provider (`'claude'` or `'codex'`) that the session already uses, or `null` for a new session, and `requested` is the provider of the new prompt. The result is `undefined` when the prompt is admitted.

```ts
// undefined, or the rejection reason
const rejected = await runtime.admit('turn-2', 'claude', 'codex')
```

### catalog

```ts
// The name in a hand's catalog frame, or null
catalog(frame: string, max_frame_bytes: number): Promise<string | null>
```

Decodes a hand's first frame as the SDK's `RemoteCatalog` and returns its `name`. The promise rejects if the frame does not decode within `max_frame_bytes`.

```ts
declare const frame: string
// Reads the hand name before the host attaches it
const name = await runtime.catalog(frame, 1_048_576)
```

### identity

```ts
// The name and runtime ID in a hand's catalog frame
identity(
  frame: string,
  max_frame_bytes: number,
): Promise<{ name: string | null; runtime_id: string | null }>
```

Decodes the same catalog frame as `catalog`, and also returns the runtime ID that the hand advertises.

```ts
declare const frame: string
// Both fields can be null
const { name, runtime_id } = await runtime.identity(frame, 1_048_576)
```

## Log Methods

### interrupted

```ts
// Sets a note on a running turn
interrupted(
  scope: string,
  request_id: string,
  note: string,
): Promise<void>
```

Records `note` on a turn whose status is `running`. The turn report shows the note as its `error`. The call does nothing for turns in another status.

```ts
// Records why the host stopped the turn
await runtime.interrupted(
  'owner',
  'turn-1',
  'the host restarted during the turn',
)
```

### retry

```ts
// Records the retry reason and the next retry time
retry(
  scope: string,
  request_id: string,
  error: string,
  kind: string,
  retry_at: number,
): Promise<TurnView | null>
```

Records why a queued or running turn waits on its host, with an error, a kind, and the next retry time in milliseconds since the epoch. Each call increments `attempts`. The SDK logs a `retrying` event when the error or kind changes. The result is the updated turn, or `null` if no turn has that ID.

```ts
// Retries in 30 seconds
const turn = await runtime.retry(
  'owner',
  'turn-1',
  'model rate limited',
  'rate_limit',
  Date.now() + 30_000,
)
```

### finish

```ts
// Ends a queued or running turn
finish(
  scope: string,
  request_id: string,
  status: 'failed' | 'cancelled',
  fields: Record<string, unknown>,
): Promise<void>
```

Ends a turn that the host gives up on. The SDK logs a `result` event with `status` and `fields`, and the turn report includes the fields. If the SQL host sets `value_limit` and the result does not fit, the turn fails with an error about the size.

```ts
// Fails the turn with an error the report shows
await runtime.finish('owner', 'turn-1', 'failed', {
  error: 'the hand disconnected',
})
```

### import

```ts
// Returns the number of turns read from the input
import(scope: string, turns: unknown[]): Promise<number>
```

Imports turns from an earlier store into the session log. Each turn has `request_id`, `status`, `prompt`, and `metadata`, and can have `hand`, `cancel_requested`, `note`, `first_seq`, `last_seq`, and `result`. Turns that already exist stay unchanged, and statements batch within `value_limit`.

```ts
// Imports one completed turn
await runtime.import('owner', [
  {
    metadata: {},
    prompt: 'Hello.',
    request_id: 'legacy-1',
    result: { ok: {} },
    status: 'completed',
  },
])
```

## Session Methods

### attach

```ts
// Registers a hand with the Code Mode executors
attach(hand: string, connection: string): Promise<void>
```

Registers the hand named `hand` over `connection`. The SDK requests the connection's catalog through `RuntimeOptions.remote`. The call does nothing if the runtime is closed or the connection is already attached. It requires an open session.

```ts
// The connection ID routes remote frames to this hand
await runtime.attach('laptop', 'connection-1')
```

### detach

```ts
// Removes a hand connection
detach(connection: string): Promise<void>
```

Removes the hand registered over `connection`. The call does nothing if the runtime is closed or the connection is not attached.

```ts
// Runs when the hand's socket closes
await runtime.detach('connection-1')
```

### settle

```ts
// true when no turn runs and the event log is drained
settle(): Promise<boolean>
```

Waits for logged events to drain, then reports whether the session is quiescent. A host uses it before it hibernates or unloads the runtime.

```ts
// Unloads only a quiescent session
if (await runtime.settle()) await runtime.dispose()
```

### preconnect

```ts
// Opens the model socket early
preconnect(): Promise<void>
```

Asks the agent to open its Codex Responses WebSocket before the first turn. The SDK calls `RuntimeOptions.socket.open` to do it.

```ts
// Opens the socket while the prompt is composed
await runtime.preconnect()
```

### changed

```ts
// Resolves on the next change
changed(signal?: AbortSignal): Promise<void>
```

Resolves after the next command reply, logged batch, or wake from the module, or when `signal` aborts. It resolves at once if the runtime stopped or the signal already aborted.

```ts
// Waits up to one second for progress
await runtime.changed(AbortSignal.timeout(1_000))
```

### releaseConnections

```ts
// Closes every open model socket
releaseConnections(): void
```

Closes the open Codex sockets with the reason "released while the session is idle". The SDK opens a new socket for the next model request.

```ts
// Frees sockets before the host hibernates
runtime.releaseConnections()
```

## Properties

### closed

* **Type:** `boolean`

`true` after the runtime stopped or `dispose` started.

```ts
// Instantiates a new runtime after a retry result
if (runtime.closed) console.log('reopen the session')
```

### connected

* **Type:** `boolean`

`true` while the runtime holds an open model socket.

```ts
// Releases the socket only when one is open
if (runtime.connected) runtime.releaseConnections()
```

### hands, remote, subscription

* **Type:** `boolean`

The values of `config.hands`, `config.remote`, and `config.subscription` from `open`. All three are `false` before `open`.

```ts
// Accepts hand connections only for sessions with hands
if (runtime.hands) await runtime.attach('laptop', 'connection-1')
```

### memoryBytes

* **Type:** `number`

The size of the module's linear memory in bytes.

```ts
// Logs the WASM memory size
console.log(runtime.memoryBytes)
```

### metrics

* **Type:** `Readonly<RuntimeMetrics>`

Counters, WASM memory, timing spans, and the last `RECENT_MODEL_LIMIT` (64) model dispatches. [Types](/docs/api/wasm/types#runtimemetrics) documents the shape.

```ts
// Counts model requests since instantiation
console.log(runtime.metrics.model_requests)
```

### opened

* **Type:** `boolean`

`true` after `open` started, even if it failed.

```ts
// Opens only once
if (!runtime.opened) console.log('call open')
```

### reusable

* **Type:** `boolean`

`true` while the runtime is not closed and `RuntimeOptions.remote.failed()` does not return `true`.

```ts
// Keeps the runtime cached only while it is reusable
if (!runtime.reusable) await runtime.dispose()
```
