gsd-pi 2.36.0-dev.d612764 → 2.36.0-dev.f887f4e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/gsd/auto-dashboard.js +104 -334
- package/dist/resources/extensions/gsd/auto-loop.js +0 -11
- package/dist/resources/extensions/gsd/auto.js +0 -16
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +1 -51
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -25
- package/dist/resources/extensions/gsd/index.js +0 -5
- package/dist/resources/extensions/gsd/notifications.js +1 -10
- package/dist/resources/extensions/gsd/preferences-types.js +0 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +0 -29
- package/dist/resources/extensions/gsd/preferences.js +0 -3
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +3 -4
- package/dist/resources/extensions/gsd/prompts/research-slice.md +2 -3
- package/dist/resources/extensions/gsd/templates/preferences.md +0 -6
- package/dist/resources/extensions/search-the-web/native-search.js +4 -45
- package/dist/resources/extensions/shared/terminal.js +0 -5
- package/dist/resources/extensions/subagent/index.js +60 -180
- package/package.json +1 -1
- package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal-image.js +0 -4
- package/packages/pi-tui/dist/terminal-image.js.map +1 -1
- package/packages/pi-tui/src/terminal-image.ts +0 -5
- package/src/resources/extensions/gsd/auto-dashboard.ts +116 -363
- package/src/resources/extensions/gsd/auto-loop.ts +0 -42
- package/src/resources/extensions/gsd/auto.ts +0 -21
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +1 -54
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -25
- package/src/resources/extensions/gsd/index.ts +0 -8
- package/src/resources/extensions/gsd/notifications.ts +1 -10
- package/src/resources/extensions/gsd/preferences-types.ts +0 -13
- package/src/resources/extensions/gsd/preferences-validation.ts +0 -26
- package/src/resources/extensions/gsd/preferences.ts +0 -4
- package/src/resources/extensions/gsd/prompts/research-milestone.md +3 -4
- package/src/resources/extensions/gsd/prompts/research-slice.md +2 -3
- package/src/resources/extensions/gsd/templates/preferences.md +0 -6
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -23
- package/src/resources/extensions/search-the-web/native-search.ts +4 -50
- package/src/resources/extensions/shared/terminal.ts +0 -5
- package/src/resources/extensions/subagent/index.ts +79 -236
- package/dist/resources/extensions/cmux/index.js +0 -321
- package/dist/resources/extensions/gsd/commands-cmux.js +0 -120
- package/src/resources/extensions/cmux/index.ts +0 -384
- package/src/resources/extensions/gsd/commands-cmux.ts +0 -143
- package/src/resources/extensions/gsd/tests/cmux.test.ts +0 -98
|
@@ -16,16 +16,6 @@ export const CUSTOM_SEARCH_TOOL_NAMES = ["search-the-web", "search_and_read", "g
|
|
|
16
16
|
/** Thinking block types that require signature validation by the API */
|
|
17
17
|
const THINKING_TYPES = new Set(["thinking", "redacted_thinking"]);
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Maximum number of native web searches allowed per session (agent unit).
|
|
21
|
-
* The Anthropic API's `max_uses` is per-request — it resets on each API call.
|
|
22
|
-
* When `pause_turn` triggers a resubmit, the model gets a fresh budget.
|
|
23
|
-
* This session-level cap prevents unbounded search accumulation (#1309).
|
|
24
|
-
*
|
|
25
|
-
* 15 = 3 full turns of 5 searches each — generous for research, but bounded.
|
|
26
|
-
*/
|
|
27
|
-
export const MAX_NATIVE_SEARCHES_PER_SESSION = 15;
|
|
28
|
-
|
|
29
19
|
/** When true, skip native web search injection and keep Brave/custom tools active on Anthropic. */
|
|
30
20
|
export function preferBraveSearch(): boolean {
|
|
31
21
|
// preferences.md takes priority over env var
|
|
@@ -84,11 +74,6 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
|
|
|
84
74
|
let isAnthropicProvider = false;
|
|
85
75
|
let modelSelectFired = false;
|
|
86
76
|
|
|
87
|
-
// Session-level native search counter (#1309).
|
|
88
|
-
// Tracks cumulative web_search_tool_result blocks across all turns in a session.
|
|
89
|
-
// Reset on session_start. Used to compute remaining budget for max_uses.
|
|
90
|
-
let sessionSearchCount = 0;
|
|
91
|
-
|
|
92
77
|
// Track provider changes via model selection — also handles diagnostics
|
|
93
78
|
// since model_select fires AFTER session_start and knows the provider.
|
|
94
79
|
pi.on("model_select", async (event: any, ctx: any) => {
|
|
@@ -176,41 +161,13 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
|
|
|
176
161
|
);
|
|
177
162
|
payload.tools = tools;
|
|
178
163
|
|
|
179
|
-
// ── Session-level search budget (#1309) ──────────────────────────────
|
|
180
|
-
// Count web_search_tool_result blocks in the conversation history to
|
|
181
|
-
// determine how many native searches have already been used this session.
|
|
182
|
-
// The Anthropic API's max_uses resets per request, so without this guard,
|
|
183
|
-
// pause_turn → resubmit cycles allow unlimited total searches.
|
|
184
|
-
if (Array.isArray(messages)) {
|
|
185
|
-
let historySearchCount = 0;
|
|
186
|
-
for (const msg of messages) {
|
|
187
|
-
const content = msg.content;
|
|
188
|
-
if (!Array.isArray(content)) continue;
|
|
189
|
-
for (const block of content) {
|
|
190
|
-
if ((block as any)?.type === "web_search_tool_result") {
|
|
191
|
-
historySearchCount++;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
// Sync counter from history (handles session restore / context replay)
|
|
196
|
-
sessionSearchCount = historySearchCount;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const remaining = Math.max(0, MAX_NATIVE_SEARCHES_PER_SESSION - sessionSearchCount);
|
|
200
|
-
|
|
201
|
-
if (remaining <= 0) {
|
|
202
|
-
// Budget exhausted — don't inject the search tool at all.
|
|
203
|
-
// The model will proceed without web search capability.
|
|
204
|
-
return payload;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
164
|
tools.push({
|
|
208
165
|
type: "web_search_20250305",
|
|
209
166
|
name: "web_search",
|
|
210
|
-
// Cap
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
max_uses:
|
|
167
|
+
// Cap server-side searches per response to prevent the model from
|
|
168
|
+
// looping on web_search without synthesizing results (#817).
|
|
169
|
+
// 5 searches is generous — most queries need 1-2.
|
|
170
|
+
max_uses: 5,
|
|
214
171
|
});
|
|
215
172
|
|
|
216
173
|
return payload;
|
|
@@ -218,9 +175,6 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
|
|
|
218
175
|
|
|
219
176
|
// Basic startup diagnostics — provider-specific info comes from model_select
|
|
220
177
|
pi.on("session_start", async (_event: any, ctx: any) => {
|
|
221
|
-
// Reset session-level search budget (#1309)
|
|
222
|
-
sessionSearchCount = 0;
|
|
223
|
-
|
|
224
178
|
const hasBrave = !!process.env.BRAVE_API_KEY;
|
|
225
179
|
const hasJina = !!process.env.JINA_API_KEY;
|
|
226
180
|
const hasAnswers = !!process.env.BRAVE_ANSWERS_KEY;
|
|
@@ -7,14 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
const UNSUPPORTED_TERMS = ["apple_terminal", "warpterm"];
|
|
9
9
|
|
|
10
|
-
export function isCmuxTerminal(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
11
|
-
return Boolean(env.CMUX_WORKSPACE_ID && env.CMUX_SURFACE_ID);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
10
|
export function supportsCtrlAltShortcuts(): boolean {
|
|
15
11
|
const term = (process.env.TERM_PROGRAM || "").toLowerCase();
|
|
16
12
|
const jetbrains = (process.env.TERMINAL_EMULATOR || "").toLowerCase().includes("jetbrains");
|
|
17
|
-
if (isCmuxTerminal()) return true;
|
|
18
13
|
return !UNSUPPORTED_TERMS.some((t) => term.includes(t)) && !jetbrains;
|
|
19
14
|
}
|
|
20
15
|
|
|
@@ -34,8 +34,6 @@ import {
|
|
|
34
34
|
readIsolationMode,
|
|
35
35
|
} from "./isolation.js";
|
|
36
36
|
import { registerWorker, updateWorker } from "./worker-registry.js";
|
|
37
|
-
import { loadEffectiveGSDPreferences } from "../gsd/preferences.js";
|
|
38
|
-
import { CmuxClient, shellEscape } from "../cmux/index.js";
|
|
39
37
|
|
|
40
38
|
const MAX_PARALLEL_TASKS = 8;
|
|
41
39
|
const MAX_CONCURRENCY = 4;
|
|
@@ -259,70 +257,6 @@ function writePromptToTempFile(agentName: string, prompt: string): { dir: string
|
|
|
259
257
|
return { dir: tmpDir, filePath };
|
|
260
258
|
}
|
|
261
259
|
|
|
262
|
-
function buildSubagentProcessArgs(
|
|
263
|
-
agent: AgentConfig,
|
|
264
|
-
task: string,
|
|
265
|
-
tmpPromptPath: string | null,
|
|
266
|
-
): string[] {
|
|
267
|
-
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
268
|
-
if (agent.model) args.push("--model", agent.model);
|
|
269
|
-
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
270
|
-
if (tmpPromptPath) args.push("--append-system-prompt", tmpPromptPath);
|
|
271
|
-
args.push(`Task: ${task}`);
|
|
272
|
-
return args;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function processSubagentEventLine(
|
|
276
|
-
line: string,
|
|
277
|
-
currentResult: SingleResult,
|
|
278
|
-
emitUpdate: () => void,
|
|
279
|
-
): void {
|
|
280
|
-
if (!line.trim()) return;
|
|
281
|
-
let event: any;
|
|
282
|
-
try {
|
|
283
|
-
event = JSON.parse(line);
|
|
284
|
-
} catch {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (event.type === "message_end" && event.message) {
|
|
289
|
-
const msg = event.message as Message;
|
|
290
|
-
currentResult.messages.push(msg);
|
|
291
|
-
|
|
292
|
-
if (msg.role === "assistant") {
|
|
293
|
-
currentResult.usage.turns++;
|
|
294
|
-
const usage = msg.usage;
|
|
295
|
-
if (usage) {
|
|
296
|
-
currentResult.usage.input += usage.input || 0;
|
|
297
|
-
currentResult.usage.output += usage.output || 0;
|
|
298
|
-
currentResult.usage.cacheRead += usage.cacheRead || 0;
|
|
299
|
-
currentResult.usage.cacheWrite += usage.cacheWrite || 0;
|
|
300
|
-
currentResult.usage.cost += usage.cost?.total || 0;
|
|
301
|
-
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
302
|
-
}
|
|
303
|
-
if (!currentResult.model && msg.model) currentResult.model = msg.model;
|
|
304
|
-
if (msg.stopReason) currentResult.stopReason = msg.stopReason;
|
|
305
|
-
if (msg.errorMessage) currentResult.errorMessage = msg.errorMessage;
|
|
306
|
-
}
|
|
307
|
-
emitUpdate();
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (event.type === "tool_result_end" && event.message) {
|
|
311
|
-
currentResult.messages.push(event.message as Message);
|
|
312
|
-
emitUpdate();
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
async function waitForFile(filePath: string, signal: AbortSignal | undefined, timeoutMs = 30 * 60 * 1000): Promise<boolean> {
|
|
317
|
-
const started = Date.now();
|
|
318
|
-
while (Date.now() - started < timeoutMs) {
|
|
319
|
-
if (signal?.aborted) return false;
|
|
320
|
-
if (fs.existsSync(filePath)) return true;
|
|
321
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
322
|
-
}
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
260
|
type OnUpdateCallback = (partial: AgentToolResult<SubagentDetails>) => void;
|
|
327
261
|
|
|
328
262
|
async function runSingleAgent(
|
|
@@ -352,6 +286,10 @@ async function runSingleAgent(
|
|
|
352
286
|
};
|
|
353
287
|
}
|
|
354
288
|
|
|
289
|
+
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
290
|
+
if (agent.model) args.push("--model", agent.model);
|
|
291
|
+
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
292
|
+
|
|
355
293
|
let tmpPromptDir: string | null = null;
|
|
356
294
|
let tmpPromptPath: string | null = null;
|
|
357
295
|
|
|
@@ -381,8 +319,10 @@ async function runSingleAgent(
|
|
|
381
319
|
const tmp = writePromptToTempFile(agent.name, agent.systemPrompt);
|
|
382
320
|
tmpPromptDir = tmp.dir;
|
|
383
321
|
tmpPromptPath = tmp.filePath;
|
|
322
|
+
args.push("--append-system-prompt", tmpPromptPath);
|
|
384
323
|
}
|
|
385
|
-
|
|
324
|
+
|
|
325
|
+
args.push(`Task: ${task}`);
|
|
386
326
|
let wasAborted = false;
|
|
387
327
|
|
|
388
328
|
const exitCode = await new Promise<number>((resolve) => {
|
|
@@ -396,11 +336,48 @@ async function runSingleAgent(
|
|
|
396
336
|
liveSubagentProcesses.add(proc);
|
|
397
337
|
let buffer = "";
|
|
398
338
|
|
|
339
|
+
const processLine = (line: string) => {
|
|
340
|
+
if (!line.trim()) return;
|
|
341
|
+
let event: any;
|
|
342
|
+
try {
|
|
343
|
+
event = JSON.parse(line);
|
|
344
|
+
} catch {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (event.type === "message_end" && event.message) {
|
|
349
|
+
const msg = event.message as Message;
|
|
350
|
+
currentResult.messages.push(msg);
|
|
351
|
+
|
|
352
|
+
if (msg.role === "assistant") {
|
|
353
|
+
currentResult.usage.turns++;
|
|
354
|
+
const usage = msg.usage;
|
|
355
|
+
if (usage) {
|
|
356
|
+
currentResult.usage.input += usage.input || 0;
|
|
357
|
+
currentResult.usage.output += usage.output || 0;
|
|
358
|
+
currentResult.usage.cacheRead += usage.cacheRead || 0;
|
|
359
|
+
currentResult.usage.cacheWrite += usage.cacheWrite || 0;
|
|
360
|
+
currentResult.usage.cost += usage.cost?.total || 0;
|
|
361
|
+
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
362
|
+
}
|
|
363
|
+
if (!currentResult.model && msg.model) currentResult.model = msg.model;
|
|
364
|
+
if (msg.stopReason) currentResult.stopReason = msg.stopReason;
|
|
365
|
+
if (msg.errorMessage) currentResult.errorMessage = msg.errorMessage;
|
|
366
|
+
}
|
|
367
|
+
emitUpdate();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (event.type === "tool_result_end" && event.message) {
|
|
371
|
+
currentResult.messages.push(event.message as Message);
|
|
372
|
+
emitUpdate();
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
399
376
|
proc.stdout.on("data", (data) => {
|
|
400
377
|
buffer += data.toString();
|
|
401
378
|
const lines = buffer.split("\n");
|
|
402
379
|
buffer = lines.pop() || "";
|
|
403
|
-
for (const line of lines)
|
|
380
|
+
for (const line of lines) processLine(line);
|
|
404
381
|
});
|
|
405
382
|
|
|
406
383
|
proc.stderr.on("data", (data) => {
|
|
@@ -409,7 +386,7 @@ async function runSingleAgent(
|
|
|
409
386
|
|
|
410
387
|
proc.on("close", (code) => {
|
|
411
388
|
liveSubagentProcesses.delete(proc);
|
|
412
|
-
if (buffer.trim())
|
|
389
|
+
if (buffer.trim()) processLine(buffer);
|
|
413
390
|
resolve(code ?? 0);
|
|
414
391
|
});
|
|
415
392
|
|
|
@@ -450,120 +427,6 @@ async function runSingleAgent(
|
|
|
450
427
|
}
|
|
451
428
|
}
|
|
452
429
|
|
|
453
|
-
async function runSingleAgentInCmuxSplit(
|
|
454
|
-
cmuxClient: CmuxClient,
|
|
455
|
-
direction: "right" | "down",
|
|
456
|
-
defaultCwd: string,
|
|
457
|
-
agents: AgentConfig[],
|
|
458
|
-
agentName: string,
|
|
459
|
-
task: string,
|
|
460
|
-
cwd: string | undefined,
|
|
461
|
-
step: number | undefined,
|
|
462
|
-
signal: AbortSignal | undefined,
|
|
463
|
-
onUpdate: OnUpdateCallback | undefined,
|
|
464
|
-
makeDetails: (results: SingleResult[]) => SubagentDetails,
|
|
465
|
-
): Promise<SingleResult> {
|
|
466
|
-
const agent = agents.find((a) => a.name === agentName);
|
|
467
|
-
if (!agent) {
|
|
468
|
-
return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
let tmpPromptDir: string | null = null;
|
|
472
|
-
let tmpPromptPath: string | null = null;
|
|
473
|
-
let tmpOutputDir: string | null = null;
|
|
474
|
-
|
|
475
|
-
const currentResult: SingleResult = {
|
|
476
|
-
agent: agentName,
|
|
477
|
-
agentSource: agent.source,
|
|
478
|
-
task,
|
|
479
|
-
exitCode: 0,
|
|
480
|
-
messages: [],
|
|
481
|
-
stderr: "",
|
|
482
|
-
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
483
|
-
model: agent.model,
|
|
484
|
-
step,
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
const emitUpdate = () => {
|
|
488
|
-
if (onUpdate) {
|
|
489
|
-
onUpdate({
|
|
490
|
-
content: [{ type: "text", text: getFinalOutput(currentResult.messages) || "(running...)" }],
|
|
491
|
-
details: makeDetails([currentResult]),
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
try {
|
|
497
|
-
if (agent.systemPrompt.trim()) {
|
|
498
|
-
const tmp = writePromptToTempFile(agent.name, agent.systemPrompt);
|
|
499
|
-
tmpPromptDir = tmp.dir;
|
|
500
|
-
tmpPromptPath = tmp.filePath;
|
|
501
|
-
}
|
|
502
|
-
tmpOutputDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-cmux-"));
|
|
503
|
-
const stdoutPath = path.join(tmpOutputDir, "stdout.jsonl");
|
|
504
|
-
const stderrPath = path.join(tmpOutputDir, "stderr.log");
|
|
505
|
-
const exitPath = path.join(tmpOutputDir, "exit.code");
|
|
506
|
-
const cmuxSurfaceId = await cmuxClient.createSplit(direction);
|
|
507
|
-
if (!cmuxSurfaceId) {
|
|
508
|
-
return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const bundledPaths = (process.env.GSD_BUNDLED_EXTENSION_PATHS ?? "").split(path.delimiter).map((s) => s.trim()).filter(Boolean);
|
|
512
|
-
const extensionArgs = bundledPaths.flatMap((p) => ["--extension", p]);
|
|
513
|
-
const processArgs = [process.env.GSD_BIN_PATH!, ...extensionArgs, ...buildSubagentProcessArgs(agent, task, tmpPromptPath)];
|
|
514
|
-
const innerScript = [
|
|
515
|
-
`cd ${shellEscape(cwd ?? defaultCwd)}`,
|
|
516
|
-
"set -o pipefail",
|
|
517
|
-
`${shellEscape(process.execPath)} ${processArgs.map(shellEscape).join(" ")} 2> >(tee ${shellEscape(stderrPath)} >&2) | tee ${shellEscape(stdoutPath)}`,
|
|
518
|
-
"status=${PIPESTATUS[0]}",
|
|
519
|
-
`printf '%s' "$status" > ${shellEscape(exitPath)}`,
|
|
520
|
-
].join("; ");
|
|
521
|
-
|
|
522
|
-
const sent = await cmuxClient.sendSurface(cmuxSurfaceId, `bash -lc ${shellEscape(innerScript)}`);
|
|
523
|
-
if (!sent) {
|
|
524
|
-
return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const finished = await waitForFile(exitPath, signal);
|
|
528
|
-
if (!finished) {
|
|
529
|
-
currentResult.exitCode = 1;
|
|
530
|
-
currentResult.stderr = "cmux split execution timed out or was aborted";
|
|
531
|
-
return currentResult;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
if (fs.existsSync(stdoutPath)) {
|
|
535
|
-
const stdout = fs.readFileSync(stdoutPath, "utf-8");
|
|
536
|
-
for (const line of stdout.split("\n")) {
|
|
537
|
-
processSubagentEventLine(line, currentResult, emitUpdate);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
if (fs.existsSync(stderrPath)) {
|
|
541
|
-
currentResult.stderr = fs.readFileSync(stderrPath, "utf-8");
|
|
542
|
-
}
|
|
543
|
-
currentResult.exitCode = Number.parseInt(fs.readFileSync(exitPath, "utf-8").trim() || "1", 10) || 0;
|
|
544
|
-
return currentResult;
|
|
545
|
-
} finally {
|
|
546
|
-
if (tmpPromptPath)
|
|
547
|
-
try {
|
|
548
|
-
fs.unlinkSync(tmpPromptPath);
|
|
549
|
-
} catch {
|
|
550
|
-
/* ignore */
|
|
551
|
-
}
|
|
552
|
-
if (tmpPromptDir)
|
|
553
|
-
try {
|
|
554
|
-
fs.rmdirSync(tmpPromptDir);
|
|
555
|
-
} catch {
|
|
556
|
-
/* ignore */
|
|
557
|
-
}
|
|
558
|
-
if (tmpOutputDir)
|
|
559
|
-
try {
|
|
560
|
-
fs.rmSync(tmpOutputDir, { recursive: true, force: true });
|
|
561
|
-
} catch {
|
|
562
|
-
/* ignore */
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
430
|
const TaskItem = Type.Object({
|
|
568
431
|
agent: Type.String({ description: "Name of the agent to invoke" }),
|
|
569
432
|
task: Type.String({ description: "Task to delegate to the agent" }),
|
|
@@ -648,8 +511,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
648
511
|
const discovery = discoverAgents(ctx.cwd, agentScope);
|
|
649
512
|
const agents = discovery.agents;
|
|
650
513
|
const confirmProjectAgents = params.confirmProjectAgents ?? false;
|
|
651
|
-
const cmuxClient = CmuxClient.fromPreferences(loadEffectiveGSDPreferences()?.preferences);
|
|
652
|
-
const cmuxSplitsEnabled = cmuxClient.getConfig().splits;
|
|
653
514
|
|
|
654
515
|
// Resolve isolation mode
|
|
655
516
|
const isolationMode = readIsolationMode();
|
|
@@ -808,26 +669,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
808
669
|
const batchSize = params.tasks.length;
|
|
809
670
|
const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
|
|
810
671
|
const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
672
|
+
let result = await runSingleAgent(
|
|
673
|
+
ctx.cwd,
|
|
674
|
+
agents,
|
|
675
|
+
t.agent,
|
|
676
|
+
t.task,
|
|
677
|
+
t.cwd,
|
|
678
|
+
undefined,
|
|
679
|
+
signal,
|
|
680
|
+
// Per-task update callback
|
|
681
|
+
(partial) => {
|
|
682
|
+
if (partial.details?.results[0]) {
|
|
683
|
+
allResults[index] = partial.details.results[0];
|
|
684
|
+
emitParallelUpdate();
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
makeDetails("parallel"),
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
// Auto-retry failed tasks (likely API rate limit or transient error)
|
|
691
|
+
const isFailed = result.exitCode !== 0 || (result.messages.length === 0 && !signal?.aborted);
|
|
692
|
+
if (isFailed && MAX_RETRIES > 0 && !signal?.aborted) {
|
|
693
|
+
result = await runSingleAgent(
|
|
831
694
|
ctx.cwd,
|
|
832
695
|
agents,
|
|
833
696
|
t.agent,
|
|
@@ -843,12 +706,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
843
706
|
},
|
|
844
707
|
makeDetails("parallel"),
|
|
845
708
|
);
|
|
846
|
-
let result = await runTask();
|
|
847
|
-
|
|
848
|
-
// Auto-retry failed tasks (likely API rate limit or transient error)
|
|
849
|
-
const isFailed = result.exitCode !== 0 || (result.messages.length === 0 && !signal?.aborted);
|
|
850
|
-
if (isFailed && MAX_RETRIES > 0 && !signal?.aborted) {
|
|
851
|
-
result = await runTask();
|
|
852
709
|
}
|
|
853
710
|
|
|
854
711
|
updateWorker(workerId, result.exitCode === 0 ? "completed" : "failed");
|
|
@@ -887,31 +744,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
887
744
|
isolation = await createIsolation(effectiveCwd, taskId, isolationMode);
|
|
888
745
|
}
|
|
889
746
|
|
|
890
|
-
const result =
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
onUpdate,
|
|
902
|
-
makeDetails("single"),
|
|
903
|
-
)
|
|
904
|
-
: await runSingleAgent(
|
|
905
|
-
ctx.cwd,
|
|
906
|
-
agents,
|
|
907
|
-
params.agent,
|
|
908
|
-
params.task,
|
|
909
|
-
isolation ? isolation.workDir : params.cwd,
|
|
910
|
-
undefined,
|
|
911
|
-
signal,
|
|
912
|
-
onUpdate,
|
|
913
|
-
makeDetails("single"),
|
|
914
|
-
);
|
|
747
|
+
const result = await runSingleAgent(
|
|
748
|
+
ctx.cwd,
|
|
749
|
+
agents,
|
|
750
|
+
params.agent,
|
|
751
|
+
params.task,
|
|
752
|
+
isolation ? isolation.workDir : params.cwd,
|
|
753
|
+
undefined,
|
|
754
|
+
signal,
|
|
755
|
+
onUpdate,
|
|
756
|
+
makeDetails("single"),
|
|
757
|
+
);
|
|
915
758
|
|
|
916
759
|
// Capture and merge delta if isolated
|
|
917
760
|
if (isolation) {
|