@markusylisiurunen/tau 0.3.94 → 0.3.96
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/code_mode/command.js +3 -1
- package/dist/code_mode/command.js.map +1 -1
- package/dist/code_mode/output.d.ts +3 -0
- package/dist/code_mode/output.js +8 -2
- package/dist/code_mode/output.js.map +1 -1
- package/dist/code_mode/runtime.d.ts +7 -1
- package/dist/code_mode/runtime.js +18 -14
- package/dist/code_mode/runtime.js.map +1 -1
- package/dist/core/code_mode/agents.js +434 -0
- package/dist/core/code_mode/agents.js.map +1 -0
- package/dist/core/code_mode/bash.js +14 -17
- package/dist/core/code_mode/bash.js.map +1 -1
- package/dist/core/code_mode/history.js +1 -0
- package/dist/core/code_mode/history.js.map +1 -1
- package/dist/core/code_mode/models.js +31 -4
- package/dist/core/code_mode/models.js.map +1 -1
- package/dist/core/code_mode/sdk.js +6 -0
- package/dist/core/code_mode/sdk.js.map +1 -1
- package/dist/core/config/content_loader.js +3 -13
- package/dist/core/config/content_loader.js.map +1 -1
- package/dist/core/debug.js +2 -2
- package/dist/core/debug.js.map +1 -1
- package/dist/core/history/history_manager.js +7 -4
- package/dist/core/history/history_manager.js.map +1 -1
- package/dist/core/index.js +0 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/mcp/manager.js +7 -0
- package/dist/core/mcp/manager.js.map +1 -1
- package/dist/core/models/openrouter.js +25 -1
- package/dist/core/models/openrouter.js.map +1 -1
- package/dist/core/personas.js +3 -11
- package/dist/core/personas.js.map +1 -1
- package/dist/core/runtime/chat_runtime.js +11 -10
- package/dist/core/runtime/chat_runtime.js.map +1 -1
- package/dist/core/runtime/session_prompt_composer.js +9 -8
- package/dist/core/runtime/session_prompt_composer.js.map +1 -1
- package/dist/core/static/code_mode/history/documentation.md +2 -1
- package/dist/core/static/code_mode/sandbox_runner.mjs +4 -0
- package/dist/core/static/index.js +18 -5
- package/dist/core/static/index.js.map +1 -1
- package/dist/core/static/prompts/subagent-prefix.md +17 -0
- package/dist/core/static/tau_docs/client-tools.md +4 -0
- package/dist/core/static/tau_docs/code-mode.md +77 -0
- package/dist/core/static/tau_docs/config-reference.md +4 -2
- package/dist/core/static/tau_docs/history.md +5 -3
- package/dist/core/static/tau_docs/index.md +2 -1
- package/dist/core/static/tau_docs/manifest.json +1 -0
- package/dist/core/static/tau_docs/node-sdk.md +2 -0
- package/dist/core/static/tau_docs/openrouter.md +2 -0
- package/dist/core/static/tau_docs/pdf-unpacking.md +1 -1
- package/dist/core/static/tau_docs/personas.md +4 -5
- package/dist/core/static/tau_docs/prompts-and-project-context.md +1 -1
- package/dist/core/static/tau_docs/security.md +1 -1
- package/dist/core/static/tau_docs/sessions.md +1 -1
- package/dist/core/static/tau_docs/subagents.md +106 -27
- package/dist/core/static/tau_docs/tools.md +28 -64
- package/dist/core/static/tau_docs/troubleshooting.md +2 -2
- package/dist/core/static/tau_docs/tui.md +2 -0
- package/dist/core/subagents/agent_supervisor.js +185 -339
- package/dist/core/subagents/agent_supervisor.js.map +1 -1
- package/dist/core/subagents/claude_code_protocol.js +4 -0
- package/dist/core/subagents/claude_code_protocol.js.map +1 -1
- package/dist/core/subagents/claude_code_runner.js +134 -11
- package/dist/core/subagents/claude_code_runner.js.map +1 -1
- package/dist/core/subagents/claude_code_runner.mjs +80 -77
- package/dist/core/subagents/claude_code_runtime.js +250 -9
- package/dist/core/subagents/claude_code_runtime.js.map +1 -1
- package/dist/core/subagents/errors.js +18 -0
- package/dist/core/subagents/errors.js.map +1 -0
- package/dist/core/subagents/events.js +25 -0
- package/dist/core/subagents/events.js.map +1 -0
- package/dist/core/subagents/format.js +2 -98
- package/dist/core/subagents/format.js.map +1 -1
- package/dist/core/subagents/native_subagent_runtime.js +270 -0
- package/dist/core/subagents/native_subagent_runtime.js.map +1 -0
- package/dist/core/subagents/prompt.js +3 -5
- package/dist/core/subagents/prompt.js.map +1 -1
- package/dist/core/subagents/registry.js +5 -3
- package/dist/core/subagents/registry.js.map +1 -1
- package/dist/core/subagents/runtime.js +13 -0
- package/dist/core/subagents/runtime.js.map +1 -0
- package/dist/core/subagents/trace.js +149 -0
- package/dist/core/subagents/trace.js.map +1 -0
- package/dist/core/tools/agent_jobs.js +51 -0
- package/dist/core/tools/agent_jobs.js.map +1 -0
- package/dist/core/tools/background_jobs.js +205 -0
- package/dist/core/tools/background_jobs.js.map +1 -0
- package/dist/core/tools/bash.js +11 -45
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/bash_jobs.js +44 -238
- package/dist/core/tools/bash_jobs.js.map +1 -1
- package/dist/core/tools/catalog.js +37 -72
- package/dist/core/tools/catalog.js.map +1 -1
- package/dist/core/tools/code.js +177 -54
- package/dist/core/tools/code.js.map +1 -1
- package/dist/core/tools/code_jobs.js +128 -0
- package/dist/core/tools/code_jobs.js.map +1 -0
- package/dist/core/tools/code_mode.js +88 -101
- package/dist/core/tools/code_mode.js.map +1 -1
- package/dist/core/tools/code_mode_worker.js +13 -7
- package/dist/core/tools/code_mode_worker.js.map +1 -1
- package/dist/core/tools/job_output.js +53 -0
- package/dist/core/tools/job_output.js.map +1 -0
- package/dist/core/tools/output_file.js +38 -0
- package/dist/core/tools/output_file.js.map +1 -0
- package/dist/core/tools/presentation.js +19 -9
- package/dist/core/tools/presentation.js.map +1 -1
- package/dist/core/tools/tool_names.js +8 -6
- package/dist/core/tools/tool_names.js.map +1 -1
- package/dist/core/tools/wait_for_agents.js +47 -38
- package/dist/core/tools/wait_for_agents.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/diff_tool/app/dist/assets/{core-33fyIuI3.js → core-NkxL1yZC.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{highlights-BZwSBtSw.js → highlights-Coi8xHW0.js} +1 -1
- package/dist/diff_tool/app/dist/assets/index-Jt-l2Q96.js +205 -0
- package/dist/diff_tool/app/dist/assets/langs-qJAo26KT.js +1 -0
- package/dist/diff_tool/app/dist/assets/{shiki-CC72VX9t.js → shiki-B8l9Ufae.js} +2 -2
- package/dist/diff_tool/app/dist/assets/{stream-tokenizer-C6SDg59j.js → stream-tokenizer-h1lwRQEk.js} +1 -1
- package/dist/diff_tool/app/dist/assets/{themes-2vjCbM1J.js → themes-D3P9yN9_.js} +2 -2
- package/dist/diff_tool/app/dist/index.html +1 -1
- package/dist/host/hosted_ephemeral_agent_session.js +13 -3
- package/dist/host/hosted_ephemeral_agent_session.js.map +1 -1
- package/dist/host/local_session_host.js +5 -5
- package/dist/host/local_session_host.js.map +1 -1
- package/dist/sdk/code_mode.js +2 -2
- package/dist/sdk/code_mode.js.map +1 -1
- package/dist/store/session_snapshot_migrations.js +27 -1
- package/dist/store/session_snapshot_migrations.js.map +1 -1
- package/dist/tui/ui/subagent_panel.js +4 -1
- package/dist/tui/ui/subagent_panel.js.map +1 -1
- package/package.json +5 -5
- package/dist/core/static/prompts/subagent-wrapper.md +0 -20
- package/dist/core/tools/interrupt_agent.js +0 -142
- package/dist/core/tools/interrupt_agent.js.map +0 -1
- package/dist/core/tools/list_agents.js +0 -85
- package/dist/core/tools/list_agents.js.map +0 -1
- package/dist/core/tools/send_input_to_agent.js +0 -177
- package/dist/core/tools/send_input_to_agent.js.map +0 -1
- package/dist/core/tools/spawn_agent.js +0 -275
- package/dist/core/tools/spawn_agent.js.map +0 -1
- package/dist/diff_tool/app/dist/assets/index-Bv8JVYFD.js +0 -202
- package/dist/diff_tool/app/dist/assets/langs-DnGrwrZ-.js +0 -1
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { AgentRuntime, createAgentSpec } from "../agent/agent_runtime.js";
|
|
3
|
-
import { resolveAgentModel } from "../runtime/agent_model.js";
|
|
4
2
|
import { createDefaultCoreDeps } from "../runtime/deps.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { appendUsageLogEntry, getUsageCostTotal, getUsageTotals, } from "../usage/logs.js";
|
|
8
|
-
import { extractAssistantText } from "../utils/messages.js";
|
|
9
|
-
import { ClaudeCodeRuntime } from "./claude_code_runtime.js";
|
|
3
|
+
import { appendUsageLogEntry } from "../usage/logs.js";
|
|
4
|
+
import { AgentRequestError } from "./errors.js";
|
|
10
5
|
import { formatActiveSubagentsForCompaction } from "./format.js";
|
|
6
|
+
import { createSubagentRuntime } from "./runtime.js";
|
|
7
|
+
import { AgentTrace } from "./trace.js";
|
|
11
8
|
const MAX_ACTIVE_SUBAGENTS = 8;
|
|
12
9
|
async function raceWithAbort(promise, signal) {
|
|
13
10
|
if (!signal)
|
|
@@ -51,7 +48,7 @@ export class AgentSupervisor {
|
|
|
51
48
|
return [...this.records.values()].filter((record) => record.run.status === "running").length;
|
|
52
49
|
}
|
|
53
50
|
getActiveCompactionContext() {
|
|
54
|
-
const active = this.
|
|
51
|
+
const active = this.list().filter((state) => state.status !== "idle");
|
|
55
52
|
if (active.length === 0)
|
|
56
53
|
return undefined;
|
|
57
54
|
const state = formatActiveSubagentsForCompaction(active);
|
|
@@ -59,63 +56,25 @@ export class AgentSupervisor {
|
|
|
59
56
|
}
|
|
60
57
|
spawn(options) {
|
|
61
58
|
if (this.getActiveCount() >= MAX_ACTIVE_SUBAGENTS) {
|
|
62
|
-
|
|
63
|
-
ok: false,
|
|
64
|
-
reason: `Subagent limit reached (max ${MAX_ACTIVE_SUBAGENTS} active). Wait for existing agents to finish.`,
|
|
65
|
-
};
|
|
59
|
+
throw new AgentRequestError("operation_failed", `Subagent limit reached (max ${MAX_ACTIVE_SUBAGENTS} active). Wait for existing agents to finish.`);
|
|
66
60
|
}
|
|
67
61
|
const workingDirectory = options.runtimeConfig.workingDirectory.trim();
|
|
68
62
|
if (!workingDirectory) {
|
|
69
|
-
|
|
63
|
+
throw new AgentRequestError("invalid_arguments", "Subagent workingDirectory must not be blank.");
|
|
70
64
|
}
|
|
71
65
|
const id = randomUUID();
|
|
72
66
|
const runtimeConfig = { ...options.runtimeConfig, workingDirectory };
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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),
|
|
103
|
-
}),
|
|
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
|
-
}
|
|
67
|
+
const runtime = createSubagentRuntime(runtimeConfig, {
|
|
68
|
+
...options,
|
|
69
|
+
id,
|
|
70
|
+
deps: this.deps,
|
|
71
|
+
onEvent: async (event) => await this.recordRuntimeEvent(id, event),
|
|
72
|
+
});
|
|
110
73
|
const createdAt = this.deps.clock.now();
|
|
111
74
|
const record = {
|
|
112
75
|
id,
|
|
113
76
|
title: options.title,
|
|
114
|
-
model:
|
|
115
|
-
provider: runtimeConfig.model.provider,
|
|
116
|
-
id: runtimeConfig.model.id,
|
|
117
|
-
reasoning,
|
|
118
|
-
},
|
|
77
|
+
model: runtime.model,
|
|
119
78
|
workingDirectory,
|
|
120
79
|
createdAt,
|
|
121
80
|
originHistoryEntryId: options.originHistoryEntryId,
|
|
@@ -134,157 +93,185 @@ export class AgentSupervisor {
|
|
|
134
93
|
cacheRead: 0,
|
|
135
94
|
cacheWrite: 0,
|
|
136
95
|
contextWindowUsageTokens: 0,
|
|
137
|
-
contextWindow:
|
|
96
|
+
contextWindow: runtime.contextWindow,
|
|
138
97
|
},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
completion: Promise.resolve(undefined),
|
|
98
|
+
completion: Promise.resolve(),
|
|
99
|
+
trace: new AgentTrace(),
|
|
100
|
+
pending: [],
|
|
143
101
|
};
|
|
144
102
|
this.records.set(id, record);
|
|
103
|
+
record.trace.append({
|
|
104
|
+
kind: "input",
|
|
105
|
+
mode: "initial",
|
|
106
|
+
content: [{ type: "text", text: options.prompt }],
|
|
107
|
+
});
|
|
145
108
|
this.startRun(record, options.prompt, "subagent_spawned");
|
|
146
|
-
return
|
|
109
|
+
return this.read(record.id);
|
|
147
110
|
}
|
|
148
111
|
sendInput(options) {
|
|
149
|
-
const record = this.
|
|
150
|
-
if (
|
|
151
|
-
|
|
112
|
+
const record = this.requireRecord(options.id);
|
|
113
|
+
if (record.run.interruptRequested && record.run.status === "running")
|
|
114
|
+
throw new AgentRequestError("operation_failed", "Subagent is stopping");
|
|
115
|
+
if (record.run.status !== "running" && this.getActiveCount() >= MAX_ACTIVE_SUBAGENTS)
|
|
116
|
+
throw new AgentRequestError("operation_failed", "Subagent capacity reached");
|
|
117
|
+
if (record.pending.length + record.runtime.pendingSteering >= 64)
|
|
118
|
+
throw new AgentRequestError("operation_failed", "Subagent input queue is full");
|
|
119
|
+
record.trace.append({
|
|
120
|
+
kind: "input",
|
|
121
|
+
mode: options.mode,
|
|
122
|
+
content: [{ type: "text", text: options.prompt }],
|
|
123
|
+
});
|
|
152
124
|
if (record.run.status === "running") {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
125
|
+
if (options.mode === "steer")
|
|
126
|
+
record.runtime.steer(options.prompt);
|
|
127
|
+
else
|
|
128
|
+
record.pending.push({ prompt: options.prompt, mode: options.mode });
|
|
157
129
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
ok: false,
|
|
161
|
-
reason: `Subagent limit reached (max ${MAX_ACTIVE_SUBAGENTS} active). Wait for existing agents to finish.`,
|
|
162
|
-
};
|
|
130
|
+
else {
|
|
131
|
+
this.beginFollowup(record, options.prompt);
|
|
163
132
|
}
|
|
133
|
+
return this.read(record.id);
|
|
134
|
+
}
|
|
135
|
+
beginFollowup(record, prompt) {
|
|
136
|
+
record.lastAssistantSeq = undefined;
|
|
164
137
|
record.run = {
|
|
165
138
|
revision: record.run.revision + 1,
|
|
166
139
|
status: "running",
|
|
167
140
|
startedAt: this.deps.clock.now(),
|
|
168
141
|
interruptRequested: false,
|
|
169
142
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
143
|
+
this.startRun(record, prompt, "subagent_run_started");
|
|
144
|
+
}
|
|
145
|
+
read(id) {
|
|
146
|
+
const record = this.requireRecord(id);
|
|
147
|
+
const { run } = record;
|
|
148
|
+
return {
|
|
149
|
+
id,
|
|
150
|
+
title: record.title,
|
|
151
|
+
model: `${record.model.provider}/${record.model.id}:${record.model.reasoning}`,
|
|
152
|
+
workingDirectory: record.workingDirectory,
|
|
153
|
+
...(run.status === "running"
|
|
154
|
+
? { status: run.interruptRequested ? "stopping" : "working" }
|
|
155
|
+
: run.status === "failed"
|
|
156
|
+
? {
|
|
157
|
+
status: "idle",
|
|
158
|
+
reason: "failed",
|
|
159
|
+
error: { code: run.failure.kind, message: run.failure.message },
|
|
160
|
+
}
|
|
161
|
+
: { status: "idle", reason: run.status === "succeeded" ? "completed" : "interrupted" }),
|
|
162
|
+
pending: {
|
|
163
|
+
queue: record.pending.filter((input) => input.mode === "queue").length,
|
|
164
|
+
steer: record.runtime.pendingSteering +
|
|
165
|
+
record.pending.filter((input) => input.mode === "steer").length,
|
|
166
|
+
},
|
|
167
|
+
usage: {
|
|
168
|
+
input: record.usage.input,
|
|
169
|
+
output: record.usage.output,
|
|
170
|
+
cacheRead: record.usage.cacheRead,
|
|
171
|
+
cacheWrite: record.usage.cacheWrite,
|
|
172
|
+
contextTokens: record.usage.contextWindowUsageTokens,
|
|
173
|
+
contextWindow: record.usage.contextWindow,
|
|
174
|
+
cost: record.costTotal,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
getTrace(id) {
|
|
179
|
+
const record = this.requireRecord(id);
|
|
180
|
+
return record.trace;
|
|
174
181
|
}
|
|
175
182
|
async waitForAgents(ids, signal) {
|
|
176
183
|
const missing = ids.filter((id) => !this.records.has(id));
|
|
177
184
|
if (missing.length > 0)
|
|
178
|
-
throw new
|
|
185
|
+
throw new AgentRequestError("unknown_agent", `Unknown subagent ID(s): ${missing.join(", ")}`);
|
|
179
186
|
await raceWithAbort(Promise.race(ids.map((id) => this.waitForRecord(id))), signal);
|
|
180
|
-
return ids.map((id) =>
|
|
181
|
-
const record = this.records.get(id);
|
|
182
|
-
if (!record)
|
|
183
|
-
throw new Error(`Unknown subagent ID: ${id}`);
|
|
184
|
-
return this.toSnapshot(record);
|
|
185
|
-
});
|
|
187
|
+
return ids.map((id) => this.read(id));
|
|
186
188
|
}
|
|
187
|
-
async interrupt(id, signal) {
|
|
188
|
-
const record = this.
|
|
189
|
-
if (!record)
|
|
190
|
-
return undefined;
|
|
189
|
+
async interrupt(id, signal, wait = true) {
|
|
190
|
+
const record = this.requireRecord(id);
|
|
191
191
|
if (record.run.status === "running") {
|
|
192
|
+
if (record.pending.length || record.runtime.pendingSteering)
|
|
193
|
+
record.trace.append({
|
|
194
|
+
kind: "notice",
|
|
195
|
+
code: "input.discarded",
|
|
196
|
+
content: [{ type: "text", text: "Pending inputs discarded by interruption." }],
|
|
197
|
+
});
|
|
198
|
+
record.pending = [];
|
|
192
199
|
record.run.interruptRequested = true;
|
|
200
|
+
record.trace.append({ kind: "status", status: "stopping" });
|
|
193
201
|
record.runtime.interrupt();
|
|
194
202
|
await this.emit({ type: "subagent_interrupt_requested", state: this.toSnapshot(record) });
|
|
195
203
|
}
|
|
196
|
-
|
|
204
|
+
if (wait)
|
|
205
|
+
await raceWithAbort(this.waitForRecord(id), signal);
|
|
206
|
+
return this.read(id);
|
|
207
|
+
}
|
|
208
|
+
getLastResponse(id) {
|
|
209
|
+
const record = this.requireRecord(id);
|
|
210
|
+
const seq = record.lastAssistantSeq;
|
|
211
|
+
if (record.run.status === "running" || seq === undefined)
|
|
212
|
+
return undefined;
|
|
213
|
+
try {
|
|
214
|
+
return { seq, ...record.trace.readText(seq, Number.POSITIVE_INFINITY) };
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
if (!(error instanceof AgentRequestError) || error.code !== "expired_content")
|
|
218
|
+
throw error;
|
|
219
|
+
return { seq, text: "", complete: false };
|
|
220
|
+
}
|
|
197
221
|
}
|
|
198
222
|
getSnapshot(id) {
|
|
199
223
|
const record = this.records.get(id);
|
|
200
224
|
return record ? this.toSnapshot(record) : undefined;
|
|
201
225
|
}
|
|
202
|
-
listSnapshots() {
|
|
203
|
-
return [...this.records.values()].map((record) => this.toSnapshot(record));
|
|
204
|
-
}
|
|
205
226
|
startRun(record, prompt, eventType) {
|
|
206
|
-
record.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.
|
|
210
|
-
|
|
211
|
-
else if (record.runtime instanceof ClaudeCodeRuntime) {
|
|
212
|
-
const completion = await record.runtime.submit(prompt);
|
|
213
|
-
if (record.run.interruptRequested || completion.status === "interrupted") {
|
|
227
|
+
record.trace.append({ kind: "status", status: "working" });
|
|
228
|
+
record.completion = (async () => {
|
|
229
|
+
try {
|
|
230
|
+
await this.emit({ type: eventType, state: this.toSnapshot(record) });
|
|
231
|
+
if (record.run.interruptRequested) {
|
|
214
232
|
this.finishInterrupted(record);
|
|
215
233
|
}
|
|
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
234
|
else {
|
|
224
|
-
|
|
235
|
+
const { outcome, unconsumed } = await record.runtime.submit(prompt);
|
|
236
|
+
if (record.run.interruptRequested || outcome.status === "interrupted") {
|
|
237
|
+
this.finishInterrupted(record);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
record.pending.push(...unconsumed.map((prompt) => ({ prompt, mode: "steer" })));
|
|
241
|
+
if (outcome.status === "failed")
|
|
242
|
+
this.finishFailed(record, outcome.failure);
|
|
243
|
+
else
|
|
244
|
+
this.finishSucceeded(record, outcome.response);
|
|
245
|
+
}
|
|
225
246
|
}
|
|
226
247
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
248
|
+
catch (error) {
|
|
249
|
+
if (record.run.interruptRequested)
|
|
250
|
+
this.finishInterrupted(record);
|
|
251
|
+
else
|
|
252
|
+
this.finishFailed(record, {
|
|
253
|
+
kind: "runtime-error",
|
|
254
|
+
message: error instanceof Error ? error.message : String(error),
|
|
255
|
+
});
|
|
235
256
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
257
|
+
if (this.records.get(record.id) === record) {
|
|
258
|
+
const next = record.pending.shift();
|
|
259
|
+
if (next && !record.run.interruptRequested) {
|
|
260
|
+
this.beginFollowup(record, next.prompt);
|
|
261
|
+
return await record.completion;
|
|
262
|
+
}
|
|
263
|
+
record.trace.append({
|
|
264
|
+
kind: "status",
|
|
265
|
+
status: "idle",
|
|
266
|
+
reason: record.run.status === "succeeded"
|
|
267
|
+
? "completed"
|
|
268
|
+
: record.run.status === "interrupted"
|
|
269
|
+
? "interrupted"
|
|
270
|
+
: "failed",
|
|
240
271
|
});
|
|
272
|
+
await this.emit({ type: "subagent_finished", state: this.toSnapshot(record) });
|
|
241
273
|
}
|
|
242
|
-
|
|
243
|
-
})
|
|
244
|
-
.then(async (resolved) => {
|
|
245
|
-
if (this.records.get(resolved.id) === resolved) {
|
|
246
|
-
await this.emit({ type: "subagent_finished", state: this.toSnapshot(resolved) });
|
|
247
|
-
}
|
|
248
|
-
return resolved;
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
applyTurnResult(record, result) {
|
|
252
|
-
if (record.run.interruptRequested ||
|
|
253
|
-
result.aborted ||
|
|
254
|
-
result.finalMessage?.stopReason === "aborted") {
|
|
255
|
-
this.finishInterrupted(record);
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (result.blocked) {
|
|
259
|
-
this.finishFailed(record, {
|
|
260
|
-
kind: result.blocked.reason,
|
|
261
|
-
message: result.blocked.message,
|
|
262
|
-
});
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (result.limitReached) {
|
|
266
|
-
this.finishFailed(record, {
|
|
267
|
-
kind: result.limitReached.reason,
|
|
268
|
-
message: result.limitReached.message,
|
|
269
|
-
});
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
if (!result.finalMessage) {
|
|
273
|
-
this.finishFailed(record, {
|
|
274
|
-
kind: "runtime-error",
|
|
275
|
-
message: "Subagent run completed without a final assistant message.",
|
|
276
|
-
});
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
if (result.finalMessage.stopReason === "error") {
|
|
280
|
-
this.finishFailed(record, {
|
|
281
|
-
kind: "provider-error",
|
|
282
|
-
message: result.finalMessage.errorMessage ?? "model returned an unspecified error.",
|
|
283
|
-
stopReason: result.finalMessage.stopReason,
|
|
284
|
-
});
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
this.finishSucceeded(record, extractAssistantText(result.finalMessage).trim());
|
|
274
|
+
})();
|
|
288
275
|
}
|
|
289
276
|
finishSucceeded(record, response) {
|
|
290
277
|
record.run = {
|
|
@@ -307,6 +294,11 @@ export class AgentSupervisor {
|
|
|
307
294
|
};
|
|
308
295
|
}
|
|
309
296
|
finishFailed(record, failure) {
|
|
297
|
+
record.trace.append({
|
|
298
|
+
kind: "notice",
|
|
299
|
+
code: failure.kind,
|
|
300
|
+
content: [{ type: "text", text: failure.message }],
|
|
301
|
+
});
|
|
310
302
|
record.run = {
|
|
311
303
|
revision: record.run.revision,
|
|
312
304
|
status: "failed",
|
|
@@ -316,203 +308,57 @@ export class AgentSupervisor {
|
|
|
316
308
|
failure,
|
|
317
309
|
};
|
|
318
310
|
}
|
|
319
|
-
async
|
|
311
|
+
async recordRuntimeEvent(id, event) {
|
|
320
312
|
const record = this.records.get(id);
|
|
321
313
|
if (!record)
|
|
322
314
|
return;
|
|
323
|
-
if (event.type === "
|
|
324
|
-
|
|
325
|
-
if (event.
|
|
326
|
-
|
|
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) });
|
|
315
|
+
if (event.type === "trace") {
|
|
316
|
+
const seq = record.trace.append(event.event);
|
|
317
|
+
if (event.event.kind === "assistant")
|
|
318
|
+
record.lastAssistantSeq = seq;
|
|
385
319
|
}
|
|
386
|
-
else if (event.type === "
|
|
320
|
+
else if (event.type === "activity") {
|
|
387
321
|
await this.emit({
|
|
388
322
|
type: "subagent_activity",
|
|
389
323
|
state: this.toSnapshot(record),
|
|
390
|
-
activity:
|
|
324
|
+
activity: event.activity,
|
|
391
325
|
});
|
|
392
326
|
}
|
|
393
|
-
else
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
}
|
|
412
|
-
async recordAgentEvent(id, event) {
|
|
413
|
-
const record = this.records.get(id);
|
|
414
|
-
if (!record)
|
|
415
|
-
return;
|
|
416
|
-
if (event.type === "assistant_final") {
|
|
417
|
-
const usage = getUsageTotals(event.message.usage);
|
|
418
|
-
const cost = getUsageCostTotal(event.message.usage);
|
|
419
|
-
record.costTotal += cost;
|
|
327
|
+
else {
|
|
328
|
+
const { usage, cost } = event.entry;
|
|
329
|
+
record.costTotal += cost.total;
|
|
420
330
|
record.usage = {
|
|
421
331
|
input: record.usage.input + usage.input,
|
|
422
332
|
output: record.usage.output + usage.output,
|
|
423
333
|
cacheRead: record.usage.cacheRead + usage.cacheRead,
|
|
424
334
|
cacheWrite: record.usage.cacheWrite + usage.cacheWrite,
|
|
425
|
-
contextWindowUsageTokens:
|
|
426
|
-
contextWindow:
|
|
335
|
+
contextWindowUsageTokens: event.contextTokens,
|
|
336
|
+
contextWindow: event.contextWindow,
|
|
427
337
|
};
|
|
428
338
|
(this.options.recordUsage ?? appendUsageLogEntry)({
|
|
429
|
-
|
|
339
|
+
...event.entry,
|
|
430
340
|
sessionId: record.id,
|
|
431
|
-
personaId: record.personaId ?? event.personaId,
|
|
432
|
-
provider: event.message.provider,
|
|
433
|
-
model: event.message.model,
|
|
434
|
-
api: event.message.api,
|
|
435
|
-
reasoningEffort: event.reasoningEffort,
|
|
436
|
-
usage,
|
|
437
|
-
cost: { total: cost },
|
|
341
|
+
personaId: record.personaId ?? event.entry.personaId,
|
|
438
342
|
agent: { type: "subagent" },
|
|
439
343
|
});
|
|
440
344
|
await this.emit({ type: "subagent_updated", state: this.toSnapshot(record) });
|
|
441
|
-
const text = extractAssistantText(event.message).trim();
|
|
442
|
-
if (text) {
|
|
443
|
-
await this.emit({
|
|
444
|
-
type: "subagent_activity",
|
|
445
|
-
state: this.toSnapshot(record),
|
|
446
|
-
activity: { type: "assistant", text },
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (event.type === "tool_activity") {
|
|
452
|
-
record.toolPresentations.set(event.activity.toolCallId, event.activity.presentation);
|
|
453
|
-
const blockedToolName = record.blockedToolCalls.get(event.activity.toolCallId);
|
|
454
|
-
if (blockedToolName) {
|
|
455
|
-
record.blockedToolCalls.delete(event.activity.toolCallId);
|
|
456
|
-
await this.emitSettledToolActivity(record, blockedToolName, "blocked", event.activity.presentation);
|
|
457
|
-
record.toolPresentations.delete(event.activity.toolCallId);
|
|
458
|
-
}
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
if (event.type === "tool_run_blocked") {
|
|
462
|
-
const presentation = record.toolPresentations.get(event.toolCallId);
|
|
463
|
-
if (!presentation) {
|
|
464
|
-
record.blockedToolCalls.set(event.toolCallId, event.toolName);
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
record.toolPresentations.delete(event.toolCallId);
|
|
468
|
-
await this.emitSettledToolActivity(record, event.toolName, "blocked", presentation);
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
if (event.type === "tool_run_finished") {
|
|
472
|
-
const presentation = record.toolPresentations.get(event.toolCallId);
|
|
473
|
-
if (!presentation) {
|
|
474
|
-
throw new Error(`missing presentation for settled tool call '${event.toolCallId}'`);
|
|
475
|
-
}
|
|
476
|
-
record.toolPresentations.delete(event.toolCallId);
|
|
477
|
-
await this.emitSettledToolActivity(record, event.toolName, event.outcome, presentation);
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
if (event.type === "feedback") {
|
|
481
|
-
await this.emit({
|
|
482
|
-
type: "subagent_activity",
|
|
483
|
-
state: this.toSnapshot(record),
|
|
484
|
-
activity: {
|
|
485
|
-
type: "notice",
|
|
486
|
-
severity: event.tone === "error" ? "error" : "info",
|
|
487
|
-
title: event.title,
|
|
488
|
-
...(event.presentation === "transcript" && event.content
|
|
489
|
-
? { content: event.content }
|
|
490
|
-
: {}),
|
|
491
|
-
},
|
|
492
|
-
});
|
|
493
345
|
}
|
|
494
346
|
}
|
|
495
|
-
|
|
496
|
-
const { actionByStatus, ...terminalPresentation } = presentation;
|
|
497
|
-
await this.emit({
|
|
498
|
-
type: "subagent_activity",
|
|
499
|
-
state: this.toSnapshot(record),
|
|
500
|
-
activity: {
|
|
501
|
-
type: "tool",
|
|
502
|
-
toolName,
|
|
503
|
-
outcome,
|
|
504
|
-
presentation: {
|
|
505
|
-
action: actionByStatus[outcome],
|
|
506
|
-
...structuredClone(terminalPresentation),
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
async waitForRecord(id) {
|
|
347
|
+
requireRecord(id) {
|
|
512
348
|
const record = this.records.get(id);
|
|
513
349
|
if (!record)
|
|
514
|
-
throw new
|
|
515
|
-
return
|
|
350
|
+
throw new AgentRequestError("unknown_agent", `Unknown subagent ID: ${id}`);
|
|
351
|
+
return record;
|
|
352
|
+
}
|
|
353
|
+
list() {
|
|
354
|
+
return [...this.records.keys()].map((id) => this.read(id));
|
|
355
|
+
}
|
|
356
|
+
async waitForRecord(id) {
|
|
357
|
+
const record = this.requireRecord(id);
|
|
358
|
+
do {
|
|
359
|
+
await record.completion;
|
|
360
|
+
} while (record.run.status === "running");
|
|
361
|
+
return record;
|
|
516
362
|
}
|
|
517
363
|
toSnapshot(record) {
|
|
518
364
|
return {
|