gsd-pi 2.78.1-dev.84a383f51 → 2.78.1-dev.8a893322c
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/README.md +1 -0
- package/dist/bundled-resource-path.d.ts +7 -0
- package/dist/bundled-resource-path.js +34 -2
- package/dist/claude-cli-check.js +18 -6
- package/dist/headless-query.js +21 -6
- package/dist/loader.js +2 -3
- package/dist/resource-loader.js +2 -8
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/readiness.js +19 -7
- package/dist/resources/extensions/google-search/index.js +2 -6
- package/dist/resources/extensions/gsd/auto/phases.js +3 -11
- package/dist/resources/extensions/gsd/auto/session.js +2 -6
- package/dist/resources/extensions/gsd/auto-dashboard.js +3 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -6
- package/dist/resources/extensions/gsd/auto-prompts.js +63 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -13
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +19 -1
- package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +22 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -0
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +84 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +8 -0
- package/dist/resources/extensions/gsd/commands-config.js +3 -2
- package/dist/resources/extensions/gsd/commands-extensions.js +46 -3
- package/dist/resources/extensions/gsd/commands-handlers.js +3 -2
- package/dist/resources/extensions/gsd/commands-worktree.js +309 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +6 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +2 -1
- package/dist/resources/extensions/gsd/forensics.js +8 -6
- package/dist/resources/extensions/gsd/guided-flow.js +2 -1
- package/dist/resources/extensions/gsd/home-dir.js +16 -0
- package/dist/resources/extensions/gsd/key-manager.js +2 -1
- package/dist/resources/extensions/gsd/migrate/command.js +3 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +10 -0
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +10 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +29 -4
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -1
- package/dist/resources/extensions/gsd/worktree-resolver.js +4 -13
- package/dist/resources/extensions/gsd/worktree-root.js +124 -0
- package/dist/resources/extensions/gsd/worktree.js +4 -115
- package/dist/resources/extensions/mcp-client/index.js +0 -6
- package/dist/resources/extensions/ollama/index.js +15 -2
- package/dist/resources/extensions/ollama/model-capabilities.js +31 -0
- package/dist/resources/extensions/ollama/ollama-client.js +40 -4
- package/dist/resources/extensions/subagent/index.js +324 -178
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/welcome-screen.js +27 -1
- package/dist/worktree-cli.d.ts +1 -0
- package/dist/worktree-cli.js +9 -3
- package/package.json +1 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +52 -0
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +20 -7
- package/src/resources/extensions/google-search/index.ts +2 -9
- package/src/resources/extensions/gsd/auto/phases.ts +3 -11
- package/src/resources/extensions/gsd/auto/session.ts +2 -6
- package/src/resources/extensions/gsd/auto-dashboard.ts +3 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +60 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +44 -12
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +19 -0
- package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +20 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +103 -1
- package/src/resources/extensions/gsd/commands/catalog.ts +8 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands-config.ts +3 -2
- package/src/resources/extensions/gsd/commands-extensions.ts +43 -3
- package/src/resources/extensions/gsd/commands-handlers.ts +3 -2
- package/src/resources/extensions/gsd/commands-worktree.ts +383 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +6 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +2 -1
- package/src/resources/extensions/gsd/forensics.ts +10 -5
- package/src/resources/extensions/gsd/guided-flow.ts +2 -1
- package/src/resources/extensions/gsd/home-dir.ts +19 -0
- package/src/resources/extensions/gsd/journal.ts +4 -1
- package/src/resources/extensions/gsd/key-manager.ts +2 -1
- package/src/resources/extensions/gsd/migrate/command.ts +3 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +10 -0
- package/src/resources/extensions/gsd/prompts/refine-slice.md +10 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +50 -27
- package/src/resources/extensions/gsd/tests/commands-extensions-version-compare.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +25 -65
- package/src/resources/extensions/gsd/tests/home-dir.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +50 -1
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +17 -1
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +34 -33
- package/src/resources/extensions/gsd/tests/worktree.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +116 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +36 -4
- package/src/resources/extensions/gsd/worktree-manager.ts +40 -1
- package/src/resources/extensions/gsd/worktree-resolver.ts +4 -14
- package/src/resources/extensions/gsd/worktree-root.ts +144 -0
- package/src/resources/extensions/gsd/worktree.ts +8 -119
- package/src/resources/extensions/mcp-client/index.ts +0 -7
- package/src/resources/extensions/ollama/index.ts +16 -2
- package/src/resources/extensions/ollama/model-capabilities.ts +34 -0
- package/src/resources/extensions/ollama/ollama-client.ts +41 -4
- package/src/resources/extensions/ollama/tests/model-capabilities.test.ts +96 -0
- package/src/resources/extensions/ollama/tests/ollama-client-timeout-env.test.ts +147 -0
- package/src/resources/extensions/subagent/index.ts +165 -7
- /package/dist/web/standalone/.next/static/{UF5VF4F1tB0miEtJS7LyX → QK8fABiGPmonfTgboN0Y9}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UF5VF4F1tB0miEtJS7LyX → QK8fABiGPmonfTgboN0Y9}/_ssgManifest.js +0 -0
|
@@ -26,6 +26,7 @@ import { discoverAgents } from "./agents.js";
|
|
|
26
26
|
import { createIsolation, mergeDeltaPatches, readIsolationMode, } from "./isolation.js";
|
|
27
27
|
import { registerWorker, updateWorker } from "./worker-registry.js";
|
|
28
28
|
import { loadEffectiveGSDPreferences } from "../gsd/preferences.js";
|
|
29
|
+
import { emitJournalEvent } from "../gsd/journal.js";
|
|
29
30
|
import { CmuxClient, shellEscape } from "../cmux/index.js";
|
|
30
31
|
const MAX_PARALLEL_TASKS = 8;
|
|
31
32
|
const MAX_CONCURRENCY = 4;
|
|
@@ -541,11 +542,13 @@ export default function (pi) {
|
|
|
541
542
|
"Use chain mode to pipeline: scout finds context, planner designs, worker implements.",
|
|
542
543
|
].join(" "),
|
|
543
544
|
promptGuidelines: [
|
|
544
|
-
"
|
|
545
|
-
"
|
|
546
|
-
"
|
|
547
|
-
"
|
|
548
|
-
"
|
|
545
|
+
"Prefer subagent dispatch over inline work whenever a task is self-contained — recon, planning, review, refactor, test writing, security audit, doc writing. Each dispatch gets a fresh context window, so your main session stays focused on synthesis.",
|
|
546
|
+
"Before reading more than ~3 files to understand something, dispatch the scout agent and work from its compressed report instead.",
|
|
547
|
+
"Before any change touching ≥2 packages, the orchestration kernel, auto-mode, or a public API, dispatch the planner agent first. Plan first, then implement.",
|
|
548
|
+
"You MUST use parallel mode when ≥2 ready tasks are independent of each other's output. Do not serialize independent tasks manually — that wastes wall time and context.",
|
|
549
|
+
"Use chain mode for sequential pipelines where each step's output feeds the next: scout → planner → worker, or worker → reviewer → worker.",
|
|
550
|
+
"Before opening a PR or marking a slice complete, dispatch the reviewer agent (and security agent if the change touches auth, network, parsing, file IO, or shell exec).",
|
|
551
|
+
"Always check available agents with /subagent before choosing one — there are bundled specialists plus any project-scoped agents.",
|
|
549
552
|
],
|
|
550
553
|
parameters: SubagentParams,
|
|
551
554
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
@@ -580,204 +583,347 @@ export default function (pi) {
|
|
|
580
583
|
details: makeDetails("single")([]),
|
|
581
584
|
};
|
|
582
585
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
586
|
+
// Dispatch telemetry — emit invoked once per dispatch and completed before each return.
|
|
587
|
+
// Fresh flowId per dispatch (subagent runs aren't currently plumbed with the parent
|
|
588
|
+
// auto-mode flowId; per-dispatch ids still let us measure frequency, batch size, mode).
|
|
589
|
+
const dispatchMode = hasChain ? "chain" : hasTasks ? "parallel" : "single";
|
|
590
|
+
const dispatchAgents = hasChain
|
|
591
|
+
? params.chain.map((s) => s.agent)
|
|
592
|
+
: hasTasks
|
|
593
|
+
? params.tasks.map((t) => t.agent)
|
|
594
|
+
: params.agent
|
|
595
|
+
? [params.agent]
|
|
596
|
+
: [];
|
|
597
|
+
const dispatchTasks = hasChain
|
|
598
|
+
? params.chain.map((s) => s.task)
|
|
599
|
+
: hasTasks
|
|
600
|
+
? params.tasks.map((t) => t.task)
|
|
601
|
+
: params.task
|
|
602
|
+
? [params.task]
|
|
603
|
+
: [];
|
|
604
|
+
const dispatchId = crypto.randomUUID();
|
|
605
|
+
const dispatchStartMs = Date.now();
|
|
606
|
+
let finalResults = [];
|
|
607
|
+
let dispatchCompletedEmitted = false;
|
|
608
|
+
emitJournalEvent(ctx.cwd, {
|
|
609
|
+
ts: new Date().toISOString(),
|
|
610
|
+
flowId: dispatchId,
|
|
611
|
+
seq: 0,
|
|
612
|
+
eventType: "subagent-invoked",
|
|
613
|
+
data: {
|
|
614
|
+
dispatchId,
|
|
615
|
+
mode: dispatchMode,
|
|
616
|
+
agents: dispatchAgents,
|
|
617
|
+
batchSize: dispatchAgents.length,
|
|
618
|
+
unitType: getCurrentPhase() ?? null,
|
|
619
|
+
isolated: useIsolation,
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
const zeroUsage = () => ({
|
|
623
|
+
input: 0,
|
|
624
|
+
output: 0,
|
|
625
|
+
cacheRead: 0,
|
|
626
|
+
cacheWrite: 0,
|
|
627
|
+
cost: 0,
|
|
628
|
+
contextTokens: 0,
|
|
629
|
+
turns: 0,
|
|
630
|
+
});
|
|
631
|
+
const errorMessageFor = (err) => err instanceof Error ? err.message : String(err || "subagent dispatch failed");
|
|
632
|
+
const makeFailureResult = (err, agent, task, step) => {
|
|
633
|
+
const message = errorMessageFor(err);
|
|
634
|
+
return {
|
|
635
|
+
agent,
|
|
636
|
+
agentSource: "unknown",
|
|
637
|
+
task,
|
|
638
|
+
exitCode: 1,
|
|
639
|
+
messages: [],
|
|
640
|
+
stderr: message,
|
|
641
|
+
usage: zeroUsage(),
|
|
642
|
+
stopReason: signal?.aborted ? "aborted" : "error",
|
|
643
|
+
errorMessage: message,
|
|
644
|
+
...(step !== undefined ? { step } : {}),
|
|
645
|
+
};
|
|
646
|
+
};
|
|
647
|
+
const synthesizeFailureResults = (err) => {
|
|
648
|
+
if (finalResults.length > 0) {
|
|
649
|
+
let patchedRunning = false;
|
|
650
|
+
const patched = finalResults.map((result) => {
|
|
651
|
+
if (result.exitCode !== -1)
|
|
652
|
+
return result;
|
|
653
|
+
patchedRunning = true;
|
|
654
|
+
const message = errorMessageFor(err);
|
|
601
655
|
return {
|
|
602
|
-
|
|
603
|
-
|
|
656
|
+
...result,
|
|
657
|
+
exitCode: 1,
|
|
658
|
+
stderr: result.stderr || message,
|
|
659
|
+
stopReason: signal?.aborted ? "aborted" : "error",
|
|
660
|
+
errorMessage: result.errorMessage || message,
|
|
661
|
+
usage: result.usage ?? zeroUsage(),
|
|
604
662
|
};
|
|
663
|
+
});
|
|
664
|
+
if (patchedRunning || patched.some((result) => result.exitCode !== 0))
|
|
665
|
+
return patched;
|
|
666
|
+
const nextIndex = finalResults.length < dispatchAgents.length ? finalResults.length : 0;
|
|
667
|
+
if (nextIndex > 0) {
|
|
668
|
+
return [
|
|
669
|
+
...finalResults,
|
|
670
|
+
makeFailureResult(err, dispatchAgents[nextIndex] ?? "unknown", dispatchTasks[nextIndex] ?? "", dispatchMode === "chain" ? nextIndex + 1 : undefined),
|
|
671
|
+
];
|
|
672
|
+
}
|
|
605
673
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
674
|
+
const agentsForFailure = dispatchAgents.length > 0 ? dispatchAgents : ["unknown"];
|
|
675
|
+
return agentsForFailure.map((agent, index) => makeFailureResult(err, agent, dispatchTasks[index] ?? "", dispatchMode === "chain" ? index + 1 : undefined));
|
|
676
|
+
};
|
|
677
|
+
const finishDispatch = (results) => {
|
|
678
|
+
if (dispatchCompletedEmitted)
|
|
679
|
+
return;
|
|
680
|
+
finalResults = results;
|
|
681
|
+
dispatchCompletedEmitted = true;
|
|
682
|
+
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
683
|
+
const failureCount = results.filter((r) => r.exitCode !== 0).length;
|
|
684
|
+
const totalCost = results.reduce((s, r) => s + (r.usage?.cost ?? 0), 0);
|
|
685
|
+
const totalInputTokens = results.reduce((s, r) => s + (r.usage?.input ?? 0), 0);
|
|
686
|
+
const totalOutputTokens = results.reduce((s, r) => s + (r.usage?.output ?? 0), 0);
|
|
687
|
+
emitJournalEvent(ctx.cwd, {
|
|
688
|
+
ts: new Date().toISOString(),
|
|
689
|
+
flowId: dispatchId,
|
|
690
|
+
seq: 1,
|
|
691
|
+
eventType: "subagent-completed",
|
|
692
|
+
data: {
|
|
693
|
+
dispatchId,
|
|
694
|
+
mode: dispatchMode,
|
|
695
|
+
agents: dispatchAgents,
|
|
696
|
+
successCount,
|
|
697
|
+
failureCount,
|
|
698
|
+
totalCost,
|
|
699
|
+
totalInputTokens,
|
|
700
|
+
totalOutputTokens,
|
|
701
|
+
wallTimeMs: Date.now() - dispatchStartMs,
|
|
702
|
+
},
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
try {
|
|
706
|
+
if ((agentScope === "project" || agentScope === "both") && confirmProjectAgents && ctx.hasUI) {
|
|
707
|
+
const requestedAgentNames = new Set();
|
|
708
|
+
if (params.chain)
|
|
709
|
+
for (const step of params.chain)
|
|
710
|
+
requestedAgentNames.add(step.agent);
|
|
711
|
+
if (params.tasks)
|
|
712
|
+
for (const t of params.tasks)
|
|
713
|
+
requestedAgentNames.add(t.agent);
|
|
714
|
+
if (params.agent)
|
|
715
|
+
requestedAgentNames.add(params.agent);
|
|
716
|
+
const projectAgentsRequested = Array.from(requestedAgentNames)
|
|
717
|
+
.map((name) => agents.find((a) => a.name === name))
|
|
718
|
+
.filter((a) => a?.source === "project");
|
|
719
|
+
if (projectAgentsRequested.length > 0) {
|
|
720
|
+
const names = projectAgentsRequested.map((a) => a.name).join(", ");
|
|
721
|
+
const dir = discovery.projectAgentsDir ?? "(unknown)";
|
|
722
|
+
const ok = await ctx.ui.confirm("Run project-local agents?", `Agents: ${names}\nSource: ${dir}\n\nProject agents are repo-controlled. Only continue for trusted repositories.`);
|
|
723
|
+
if (!ok) {
|
|
724
|
+
finishDispatch([]);
|
|
725
|
+
return {
|
|
726
|
+
content: [{ type: "text", text: "Canceled: project-local agents not approved." }],
|
|
727
|
+
details: makeDetails(hasChain ? "chain" : hasTasks ? "parallel" : "single")([]),
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (params.chain && params.chain.length > 0) {
|
|
733
|
+
const results = [];
|
|
734
|
+
finalResults = results;
|
|
735
|
+
let previousOutput = "";
|
|
736
|
+
for (let i = 0; i < params.chain.length; i++) {
|
|
737
|
+
const step = params.chain[i];
|
|
738
|
+
const taskWithContext = step.task.replace(/\{previous\}/g, previousOutput);
|
|
739
|
+
// Create update callback that includes all previous results
|
|
740
|
+
const chainUpdate = onUpdate
|
|
741
|
+
? (partial) => {
|
|
742
|
+
// Combine completed results with current streaming result
|
|
743
|
+
const currentResult = partial.details?.results[0];
|
|
744
|
+
if (currentResult) {
|
|
745
|
+
const allResults = [...results, currentResult];
|
|
746
|
+
onUpdate({
|
|
747
|
+
content: partial.content,
|
|
748
|
+
details: makeDetails("chain")(allResults),
|
|
749
|
+
});
|
|
750
|
+
}
|
|
624
751
|
}
|
|
752
|
+
: undefined;
|
|
753
|
+
const result = await runSingleAgent(ctx.cwd, agents, step.agent, taskWithContext, step.cwd, i + 1, signal, chainUpdate, makeDetails("chain"));
|
|
754
|
+
results.push(result);
|
|
755
|
+
const isError = result.exitCode !== 0 || result.stopReason === "error" || result.stopReason === "aborted";
|
|
756
|
+
if (isError) {
|
|
757
|
+
const errorMsg = result.errorMessage || result.stderr || getFinalOutput(result.messages) || "(no output)";
|
|
758
|
+
finishDispatch(results);
|
|
759
|
+
return {
|
|
760
|
+
content: [{ type: "text", text: `Chain stopped at step ${i + 1} (${step.agent}): ${errorMsg}` }],
|
|
761
|
+
details: makeDetails("chain")(results),
|
|
762
|
+
isError: true,
|
|
763
|
+
};
|
|
625
764
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
results
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
765
|
+
previousOutput = getFinalOutput(result.messages);
|
|
766
|
+
}
|
|
767
|
+
finishDispatch(results);
|
|
768
|
+
return {
|
|
769
|
+
content: [{ type: "text", text: getFinalOutput(results[results.length - 1].messages) || "(no output)" }],
|
|
770
|
+
details: makeDetails("chain")(results),
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
if (params.tasks && params.tasks.length > 0) {
|
|
774
|
+
if (params.tasks.length > MAX_PARALLEL_TASKS) {
|
|
775
|
+
finishDispatch([]);
|
|
632
776
|
return {
|
|
633
|
-
content: [
|
|
634
|
-
|
|
635
|
-
|
|
777
|
+
content: [
|
|
778
|
+
{
|
|
779
|
+
type: "text",
|
|
780
|
+
text: `Too many parallel tasks (${params.tasks.length}). Max is ${MAX_PARALLEL_TASKS}.`,
|
|
781
|
+
},
|
|
782
|
+
],
|
|
783
|
+
details: makeDetails("parallel")([]),
|
|
636
784
|
};
|
|
637
785
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
786
|
+
// Track all results for streaming updates
|
|
787
|
+
const allResults = new Array(params.tasks.length);
|
|
788
|
+
// Initialize placeholder results
|
|
789
|
+
for (let i = 0; i < params.tasks.length; i++) {
|
|
790
|
+
allResults[i] = {
|
|
791
|
+
agent: params.tasks[i].agent,
|
|
792
|
+
agentSource: "unknown",
|
|
793
|
+
task: params.tasks[i].task,
|
|
794
|
+
exitCode: -1, // -1 = still running
|
|
795
|
+
messages: [],
|
|
796
|
+
stderr: "",
|
|
797
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
finalResults = allResults;
|
|
801
|
+
const emitParallelUpdate = () => {
|
|
802
|
+
if (onUpdate) {
|
|
803
|
+
const running = allResults.filter((r) => r.exitCode === -1).length;
|
|
804
|
+
const done = allResults.filter((r) => r.exitCode !== -1).length;
|
|
805
|
+
onUpdate({
|
|
806
|
+
content: [
|
|
807
|
+
{ type: "text", text: `Parallel: ${done}/${allResults.length} done, ${running} running...` },
|
|
808
|
+
],
|
|
809
|
+
details: makeDetails("parallel")([...allResults]),
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
const MAX_RETRIES = 1; // Retry failed tasks once
|
|
814
|
+
const batchId = crypto.randomUUID();
|
|
815
|
+
const batchSize = params.tasks.length;
|
|
816
|
+
// Pre-create a grid layout for cmux splits so agents get a clean tiled arrangement
|
|
817
|
+
const gridSurfaces = cmuxSplitsEnabled
|
|
818
|
+
? await cmuxClient.createGridLayout(Math.min(batchSize, MAX_CONCURRENCY))
|
|
819
|
+
: [];
|
|
820
|
+
const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
|
|
821
|
+
const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
|
|
822
|
+
const runTask = () => cmuxSplitsEnabled
|
|
823
|
+
? runSingleAgentInCmuxSplit(cmuxClient, gridSurfaces[index] ?? (index % 2 === 0 ? "right" : "down"), ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
|
|
824
|
+
if (partial.details?.results[0]) {
|
|
825
|
+
allResults[index] = partial.details.results[0];
|
|
826
|
+
emitParallelUpdate();
|
|
827
|
+
}
|
|
828
|
+
}, makeDetails("parallel"))
|
|
829
|
+
: runSingleAgent(ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
|
|
830
|
+
if (partial.details?.results[0]) {
|
|
831
|
+
allResults[index] = partial.details.results[0];
|
|
832
|
+
emitParallelUpdate();
|
|
833
|
+
}
|
|
834
|
+
}, makeDetails("parallel"));
|
|
835
|
+
let result = await runTask();
|
|
836
|
+
// Auto-retry failed tasks (likely API rate limit or transient error)
|
|
837
|
+
const isFailed = result.exitCode !== 0 || (result.messages.length === 0 && !signal?.aborted);
|
|
838
|
+
if (isFailed && MAX_RETRIES > 0 && !signal?.aborted) {
|
|
839
|
+
result = await runTask();
|
|
840
|
+
}
|
|
841
|
+
updateWorker(workerId, result.exitCode === 0 ? "completed" : "failed");
|
|
842
|
+
allResults[index] = result;
|
|
843
|
+
emitParallelUpdate();
|
|
844
|
+
return result;
|
|
845
|
+
});
|
|
846
|
+
finalResults = results;
|
|
847
|
+
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
848
|
+
const summaries = results.map((r) => {
|
|
849
|
+
const isError = r.exitCode !== 0 || r.stopReason === "error" || r.stopReason === "aborted";
|
|
850
|
+
const output = isError
|
|
851
|
+
? (r.errorMessage || r.stderr || getFinalOutput(r.messages) || "(no output)")
|
|
852
|
+
: getFinalOutput(r.messages);
|
|
853
|
+
return `[${r.agent}] ${r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`}: ${output || "(no output)"}`;
|
|
854
|
+
});
|
|
855
|
+
finishDispatch(results);
|
|
647
856
|
return {
|
|
648
857
|
content: [
|
|
649
858
|
{
|
|
650
859
|
type: "text",
|
|
651
|
-
text: `
|
|
860
|
+
text: `Parallel: ${successCount}/${results.length} succeeded\n\n${summaries.join("\n\n")}`,
|
|
652
861
|
},
|
|
653
862
|
],
|
|
654
|
-
details: makeDetails("parallel")(
|
|
655
|
-
};
|
|
656
|
-
// Track all results for streaming updates
|
|
657
|
-
const allResults = new Array(params.tasks.length);
|
|
658
|
-
// Initialize placeholder results
|
|
659
|
-
for (let i = 0; i < params.tasks.length; i++) {
|
|
660
|
-
allResults[i] = {
|
|
661
|
-
agent: params.tasks[i].agent,
|
|
662
|
-
agentSource: "unknown",
|
|
663
|
-
task: params.tasks[i].task,
|
|
664
|
-
exitCode: -1, // -1 = still running
|
|
665
|
-
messages: [],
|
|
666
|
-
stderr: "",
|
|
667
|
-
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
863
|
+
details: makeDetails("parallel")(results),
|
|
668
864
|
};
|
|
669
865
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
: [];
|
|
689
|
-
const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
|
|
690
|
-
const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
|
|
691
|
-
const runTask = () => cmuxSplitsEnabled
|
|
692
|
-
? runSingleAgentInCmuxSplit(cmuxClient, gridSurfaces[index] ?? (index % 2 === 0 ? "right" : "down"), ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
|
|
693
|
-
if (partial.details?.results[0]) {
|
|
694
|
-
allResults[index] = partial.details.results[0];
|
|
695
|
-
emitParallelUpdate();
|
|
696
|
-
}
|
|
697
|
-
}, makeDetails("parallel"))
|
|
698
|
-
: runSingleAgent(ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
|
|
699
|
-
if (partial.details?.results[0]) {
|
|
700
|
-
allResults[index] = partial.details.results[0];
|
|
701
|
-
emitParallelUpdate();
|
|
866
|
+
if (params.agent && params.task) {
|
|
867
|
+
let isolation = null;
|
|
868
|
+
let mergeResult;
|
|
869
|
+
try {
|
|
870
|
+
const effectiveCwd = params.cwd ?? ctx.cwd;
|
|
871
|
+
if (useIsolation) {
|
|
872
|
+
const taskId = crypto.randomUUID();
|
|
873
|
+
isolation = await createIsolation(effectiveCwd, taskId, isolationMode);
|
|
874
|
+
}
|
|
875
|
+
const result = cmuxSplitsEnabled
|
|
876
|
+
? await runSingleAgentInCmuxSplit(cmuxClient, "right", ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"))
|
|
877
|
+
: await runSingleAgent(ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"));
|
|
878
|
+
finalResults = [result];
|
|
879
|
+
// Capture and merge delta if isolated
|
|
880
|
+
if (isolation) {
|
|
881
|
+
const patches = await isolation.captureDelta();
|
|
882
|
+
if (patches.length > 0) {
|
|
883
|
+
mergeResult = await mergeDeltaPatches(effectiveCwd, patches);
|
|
702
884
|
}
|
|
703
|
-
}, makeDetails("parallel"));
|
|
704
|
-
let result = await runTask();
|
|
705
|
-
// Auto-retry failed tasks (likely API rate limit or transient error)
|
|
706
|
-
const isFailed = result.exitCode !== 0 || (result.messages.length === 0 && !signal?.aborted);
|
|
707
|
-
if (isFailed && MAX_RETRIES > 0 && !signal?.aborted) {
|
|
708
|
-
result = await runTask();
|
|
709
|
-
}
|
|
710
|
-
updateWorker(workerId, result.exitCode === 0 ? "completed" : "failed");
|
|
711
|
-
allResults[index] = result;
|
|
712
|
-
emitParallelUpdate();
|
|
713
|
-
return result;
|
|
714
|
-
});
|
|
715
|
-
const successCount = results.filter((r) => r.exitCode === 0).length;
|
|
716
|
-
const summaries = results.map((r) => {
|
|
717
|
-
const isError = r.exitCode !== 0 || r.stopReason === "error" || r.stopReason === "aborted";
|
|
718
|
-
const output = isError
|
|
719
|
-
? (r.errorMessage || r.stderr || getFinalOutput(r.messages) || "(no output)")
|
|
720
|
-
: getFinalOutput(r.messages);
|
|
721
|
-
return `[${r.agent}] ${r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`}: ${output || "(no output)"}`;
|
|
722
|
-
});
|
|
723
|
-
return {
|
|
724
|
-
content: [
|
|
725
|
-
{
|
|
726
|
-
type: "text",
|
|
727
|
-
text: `Parallel: ${successCount}/${results.length} succeeded\n\n${summaries.join("\n\n")}`,
|
|
728
|
-
},
|
|
729
|
-
],
|
|
730
|
-
details: makeDetails("parallel")(results),
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
if (params.agent && params.task) {
|
|
734
|
-
let isolation = null;
|
|
735
|
-
let mergeResult;
|
|
736
|
-
try {
|
|
737
|
-
const effectiveCwd = params.cwd ?? ctx.cwd;
|
|
738
|
-
if (useIsolation) {
|
|
739
|
-
const taskId = crypto.randomUUID();
|
|
740
|
-
isolation = await createIsolation(effectiveCwd, taskId, isolationMode);
|
|
741
|
-
}
|
|
742
|
-
const result = cmuxSplitsEnabled
|
|
743
|
-
? await runSingleAgentInCmuxSplit(cmuxClient, "right", ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"))
|
|
744
|
-
: await runSingleAgent(ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"));
|
|
745
|
-
// Capture and merge delta if isolated
|
|
746
|
-
if (isolation) {
|
|
747
|
-
const patches = await isolation.captureDelta();
|
|
748
|
-
if (patches.length > 0) {
|
|
749
|
-
mergeResult = await mergeDeltaPatches(effectiveCwd, patches);
|
|
750
885
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
886
|
+
const isError = result.exitCode !== 0 || result.stopReason === "error" || result.stopReason === "aborted";
|
|
887
|
+
if (isError) {
|
|
888
|
+
const errorMsg = result.errorMessage || result.stderr || getFinalOutput(result.messages) || "(no output)";
|
|
889
|
+
finishDispatch([result]);
|
|
890
|
+
return {
|
|
891
|
+
content: [{ type: "text", text: `Agent ${result.stopReason || "failed"}: ${errorMsg}` }],
|
|
892
|
+
details: makeDetails("single")([result]),
|
|
893
|
+
isError: true,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
let outputText = getFinalOutput(result.messages) || "(no output)";
|
|
897
|
+
if (mergeResult && !mergeResult.success) {
|
|
898
|
+
outputText += `\n\n⚠ Patch merge failed: ${mergeResult.error || "unknown error"}`;
|
|
899
|
+
}
|
|
900
|
+
finishDispatch([result]);
|
|
755
901
|
return {
|
|
756
|
-
content: [{ type: "text", text:
|
|
902
|
+
content: [{ type: "text", text: outputText }],
|
|
757
903
|
details: makeDetails("single")([result]),
|
|
758
|
-
isError: true,
|
|
759
904
|
};
|
|
760
905
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
return {
|
|
766
|
-
content: [{ type: "text", text: outputText }],
|
|
767
|
-
details: makeDetails("single")([result]),
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
finally {
|
|
771
|
-
if (isolation) {
|
|
772
|
-
await isolation.cleanup();
|
|
906
|
+
finally {
|
|
907
|
+
if (isolation) {
|
|
908
|
+
await isolation.cleanup();
|
|
909
|
+
}
|
|
773
910
|
}
|
|
774
911
|
}
|
|
912
|
+
finishDispatch([]);
|
|
913
|
+
const available = agents.map((a) => `${a.name} (${a.source})`).join(", ") || "none";
|
|
914
|
+
return {
|
|
915
|
+
content: [{ type: "text", text: `Invalid parameters. Available agents: ${available}` }],
|
|
916
|
+
details: makeDetails("single")([]),
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
catch (err) {
|
|
920
|
+
if (!dispatchCompletedEmitted)
|
|
921
|
+
finalResults = synthesizeFailureResults(err);
|
|
922
|
+
throw err;
|
|
923
|
+
}
|
|
924
|
+
finally {
|
|
925
|
+
finishDispatch(finalResults);
|
|
775
926
|
}
|
|
776
|
-
const available = agents.map((a) => `${a.name} (${a.source})`).join(", ") || "none";
|
|
777
|
-
return {
|
|
778
|
-
content: [{ type: "text", text: `Invalid parameters. Available agents: ${available}` }],
|
|
779
|
-
details: makeDetails("single")([]),
|
|
780
|
-
};
|
|
781
927
|
},
|
|
782
928
|
renderCall(args, theme) {
|
|
783
929
|
const scope = args.agentScope ?? "both";
|