@markusylisiurunen/tau 0.3.89 → 0.3.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/core/history/history_manager.js +188 -94
  2. package/dist/core/history/history_manager.js.map +1 -1
  3. package/dist/core/history/history_store_worker.js +21 -0
  4. package/dist/core/history/history_store_worker.js.map +1 -0
  5. package/dist/core/history/local_history_store.js +11 -5
  6. package/dist/core/history/local_history_store.js.map +1 -1
  7. package/dist/core/history/threaded_history_store.js +107 -0
  8. package/dist/core/history/threaded_history_store.js.map +1 -0
  9. package/dist/core/static/tau_docs/client-tools.md +1 -1
  10. package/dist/core/static/tau_docs/credentials.md +4 -1
  11. package/dist/core/static/tau_docs/history.md +9 -3
  12. package/dist/core/static/tau_docs/node-sdk.md +1 -1
  13. package/dist/core/static/tau_docs/ownership-and-scope.md +2 -0
  14. package/dist/core/static/tau_docs/security.md +2 -0
  15. package/dist/core/static/tau_docs/session-protocol-methods.md +1 -1
  16. package/dist/core/static/tau_docs/session-protocol.md +2 -0
  17. package/dist/core/static/tau_docs/subagents.md +54 -8
  18. package/dist/core/static/tau_docs/tui.md +2 -0
  19. package/dist/core/subagents/agent_supervisor.js +158 -37
  20. package/dist/core/subagents/agent_supervisor.js.map +1 -1
  21. package/dist/core/subagents/claude_code_models.js +11 -0
  22. package/dist/core/subagents/claude_code_models.js.map +1 -0
  23. package/dist/core/subagents/claude_code_protocol.js +44 -0
  24. package/dist/core/subagents/claude_code_protocol.js.map +1 -0
  25. package/dist/core/subagents/claude_code_runner.js +226 -0
  26. package/dist/core/subagents/claude_code_runner.js.map +1 -0
  27. package/dist/core/subagents/claude_code_runner.mjs +176 -0
  28. package/dist/core/subagents/claude_code_runtime.js +145 -0
  29. package/dist/core/subagents/claude_code_runtime.js.map +1 -0
  30. package/dist/core/subagents/format.js +3 -1
  31. package/dist/core/subagents/format.js.map +1 -1
  32. package/dist/core/subagents/launch_model.js +23 -7
  33. package/dist/core/subagents/launch_model.js.map +1 -1
  34. package/dist/core/subagents/registry.js +10 -1
  35. package/dist/core/subagents/registry.js.map +1 -1
  36. package/dist/core/subagents/types.js.map +1 -1
  37. package/dist/core/tools/spawn_agent.js +31 -26
  38. package/dist/core/tools/spawn_agent.js.map +1 -1
  39. package/dist/core/version.js +1 -1
  40. package/dist/host/client_tool_broker.js +8 -4
  41. package/dist/host/client_tool_broker.js.map +1 -1
  42. package/dist/host/local_session_host.js +3 -2
  43. package/dist/host/local_session_host.js.map +1 -1
  44. package/dist/main.js +4 -3
  45. package/dist/main.js.map +1 -1
  46. package/dist/protocol/session_protocol.d.ts +2 -2
  47. package/dist/protocol/session_protocol.js +2 -2
  48. package/dist/protocol/session_protocol.js.map +1 -1
  49. package/dist/sdk/local_client.js +1 -0
  50. package/dist/sdk/local_client.js.map +1 -1
  51. package/dist/sdk/types.d.ts +7 -0
  52. package/dist/tui/session_chat_app.js +1 -1
  53. package/dist/tui/session_chat_app.js.map +1 -1
  54. package/dist/tui/session_chat_controller.js +12 -1
  55. package/dist/tui/session_chat_controller.js.map +1 -1
  56. package/package.json +7 -5
