@code-yeongyu/senpi 2026.5.15 → 2026.5.16
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/CHANGELOG.md +1113 -1177
- package/README.md +1 -2
- package/dist/core/agent-session.d.ts +9 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +114 -8
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/dynamic-prompt/verification.d.ts +31 -0
- package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
- package/dist/core/dynamic-prompt/verification.js +41 -0
- package/dist/core/dynamic-prompt/verification.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-reduction.d.ts +97 -0
- package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/context-reduction.js +420 -0
- package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +168 -31
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +197 -0
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote.js +690 -0
- package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/prompts.d.ts +3 -3
- package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/prompts.js +0 -22
- package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts +4 -0
- package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js +48 -0
- package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +3 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +80 -33
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts +8 -0
- package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/todo-bridge.js +12 -6
- package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
- package/dist/core/extensions/builtin/diff.d.ts.map +1 -1
- package/dist/core/extensions/builtin/diff.js +1 -1
- package/dist/core/extensions/builtin/diff.js.map +1 -1
- package/dist/core/extensions/builtin/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/index.js +0 -2
- package/dist/core/extensions/builtin/index.js.map +1 -1
- package/dist/core/extensions/builtin/openai-web-search/index.d.ts +6 -2
- package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/openai-web-search/index.js +82 -10
- package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/prompt.js +0 -5
- package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/system-messages.d.ts +1 -1
- package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
- package/dist/core/extensions/builtin/system-messages.js.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/index.js +8 -4
- package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts +3 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js +89 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts +3 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js +122 -0
- package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +18 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +22 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/messages.d.ts +3 -3
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +5 -10
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +7 -22
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +0 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/thinking-levels.d.ts +6 -0
- package/dist/core/thinking-levels.d.ts.map +1 -0
- package/dist/core/thinking-levels.js +36 -0
- package/dist/core/thinking-levels.js.map +1 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +15 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +20 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +3 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +8 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +137 -49
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/working-status.d.ts +15 -0
- package/dist/modes/interactive/working-status.d.ts.map +1 -0
- package/dist/modes/interactive/working-status.js +60 -0
- package/dist/modes/interactive/working-status.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/docs/extensions.md +0 -1
- package/docs/index.md +0 -1
- package/docs/models.md +9 -0
- package/docs/sdk.md +0 -1
- package/docs/settings.md +1 -29
- package/docs/termux.md +2 -2
- package/docs/usage.md +1 -1
- package/examples/README.md +1 -1
- package/examples/extensions/README.md +0 -1
- package/examples/extensions/overlay-qa-tests.ts +1 -1
- package/package.json +4 -4
- package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts +0 -10
- package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/cancel-tool.js +0 -109
- package/dist/core/extensions/builtin/background-task/cancel-tool.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/index.d.ts +0 -3
- package/dist/core/extensions/builtin/background-task/index.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/index.js +0 -207
- package/dist/core/extensions/builtin/background-task/index.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/manager.d.ts +0 -17
- package/dist/core/extensions/builtin/background-task/manager.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/manager.js +0 -114
- package/dist/core/extensions/builtin/background-task/manager.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/notification.d.ts +0 -22
- package/dist/core/extensions/builtin/background-task/notification.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/notification.js +0 -105
- package/dist/core/extensions/builtin/background-task/notification.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/output-tool.d.ts +0 -11
- package/dist/core/extensions/builtin/background-task/output-tool.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/output-tool.js +0 -127
- package/dist/core/extensions/builtin/background-task/output-tool.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/spawner.d.ts +0 -8
- package/dist/core/extensions/builtin/background-task/spawner.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/spawner.js +0 -207
- package/dist/core/extensions/builtin/background-task/spawner.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/task-tool.d.ts +0 -20
- package/dist/core/extensions/builtin/background-task/task-tool.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/task-tool.js +0 -302
- package/dist/core/extensions/builtin/background-task/task-tool.js.map +0 -1
- package/dist/core/extensions/builtin/background-task/types.d.ts +0 -72
- package/dist/core/extensions/builtin/background-task/types.d.ts.map +0 -1
- package/dist/core/extensions/builtin/background-task/types.js +0 -32
- package/dist/core/extensions/builtin/background-task/types.js.map +0 -1
- package/docs/agents.md +0 -348
- package/examples/extensions/subagent/README.md +0 -172
- package/examples/extensions/subagent/agents/planner.md +0 -37
- package/examples/extensions/subagent/agents/reviewer.md +0 -35
- package/examples/extensions/subagent/agents/scout.md +0 -50
- package/examples/extensions/subagent/agents/worker.md +0 -24
- package/examples/extensions/subagent/agents.ts +0 -126
- package/examples/extensions/subagent/index.ts +0 -987
- package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
- package/examples/extensions/subagent/prompts/implement.md +0 -10
- package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
2
|
-
import { Type } from "typebox";
|
|
3
|
-
import { DEFAULT_BLOCK_TIMEOUT, MAX_BLOCK_TIMEOUT } from "./types.js";
|
|
4
|
-
const BackgroundOutputParams = Type.Object({
|
|
5
|
-
task_id: Type.String({ description: "Task ID to get output from" }),
|
|
6
|
-
block: Type.Optional(Type.Boolean({
|
|
7
|
-
description: "Wait for completion (default: false). System notifies when done, so blocking is rarely needed.",
|
|
8
|
-
})),
|
|
9
|
-
timeout: Type.Optional(Type.Number({ description: "Max wait time in ms (default: 60000, max: 300000)" })),
|
|
10
|
-
});
|
|
11
|
-
function formatDuration(startedAt, completedAt) {
|
|
12
|
-
const end = completedAt ?? new Date();
|
|
13
|
-
const ms = end.getTime() - startedAt.getTime();
|
|
14
|
-
if (ms < 1000)
|
|
15
|
-
return `${ms}ms`;
|
|
16
|
-
if (ms < 60000)
|
|
17
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
18
|
-
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
19
|
-
}
|
|
20
|
-
function formatTaskOutput(task) {
|
|
21
|
-
const lines = [`Task: ${task.id}`, `Description: ${task.description}`, `Status: ${task.status}`];
|
|
22
|
-
if (task.status === "completed") {
|
|
23
|
-
lines.push(`Duration: ${formatDuration(task.startedAt, task.completedAt)}`);
|
|
24
|
-
lines.push("Result:");
|
|
25
|
-
lines.push(task.result ?? "(no output)");
|
|
26
|
-
}
|
|
27
|
-
else if (task.status === "error") {
|
|
28
|
-
lines.push(`Duration: ${formatDuration(task.startedAt, task.completedAt)}`);
|
|
29
|
-
lines.push("Error:");
|
|
30
|
-
lines.push(task.error ?? "(unknown error)");
|
|
31
|
-
}
|
|
32
|
-
else if (task.status === "cancelled") {
|
|
33
|
-
lines.push(`Duration: ${formatDuration(task.startedAt, task.completedAt)}`);
|
|
34
|
-
lines.push("Status: Cancelled");
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
lines.push(`Elapsed: ${formatDuration(task.startedAt, undefined)}`);
|
|
38
|
-
}
|
|
39
|
-
return lines.join("\n");
|
|
40
|
-
}
|
|
41
|
-
export function createBackgroundOutputTool(manager) {
|
|
42
|
-
return {
|
|
43
|
-
name: "background_output",
|
|
44
|
-
label: "BackgroundOutput",
|
|
45
|
-
description: `Get output from background task. System notifies on completion, so block=true rarely needed.
|
|
46
|
-
|
|
47
|
-
IMPORTANT: ONLY call this tool AFTER receiving a <system-reminder> notification for the task. Do NOT call immediately after launching a background task - wait for the notification first.`,
|
|
48
|
-
promptSnippet: "Retrieve results from a completed background task by task_id.",
|
|
49
|
-
promptGuidelines: [
|
|
50
|
-
"ONLY call this tool AFTER receiving a <system-reminder> notification for the task.",
|
|
51
|
-
"Do NOT call this immediately after launching a background task - wait for the notification first.",
|
|
52
|
-
"Set block=true only if you must wait synchronously (rarely needed).",
|
|
53
|
-
],
|
|
54
|
-
parameters: BackgroundOutputParams,
|
|
55
|
-
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
56
|
-
const task = manager.getTask(params.task_id);
|
|
57
|
-
if (!task) {
|
|
58
|
-
return {
|
|
59
|
-
content: [{ type: "text", text: `Error: Task not found: ${params.task_id}` }],
|
|
60
|
-
details: undefined,
|
|
61
|
-
isError: true,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
if (task.status === "completed" || task.status === "error" || task.status === "cancelled") {
|
|
65
|
-
return {
|
|
66
|
-
content: [{ type: "text", text: formatTaskOutput(task) }],
|
|
67
|
-
details: { task },
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
if (!params.block) {
|
|
71
|
-
return {
|
|
72
|
-
content: [
|
|
73
|
-
{
|
|
74
|
-
type: "text",
|
|
75
|
-
text: `Task ${params.task_id} is still running. Do NOT call BackgroundOutput again for this task - the system will notify you when it completes. Continue with other work or end your response.`,
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
details: { task },
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
const timeoutMs = Math.min(params.timeout ?? DEFAULT_BLOCK_TIMEOUT, MAX_BLOCK_TIMEOUT);
|
|
82
|
-
const startTime = Date.now();
|
|
83
|
-
try {
|
|
84
|
-
const finalTask = await new Promise((resolve, reject) => {
|
|
85
|
-
const interval = setInterval(() => {
|
|
86
|
-
const current = manager.getTask(params.task_id);
|
|
87
|
-
if (!current) {
|
|
88
|
-
clearInterval(interval);
|
|
89
|
-
reject(new Error(`Task not found: ${params.task_id}`));
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (current.status === "completed" || current.status === "error" || current.status === "cancelled") {
|
|
93
|
-
clearInterval(interval);
|
|
94
|
-
resolve(current);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
if (Date.now() - startTime >= timeoutMs) {
|
|
98
|
-
clearInterval(interval);
|
|
99
|
-
reject(new Error(`Timeout waiting for task ${params.task_id}`));
|
|
100
|
-
}
|
|
101
|
-
}, 100);
|
|
102
|
-
});
|
|
103
|
-
return {
|
|
104
|
-
content: [{ type: "text", text: formatTaskOutput(finalTask) }],
|
|
105
|
-
details: { task: finalTask },
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
-
return {
|
|
111
|
-
content: [{ type: "text", text: `Error: ${message}` }],
|
|
112
|
-
details: undefined,
|
|
113
|
-
isError: true,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
renderCall(args, theme) {
|
|
118
|
-
return new Text(theme.fg("toolTitle", theme.bold("BackgroundOutput ")) + theme.fg("accent", args.task_id), 0, 0);
|
|
119
|
-
},
|
|
120
|
-
renderResult(result, _options, theme) {
|
|
121
|
-
const firstContent = result.content[0];
|
|
122
|
-
const text = firstContent?.type === "text" ? firstContent.text : "(no output)";
|
|
123
|
-
return new Text(theme.fg("muted", text), 0, 0);
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
//# sourceMappingURL=output-tool.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"output-tool.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/output-tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { Text } from \"@earendil-works/pi-tui\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../types.js\";\nimport type { BackgroundManager } from \"./manager.js\";\nimport type { BackgroundTask } from \"./types.js\";\nimport { DEFAULT_BLOCK_TIMEOUT, MAX_BLOCK_TIMEOUT } from \"./types.js\";\n\nconst BackgroundOutputParams = Type.Object({\n\ttask_id: Type.String({ description: \"Task ID to get output from\" }),\n\tblock: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Wait for completion (default: false). System notifies when done, so blocking is rarely needed.\",\n\t\t}),\n\t),\n\ttimeout: Type.Optional(Type.Number({ description: \"Max wait time in ms (default: 60000, max: 300000)\" })),\n});\n\nfunction formatDuration(startedAt: Date, completedAt: Date | undefined): string {\n\tconst end = completedAt ?? new Date();\n\tconst ms = end.getTime() - startedAt.getTime();\n\tif (ms < 1000) return `${ms}ms`;\n\tif (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n\treturn `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;\n}\n\nfunction formatTaskOutput(task: BackgroundTask): string {\n\tconst lines: string[] = [`Task: ${task.id}`, `Description: ${task.description}`, `Status: ${task.status}`];\n\n\tif (task.status === \"completed\") {\n\t\tlines.push(`Duration: ${formatDuration(task.startedAt, task.completedAt)}`);\n\t\tlines.push(\"Result:\");\n\t\tlines.push(task.result ?? \"(no output)\");\n\t} else if (task.status === \"error\") {\n\t\tlines.push(`Duration: ${formatDuration(task.startedAt, task.completedAt)}`);\n\t\tlines.push(\"Error:\");\n\t\tlines.push(task.error ?? \"(unknown error)\");\n\t} else if (task.status === \"cancelled\") {\n\t\tlines.push(`Duration: ${formatDuration(task.startedAt, task.completedAt)}`);\n\t\tlines.push(\"Status: Cancelled\");\n\t} else {\n\t\tlines.push(`Elapsed: ${formatDuration(task.startedAt, undefined)}`);\n\t}\n\n\treturn lines.join(\"\\n\");\n}\n\nexport function createBackgroundOutputTool(manager: BackgroundManager): ToolDefinition<typeof BackgroundOutputParams> {\n\treturn {\n\t\tname: \"background_output\",\n\t\tlabel: \"BackgroundOutput\",\n\t\tdescription: `Get output from background task. System notifies on completion, so block=true rarely needed.\n\nIMPORTANT: ONLY call this tool AFTER receiving a <system-reminder> notification for the task. Do NOT call immediately after launching a background task - wait for the notification first.`,\n\t\tpromptSnippet: \"Retrieve results from a completed background task by task_id.\",\n\t\tpromptGuidelines: [\n\t\t\t\"ONLY call this tool AFTER receiving a <system-reminder> notification for the task.\",\n\t\t\t\"Do NOT call this immediately after launching a background task - wait for the notification first.\",\n\t\t\t\"Set block=true only if you must wait synchronously (rarely needed).\",\n\t\t],\n\t\tparameters: BackgroundOutputParams,\n\t\tasync execute(_toolCallId, params, _signal, _onUpdate, _ctx) {\n\t\t\tconst task = manager.getTask(params.task_id);\n\n\t\t\tif (!task) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Error: Task not found: ${params.task_id}` }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (task.status === \"completed\" || task.status === \"error\" || task.status === \"cancelled\") {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: formatTaskOutput(task) }],\n\t\t\t\t\tdetails: { task },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!params.block) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Task ${params.task_id} is still running. Do NOT call BackgroundOutput again for this task - the system will notify you when it completes. Continue with other work or end your response.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst timeoutMs = Math.min(params.timeout ?? DEFAULT_BLOCK_TIMEOUT, MAX_BLOCK_TIMEOUT);\n\t\t\tconst startTime = Date.now();\n\n\t\t\ttry {\n\t\t\t\tconst finalTask = await new Promise<BackgroundTask>((resolve, reject) => {\n\t\t\t\t\tconst interval = setInterval(() => {\n\t\t\t\t\t\tconst current = manager.getTask(params.task_id);\n\t\t\t\t\t\tif (!current) {\n\t\t\t\t\t\t\tclearInterval(interval);\n\t\t\t\t\t\t\treject(new Error(`Task not found: ${params.task_id}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (current.status === \"completed\" || current.status === \"error\" || current.status === \"cancelled\") {\n\t\t\t\t\t\t\tclearInterval(interval);\n\t\t\t\t\t\t\tresolve(current);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Date.now() - startTime >= timeoutMs) {\n\t\t\t\t\t\t\tclearInterval(interval);\n\t\t\t\t\t\t\treject(new Error(`Timeout waiting for task ${params.task_id}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 100);\n\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: formatTaskOutput(finalTask) }],\n\t\t\t\t\tdetails: { task: finalTask },\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Error: ${message}` }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\treturn new Text(\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"BackgroundOutput \")) + theme.fg(\"accent\", args.task_id),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t},\n\t\trenderResult(result, _options, theme) {\n\t\t\tconst firstContent = result.content[0];\n\t\t\tconst text = firstContent?.type === \"text\" ? firstContent.text : \"(no output)\";\n\t\t\treturn new Text(theme.fg(\"muted\", text), 0, 0);\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type SpawnedAgent, type SpawnOptions } from "./types.js";
|
|
2
|
-
export type { SpawnOptions, SpawnedAgent };
|
|
3
|
-
export declare function getPiInvocation(args: string[]): {
|
|
4
|
-
command: string;
|
|
5
|
-
args: string[];
|
|
6
|
-
};
|
|
7
|
-
export declare function spawnSubagent(options: SpawnOptions): SpawnedAgent;
|
|
8
|
-
//# sourceMappingURL=spawner.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/spawner.ts"],"names":[],"mappings":"AAIA,OAAO,EAKN,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AAE3C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAanF;AA0BD,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAkMjE","sourcesContent":["import { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { APP_NAME } from \"../../../../config.js\";\nimport {\n\tAGENT_TYPE_ENV_VAR,\n\tDEPTH_ENV_VAR,\n\tMAX_SUBAGENT_DEPTH,\n\ttype SpawnEvent,\n\ttype SpawnedAgent,\n\ttype SpawnOptions,\n} from \"./types.js\";\n\nexport type { SpawnOptions, SpawnedAgent };\n\nexport function getPiInvocation(args: string[]): { command: string; args: string[] } {\n\tconst currentScript = process.argv[1];\n\tif (currentScript && fs.existsSync(currentScript)) {\n\t\treturn { command: process.execPath, args: [currentScript, ...args] };\n\t}\n\n\tconst execName = path.basename(process.execPath).toLowerCase();\n\tconst isGenericRuntime = /^(node|bun)(\\.exe)?$/.test(execName);\n\tif (!isGenericRuntime) {\n\t\treturn { command: process.execPath, args };\n\t}\n\n\treturn { command: APP_NAME, args };\n}\n\nconst trackedPids = new Set<number>();\n\nlet cleanupRegistered = false;\nfunction ensureCleanupRegistered(): void {\n\tif (cleanupRegistered) return;\n\tcleanupRegistered = true;\n\tprocess.on(\"exit\", () => {\n\t\tfor (const pid of trackedPids) {\n\t\t\ttry {\n\t\t\t\tprocess.kill(pid, \"SIGTERM\");\n\t\t\t} catch {\n\t\t\t\t/* process may already be dead */\n\t\t\t}\n\t\t}\n\t});\n}\n\ntype MessagePart = { type: string; text?: string };\ntype NdjsonMessage = { role: string; content: MessagePart[] };\n\nfunction emitSpawnEvent(options: SpawnOptions, event: SpawnEvent): void {\n\toptions.onEvent?.(event);\n}\n\nexport function spawnSubagent(options: SpawnOptions): SpawnedAgent {\n\tconst currentDepth = parseInt(process.env[DEPTH_ENV_VAR] ?? \"0\", 10);\n\tif (currentDepth >= MAX_SUBAGENT_DEPTH) {\n\t\tthrow new Error(`Maximum subagent depth (${MAX_SUBAGENT_DEPTH}) exceeded`);\n\t}\n\n\tconst args: string[] = [\"--mode\", \"json\", \"-p\", options.prompt];\n\n\tif (options.model) {\n\t\targs.push(\"--model\", options.model);\n\t}\n\n\tif (options.sessionPath) {\n\t\targs.push(\"--session\", options.sessionPath);\n\t}\n\n\tif (options.permissionFlag) {\n\t\targs.push(\"--permission\", options.permissionFlag);\n\t}\n\n\tconst invocation = getPiInvocation(args);\n\n\tconst childEnv: Record<string, string | undefined> = {\n\t\t...process.env,\n\t\t[DEPTH_ENV_VAR]: String(currentDepth + 1),\n\t\t...options.env,\n\t};\n\n\tif (options.agentType) {\n\t\tchildEnv[AGENT_TYPE_ENV_VAR] = options.agentType;\n\t}\n\n\tconst proc = spawn(invocation.command, invocation.args, {\n\t\tcwd: options.cwd,\n\t\tshell: false,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tenv: childEnv,\n\t});\n\n\tif (proc.pid) {\n\t\ttrackedPids.add(proc.pid);\n\t\tensureCleanupRegistered();\n\t}\n\n\tlet buffer = \"\";\n\tconst accumulatedText: string[] = [];\n\tconst updateText: string[] = [];\n\n\tconst processLine = (line: string): void => {\n\t\tif (!line.trim()) return;\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof event === \"object\" && event !== null && \"type\" in event && \"message\" in event) {\n\t\t\tconst typedEvent = event as { type: string; message: NdjsonMessage };\n\n\t\t\t// Accumulate text from message_update events\n\t\t\tif (typedEvent.type === \"message_update\") {\n\t\t\t\tconst message = typedEvent.message;\n\t\t\t\tif (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\t\tif (part.type === \"text\" && part.text) {\n\t\t\t\t\t\t\tupdateText.push(part.text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Extract final result from message_end, or fall back to accumulated update text\n\t\t\tif (typedEvent.type === \"message_end\") {\n\t\t\t\tconst message = typedEvent.message;\n\t\t\t\tif (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\t\t\tconst endText: string[] = [];\n\t\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\t\tif (part.type === \"text\" && part.text) {\n\t\t\t\t\t\t\tendText.push(part.text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (endText.length > 0) {\n\t\t\t\t\t\t// message_end has the final text - use it\n\t\t\t\t\t\taccumulatedText.length = 0;\n\t\t\t\t\t\taccumulatedText.push(...endText);\n\t\t\t\t\t} else if (updateText.length > 0) {\n\t\t\t\t\t\t// Fall back to accumulated update text\n\t\t\t\t\t\taccumulatedText.length = 0;\n\t\t\t\t\t\taccumulatedText.push(...updateText);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\ttypeof event === \"object\" &&\n\t\t\tevent !== null &&\n\t\t\t\"type\" in event &&\n\t\t\t\"toolCallId\" in event &&\n\t\t\t\"toolName\" in event\n\t\t) {\n\t\t\tconst typedEvent = event as {\n\t\t\t\ttype: string;\n\t\t\t\ttoolCallId: unknown;\n\t\t\t\ttoolName: unknown;\n\t\t\t};\n\n\t\t\tif (typeof typedEvent.toolCallId !== \"string\" || typeof typedEvent.toolName !== \"string\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (typedEvent.type === \"tool_execution_start\") {\n\t\t\t\temitSpawnEvent(options, {\n\t\t\t\t\ttype: \"tool_execution_start\",\n\t\t\t\t\ttoolCallId: typedEvent.toolCallId,\n\t\t\t\t\ttoolName: typedEvent.toolName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (typedEvent.type === \"tool_execution_end\") {\n\t\t\t\temitSpawnEvent(options, {\n\t\t\t\t\ttype: \"tool_execution_end\",\n\t\t\t\t\ttoolCallId: typedEvent.toolCallId,\n\t\t\t\t\ttoolName: typedEvent.toolName,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t};\n\n\tproc.stdout.on(\"data\", (data: Buffer) => {\n\t\tbuffer += data.toString();\n\t\tconst lines = buffer.split(\"\\n\");\n\t\tbuffer = lines.pop() ?? \"\";\n\t\tfor (const line of lines) {\n\t\t\tprocessLine(line);\n\t\t}\n\t});\n\n\tif (options.signal) {\n\t\tconst killProc = (): void => {\n\t\t\tif (proc.pid) {\n\t\t\t\ttry {\n\t\t\t\t\tproc.kill(\"SIGTERM\");\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (!proc.killed && proc.pid) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tproc.kill(\"SIGKILL\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* ignore */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, 5000);\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal.aborted) {\n\t\t\tkillProc();\n\t\t} else {\n\t\t\toptions.signal.addEventListener(\"abort\", killProc, { once: true });\n\t\t}\n\t}\n\n\tconst result = new Promise<{ text: string; exitCode: number }>((resolve) => {\n\t\tproc.on(\"close\", (code) => {\n\t\t\tif (buffer.trim()) {\n\t\t\t\tprocessLine(buffer);\n\t\t\t}\n\n\t\t\tif (proc.pid) {\n\t\t\t\ttrackedPids.delete(proc.pid);\n\t\t\t}\n\n\t\t\tresolve({\n\t\t\t\ttext: accumulatedText.join(\"\"),\n\t\t\t\texitCode: code ?? 0,\n\t\t\t});\n\t\t});\n\n\t\tproc.on(\"error\", () => {\n\t\t\tif (proc.pid) {\n\t\t\t\ttrackedPids.delete(proc.pid);\n\t\t\t}\n\t\t\tresolve({\n\t\t\t\ttext: accumulatedText.join(\"\"),\n\t\t\t\texitCode: 1,\n\t\t\t});\n\t\t});\n\t});\n\n\treturn { process: proc, result };\n}\n"]}
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { APP_NAME } from "../../../../config.js";
|
|
5
|
-
import { AGENT_TYPE_ENV_VAR, DEPTH_ENV_VAR, MAX_SUBAGENT_DEPTH, } from "./types.js";
|
|
6
|
-
export function getPiInvocation(args) {
|
|
7
|
-
const currentScript = process.argv[1];
|
|
8
|
-
if (currentScript && fs.existsSync(currentScript)) {
|
|
9
|
-
return { command: process.execPath, args: [currentScript, ...args] };
|
|
10
|
-
}
|
|
11
|
-
const execName = path.basename(process.execPath).toLowerCase();
|
|
12
|
-
const isGenericRuntime = /^(node|bun)(\.exe)?$/.test(execName);
|
|
13
|
-
if (!isGenericRuntime) {
|
|
14
|
-
return { command: process.execPath, args };
|
|
15
|
-
}
|
|
16
|
-
return { command: APP_NAME, args };
|
|
17
|
-
}
|
|
18
|
-
const trackedPids = new Set();
|
|
19
|
-
let cleanupRegistered = false;
|
|
20
|
-
function ensureCleanupRegistered() {
|
|
21
|
-
if (cleanupRegistered)
|
|
22
|
-
return;
|
|
23
|
-
cleanupRegistered = true;
|
|
24
|
-
process.on("exit", () => {
|
|
25
|
-
for (const pid of trackedPids) {
|
|
26
|
-
try {
|
|
27
|
-
process.kill(pid, "SIGTERM");
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
/* process may already be dead */
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
function emitSpawnEvent(options, event) {
|
|
36
|
-
options.onEvent?.(event);
|
|
37
|
-
}
|
|
38
|
-
export function spawnSubagent(options) {
|
|
39
|
-
const currentDepth = parseInt(process.env[DEPTH_ENV_VAR] ?? "0", 10);
|
|
40
|
-
if (currentDepth >= MAX_SUBAGENT_DEPTH) {
|
|
41
|
-
throw new Error(`Maximum subagent depth (${MAX_SUBAGENT_DEPTH}) exceeded`);
|
|
42
|
-
}
|
|
43
|
-
const args = ["--mode", "json", "-p", options.prompt];
|
|
44
|
-
if (options.model) {
|
|
45
|
-
args.push("--model", options.model);
|
|
46
|
-
}
|
|
47
|
-
if (options.sessionPath) {
|
|
48
|
-
args.push("--session", options.sessionPath);
|
|
49
|
-
}
|
|
50
|
-
if (options.permissionFlag) {
|
|
51
|
-
args.push("--permission", options.permissionFlag);
|
|
52
|
-
}
|
|
53
|
-
const invocation = getPiInvocation(args);
|
|
54
|
-
const childEnv = {
|
|
55
|
-
...process.env,
|
|
56
|
-
[DEPTH_ENV_VAR]: String(currentDepth + 1),
|
|
57
|
-
...options.env,
|
|
58
|
-
};
|
|
59
|
-
if (options.agentType) {
|
|
60
|
-
childEnv[AGENT_TYPE_ENV_VAR] = options.agentType;
|
|
61
|
-
}
|
|
62
|
-
const proc = spawn(invocation.command, invocation.args, {
|
|
63
|
-
cwd: options.cwd,
|
|
64
|
-
shell: false,
|
|
65
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
-
env: childEnv,
|
|
67
|
-
});
|
|
68
|
-
if (proc.pid) {
|
|
69
|
-
trackedPids.add(proc.pid);
|
|
70
|
-
ensureCleanupRegistered();
|
|
71
|
-
}
|
|
72
|
-
let buffer = "";
|
|
73
|
-
const accumulatedText = [];
|
|
74
|
-
const updateText = [];
|
|
75
|
-
const processLine = (line) => {
|
|
76
|
-
if (!line.trim())
|
|
77
|
-
return;
|
|
78
|
-
let event;
|
|
79
|
-
try {
|
|
80
|
-
event = JSON.parse(line);
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (typeof event === "object" && event !== null && "type" in event && "message" in event) {
|
|
86
|
-
const typedEvent = event;
|
|
87
|
-
// Accumulate text from message_update events
|
|
88
|
-
if (typedEvent.type === "message_update") {
|
|
89
|
-
const message = typedEvent.message;
|
|
90
|
-
if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
91
|
-
for (const part of message.content) {
|
|
92
|
-
if (part.type === "text" && part.text) {
|
|
93
|
-
updateText.push(part.text);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Extract final result from message_end, or fall back to accumulated update text
|
|
99
|
-
if (typedEvent.type === "message_end") {
|
|
100
|
-
const message = typedEvent.message;
|
|
101
|
-
if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
102
|
-
const endText = [];
|
|
103
|
-
for (const part of message.content) {
|
|
104
|
-
if (part.type === "text" && part.text) {
|
|
105
|
-
endText.push(part.text);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (endText.length > 0) {
|
|
109
|
-
// message_end has the final text - use it
|
|
110
|
-
accumulatedText.length = 0;
|
|
111
|
-
accumulatedText.push(...endText);
|
|
112
|
-
}
|
|
113
|
-
else if (updateText.length > 0) {
|
|
114
|
-
// Fall back to accumulated update text
|
|
115
|
-
accumulatedText.length = 0;
|
|
116
|
-
accumulatedText.push(...updateText);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (typeof event === "object" &&
|
|
122
|
-
event !== null &&
|
|
123
|
-
"type" in event &&
|
|
124
|
-
"toolCallId" in event &&
|
|
125
|
-
"toolName" in event) {
|
|
126
|
-
const typedEvent = event;
|
|
127
|
-
if (typeof typedEvent.toolCallId !== "string" || typeof typedEvent.toolName !== "string") {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
if (typedEvent.type === "tool_execution_start") {
|
|
131
|
-
emitSpawnEvent(options, {
|
|
132
|
-
type: "tool_execution_start",
|
|
133
|
-
toolCallId: typedEvent.toolCallId,
|
|
134
|
-
toolName: typedEvent.toolName,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
if (typedEvent.type === "tool_execution_end") {
|
|
138
|
-
emitSpawnEvent(options, {
|
|
139
|
-
type: "tool_execution_end",
|
|
140
|
-
toolCallId: typedEvent.toolCallId,
|
|
141
|
-
toolName: typedEvent.toolName,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
proc.stdout.on("data", (data) => {
|
|
147
|
-
buffer += data.toString();
|
|
148
|
-
const lines = buffer.split("\n");
|
|
149
|
-
buffer = lines.pop() ?? "";
|
|
150
|
-
for (const line of lines) {
|
|
151
|
-
processLine(line);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
if (options.signal) {
|
|
155
|
-
const killProc = () => {
|
|
156
|
-
if (proc.pid) {
|
|
157
|
-
try {
|
|
158
|
-
proc.kill("SIGTERM");
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
/* ignore */
|
|
162
|
-
}
|
|
163
|
-
setTimeout(() => {
|
|
164
|
-
if (!proc.killed && proc.pid) {
|
|
165
|
-
try {
|
|
166
|
-
proc.kill("SIGKILL");
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
/* ignore */
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}, 5000);
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
if (options.signal.aborted) {
|
|
176
|
-
killProc();
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
options.signal.addEventListener("abort", killProc, { once: true });
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const result = new Promise((resolve) => {
|
|
183
|
-
proc.on("close", (code) => {
|
|
184
|
-
if (buffer.trim()) {
|
|
185
|
-
processLine(buffer);
|
|
186
|
-
}
|
|
187
|
-
if (proc.pid) {
|
|
188
|
-
trackedPids.delete(proc.pid);
|
|
189
|
-
}
|
|
190
|
-
resolve({
|
|
191
|
-
text: accumulatedText.join(""),
|
|
192
|
-
exitCode: code ?? 0,
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
proc.on("error", () => {
|
|
196
|
-
if (proc.pid) {
|
|
197
|
-
trackedPids.delete(proc.pid);
|
|
198
|
-
}
|
|
199
|
-
resolve({
|
|
200
|
-
text: accumulatedText.join(""),
|
|
201
|
-
exitCode: 1,
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
return { process: proc, result };
|
|
206
|
-
}
|
|
207
|
-
//# sourceMappingURL=spawner.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spawner.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/spawner.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { APP_NAME } from \"../../../../config.js\";\nimport {\n\tAGENT_TYPE_ENV_VAR,\n\tDEPTH_ENV_VAR,\n\tMAX_SUBAGENT_DEPTH,\n\ttype SpawnEvent,\n\ttype SpawnedAgent,\n\ttype SpawnOptions,\n} from \"./types.js\";\n\nexport type { SpawnOptions, SpawnedAgent };\n\nexport function getPiInvocation(args: string[]): { command: string; args: string[] } {\n\tconst currentScript = process.argv[1];\n\tif (currentScript && fs.existsSync(currentScript)) {\n\t\treturn { command: process.execPath, args: [currentScript, ...args] };\n\t}\n\n\tconst execName = path.basename(process.execPath).toLowerCase();\n\tconst isGenericRuntime = /^(node|bun)(\\.exe)?$/.test(execName);\n\tif (!isGenericRuntime) {\n\t\treturn { command: process.execPath, args };\n\t}\n\n\treturn { command: APP_NAME, args };\n}\n\nconst trackedPids = new Set<number>();\n\nlet cleanupRegistered = false;\nfunction ensureCleanupRegistered(): void {\n\tif (cleanupRegistered) return;\n\tcleanupRegistered = true;\n\tprocess.on(\"exit\", () => {\n\t\tfor (const pid of trackedPids) {\n\t\t\ttry {\n\t\t\t\tprocess.kill(pid, \"SIGTERM\");\n\t\t\t} catch {\n\t\t\t\t/* process may already be dead */\n\t\t\t}\n\t\t}\n\t});\n}\n\ntype MessagePart = { type: string; text?: string };\ntype NdjsonMessage = { role: string; content: MessagePart[] };\n\nfunction emitSpawnEvent(options: SpawnOptions, event: SpawnEvent): void {\n\toptions.onEvent?.(event);\n}\n\nexport function spawnSubagent(options: SpawnOptions): SpawnedAgent {\n\tconst currentDepth = parseInt(process.env[DEPTH_ENV_VAR] ?? \"0\", 10);\n\tif (currentDepth >= MAX_SUBAGENT_DEPTH) {\n\t\tthrow new Error(`Maximum subagent depth (${MAX_SUBAGENT_DEPTH}) exceeded`);\n\t}\n\n\tconst args: string[] = [\"--mode\", \"json\", \"-p\", options.prompt];\n\n\tif (options.model) {\n\t\targs.push(\"--model\", options.model);\n\t}\n\n\tif (options.sessionPath) {\n\t\targs.push(\"--session\", options.sessionPath);\n\t}\n\n\tif (options.permissionFlag) {\n\t\targs.push(\"--permission\", options.permissionFlag);\n\t}\n\n\tconst invocation = getPiInvocation(args);\n\n\tconst childEnv: Record<string, string | undefined> = {\n\t\t...process.env,\n\t\t[DEPTH_ENV_VAR]: String(currentDepth + 1),\n\t\t...options.env,\n\t};\n\n\tif (options.agentType) {\n\t\tchildEnv[AGENT_TYPE_ENV_VAR] = options.agentType;\n\t}\n\n\tconst proc = spawn(invocation.command, invocation.args, {\n\t\tcwd: options.cwd,\n\t\tshell: false,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tenv: childEnv,\n\t});\n\n\tif (proc.pid) {\n\t\ttrackedPids.add(proc.pid);\n\t\tensureCleanupRegistered();\n\t}\n\n\tlet buffer = \"\";\n\tconst accumulatedText: string[] = [];\n\tconst updateText: string[] = [];\n\n\tconst processLine = (line: string): void => {\n\t\tif (!line.trim()) return;\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof event === \"object\" && event !== null && \"type\" in event && \"message\" in event) {\n\t\t\tconst typedEvent = event as { type: string; message: NdjsonMessage };\n\n\t\t\t// Accumulate text from message_update events\n\t\t\tif (typedEvent.type === \"message_update\") {\n\t\t\t\tconst message = typedEvent.message;\n\t\t\t\tif (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\t\tif (part.type === \"text\" && part.text) {\n\t\t\t\t\t\t\tupdateText.push(part.text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Extract final result from message_end, or fall back to accumulated update text\n\t\t\tif (typedEvent.type === \"message_end\") {\n\t\t\t\tconst message = typedEvent.message;\n\t\t\t\tif (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\t\t\tconst endText: string[] = [];\n\t\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\t\tif (part.type === \"text\" && part.text) {\n\t\t\t\t\t\t\tendText.push(part.text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (endText.length > 0) {\n\t\t\t\t\t\t// message_end has the final text - use it\n\t\t\t\t\t\taccumulatedText.length = 0;\n\t\t\t\t\t\taccumulatedText.push(...endText);\n\t\t\t\t\t} else if (updateText.length > 0) {\n\t\t\t\t\t\t// Fall back to accumulated update text\n\t\t\t\t\t\taccumulatedText.length = 0;\n\t\t\t\t\t\taccumulatedText.push(...updateText);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\ttypeof event === \"object\" &&\n\t\t\tevent !== null &&\n\t\t\t\"type\" in event &&\n\t\t\t\"toolCallId\" in event &&\n\t\t\t\"toolName\" in event\n\t\t) {\n\t\t\tconst typedEvent = event as {\n\t\t\t\ttype: string;\n\t\t\t\ttoolCallId: unknown;\n\t\t\t\ttoolName: unknown;\n\t\t\t};\n\n\t\t\tif (typeof typedEvent.toolCallId !== \"string\" || typeof typedEvent.toolName !== \"string\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (typedEvent.type === \"tool_execution_start\") {\n\t\t\t\temitSpawnEvent(options, {\n\t\t\t\t\ttype: \"tool_execution_start\",\n\t\t\t\t\ttoolCallId: typedEvent.toolCallId,\n\t\t\t\t\ttoolName: typedEvent.toolName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (typedEvent.type === \"tool_execution_end\") {\n\t\t\t\temitSpawnEvent(options, {\n\t\t\t\t\ttype: \"tool_execution_end\",\n\t\t\t\t\ttoolCallId: typedEvent.toolCallId,\n\t\t\t\t\ttoolName: typedEvent.toolName,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t};\n\n\tproc.stdout.on(\"data\", (data: Buffer) => {\n\t\tbuffer += data.toString();\n\t\tconst lines = buffer.split(\"\\n\");\n\t\tbuffer = lines.pop() ?? \"\";\n\t\tfor (const line of lines) {\n\t\t\tprocessLine(line);\n\t\t}\n\t});\n\n\tif (options.signal) {\n\t\tconst killProc = (): void => {\n\t\t\tif (proc.pid) {\n\t\t\t\ttry {\n\t\t\t\t\tproc.kill(\"SIGTERM\");\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (!proc.killed && proc.pid) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tproc.kill(\"SIGKILL\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* ignore */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, 5000);\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal.aborted) {\n\t\t\tkillProc();\n\t\t} else {\n\t\t\toptions.signal.addEventListener(\"abort\", killProc, { once: true });\n\t\t}\n\t}\n\n\tconst result = new Promise<{ text: string; exitCode: number }>((resolve) => {\n\t\tproc.on(\"close\", (code) => {\n\t\t\tif (buffer.trim()) {\n\t\t\t\tprocessLine(buffer);\n\t\t\t}\n\n\t\t\tif (proc.pid) {\n\t\t\t\ttrackedPids.delete(proc.pid);\n\t\t\t}\n\n\t\t\tresolve({\n\t\t\t\ttext: accumulatedText.join(\"\"),\n\t\t\t\texitCode: code ?? 0,\n\t\t\t});\n\t\t});\n\n\t\tproc.on(\"error\", () => {\n\t\t\tif (proc.pid) {\n\t\t\t\ttrackedPids.delete(proc.pid);\n\t\t\t}\n\t\t\tresolve({\n\t\t\t\ttext: accumulatedText.join(\"\"),\n\t\t\t\texitCode: 1,\n\t\t\t});\n\t\t});\n\t});\n\n\treturn { process: proc, result };\n}\n"]}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Type } from "typebox";
|
|
2
|
-
import type { ExtensionAPI, ToolDefinition } from "../../types.js";
|
|
3
|
-
import type { BackgroundManager } from "./manager.js";
|
|
4
|
-
import type { spawnSubagent } from "./spawner.js";
|
|
5
|
-
type TaskToolDetails = {
|
|
6
|
-
agentType?: string;
|
|
7
|
-
model?: string;
|
|
8
|
-
activeToolNames?: string[];
|
|
9
|
-
};
|
|
10
|
-
declare const TaskToolParams: Type.TObject<{
|
|
11
|
-
description: Type.TString;
|
|
12
|
-
prompt: Type.TString;
|
|
13
|
-
run_in_background: Type.TBoolean;
|
|
14
|
-
session_id: Type.TOptional<Type.TString>;
|
|
15
|
-
model: Type.TOptional<Type.TString>;
|
|
16
|
-
agent_type: Type.TOptional<Type.TString>;
|
|
17
|
-
}>;
|
|
18
|
-
export declare function createTaskTool(manager: BackgroundManager, spawner: typeof spawnSubagent, pi: ExtensionAPI, agentDescriptions?: string): ToolDefinition<typeof TaskToolParams, TaskToolDetails>;
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=task-tool.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"task-tool.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/task-tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAoB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,KAAK,eAAe,GAAG;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAmCF,QAAA,MAAM,cAAc;;;;;;;EAmBlB,CAAC;AA8BH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,aAAa,EAC7B,EAAE,EAAE,YAAY,EAChB,iBAAiB,CAAC,EAAE,MAAM,GACxB,cAAc,CAAC,OAAO,cAAc,EAAE,eAAe,CAAC,CA+RxD","sourcesContent":["import { Text } from \"@earendil-works/pi-tui\";\nimport { Type } from \"typebox\";\nimport type { ExtensionAPI, ExtensionContext, ToolDefinition } from \"../../types.js\";\nimport type { BackgroundManager } from \"./manager.js\";\nimport type { spawnSubagent } from \"./spawner.js\";\nimport { DEPTH_ENV_VAR, MAX_SUBAGENT_DEPTH, TASK_ENTRY_TYPE } from \"./types.js\";\n\ntype TaskToolDetails = {\n\tagentType?: string;\n\tmodel?: string;\n\tactiveToolNames?: string[];\n};\n\nfunction buildTaskMetadata(args: {\n\tdescription: string;\n\trunInBackground: boolean;\n\tagentType?: string;\n\tmodel?: string;\n\tactiveToolNames?: string[];\n}): { headline: string; overview?: string } {\n\tconst overview = [\n\t\targs.agentType,\n\t\targs.model,\n\t\targs.activeToolNames && args.activeToolNames.length > 0 ? `tools: ${args.activeToolNames.join(\", \")}` : undefined,\n\t].filter((value): value is string => typeof value === \"string\" && value.length > 0);\n\n\treturn {\n\t\theadline: `${args.description}${args.runInBackground ? \" [async]\" : \" [sync]\"}`,\n\t\t...(overview.length > 0 ? { overview: overview.join(\" · \") } : {}),\n\t};\n}\n\nfunction buildTaskOverviewItems(details: TaskToolDetails | undefined): string[] {\n\tif (!details) {\n\t\treturn [];\n\t}\n\n\treturn [\n\t\tdetails.agentType,\n\t\tdetails.model,\n\t\tdetails.activeToolNames && details.activeToolNames.length > 0\n\t\t\t? `tools: ${details.activeToolNames.join(\", \")}`\n\t\t\t: undefined,\n\t].filter((value): value is string => typeof value === \"string\" && value.length > 0);\n}\n\nconst TaskToolParams = Type.Object({\n\tdescription: Type.String({\n\t\tdescription: \"A short (3-5 words) description of the task\",\n\t}),\n\tprompt: Type.String({\n\t\tdescription: \"The task for the agent to perform\",\n\t}),\n\trun_in_background: Type.Boolean({\n\t\tdescription:\n\t\t\t\"REQUIRED. true=async (returns task_id, system notifies on completion), false=sync (waits for result).\",\n\t}),\n\tsession_id: Type.Optional(Type.String({ description: \"Existing Task session to continue\" })),\n\tmodel: Type.Optional(Type.String({ description: \"Model to use for this task\" })),\n\tagent_type: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Agent type to use for this task (e.g. 'explore', 'general'). Determines available tools and permissions.\",\n\t\t}),\n\t),\n});\n\nfunction createErrorResult(text: string): {\n\tcontent: [{ type: \"text\"; text: string }];\n\tdetails: TaskToolDetails;\n\tisError: true;\n} {\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails: {},\n\t\tisError: true,\n\t};\n}\n\nfunction resolveModel(params: { model?: string }, ctx: ExtensionContext): string | undefined {\n\tif (params.model) {\n\t\treturn params.model;\n\t}\n\n\tif (!ctx.model) {\n\t\treturn undefined;\n\t}\n\n\treturn `${ctx.model.provider}/${ctx.model.id}`;\n}\n\nfunction isCancelledTask(manager: BackgroundManager, taskId: string): boolean {\n\treturn manager.getTask(taskId)?.status === \"cancelled\";\n}\n\nexport function createTaskTool(\n\tmanager: BackgroundManager,\n\tspawner: typeof spawnSubagent,\n\tpi: ExtensionAPI,\n\tagentDescriptions?: string,\n): ToolDefinition<typeof TaskToolParams, TaskToolDetails> {\n\tconst baseDescription = `Run a sub-agent in sync or async mode.\n\nSync mode (run_in_background=false): waits for the sub-agent to finish and returns its text output directly.\nAsync mode (run_in_background=true): starts the sub-agent, returns a task_id immediately. System sends a notification when done.\n\nsession_id is optional and continues an existing session when provided.\nmodel is optional and defaults to the current model.`;\n\n\tconst fullDescription = agentDescriptions\n\t\t? `${baseDescription}\\n\\nAvailable agent types:\\n${agentDescriptions}`\n\t\t: baseDescription;\n\n\tconst taskTool: ToolDefinition<typeof TaskToolParams, TaskToolDetails> = {\n\t\tname: \"task\",\n\t\tlabel: \"Task\",\n\t\tdescription: fullDescription,\n\t\tpromptSnippet:\n\t\t\t\"Run a sub-agent either synchronously for direct output or asynchronously for a background task_id.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use run_in_background=false when you need the sub-agent result in the same turn.\",\n\t\t\t\"Use run_in_background=true for parallel work. System notifies on completion via <system-reminder>.\",\n\t\t\t\"After launching a background task, do NOT call background_output - wait for the <system-reminder> notification first.\",\n\t\t\t\"Pass session_id to continue an existing sub-agent session.\",\n\t\t],\n\t\tparameters: TaskToolParams,\n\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx) {\n\t\t\tif (!params.description.trim()) {\n\t\t\t\treturn createErrorResult(\"Error: description is required\");\n\t\t\t}\n\n\t\t\tconst currentDepth = Number.parseInt(process.env[DEPTH_ENV_VAR] ?? \"0\", 10);\n\t\t\tif (currentDepth >= MAX_SUBAGENT_DEPTH) {\n\t\t\t\treturn createErrorResult(`Error: max subagent depth (${MAX_SUBAGENT_DEPTH}) exceeded`);\n\t\t\t}\n\n\t\t\tconst model = resolveModel(params, ctx);\n\t\t\tconst taskDetails: TaskToolDetails = {\n\t\t\t\tagentType: params.agent_type,\n\t\t\t\tmodel,\n\t\t\t\tactiveToolNames: [],\n\t\t\t};\n\n\t\t\tconst permissionFlag =\n\t\t\t\ttypeof pi.getFlag === \"function\"\n\t\t\t\t\t? ((pi.getFlag(\"permission\") as string | undefined) ?? undefined)\n\t\t\t\t\t: undefined;\n\n\t\t\tif (!params.run_in_background) {\n\t\t\t\tconst spawned = spawner({\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\tmodel,\n\t\t\t\t\tagentType: params.agent_type,\n\t\t\t\t\tsessionPath: params.session_id,\n\t\t\t\t\tpermissionFlag: permissionFlag ?? undefined,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tconst result = await spawned.result;\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: result.text || \"(no output)\" }],\n\t\t\t\t\tdetails: taskDetails,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tlet executeReturned = false;\n\t\t\ttry {\n\t\t\t\tconst task = manager.launch({\n\t\t\t\t\tdescription: params.description,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\tmodel,\n\t\t\t\t\tagentType: params.agent_type,\n\t\t\t\t\tpid: undefined,\n\t\t\t\t\tsessionPath: params.session_id,\n\t\t\t\t\tactiveToolNames: [],\n\t\t\t\t\tcompletedAt: undefined,\n\t\t\t\t\tresult: undefined,\n\t\t\t\t\terror: undefined,\n\t\t\t\t\tparentSessionId: \"unknown\",\n\t\t\t\t});\n\n\t\t\t\tpi.appendEntry(TASK_ENTRY_TYPE, task);\n\n\t\t\t\tconst spawned = spawner({\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\tmodel,\n\t\t\t\t\tagentType: params.agent_type,\n\t\t\t\t\tsessionPath: params.session_id,\n\t\t\t\t\tpermissionFlag: permissionFlag ?? undefined,\n\t\t\t\t\tsignal,\n\t\t\t\t\tonEvent: (() => {\n\t\t\t\t\t\tconst activeToolCalls = new Map<string, string>();\n\t\t\t\t\t\treturn (event) => {\n\t\t\t\t\t\t\tif (event.type === \"tool_execution_start\") {\n\t\t\t\t\t\t\t\tactiveToolCalls.set(event.toolCallId, event.toolName);\n\t\t\t\t\t\t\t} else if (event.type === \"tool_execution_end\") {\n\t\t\t\t\t\t\t\tactiveToolCalls.delete(event.toolCallId);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst currentTask = manager.getTask(task.id);\n\t\t\t\t\t\t\tif (!currentTask) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst activeToolNames = Array.from(activeToolCalls.values());\n\n\t\t\t\t\t\t\tmanager.updateTask(task.id, {\n\t\t\t\t\t\t\t\tactiveToolNames,\n\t\t\t\t\t\t\t\t...(activeToolCalls.size > 0 && currentTask.status === \"pending\" ? { status: \"running\" } : {}),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (!executeReturned) {\n\t\t\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\t\t\tcontent: [],\n\t\t\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\t\t\t...taskDetails,\n\t\t\t\t\t\t\t\t\t\tactiveToolNames,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst activeTask = manager.getTask(task.id);\n\t\t\t\t\t\t\tif (activeTask) {\n\t\t\t\t\t\t\t\tpi.appendEntry(TASK_ENTRY_TYPE, activeTask);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t})(),\n\t\t\t\t});\n\n\t\t\t\tif (spawned.process.pid !== undefined) {\n\t\t\t\t\tmanager.updateTask(task.id, { pid: spawned.process.pid, status: \"running\" });\n\t\t\t\t\tonUpdate?.({ content: [], details: taskDetails });\n\t\t\t\t\tconst runningTask = manager.getTask(task.id);\n\t\t\t\t\tif (runningTask) {\n\t\t\t\t\t\tpi.appendEntry(TASK_ENTRY_TYPE, runningTask);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tspawned.result\n\t\t\t\t\t.then((result) => {\n\t\t\t\t\t\tif (isCancelledTask(manager, task.id)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmanager.updateTask(task.id, {\n\t\t\t\t\t\t\tstatus: result.exitCode === 0 ? \"completed\" : \"error\",\n\t\t\t\t\t\t\tcompletedAt: new Date(),\n\t\t\t\t\t\t\tactiveToolNames: [],\n\t\t\t\t\t\t\tresult: result.text,\n\t\t\t\t\t\t\terror: result.exitCode === 0 ? undefined : `Sub-agent exited with code ${result.exitCode}`,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst completedTask = manager.getTask(task.id);\n\t\t\t\t\t\tif (completedTask) {\n\t\t\t\t\t\t\tpi.appendEntry(TASK_ENTRY_TYPE, completedTask);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpi.sendMessage(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcustomType: \"background-task.complete\",\n\t\t\t\t\t\t\t\tdisplay: true,\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: [\n\t\t\t\t\t\t\t\t\t\t\t`Task ${task.id} completed: ${params.description}`,\n\t\t\t\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t\t\t\t\"Result:\",\n\t\t\t\t\t\t\t\t\t\t\tresult.text || \"(no output)\",\n\t\t\t\t\t\t\t\t\t\t].join(\"\\n\"),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t\t\t\t\t\t);\n\t\t\t\t\t})\n\t\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\t\tif (isCancelledTask(manager, task.id)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmanager.updateTask(task.id, {\n\t\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\t\tcompletedAt: new Date(),\n\t\t\t\t\t\t\tactiveToolNames: [],\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst erroredTask = manager.getTask(task.id);\n\t\t\t\t\t\tif (erroredTask) {\n\t\t\t\t\t\t\tpi.appendEntry(TASK_ENTRY_TYPE, erroredTask);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\tpi.sendMessage(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcustomType: \"background-task.complete\",\n\t\t\t\t\t\t\t\tdisplay: true,\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: [`Task ${task.id} failed: ${params.description}`, \"\", \"Error:\", errorMsg].join(\n\t\t\t\t\t\t\t\t\t\t\t\"\\n\",\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\texecuteReturned = true;\n\n\t\t\t\tconst sessionIdBlock = params.session_id\n\t\t\t\t\t? `\\n\\n<task_metadata>\\nsession_id: ${params.session_id}\\ntask_id: ${task.id}\\nbackground_task_id: ${task.id}\\n</task_metadata>\\n\\nto continue: task(session_id=\"${params.session_id}\", run_in_background=false, prompt=\"...\")`\n\t\t\t\t\t: `\\n\\n<task_metadata>\\ntask_id: ${task.id}\\nbackground_task_id: ${task.id}\\n</task_metadata>`;\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Background task launched.\n\nBackground Task ID: ${task.id}\nDescription: ${params.description}\nAgent: ${params.agent_type ?? \"default\"}\nStatus: ${task.status}\n\nSystem notifies on completion. Use \\`background_output\\` with task_id=\"${task.id}\" to check.\n\nDo NOT call background_output now. Wait for <system-reminder> notification first.${sessionIdBlock}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: taskDetails,\n\t\t\t\t};\n\t\t\t} catch (error: unknown) {\n\t\t\t\treturn createErrorResult(error instanceof Error ? error.message : String(error));\n\t\t\t}\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\tconst metadata = buildTaskMetadata({\n\t\t\t\tdescription: args.description,\n\t\t\t\trunInBackground: args.run_in_background,\n\t\t\t\tagentType: args.agent_type,\n\t\t\t\tmodel: args.model,\n\t\t\t});\n\t\t\treturn new Text(\n\t\t\t\t[\n\t\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"Task \")) + theme.fg(\"accent\", metadata.headline),\n\t\t\t\t\t...(metadata.overview ? [theme.fg(\"muted\", ` ${metadata.overview}`)] : []),\n\t\t\t\t].join(\"\\n\"),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst firstContent = result.content[0];\n\t\t\tconst text = firstContent?.type === \"text\" ? firstContent.text : undefined;\n\t\t\tconst argDetails: TaskToolDetails = {\n\t\t\t\tagentType: context.args.agent_type,\n\t\t\t\tmodel: context.args.model,\n\t\t\t};\n\t\t\tconst callOverviewItems = new Set(buildTaskOverviewItems(argDetails));\n\t\t\tconst extraOverviewItems = buildTaskOverviewItems(result.details).filter(\n\t\t\t\t(item) => !callOverviewItems.has(item),\n\t\t\t);\n\t\t\tconst overview = extraOverviewItems.length > 0 ? extraOverviewItems.join(\" · \") : undefined;\n\n\t\t\tif (!text && !overview) {\n\t\t\t\treturn new Text(\"\", 0, 0);\n\t\t\t}\n\n\t\t\tif (options.isPartial && !text) {\n\t\t\t\treturn new Text(overview ? theme.fg(\"muted\", overview) : \"\", 0, 0);\n\t\t\t}\n\n\t\t\treturn new Text(\n\t\t\t\t[...(overview ? [theme.fg(\"muted\", overview)] : []), ...(text ? [theme.fg(\"muted\", text)] : [])].join(\n\t\t\t\t\t\"\\n\\n\",\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t);\n\t\t},\n\t};\n\n\treturn taskTool;\n}\n"]}
|