@shanepadgett/tau-agent 0.33.1 → 0.35.0
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/docs/context.md +29 -20
- package/docs/extending-tau-agent.md +63 -24
- package/extensions/appshot/index.ts +112 -111
- package/extensions/aside/README.md +7 -0
- package/extensions/aside/index.ts +168 -0
- package/extensions/aside/panel.ts +83 -0
- package/extensions/bash-approval/README.md +20 -0
- package/extensions/bash-approval/index.ts +235 -0
- package/extensions/bash-approval/settings.ts +24 -0
- package/extensions/branch/index.ts +38 -21
- package/extensions/cache-diagnostics/index.ts +70 -58
- package/extensions/checkpoint/README.md +9 -0
- package/extensions/checkpoint/checkpoint-budget.ts +66 -0
- package/extensions/checkpoint/checkpoint.ts +279 -0
- package/extensions/checkpoint/index.ts +102 -0
- package/extensions/checkpoint/messages.ts +170 -0
- package/extensions/checkpoint/prompt.ts +24 -0
- package/extensions/checkpoint/settings.ts +27 -0
- package/extensions/commit/commit-effort.ts +3 -3
- package/extensions/commit/commit-plan.ts +2 -2
- package/extensions/context/README.md +12 -8
- package/extensions/context/definitions.ts +178 -55
- package/extensions/context/index.ts +192 -160
- package/extensions/context/panel.ts +57 -37
- package/extensions/context/sync.ts +139 -89
- package/extensions/context/validation.ts +37 -25
- package/extensions/effort/README.md +1 -1
- package/extensions/effort/index.ts +3 -3
- package/extensions/effort/state.ts +3 -18
- package/extensions/explore/guidance.ts +6 -47
- package/extensions/explore/index.ts +38 -53
- package/extensions/explore/{ast/read → read}/hook.ts +12 -7
- package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
- package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
- package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
- package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
- package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
- package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
- package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
- package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
- package/extensions/explore/tools/target-params.ts +9 -0
- package/extensions/footer/index.ts +75 -56
- package/extensions/handoff/index.ts +8 -30
- package/extensions/image-gen/index.ts +46 -40
- package/extensions/patch/executor.ts +190 -135
- package/extensions/patch/matcher.ts +82 -48
- package/extensions/patch/parser.ts +149 -105
- package/extensions/patch/render.ts +32 -39
- package/extensions/patch/summary.ts +30 -26
- package/extensions/ready/README.md +7 -0
- package/extensions/ready/index.ts +58 -0
- package/extensions/ready/model.ts +128 -0
- package/extensions/ready/packs.ts +272 -0
- package/extensions/ready/render.ts +445 -0
- package/extensions/ready/scan.ts +760 -0
- package/extensions/reference/index.ts +2 -0
- package/extensions/reference/panel.ts +94 -31
- package/extensions/review/README.md +14 -6
- package/extensions/review/index.ts +58 -99
- package/extensions/review/model.ts +13 -33
- package/extensions/review/session.ts +9 -23
- package/extensions/runtime-context/context.ts +30 -26
- package/extensions/script-runner/README.md +2 -2
- package/extensions/script-runner/index.ts +122 -82
- package/extensions/silent-command-runner/index.ts +38 -23
- package/extensions/soul/README.md +2 -2
- package/extensions/soul/index.ts +4 -4
- package/extensions/soul/prompt.ts +6 -10
- package/extensions/soul/settings.ts +6 -3
- package/extensions/subagent/agents/context-sync.md +185 -33
- package/extensions/subagent/agents/scout.md +2 -2
- package/extensions/subagent/agents/web-research.md +2 -2
- package/extensions/subagent/agents.ts +137 -84
- package/extensions/subagent/cmux-dashboard.ts +39 -30
- package/extensions/subagent/index.ts +79 -59
- package/extensions/subagent/resume.ts +1 -1
- package/extensions/subagent/run.ts +308 -193
- package/extensions/subagent/runtime.ts +781 -343
- package/extensions/tau/index.ts +47 -38
- package/extensions/tau-help/help.md +20 -8
- package/extensions/tool-loader/README.md +4 -2
- package/extensions/tool-loader/index.ts +77 -52
- package/extensions/web/index.ts +6 -3
- package/extensions/web/webfetch.ts +114 -86
- package/package.json +3 -3
- package/schemas/tau.schema.json +38 -35
- package/shared/bounded-text-result.ts +1 -0
- package/shared/checkpoint-visibility.ts +9 -0
- package/shared/events.ts +28 -10
- package/shared/isolated-session.ts +72 -30
- package/shared/model-effort.ts +23 -22
- package/shared/model-fallback/index.ts +66 -41
- package/shared/settings/load.ts +45 -38
- package/shared/temporary-output-store.ts +16 -10
- package/{extensions/explore → src}/ast/adapter.ts +11 -2
- package/{extensions/explore → src}/ast/engine.ts +54 -38
- package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
- package/{extensions/explore → src}/ast/format/context.ts +3 -6
- package/src/ast/format/deps.ts +94 -0
- package/{extensions/explore → src}/ast/format/discover.ts +2 -2
- package/{extensions/explore → src}/ast/format/impact.ts +9 -11
- package/{extensions/explore → src}/ast/format/outline.ts +2 -2
- package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
- package/{extensions/explore → src}/ast/format/show.ts +1 -1
- package/src/ast/format/targeted-result.ts +20 -0
- package/src/ast/grammars/c_sharp.wasm +0 -0
- package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
- package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
- package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
- package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
- package/{extensions/explore → src}/ast/ir.ts +6 -0
- package/src/ast/languages/csharp-file-deps.ts +90 -0
- package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
- package/src/ast/languages/dotted-index.ts +131 -0
- package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
- package/src/ast/languages/fixtures/sample.cs +53 -0
- package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
- package/src/ast/languages/fixtures/sample.go +37 -0
- package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
- package/src/ast/languages/fixtures/sample.java +40 -0
- package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
- package/src/ast/languages/fixtures/sample.kt +31 -0
- package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
- package/src/ast/languages/fixtures/sample.md +23 -0
- package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
- package/src/ast/languages/fixtures/sample.rs +54 -0
- package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
- package/src/ast/languages/fixtures/sample.swift +45 -0
- package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
- package/src/ast/languages/fixtures/sample.ts +45 -0
- package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
- package/src/ast/languages/fixtures/sample.tsx +36 -0
- package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
- package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
- package/{extensions/explore → src}/ast/languages/go.ts +21 -3
- package/{extensions/explore → src}/ast/languages/java.ts +5 -38
- package/src/ast/languages/jvm-file-deps.ts +135 -0
- package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
- package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
- package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
- package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
- package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
- package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
- package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
- package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
- package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
- package/{extensions/explore → src}/ast/markdown.ts +72 -47
- package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
- package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
- package/{extensions/explore → src}/ast/queries/context.ts +116 -80
- package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
- package/src/ast/queries/impact.ts +291 -0
- package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
- package/{extensions/explore → src}/ast/queries/show.ts +46 -42
- package/{extensions/explore → src}/ast/query.ts +1 -1
- package/{extensions/explore → src}/ast/registry.ts +1 -2
- package/{extensions/explore → src}/ast/scan.ts +1 -1
- package/src/ast/session.ts +50 -0
- package/{extensions/explore → src}/ast/slice.ts +8 -0
- package/{extensions/explore → src/ast}/traverse.ts +32 -20
- package/src/file-injection/index.ts +547 -0
- package/src/image-generation/client.ts +50 -31
- package/src/image-generation/index.ts +177 -134
- package/src/index.ts +2 -0
- package/src/tool-loading/index.ts +58 -0
- package/extensions/context/evidence.ts +0 -517
- package/extensions/context/projection.ts +0 -141
- package/extensions/context/state.ts +0 -30
- package/extensions/context/write-scope.ts +0 -109
- package/extensions/explore/ast/format/deps.ts +0 -86
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
- package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
- package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
- package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
- package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
- package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
- package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
- package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
- package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
- package/extensions/explore/ast/languages/odin.ts +0 -407
- package/extensions/explore/ast/queries/impact.ts +0 -250
- package/extensions/explore/outline-injection.ts +0 -151
- package/extensions/explore/read/autoread.ts +0 -118
- package/extensions/review/panel.ts +0 -128
- package/extensions/working-memory/README.md +0 -17
- package/extensions/working-memory/checkpoint.ts +0 -242
- package/extensions/working-memory/index.ts +0 -341
- package/extensions/working-memory/memory.ts +0 -235
- package/extensions/working-memory/render.ts +0 -185
- package/extensions/working-memory/settings.ts +0 -38
- package/extensions/working-memory/state.ts +0 -158
- package/shared/autoread.ts +0 -234
- package/shared/context-messages.ts +0 -19
- package/shared/outline-injection.ts +0 -56
- /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
- /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
- /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
- /package/{extensions/explore → src}/ast/identity.ts +0 -0
- /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
- /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
} from "@earendil-works/pi-coding-agent";
|
|
15
15
|
import { createCompleteFileMeta } from "../../shared/full-file-knowledge.ts";
|
|
16
16
|
import { createIsolatedSessionResource, type IsolatedSessionResource } from "../../shared/isolated-session.ts";
|
|
17
|
+
import { FILE_INJECTION_TYPE } from "@shanepadgett/tau-agent";
|
|
17
18
|
import type { AgentDefinition } from "./agents.ts";
|
|
18
19
|
import { emptySubagentResumeState, type RetainedTurnOutcome, type SubagentResumeState } from "./resume.ts";
|
|
19
20
|
import { resolveSubagentSessionInputs, type SubagentSessionInputs } from "./session-resource.ts";
|
|
20
21
|
|
|
21
22
|
const PREVIEW_LIMIT = 600;
|
|
22
23
|
const VALUE_LIMIT = 180;
|
|
23
|
-
const AUTOREAD_MESSAGE_TYPE = "tau.autoread";
|
|
24
24
|
|
|
25
25
|
export interface SubagentAction {
|
|
26
26
|
tool: string;
|
|
@@ -163,7 +163,7 @@ function capped(value: unknown, limit = VALUE_LIMIT): string {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/** Rolling tail so live previews keep changing after the limit. */
|
|
166
|
-
|
|
166
|
+
function cappedTail(value: unknown, limit = PREVIEW_LIMIT): string {
|
|
167
167
|
let text: string;
|
|
168
168
|
try {
|
|
169
169
|
text = typeof value === "string" ? value : (JSON.stringify(value) ?? String(value));
|
|
@@ -248,6 +248,289 @@ export async function disposeSubagentThread(thread: SubagentThread): Promise<voi
|
|
|
248
248
|
await thread.resource.dispose();
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
function emptyTurnUsage(): { usage: SubagentUsage; toolUsage: Usage } {
|
|
252
|
+
return {
|
|
253
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
|
|
254
|
+
toolUsage: {
|
|
255
|
+
input: 0,
|
|
256
|
+
output: 0,
|
|
257
|
+
cacheRead: 0,
|
|
258
|
+
cacheWrite: 0,
|
|
259
|
+
totalTokens: 0,
|
|
260
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function accumulateAssistantUsage(usage: SubagentUsage, toolUsage: Usage, message: AssistantMessage): void {
|
|
266
|
+
usage.input += message.usage.input;
|
|
267
|
+
usage.output += message.usage.output;
|
|
268
|
+
usage.cacheRead += message.usage.cacheRead;
|
|
269
|
+
usage.cacheWrite += message.usage.cacheWrite;
|
|
270
|
+
usage.cost += message.usage.cost.total;
|
|
271
|
+
usage.turns += 1;
|
|
272
|
+
toolUsage.input += message.usage.input;
|
|
273
|
+
toolUsage.output += message.usage.output;
|
|
274
|
+
toolUsage.cacheRead += message.usage.cacheRead;
|
|
275
|
+
toolUsage.cacheWrite += message.usage.cacheWrite;
|
|
276
|
+
toolUsage.totalTokens += message.usage.totalTokens;
|
|
277
|
+
toolUsage.cost.input += message.usage.cost.input;
|
|
278
|
+
toolUsage.cost.output += message.usage.cost.output;
|
|
279
|
+
toolUsage.cost.cacheRead += message.usage.cost.cacheRead;
|
|
280
|
+
toolUsage.cost.cacheWrite += message.usage.cost.cacheWrite;
|
|
281
|
+
toolUsage.cost.total += message.usage.cost.total;
|
|
282
|
+
if (message.usage.cacheWrite1h !== undefined)
|
|
283
|
+
toolUsage.cacheWrite1h = (toolUsage.cacheWrite1h ?? 0) + message.usage.cacheWrite1h;
|
|
284
|
+
if (message.usage.reasoning !== undefined)
|
|
285
|
+
toolUsage.reasoning = (toolUsage.reasoning ?? 0) + message.usage.reasoning;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function pushBoundedAction(details: SubagentDetails, action: SubagentAction): void {
|
|
289
|
+
details.actions.push(action);
|
|
290
|
+
if (details.actions.length <= 20) return;
|
|
291
|
+
const removable = details.actions.findIndex((item) => !item.error);
|
|
292
|
+
const [removed] = details.actions.splice(removable >= 0 ? removable : 0, 1);
|
|
293
|
+
details.omittedActions += 1;
|
|
294
|
+
if (removed?.error) details.omittedErrors += 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function subscribeTurnEvents(options: {
|
|
298
|
+
session: SubagentThread["resource"]["session"];
|
|
299
|
+
details: SubagentDetails;
|
|
300
|
+
usage: SubagentUsage;
|
|
301
|
+
toolUsage: Usage;
|
|
302
|
+
turnMessages: AssistantMessage[];
|
|
303
|
+
assistantMessageEndAt: number[];
|
|
304
|
+
publish: (force?: boolean) => void;
|
|
305
|
+
}): () => void {
|
|
306
|
+
const { session, details, usage, toolUsage, turnMessages, assistantMessageEndAt, publish } = options;
|
|
307
|
+
const actionById = new Map<string, string>();
|
|
308
|
+
return session.subscribe((event: AgentSessionEvent) => {
|
|
309
|
+
if (event.type === "message_start" && event.message.role === "assistant") {
|
|
310
|
+
details.response = "";
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
314
|
+
details.response = cappedTail(details.response + event.assistantMessageEvent.delta, PREVIEW_LIMIT);
|
|
315
|
+
publish();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (event.type === "message_end" && event.message.role === "assistant") {
|
|
319
|
+
turnMessages.push(event.message);
|
|
320
|
+
assistantMessageEndAt.push(Date.now());
|
|
321
|
+
accumulateAssistantUsage(usage, toolUsage, event.message);
|
|
322
|
+
details.response = cappedTail(textOf(event.message), PREVIEW_LIMIT);
|
|
323
|
+
details.currentActivity = undefined;
|
|
324
|
+
publish(true);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (event.type === "tool_execution_start") {
|
|
328
|
+
details.toolCalls += 1;
|
|
329
|
+
const summary = `${event.toolName} ${capped(event.args)}`.trim();
|
|
330
|
+
actionById.set(event.toolCallId, summary);
|
|
331
|
+
details.currentActivity = summary;
|
|
332
|
+
publish(true);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (event.type === "tool_execution_end") {
|
|
336
|
+
pushBoundedAction(details, {
|
|
337
|
+
tool: event.toolName,
|
|
338
|
+
summary: actionById.get(event.toolCallId) ?? event.toolName,
|
|
339
|
+
error: event.isError,
|
|
340
|
+
});
|
|
341
|
+
details.currentActivity = undefined;
|
|
342
|
+
publish(true);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function buildAutoreadMessages(options: { files: readonly string[]; cwd: string; invocationId: string }): Promise<
|
|
348
|
+
Array<{
|
|
349
|
+
customType: typeof FILE_INJECTION_TYPE;
|
|
350
|
+
content: string;
|
|
351
|
+
display: false;
|
|
352
|
+
details: Record<string, unknown>;
|
|
353
|
+
}>
|
|
354
|
+
> {
|
|
355
|
+
const { files, cwd, invocationId } = options;
|
|
356
|
+
return Promise.all(
|
|
357
|
+
files.map(async (file, index) => {
|
|
358
|
+
const pathKey = resolve(cwd, file);
|
|
359
|
+
const common = {
|
|
360
|
+
v: 1 as const,
|
|
361
|
+
rowId: `${invocationId}:${index}`,
|
|
362
|
+
path: file,
|
|
363
|
+
cwd,
|
|
364
|
+
source: "subagent",
|
|
365
|
+
batchId: invocationId,
|
|
366
|
+
};
|
|
367
|
+
try {
|
|
368
|
+
const bytes = await readFile(pathKey);
|
|
369
|
+
const content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
370
|
+
const lines = content.split("\n");
|
|
371
|
+
const numbered = lines.map((line, lineIndex) => `${lineIndex + 1}: ${line}`).join("\n");
|
|
372
|
+
return {
|
|
373
|
+
customType: FILE_INJECTION_TYPE,
|
|
374
|
+
content: `${file}\n${numbered}`,
|
|
375
|
+
display: false as const,
|
|
376
|
+
details: {
|
|
377
|
+
...common,
|
|
378
|
+
kind: "full",
|
|
379
|
+
status: "injected",
|
|
380
|
+
readCache: createCompleteFileMeta({
|
|
381
|
+
pathKey,
|
|
382
|
+
presentation: "line-numbered",
|
|
383
|
+
servedHash: createHash("sha256").update(bytes).digest("hex"),
|
|
384
|
+
mode: "baseline",
|
|
385
|
+
sourceText: content,
|
|
386
|
+
returnedText: `${file}\n${numbered}`,
|
|
387
|
+
totalLines: lines.length,
|
|
388
|
+
summary: `${lines.length} lines`,
|
|
389
|
+
}),
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
} catch (error) {
|
|
393
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
394
|
+
return {
|
|
395
|
+
customType: FILE_INJECTION_TYPE,
|
|
396
|
+
content: `${file}\nInjection failed: ${message}`,
|
|
397
|
+
display: false as const,
|
|
398
|
+
details: { ...common, kind: "full", status: "failed", error: message },
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}),
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function buildTurnPrompt(options: {
|
|
406
|
+
definition: AgentDefinition;
|
|
407
|
+
task: string;
|
|
408
|
+
files: readonly string[];
|
|
409
|
+
initial: boolean;
|
|
410
|
+
resumePrompt: string | undefined;
|
|
411
|
+
}): string {
|
|
412
|
+
const { definition, task, files, initial, resumePrompt } = options;
|
|
413
|
+
if (resumePrompt !== undefined) return resumePrompt;
|
|
414
|
+
const fileNote = files.length
|
|
415
|
+
? " Parent-supplied autoread files are included as line-numbered context for this turn."
|
|
416
|
+
: "";
|
|
417
|
+
if (initial) {
|
|
418
|
+
return `You are an isolated delegated child agent. Stay within the delegated task and return only the requested result.${fileNote}\n\n## Agent instructions\n${definition.prompt}\n\n## Delegated task\n${task}`;
|
|
419
|
+
}
|
|
420
|
+
return `Continue the existing delegated work using the context already in this thread. Return only the requested result.${fileNote}\n\n## Parent follow-up\n${task}`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async function applyTerminalOutcome(
|
|
424
|
+
details: SubagentDetails,
|
|
425
|
+
terminal: AssistantMessage,
|
|
426
|
+
definitionName: string,
|
|
427
|
+
): Promise<void> {
|
|
428
|
+
const response = textOf(terminal);
|
|
429
|
+
if (terminal.stopReason === "aborted") {
|
|
430
|
+
details.status = "aborted";
|
|
431
|
+
details.completionState = "aborted";
|
|
432
|
+
details.error = capped(terminal.errorMessage ?? "Child aborted");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (terminal.stopReason === "error") {
|
|
436
|
+
details.status = "failed";
|
|
437
|
+
details.completionState = "error";
|
|
438
|
+
details.error = capped(terminal.errorMessage ?? "Child terminal error");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if ((terminal.stopReason === "stop" || terminal.stopReason === "length") && response.trim()) {
|
|
442
|
+
details.status = "completed";
|
|
443
|
+
details.completionState = terminal.stopReason;
|
|
444
|
+
details.phase = "output";
|
|
445
|
+
const truncation = truncateHead(response, { maxBytes: DEFAULT_MAX_BYTES, maxLines: DEFAULT_MAX_LINES });
|
|
446
|
+
let returned = truncation.content;
|
|
447
|
+
let path: string | undefined;
|
|
448
|
+
if (truncation.truncated) {
|
|
449
|
+
const directory = await mkdtemp(join(tmpdir(), "tau-subagent-"));
|
|
450
|
+
path = join(directory, "output.md");
|
|
451
|
+
await writeFile(path, response, { encoding: "utf8", mode: 0o600 });
|
|
452
|
+
returned += `\n\n[Output truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}). Full output saved to: ${path}]`;
|
|
453
|
+
}
|
|
454
|
+
details.response = returned;
|
|
455
|
+
details.truncation = {
|
|
456
|
+
truncated: truncation.truncated,
|
|
457
|
+
path,
|
|
458
|
+
outputLines: truncation.outputLines,
|
|
459
|
+
totalLines: truncation.totalLines,
|
|
460
|
+
outputBytes: truncation.outputBytes,
|
|
461
|
+
totalBytes: truncation.totalBytes,
|
|
462
|
+
};
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
details.status = "failed";
|
|
466
|
+
details.completionState = terminal.stopReason;
|
|
467
|
+
details.error = `Agent ${definitionName} run failed: empty or non-terminal response`;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function deliverTurnPrompt(options: {
|
|
471
|
+
session: SubagentThread["resource"]["session"];
|
|
472
|
+
thread: SubagentThread;
|
|
473
|
+
task: string;
|
|
474
|
+
files: readonly string[];
|
|
475
|
+
invocationId: string;
|
|
476
|
+
initial: boolean;
|
|
477
|
+
resumePrompt: string | undefined;
|
|
478
|
+
signal: AbortSignal;
|
|
479
|
+
}): Promise<void> {
|
|
480
|
+
const { session, thread, task, files, invocationId, initial, resumePrompt, signal } = options;
|
|
481
|
+
const abort = () => {
|
|
482
|
+
void session.abort().catch(() => undefined);
|
|
483
|
+
};
|
|
484
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
485
|
+
try {
|
|
486
|
+
const autoreadMessages = await buildAutoreadMessages({
|
|
487
|
+
files,
|
|
488
|
+
cwd: thread.cwd,
|
|
489
|
+
invocationId,
|
|
490
|
+
});
|
|
491
|
+
if (signal.aborted) throw new Error("Subagent call aborted before prompt");
|
|
492
|
+
for (const message of autoreadMessages) {
|
|
493
|
+
if (signal.aborted) throw new Error("Subagent call aborted during autoread delivery");
|
|
494
|
+
await session.sendCustomMessage<unknown>(message, { deliverAs: "nextTurn" });
|
|
495
|
+
}
|
|
496
|
+
if (signal.aborted) throw new Error("Subagent call aborted before prompt");
|
|
497
|
+
await session.prompt(buildTurnPrompt({ definition: thread.definition, task, files, initial, resumePrompt }), {
|
|
498
|
+
expandPromptTemplates: false,
|
|
499
|
+
});
|
|
500
|
+
} finally {
|
|
501
|
+
signal.removeEventListener("abort", abort);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function turnResult(
|
|
506
|
+
details: SubagentDetails,
|
|
507
|
+
retainable: boolean,
|
|
508
|
+
assistantMessageEndAt: readonly number[],
|
|
509
|
+
usage: SubagentUsage,
|
|
510
|
+
toolUsage: Usage,
|
|
511
|
+
definitionName: string,
|
|
512
|
+
): {
|
|
513
|
+
content: string;
|
|
514
|
+
details: SubagentDetails;
|
|
515
|
+
retainable: boolean;
|
|
516
|
+
terminalOutcome: RetainedTurnOutcome;
|
|
517
|
+
assistantMessageEndAt: readonly number[];
|
|
518
|
+
usage?: Usage;
|
|
519
|
+
} {
|
|
520
|
+
return {
|
|
521
|
+
content:
|
|
522
|
+
details.status === "completed"
|
|
523
|
+
? (details.response ?? "")
|
|
524
|
+
: (details.error ?? `Agent ${definitionName} failed`),
|
|
525
|
+
details,
|
|
526
|
+
retainable,
|
|
527
|
+
terminalOutcome:
|
|
528
|
+
details.status === "completed" ? "completed" : details.status === "aborted" ? "aborted" : "failed",
|
|
529
|
+
assistantMessageEndAt,
|
|
530
|
+
...(usage.turns === 0 ? {} : { usage: toolUsage }),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
251
534
|
export async function runSubagentTurn(options: {
|
|
252
535
|
thread: SubagentThread;
|
|
253
536
|
task: string;
|
|
@@ -269,15 +552,7 @@ export async function runSubagentTurn(options: {
|
|
|
269
552
|
const { definition } = thread;
|
|
270
553
|
const { session } = thread.resource;
|
|
271
554
|
const started = Date.now();
|
|
272
|
-
const
|
|
273
|
-
const toolUsage: Usage = {
|
|
274
|
-
input: 0,
|
|
275
|
-
output: 0,
|
|
276
|
-
cacheRead: 0,
|
|
277
|
-
cacheWrite: 0,
|
|
278
|
-
totalTokens: 0,
|
|
279
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
280
|
-
};
|
|
555
|
+
const { usage, toolUsage } = emptyTurnUsage();
|
|
281
556
|
const details: SubagentDetails = {
|
|
282
557
|
agent: definition.name,
|
|
283
558
|
displayName: thread.displayName,
|
|
@@ -320,188 +595,35 @@ export async function runSubagentTurn(options: {
|
|
|
320
595
|
details.error = "Subagent call aborted before prompt";
|
|
321
596
|
details.durationMs = Date.now() - started;
|
|
322
597
|
publish(true);
|
|
323
|
-
return
|
|
324
|
-
content: details.error,
|
|
325
|
-
details,
|
|
326
|
-
retainable: false,
|
|
327
|
-
terminalOutcome: "aborted",
|
|
328
|
-
assistantMessageEndAt,
|
|
329
|
-
};
|
|
598
|
+
return turnResult(details, false, assistantMessageEndAt, usage, toolUsage, definition.name);
|
|
330
599
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
toolUsage.totalTokens += event.message.usage.totalTokens;
|
|
350
|
-
toolUsage.cost.input += event.message.usage.cost.input;
|
|
351
|
-
toolUsage.cost.output += event.message.usage.cost.output;
|
|
352
|
-
toolUsage.cost.cacheRead += event.message.usage.cost.cacheRead;
|
|
353
|
-
toolUsage.cost.cacheWrite += event.message.usage.cost.cacheWrite;
|
|
354
|
-
toolUsage.cost.total += event.message.usage.cost.total;
|
|
355
|
-
if (event.message.usage.cacheWrite1h !== undefined)
|
|
356
|
-
toolUsage.cacheWrite1h = (toolUsage.cacheWrite1h ?? 0) + event.message.usage.cacheWrite1h;
|
|
357
|
-
if (event.message.usage.reasoning !== undefined)
|
|
358
|
-
toolUsage.reasoning = (toolUsage.reasoning ?? 0) + event.message.usage.reasoning;
|
|
359
|
-
details.response = cappedTail(textOf(event.message), PREVIEW_LIMIT);
|
|
360
|
-
details.currentActivity = undefined;
|
|
361
|
-
publish(true);
|
|
362
|
-
} else if (event.type === "tool_execution_start") {
|
|
363
|
-
details.toolCalls += 1;
|
|
364
|
-
const summary = `${event.toolName} ${capped(event.args)}`.trim();
|
|
365
|
-
actionById.set(event.toolCallId, summary);
|
|
366
|
-
details.currentActivity = summary;
|
|
367
|
-
publish(true);
|
|
368
|
-
} else if (event.type === "tool_execution_end") {
|
|
369
|
-
const action = {
|
|
370
|
-
tool: event.toolName,
|
|
371
|
-
summary: actionById.get(event.toolCallId) ?? event.toolName,
|
|
372
|
-
error: event.isError,
|
|
373
|
-
};
|
|
374
|
-
details.actions.push(action);
|
|
375
|
-
if (details.actions.length > 20) {
|
|
376
|
-
const removable = details.actions.findIndex((item) => !item.error);
|
|
377
|
-
const [removed] = details.actions.splice(removable >= 0 ? removable : 0, 1);
|
|
378
|
-
details.omittedActions += 1;
|
|
379
|
-
if (removed?.error) details.omittedErrors += 1;
|
|
380
|
-
}
|
|
381
|
-
details.currentActivity = undefined;
|
|
382
|
-
publish(true);
|
|
383
|
-
}
|
|
600
|
+
unsubscribe = subscribeTurnEvents({
|
|
601
|
+
session,
|
|
602
|
+
details,
|
|
603
|
+
usage,
|
|
604
|
+
toolUsage,
|
|
605
|
+
turnMessages,
|
|
606
|
+
assistantMessageEndAt,
|
|
607
|
+
publish,
|
|
608
|
+
});
|
|
609
|
+
await deliverTurnPrompt({
|
|
610
|
+
session,
|
|
611
|
+
thread,
|
|
612
|
+
task,
|
|
613
|
+
files,
|
|
614
|
+
invocationId,
|
|
615
|
+
initial,
|
|
616
|
+
resumePrompt,
|
|
617
|
+
signal,
|
|
384
618
|
});
|
|
385
|
-
const abort = () => {
|
|
386
|
-
void session.abort().catch(() => undefined);
|
|
387
|
-
};
|
|
388
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
389
|
-
try {
|
|
390
|
-
const autoreadMessages = await Promise.all(
|
|
391
|
-
files.map(async (file, index) => {
|
|
392
|
-
const pathKey = resolve(thread.cwd, file);
|
|
393
|
-
const common = {
|
|
394
|
-
rowId: `${invocationId}:${index}`,
|
|
395
|
-
path: file,
|
|
396
|
-
cwd: thread.cwd,
|
|
397
|
-
source: "subagent",
|
|
398
|
-
batchId: invocationId,
|
|
399
|
-
};
|
|
400
|
-
try {
|
|
401
|
-
const bytes = await readFile(pathKey);
|
|
402
|
-
const content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
403
|
-
const lines = content.split("\n");
|
|
404
|
-
const numbered = lines.map((line, lineIndex) => `${lineIndex + 1}: ${line}`).join("\n");
|
|
405
|
-
return {
|
|
406
|
-
customType: AUTOREAD_MESSAGE_TYPE,
|
|
407
|
-
content: `${file}\n${numbered}`,
|
|
408
|
-
display: false,
|
|
409
|
-
details: {
|
|
410
|
-
...common,
|
|
411
|
-
status: "read",
|
|
412
|
-
readCache: createCompleteFileMeta({
|
|
413
|
-
pathKey,
|
|
414
|
-
presentation: "line-numbered",
|
|
415
|
-
servedHash: createHash("sha256").update(bytes).digest("hex"),
|
|
416
|
-
mode: "baseline",
|
|
417
|
-
sourceText: content,
|
|
418
|
-
returnedText: `${file}\n${numbered}`,
|
|
419
|
-
totalLines: lines.length,
|
|
420
|
-
summary: `${lines.length} lines`,
|
|
421
|
-
}),
|
|
422
|
-
},
|
|
423
|
-
};
|
|
424
|
-
} catch (error) {
|
|
425
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
426
|
-
return {
|
|
427
|
-
customType: AUTOREAD_MESSAGE_TYPE,
|
|
428
|
-
content: `${file}\nAutoread failed: ${message}`,
|
|
429
|
-
display: false,
|
|
430
|
-
details: { ...common, status: "failed", error: message },
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
}),
|
|
434
|
-
);
|
|
435
|
-
if (signal.aborted) throw new Error("Subagent call aborted before prompt");
|
|
436
|
-
for (const message of autoreadMessages) {
|
|
437
|
-
if (signal.aborted) throw new Error("Subagent call aborted during autoread delivery");
|
|
438
|
-
await session.sendCustomMessage<unknown>(message, { deliverAs: "nextTurn" });
|
|
439
|
-
}
|
|
440
|
-
if (signal.aborted) throw new Error("Subagent call aborted before prompt");
|
|
441
|
-
await session.prompt(
|
|
442
|
-
resumePrompt ??
|
|
443
|
-
(initial
|
|
444
|
-
? `You are an isolated delegated child agent. Stay within the delegated task and return only the requested result.${files.length ? " Parent-supplied autoread files are included as line-numbered context for this turn." : ""}\n\n## Agent instructions\n${definition.prompt}\n\n## Delegated task\n${task}`
|
|
445
|
-
: `Continue the existing delegated work using the context already in this thread. Return only the requested result.${files.length ? " Parent-supplied autoread files are included as line-numbered context for this turn." : ""}\n\n## Parent follow-up\n${task}`),
|
|
446
|
-
{ expandPromptTemplates: false },
|
|
447
|
-
);
|
|
448
|
-
} finally {
|
|
449
|
-
signal.removeEventListener("abort", abort);
|
|
450
|
-
}
|
|
451
619
|
// Prompt returned without throw — session remains usable for retention decisions.
|
|
452
620
|
retainable = true;
|
|
453
621
|
const terminal = turnMessages.at(-1);
|
|
454
622
|
if (!terminal) throw new Error(`Agent ${definition.name} run failed: no terminal assistant response`);
|
|
455
|
-
|
|
456
|
-
if (terminal.stopReason === "aborted") {
|
|
457
|
-
details.status = "aborted";
|
|
458
|
-
details.completionState = "aborted";
|
|
459
|
-
details.error = capped(terminal.errorMessage ?? "Child aborted");
|
|
460
|
-
} else if (terminal.stopReason === "error") {
|
|
461
|
-
details.status = "failed";
|
|
462
|
-
details.completionState = "error";
|
|
463
|
-
details.error = capped(terminal.errorMessage ?? "Child terminal error");
|
|
464
|
-
} else if ((terminal.stopReason === "stop" || terminal.stopReason === "length") && response.trim()) {
|
|
465
|
-
details.status = "completed";
|
|
466
|
-
details.completionState = terminal.stopReason;
|
|
467
|
-
details.phase = "output";
|
|
468
|
-
const truncation = truncateHead(response, { maxBytes: DEFAULT_MAX_BYTES, maxLines: DEFAULT_MAX_LINES });
|
|
469
|
-
let returned = truncation.content;
|
|
470
|
-
let path: string | undefined;
|
|
471
|
-
if (truncation.truncated) {
|
|
472
|
-
const directory = await mkdtemp(join(tmpdir(), "tau-subagent-"));
|
|
473
|
-
path = join(directory, "output.md");
|
|
474
|
-
await writeFile(path, response, { encoding: "utf8", mode: 0o600 });
|
|
475
|
-
returned += `\n\n[Output truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}). Full output saved to: ${path}]`;
|
|
476
|
-
}
|
|
477
|
-
details.response = returned;
|
|
478
|
-
details.truncation = {
|
|
479
|
-
truncated: truncation.truncated,
|
|
480
|
-
path,
|
|
481
|
-
outputLines: truncation.outputLines,
|
|
482
|
-
totalLines: truncation.totalLines,
|
|
483
|
-
outputBytes: truncation.outputBytes,
|
|
484
|
-
totalBytes: truncation.totalBytes,
|
|
485
|
-
};
|
|
486
|
-
} else {
|
|
487
|
-
details.status = "failed";
|
|
488
|
-
details.completionState = terminal.stopReason;
|
|
489
|
-
details.error = `Agent ${definition.name} run failed: empty or non-terminal response`;
|
|
490
|
-
}
|
|
623
|
+
await applyTerminalOutcome(details, terminal, definition.name);
|
|
491
624
|
details.durationMs = Date.now() - started;
|
|
492
625
|
publish(true);
|
|
493
|
-
return
|
|
494
|
-
content:
|
|
495
|
-
details.status === "completed"
|
|
496
|
-
? (details.response ?? "")
|
|
497
|
-
: (details.error ?? `Agent ${definition.name} failed`),
|
|
498
|
-
details,
|
|
499
|
-
retainable,
|
|
500
|
-
terminalOutcome:
|
|
501
|
-
details.status === "completed" ? "completed" : details.status === "aborted" ? "aborted" : "failed",
|
|
502
|
-
assistantMessageEndAt,
|
|
503
|
-
...(usage.turns === 0 ? {} : { usage: toolUsage }),
|
|
504
|
-
};
|
|
626
|
+
return turnResult(details, retainable, assistantMessageEndAt, usage, toolUsage, definition.name);
|
|
505
627
|
} catch (error) {
|
|
506
628
|
details.status = signal.aborted ? "aborted" : "failed";
|
|
507
629
|
details.error = capped(error instanceof Error ? error.message : "Subagent failed");
|
|
@@ -509,14 +631,7 @@ export async function runSubagentTurn(options: {
|
|
|
509
631
|
// Prompt/session failures leave the child unsafe for reuse.
|
|
510
632
|
retainable = false;
|
|
511
633
|
publish(true);
|
|
512
|
-
return
|
|
513
|
-
content: details.error,
|
|
514
|
-
details,
|
|
515
|
-
retainable,
|
|
516
|
-
terminalOutcome: details.status === "aborted" ? "aborted" : "failed",
|
|
517
|
-
assistantMessageEndAt,
|
|
518
|
-
...(usage.turns === 0 ? {} : { usage: toolUsage }),
|
|
519
|
-
};
|
|
634
|
+
return turnResult(details, retainable, assistantMessageEndAt, usage, toolUsage, definition.name);
|
|
520
635
|
} finally {
|
|
521
636
|
unsubscribe?.();
|
|
522
637
|
thread.turns += 1;
|