@@ -0,0 +1,107 @@
1
+ import { Worker } from "node:worker_threads";
2
+ export class ThreadedHistoryStore {
3
+ path;
4
+ worker;
5
+ pending = new Map();
6
+ nextId = 0;
7
+ closed = false;
8
+ constructor(path) {
9
+ this.path = path;
10
+ }
11
+ createSession(...args) {
12
+ return this.request("createSession", args);
13
+ }
14
+ append(...args) {
15
+ return this.request("append", args);
16
+ }
17
+ truncateFromSources(...args) {
18
+ return this.request("truncateFromSources", args);
19
+ }
20
+ search(...args) {
21
+ return this.request("search", args);
22
+ }
23
+ read(...args) {
24
+ return this.request("read", args);
25
+ }
26
+ listPendingOperations(...args) {
27
+ return this.request("listPendingOperations", args);
28
+ }
29
+ listPendingOperationLanes(...args) {
30
+ return this.request("listPendingOperationLanes", args);
31
+ }
32
+ acknowledgeOperations(...args) {
33
+ return this.request("acknowledgeOperations", args);
34
+ }
35
+ quarantineReplicationSession(...args) {
36
+ return this.request("quarantineReplicationSession", args);
37
+ }
38
+ clearReplicationFailure(...args) {
39
+ return this.request("clearReplicationFailure", args);
40
+ }
41
+ listReplicationFailures(...args) {
42
+ return this.request("listReplicationFailures", args);
43
+ }
44
+ close() {
45
+ this.closed = true;
46
+ const worker = this.worker;
47
+ this.fail(new Error("history store is closed"));
48
+ if (worker)
49
+ void worker.terminate();
50
+ }
51
+ request(method, args) {
52
+ if (this.closed)
53
+ return Promise.reject(new Error("history store is closed"));
54
+ if (!this.worker) {
55
+ const worker = new Worker(new URL("./history_store_worker.js", import.meta.url), {
56
+ workerData: { path: this.path },
57
+ execArgv: [],
58
+ });
59
+ this.worker = worker;
60
+ worker.on("message", ({ id, result, error }) => {
61
+ const pending = this.pending.get(id);
62
+ if (!pending)
63
+ return;
64
+ this.pending.delete(id);
65
+ if (error)
66
+ pending.reject(Object.assign(new Error(error.message), { errcode: error.errcode }));
67
+ else
68
+ pending.resolve(result);
69
+ if (this.pending.size === 0)
70
+ worker.unref();
71
+ });
72
+ worker.on("error", (error) => {
73
+ if (this.worker === worker)
74
+ this.fail(error);
75
+ });
76
+ worker.on("exit", (code) => {
77
+ if (this.worker === worker)
78
+ this.fail(new Error(`history worker exited (${code})`));
79
+ });
80
+ }
81
+ const worker = this.worker;
82
+ worker.ref();
83
+ return new Promise((resolve, reject) => {
84
+ const id = ++this.nextId;
85
+ this.pending.set(id, {
86
+ resolve: (value) => resolve(value),
87
+ reject,
88
+ });
89
+ try {
90
+ worker.postMessage({ id, method, args });
91
+ }
92
+ catch (error) {
93
+ this.pending.delete(id);
94
+ if (this.pending.size === 0)
95
+ worker.unref();
96
+ reject(error);
97
+ }
98
+ });
99
+ }
100
+ fail(error) {
101
+ this.worker = undefined;
102
+ for (const pending of this.pending.values())
103
+ pending.reject(error);
104
+ this.pending.clear();
105
+ }
106
+ }
107
+ //# sourceMappingURL=threaded_history_store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threaded_history_store.js","sourceRoot":"","sources":["../../../src/core/history/threaded_history_store.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,7 @@ The TUI also offers the configured command client tools selected for its own wor
15
15
 
16
16
  A Telegram runner offers the built-in `send_photo_to_telegram`, `send_video_to_telegram`, `send_audio_to_telegram`, and `send_document_to_telegram` tools, plus the command client tools selected by each prepared workspace's Tau configuration. The built-in [file delivery tools](tools.md#sending-files-to-telegram) send files to the session's chat and are not affected by `enabledClientTools`. The runner does not offer the TUI's tools. The session's workspace, whether repository, persistent directory, or composite, decides which configuration selects its tools. See [Telegram projects and workspaces](telegram-projects.md) for workspace types.
17
17
 
18
- Node SDK clients can pass `TauSdkClientTool` handlers when they initialize. The same rules for routing, cancellation, and execution-environment access apply.
18
+ Node SDK clients can pass `TauSdkClientTool` handlers when they initialize. Their schemas may set `executionTimeoutMs` to `null` for no execution deadline, or omit it for the 60-second default. Configured command tools require a positive timeout and do not accept `null`. The same rules for routing, cancellation, and execution-environment access apply.
19
19
 
20
20
  Only a client that is observing a session can add tools to it. The tools disappear when that client detaches or disconnects. When several clients observe one session, only one may offer a given tool name, and Tau rejects the later client if names collide. Client tool names also must not match host tools or built-in tools.
21
21
 
@@ -10,7 +10,8 @@ The host makes model calls and runs services such as web search and remote histo
10
10
 
11
11
  | Operation | Where the credential belongs |
12
12
  | --- | --- |
13
- | Main-agent, subagent, sampling, and maintenance model calls | Session host |
13
+ | Main-agent, native Tau subagent, sampling, and maintenance model calls | Session host |
14
+ | Claude Code subagent subscription login | Claude Code on the execution environment |
14
15
  | OpenAI Codex OAuth accounts | Session host home |
15
16
  | Exa-backed `web.search` and `web.fetch` | Session host |
16
17
  | Remote history replication and queries | Session host |
@@ -22,6 +23,8 @@ The host makes model calls and runs services such as web search and remote histo
22
23
 
23
24
  With local `tau`, all of these are usually one machine. With `tau attach`, a key set only in the attached client's shell does not reach the remote host. Run `tau auth` on the host machine, and set host environment variables where `tau serve` or the SDK host runs. See [ownership and scope](ownership-and-scope.md) for the full boundary.
24
25
 
