iosm-cli 0.2.4 → 0.2.5
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 +31 -0
- package/README.md +23 -17
- package/dist/core/agent-profiles.d.ts +1 -0
- package/dist/core/agent-profiles.d.ts.map +1 -1
- package/dist/core/agent-profiles.js +10 -6
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agent-teams.d.ts.map +1 -1
- package/dist/core/agent-teams.js +90 -19
- package/dist/core/agent-teams.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +6 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +9 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/parallel-task-agent.d.ts +23 -1
- package/dist/core/parallel-task-agent.d.ts.map +1 -1
- package/dist/core/parallel-task-agent.js +110 -20
- package/dist/core/parallel-task-agent.js.map +1 -1
- package/dist/core/shared-memory.d.ts +2 -2
- package/dist/core/shared-memory.d.ts.map +1 -1
- package/dist/core/shared-memory.js +220 -91
- package/dist/core/shared-memory.js.map +1 -1
- package/dist/core/singular.d.ts.map +1 -1
- package/dist/core/singular.js +3 -1
- package/dist/core/singular.js.map +1 -1
- package/dist/core/subagents.d.ts +1 -1
- package/dist/core/subagents.d.ts.map +1 -1
- package/dist/core/subagents.js +11 -3
- package/dist/core/subagents.js.map +1 -1
- package/dist/core/swarm/planner.d.ts.map +1 -1
- package/dist/core/swarm/planner.js +200 -12
- package/dist/core/swarm/planner.js.map +1 -1
- package/dist/core/swarm/scheduler.d.ts +2 -0
- package/dist/core/swarm/scheduler.d.ts.map +1 -1
- package/dist/core/swarm/scheduler.js +87 -6
- package/dist/core/swarm/scheduler.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -0
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ast-grep.d.ts.map +1 -1
- package/dist/core/tools/ast-grep.js +2 -0
- package/dist/core/tools/ast-grep.js.map +1 -1
- package/dist/core/tools/shared-memory.d.ts.map +1 -1
- package/dist/core/tools/shared-memory.js +34 -6
- package/dist/core/tools/shared-memory.js.map +1 -1
- package/dist/core/tools/task.d.ts.map +1 -1
- package/dist/core/tools/task.js +464 -73
- package/dist/core/tools/task.js.map +1 -1
- package/dist/core/tools/yq.d.ts.map +1 -1
- package/dist/core/tools/yq.js +2 -0
- package/dist/core/tools/yq.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +2 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +756 -74
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/cli-reference.md +4 -0
- package/docs/interactive-mode.md +2 -0
- package/docs/orchestration-and-subagents.md +5 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-teams.d.ts","sourceRoot":"","sources":["../../src/core/agent-teams.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,GAAG,YAAY,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"agent-teams.d.ts","sourceRoot":"","sources":["../../src/core/agent-teams.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,GAAG,YAAY,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;CACxB;AA6FD,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,GAAG,YAAY,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC5F,GAAG,aAAa,CA2BhB;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAQhF;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,aAAa,EAAE,CAkBrE;AAyCD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,cAAc,CAAC;CACvB,GAAG,aAAa,GAAG,SAAS,CAY5B"}
|
package/dist/core/agent-teams.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
3
|
import lockfile from "proper-lockfile";
|
|
4
4
|
function getTeamsDir(cwd) {
|
|
5
5
|
return join(cwd, ".iosm", "subagents", "teams");
|
|
@@ -7,9 +7,72 @@ function getTeamsDir(cwd) {
|
|
|
7
7
|
function getTeamRunPath(cwd, runId) {
|
|
8
8
|
return join(getTeamsDir(cwd), `${runId}.json`);
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const statusUpdateRetryDelayMs = 25;
|
|
11
|
+
const maxQueuedStatusAttempts = 50;
|
|
12
|
+
const pendingStatusUpdates = new Map();
|
|
13
|
+
let pendingStatusFlushTimer;
|
|
14
|
+
function isTerminalStatus(status) {
|
|
15
|
+
return status === "done" || status === "error" || status === "cancelled";
|
|
16
|
+
}
|
|
17
|
+
function shouldReplacePendingStatus(current, next) {
|
|
18
|
+
if (current === next)
|
|
19
|
+
return false;
|
|
20
|
+
if (isTerminalStatus(current))
|
|
21
|
+
return false;
|
|
22
|
+
if (isTerminalStatus(next))
|
|
23
|
+
return true;
|
|
24
|
+
if (current === "running" && next === "pending")
|
|
25
|
+
return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
function pendingStatusKey(input) {
|
|
29
|
+
return `${resolve(input.cwd).toLowerCase()}::${input.runId}::${input.taskId}`;
|
|
30
|
+
}
|
|
31
|
+
function schedulePendingStatusFlush(delayMs = statusUpdateRetryDelayMs) {
|
|
32
|
+
if (pendingStatusFlushTimer)
|
|
33
|
+
return;
|
|
34
|
+
pendingStatusFlushTimer = setTimeout(() => {
|
|
35
|
+
pendingStatusFlushTimer = undefined;
|
|
36
|
+
flushPendingStatusUpdates();
|
|
37
|
+
}, delayMs);
|
|
38
|
+
}
|
|
39
|
+
function queuePendingStatusUpdate(input) {
|
|
40
|
+
const key = pendingStatusKey(input);
|
|
41
|
+
const existing = pendingStatusUpdates.get(key);
|
|
42
|
+
if (!existing) {
|
|
43
|
+
pendingStatusUpdates.set(key, { input, attempts: 0 });
|
|
44
|
+
schedulePendingStatusFlush();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (shouldReplacePendingStatus(existing.input.status, input.status)) {
|
|
48
|
+
pendingStatusUpdates.set(key, {
|
|
49
|
+
input,
|
|
50
|
+
attempts: existing.attempts,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
schedulePendingStatusFlush();
|
|
54
|
+
}
|
|
55
|
+
function flushPendingStatusUpdates() {
|
|
56
|
+
if (pendingStatusUpdates.size === 0)
|
|
57
|
+
return;
|
|
58
|
+
for (const [key, pending] of Array.from(pendingStatusUpdates.entries())) {
|
|
59
|
+
if (pending.attempts >= maxQueuedStatusAttempts) {
|
|
60
|
+
pendingStatusUpdates.delete(key);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const result = tryUpdateTeamTaskStatus(pending.input);
|
|
64
|
+
if (result === "locked") {
|
|
65
|
+
pendingStatusUpdates.set(key, {
|
|
66
|
+
input: pending.input,
|
|
67
|
+
attempts: pending.attempts + 1,
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
pendingStatusUpdates.delete(key);
|
|
72
|
+
}
|
|
73
|
+
if (pendingStatusUpdates.size > 0) {
|
|
74
|
+
schedulePendingStatusFlush();
|
|
75
|
+
}
|
|
13
76
|
}
|
|
14
77
|
export function createTeamRun(input) {
|
|
15
78
|
const runId = `team_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -71,32 +134,27 @@ export function listTeamRuns(cwd, limit = 20) {
|
|
|
71
134
|
}
|
|
72
135
|
return runs;
|
|
73
136
|
}
|
|
74
|
-
|
|
137
|
+
function tryUpdateTeamTaskStatus(input) {
|
|
75
138
|
const runPath = getTeamRunPath(input.cwd, input.runId);
|
|
76
139
|
if (!existsSync(runPath))
|
|
77
140
|
return undefined;
|
|
78
141
|
let release;
|
|
79
142
|
try {
|
|
80
143
|
mkdirSync(getTeamsDir(input.cwd), { recursive: true });
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (code !== "ELOCKED" || attempt === maxLockAttempts) {
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
sleepSync(Math.min(120, 10 + attempt * 10));
|
|
93
|
-
}
|
|
144
|
+
try {
|
|
145
|
+
release = lockfile.lockSync(runPath, { realpath: false });
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
|
|
149
|
+
if (code === "ELOCKED")
|
|
150
|
+
return "locked";
|
|
151
|
+
return undefined;
|
|
94
152
|
}
|
|
95
153
|
if (!release)
|
|
96
154
|
return undefined;
|
|
97
155
|
const raw = readFileSync(runPath, "utf8");
|
|
98
156
|
const existing = JSON.parse(raw);
|
|
99
|
-
const nextTasks = existing.tasks.map((task) => task.id === input.taskId ? { ...task, status: input.status } : task);
|
|
157
|
+
const nextTasks = existing.tasks.map((task) => (task.id === input.taskId ? { ...task, status: input.status } : task));
|
|
100
158
|
if (!nextTasks.some((task) => task.id === input.taskId)) {
|
|
101
159
|
return undefined;
|
|
102
160
|
}
|
|
@@ -114,4 +172,17 @@ export function updateTeamTaskStatus(input) {
|
|
|
114
172
|
release?.();
|
|
115
173
|
}
|
|
116
174
|
}
|
|
175
|
+
export function updateTeamTaskStatus(input) {
|
|
176
|
+
const result = tryUpdateTeamTaskStatus(input);
|
|
177
|
+
if (result === "locked") {
|
|
178
|
+
// Non-blocking reliability path: queue status update for retry instead of dropping lifecycle transitions.
|
|
179
|
+
queuePendingStatusUpdate(input);
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
const key = pendingStatusKey(input);
|
|
183
|
+
if (pendingStatusUpdates.has(key) && result) {
|
|
184
|
+
pendingStatusUpdates.delete(key);
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
117
188
|
//# sourceMappingURL=agent-teams.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-teams.js","sourceRoot":"","sources":["../../src/core/agent-teams.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\n\nexport type TeamTaskStatus = \"pending\" | \"running\" | \"done\" | \"error\" | \"cancelled\";\n\nexport interface TeamTaskRecord {\n\tid: string;\n\tagentIndex: number;\n\tprofile: string;\n\tcwd: string;\n\tlockKey?: string;\n\tdependsOn: string[];\n\tstatus: TeamTaskStatus;\n}\n\nexport interface TeamRunRecord {\n\trunId: string;\n\tcreatedAt: string;\n\tmode: \"parallel\" | \"sequential\";\n\tagents: number;\n\tmaxParallel?: number;\n\ttask: string;\n\ttasks: TeamTaskRecord[];\n}\n\nfunction getTeamsDir(cwd: string): string {\n\treturn join(cwd, \".iosm\", \"subagents\", \"teams\");\n}\n\nfunction getTeamRunPath(cwd: string, runId: string): string {\n\treturn join(getTeamsDir(cwd), `${runId}.json`);\n}\n\nfunction sleepSync(ms: number): void {\n\tconst waiter = new Int32Array(new SharedArrayBuffer(4));\n\tAtomics.wait(waiter, 0, 0, ms);\n}\n\nexport function createTeamRun(input: {\n\tcwd: string;\n\tmode: \"parallel\" | \"sequential\";\n\tagents: number;\n\tmaxParallel?: number;\n\ttask: string;\n\tassignments: Array<{ profile: string; cwd: string; lockKey?: string; dependsOn: number[] }>;\n}): TeamRunRecord {\n\tconst runId = `team_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n\tconst tasks: TeamTaskRecord[] = input.assignments.map((assignment, index) => {\n\t\tconst id = `task_${index + 1}`;\n\t\tconst dependsOn = assignment.dependsOn.map((dep) => `task_${dep}`);\n\t\treturn {\n\t\t\tid,\n\t\t\tagentIndex: index + 1,\n\t\t\tprofile: assignment.profile,\n\t\t\tcwd: assignment.cwd,\n\t\t\tlockKey: assignment.lockKey,\n\t\t\tdependsOn,\n\t\t\tstatus: \"pending\",\n\t\t};\n\t});\n\tconst record: TeamRunRecord = {\n\t\trunId,\n\t\tcreatedAt: new Date().toISOString(),\n\t\tmode: input.mode,\n\t\tagents: input.agents,\n\t\tmaxParallel: input.maxParallel,\n\t\ttask: input.task,\n\t\ttasks,\n\t};\n\tmkdirSync(getTeamsDir(input.cwd), { recursive: true });\n\twriteFileSync(getTeamRunPath(input.cwd, runId), JSON.stringify(record, null, 2), \"utf8\");\n\treturn record;\n}\n\nexport function getTeamRun(cwd: string, runId: string): TeamRunRecord | undefined {\n\tconst path = getTeamRunPath(cwd, runId);\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf8\")) as TeamRunRecord;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function listTeamRuns(cwd: string, limit = 20): TeamRunRecord[] {\n\tconst dir = getTeamsDir(cwd);\n\tif (!existsSync(dir)) return [];\n\tconst names = readdirSync(dir)\n\t\t.filter((name) => name.toLowerCase().endsWith(\".json\"))\n\t\t.sort()\n\t\t.reverse()\n\t\t.slice(0, Math.max(1, limit));\n\tconst runs: TeamRunRecord[] = [];\n\tfor (const name of names) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(readFileSync(join(dir, name), \"utf8\")) as TeamRunRecord;\n\t\t\truns.push(parsed);\n\t\t} catch {\n\t\t\t// ignore malformed files\n\t\t}\n\t}\n\treturn runs;\n}\n\nexport function updateTeamTaskStatus(input: {\n\tcwd: string;\n\trunId: string;\n\ttaskId: string;\n\tstatus: TeamTaskStatus;\n}): TeamRunRecord | undefined {\n\tconst runPath = getTeamRunPath(input.cwd, input.runId);\n\tif (!existsSync(runPath)) return undefined;\n\tlet release: (() => void) | undefined;\n\ttry {\n\t\tmkdirSync(getTeamsDir(input.cwd), { recursive: true });\n\t\tconst maxLockAttempts = 12;\n\t\tfor (let attempt = 1; attempt <= maxLockAttempts; attempt += 1) {\n\t\t\ttry {\n\t\t\t\trelease = lockfile.lockSync(runPath, { realpath: false });\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tconst code = error && typeof error === \"object\" && \"code\" in error ? String(error.code) : \"\";\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxLockAttempts) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\tsleepSync(Math.min(120, 10 + attempt * 10));\n\t\t\t}\n\t\t}\n\t\tif (!release) return undefined;\n\t\tconst raw = readFileSync(runPath, \"utf8\");\n\t\tconst existing = JSON.parse(raw) as TeamRunRecord;\n\t\tconst nextTasks = existing.tasks.map((task) =>\n\t\t\ttask.id === input.taskId ? { ...task, status: input.status } : task,\n\t\t);\n\t\tif (!nextTasks.some((task) => task.id === input.taskId)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst next: TeamRunRecord = {\n\t\t\t...existing,\n\t\t\ttasks: nextTasks,\n\t\t};\n\t\twriteFileSync(runPath, JSON.stringify(next, null, 2), \"utf8\");\n\t\treturn next;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\trelease?.();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-teams.js","sourceRoot":"","sources":["../../src/core/agent-teams.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\n\nexport type TeamTaskStatus = \"pending\" | \"running\" | \"done\" | \"error\" | \"cancelled\";\n\nexport interface TeamTaskRecord {\n\tid: string;\n\tagentIndex: number;\n\tprofile: string;\n\tcwd: string;\n\tlockKey?: string;\n\tdependsOn: string[];\n\tstatus: TeamTaskStatus;\n}\n\nexport interface TeamRunRecord {\n\trunId: string;\n\tcreatedAt: string;\n\tmode: \"parallel\" | \"sequential\";\n\tagents: number;\n\tmaxParallel?: number;\n\ttask: string;\n\ttasks: TeamTaskRecord[];\n}\n\nfunction getTeamsDir(cwd: string): string {\n\treturn join(cwd, \".iosm\", \"subagents\", \"teams\");\n}\n\nfunction getTeamRunPath(cwd: string, runId: string): string {\n\treturn join(getTeamsDir(cwd), `${runId}.json`);\n}\n\ntype PendingStatusUpdate = {\n\tinput: {\n\t\tcwd: string;\n\t\trunId: string;\n\t\ttaskId: string;\n\t\tstatus: TeamTaskStatus;\n\t};\n\tattempts: number;\n};\n\nconst statusUpdateRetryDelayMs = 25;\nconst maxQueuedStatusAttempts = 50;\nconst pendingStatusUpdates = new Map<string, PendingStatusUpdate>();\nlet pendingStatusFlushTimer: ReturnType<typeof setTimeout> | undefined;\n\nfunction isTerminalStatus(status: TeamTaskStatus): boolean {\n\treturn status === \"done\" || status === \"error\" || status === \"cancelled\";\n}\n\nfunction shouldReplacePendingStatus(current: TeamTaskStatus, next: TeamTaskStatus): boolean {\n\tif (current === next) return false;\n\tif (isTerminalStatus(current)) return false;\n\tif (isTerminalStatus(next)) return true;\n\tif (current === \"running\" && next === \"pending\") return false;\n\treturn true;\n}\n\nfunction pendingStatusKey(input: { cwd: string; runId: string; taskId: string }): string {\n\treturn `${resolve(input.cwd).toLowerCase()}::${input.runId}::${input.taskId}`;\n}\n\nfunction schedulePendingStatusFlush(delayMs = statusUpdateRetryDelayMs): void {\n\tif (pendingStatusFlushTimer) return;\n\tpendingStatusFlushTimer = setTimeout(() => {\n\t\tpendingStatusFlushTimer = undefined;\n\t\tflushPendingStatusUpdates();\n\t}, delayMs);\n}\n\nfunction queuePendingStatusUpdate(input: {\n\tcwd: string;\n\trunId: string;\n\ttaskId: string;\n\tstatus: TeamTaskStatus;\n}): void {\n\tconst key = pendingStatusKey(input);\n\tconst existing = pendingStatusUpdates.get(key);\n\tif (!existing) {\n\t\tpendingStatusUpdates.set(key, { input, attempts: 0 });\n\t\tschedulePendingStatusFlush();\n\t\treturn;\n\t}\n\tif (shouldReplacePendingStatus(existing.input.status, input.status)) {\n\t\tpendingStatusUpdates.set(key, {\n\t\t\tinput,\n\t\t\tattempts: existing.attempts,\n\t\t});\n\t}\n\tschedulePendingStatusFlush();\n}\n\nfunction flushPendingStatusUpdates(): void {\n\tif (pendingStatusUpdates.size === 0) return;\n\tfor (const [key, pending] of Array.from(pendingStatusUpdates.entries())) {\n\t\tif (pending.attempts >= maxQueuedStatusAttempts) {\n\t\t\tpendingStatusUpdates.delete(key);\n\t\t\tcontinue;\n\t\t}\n\t\tconst result = tryUpdateTeamTaskStatus(pending.input);\n\t\tif (result === \"locked\") {\n\t\t\tpendingStatusUpdates.set(key, {\n\t\t\t\tinput: pending.input,\n\t\t\t\tattempts: pending.attempts + 1,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tpendingStatusUpdates.delete(key);\n\t}\n\tif (pendingStatusUpdates.size > 0) {\n\t\tschedulePendingStatusFlush();\n\t}\n}\n\nexport function createTeamRun(input: {\n\tcwd: string;\n\tmode: \"parallel\" | \"sequential\";\n\tagents: number;\n\tmaxParallel?: number;\n\ttask: string;\n\tassignments: Array<{ profile: string; cwd: string; lockKey?: string; dependsOn: number[] }>;\n}): TeamRunRecord {\n\tconst runId = `team_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n\tconst tasks: TeamTaskRecord[] = input.assignments.map((assignment, index) => {\n\t\tconst id = `task_${index + 1}`;\n\t\tconst dependsOn = assignment.dependsOn.map((dep) => `task_${dep}`);\n\t\treturn {\n\t\t\tid,\n\t\t\tagentIndex: index + 1,\n\t\t\tprofile: assignment.profile,\n\t\t\tcwd: assignment.cwd,\n\t\t\tlockKey: assignment.lockKey,\n\t\t\tdependsOn,\n\t\t\tstatus: \"pending\",\n\t\t};\n\t});\n\tconst record: TeamRunRecord = {\n\t\trunId,\n\t\tcreatedAt: new Date().toISOString(),\n\t\tmode: input.mode,\n\t\tagents: input.agents,\n\t\tmaxParallel: input.maxParallel,\n\t\ttask: input.task,\n\t\ttasks,\n\t};\n\tmkdirSync(getTeamsDir(input.cwd), { recursive: true });\n\twriteFileSync(getTeamRunPath(input.cwd, runId), JSON.stringify(record, null, 2), \"utf8\");\n\treturn record;\n}\n\nexport function getTeamRun(cwd: string, runId: string): TeamRunRecord | undefined {\n\tconst path = getTeamRunPath(cwd, runId);\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf8\")) as TeamRunRecord;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function listTeamRuns(cwd: string, limit = 20): TeamRunRecord[] {\n\tconst dir = getTeamsDir(cwd);\n\tif (!existsSync(dir)) return [];\n\tconst names = readdirSync(dir)\n\t\t.filter((name) => name.toLowerCase().endsWith(\".json\"))\n\t\t.sort()\n\t\t.reverse()\n\t\t.slice(0, Math.max(1, limit));\n\tconst runs: TeamRunRecord[] = [];\n\tfor (const name of names) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(readFileSync(join(dir, name), \"utf8\")) as TeamRunRecord;\n\t\t\truns.push(parsed);\n\t\t} catch {\n\t\t\t// ignore malformed files\n\t\t}\n\t}\n\treturn runs;\n}\n\nfunction tryUpdateTeamTaskStatus(input: {\n\tcwd: string;\n\trunId: string;\n\ttaskId: string;\n\tstatus: TeamTaskStatus;\n}): TeamRunRecord | \"locked\" | undefined {\n\tconst runPath = getTeamRunPath(input.cwd, input.runId);\n\tif (!existsSync(runPath)) return undefined;\n\tlet release: (() => void) | undefined;\n\ttry {\n\t\tmkdirSync(getTeamsDir(input.cwd), { recursive: true });\n\t\ttry {\n\t\t\trelease = lockfile.lockSync(runPath, { realpath: false });\n\t\t} catch (error) {\n\t\t\tconst code = error && typeof error === \"object\" && \"code\" in error ? String(error.code) : \"\";\n\t\t\tif (code === \"ELOCKED\") return \"locked\";\n\t\t\treturn undefined;\n\t\t}\n\t\tif (!release) return undefined;\n\t\tconst raw = readFileSync(runPath, \"utf8\");\n\t\tconst existing = JSON.parse(raw) as TeamRunRecord;\n\t\tconst nextTasks = existing.tasks.map((task) => (task.id === input.taskId ? { ...task, status: input.status } : task));\n\t\tif (!nextTasks.some((task) => task.id === input.taskId)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst next: TeamRunRecord = {\n\t\t\t...existing,\n\t\t\ttasks: nextTasks,\n\t\t};\n\t\twriteFileSync(runPath, JSON.stringify(next, null, 2), \"utf8\");\n\t\treturn next;\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\trelease?.();\n\t}\n}\n\nexport function updateTeamTaskStatus(input: {\n\tcwd: string;\n\trunId: string;\n\ttaskId: string;\n\tstatus: TeamTaskStatus;\n}): TeamRunRecord | undefined {\n\tconst result = tryUpdateTeamTaskStatus(input);\n\tif (result === \"locked\") {\n\t\t// Non-blocking reliability path: queue status update for retry instead of dropping lifecycle transitions.\n\t\tqueuePendingStatusUpdate(input);\n\t\treturn undefined;\n\t}\n\tconst key = pendingStatusKey(input);\n\tif (pendingStatusUpdates.has(key) && result) {\n\t\tpendingStatusUpdates.delete(key);\n\t}\n\treturn result;\n}\n"]}
|
|
@@ -8,6 +8,7 @@ export declare class FooterDataProvider {
|
|
|
8
8
|
private gitWatcher;
|
|
9
9
|
private branchChangeCallbacks;
|
|
10
10
|
private availableProviderCount;
|
|
11
|
+
private swarmBusy;
|
|
11
12
|
constructor();
|
|
12
13
|
/** Current git branch, null if not in repo, "detached" if detached HEAD */
|
|
13
14
|
getGitBranch(): string | null;
|
|
@@ -21,12 +22,16 @@ export declare class FooterDataProvider {
|
|
|
21
22
|
clearExtensionStatuses(): void;
|
|
22
23
|
/** Number of unique providers with available models (for footer display) */
|
|
23
24
|
getAvailableProviderCount(): number;
|
|
25
|
+
/** True while a foreground /swarm run is executing. */
|
|
26
|
+
getSwarmBusy(): boolean;
|
|
24
27
|
/** Internal: update available provider count */
|
|
25
28
|
setAvailableProviderCount(count: number): void;
|
|
29
|
+
/** Internal: update swarm busy status for footer state rendering. */
|
|
30
|
+
setSwarmBusy(busy: boolean): void;
|
|
26
31
|
/** Internal: cleanup */
|
|
27
32
|
dispose(): void;
|
|
28
33
|
private setupGitWatcher;
|
|
29
34
|
}
|
|
30
35
|
/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */
|
|
31
|
-
export type ReadonlyFooterDataProvider = Pick<FooterDataProvider, "getGitBranch" | "getExtensionStatuses" | "getAvailableProviderCount" | "onBranchChange">;
|
|
36
|
+
export type ReadonlyFooterDataProvider = Pick<FooterDataProvider, "getGitBranch" | "getExtensionStatuses" | "getAvailableProviderCount" | "getSwarmBusy" | "onBranchChange">;
|
|
32
37
|
//# sourceMappingURL=footer-data-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer-data-provider.d.ts","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"AAmCA;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,qBAAqB,CAAyB;IACtD,OAAO,CAAC,sBAAsB,CAAK;;
|
|
1
|
+
{"version":3,"file":"footer-data-provider.d.ts","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"AAmCA;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,qBAAqB,CAAyB;IACtD,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,SAAS,CAAS;;IAM1B,2EAA2E;IAC3E,YAAY,IAAI,MAAM,GAAG,IAAI;IAiB7B,wDAAwD;IACxD,oBAAoB,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAInD,qEAAqE;IACrE,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKhD,qCAAqC;IACrC,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAQ/D,yCAAyC;IACzC,sBAAsB,IAAI,IAAI;IAI9B,4EAA4E;IAC5E,yBAAyB,IAAI,MAAM;IAInC,uDAAuD;IACvD,YAAY,IAAI,OAAO;IAIvB,gDAAgD;IAChD,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI9C,qEAAqE;IACrE,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAIjC,wBAAwB;IACxB,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,eAAe;CAyBvB;AAED,yGAAyG;AACzG,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC5C,kBAAkB,EAClB,cAAc,GAAG,sBAAsB,GAAG,2BAA2B,GAAG,cAAc,GAAG,gBAAgB,CACzG,CAAC"}
|
|
@@ -47,6 +47,7 @@ export class FooterDataProvider {
|
|
|
47
47
|
this.gitWatcher = null;
|
|
48
48
|
this.branchChangeCallbacks = new Set();
|
|
49
49
|
this.availableProviderCount = 0;
|
|
50
|
+
this.swarmBusy = false;
|
|
50
51
|
this.setupGitWatcher();
|
|
51
52
|
}
|
|
52
53
|
/** Current git branch, null if not in repo, "detached" if detached HEAD */
|
|
@@ -93,10 +94,18 @@ export class FooterDataProvider {
|
|
|
93
94
|
getAvailableProviderCount() {
|
|
94
95
|
return this.availableProviderCount;
|
|
95
96
|
}
|
|
97
|
+
/** True while a foreground /swarm run is executing. */
|
|
98
|
+
getSwarmBusy() {
|
|
99
|
+
return this.swarmBusy;
|
|
100
|
+
}
|
|
96
101
|
/** Internal: update available provider count */
|
|
97
102
|
setAvailableProviderCount(count) {
|
|
98
103
|
this.availableProviderCount = count;
|
|
99
104
|
}
|
|
105
|
+
/** Internal: update swarm busy status for footer state rendering. */
|
|
106
|
+
setSwarmBusy(busy) {
|
|
107
|
+
this.swarmBusy = busy;
|
|
108
|
+
}
|
|
100
109
|
/** Internal: cleanup */
|
|
101
110
|
dispose() {
|
|
102
111
|
if (this.gitWatcher) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer-data-provider.js","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAkB,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE9C;;;GAGG;AACH,SAAS,eAAe;IACvB,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACxB,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAClC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAC/B,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;oBACnB,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;oBACrD,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;wBACpC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;wBAC9C,IAAI,UAAU,CAAC,QAAQ,CAAC;4BAAE,OAAO,QAAQ,CAAC;oBAC3C,CAAC;gBACF,CAAC;qBAAM,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;oBAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBACvC,IAAI,UAAU,CAAC,QAAQ,CAAC;wBAAE,OAAO,QAAQ,CAAC;gBAC3C,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACd,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,kBAAkB;
|
|
1
|
+
{"version":3,"file":"footer-data-provider.js","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, type FSWatcher, readFileSync, statSync, watch } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\n\n/**\n * Find the git HEAD path by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nfunction findGitHeadPath(): string | null {\n\tlet dir = process.cwd();\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(gitPath);\n\t\t\t\tif (stat.isFile()) {\n\t\t\t\t\tconst content = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\t\tconst gitDir = content.slice(8);\n\t\t\t\t\t\tconst headPath = resolve(dir, gitDir, \"HEAD\");\n\t\t\t\t\t\tif (existsSync(headPath)) return headPath;\n\t\t\t\t\t}\n\t\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\t\tif (existsSync(headPath)) return headPath;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate gitWatcher: FSWatcher | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate swarmBusy = false;\n\n\tconstructor() {\n\t\tthis.setupGitWatcher();\n\t}\n\n\t/** Current git branch, null if not in repo, \"detached\" if detached HEAD */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch !== undefined) return this.cachedBranch;\n\n\t\ttry {\n\t\t\tconst gitHeadPath = findGitHeadPath();\n\t\t\tif (!gitHeadPath) {\n\t\t\t\tthis.cachedBranch = null;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst content = readFileSync(gitHeadPath, \"utf8\").trim();\n\t\t\tthis.cachedBranch = content.startsWith(\"ref: refs/heads/\") ? content.slice(16) : \"detached\";\n\t\t} catch {\n\t\t\tthis.cachedBranch = null;\n\t\t}\n\t\treturn this.cachedBranch;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** True while a foreground /swarm run is executing. */\n\tgetSwarmBusy(): boolean {\n\t\treturn this.swarmBusy;\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\t/** Internal: update swarm busy status for footer state rendering. */\n\tsetSwarmBusy(busy: boolean): void {\n\t\tthis.swarmBusy = busy;\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\t\tthis.branchChangeCallbacks.clear();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\n\t\tconst gitHeadPath = findGitHeadPath();\n\t\tif (!gitHeadPath) return;\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tconst gitDir = dirname(gitHeadPath);\n\n\t\ttry {\n\t\t\tthis.gitWatcher = watch(gitDir, (_eventType, filename) => {\n\t\t\t\tif (filename === \"HEAD\") {\n\t\t\t\t\tthis.cachedBranch = undefined;\n\t\t\t\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t\t\t\t}\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently fail if we can't watch\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t\"getGitBranch\" | \"getExtensionStatuses\" | \"getAvailableProviderCount\" | \"getSwarmBusy\" | \"onBranchChange\"\n>;\n"]}
|
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
import type { Agent } from "@mariozechner/pi-agent-core";
|
|
1
|
+
import type { Agent, AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import { EventStream } from "@mariozechner/pi-ai";
|
|
2
3
|
type ToolCallContent = {
|
|
3
4
|
type: "toolCall";
|
|
4
5
|
id: string;
|
|
5
6
|
name: string;
|
|
6
7
|
arguments: unknown;
|
|
7
8
|
};
|
|
9
|
+
type ToolResultMessageLike = {
|
|
10
|
+
role: "toolResult";
|
|
11
|
+
toolCallId: string;
|
|
12
|
+
toolName: string;
|
|
13
|
+
content: Array<{
|
|
14
|
+
type: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
data?: string;
|
|
17
|
+
mimeType?: string;
|
|
18
|
+
}>;
|
|
19
|
+
details: unknown;
|
|
20
|
+
isError: boolean;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
};
|
|
8
23
|
/**
|
|
9
24
|
* Parallel policy: run tool calls concurrently only when all calls are `task`.
|
|
10
25
|
* This preserves deterministic behavior for mutating filesystem tools.
|
|
11
26
|
*/
|
|
12
27
|
export declare function shouldExecuteTaskCallsInParallel(toolCalls: ToolCallContent[]): boolean;
|
|
28
|
+
declare function executeToolCallsWithPolicy(tools: AgentTool<any>[] | undefined, assistantMessage: any, signal: AbortSignal, stream: EventStream<any, any>, getSteeringMessages?: () => Promise<AgentMessage[]>): Promise<{
|
|
29
|
+
toolResults: ToolResultMessageLike[];
|
|
30
|
+
steeringMessages?: AgentMessage[];
|
|
31
|
+
}>;
|
|
32
|
+
export declare const __parallelTaskAgentTestUtils: {
|
|
33
|
+
executeToolCallsWithPolicy: typeof executeToolCallsWithPolicy;
|
|
34
|
+
};
|
|
13
35
|
/**
|
|
14
36
|
* Patch a pi-agent-core Agent instance so multiple `task` tool calls in one assistant turn
|
|
15
37
|
* execute concurrently.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parallel-task-agent.d.ts","sourceRoot":"","sources":["../../src/core/parallel-task-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"parallel-task-agent.d.ts","sourceRoot":"","sources":["../../src/core/parallel-task-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAAE,WAAW,EAAyB,MAAM,qBAAqB,CAAC;AAEzE,KAAK,eAAe,GAAG;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAEtF;AAySD,iBAAe,0BAA0B,CACxC,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,EACnC,gBAAgB,EAAE,GAAG,EACrB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,EAC7B,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC,GACjD,OAAO,CAAC;IAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;IAAC,gBAAgB,CAAC,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CAMtF;AAED,eAAO,MAAM,4BAA4B;;CAExC,CAAC;AA2MF;;;GAGG;AACH,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CA8IrE"}
|
|
@@ -15,11 +15,57 @@ function toToolResultError(error) {
|
|
|
15
15
|
details: {},
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const STEERING_SKIP_TEXT = "Skipped due to queued user message.";
|
|
19
|
+
const STEERING_POLL_INTERVAL_MS = 40;
|
|
20
|
+
function createSteeringSkipResult() {
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: "text", text: STEERING_SKIP_TEXT }],
|
|
23
|
+
details: { steeringSkipped: true },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function createLinkedAbortController(primary, secondary) {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const cleanup = () => {
|
|
29
|
+
primary.removeEventListener("abort", onAbort);
|
|
30
|
+
secondary.removeEventListener("abort", onAbort);
|
|
22
31
|
};
|
|
32
|
+
const onAbort = () => {
|
|
33
|
+
cleanup();
|
|
34
|
+
controller.abort();
|
|
35
|
+
};
|
|
36
|
+
if (primary.aborted || secondary.aborted) {
|
|
37
|
+
controller.abort();
|
|
38
|
+
return { signal: controller.signal, cleanup };
|
|
39
|
+
}
|
|
40
|
+
primary.addEventListener("abort", onAbort, { once: true });
|
|
41
|
+
secondary.addEventListener("abort", onAbort, { once: true });
|
|
42
|
+
return { signal: controller.signal, cleanup };
|
|
43
|
+
}
|
|
44
|
+
function waitForSteeringPoll(signal) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
if (signal.aborted) {
|
|
47
|
+
resolve();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
signal.removeEventListener("abort", onAbort);
|
|
52
|
+
resolve();
|
|
53
|
+
}, STEERING_POLL_INTERVAL_MS);
|
|
54
|
+
const onAbort = () => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
signal.removeEventListener("abort", onAbort);
|
|
57
|
+
resolve();
|
|
58
|
+
};
|
|
59
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function isAbortError(error) {
|
|
63
|
+
if (!(error instanceof Error))
|
|
64
|
+
return false;
|
|
65
|
+
return error.message.toLowerCase().includes("aborted");
|
|
66
|
+
}
|
|
67
|
+
function skipToolCall(toolCall, stream) {
|
|
68
|
+
const result = createSteeringSkipResult();
|
|
23
69
|
stream.push({
|
|
24
70
|
type: "tool_execution_start",
|
|
25
71
|
toolCallId: toolCall.id,
|
|
@@ -31,15 +77,15 @@ function skipToolCall(toolCall, stream) {
|
|
|
31
77
|
toolCallId: toolCall.id,
|
|
32
78
|
toolName: toolCall.name,
|
|
33
79
|
result,
|
|
34
|
-
isError:
|
|
80
|
+
isError: false,
|
|
35
81
|
});
|
|
36
82
|
const toolResultMessage = {
|
|
37
83
|
role: "toolResult",
|
|
38
84
|
toolCallId: toolCall.id,
|
|
39
85
|
toolName: toolCall.name,
|
|
40
86
|
content: result.content,
|
|
41
|
-
details:
|
|
42
|
-
isError:
|
|
87
|
+
details: result.details,
|
|
88
|
+
isError: false,
|
|
43
89
|
timestamp: Date.now(),
|
|
44
90
|
};
|
|
45
91
|
stream.push({ type: "message_start", message: toolResultMessage });
|
|
@@ -113,6 +159,29 @@ async function executeToolCallsSequential(tools, toolCalls, signal, stream, getS
|
|
|
113
159
|
return { toolResults: results, steeringMessages };
|
|
114
160
|
}
|
|
115
161
|
async function executeToolCallsParallelTasksOnly(tools, toolCalls, signal, stream, getSteeringMessages) {
|
|
162
|
+
const steeringAbortController = new AbortController();
|
|
163
|
+
const linkedAbort = createLinkedAbortController(signal, steeringAbortController.signal);
|
|
164
|
+
const executionSignal = linkedAbort.signal;
|
|
165
|
+
let completedCount = 0;
|
|
166
|
+
let steeringMessages;
|
|
167
|
+
const steeringWatcher = getSteeringMessages
|
|
168
|
+
? (async () => {
|
|
169
|
+
while (completedCount < toolCalls.length && !executionSignal.aborted) {
|
|
170
|
+
const steering = await getSteeringMessages();
|
|
171
|
+
if (steering.length > 0) {
|
|
172
|
+
steeringMessages = steering;
|
|
173
|
+
if (!signal.aborted && !steeringAbortController.signal.aborted) {
|
|
174
|
+
steeringAbortController.abort();
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (completedCount >= toolCalls.length || executionSignal.aborted) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await waitForSteeringPoll(executionSignal);
|
|
182
|
+
}
|
|
183
|
+
})()
|
|
184
|
+
: undefined;
|
|
116
185
|
const executions = toolCalls.map(async (toolCall, index) => {
|
|
117
186
|
const tool = tools?.find((item) => item.name === toolCall.name);
|
|
118
187
|
stream.push({
|
|
@@ -128,7 +197,7 @@ async function executeToolCallsParallelTasksOnly(tools, toolCalls, signal, strea
|
|
|
128
197
|
throw new Error(`Tool ${toolCall.name} not found`);
|
|
129
198
|
}
|
|
130
199
|
const validatedArgs = validateToolArguments(tool, toolCall);
|
|
131
|
-
result = await tool.execute(toolCall.id, validatedArgs,
|
|
200
|
+
result = await tool.execute(toolCall.id, validatedArgs, executionSignal, (partialResult) => {
|
|
132
201
|
stream.push({
|
|
133
202
|
type: "tool_execution_update",
|
|
134
203
|
toolCallId: toolCall.id,
|
|
@@ -139,8 +208,13 @@ async function executeToolCallsParallelTasksOnly(tools, toolCalls, signal, strea
|
|
|
139
208
|
});
|
|
140
209
|
}
|
|
141
210
|
catch (error) {
|
|
142
|
-
|
|
143
|
-
|
|
211
|
+
const interruptedBySteering = steeringAbortController.signal.aborted && !signal.aborted;
|
|
212
|
+
const steeringSkip = interruptedBySteering && isAbortError(error);
|
|
213
|
+
result = steeringSkip ? createSteeringSkipResult() : toToolResultError(error);
|
|
214
|
+
isError = !steeringSkip;
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
completedCount += 1;
|
|
144
218
|
}
|
|
145
219
|
stream.push({
|
|
146
220
|
type: "tool_execution_end",
|
|
@@ -162,16 +236,29 @@ async function executeToolCallsParallelTasksOnly(tools, toolCalls, signal, strea
|
|
|
162
236
|
stream.push({ type: "message_end", message: toolResultMessage });
|
|
163
237
|
return { index, toolResultMessage };
|
|
164
238
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
239
|
+
try {
|
|
240
|
+
const completed = await Promise.all(executions);
|
|
241
|
+
if (steeringWatcher) {
|
|
242
|
+
await steeringWatcher;
|
|
243
|
+
}
|
|
244
|
+
const orderedResults = completed
|
|
245
|
+
.slice()
|
|
246
|
+
.sort((left, right) => left.index - right.index)
|
|
247
|
+
.map((item) => item.toolResultMessage);
|
|
248
|
+
if (!steeringMessages && getSteeringMessages) {
|
|
249
|
+
const trailingSteering = await getSteeringMessages();
|
|
250
|
+
if (trailingSteering.length > 0) {
|
|
251
|
+
steeringMessages = trailingSteering;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
toolResults: orderedResults,
|
|
256
|
+
steeringMessages: steeringMessages && steeringMessages.length > 0 ? steeringMessages : undefined,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
linkedAbort.cleanup();
|
|
261
|
+
}
|
|
175
262
|
}
|
|
176
263
|
async function executeToolCallsWithPolicy(tools, assistantMessage, signal, stream, getSteeringMessages) {
|
|
177
264
|
const toolCalls = assistantMessage.content.filter((content) => content.type === "toolCall");
|
|
@@ -180,6 +267,9 @@ async function executeToolCallsWithPolicy(tools, assistantMessage, signal, strea
|
|
|
180
267
|
}
|
|
181
268
|
return executeToolCallsSequential(tools, toolCalls, signal, stream, getSteeringMessages);
|
|
182
269
|
}
|
|
270
|
+
export const __parallelTaskAgentTestUtils = {
|
|
271
|
+
executeToolCallsWithPolicy,
|
|
272
|
+
};
|
|
183
273
|
async function streamAssistantResponse(context, config, signal, stream, streamFn) {
|
|
184
274
|
let messages = context.messages;
|
|
185
275
|
if (config.transformContext) {
|