Threads AI

agent

Describe an agent.

Pure: no I/O, no env reads, no sockets. Checked by Agent.check() or on the first run.

Import from @threads/core (TypeScript) or threads (Python).

function agent(options: {
  model: Model;
  instructions?: string;
  name?: Name;
  tools?: readonly (Tool | McpServer)[];
  extensions?: readonly Extension[];
  sandbox?: Sandbox;
  workspace?: Workspace;
  memory?: MemoryProvider;
  memoryWrite?: "deny" | "ask" | "allow_principal" | "allow";
  knowledge?: KnowledgeProvider;
  skills?: readonly Skill[];
  output?: z.ZodType<Output>;
  outputRetries?: number;
  subagents?: readonly (Agent<unknown, unknown>)[];
  handoffs?: readonly (Agent<unknown, unknown>)[];
  team?: readonly (Agent<unknown, unknown> | DynamicAgent<unknown, unknown>)[];
  teamLimits?: { concurrent?: PosInt; mailbox?: PosInt };
  fallback?: readonly Model[];
  permissions?: Partial<Policy.permissions>;
  approvers?: readonly Principal[];
  budget?: Budget;
  onUnknownUsage?: Policy.on_unknown_usage;
  retry?: Partial<Policy.retry>;
  context?: Partial<Policy.context>;
  egress?: readonly string[] | "unenforced";
  web?: { fetch?: boolean; search?: SearchBackend };
  git?: { credential: Secret; forgeUrl?: string; apiUrl?: string };
  computer?: boolean;
  lsp?: { languages: readonly string[] };
  outputStyles?: Record<string, string>;
}): Agent<Deps, Output>

Parameters

modelModelrequired

The model every turn is sent to: a model adapter such as anthropic() or openai(), or scriptedModel() in tests. Fallback models are tried after it. A model (primary, fallback or any subagent's or handoff target's) that declares info.lookup other than none without a lookup method fails check() and the first run with capability_missing. See Model.

instructionsstring

Pinned system text (the pinned prompt prefix).

nameNamedefault agent

Unique among the agents a host, subagent list or handoff list names.

toolsreadonly (Tool | McpServer)[]default []

App tools and MCP servers. Built-in tools come from the sandbox and the options below; todo_write is always pinned, and ask_user only for runs a host starts from a channel conversation or an authenticated HTTP API call (not agent.run(), schedule runs or subagents), so a thread started one way continues only the same way.

extensionsreadonly Extension[]default []

Instructions and hooks run in this order.

sandboxSandbox

The sandbox the sandbox tools run in: bash and the file tools (read, write, edit, ls, glob, grep). The git, lsp and computer options also need one. Omitted: no sandbox tools. See Sandbox.

workspaceWorkspace

Files every sandbox this thread creates starts with in /workspace, resolved on the host into one tree and pinned (policy.workspace, covered by config_hash) when the thread starts. Continuing a thread re-resolves them; any change is ConfigError invalid_config. Needs a sandbox whose sessions have exportTree and importTree, or it is capability_missing. Omitted: /workspace starts empty. See Workspace.

memoryMemoryProvider

Saved cross-run memory: localMemory() or a memory adapter. It pins save_memory, search_memory and forget_memory. Omitted: the agent has no memory tools. See MemoryProvider.

memoryWrite / memory_write"deny" | "ask" | "allow_principal" | "allow"default ask

Write authority for save_memory. A write it turns into an ask records a reason naming memory_write, shown as PendingApproval.reason. The reason uses wire names: memory_write is memoryWrite in TypeScript.

knowledgeKnowledgeProvider

Retrieval over sources the host admits: localKnowledge(paths) or a knowledge adapter. It pins search_knowledge. Omitted: the agent has no knowledge tool. See KnowledgeProvider.

skillsreadonly Skill[]default []

Pinned in config_hash (name, description, sha256 of body). The system prompt lists them after the extension instructions as "Skills you can load with load_skill:" then one "- name: description" line each, in the given order; load_skill (pinned only when this is non-empty) appends the body as injected{source: skill, trust: trusted_instruction, origin: {id: name, version: sha256 of body}}. Unique names; a description is one non-empty line.

outputz.ZodType<Output>

Structured final output: a schema the final answer must parse with. Omitted: completed.output is the final text.

outputRetries / output_retriesnumberdefault 2

Failed candidates per turn (a rejected final_output or a plain-text end) before turn_completed{output_invalid}. An integer from 0 to 2^53-1; anything else is ConfigError invalid_config.

subagentsreadonly (Agent<unknown, unknown>)[]default []

Agents spawn_agent may start, by name. Team tools come with them. A subagent with output reports the canonical JSON of its accepted output.

handoffsreadonly (Agent<unknown, unknown>)[]default []

Agents this one may hand the conversation to, pinned as policy.handoffs.

teamreadonly (Agent<unknown, unknown> | DynamicAgent<unknown, unknown>)[]

Agents or dynamic agents (dynamicAgent) this one may start as team members, with the start, send, ask, reply, wait, monitor and cancel tools. Passing it, even as [], makes agent() return a TeamAgent whose run() result carries team, the Team handle; [] is a team no model can grow (start is refused unknown_agent). A member whose definition lists handoffs is refused at setup (handoff_in_team), and so is an own tool named start, send, ask, reply, wait, monitor or cancel (duplicate_name). Omitted: no team, a plain Agent. An agent named operator anywhere in the team tree is refused at setup (invalid_config): the name is reserved.

