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
modelModelrequiredThe 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.
instructionsstringPinned system text (the pinned prompt prefix).
nameNamedefault agentUnique 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.
sandboxSandboxThe 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.
workspaceWorkspaceFiles 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.
memoryMemoryProviderSaved 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 askWrite 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.
knowledgeKnowledgeProviderRetrieval 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 2Failed 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 4Most members running at once; a start past it is refused concurrency_cap. Default 4.
teamLimits.mailbox / team_limits.mailboxPosIntdefault 100Most 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).
budgetBudgetThread 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_boundHow 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 falsetrue pins web_fetch, which fetches a URL from the host. false (the default) leaves it out.
web.searchSearchBackendThe 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.credentialSecretrequiredThe 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.comA repo owner/name lives at <forge_url>/<owner>/<name>.git.
git.apiUrl / git.api_urlstringdefault https://api.github.comThe forge REST API open_pull_request uses.
computerbooleandefault falsePins 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[]requiredThe 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.