26
+ Claude Code subagents use a Claude subscription login on the execution environment. Sign in with Claude Code there; `tau auth` does not manage this account. Tau checks the account before sending a task and does not fall back to API billing. See [Claude Code subagents](subagents.md#claude-code-subagents) for setup and extra-usage charges.
27
+
25
28
  ## Provider API keys
26
29
 
27
30
  Tau accepts provider API keys in global configuration:
@@ -23,7 +23,13 @@ For each session, history stores the creation attributes and the active transcri
23
23
 
24
24
  `<system>...</system>` blocks at the start of user messages stay in history. Tool arguments and results can contain file contents, command output, and other sensitive data, so treat the database as private. Tau creates its directory and file with private permissions, but the host user and the machine's administrators can still read them. Never copy the database into a repository or share it.
25
25
 
26
- Recording history is best effort. If the database cannot open, or a later write fails, the session keeps running. Tau adds one `history unavailable` warning to the session, which stays in the session, and turns history off until the host restarts. A restart tries to open the database again.
26
+ Recording history is best effort and never waits for database locks on the agent's event loop. The host buffers changes in memory and writes them in order from a background worker. Temporary SQLite contention delays recording; the worker retries automatically, including when opening the database. Normal appends and rewinds retain their order while waiting.
27
+
28
+ The buffer holds up to 64 MiB of serialized changes per host. When it fills, new changes are dropped until space is available. Other local write errors drop the affected change rather than disabling history. Later changes in the same session are still recorded. Session files and agent work are unaffected by history failures.
29
+
30
+ Pending changes have not reached durable storage. A crash can lose them; normal shutdown gives recording and replication up to two seconds to flush. Events already committed to SQLite remain on disk, including any pending remote replication. Tau does not automatically reconstruct missing history from session files.
31
+
32
+ Hosts emit `history_recording` diagnostics with a `delayed`, `dropped`, or `recovered` status, a session ID, and pending operation and byte counts. The TUI displays them, headless hosts write them to stderr, and in-process SDK hosts deliver them through `onDiagnostic`. Repeated drop diagnostics are rate-limited. A recording gap does not stop later recording or require a host restart.
27
33
 
28
34
  ## How session operations affect history
29
35
 
@@ -202,13 +208,13 @@ Cloudflare problems show up in the normal Worker logs, Cron Events, and D1 diagn
202
208
 
203
209
  **The agent has no history capability.** The active persona must select `history`. Fix the persona and run `/reload` while idle.
204
210
 
205
- **The history capability returns a service error while the session works.** Remote searches and replication can fail without affecting the session. Check that the endpoint is reachable and read the Worker logs, without printing the key. Local recording continues unless the session also shows a `history unavailable` warning.
211
+ **The history capability returns a service error while the session works.** Remote searches and replication can fail without affecting the session or local recording. Check that the endpoint is reachable and read the Worker logs, without printing the key. Check `history_recording` diagnostics separately for local write failures.
206
212
 
207
213
  **The browser keeps asking for credentials.** Use username `tau` and the viewer password from the latest setup. The API key is for hosts and does not work in the browser.
208
214
 
209
215
  **A session does not appear in remote search.** Confirm the host was restarted with the remote config, the session was used while the config was active, and enough history activity has happened to send the outbox. Check host logs for `history_replication_failed`. A permanent rejection affects only the named session, while other failures are retried. Summaries are not immediate.
210
216
 
211
- **Local history became unavailable.** On the host, check that you are running as the expected user and home, and check file access, free space, and ownership of `~/.config/tau`, the Node version, and whether another process is using the database. A restart is needed to reopen history after a local failure. Do not open, edit, replace, or delete the SQLite files as a first repair, and do not inspect outbox rows or keys.
217
+ **Local history is delayed or dropping changes.** Brief SQLite contention is retried automatically. For sustained failures, check the host's `history_recording` diagnostics, file access, free space, ownership of `~/.config/tau`, and whether another process holds a long database transaction. Fixing the underlying problem lets later writes proceed without restarting the host; dropped changes remain gaps. `tau history status` reports remote replication quarantines, not the live recording buffer. Do not edit, replace, or delete the SQLite files as a first repair, and do not inspect outbox rows or keys.
212
218
 
213
219
  **Search finds material removed from the model's context.** This is expected after compaction. After a rewind, the removed entries should disappear; if the remote copy still has them, wait for the deletion to replicate.
214
220
 
@@ -52,7 +52,7 @@ type TauSdkClientOptions = {
52
52
 
53
53
  `persona`, `reasoning`, and `noAgentContextFiles` apply to sessions created by this host. `connectTimeoutMs` defaults to 5,000 ms. Default initialization metadata is `{ client: { name: "tau-sdk", version: "1" } }`.
54
54
 
55
- The in-process host never writes background diagnostics to stdout or stderr. Set `onDiagnostic` to receive them as structured objects, such as history replication failures that will be retried, and route them to your application's logging.
55
+ The in-process host never writes background diagnostics to stdout or stderr. Set `onDiagnostic` to receive them as structured objects and route them to your application's logging. `history_recording` reports delayed, dropped, or recovered local recording, with a session ID and pending operation and byte counts. `history_replication_failed` reports remote delivery failures. Neither stops agent work; see [history](history.md) for retry behavior and durability limits.
56
56
 
57
57
  By default, creating an in-process client starts one background check of the model catalog against `pi.dev`. The host loads `~/.config/tau/models-store.json` first, never refreshes on a timer, and keeps each session on the catalog it started with until `session.reload()`. Set `refreshModelCatalog: false` to skip this client's check, or set `TAU_OFFLINE` to skip automatic checks for the whole process. Neither removes an existing cache or blocks an explicit `tau models refresh`. See [models](models.md) for details and [security](security.md) for why the catalog is trusted.
58
58
 
@@ -24,6 +24,8 @@ The built-in `tau_docs` tool also belongs to the host. It reads the documentatio
24
24
 
25
25
  The **execution environment** is the machine the agent can act on. It owns the agent-visible working directory (`cwd`), home, platform, environment, filesystem, processes, project repository, project configuration and content, `AGENTS.md` files, skills, and command resolution.
26
26
 
27
+ [Claude Code subagents](subagents.md#claude-code-subagents) run their model calls here and keep their subscription login and conversation files here. The host starts, observes, and stops these workers through the execution environment. Their credentials remain with Claude Code.
28
+
27
29
  Tau reads files and runs commands through the execution environment, even when it is on the same machine as the host. Each session has exactly one execution environment. Paths shown to the agent and paths passed to its tools are always paths on that environment.
28
30
 
29
31
  ### Telegram runner
@@ -20,6 +20,8 @@ Give each part only the access it needs:
20
20
 
21
21
  Interruption, timeouts, output limits, and process-group termination limit how long and how much a command runs. They do not ask for approval and cannot undo anything already done. Review destructive commands, and use Tau's normal session operations instead of asking an agent to change Tau's internal files.
22
22
 
23
+ [Claude Code subagents](subagents.md#claude-code-subagents) run on the execution environment with permission checks bypassed and the Claude Code sandbox disabled. They load local Claude settings, project instructions, skills, and normally configured MCP servers. Review those files and server permissions before enabling workers. The parent persona controls the selected built-in tools; Claude Code's own configuration controls its MCP connections. Tau disables hooks and nested agents. Managed Claude policies still apply. Claude credentials and transcripts stay on that environment; Tau does not forward host service credentials or MCP connections. Subscription extra usage can incur charges if enabled in the Claude account.
24
+
23
25
  ## Treat project content as executable policy
24
26
 
25
27
  Tau loads more than source files from the execution environment. It reads project `.tau/config.json`, personas, prompts, skills, `.agents/skills`, and `AGENTS.md` from the working directory and its parents. This content can choose models and tools, add instructions, define workflows, and change which of your global client tools are offered.
@@ -41,7 +41,7 @@ params: {
41
41
  name: string;
42
42
  description: string;
43
43
  parameters: unknown;
44
- executionTimeoutMs?: number;
44
+ executionTimeoutMs?: number | null; // omitted: 60 seconds; null: no execution deadline
45
45
  }>;
46
46
  };
47
47
  }
@@ -228,6 +228,8 @@ An initialized client that advertised a tool can receive:
228
228
  }
229
229
  ```
230
230
 
231
+ `executionDeadlineMs` is a positive duration in milliseconds, or `null` when the advertised tool has no execution deadline. Omitting `executionTimeoutMs` from the tool definition uses 60 seconds; setting it to `null` disables only the execution deadline. Acknowledgement deadlines and cancellation on interruption or client detach still apply.
232
+
231
233
  Acknowledge promptly with `session.clientTool.ack`, optionally with a partial presentation for the running tool card. Start work only after the acknowledgement returns `{ accepted: true }`. Then send exactly one `session.clientTool.result` with `{ ok: true, content }` or `{ ok: false, error }`, optionally with a separate presentation for the finished card. `content` is an ordered array of text and image blocks. Both presentations may contain `subject`, `subjectWrap`, `details`, and `metadata`. The host sets the action and operation and fills in omitted fields. Fields you set are kept unchanged once they pass validation, while defaults are truncated the way Tau truncates its own cards. An empty `details` or `metadata` array hides that default.
232
234
 
233
235
  A successful result is rejected until the acknowledgement has completed. If preparation fails, send an error result before acknowledging; the host records a preparation failure and the tool never runs. If no result arrives, because of a timeout, cancellation, detach, or other failure, the host shows a complete default card. The result method returns `{ accepted: boolean }`; `false` means the result does not fit the call's current state or the call no longer expects it.
@@ -6,7 +6,7 @@ Subagents are background agents that the main agent starts and the host supervis
6
6
 
7
7
  Subagents are general-purpose workers. There are no named types. Several can run at once, each with its own task and conversation.
8
8
 
9
- A subagent behaves like the active main persona, wrapped in instructions that Tau maintains. Those instructions and the subagent's description cannot be configured. Put task-specific instructions, including any relevant skill guidance, in the launch prompt.
9
+ A native Tau subagent behaves like the active main persona, wrapped in instructions that Tau maintains. Those instructions and the subagent's description cannot be configured. Put task-specific instructions, including any relevant skill guidance, in the launch prompt.
10
10
 
11
11
  Whether the agent can start subagents depends on the main persona's tool list. Built-in personas, and custom personas that omit `tools`, enable all five subagent tools. A custom persona with a `tools` list that lacks `spawn_agent` cannot start subagents. Leave out all five subagent tools when no subagent interaction is wanted. There is no other setting to turn subagents on or off. See [personas](personas.md).
12
12
 
@@ -18,7 +18,7 @@ The agent follows this rule itself. Tau checks that a launch is valid, but it do
18
18
 
19
19
  ## Tools
20
20
 
21
- A subagent can receive these tools:
21
+ A native Tau subagent can receive these tools. [Claude Code subagents](#claude-code-subagents) use the corresponding Claude Code tools instead:
22
22
 
23
23
  - `bash`
24
24
  - `write`
@@ -32,7 +32,7 @@ A subagent can receive these tools:
32
32
 
33
33
  A subagent gets each of these that the main persona also has. For example, a main persona with `bash`, `edit`, `history`, and `spawn_agent` gives its subagents `bash`, `edit`, and `history`. Names such as `web` and `history` enable the matching capability in the subagent's `code` tool. MCP uses the host's existing connections, whatever the subagent's working directory. There is no separate tool list for subagents.
34
34
 
35
- Every subagent also gets the built-in `tau_docs` tool, and a persona's `tools` list cannot remove it. Subagents never get the subagent tools, client tools, or TUI tools, so a subagent cannot start subagents of its own. See [tools](tools.md) for how tool availability works in general.
35
+ Every native Tau subagent also gets the built-in `tau_docs` tool, and a persona's `tools` list cannot remove it. Subagents never get the subagent tools, client tools, or TUI tools, so a subagent cannot start subagents of its own. See [tools](tools.md) for how tool availability works in general.
36
36
 
37
37
  ## Models and settings
38
38
 
@@ -46,7 +46,7 @@ A model override uses this exact form:
46
46
 
47
47
  The provider is lowercased. The model ID is kept exactly as written and is case-sensitive. Effort is one of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`.
48
48
 
49
- The configured list of launch models is an allowlist. It does not choose a default. The main agent should normally omit the `model` argument to `spawn_agent`, and the subagent then uses the active persona's model and reasoning. When `model` is given, it must exactly match an allowlist entry after normalization. The override changes the provider, model, and reasoning, and keeps the other settings.
49
+ The configured list of launch models is an allowlist. It does not choose a default. The main agent should normally omit the `model` argument to `spawn_agent`, and the subagent then uses the active persona's model and reasoning. When `model` is given, it must exactly match an allowlist entry after normalization. For native Tau subagents, the override changes the provider, model, and reasoning, and keeps the other settings.
50
50
 
51
51
  A model ID may be missing from the catalog if its provider is known (see [models](models.md)). An entry with an invalid provider, model, effort, or format makes the configuration field invalid. Duplicate entries are removed.
52
52
 
@@ -67,17 +67,63 @@ Configure the allowlist with `subagents.launchModels` in `config.json`:
67
67
 
68
68
  The nearest `launchModels` list replaces broader ones; lists are not combined. The resulting list applies to every persona that has `spawn_agent`.
69
69
 
70
+ ## Claude Code subagents
71
+
72
+ The `claude-code` launch provider runs Claude Code through Anthropic's Agent SDK. The main agent uses the usual spawn, list, wait, follow-up, and interrupt tools to manage these workers. Claude Code models are available only as explicit subagent launch overrides.
73
+
74
+ Install Claude Code on the session's execution environment, put `claude` on its login-shell `PATH`, and sign in there with a Claude subscription. A login on the host or attached laptop does not reach a separate execution environment. Tau includes Agent SDK 0.3.308, so no separate SDK installation is needed.
75
+
76
+ Add the desired model/effort combinations to the launch allowlist:
77
+
78
+ ```json
79
+ {
80
+ "subagents": {
81
+ "launchModels": [
82
+ "claude-code/claude-opus-5-5:medium",
83
+ "claude-code/claude-sonnet-5-5:medium",
84
+ "claude-code/claude-haiku-5-5:low"
85
+ ]
86
+ }
87
+ }
88
+ ```
89
+
90
+ These are the three supported model IDs. Effort may be `low`, `medium`, `high`, `xhigh`, or `max`. Each permitted combination needs its own allowlist entry. Model availability also depends on the Claude account and installed Claude Code version. If Claude Code rejects a model, Tau reports the failure.
91
+
92
+ Claude Code manages its conversation, retries, and compaction. Each run allows up to 1,024 agentic turns. Tau passes the selected model, effort, working directory, and task. Other Tau model settings, such as service tier, do not apply. Follow-ups resume the same Claude session.
93
+
94
+ The worker uses Claude Code's native system prompt with a short addition: complete the task autonomously, preserve other agents' work, and return a self-contained result. It cannot ask the parent or user for clarification. Tau does not copy the parent conversation, persona prompt, project instructions, or skill descriptions. The worker is instructed to follow applicable project instructions itself.
95
+
96
+ Claude Code loads its user, project, and local settings and discovers its own instructions and skills. The `Skill` tool is always available. Other native tools follow the parent persona's selection:
97
+
98
+ | Inherited Tau tool | Claude Code tools |
99
+ | ------------------ | ---------------------- |
100
+ | `bash` | Bash, Read, Glob, Grep |
101
+ | `view_image` | Read, Glob, Grep |
102
+ | `write` | Write |
103
+ | `edit` | Edit |
104
+ | `web` | WebSearch, WebFetch |
105
+
106
+ Workers receive no Tau `code` capabilities, host MCP connections, `tau_docs`, or client tools. Nested agents and interactive questions are disabled. Hooks and automatic memory are disabled too. Claude Code discovers its own MCP servers from its normal configuration sources, including project and user configuration on the execution environment. These connections are independent of the parent persona's `mcp` tool selection; Tau does not map or forward its host MCP connections. Managed Claude Code policies still apply.
107
+
108
+ Selected tools use `bypassPermissions`, with Claude Code's sandbox disabled. They run with the execution environment's operating-system authority, like Tau's tools. Review the local Claude settings, skills, and MCP configuration before enabling these workers.
109
+
110
+ Tau displays short assistant updates and tool completion summaries. Full tool output stays in Claude Code's conversation files. Tau records SDK-reported assistant token usage as it arrives, so interruption preserves counts already received. Repeated message blocks and resumed messages are counted once. Final SDK totals reconcile these counts and supply cost estimates, including earlier usage when a session resumes. Cost from an interrupted run remains unavailable until the SDK reports it. The latest assistant usage supplies a context-token estimate; after a successful run, the SDK's context summary supplies the window size and a fuller measurement.
111
+
112
+ Before sending the task, Tau checks that the SDK reports a subscription login without API-key authentication. Claude Code keeps its own credentials. Tau removes API and cloud-provider routing overrides from the child environment and provides no API fallback. Fast mode is disabled. Account-level extra usage can still incur charges; disable it in Claude to use only the included subscription allowance. The SDK's dollar estimates do not report subscription charges.
113
+
114
+ Claude workers share the eight-worker limit and the usual subagent lifetime. Interrupting a worker terminates its process group. A follow-up can resume its Claude session while the parent session remains alive. Host restart, session disposal, and rewind remove workers under the same rules as native Tau subagents. Claude's transcript files remain on disk, but Tau does not use them to restore workers after a restart.
115
+
70
116
  ## Working directory
71
117
 
72
118
  `spawn_agent` normally runs the subagent in the main session's `cwd`. The optional `workingDirectory` may be absolute or relative to that `cwd`. Tau resolves it to an absolute path on the execution environment.
73
119
 
74
- When that path differs from the parent's `cwd`, Tau rebuilds the subagent's context for the new directory. It detects the platform and repository there, reads the applicable `AGENTS.md` files, and discovers that directory's skills.
120
+ For native Tau subagents, when that path differs from the parent's `cwd`, Tau rebuilds the subagent's context for the new directory. It detects the platform and repository there, reads the applicable `AGENTS.md` files, and discovers that directory's skills.
75
121
 
76
- The new directory does **not** change the subagent's persona, model catalog, runtime configuration, credentials, or tool policy. Those always come from the parent session. Configuration in the new directory is read only where context needs it, such as for skill discovery.
122
+ For native Tau subagents, the new directory does not change the persona, model catalog, runtime configuration, credentials, or tool policy. Those come from the parent session. Configuration in the new directory is read only where context needs it, such as for skill discovery.
77
123
 
78
124
  `workingDirectory` is always a path on the execution environment, which matters in monorepos and hosted environments. The host and attached clients never resolve it against their own filesystems.
79
125
 
80
- A launch fails if Tau cannot build context for the directory, if the directory is invalid for the environment, or if the environment cannot resolve working directories. A path that resolves to the parent's `cwd`, such as `.`, reuses the parent's context.
126
+ A native Tau launch fails if Tau cannot build context for the directory, if the directory is invalid for the environment, or if the environment cannot resolve working directories. A path that resolves to the parent's `cwd`, such as `.`, reuses the parent's context. Claude Code workers load context themselves in the selected directory; an invalid directory fails when the worker starts.
81
127
 
82
128
  ## Lifecycle and subagent tools
83
129
 
@@ -113,7 +159,7 @@ In the TUI, `Ctrl+G` interrupts the selected running subagent. Interrupting the
113
159
 
114
160
  Subagents belong to the running session on the host, not to an attached TUI. Detaching a client, or losing the connection, does not stop them while the session stays alive. Another client can attach to the session and see their current state.
115
161
 
116
- Subagents do not survive the end of a session on the host or a host restart. Their status may be saved while the session is live, but recovery removes those records, because the subagents' conversations no longer exist. A recovered session cannot send input to an old subagent ID.
162
+ Subagents do not survive the end of a session on the host or a host restart. Their status may be saved while the session is live, but recovery removes those records, because their runtimes no longer exist. A recovered session cannot send input to an old subagent ID.
117
163
 
118
164
  Rewind also removes subagents that were started by an assistant message the rewind removed. Host shutdown or session disposal interrupts and removes all subagents.
119
165
 
@@ -208,6 +208,8 @@ The session host provides temporary review agents, and the local tool runs the b
208
208
 
209
209
  The TUI also offers diff review to the agent as a client tool, unless `--no-client-tools` is set. `/diff` works either way. See [client tools](client-tools.md) for what this means with several clients.
210
210
 
211
+ Agent-launched diff reviews have no execution deadline. They remain open until submitted or closed, or until the turn is interrupted or the owning client disconnects. Acknowledgement and review initialization still have startup timeouts.
212
+
211
213
  The agent can use `diff_review` with `workingDirectory` to review another directory without changing the session directory. Paths refer to the session's execution environment; relative paths resolve from the session directory. The selected directory must be inside a Git repository and is used by review assistants and for relative patch-file paths. Omitting it uses the session directory.
212
214
 
213
215
  ## Use speech
@@ -6,6 +6,7 @@ import { createAutoCompactionArchiver } from "../session/auto_compaction_archive
6
6
  import { ToolCatalog } from "../tools/catalog.js";
7
7
  import { appendUsageLogEntry, getUsageCostTotal, getUsageTotals, } from "../usage/logs.js";
8
8
  import { extractAssistantText } from "../utils/messages.js";
9
+ import { ClaudeCodeRuntime } from "./claude_code_runtime.js";
9
10
  import { formatActiveSubagentsForCompaction } from "./format.js";
10
11
  const MAX_ACTIVE_SUBAGENTS = 8;
11
12
  async function raceWithAbort(promise, signal) {
@@ -69,31 +70,43 @@ export class AgentSupervisor {
69
70
  }
70
71
  const id = randomUUID();
71
72
  const runtimeConfig = { ...options.runtimeConfig, workingDirectory };
72
- const persona = {
73
- id: `${options.personaId ?? "agent"}-subagent`,
74
- label: "subagent",
75
- description: "Background agent",
76
- source: "project",
77
- systemPrompt: runtimeConfig.systemPrompt,
78
- model: runtimeConfig.model,
79
- settings: runtimeConfig.settings,
80
- tools: runtimeConfig.tools,
81
- subagentLaunchModels: [],
82
- };
83
- const runtime = new AgentRuntime({
84
- spec: createAgentSpec({
85
- autoCompactThresholdTokens: null,
86
- ...resolveAgentModel(persona, options.config, {
87
- includeModelNotice: true,
88
- deps: this.deps,
73
+ let runtime;
74
+ let reasoning;
75
+ if (runtimeConfig.kind === "claude-code") {
76
+ runtime = new ClaudeCodeRuntime({
77
+ config: runtimeConfig,
78
+ backend: options.backend,
79
+ onEvent: async (event) => await this.recordClaudeCodeEvent(id, event),
80
+ });
81
+ reasoning = runtimeConfig.effort;
82
+ }
83
+ else {
84
+ runtime = new AgentRuntime({
85
+ spec: createAgentSpec({
86
+ autoCompactThresholdTokens: null,
87
+ ...resolveAgentModel({
88
+ id: `${options.personaId ?? "agent"}-subagent`,
89
+ label: "subagent",
90
+ description: "Background agent",
91
+ source: "project",
92
+ systemPrompt: runtimeConfig.systemPrompt,
93
+ model: runtimeConfig.model,
94
+ settings: runtimeConfig.settings,
95
+ tools: runtimeConfig.tools,
96
+ subagentLaunchModels: [],
97
+ }, options.config, {
98
+ includeModelNotice: true,
99
+ deps: this.deps,
100
+ }),
101
+ systemPrompt: runtimeConfig.systemPrompt,
102
+ tools: ToolCatalog.createSubagentRegistry(runtimeConfig.tools, options.backend, workingDirectory, options.config, options.bashJobs, options.history, options.mcp),
89
103
  }),
90
- systemPrompt: runtimeConfig.systemPrompt,
91
- tools: ToolCatalog.createSubagentRegistry(runtimeConfig.tools, options.backend, workingDirectory, options.config, options.bashJobs, options.history, options.mcp),
92
- }),
93
- eventSink: async (event) => await this.recordAgentEvent(id, event),
94
- clock: this.deps.clock,
95
- archiveAutoCompaction: createAutoCompactionArchiver(options.backend),
96
- });
104
+ eventSink: async (event) => await this.recordAgentEvent(id, event),
105
+ clock: this.deps.clock,
106
+ archiveAutoCompaction: createAutoCompactionArchiver(options.backend),
107
+ });
108
+ reasoning = runtime.spec.attribution.reasoningEffort;
109
+ }
97
110
  const createdAt = this.deps.clock.now();
98
111
  const record = {
99
112
  id,
@@ -101,12 +114,11 @@ export class AgentSupervisor {
101
114
  model: {
102
115
  provider: runtimeConfig.model.provider,
103
116
  id: runtimeConfig.model.id,
104
- reasoning: runtime.spec.attribution.reasoningEffort,
117
+ reasoning,
105
118
  },
106
119
  workingDirectory,
107
120
  createdAt,
108
121
  originHistoryEntryId: options.originHistoryEntryId,
109
- runtimeConfig,
110
122
  personaId: options.personaId,
111
123
  runtime,
112
124
  run: {
@@ -122,8 +134,9 @@ export class AgentSupervisor {
122
134
  cacheRead: 0,
123
135
  cacheWrite: 0,
124
136
  contextWindowUsageTokens: 0,
125
- contextWindow: runtimeConfig.model.contextWindow,
137
+ contextWindow: runtimeConfig.kind === "native" ? runtimeConfig.model.contextWindow : 0,
126
138
  },
139
+ claudeMessageUsage: new Map(),
127
140
  toolPresentations: new Map(),
128
141
  blockedToolCalls: new Map(),
129
142
  completion: Promise.resolve(undefined),
@@ -193,15 +206,27 @@ export class AgentSupervisor {
193
206
  record.completion = this.emit({ type: eventType, state: this.toSnapshot(record) })
194
207
  .then(async () => {
195
208
  if (record.run.interruptRequested) {
196
- return {
197
- aborted: true,
198
- terminalResult: { aborted: true },
199
- };
209
+ this.finishInterrupted(record);
210
+ }
211
+ else if (record.runtime instanceof ClaudeCodeRuntime) {
212
+ const completion = await record.runtime.submit(prompt);
213
+ if (record.run.interruptRequested || completion.status === "interrupted") {
214
+ this.finishInterrupted(record);
215
+ }
216
+ else if (completion.result.failed) {
217
+ this.finishFailed(record, {
218
+ kind: "provider-error",
219
+ message: completion.result.response,
220
+ stopReason: "error",
221
+ });
222
+ }
223
+ else {
224
+ this.finishSucceeded(record, completion.result.response);
225
+ }
226
+ }
227
+ else {
228
+ this.applyTurnResult(record, await record.runtime.submit(prompt));
200
229
  }
201
- return await record.runtime.submit(prompt);
202
- })
203
- .then((result) => {
204
- this.applyTurnResult(record, result);
205
230
  return record;
206
231
  })
207
232
  .catch((error) => {
@@ -259,13 +284,16 @@ export class AgentSupervisor {
259
284
  });
260
285
  return;
261
286
  }
287
+ this.finishSucceeded(record, extractAssistantText(result.finalMessage).trim());
288
+ }
289
+ finishSucceeded(record, response) {
262
290
  record.run = {
263
291
  revision: record.run.revision,
264
292
  status: "succeeded",
265
293
  startedAt: record.run.startedAt,
266
294
  finishedAt: this.deps.clock.now(),
267
295
  interruptRequested: false,
268
- response: extractAssistantText(result.finalMessage).trim(),
296
+ response,
269
297
  };
270
298
  }
271
299
  finishInterrupted(record) {
@@ -288,6 +316,99 @@ export class AgentSupervisor {
288
316
  failure,
289
317
  };
290
318
  }
319
+ async recordClaudeCodeEvent(id, event) {
320
+ const record = this.records.get(id);
321
+ if (!record)
322
+ return;
323
+ if (event.type === "usage" || event.type === "result") {
324
+ let reported = event.usage;
325
+ if (event.type === "usage") {
326
+ const previous = record.claudeMessageUsage.get(event.messageId);
327
+ // Final result totals already cover these messages, including resumed replays.
328
+ if (previous === null)
329
+ return;
330
+ const current = {
331
+ input: Math.max(previous?.input ?? 0, event.usage.input),
332
+ output: Math.max(previous?.output ?? 0, event.usage.output),
333
+ cacheRead: Math.max(previous?.cacheRead ?? 0, event.usage.cacheRead),
334
+ cacheWrite: Math.max(previous?.cacheWrite ?? 0, event.usage.cacheWrite),
335
+ };
336
+ record.claudeMessageUsage.set(event.messageId, current);
337
+ reported = {
338
+ input: record.usage.input + current.input - (previous?.input ?? 0),
339
+ output: record.usage.output + current.output - (previous?.output ?? 0),
340
+ cacheRead: record.usage.cacheRead + current.cacheRead - (previous?.cacheRead ?? 0),
341
+ cacheWrite: record.usage.cacheWrite + current.cacheWrite - (previous?.cacheWrite ?? 0),
342
+ };
343
+ if (getUsageTotals(current).total === getUsageTotals(previous).total)
344
+ return;
345
+ record.usage.contextWindowUsageTokens = getUsageTotals(current).total;
346
+ }
347
+ else {
348
+ for (const messageId of record.claudeMessageUsage.keys()) {
349
+ record.claudeMessageUsage.set(messageId, null);
350
+ }
351
+ record.usage.contextWindowUsageTokens = event.usage.contextWindowUsageTokens;
352
+ record.usage.contextWindow = event.usage.contextWindow;
353
+ }
354
+ // SDK totals include the history restored on resume. Record only the delta,
355
+ // and replace (rather than sum) the current context-window measurement.
356
+ const usage = getUsageTotals({
357
+ input: Math.max(0, reported.input - record.usage.input),
358
+ output: Math.max(0, reported.output - record.usage.output),
359
+ cacheRead: Math.max(0, reported.cacheRead - record.usage.cacheRead),
360
+ cacheWrite: Math.max(0, reported.cacheWrite - record.usage.cacheWrite),
361
+ });
362
+ const cost = event.type === "result" ? Math.max(0, event.cost - record.costTotal) : 0;
363
+ // Startup failures can report zero totals even for a resumed session.
364
+ record.costTotal += cost;
365
+ record.usage = {
366
+ ...record.usage,
367
+ input: record.usage.input + usage.input,
368
+ output: record.usage.output + usage.output,
369
+ cacheRead: record.usage.cacheRead + usage.cacheRead,
370
+ cacheWrite: record.usage.cacheWrite + usage.cacheWrite,
371
+ };
372
+ (this.options.recordUsage ?? appendUsageLogEntry)({
373
+ timestamp: this.deps.clock.now(),
374
+ sessionId: record.id,
375
+ personaId: record.personaId,
376
+ provider: record.model.provider,
377
+ model: record.model.id,
378
+ api: "claude-agent-sdk",
379
+ reasoningEffort: record.model.reasoning,
380
+ usage,
381
+ cost: { total: cost },
382
+ agent: { type: "subagent" },
383
+ });
384
+ await this.emit({ type: "subagent_updated", state: this.toSnapshot(record) });
385
+ }
386
+ else if (event.type === "assistant") {
387
+ await this.emit({
388
+ type: "subagent_activity",
389
+ state: this.toSnapshot(record),
390
+ activity: { type: "assistant", text: event.text },
391
+ });
392
+ }
393
+ else if (event.type === "tool") {
394
+ await this.emit({
395
+ type: "subagent_activity",
396
+ state: this.toSnapshot(record),
397
+ activity: {
398
+ type: "tool",
399
+ toolName: event.name,
400
+ outcome: event.failed ? "failed" : "succeeded",
401
+ presentation: {
402
+ action: event.failed ? "failed" : "completed",
403
+ subject: event.subject,
404
+ subjectWrap: "word",
405
+ details: [],
406
+ metadata: [],
407
+ },
408
+ },
409
+ });
410
+ }
411
+ }
291
412
  async recordAgentEvent(id, event) {
292
413
  const record = this.records.get(id);
293
414
  if (!record)
@@ -302,7 +423,7 @@ export class AgentSupervisor {
302
423
  cacheRead: record.usage.cacheRead + usage.cacheRead,
303
424
  cacheWrite: record.usage.cacheWrite + usage.cacheWrite,
304
425
  contextWindowUsageTokens: usage.input + usage.output + usage.cacheRead + usage.cacheWrite,
305
- contextWindow: record.runtimeConfig.model.contextWindow,
426
+ contextWindow: record.usage.contextWindow,
306
427
  };
307
428
  (this.options.recordUsage ?? appendUsageLogEntry)({
308
429
  timestamp: event.message.timestamp,