The SDK entry point¶
ContextFile¶
class ContextFile(path: Path, content: str)
tau_agent_core.sdk.ContextFile
One discovered context file: its resolved path and its text.
Constructor parameters
path: Path— (no description)content: str— (no description)
ContextFileError¶
tau_agent_core.sdk.ContextFileError
A context file was found but could not be used.
Fail-Early: discovery deliberately looked for this file and located it, so
a read failure (permissions, a truncated mount, non-UTF-8 bytes) is a real
problem and not a reason to quietly prompt the model with less context than
the user wrote. pi warns to stderr and continues here
(resource-loader.ts:82); τ raises, and names the path plus the escape
hatch, because a prompt silently missing its project instructions looks
exactly like a model that ignored them.
Settings¶
class Settings(default_model: str = 'gpt-4o', thinking_level: str = 'off', compaction_enabled: bool = True, context_margin: int = 2000, extension_dirs: list[str] = (lambda: [str(Path.home() / '.tau' / 'extensions')])(), api_keys: dict[str, str] = dict(), custom_system_prompt: str | None = None, tool_execution_mode: str = 'parallel', max_retries: int = 3, temperature: float | None = None, max_tokens: int | None = None, reasoning_level: str = 'off')
tau_agent_core.settings.Settings
τ settings (from ~/.tau/settings.json).
Constructor parameters
default_model: str = 'gpt-4o'— Default model identifier for LLM callsthinking_level: str = 'off'— Thinking mode ("off", "low", "high")compaction_enabled: bool = True— Whether automatic session compaction is enabledcontext_margin: int = 2000— Token margin before hitting context limitextension_dirs: list[str] = (lambda: [str(Path.home() / '.tau' / 'extensions')])()— Directories to search for extension modulesapi_keys: dict[str, str] = dict()— Mapping of provider name to API keycustom_system_prompt: str | None = None— Optional custom system prompt overridetool_execution_mode: str = 'parallel'— Default tool execution mode ("parallel", "sequential")max_retries: int = 3— Maximum number of retries for failed LLM callstemperature: float | None = None— Default sampling temperature (None = let the endpoint choose)max_tokens: int | None = None— Maximum output tokens (None = provider default)reasoning_level: str = 'off'— Reasoning mode ("off", "low", "high")
load¶
load(cwd: str | None = None) -> 'Settings'
tau_agent_core.settings.Settings.load
Load settings from ~/.tau/settings.json and project-local override.
Settings are loaded in order of precedence (later overrides earlier): 1. Default values (built-in) 2. Global settings from ~/.tau/settings.json 3. Project-local settings from {cwd}/.tau/settings.json
Parameters
cwd: str | None = None— Working directory for project-local settings lookup.
Returns
A fully resolved Settings instance.
SystemPromptFieldError¶
tau_agent_core.sdk.SystemPromptFieldError
A {{field}} in a system prompt names something τ cannot supply.
Raised rather than rendered literally. A misspelled {{tols}} left in the
text would reach the model as the four characters it is, say nothing, and
look exactly like a prompt that worked — the silent failure the whole
"loud failure over a quiet guess" rule exists to prevent.
append_system_prompt¶
append_system_prompt(base: str, sections: list[str] | None) -> str
tau_agent_core.sdk.append_system_prompt
Append --append-system-prompt sections to a base system prompt.
Sections augment rather than replace the base (pi appendSystemPrompt,
system-prompt.ts:48), joined by blank lines. An empty/absent list returns the
base unchanged; an empty base with sections yields just the sections.
Lives here, beside :func:_build_system_prompt, because it composes the
base text slot and nothing else — the appended sections land ahead of the
project context and the tool list, not after them. It was previously a
private helper in tau_coding_agent.headless that three frontends reached
across for; headless re-exports this name so those imports keep working.
Parameters
base: str— (no description)sections: list[str] | None— (no description)
create_agent_session¶
create_agent_session(model: str | Model = 'gpt-4o', provider: str = 'openai', base_url: str | None = None, api_key: str | None = None, tools: list[str] | None = None, session_log: SessionLog | None = None, extensions: list[Callable] | None = None, system_prompt: str | None = None, no_context_files: bool = False, thinking_level: str = 'off', cwd: str | None = None, tool_execution_mode: Literal['sequential', 'parallel'] = 'parallel', compaction_policy: CompactionPolicy | None = None, bus_available: bool = False, no_tools: Literal['all', 'builtin'] | None = None, max_turns: int | None = None, tool_options: dict[str, dict[str, Any]] | None = None) -> AgentSession
tau_agent_core.sdk.create_agent_session
Create an AgentSession with all defaults.
This is the main SDK entry point. It handles: - Model resolution (string → Model object) - Tool discovery (string names → AgentTool objects) - Extension registration (inline factory callables invoked at construction) - System prompt building (base prompt + discovered context files + tools)
It does NOT load ~/.tau/settings.json. Nothing in any package reads
Settings outside tests; every value this factory uses is either passed
in by the caller or defaulted here.
There is deliberately no settings parameter (B5). One was accepted here
and never read — a caller passing settings={...} got a session that ignored
it and looked fine, which is the same "declared and not consulted" failure H1
fixed for execution_mode, and pi's agent package has no settings concept
to port from. It is removed rather than made to raise, because a parameter whose
only behaviour is rejection still advertises a capability that does not exist;
an unexpected-keyword TypeError says the true thing. Deciding what such a
dict would mean — how it composes with the ~/.tau/config.json the CLI and
TUI actually read — is the config-precedence design question (B2) and is not
settled here.
Parameters
model: str | Model = 'gpt-4o'— Model identifier string or Model object (default: "gpt-4o").provider: str = 'openai'— Provider name for model resolution (default: "openai").base_url: str | None = None— Optional custom API base URL.api_key: str | None = None— Optional API key.tools: list[str] | None = None— List of BUILT-IN tool name strings (e.g., ["read", "bash"]), resolved by :func:_resolve_tools, which raisesValueErroron any name it does not recognize (NODE-ADDRESSABLE-AGENTS.md §5/W5). This parameter does not accept :class:~tau_agent_core.tools.base.AgentToolinstances and gains no fallback that does — a custom tool must go through the :class:~tau_agent_core.agent_session.AgentSessionconstructor'stools=directly, or be registered by an extension (ExtensionAPItool registration, loaded viaextensions=/load_extensions). Passing a non-empty list together withno_toolsraises — see that parameter.session_log: SessionLog | None = None— Optional SessionLog to persist through (the coding-agent's file Session on the live path). Defaults to an in-memory log.extensions: list[Callable] | None = None— List of extension factory callables.system_prompt: str | None = None— Optional custom system prompt. Replaces the base prompt ONLY — project context files still load and are appended after it. Passno_context_files=Trueto suppress those as well.no_context_files: bool = False— Skip context-file discovery entirely (--no-context-files/-nc). The prompt is then the base (or custom) text plus the tool list, and nothing read from disk.thinking_level: str = 'off'— Thinking level ("off", "minimal", "low", "medium", "high", "xhigh"). A non-"off" level marks the model reasoning-capable and is forwarded to the provider asreasoning_effort.cwd: str | None = None— Current working directory.tool_execution_mode: Literal['sequential', 'parallel'] = 'parallel'— Batch-level tool execution policy ("sequential" or "parallel", default "parallel") forwarded to AgentSession, which threads it into every AgentLoopConfig the session builds. A tool declaring a per-tool "sequential" execution_mode still forces its batch to run sequentially regardless of this setting.compaction_policy: CompactionPolicy | None = None— Optional declared :class:~tau_agent_core.compaction_policy.CompactionPolicy(H5, SIM_SPEC_v2 §16.8).None— the default — is the shipped behaviour: auto-compaction on, summarising through this session's own model. A MEASUREMENT run declares one, because a compaction is a model call at the tail of a prompt and an undeclared one lands inside §5.2's headline latency number and on the far side of §11.1's partition. Declaring a policy never makes compaction quieter — it adds construction-time and runtime checks that raise.bus_available: bool = False— Whether this session has a bus transport a loaded extension may declare against (H8, SIM_SPEC_v2 §16.10).False— the default — refuses to load any file extension that declaresTOUCHES_BUS = True(see :func:_load_one_extension); no NATS wiring exists in this package yet (tau-007), so there is nothing to back that capability with until a caller sets thisTrue.no_tools: Literal['all', 'builtin'] | None = None— Tool-suppression policy, the SDK equivalent of the CLI's--no-tools/-ntand--no-builtin-tools/-nbt. One resolved value rather than two booleans, for the reasonheadless.resolve_no_toolsgives: flags that only have meaning against each other become the same flag once each consumer re-derives the interaction. -"all"— the model is offered nothing: no built-ins, and no extension-registered tools either. Extensions still LOAD; hooks, event subscriptions, slash commands and message injections are untouched. Only callable tools are withheld. -"builtin"— built-ins only. Extension-registered tools survive and are offered. -None— the default — no suppression. Either value resolves zero built-ins here, which is what gives"builtin"behaviour inside this package rather than leaving it a display label that only the coding-agent's argv boundary honoured. An unrecognised value raises inAgentSession.max_turns: int | None = None— Stop the loop after this many LLM calls.None— the default — is no ceiling, which is alsoAgentLoopConfig's default and pi's behaviour. This factory did not take the parameter at all until now, and neither did any CLI flag or config key, so the ceiling that did exist (a hardcoded 50) was unreachable from every caller τ ships. What bounds a runaway run without one is the abort signal: an extension's budget guard, or Escape in the TUI.tool_options: dict[str, dict[str, Any]] | None = None— Keyword arguments for individual built-in tools, keyed by tool name — e.g.{"read": {"max_image_dimension": 2000}}. Ignored for tools not intools, and irrelevant whenno_toolsis set, since then no built-in is constructed at all. See :func:_resolve_tools.
Returns
Fully configured AgentSession instance.
Raises
ValueError— ifno_toolsis given together with a non-emptytools(a contradictory request, refused rather than silently resolved), or iftoolsnames a tool that is not a built-in (:func:_resolve_tools).
Example
session = create_agent_session(model="gpt-4o", tools=["read", "bash"]) messages = await session.prompt("Hello, world!")
load_project_context_files¶
load_project_context_files(cwd: str | Path | None = None, agent_dir: str | Path | None = None) -> list[ContextFile]
tau_agent_core.sdk.load_project_context_files
Discover the context files that apply to cwd, weakest first.
A port of pi's loadProjectContextFiles (resource-loader.ts:119 at
5cd93f688), plus τ's own .tau/SYSTEM.md. The returned order is the
order they belong in a prompt — general first, specific last — because
the nearest file is the one that should have the last word:
~/.tau's context file (:data:_AGENT_DIR), the user's global one.- Every ancestor of
cwdthat has one, root-most first, ending withcwd's own — so a repo'sAGENTS.mdis read after$HOME's and a package's after its monorepo's. cwd's.tau/SYSTEM.md, τ's own slot (see :data:TAU_SYSTEM_FILE).
Deduplicated by resolved path, so running τ from ~ does not load the
same file as both the agent-dir entry and an ancestor.
On walking all the way to /: this is pi's behaviour and it is kept
deliberately, because "put your standing instructions in ~/AGENTS.md" is
a real workflow. The cost is a handful of stat calls per ancestor, once
per session — not per turn. The surprise is answered by
:func:_build_system_prompt, which labels every block with the absolute
path it came from, so a prompt can never carry instructions whose origin it
does not name; and by --no-context-files for a run that wants none.
Parameters
cwd: str | Path | None = None— (no description)agent_dir: str | Path | None = None— (no description)
resolve_model¶
resolve_model(model: str, provider: str = 'openai', base_url: str | None = None) -> Model
tau_agent_core.sdk.resolve_model
Resolve a model string to a Model object.
Parameters
model: str— Model identifier (e.g., "gpt-4o").provider: str = 'openai'— Provider name (default: "openai").base_url: str | None = None— Optional custom API base URL.
Returns
Model object with provider configuration.
Raises
KeyError— If model string is not found in defaults.