Thread

A thread positioned at one branch.

Every method reads or appends through the store; none needs the agent in memory.

A handle returned by the library.

Properties

idThreadIdrequired

The thread's id: pass it to openThread to get a handle again later.

branchBranchIdrequired

The branch this handle reads and appends to.

storeStorerequired

The SQLite log and artifact store (sqlite()).

Methods

timeline

Every step with the exact request the model saw; fork points marked.

timeline(): Promise<Result<Timeline>>

Returns an error value with one of these codes: log_corrupt, unsupported_format, unsupported_critical_event.

branches

Every visible branch of this thread, with its parent branch and fork seq. A branch still being forked, or whose fork failed, is not listed.

branches(): Promise<readonly BranchInfo[]>

forkPoints / fork_points

Eligible snapshot events only. TypeScript returns the list itself; Python returns Ok or Err.

forkPoints(): Promise<readonly ForkPoint[]>

fork

A new branch restored into an isolated sandbox. Continue it with agent.run(input, {thread: child}).

fork(point: EventId | ForkPoint, options?: {
  mode?: "live" | "stub";
  knowledge?: "pinned" | "current";
}): Promise<Result<Thread>>
pointEventId | ForkPointrequired

Where to fork: the event_id of an eligible snapshot, or a ForkPoint from forkPoints(). Any other event is no_snapshot_boundary.

mode"live" | "stub"default live

stub runs the new branch without live side effects: every mediated call is answered from what the original branch recorded after the fork point. Stubbing works in Python only today; in TypeScript a run on a stub fork is live. A stub-mode run on a live model that declares hosted tools is refused with ConfigError hosted_tool_unsupported, because hosted calls can't be stubbed.

knowledge"pinned" | "current"default pinned

"pinned": the new branch searches knowledge as it was at the snapshot. "current": it searches the live knowledge base.

Returns an error value with one of these codes: sandbox_required, no_snapshot_boundary, snapshot_expired, snapshot_missing, snapshot_restore_failed, snapshot_manifest_mismatch, resource_unknown, egress_policy_unsupported.

saveCase / save_case

Writes a conformance case directory in exactly the spec/conformance layout, so the same runners replay it: case.json (with expect and input.text), log.threads-ts.jsonl and log.threads-py.jsonl with matching expected.<impl>.json, artifacts/, model.json and stubs.json. The kind is stub: external effects replay from stubs.json and unmatched invocations fail closed. The case is built from typed values, so it is valid against case.schema.json by construction; core carries no JSON Schema evaluator, and both languages' tests validate every saved case against case.schema.json.

saveCase(name: string, options: {
  expect: CaseExpectation;
  externalEffects: "stub";
  at?: EventId;
  dir?: string;
}): Promise<Result<SavedCase>>
namestringrequired

The case's directory name under dir: lowercase letters, digits and hyphens, starting with a letter or digit. Anything else is invalid_request.

expectCaseExpectationrequired

What a replay of the case must show; a case needs at least one must assertion, else invalid_request. See CaseExpectation.

externalEffects / external_effects"stub"required

The effects policy is required; stub is the only one.

atEventId

The snapshot event the case restores. Omitted: the branch's latest fork point; a branch without one is no_snapshot_boundary.

dirstringdefault cases

Directory in the app repo; the case is <dir>/<name>/.

Returns an error value with one of these codes: invalid_request, no_snapshot_boundary, case_missing_dependency, egress_policy_unsupported.

pendingApprovals / pending_approvals

Open approval challenges on this branch, each with the tool call it guards and the suggested_rules an approver may keep. Python returns Ok or Err.

pendingApprovals(): Promise<readonly PendingApproval[]>

approve

Grants an open approval challenge: appends approval_granted and resumes the branch if it was parked on it. Each challenge takes one answer: a second is approval_duplicate, and one after it expires is approval_expired.

approve(challengeId: Uuid, principal: Principal, options?: {
  rememberRule?: PermissionRule;
}): Promise<Result<Appended>>
challengeId / challenge_idUuidrequired

The challenge_id of an open approval, from pendingApprovals() or the approval_requested event.

principalPrincipalrequired

The approver answering, recorded as the event's actor. It must be one of the agent's approvers, else forbidden.

rememberRule / remember_rulePermissionRule

One of the challenge's suggested_rules, also recorded as an allow rule so matching calls later in the thread need no approval. Any other rule is invalid_request. Omitted: the approval covers this one call.

Returns an error value with one of these codes: forbidden, not_found, approval_mismatch, approval_expired, approval_duplicate, branch_busy.

deny

Refuses an open approval challenge: appends approval_denied, so the tool call does not run, and resumes the branch if it was parked on it. Each challenge takes one answer.

deny(challengeId: Uuid, principal: Principal, options?: {
  reason?: string;
}): Promise<Result<Appended>>
challengeId / challenge_idUuidrequired

The challenge_id of an open approval, from pendingApprovals() or the approval_requested event.

