@code-yeongyu/senpi 2026.5.14 → 2026.5.15-2
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 +1107 -1182
- 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 +109 -7
- 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/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +157 -29
- 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 +82 -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/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/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 +7 -7
- package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
- package/dist/core/extensions/builtin/system-messages.js +10 -10
- package/dist/core/extensions/builtin/system-messages.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts +1 -1
- package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/continuation/prompt.js +1 -1
- package/dist/core/extensions/builtin/todotools/continuation/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/state.d.ts +1 -1
- package/dist/core/extensions/builtin/todotools/state.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/state.js +1 -1
- package/dist/core/extensions/builtin/todotools/state.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/system-messages.d.ts +3 -3
- package/dist/core/extensions/builtin/todotools/system-messages.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/system-messages.js +6 -6
- package/dist/core/extensions/builtin/todotools/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/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 +11 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +96 -49
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/extensions.md +0 -1
- package/docs/index.md +0 -1
- 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,114 +0,0 @@
|
|
|
1
|
-
import { MAX_CONCURRENT_TASKS } from "./types.js";
|
|
2
|
-
export class BackgroundManager {
|
|
3
|
-
tasks;
|
|
4
|
-
constructor() {
|
|
5
|
-
this.tasks = new Map();
|
|
6
|
-
}
|
|
7
|
-
launch(task) {
|
|
8
|
-
const activeTasks = this.getActiveTasks();
|
|
9
|
-
if (activeTasks.length >= MAX_CONCURRENT_TASKS) {
|
|
10
|
-
throw new Error(`Maximum concurrent tasks (${MAX_CONCURRENT_TASKS}) reached. Cancel some tasks before launching new ones.`);
|
|
11
|
-
}
|
|
12
|
-
const newTask = {
|
|
13
|
-
...task,
|
|
14
|
-
id: generateTaskId(),
|
|
15
|
-
status: "pending",
|
|
16
|
-
startedAt: new Date(),
|
|
17
|
-
};
|
|
18
|
-
this.tasks.set(newTask.id, newTask);
|
|
19
|
-
return newTask;
|
|
20
|
-
}
|
|
21
|
-
getTask(id) {
|
|
22
|
-
return this.tasks.get(id);
|
|
23
|
-
}
|
|
24
|
-
getAllTasks() {
|
|
25
|
-
return Array.from(this.tasks.values());
|
|
26
|
-
}
|
|
27
|
-
getActiveTasks() {
|
|
28
|
-
return this.getAllTasks().filter((task) => task.status === "pending" || task.status === "running");
|
|
29
|
-
}
|
|
30
|
-
updateTask(id, updates) {
|
|
31
|
-
const task = this.tasks.get(id);
|
|
32
|
-
if (!task) {
|
|
33
|
-
throw new Error(`Task ${id} not found`);
|
|
34
|
-
}
|
|
35
|
-
const updatedTask = { ...task, ...updates };
|
|
36
|
-
this.tasks.set(id, updatedTask);
|
|
37
|
-
}
|
|
38
|
-
cancelTask(id) {
|
|
39
|
-
const task = this.tasks.get(id);
|
|
40
|
-
if (!task) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
if (task.status === "pending" || task.status === "running") {
|
|
44
|
-
this.tasks.set(id, { ...task, status: "cancelled" });
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
cancelAll() {
|
|
50
|
-
const cancelled = [];
|
|
51
|
-
for (const task of this.tasks.values()) {
|
|
52
|
-
if (task.status === "pending" || task.status === "running") {
|
|
53
|
-
this.tasks.set(task.id, { ...task, status: "cancelled" });
|
|
54
|
-
cancelled.push(this.tasks.get(task.id));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return cancelled;
|
|
58
|
-
}
|
|
59
|
-
getTasksByParent(parentSessionId) {
|
|
60
|
-
return this.getAllTasks().filter((task) => task.parentSessionId === parentSessionId);
|
|
61
|
-
}
|
|
62
|
-
restoreTask(task) {
|
|
63
|
-
this.tasks.set(task.id, task);
|
|
64
|
-
}
|
|
65
|
-
clearTasks() {
|
|
66
|
-
this.tasks.clear();
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function formatTaskMetadata(task) {
|
|
70
|
-
const metadata = [task.agentType, task.model].filter((value) => typeof value === "string" && value.length > 0);
|
|
71
|
-
if (metadata.length === 0) {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
return metadata.join(" · ");
|
|
75
|
-
}
|
|
76
|
-
function formatActiveTools(task) {
|
|
77
|
-
if (task.activeToolNames.length === 0) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
const counts = new Map();
|
|
81
|
-
for (const toolName of task.activeToolNames) {
|
|
82
|
-
counts.set(toolName, (counts.get(toolName) ?? 0) + 1);
|
|
83
|
-
}
|
|
84
|
-
return Array.from(counts.entries())
|
|
85
|
-
.map(([toolName, count]) => (count > 1 ? `${toolName}×${count}` : toolName))
|
|
86
|
-
.join(", ");
|
|
87
|
-
}
|
|
88
|
-
export function getWidgetLines(manager) {
|
|
89
|
-
const activeTasks = manager.getActiveTasks();
|
|
90
|
-
if (activeTasks.length === 0) {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
const taskLines = activeTasks.flatMap((task) => {
|
|
94
|
-
const indicator = task.status === "pending" ? "[⏳]" : "[▶]";
|
|
95
|
-
const lines = [`${indicator} ${task.description}`];
|
|
96
|
-
const metadata = formatTaskMetadata(task);
|
|
97
|
-
const activeTools = formatActiveTools(task);
|
|
98
|
-
if (metadata) {
|
|
99
|
-
lines.push(` ${metadata}`);
|
|
100
|
-
}
|
|
101
|
-
if (activeTools) {
|
|
102
|
-
lines.push(` tools: ${activeTools}`);
|
|
103
|
-
}
|
|
104
|
-
return lines;
|
|
105
|
-
});
|
|
106
|
-
return ["Background Tasks", ...taskLines];
|
|
107
|
-
}
|
|
108
|
-
function generateTaskId() {
|
|
109
|
-
return ("bg_" +
|
|
110
|
-
Math.floor(Math.random() * 0xffffffff)
|
|
111
|
-
.toString(16)
|
|
112
|
-
.padStart(8, "0"));
|
|
113
|
-
}
|
|
114
|
-
//# sourceMappingURL=manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BackgroundTask } from \"./types.js\";\nimport { MAX_CONCURRENT_TASKS } from \"./types.js\";\n\nexport class BackgroundManager {\n\tprivate tasks: Map<string, BackgroundTask>;\n\n\tconstructor() {\n\t\tthis.tasks = new Map();\n\t}\n\n\tlaunch(task: Omit<BackgroundTask, \"id\" | \"status\" | \"startedAt\">): BackgroundTask {\n\t\tconst activeTasks = this.getActiveTasks();\n\t\tif (activeTasks.length >= MAX_CONCURRENT_TASKS) {\n\t\t\tthrow new Error(\n\t\t\t\t`Maximum concurrent tasks (${MAX_CONCURRENT_TASKS}) reached. Cancel some tasks before launching new ones.`,\n\t\t\t);\n\t\t}\n\n\t\tconst newTask: BackgroundTask = {\n\t\t\t...task,\n\t\t\tid: generateTaskId(),\n\t\t\tstatus: \"pending\",\n\t\t\tstartedAt: new Date(),\n\t\t};\n\n\t\tthis.tasks.set(newTask.id, newTask);\n\t\treturn newTask;\n\t}\n\n\tgetTask(id: string): BackgroundTask | undefined {\n\t\treturn this.tasks.get(id);\n\t}\n\n\tgetAllTasks(): BackgroundTask[] {\n\t\treturn Array.from(this.tasks.values());\n\t}\n\n\tgetActiveTasks(): BackgroundTask[] {\n\t\treturn this.getAllTasks().filter((task) => task.status === \"pending\" || task.status === \"running\");\n\t}\n\n\tupdateTask(id: string, updates: Partial<BackgroundTask>): void {\n\t\tconst task = this.tasks.get(id);\n\t\tif (!task) {\n\t\t\tthrow new Error(`Task ${id} not found`);\n\t\t}\n\n\t\tconst updatedTask = { ...task, ...updates };\n\t\tthis.tasks.set(id, updatedTask);\n\t}\n\n\tcancelTask(id: string): boolean {\n\t\tconst task = this.tasks.get(id);\n\t\tif (!task) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (task.status === \"pending\" || task.status === \"running\") {\n\t\t\tthis.tasks.set(id, { ...task, status: \"cancelled\" });\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tcancelAll(): BackgroundTask[] {\n\t\tconst cancelled: BackgroundTask[] = [];\n\n\t\tfor (const task of this.tasks.values()) {\n\t\t\tif (task.status === \"pending\" || task.status === \"running\") {\n\t\t\t\tthis.tasks.set(task.id, { ...task, status: \"cancelled\" });\n\t\t\t\tcancelled.push(this.tasks.get(task.id)!);\n\t\t\t}\n\t\t}\n\n\t\treturn cancelled;\n\t}\n\n\tgetTasksByParent(parentSessionId: string): BackgroundTask[] {\n\t\treturn this.getAllTasks().filter((task) => task.parentSessionId === parentSessionId);\n\t}\n\n\trestoreTask(task: BackgroundTask): void {\n\t\tthis.tasks.set(task.id, task);\n\t}\n\n\tclearTasks(): void {\n\t\tthis.tasks.clear();\n\t}\n}\n\nfunction formatTaskMetadata(task: BackgroundTask): string | undefined {\n\tconst metadata = [task.agentType, task.model].filter(\n\t\t(value): value is string => typeof value === \"string\" && value.length > 0,\n\t);\n\tif (metadata.length === 0) {\n\t\treturn undefined;\n\t}\n\treturn metadata.join(\" · \");\n}\n\nfunction formatActiveTools(task: BackgroundTask): string | undefined {\n\tif (task.activeToolNames.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst counts = new Map<string, number>();\n\tfor (const toolName of task.activeToolNames) {\n\t\tcounts.set(toolName, (counts.get(toolName) ?? 0) + 1);\n\t}\n\n\treturn Array.from(counts.entries())\n\t\t.map(([toolName, count]) => (count > 1 ? `${toolName}×${count}` : toolName))\n\t\t.join(\", \");\n}\n\nexport function getWidgetLines(manager: BackgroundManager): string[] | undefined {\n\tconst activeTasks = manager.getActiveTasks();\n\n\tif (activeTasks.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst taskLines = activeTasks.flatMap((task) => {\n\t\tconst indicator = task.status === \"pending\" ? \"[⏳]\" : \"[▶]\";\n\t\tconst lines = [`${indicator} ${task.description}`];\n\t\tconst metadata = formatTaskMetadata(task);\n\t\tconst activeTools = formatActiveTools(task);\n\n\t\tif (metadata) {\n\t\t\tlines.push(` ${metadata}`);\n\t\t}\n\n\t\tif (activeTools) {\n\t\t\tlines.push(` tools: ${activeTools}`);\n\t\t}\n\n\t\treturn lines;\n\t});\n\n\treturn [\"Background Tasks\", ...taskLines];\n}\n\nfunction generateTaskId(): string {\n\treturn (\n\t\t\"bg_\" +\n\t\tMath.floor(Math.random() * 0xffffffff)\n\t\t\t.toString(16)\n\t\t\t.padStart(8, \"0\")\n\t);\n}\n"]}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "../../types.js";
|
|
2
|
-
import type { BackgroundManager } from "./manager.js";
|
|
3
|
-
import type { BackgroundTask } from "./types.js";
|
|
4
|
-
export type NotificationStatus = "COMPLETED" | "CANCELLED" | "ERROR";
|
|
5
|
-
export interface NotificationTask {
|
|
6
|
-
id: string;
|
|
7
|
-
description: string;
|
|
8
|
-
status: BackgroundTask["status"];
|
|
9
|
-
error?: string;
|
|
10
|
-
result?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface NotificationInput {
|
|
13
|
-
task: NotificationTask;
|
|
14
|
-
duration: string;
|
|
15
|
-
statusText: NotificationStatus;
|
|
16
|
-
allComplete: boolean;
|
|
17
|
-
remainingCount: number;
|
|
18
|
-
completedTasks: NotificationTask[];
|
|
19
|
-
}
|
|
20
|
-
export declare function formatCompletionNotification(input: NotificationInput): string;
|
|
21
|
-
export declare function sendCompletionNotification(pi: ExtensionAPI, task: BackgroundTask, manager: BackgroundManager): void;
|
|
22
|
-
//# sourceMappingURL=notification.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/notification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAMD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAqB7E;AAsED,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAkCnH","sourcesContent":["import type { ExtensionAPI } from \"../../types.js\";\nimport { sendBuiltinCustomMessage } from \"../system-messages.js\";\nimport type { BackgroundManager } from \"./manager.js\";\nimport type { BackgroundTask } from \"./types.js\";\n\nexport type NotificationStatus = \"COMPLETED\" | \"CANCELLED\" | \"ERROR\";\n\nexport interface NotificationTask {\n\tid: string;\n\tdescription: string;\n\tstatus: BackgroundTask[\"status\"];\n\terror?: string;\n\tresult?: string;\n}\n\nexport interface NotificationInput {\n\ttask: NotificationTask;\n\tduration: string;\n\tstatusText: NotificationStatus;\n\tallComplete: boolean;\n\tremainingCount: number;\n\tcompletedTasks: NotificationTask[];\n}\n\nfunction safeDescription(task: NotificationTask): string {\n\treturn task.description || task.id;\n}\n\nexport function formatCompletionNotification(input: NotificationInput): string {\n\tconst { task, duration, statusText, allComplete, remainingCount, completedTasks } = input;\n\tconst errorInfo = task.error ? `\\n**Error:** ${task.error}` : \"\";\n\n\tif (allComplete) {\n\t\treturn formatAllCompleteNotification(task, completedTasks);\n\t}\n\n\tconst isFailure = statusText !== \"COMPLETED\";\n\n\treturn `<system-reminder>\n[BACKGROUND TASK ${statusText}]\n**ID:** \\`${task.id}\\`\n**Description:** ${safeDescription(task)}\n**Duration:** ${duration}${errorInfo}\n\n**${remainingCount} task${remainingCount === 1 ? \"\" : \"s\"} still in progress.** You WILL be notified when ALL complete.\n${isFailure ? \"**ACTION REQUIRED:** This task failed. Check the error and decide whether to retry, cancel remaining tasks, or continue.\" : \"Do NOT poll - continue productive work.\"}\n\nUse \\`background_output(task_id=\"${task.id}\")\\` to retrieve this result when ready.\n</system-reminder>`;\n}\n\nfunction formatAllCompleteNotification(task: NotificationTask, completedTasks: NotificationTask[]): string {\n\tconst succeededTasks = completedTasks.filter((t) => t.status === \"completed\");\n\tconst failedTasks = completedTasks.filter((t) => t.status !== \"completed\");\n\n\tconst succeededText =\n\t\tsucceededTasks.length > 0 ? succeededTasks.map((t) => `- \\`${t.id}\\`: ${safeDescription(t)}`).join(\"\\n\") : \"\";\n\tconst failedText =\n\t\tfailedTasks.length > 0\n\t\t\t? failedTasks\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(t) =>\n\t\t\t\t\t\t\t`- \\`${t.id}\\`: ${safeDescription(t)} [${t.status.toUpperCase()}]${t.error ? ` - ${t.error}` : \"\"}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\\n\")\n\t\t\t: \"\";\n\n\tconst hasFailures = failedTasks.length > 0;\n\tconst header = hasFailures\n\t\t? `[ALL BACKGROUND TASKS FINISHED - ${failedTasks.length} FAILED]`\n\t\t: \"[ALL BACKGROUND TASKS COMPLETE]\";\n\n\tlet body = \"\";\n\tif (succeededText) {\n\t\tbody += `**Completed:**\\n${succeededText}\\n`;\n\t}\n\tif (failedText) {\n\t\tbody += `\\n**Failed:**\\n${failedText}\\n`;\n\t}\n\tif (!body) {\n\t\tbody = `- \\`${task.id}\\`: ${safeDescription(task)} [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : \"\"}\\n`;\n\t}\n\n\treturn `<system-reminder>\n${header}\n\n${body.trim()}\n\nUse \\`background_output(task_id=\"<id>\")\\` to retrieve each result.${hasFailures ? `\\n\\n**ACTION REQUIRED:** ${failedTasks.length} task(s) failed. Check errors above and decide whether to retry or proceed.` : \"\"}\n</system-reminder>`;\n}\n\nfunction formatDuration(startedAt: Date, completedAt: Date | undefined): string {\n\tconst end = completedAt ?? new Date();\n\tconst milliseconds = end.getTime() - startedAt.getTime();\n\tif (milliseconds < 1000) {\n\t\treturn `${milliseconds}ms`;\n\t}\n\tif (milliseconds < 60000) {\n\t\treturn `${(milliseconds / 1000).toFixed(1)}s`;\n\t}\n\treturn `${Math.floor(milliseconds / 60000)}m ${Math.floor((milliseconds % 60000) / 1000)}s`;\n}\n\nfunction mapStatus(task: BackgroundTask): NotificationStatus {\n\tswitch (task.status) {\n\t\tcase \"cancelled\":\n\t\t\treturn \"CANCELLED\";\n\t\tcase \"error\":\n\t\t\treturn \"ERROR\";\n\t\tdefault:\n\t\t\treturn \"COMPLETED\";\n\t}\n}\n\nfunction isTerminalStatus(status: BackgroundTask[\"status\"]): boolean {\n\treturn status === \"completed\" || status === \"error\" || status === \"cancelled\";\n}\n\nexport function sendCompletionNotification(pi: ExtensionAPI, task: BackgroundTask, manager: BackgroundManager): void {\n\tconst activeTasks = manager.getActiveTasks();\n\tconst allComplete = activeTasks.length === 0;\n\tconst remainingCount = activeTasks.length;\n\n\tconst completedTasks: NotificationTask[] = allComplete\n\t\t? manager\n\t\t\t\t.getAllTasks()\n\t\t\t\t.filter((t) => isTerminalStatus(t.status))\n\t\t\t\t.map((t) => ({ id: t.id, description: t.description, status: t.status, error: t.error, result: t.result }))\n\t\t: [];\n\n\tconst duration = formatDuration(task.startedAt, task.completedAt);\n\tconst statusText = mapStatus(task);\n\n\tconst message = formatCompletionNotification({\n\t\ttask: { id: task.id, description: task.description, status: task.status, error: task.error, result: task.result },\n\t\tduration,\n\t\tstatusText,\n\t\tallComplete,\n\t\tremainingCount,\n\t\tcompletedTasks,\n\t});\n\n\tsendBuiltinCustomMessage(\n\t\tpi,\n\t\t\"background-task.notification\",\n\t\t{\n\t\t\tcustomType: \"background-task.complete\",\n\t\t\tdisplay: true,\n\t\t\tcontent: [{ type: \"text\", text: message }],\n\t\t},\n\t\t{ triggerTurn: true, deliverAs: \"followUp\", sessionId: task.parentSessionId },\n\t);\n}\n"]}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { sendBuiltinCustomMessage } from "../system-messages.js";
|
|
2
|
-
function safeDescription(task) {
|
|
3
|
-
return task.description || task.id;
|
|
4
|
-
}
|
|
5
|
-
export function formatCompletionNotification(input) {
|
|
6
|
-
const { task, duration, statusText, allComplete, remainingCount, completedTasks } = input;
|
|
7
|
-
const errorInfo = task.error ? `\n**Error:** ${task.error}` : "";
|
|
8
|
-
if (allComplete) {
|
|
9
|
-
return formatAllCompleteNotification(task, completedTasks);
|
|
10
|
-
}
|
|
11
|
-
const isFailure = statusText !== "COMPLETED";
|
|
12
|
-
return `<system-reminder>
|
|
13
|
-
[BACKGROUND TASK ${statusText}]
|
|
14
|
-
**ID:** \`${task.id}\`
|
|
15
|
-
**Description:** ${safeDescription(task)}
|
|
16
|
-
**Duration:** ${duration}${errorInfo}
|
|
17
|
-
|
|
18
|
-
**${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
|
|
19
|
-
${isFailure ? "**ACTION REQUIRED:** This task failed. Check the error and decide whether to retry, cancel remaining tasks, or continue." : "Do NOT poll - continue productive work."}
|
|
20
|
-
|
|
21
|
-
Use \`background_output(task_id="${task.id}")\` to retrieve this result when ready.
|
|
22
|
-
</system-reminder>`;
|
|
23
|
-
}
|
|
24
|
-
function formatAllCompleteNotification(task, completedTasks) {
|
|
25
|
-
const succeededTasks = completedTasks.filter((t) => t.status === "completed");
|
|
26
|
-
const failedTasks = completedTasks.filter((t) => t.status !== "completed");
|
|
27
|
-
const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) => `- \`${t.id}\`: ${safeDescription(t)}`).join("\n") : "";
|
|
28
|
-
const failedText = failedTasks.length > 0
|
|
29
|
-
? failedTasks
|
|
30
|
-
.map((t) => `- \`${t.id}\`: ${safeDescription(t)} [${t.status.toUpperCase()}]${t.error ? ` - ${t.error}` : ""}`)
|
|
31
|
-
.join("\n")
|
|
32
|
-
: "";
|
|
33
|
-
const hasFailures = failedTasks.length > 0;
|
|
34
|
-
const header = hasFailures
|
|
35
|
-
? `[ALL BACKGROUND TASKS FINISHED - ${failedTasks.length} FAILED]`
|
|
36
|
-
: "[ALL BACKGROUND TASKS COMPLETE]";
|
|
37
|
-
let body = "";
|
|
38
|
-
if (succeededText) {
|
|
39
|
-
body += `**Completed:**\n${succeededText}\n`;
|
|
40
|
-
}
|
|
41
|
-
if (failedText) {
|
|
42
|
-
body += `\n**Failed:**\n${failedText}\n`;
|
|
43
|
-
}
|
|
44
|
-
if (!body) {
|
|
45
|
-
body = `- \`${task.id}\`: ${safeDescription(task)} [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : ""}\n`;
|
|
46
|
-
}
|
|
47
|
-
return `<system-reminder>
|
|
48
|
-
${header}
|
|
49
|
-
|
|
50
|
-
${body.trim()}
|
|
51
|
-
|
|
52
|
-
Use \`background_output(task_id="<id>")\` to retrieve each result.${hasFailures ? `\n\n**ACTION REQUIRED:** ${failedTasks.length} task(s) failed. Check errors above and decide whether to retry or proceed.` : ""}
|
|
53
|
-
</system-reminder>`;
|
|
54
|
-
}
|
|
55
|
-
function formatDuration(startedAt, completedAt) {
|
|
56
|
-
const end = completedAt ?? new Date();
|
|
57
|
-
const milliseconds = end.getTime() - startedAt.getTime();
|
|
58
|
-
if (milliseconds < 1000) {
|
|
59
|
-
return `${milliseconds}ms`;
|
|
60
|
-
}
|
|
61
|
-
if (milliseconds < 60000) {
|
|
62
|
-
return `${(milliseconds / 1000).toFixed(1)}s`;
|
|
63
|
-
}
|
|
64
|
-
return `${Math.floor(milliseconds / 60000)}m ${Math.floor((milliseconds % 60000) / 1000)}s`;
|
|
65
|
-
}
|
|
66
|
-
function mapStatus(task) {
|
|
67
|
-
switch (task.status) {
|
|
68
|
-
case "cancelled":
|
|
69
|
-
return "CANCELLED";
|
|
70
|
-
case "error":
|
|
71
|
-
return "ERROR";
|
|
72
|
-
default:
|
|
73
|
-
return "COMPLETED";
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function isTerminalStatus(status) {
|
|
77
|
-
return status === "completed" || status === "error" || status === "cancelled";
|
|
78
|
-
}
|
|
79
|
-
export function sendCompletionNotification(pi, task, manager) {
|
|
80
|
-
const activeTasks = manager.getActiveTasks();
|
|
81
|
-
const allComplete = activeTasks.length === 0;
|
|
82
|
-
const remainingCount = activeTasks.length;
|
|
83
|
-
const completedTasks = allComplete
|
|
84
|
-
? manager
|
|
85
|
-
.getAllTasks()
|
|
86
|
-
.filter((t) => isTerminalStatus(t.status))
|
|
87
|
-
.map((t) => ({ id: t.id, description: t.description, status: t.status, error: t.error, result: t.result }))
|
|
88
|
-
: [];
|
|
89
|
-
const duration = formatDuration(task.startedAt, task.completedAt);
|
|
90
|
-
const statusText = mapStatus(task);
|
|
91
|
-
const message = formatCompletionNotification({
|
|
92
|
-
task: { id: task.id, description: task.description, status: task.status, error: task.error, result: task.result },
|
|
93
|
-
duration,
|
|
94
|
-
statusText,
|
|
95
|
-
allComplete,
|
|
96
|
-
remainingCount,
|
|
97
|
-
completedTasks,
|
|
98
|
-
});
|
|
99
|
-
sendBuiltinCustomMessage(pi, "background-task.notification", {
|
|
100
|
-
customType: "background-task.complete",
|
|
101
|
-
display: true,
|
|
102
|
-
content: [{ type: "text", text: message }],
|
|
103
|
-
}, { triggerTurn: true, deliverAs: "followUp", sessionId: task.parentSessionId });
|
|
104
|
-
}
|
|
105
|
-
//# sourceMappingURL=notification.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"notification.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/notification.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionAPI } from \"../../types.js\";\nimport { sendBuiltinCustomMessage } from \"../system-messages.js\";\nimport type { BackgroundManager } from \"./manager.js\";\nimport type { BackgroundTask } from \"./types.js\";\n\nexport type NotificationStatus = \"COMPLETED\" | \"CANCELLED\" | \"ERROR\";\n\nexport interface NotificationTask {\n\tid: string;\n\tdescription: string;\n\tstatus: BackgroundTask[\"status\"];\n\terror?: string;\n\tresult?: string;\n}\n\nexport interface NotificationInput {\n\ttask: NotificationTask;\n\tduration: string;\n\tstatusText: NotificationStatus;\n\tallComplete: boolean;\n\tremainingCount: number;\n\tcompletedTasks: NotificationTask[];\n}\n\nfunction safeDescription(task: NotificationTask): string {\n\treturn task.description || task.id;\n}\n\nexport function formatCompletionNotification(input: NotificationInput): string {\n\tconst { task, duration, statusText, allComplete, remainingCount, completedTasks } = input;\n\tconst errorInfo = task.error ? `\\n**Error:** ${task.error}` : \"\";\n\n\tif (allComplete) {\n\t\treturn formatAllCompleteNotification(task, completedTasks);\n\t}\n\n\tconst isFailure = statusText !== \"COMPLETED\";\n\n\treturn `<system-reminder>\n[BACKGROUND TASK ${statusText}]\n**ID:** \\`${task.id}\\`\n**Description:** ${safeDescription(task)}\n**Duration:** ${duration}${errorInfo}\n\n**${remainingCount} task${remainingCount === 1 ? \"\" : \"s\"} still in progress.** You WILL be notified when ALL complete.\n${isFailure ? \"**ACTION REQUIRED:** This task failed. Check the error and decide whether to retry, cancel remaining tasks, or continue.\" : \"Do NOT poll - continue productive work.\"}\n\nUse \\`background_output(task_id=\"${task.id}\")\\` to retrieve this result when ready.\n</system-reminder>`;\n}\n\nfunction formatAllCompleteNotification(task: NotificationTask, completedTasks: NotificationTask[]): string {\n\tconst succeededTasks = completedTasks.filter((t) => t.status === \"completed\");\n\tconst failedTasks = completedTasks.filter((t) => t.status !== \"completed\");\n\n\tconst succeededText =\n\t\tsucceededTasks.length > 0 ? succeededTasks.map((t) => `- \\`${t.id}\\`: ${safeDescription(t)}`).join(\"\\n\") : \"\";\n\tconst failedText =\n\t\tfailedTasks.length > 0\n\t\t\t? failedTasks\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(t) =>\n\t\t\t\t\t\t\t`- \\`${t.id}\\`: ${safeDescription(t)} [${t.status.toUpperCase()}]${t.error ? ` - ${t.error}` : \"\"}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\\n\")\n\t\t\t: \"\";\n\n\tconst hasFailures = failedTasks.length > 0;\n\tconst header = hasFailures\n\t\t? `[ALL BACKGROUND TASKS FINISHED - ${failedTasks.length} FAILED]`\n\t\t: \"[ALL BACKGROUND TASKS COMPLETE]\";\n\n\tlet body = \"\";\n\tif (succeededText) {\n\t\tbody += `**Completed:**\\n${succeededText}\\n`;\n\t}\n\tif (failedText) {\n\t\tbody += `\\n**Failed:**\\n${failedText}\\n`;\n\t}\n\tif (!body) {\n\t\tbody = `- \\`${task.id}\\`: ${safeDescription(task)} [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : \"\"}\\n`;\n\t}\n\n\treturn `<system-reminder>\n${header}\n\n${body.trim()}\n\nUse \\`background_output(task_id=\"<id>\")\\` to retrieve each result.${hasFailures ? `\\n\\n**ACTION REQUIRED:** ${failedTasks.length} task(s) failed. Check errors above and decide whether to retry or proceed.` : \"\"}\n</system-reminder>`;\n}\n\nfunction formatDuration(startedAt: Date, completedAt: Date | undefined): string {\n\tconst end = completedAt ?? new Date();\n\tconst milliseconds = end.getTime() - startedAt.getTime();\n\tif (milliseconds < 1000) {\n\t\treturn `${milliseconds}ms`;\n\t}\n\tif (milliseconds < 60000) {\n\t\treturn `${(milliseconds / 1000).toFixed(1)}s`;\n\t}\n\treturn `${Math.floor(milliseconds / 60000)}m ${Math.floor((milliseconds % 60000) / 1000)}s`;\n}\n\nfunction mapStatus(task: BackgroundTask): NotificationStatus {\n\tswitch (task.status) {\n\t\tcase \"cancelled\":\n\t\t\treturn \"CANCELLED\";\n\t\tcase \"error\":\n\t\t\treturn \"ERROR\";\n\t\tdefault:\n\t\t\treturn \"COMPLETED\";\n\t}\n}\n\nfunction isTerminalStatus(status: BackgroundTask[\"status\"]): boolean {\n\treturn status === \"completed\" || status === \"error\" || status === \"cancelled\";\n}\n\nexport function sendCompletionNotification(pi: ExtensionAPI, task: BackgroundTask, manager: BackgroundManager): void {\n\tconst activeTasks = manager.getActiveTasks();\n\tconst allComplete = activeTasks.length === 0;\n\tconst remainingCount = activeTasks.length;\n\n\tconst completedTasks: NotificationTask[] = allComplete\n\t\t? manager\n\t\t\t\t.getAllTasks()\n\t\t\t\t.filter((t) => isTerminalStatus(t.status))\n\t\t\t\t.map((t) => ({ id: t.id, description: t.description, status: t.status, error: t.error, result: t.result }))\n\t\t: [];\n\n\tconst duration = formatDuration(task.startedAt, task.completedAt);\n\tconst statusText = mapStatus(task);\n\n\tconst message = formatCompletionNotification({\n\t\ttask: { id: task.id, description: task.description, status: task.status, error: task.error, result: task.result },\n\t\tduration,\n\t\tstatusText,\n\t\tallComplete,\n\t\tremainingCount,\n\t\tcompletedTasks,\n\t});\n\n\tsendBuiltinCustomMessage(\n\t\tpi,\n\t\t\"background-task.notification\",\n\t\t{\n\t\t\tcustomType: \"background-task.complete\",\n\t\t\tdisplay: true,\n\t\t\tcontent: [{ type: \"text\", text: message }],\n\t\t},\n\t\t{ triggerTurn: true, deliverAs: \"followUp\", sessionId: task.parentSessionId },\n\t);\n}\n"]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Type } from "typebox";
|
|
2
|
-
import type { ToolDefinition } from "../../types.js";
|
|
3
|
-
import type { BackgroundManager } from "./manager.js";
|
|
4
|
-
declare const BackgroundOutputParams: Type.TObject<{
|
|
5
|
-
task_id: Type.TString;
|
|
6
|
-
block: Type.TOptional<Type.TBoolean>;
|
|
7
|
-
timeout: Type.TOptional<Type.TNumber>;
|
|
8
|
-
}>;
|
|
9
|
-
export declare function createBackgroundOutputTool(manager: BackgroundManager): ToolDefinition<typeof BackgroundOutputParams>;
|
|
10
|
-
export {};
|
|
11
|
-
//# sourceMappingURL=output-tool.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"output-tool.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/background-task/output-tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAItD,QAAA,MAAM,sBAAsB;;;;EAQ1B,CAAC;AA+BH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CA8FpH","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,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"]}
|