teamLimits / team_limits{ concurrent?: PosInt; mailbox?: PosInt }

The team's limits. Omitted: 4 running members and 100 pending mails per member.

teamLimits.concurrent / team_limits.concurrentPosIntdefault 4

Most members running at once; a start past it is refused concurrency_cap. Default 4.

teamLimits.mailbox / team_limits.mailboxPosIntdefault 100

Most pending mails per member; a send past it is refused mailbox_full. Default 100.

fallbackreadonly Model[]default []

Fallback models in order. With the default fallback_scope turn, the next user_input reverts to the settings before the fallback (settings_changed{reason: revert}), gated by before_model_switch.

permissionsPartial<Policy.permissions>

The permission policy every tool call is decided under: mode, allow, ask and deny rules, protected_paths, allow_bypass and plan_exit_mode. The rule bash() matches every shell command, whatever it contains, in allow, ask and deny. The .threads guard, deny rules, plan mode and ask rules still decide first (protected_paths cover the file tools, not bash). The guard and deny rules match command text, so they are guardrails: with bash(), the sandbox is the boundary. A bare bash rule allows only commands that parse. The fields you give override the defaults. Omitted: mode default, no rules, the built-in protected paths, allow_bypass false and plan_exit_mode default.

approversreadonly Principal[]

Who may answer approval challenges and resolve parked effects for runs this agent roots, its subagents and handoff targets included. Omitted: only the root run's originating principal (the principal of the root thread's latest user_input).

budgetBudget

Thread budget: limits such as max_turns, max_model_requests, max_input_tokens, max_output_tokens, max_cost_nanos and max_wall_ms, checked before every model request. It covers the thread and every descendant. Omitted: no thread budget.

onUnknownUsage / on_unknown_usagePolicy.on_unknown_usagedefault upper_bound

How a budget counts a model attempt the provider reports no usage for. upper_bound counts it at its conservative upper bound; stop lets a limit the model can't bound per attempt pass setup and refuses it at run time instead of failing setup with budget_unenforceable.

retryPartial<Policy.retry>

Model retry settings: max_retries, base_delay_ms, max_delay_ms, max_retry_after_ms, max_total_wait_ms, crash_resends, fallback_after, fallback_scope and heartbeat_ms. The fields you give override the defaults. Omitted: the default policy (8 retries, 1000 ms base delay doubling up to 32000 ms, fallback after 3 failures in a turn).

contextPartial<Policy.context>

Context-window settings: reserve_tokens, clearing old tool results, spilling large results, compaction and what is restored after it, output continuations and tool deferral. The fields you give override the defaults. defer_tools is auto (the default: tools with defer are deferred), never (nothing is) or always (every app, extension and MCP tool without ends_turn is); a subagent, team member or handoff target that doesn't set it inherits its parent's resolved value. defer_threshold is reserved and not read. Unless you set cache_ttl_ms, it comes from the models' declared cache lifetimes (ModelInfo.cache) when they agree; models that disagree, or whose lifetime is unknown, need cache_ttl_ms set (checked at check() or the first run).

egressreadonly string[] | "unenforced"default []

Sandbox egress allowlist of hosts. [] is deny-all. unenforced opts in to a provider that can't enforce it; stub mode then refuses. Host allowlists are not supported yet in either language: use [] (deny-all) or "unenforced".

web{ fetch?: boolean; search?: SearchBackend }

Host-side web tools: web_fetch and web_search run on the host, not in the sandbox. Omitted: no web tools.

web.fetchbooleandefault false

true pins web_fetch, which fetches a URL from the host. false (the default) leaves it out.

web.searchSearchBackend

The search backend web_search uses (an adapter such as exa, brave or tavily). Omitted: no web_search tool.

git{ credential: Secret; forgeUrl?: string; apiUrl?: string }

Enables the git gateway tools git_clone, git_fetch, git_push and open_pull_request. The forge is GitHub; the credential is revealed only on the host and never enters the sandbox. Needs a sandbox. Omitted: no git tools.

git.credentialSecretrequired

The forge token, as a secret(). It is revealed only on the host when a git tool talks to the forge, never in the sandbox.

git.forgeUrl / git.forge_urlstringdefault https://github.com

A repo owner/name lives at <forge_url>/<owner>/<name>.git.

git.apiUrl / git.api_urlstringdefault https://api.github.com

The forge REST API open_pull_request uses.

computerbooleandefault false

Pins computer_screenshot (read_only) and computer (actions, unguarded). Needs a sandbox whose info.desktop is native or image; otherwise a setup error capability_missing.

lsp{ languages: readonly string[] }

Pins lsp for these languages (typescript, python, go, rust), run in the sandbox image's language servers. Needs a sandbox; an unknown language is unknown_preset. A server that isn't installed or ready answers unavailable at run time. Omitted: no lsp tool.

lsp.languagesreadonly string[]required

The languages lsp serves: typescript, python, go or rust. Any other name is a setup error unknown_preset.

outputStyles / output_stylesRecord<string, string>default {}

Named instructions an operator can switch a thread to with Thread.setOutputStyle. Pinned with the agent config; keys and texts non-empty. Omitted: no styles.

Returns

Agent<Deps, Output>

Edit on GitHub

On this page