@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.
- package/dist/core/history/history_manager.js +188 -94
- package/dist/core/history/history_manager.js.map +1 -1
- package/dist/core/history/history_store_worker.js +21 -0
- package/dist/core/history/history_store_worker.js.map +1 -0
- package/dist/core/history/local_history_store.js +11 -5
- package/dist/core/history/local_history_store.js.map +1 -1
- package/dist/core/history/threaded_history_store.js +107 -0
- package/dist/core/history/threaded_history_store.js.map +1 -0
- package/dist/core/static/tau_docs/client-tools.md +1 -1
- package/dist/core/static/tau_docs/credentials.md +4 -1
- package/dist/core/static/tau_docs/history.md +9 -3
- package/dist/core/static/tau_docs/node-sdk.md +1 -1
- package/dist/core/static/tau_docs/ownership-and-scope.md +2 -0
- package/dist/core/static/tau_docs/security.md +2 -0
- package/dist/core/static/tau_docs/session-protocol-methods.md +1 -1
- package/dist/core/static/tau_docs/session-protocol.md +2 -0
- package/dist/core/static/tau_docs/subagents.md +54 -8
- package/dist/core/static/tau_docs/tui.md +2 -0
- package/dist/core/subagents/agent_supervisor.js +158 -37
- package/dist/core/subagents/agent_supervisor.js.map +1 -1
- package/dist/core/subagents/claude_code_models.js +11 -0
- package/dist/core/subagents/claude_code_models.js.map +1 -0
- package/dist/core/subagents/claude_code_protocol.js +44 -0
- package/dist/core/subagents/claude_code_protocol.js.map +1 -0
- package/dist/core/subagents/claude_code_runner.js +226 -0
- package/dist/core/subagents/claude_code_runner.js.map +1 -0
- package/dist/core/subagents/claude_code_runner.mjs +176 -0
- package/dist/core/subagents/claude_code_runtime.js +145 -0
- package/dist/core/subagents/claude_code_runtime.js.map +1 -0
- package/dist/core/subagents/format.js +3 -1
- package/dist/core/subagents/format.js.map +1 -1
- package/dist/core/subagents/launch_model.js +23 -7
- package/dist/core/subagents/launch_model.js.map +1 -1
- package/dist/core/subagents/registry.js +10 -1
- package/dist/core/subagents/registry.js.map +1 -1
- package/dist/core/subagents/types.js.map +1 -1
- package/dist/core/tools/spawn_agent.js +31 -26
- package/dist/core/tools/spawn_agent.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/host/client_tool_broker.js +8 -4
- package/dist/host/client_tool_broker.js.map +1 -1
- package/dist/host/local_session_host.js +3 -2
- package/dist/host/local_session_host.js.map +1 -1
- package/dist/main.js +4 -3
- package/dist/main.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +2 -2
- package/dist/protocol/session_protocol.js +2 -2
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/sdk/local_client.js +1 -0
- package/dist/sdk/local_client.js.map +1 -1
- package/dist/sdk/types.d.ts +7 -0
- package/dist/tui/session_chat_app.js +1 -1
- package/dist/tui/session_chat_app.js.map +1 -1
- package/dist/tui/session_chat_controller.js +12 -1
- package/dist/tui/session_chat_controller.js.map +1 -1
- 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
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
|
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.
|
|
426
|
+
contextWindow: record.usage.contextWindow,
|
|
306
427
|
};
|
|
307
428
|
(this.options.recordUsage ?? appendUsageLogEntry)({
|
|
308
429
|
timestamp: event.message.timestamp,
|