principalPrincipalrequired

The approver answering, recorded as the event's actor. It must be one of the agent's approvers, else forbidden.

reasonstring

Free text recorded on approval_denied, for example why the call was refused. Omitted: no reason is recorded.

Returns an error value with one of these codes: forbidden, not_found, approval_mismatch, approval_expired, approval_duplicate, branch_busy.

resolveParked / resolve_parked

A person settles a side effect whose outcome is unknown and that parked the branch, then the branch resumes. Use it when the effect can't be checked automatically.

resolveParked(effectKey: string, resolution: "assume_done" | "assume_not_done", principal: Principal): Promise<Result<Appended>>
effectKey / effect_keystringrequired

The parked effect's key, <branch_id>:<call_id>, as listed in the parked result's pending. A key that isn't parked is not_parked.

resolution"assume_done" | "assume_not_done"required

"assume_done": record that the effect happened, so it never runs again. "assume_not_done": let it run again, accepting the risk that it happens twice.

principalPrincipalrequired

The person settling it, recorded as the event's actor. It must be one of the agent's approvers, else forbidden.

Returns an error value with one of these codes: forbidden, not_parked, branch_busy.

cancel

Durable cancel_requested; unsettled effects park. Tree-wide: every descendant subagent without agent_finished gets cancel_requested{scope: tree} too, recursively, and the parent records each child's agent_finished{cancelled} before its own cancelled. A control appends through the run's own writer when this process runs the branch; otherwise it takes the branch lease for the one append. A lease another process holds is branch_busy and nothing is appended.

cancel(principal: Principal): Promise<Result<Appended>>
principalPrincipalrequired

Who takes this action; recorded as the event's actor and authorized by the host.

Returns an error value with one of these codes: forbidden, branch_busy.

setModel / set_model

Changes the model for this thread's next requests, recorded as settings_changed{reason: user}. before_model_switch doesn't run for it: that hook gates only automatic switches (a fallback and its revert). forbidden: the principal can't act on this thread, such as another tenant's.

setModel(settings: SettingsChange, principal: Principal): Promise<Result<Appended>>
settingsSettingsChangerequired

The model to switch to, with optional model_params and settings. The host resolves it to the model of that name in the agent's model or fallback list.

principalPrincipalrequired

Who takes this action; recorded as the event's actor and authorized by the host.

Returns an error value with one of these codes: forbidden, invalid_transition, branch_busy.

setMode / set_mode

Changes the branch's permission mode, recorded as mode_changed and used by later permission decisions. Switching to bypass is invalid_transition unless the agent's permissions set allow_bypass.

setMode(mode: PermissionMode, principal: Principal): Promise<Result<Appended>>
modePermissionModerequired

The new mode: plan, dont_ask, default, accept_edits or bypass.

principalPrincipalrequired

Who takes this action; recorded as the event's actor and authorized by the host.

Returns an error value with one of these codes: forbidden, invalid_transition, branch_busy.

usage

Token totals over this branch (a fork counts its parent's prefix). A response whose counts the provider didn't report, or that would take a total past 2^53-1, is counted in unknown_responses: never as zero and never out of range.

usage(): Promise<Result<UsageTotals>>

Returns an error value with one of these codes: log_corrupt, unsupported_format, unsupported_critical_event.

cost

What this thread spent, in nano-units of its pinned currency (USD for agent()), with a conservative upper bound; null when its pinned policy has no currency or no model list (a pinned currency with an unpriced model gives an incomplete cost instead). With tree: true it also counts every subagent at any depth, each from its own log, in the root's currency (else the first priced subagent's). complete and bounded become false when some spend can't be counted: a thread priced in another currency, or an unpriced thread that called a model. A subagent that never started counts nothing. Errors: a subagent's read error; log_corrupt when a finished subagent's log is missing, a subagent doesn't name the spawn that started it, or the tree loops; cost_overflow past 2^53-1 nanos. Messages name the path of child thread ids.

cost(options?: {
  tree?: boolean;
}): Promise<Result<Cost | null>>
treebooleandefault false

Also count every descendant subagent, each from its own log.

Returns an error value with one of these codes: log_corrupt, unsupported_format, unsupported_critical_event, cost_overflow.

cacheBreaks / cache_breaks

Turns whose prompt-cache reads dropped sharply, each with its likely cause. Judged by the pinned policy.context.cache_ttl_ms, else the default (300000 ms), so always a list.

cacheBreaks(): Promise<Result<readonly CacheBreak[]>>

Returns an error value with one of these codes: log_corrupt, unsupported_format, unsupported_critical_event.

todos

The agent's current todo list: the latest one it wrote on this branch, or empty. TypeScript returns the list itself; Python returns Ok or Err.

todos(): Promise<readonly Todo[]>

children

Every subagent this thread started, in start order, with its final status or running. TypeScript returns the list itself; Python returns Ok or Err.

children(): Promise<readonly Child[]>
Edit on GitHub

On this page