pi-subagents 0.40.0 → 0.41.0
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 +76 -0
- package/README.md +246 -525
- package/agents/oracle.md +1 -0
- package/package.json +12 -4
- package/prompts/parallel-context-build.md +1 -1
- package/prompts/parallel-handoff-plan.md +1 -1
- package/prompts/review-loop.md +1 -1
- package/skills/pi-subagents/SKILL.md +6 -6
- package/skills/pi-subagents/references/constraints-and-recipes.md +19 -26
- package/skills/pi-subagents/references/execution-controls.md +98 -97
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +18 -27
- package/src/agents/agent-management.ts +155 -65
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +154 -71
- package/src/agents/chain-serializer.ts +10 -7
- package/src/agents/frontmatter.ts +5 -3
- package/src/agents/identity.ts +1 -1
- package/src/agents/proactive-skills.ts +13 -10
- package/src/agents/skills.ts +23 -6
- package/src/api/control-channel.ts +4 -0
- package/src/api/delegation.ts +26 -194
- package/src/api/external-runs.ts +129 -0
- package/src/api/intercom-bridge.ts +3 -0
- package/src/api/pi-args.ts +5 -0
- package/src/api/preflight.ts +3 -3
- package/src/api/shared-types.ts +19 -0
- package/src/extension/config.ts +10 -0
- package/src/extension/control-notices.ts +5 -39
- package/src/extension/doctor.ts +10 -9
- package/src/extension/fanout-child.ts +7 -4
- package/src/extension/index.ts +232 -68
- package/src/extension/rpc.ts +18 -12
- package/src/extension/schemas.ts +48 -37
- package/src/extension/tool-description.ts +36 -86
- package/src/inspectors/herdr/actions.ts +229 -0
- package/src/inspectors/herdr/client.ts +130 -0
- package/src/inspectors/herdr/inspector-runner.ts +141 -0
- package/src/inspectors/herdr/project-panes.ts +154 -0
- package/src/integrations/herdr-status.ts +330 -0
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/result-intercom.ts +5 -1
- package/src/missions/actions.ts +372 -0
- package/src/missions/lifecycle.ts +314 -0
- package/src/missions/store.ts +442 -0
- package/src/missions/types.ts +135 -0
- package/src/policy/authority.ts +46 -0
- package/src/profiles/profiles.ts +29 -3
- package/src/runs/background/async-execution.ts +98 -49
- package/src/runs/background/async-job-tracker.ts +10 -2
- package/src/runs/background/async-resume.ts +6 -6
- package/src/runs/background/async-status.ts +29 -1
- package/src/runs/background/auto-drain.ts +3 -3
- package/src/runs/background/chain-append.ts +3 -2
- package/src/runs/background/control-channel.ts +9 -7
- package/src/runs/background/fleet-view.ts +3 -4
- package/src/runs/background/notify.ts +2 -1
- package/src/runs/background/process-terminal.ts +5 -5
- package/src/runs/background/result-watcher.ts +13 -5
- package/src/runs/background/run-id-resolver.ts +3 -3
- package/src/runs/background/run-status.ts +35 -8
- package/src/runs/background/scheduled-runs.ts +602 -375
- package/src/runs/background/stale-run-reconciler.ts +3 -3
- package/src/runs/background/subagent-runner.ts +608 -445
- package/src/runs/background/subagent-wait.ts +50 -9
- package/src/runs/background/wait-subscriptions.ts +253 -0
- package/src/runs/background/wait-tool.ts +12 -4
- package/src/runs/foreground/async-steering-action.ts +3 -3
- package/src/runs/foreground/chain-clarify.ts +8 -4
- package/src/runs/foreground/chain-execution.ts +56 -30
- package/src/runs/foreground/execution.ts +15 -2
- package/src/runs/foreground/subagent-executor.ts +1023 -273
- package/src/runs/shared/acceptance.ts +28 -6
- package/src/runs/shared/child-protocol.ts +302 -22
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/external-cli-runner.ts +130 -0
- package/src/runs/shared/long-running-guard.ts +42 -1
- package/src/runs/shared/nested-events.ts +59 -5
- package/src/runs/shared/nested-render.ts +9 -4
- package/src/runs/shared/parallel-handoff.ts +86 -2
- package/src/runs/shared/parallel-utils.ts +11 -2
- package/src/runs/shared/permissions.ts +95 -0
- package/src/runs/shared/pi-args.ts +11 -1
- package/src/runs/shared/pi-spawn.ts +11 -1
- package/src/runs/shared/run-history.ts +1 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
- package/src/runs/shared/subagent-startup-retry.ts +5 -2
- package/src/runs/shared/turn-budget.ts +6 -6
- package/src/runs/shared/worktree.ts +122 -12
- package/src/shared/accessible-dir.ts +29 -7
- package/src/shared/artifacts.ts +18 -1
- package/src/shared/fork-context.ts +3 -2
- package/src/shared/launch-contract.ts +1 -0
- package/src/shared/settings.ts +10 -0
- package/src/shared/types.ts +156 -14
- package/src/shared/utils.ts +8 -6
- package/src/slash/delegation-adapters.ts +32 -194
- package/src/slash/delegation-request.ts +43 -126
- package/src/slash/prompt-template-bridge.ts +158 -205
- package/src/slash/prompt-workflows.ts +21 -57
- package/src/slash/slash-bridge.ts +14 -0
- package/src/slash/slash-commands.ts +31 -632
- package/src/slash/subagents-admin.ts +18 -14
- package/src/tui/fleet-status.ts +156 -21
- package/src/tui/fleet-transcript.ts +110 -5
- package/src/tui/fleet.ts +56 -24
- package/src/tui/render.ts +291 -109
- package/src/types/pi-runtime-compat.d.ts +14 -0
- package/src/watchdog/lsp-diagnostics.ts +12 -7
- package/src/watchdog/model-selection.ts +2 -2
- package/src/watchdog/permission-arbiter.ts +145 -0
- package/src/watchdog/register-child.ts +1 -1
- package/src/watchdog/register-main.ts +1 -1
- package/src/watchdog/review.ts +4 -1
- package/src/watchdog/runtime.ts +3 -2
- package/src/workflows/chat-progress.ts +140 -0
- package/src/workflows/scripted-workflow.ts +415 -0
- package/agents/advisor.md +0 -73
- package/src/extension/chain-validation.ts +0 -181
package/src/tui/render.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
|
|
|
24
24
|
import { formatNestedAggregate } from "../runs/shared/nested-render.ts";
|
|
25
25
|
import { aggregateStepStatus, formatActivityLabel, formatAgentRunningLabel, formatParallelOutcome } from "../shared/status-format.ts";
|
|
26
26
|
import { contextModeBadge, contextModePrefix } from "../runs/shared/context-mode.ts";
|
|
27
|
+
import { buildWorkflowChatProgressRows, type WorkflowChatProgressRow } from "../workflows/chat-progress.ts";
|
|
27
28
|
|
|
28
29
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
29
30
|
|
|
@@ -277,17 +278,81 @@ function resultStatusLine(result: Details["results"][number], output: string): s
|
|
|
277
278
|
return "Done";
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
type ResultPresentation = {
|
|
282
|
+
glyph: string;
|
|
283
|
+
label: "running" | "detached" | "stopped" | "paused" | "failed" | "completed";
|
|
284
|
+
tone: "accent" | "warning" | "error" | "success";
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
function semanticResultPresentation(input: {
|
|
288
|
+
running?: boolean;
|
|
289
|
+
detached?: boolean;
|
|
290
|
+
stopped?: boolean;
|
|
291
|
+
interrupted?: boolean;
|
|
292
|
+
failed?: boolean;
|
|
293
|
+
completedWithoutOutput?: boolean;
|
|
294
|
+
seed?: number;
|
|
295
|
+
frame?: number;
|
|
296
|
+
}): ResultPresentation {
|
|
297
|
+
if (input.running) {
|
|
298
|
+
const glyph = input.frame !== undefined ? runningGlyph((input.seed ?? 0) + input.frame) : runningGlyph(input.seed);
|
|
299
|
+
return { glyph, label: "running", tone: "accent" };
|
|
284
300
|
}
|
|
285
|
-
if (
|
|
286
|
-
if (
|
|
287
|
-
if (
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
301
|
+
if (input.detached) return { glyph: "■", label: "detached", tone: "warning" };
|
|
302
|
+
if (input.stopped) return { glyph: "■", label: "stopped", tone: "warning" };
|
|
303
|
+
if (input.interrupted) return { glyph: "■", label: "paused", tone: "warning" };
|
|
304
|
+
if (input.failed) return { glyph: "✗", label: "failed", tone: "error" };
|
|
305
|
+
return { glyph: "✓", label: "completed", tone: input.completedWithoutOutput ? "warning" : "success" };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function hasTerminalResultFlag(result: Details["results"][number]): boolean {
|
|
309
|
+
return Boolean(result.detached || result.stopped || result.interrupted);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function hasTerminalResult(result: Details["results"][number]): boolean {
|
|
313
|
+
if (hasTerminalResultFlag(result)) return true;
|
|
314
|
+
const status = result.progress?.status;
|
|
315
|
+
if (status === "running" || status === "pending") return false;
|
|
316
|
+
return result.exitCode !== undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function isResultRunning(result: Details["results"][number], status = result.progress?.status): boolean {
|
|
320
|
+
return status === "running" && !hasTerminalResultFlag(result);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function detailsHaveRunningResult(details: Details): boolean {
|
|
324
|
+
return details.progress?.some((progress) => {
|
|
325
|
+
if (progress.status !== "running") return false;
|
|
326
|
+
const result = details.results.find((entry) => entry.progress?.index === progress.index) ?? details.results[progress.index];
|
|
327
|
+
return !result || !hasTerminalResultFlag(result);
|
|
328
|
+
})
|
|
329
|
+
|| details.results.some((result) => isResultRunning(result))
|
|
330
|
+
|| workflowGraphHasStatus(details, ["running"]);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function resultPresentation(result: Details["results"][number], output: string, running = isResultRunning(result), seed = progressRunningSeed(result.progress ?? result.progressSummary), frame?: number): ResultPresentation {
|
|
334
|
+
return semanticResultPresentation({
|
|
335
|
+
running,
|
|
336
|
+
detached: result.detached,
|
|
337
|
+
stopped: result.stopped,
|
|
338
|
+
interrupted: result.interrupted,
|
|
339
|
+
failed: result.exitCode !== 0,
|
|
340
|
+
completedWithoutOutput: hasEmptyTextOutputWithoutOutputTarget(result.task, output),
|
|
341
|
+
seed,
|
|
342
|
+
frame,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function resultGlyph(result: Details["results"][number], output: string, theme: Theme, running = isResultRunning(result), seed = progressRunningSeed(result.progress ?? result.progressSummary), frame?: number): string {
|
|
347
|
+
const presentation = resultPresentation(result, output, running, seed, frame);
|
|
348
|
+
return theme.fg(presentation.tone, presentation.glyph);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function styledResultPresentation(presentation: ResultPresentation, theme: Theme): { glyph: string; label: string } {
|
|
352
|
+
return {
|
|
353
|
+
glyph: theme.fg(presentation.tone, presentation.glyph),
|
|
354
|
+
label: theme.fg(presentation.tone, presentation.label),
|
|
355
|
+
};
|
|
291
356
|
}
|
|
292
357
|
|
|
293
358
|
function compactCurrentActivity(progress: AgentProgress): string {
|
|
@@ -471,7 +536,7 @@ function widgetParallelAgentDetails(job: AsyncJobState, theme: Theme, expanded =
|
|
|
471
536
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
472
537
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
473
538
|
lines.push(` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`);
|
|
474
|
-
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 :
|
|
539
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 6)) lines.push(` ${nestedLine}`);
|
|
475
540
|
}
|
|
476
541
|
return lines;
|
|
477
542
|
}
|
|
@@ -639,14 +704,12 @@ function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "pr
|
|
|
639
704
|
|| details.progress?.find((progress) => progress.agent === result.agent && progress.status === "running");
|
|
640
705
|
const index = result.progress?.index ?? progressFromArray?.index ?? i;
|
|
641
706
|
if (index < 0 || index >= totalCount) continue;
|
|
642
|
-
const status = result.
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
? "completed"
|
|
649
|
-
: "failed");
|
|
707
|
+
const status = result.stopped
|
|
708
|
+
? "stopped"
|
|
709
|
+
: result.interrupted || result.detached
|
|
710
|
+
? "detached"
|
|
711
|
+
: result.progress?.status
|
|
712
|
+
?? (result.exitCode === 0 ? "completed" : "failed");
|
|
650
713
|
statuses[index] = status;
|
|
651
714
|
}
|
|
652
715
|
const running = statuses.filter((status) => status === "running").length;
|
|
@@ -667,8 +730,8 @@ function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "pr
|
|
|
667
730
|
let done = 0;
|
|
668
731
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
669
732
|
const progressEntry = details.progress?.find((progress) => progress.index === index);
|
|
670
|
-
const resultEntry = details.results.find((result) => result.progress?.index === index);
|
|
671
|
-
if (progressEntry?.status === "running") {
|
|
733
|
+
const resultEntry = details.results.find((result) => result.progress?.index === index) ?? details.results[index];
|
|
734
|
+
if (progressEntry?.status === "running" && (!resultEntry || !hasTerminalResultFlag(resultEntry))) {
|
|
672
735
|
running++;
|
|
673
736
|
continue;
|
|
674
737
|
}
|
|
@@ -847,9 +910,56 @@ function nestedActivity(input: Pick<NestedRunSummary | NestedStepSummary, "activ
|
|
|
847
910
|
function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme: Theme, width: number, expanded: boolean, snapshotNow?: number, lineBudget = expanded ? 12 : 1): string[] {
|
|
848
911
|
if (!children?.length || lineBudget <= 0) return [];
|
|
849
912
|
if (!expanded) {
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
|
|
913
|
+
type CollapsedRow = { text: string; prefix: string };
|
|
914
|
+
const rows: CollapsedRow[] = [];
|
|
915
|
+
const maxLeaves = 4;
|
|
916
|
+
const maxLines = Math.min(6, lineBudget);
|
|
917
|
+
let leaves = 0;
|
|
918
|
+
let overflow = 0;
|
|
919
|
+
const appendLeaf = (step: NestedStepSummary | NestedRunSummary, prefix: string, fallback?: number): void => {
|
|
920
|
+
if (leaves >= maxLeaves) {
|
|
921
|
+
overflow++;
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
leaves++;
|
|
925
|
+
const state = "status" in step ? step.status : step.state;
|
|
926
|
+
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
927
|
+
const activity = nestedActivity(step, state, snapshotNow ?? fallback);
|
|
928
|
+
const timestamp = "status" in step ? nestedStepTimestamp(step, fallback) : formatClockTime(nestedRunEventTime(step));
|
|
929
|
+
const error = step.error ? ` · ${step.error}` : "";
|
|
930
|
+
const name = "status" in step ? step.agent : nestedRunName(step);
|
|
931
|
+
rows.push({
|
|
932
|
+
prefix,
|
|
933
|
+
text: `${nestedTimestampPrefix(timestamp)}${nestedStatusGlyph(state, theme)} ${name} · ${state}${modelThinking ? ` · ${modelThinking}` : ""}${activity ? ` · ${activity}` : ""}${error}`,
|
|
934
|
+
});
|
|
935
|
+
};
|
|
936
|
+
for (const child of children) {
|
|
937
|
+
const steps = (child.mode === "parallel" || child.mode === "chain") ? child.steps ?? [] : [];
|
|
938
|
+
if (steps.length > 0) {
|
|
939
|
+
const ownerModelThinking = formatModelThinking(child.model, child.thinking);
|
|
940
|
+
const ownerActivity = nestedActivity(child, child.state, snapshotNow ?? child.lastUpdate);
|
|
941
|
+
const ownerError = child.error ? ` · ${child.error}` : "";
|
|
942
|
+
rows.push({
|
|
943
|
+
prefix: "↳ ",
|
|
944
|
+
text: `OWNER ${nestedStatusGlyph(child.state, theme, nestedRunSeed(child))} ${nestedRunName(child)} · ${child.state}${ownerModelThinking ? ` · ${ownerModelThinking}` : ""}${ownerActivity ? ` · ${ownerActivity}` : ""}${ownerError}`,
|
|
945
|
+
});
|
|
946
|
+
for (const step of steps) appendLeaf(step, "↳ │ ", child.lastUpdate);
|
|
947
|
+
} else {
|
|
948
|
+
appendLeaf(child, "↳ ", child.lastUpdate);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (overflow > 0) rows.push({ prefix: "↳ ", text: `… +${overflow} more nested leaves` });
|
|
952
|
+
const visibleRows = rows.length <= maxLines
|
|
953
|
+
? rows
|
|
954
|
+
: overflow > 0
|
|
955
|
+
? [...rows.slice(0, Math.max(0, maxLines - 1)), rows.at(-1)!]
|
|
956
|
+
: rows.slice(0, maxLines);
|
|
957
|
+
return visibleRows.map((row, index) => {
|
|
958
|
+
const marker = index === visibleRows.length - 1 ? "└─" : "├─";
|
|
959
|
+
const prefix = row.prefix;
|
|
960
|
+
const text = row.text.startsWith("OWNER ") ? row.text.slice("OWNER ".length) : row.text;
|
|
961
|
+
return truncLine(theme.fg("dim", `${prefix}${marker} ${text}`), width);
|
|
962
|
+
});
|
|
853
963
|
}
|
|
854
964
|
const lines: string[] = [];
|
|
855
965
|
const maxDepth = 2;
|
|
@@ -869,7 +979,8 @@ function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme
|
|
|
869
979
|
}
|
|
870
980
|
const activity = nestedActivity(child, child.state, snapshotNow ?? child.lastUpdate);
|
|
871
981
|
const error = child.error ? ` · ${child.error}` : "";
|
|
872
|
-
|
|
982
|
+
const modelThinking = formatModelThinking(child.model, child.thinking);
|
|
983
|
+
lines.push(theme.fg("dim", `${prefix}↳ ${nestedTimestampPrefix(formatClockTime(nestedRunEventTime(child)))}${nestedStatusGlyph(child.state, theme, nestedRunSeed(child))} ${nestedRunName(child)} · ${child.state}${modelThinking ? ` · ${modelThinking}` : ""} · ${activity}${error}`));
|
|
873
984
|
if (depth === maxDepth) {
|
|
874
985
|
const aggregate = formatNestedAggregate([...(child.steps?.flatMap((step) => step.children ?? []) ?? []), ...(child.children ?? [])]);
|
|
875
986
|
if (aggregate && lines.length < lineBudget) lines.push(theme.fg("dim", `${prefix} ↳ ${aggregate}`));
|
|
@@ -877,7 +988,8 @@ function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme
|
|
|
877
988
|
}
|
|
878
989
|
for (const step of child.steps ?? []) {
|
|
879
990
|
if (lines.length >= lineBudget) return;
|
|
880
|
-
|
|
991
|
+
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
992
|
+
lines.push(theme.fg("dim", `${prefix} ↳ ${nestedTimestampPrefix(nestedStepTimestamp(step, child.lastUpdate))}${nestedStatusGlyph(step.status, theme)} ${step.agent} · ${step.status}${modelThinking ? ` · ${modelThinking}` : ""} · ${nestedActivity(step, step.status, snapshotNow ?? child.lastUpdate)}`));
|
|
881
993
|
append(step.children, depth + 1, `${prefix} `);
|
|
882
994
|
}
|
|
883
995
|
append(child.children, depth + 1, `${prefix} `);
|
|
@@ -903,7 +1015,7 @@ function foregroundStyleWidgetStepLines(
|
|
|
903
1015
|
const lines = [` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1))} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
|
|
904
1016
|
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
905
1017
|
if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
|
|
906
|
-
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt)) {
|
|
1018
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
907
1019
|
lines.push(` ${nestedLine}`);
|
|
908
1020
|
}
|
|
909
1021
|
if (step.status === "running") {
|
|
@@ -929,7 +1041,7 @@ function foregroundStyleWidgetStepLines(
|
|
|
929
1041
|
function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded: boolean, width: number): string[] {
|
|
930
1042
|
if (!job.steps?.length) return [
|
|
931
1043
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
932
|
-
...formatNestedWidgetLines(job.nestedChildren, theme, width, expanded, job.updatedAt).map((line) => ` ${line}`),
|
|
1044
|
+
...formatNestedWidgetLines(job.nestedChildren, theme, width, expanded, job.updatedAt, expanded ? 12 : 6).map((line) => ` ${line}`),
|
|
933
1045
|
];
|
|
934
1046
|
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length) return widgetChainDetails(job, theme, expanded, width);
|
|
935
1047
|
const total = job.stepsTotal ?? job.steps.length;
|
|
@@ -940,7 +1052,7 @@ function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded
|
|
|
940
1052
|
}
|
|
941
1053
|
const attached = new Set(job.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
942
1054
|
const unattached = job.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
943
|
-
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt)) {
|
|
1055
|
+
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
944
1056
|
lines.push(` ${nestedLine}`);
|
|
945
1057
|
}
|
|
946
1058
|
return lines;
|
|
@@ -972,7 +1084,7 @@ function compactSingleWidgetLines(job: AsyncJobState, theme: Theme, width: numbe
|
|
|
972
1084
|
const activitySuffix = activity ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}` : "";
|
|
973
1085
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
974
1086
|
lines.push(` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
|
|
975
|
-
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt)) lines.push(` ${nestedLine}`);
|
|
1087
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt, 6)) lines.push(` ${nestedLine}`);
|
|
976
1088
|
}
|
|
977
1089
|
if (job.steps.some((step) => step.status === "running")) lines.push(theme.fg("accent", ` ${liveDetailHintText()}`));
|
|
978
1090
|
return lines.map((line) => truncLine(line, width));
|
|
@@ -1310,7 +1422,7 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[]): void
|
|
|
1310
1422
|
function renderSingleCompact(d: Details, r: Details["results"][number], theme: Theme, frame?: number): Component {
|
|
1311
1423
|
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
1312
1424
|
const progress = r.progress || r.progressSummary;
|
|
1313
|
-
const isRunning = r
|
|
1425
|
+
const isRunning = isResultRunning(r);
|
|
1314
1426
|
const contextBadge = contextModeBadge(theme, r.context ?? d.context);
|
|
1315
1427
|
const stats = statJoin(theme, [
|
|
1316
1428
|
r.usage?.turns ? `⟳ ${r.usage.turns}` : "",
|
|
@@ -1349,16 +1461,64 @@ function renderSingleCompact(d: Details, r: Details["results"][number], theme: T
|
|
|
1349
1461
|
return c;
|
|
1350
1462
|
}
|
|
1351
1463
|
|
|
1464
|
+
function workflowRowGlyph(row: WorkflowChatProgressRow, theme: Theme, frame?: number): string {
|
|
1465
|
+
if (row.state === "running") return theme.fg("accent", runningGlyph(frame));
|
|
1466
|
+
if (row.state === "complete") return theme.fg("success", "✓");
|
|
1467
|
+
return theme.fg("error", "✗");
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function workflowRowStateLabel(row: WorkflowChatProgressRow, theme: Theme): string {
|
|
1471
|
+
const label = (row.state === "complete" ? "complete" : row.state).padEnd(8);
|
|
1472
|
+
if (row.state === "running") return theme.fg("accent", label);
|
|
1473
|
+
if (row.state === "complete") return theme.fg("success", label);
|
|
1474
|
+
return theme.fg("error", label);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
function workflowOverallState(rows: WorkflowChatProgressRow[], hasTerminalValue: boolean, isError?: boolean): "running" | "complete" | "failed" {
|
|
1478
|
+
if (isError || rows.some((row) => row.state === "failed")) return "failed";
|
|
1479
|
+
if ((rows.length > 0 && rows.every((row) => row.state === "complete")) || hasTerminalValue) return "complete";
|
|
1480
|
+
return "running";
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>, theme: Theme, frame?: number): Component {
|
|
1484
|
+
const workflow = d.workflow;
|
|
1485
|
+
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace) : [];
|
|
1486
|
+
const state = workflowOverallState(rows, workflow?.value !== undefined, result.isError);
|
|
1487
|
+
const glyph = state === "running" ? theme.fg("accent", runningGlyph(frame)) : state === "complete" ? theme.fg("success", "✓") : theme.fg("error", "✗");
|
|
1488
|
+
const width = getTermWidth() - 4;
|
|
1489
|
+
const runId = d.runId ? d.runId.slice(0, 12) : "workflow";
|
|
1490
|
+
const repoLabel = d.chatProgress?.repoLabel ?? (d.chatProgress?.repoRelation === "same" ? "same repo" : "other repo");
|
|
1491
|
+
const phase = rows.find((row) => row.state === "running" && row.phase)?.phase ?? [...rows].reverse().find((row) => row.phase)?.phase;
|
|
1492
|
+
const c = new Container();
|
|
1493
|
+
c.addChild(new Text(truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold("workflow"))} ${runId} ${theme.fg("dim", "·")} ${d.chatProgress?.repoRelation === "same" ? "same repo" : "other repo"} ${theme.fg("dim", "·")} ${state}`, width), 0, 0));
|
|
1494
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` Repo ${repoLabel}`), width), 0, 0));
|
|
1495
|
+
if (phase) c.addChild(new Text(truncLine(theme.fg("dim", ` Phase ${phase}`), width), 0, 0));
|
|
1496
|
+
if (rows.length === 0) {
|
|
1497
|
+
c.addChild(new Text(truncLine(theme.fg("dim", " ◦ waiting for workflow child launches"), width), 0, 0));
|
|
1498
|
+
return c;
|
|
1499
|
+
}
|
|
1500
|
+
for (const row of rows) {
|
|
1501
|
+
const status = workflowRowStateLabel(row, theme);
|
|
1502
|
+
const label = row.label && row.label !== row.key ? ` ${row.label}` : "";
|
|
1503
|
+
const duration = row.durationMs !== undefined ? ` ${theme.fg("dim", `· ${formatDuration(row.durationMs)}`)}` : "";
|
|
1504
|
+
const run = row.runId ? ` ${theme.fg("dim", `[${row.runId.slice(0, 8)}]`)}` : "";
|
|
1505
|
+
const error = row.error ? ` ${theme.fg("error", `· ${row.error}`)}` : "";
|
|
1506
|
+
c.addChild(new Text(truncLine(` ${workflowRowGlyph(row, theme, frame)} ${status} ${theme.bold(row.key)}${label}${run}${duration}${error}`, width), 0, 0));
|
|
1507
|
+
}
|
|
1508
|
+
if (workflow?.emits.length) c.addChild(new Text(truncLine(theme.fg("dim", ` Emits ${workflow.emits.length}`), width), 0, 0));
|
|
1509
|
+
return c;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1352
1512
|
function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component {
|
|
1353
|
-
const hasRunning = d
|
|
1354
|
-
|
|
1355
|
-
|| workflowGraphHasStatus(d, ["
|
|
1356
|
-
const stopped = d.results.some((r) => r.stopped
|
|
1513
|
+
const hasRunning = detailsHaveRunningResult(d);
|
|
1514
|
+
const detached = d.results.some((r) => r.detached)
|
|
1515
|
+
|| workflowGraphHasStatus(d, ["detached"]);
|
|
1516
|
+
const stopped = d.results.some((r) => r.stopped)
|
|
1357
1517
|
|| workflowGraphHasStatus(d, ["stopped"]);
|
|
1358
|
-
const failed = d.results.some((r) => !r
|
|
1518
|
+
const failed = d.results.some((r) => !hasTerminalResultFlag(r) && r.exitCode !== 0 && !isResultRunning(r))
|
|
1359
1519
|
|| workflowGraphHasStatus(d, ["failed"]);
|
|
1360
|
-
const paused = d.results.some((r) =>
|
|
1361
|
-
|| workflowGraphHasStatus(d, ["paused"
|
|
1520
|
+
const paused = d.results.some((r) => r.interrupted)
|
|
1521
|
+
|| workflowGraphHasStatus(d, ["paused"]);
|
|
1362
1522
|
let totalSummary = d.progressSummary;
|
|
1363
1523
|
if (!totalSummary) {
|
|
1364
1524
|
let sawProgress = false;
|
|
@@ -1376,15 +1536,16 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1376
1536
|
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
1377
1537
|
const itemTitle = multiLabel.itemTitle;
|
|
1378
1538
|
const stats = statJoin(theme, [multiLabel.headerLabel, formatProgressStats(theme, totalSummary), formatTotalCostStat(d.totalCost)]);
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1539
|
+
const aggregatePresentation = semanticResultPresentation({
|
|
1540
|
+
running: hasRunning,
|
|
1541
|
+
detached,
|
|
1542
|
+
stopped,
|
|
1543
|
+
interrupted: paused,
|
|
1544
|
+
failed,
|
|
1545
|
+
seed: runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex),
|
|
1546
|
+
frame,
|
|
1547
|
+
});
|
|
1548
|
+
const glyph = theme.fg(aggregatePresentation.tone, aggregatePresentation.glyph);
|
|
1388
1549
|
const contextBadge = contextModeBadge(theme, d.context);
|
|
1389
1550
|
const c = new Container();
|
|
1390
1551
|
const width = getTermWidth() - 4;
|
|
@@ -1421,7 +1582,7 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1421
1582
|
const output = getSingleResultOutput(r);
|
|
1422
1583
|
const progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1423
1584
|
const rProg = r.progress || progressFromArray || r.progressSummary;
|
|
1424
|
-
const rRunning = rProg && "status" in rProg && rProg.status
|
|
1585
|
+
const rRunning = rProg && "status" in rProg && isResultRunning(r, rProg.status);
|
|
1425
1586
|
const rPending = rProg && "status" in rProg && rProg.status === "pending";
|
|
1426
1587
|
const stepNumber = r.progress?.index !== undefined ? r.progress.index + 1 : progressFromArray?.index !== undefined ? progressFromArray.index + 1 : i + 1;
|
|
1427
1588
|
const stepStats = formatProgressStats(theme, rProg);
|
|
@@ -1433,9 +1594,10 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1433
1594
|
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1434
1595
|
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1435
1596
|
if (rRunning && rProg && "status" in rProg) {
|
|
1436
|
-
const
|
|
1597
|
+
const liveProgress = rProg as AgentProgress;
|
|
1598
|
+
const activity = compactCurrentActivity(liveProgress);
|
|
1437
1599
|
c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0));
|
|
1438
|
-
for (const nestedLine of formatNestedWidgetLines(r.children, theme, width, false, snapshotNowForProgress(
|
|
1600
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, width, false, snapshotNowForProgress(liveProgress))) {
|
|
1439
1601
|
c.addChild(new Text(truncLine(` ${nestedLine}`, width), 0, 0));
|
|
1440
1602
|
}
|
|
1441
1603
|
c.addChild(new Text(truncLine(theme.fg("accent", ` ${liveDetailHintText()}`), width), 0, 0));
|
|
@@ -1455,6 +1617,45 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1455
1617
|
return c;
|
|
1456
1618
|
}
|
|
1457
1619
|
|
|
1620
|
+
export function renderSubagentSummary(
|
|
1621
|
+
result: AgentToolResult<Details>,
|
|
1622
|
+
options: { isPartial?: boolean },
|
|
1623
|
+
theme: Theme,
|
|
1624
|
+
): Component {
|
|
1625
|
+
const details = result.details;
|
|
1626
|
+
const results = details?.results ?? [];
|
|
1627
|
+
const hasSingleTerminalResult = results.length === 1 && hasTerminalResult(results[0]!);
|
|
1628
|
+
const hasOnlyTerminalResults = results.length > 0 && results.every(hasTerminalResult);
|
|
1629
|
+
const running = !hasSingleTerminalResult && !hasOnlyTerminalResults && (
|
|
1630
|
+
options.isPartial === true
|
|
1631
|
+
|| Boolean(details?.asyncId && details.mode !== "management")
|
|
1632
|
+
|| Boolean(details && detailsHaveRunningResult(details))
|
|
1633
|
+
);
|
|
1634
|
+
const stopped = results.some((entry) => entry.stopped)
|
|
1635
|
+
|| Boolean(details && workflowGraphHasStatus(details, ["stopped"]));
|
|
1636
|
+
const paused = results.some((entry) => entry.interrupted || entry.detached)
|
|
1637
|
+
|| Boolean(details && workflowGraphHasStatus(details, ["paused", "detached"]));
|
|
1638
|
+
const failed = result.isError === true
|
|
1639
|
+
|| results.some((entry) => !hasTerminalResultFlag(entry) && entry.exitCode !== 0 && !isResultRunning(entry))
|
|
1640
|
+
|| Boolean(details && workflowGraphHasStatus(details, ["failed"]));
|
|
1641
|
+
const state = running ? "running" : failed ? "failed" : stopped ? "stopped" : paused ? "paused" : "completed";
|
|
1642
|
+
const glyph = state === "running"
|
|
1643
|
+
? theme.fg("accent", STATIC_RUNNING_GLYPH)
|
|
1644
|
+
: state === "completed"
|
|
1645
|
+
? theme.fg("success", "✓")
|
|
1646
|
+
: state === "failed"
|
|
1647
|
+
? theme.fg("error", "✗")
|
|
1648
|
+
: theme.fg("warning", "■");
|
|
1649
|
+
const label = details?.mode === "single" && results.length === 1
|
|
1650
|
+
? results[0]?.agent || "subagent"
|
|
1651
|
+
: details?.mode || "subagent";
|
|
1652
|
+
return new Text(
|
|
1653
|
+
truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold(label))} ${theme.fg("dim", "·")} ${theme.fg(state === "failed" ? "error" : state === "completed" ? "success" : state === "running" ? "accent" : "warning", state)}`, getTermWidth() - 4),
|
|
1654
|
+
0,
|
|
1655
|
+
0,
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1458
1659
|
/**
|
|
1459
1660
|
* Render a subagent result
|
|
1460
1661
|
*/
|
|
@@ -1465,6 +1666,7 @@ export function renderSubagentResult(
|
|
|
1465
1666
|
frame?: number,
|
|
1466
1667
|
): Component {
|
|
1467
1668
|
const d = result.details;
|
|
1669
|
+
if (d?.mode === "workflow" && d.chatProgress?.mode === "live-card" && !result.isError && d.workflow?.value === undefined) return renderWorkflowChatProgress(d, result, theme, frame);
|
|
1468
1670
|
if (!d || !d.results.length) {
|
|
1469
1671
|
const t = result.content[0];
|
|
1470
1672
|
const text = t?.type === "text" ? t.text : "(no output)";
|
|
@@ -1490,17 +1692,12 @@ export function renderSubagentResult(
|
|
|
1490
1692
|
|
|
1491
1693
|
if (d.mode === "single" && d.results.length === 1) {
|
|
1492
1694
|
const r = d.results[0];
|
|
1695
|
+
if (!r) return renderMultiCompact(d, theme, frame);
|
|
1493
1696
|
if (!expanded) return renderSingleCompact(d, r, theme, frame);
|
|
1494
|
-
const isRunning = r
|
|
1495
|
-
const icon = isRunning
|
|
1496
|
-
? theme.fg("warning", "running")
|
|
1497
|
-
: r.detached
|
|
1498
|
-
? theme.fg("warning", "detached")
|
|
1499
|
-
: r.exitCode === 0
|
|
1500
|
-
? theme.fg("success", "ok")
|
|
1501
|
-
: theme.fg("error", "failed");
|
|
1697
|
+
const isRunning = isResultRunning(r);
|
|
1502
1698
|
const contextBadge = contextModeBadge(theme, r.context ?? d.context);
|
|
1503
1699
|
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
1700
|
+
const presentation = styledResultPresentation(resultPresentation(r, output, isRunning, undefined, frame), theme);
|
|
1504
1701
|
|
|
1505
1702
|
const progressInfo = isRunning && r.progress
|
|
1506
1703
|
? ` | ${r.progress.toolCount} tools, ${formatTokens(r.progress.tokens)} tok, ${formatDuration(r.progress.durationMs)}`
|
|
@@ -1512,7 +1709,7 @@ export function renderSubagentResult(
|
|
|
1512
1709
|
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
1513
1710
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
1514
1711
|
const c = new Container();
|
|
1515
|
-
c.addChild(new Text(fit(`${
|
|
1712
|
+
c.addChild(new Text(fit(`${presentation.glyph} ${theme.fg("toolTitle", theme.bold(r.agent))}${contextBadge}${progressInfo} ${theme.fg("dim", "·")} ${presentation.label}`), 0, 0));
|
|
1516
1713
|
c.addChild(new Spacer(1));
|
|
1517
1714
|
const taskMaxLen = Math.max(20, w - 8);
|
|
1518
1715
|
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
@@ -1593,31 +1790,30 @@ export function renderSubagentResult(
|
|
|
1593
1790
|
|
|
1594
1791
|
if (!expanded) return renderMultiCompact(d, theme, frame);
|
|
1595
1792
|
|
|
1596
|
-
const hasRunning = d
|
|
1597
|
-
|
|
1598
|
-
|| workflowGraphHasStatus(d, ["
|
|
1599
|
-
const
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1793
|
+
const hasRunning = detailsHaveRunningResult(d);
|
|
1794
|
+
const detached = d.results.some((r) => r.detached)
|
|
1795
|
+
|| workflowGraphHasStatus(d, ["detached"]);
|
|
1796
|
+
const stopped = d.results.some((r) => r.stopped)
|
|
1797
|
+
|| workflowGraphHasStatus(d, ["stopped"]);
|
|
1798
|
+
const failed = d.results.some((r) => !hasTerminalResultFlag(r) && r.exitCode !== 0 && !isResultRunning(r))
|
|
1799
|
+
|| workflowGraphHasStatus(d, ["failed"]);
|
|
1800
|
+
const paused = d.results.some((r) => r.interrupted)
|
|
1801
|
+
|| workflowGraphHasStatus(d, ["paused"]);
|
|
1802
|
+
const completedWithoutOutput = d.results.some((r) =>
|
|
1803
|
+
!hasTerminalResultFlag(r)
|
|
1804
|
+
&& r.exitCode === 0
|
|
1805
|
+
&& !isResultRunning(r)
|
|
1603
1806
|
&& hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r)),
|
|
1604
1807
|
);
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
: hasWorkflowStop
|
|
1615
|
-
? theme.fg("warning", "stopped")
|
|
1616
|
-
: hasWorkflowPause
|
|
1617
|
-
? theme.fg("warning", "paused")
|
|
1618
|
-
: ok === d.results.length
|
|
1619
|
-
? theme.fg("success", "ok")
|
|
1620
|
-
: theme.fg("error", "failed");
|
|
1808
|
+
const presentation = styledResultPresentation(semanticResultPresentation({
|
|
1809
|
+
running: hasRunning,
|
|
1810
|
+
detached,
|
|
1811
|
+
stopped,
|
|
1812
|
+
interrupted: paused,
|
|
1813
|
+
failed,
|
|
1814
|
+
completedWithoutOutput,
|
|
1815
|
+
frame,
|
|
1816
|
+
}), theme);
|
|
1621
1817
|
|
|
1622
1818
|
const totalSummary =
|
|
1623
1819
|
d.progressSummary ||
|
|
@@ -1654,22 +1850,14 @@ export function renderSubagentResult(
|
|
|
1654
1850
|
? d.chainAgents
|
|
1655
1851
|
.map((agent, i) => {
|
|
1656
1852
|
const result = d.results[i];
|
|
1657
|
-
const isFailed = result && result.exitCode !== 0 && result.progress?.status !== "running";
|
|
1658
|
-
const isComplete = result && result.exitCode === 0 && result.progress?.status !== "running";
|
|
1659
|
-
const isEmptyWithoutTarget = Boolean(result)
|
|
1660
|
-
&& Boolean(isComplete)
|
|
1661
|
-
&& hasEmptyTextOutputWithoutOutputTarget(result.task, getSingleResultOutput(result));
|
|
1662
1853
|
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
1663
|
-
const
|
|
1664
|
-
?
|
|
1665
|
-
:
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
? theme.fg("warning", "running")
|
|
1671
|
-
: theme.fg("dim", "pending");
|
|
1672
|
-
return `${stepIcon} ${agent}${contextModeBadge(theme, result?.context)}`;
|
|
1854
|
+
const stepPresentation = result
|
|
1855
|
+
? styledResultPresentation(resultPresentation(result, getSingleResultOutput(result), isCurrent && hasRunning && !hasTerminalResultFlag(result)), theme)
|
|
1856
|
+
: undefined;
|
|
1857
|
+
const stepStatus = stepPresentation
|
|
1858
|
+
? `${stepPresentation.glyph} ${stepPresentation.label}`
|
|
1859
|
+
: theme.fg("dim", "◦ pending");
|
|
1860
|
+
return `${stepStatus} ${agent}${contextModeBadge(theme, result?.context)}`;
|
|
1673
1861
|
})
|
|
1674
1862
|
.join(theme.fg("dim", " → "))
|
|
1675
1863
|
: null;
|
|
@@ -1679,7 +1867,7 @@ export function renderSubagentResult(
|
|
|
1679
1867
|
const c = new Container();
|
|
1680
1868
|
c.addChild(
|
|
1681
1869
|
new Text(
|
|
1682
|
-
fit(`${
|
|
1870
|
+
fit(`${presentation.glyph} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} · ${multiLabel.headerLabel}${summaryStr} ${theme.fg("dim", "·")} ${presentation.label}`),
|
|
1683
1871
|
0,
|
|
1684
1872
|
0,
|
|
1685
1873
|
),
|
|
@@ -1726,24 +1914,18 @@ export function renderSubagentResult(
|
|
|
1726
1914
|
const progressFromArray = d.progress?.find((p) => p.index === i)
|
|
1727
1915
|
|| d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1728
1916
|
const rProg = r.progress || progressFromArray || r.progressSummary;
|
|
1729
|
-
const rRunning = rProg?.status
|
|
1917
|
+
const rRunning = isResultRunning(r, rProg?.status);
|
|
1730
1918
|
const stepNumber = typeof rProg?.index === "number" ? rProg.index + 1 : i + 1;
|
|
1731
1919
|
|
|
1732
1920
|
const resultOutput = getSingleResultOutput(r);
|
|
1733
|
-
const
|
|
1734
|
-
? theme.fg("warning", "running")
|
|
1735
|
-
: r.exitCode !== 0
|
|
1736
|
-
? theme.fg("error", "failed")
|
|
1737
|
-
: hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput)
|
|
1738
|
-
? theme.fg("warning", "warning")
|
|
1739
|
-
: theme.fg("success", "done");
|
|
1921
|
+
const rowPresentation = styledResultPresentation(resultPresentation(r, resultOutput, rRunning, progressRunningSeed(rProg), frame), theme);
|
|
1740
1922
|
const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
|
|
1741
|
-
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1923
|
+
const modelDisplay = modelThinkingBadge(theme, r.model ?? rProg?.model, r.thinking ?? rProg?.thinking);
|
|
1742
1924
|
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
1743
1925
|
const contextBadge = contextModeBadge(theme, r.context);
|
|
1744
1926
|
const stepHeader = rRunning
|
|
1745
|
-
? `${
|
|
1746
|
-
: `${
|
|
1927
|
+
? `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${contextBadge}${modelDisplay}${stats} ${theme.fg("dim", "·")} ${rowPresentation.label}`
|
|
1928
|
+
: `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(r.agent)}${contextBadge}${modelDisplay}${stats} ${theme.fg("dim", "·")} ${rowPresentation.label}`;
|
|
1747
1929
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
1748
1930
|
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
1749
1931
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module "@earendil-works/pi-agent-core" {
|
|
2
|
+
interface AgentToolResult<T> {
|
|
3
|
+
/** Runtime error flag emitted and rendered by pi tool execution. */
|
|
4
|
+
isError?: boolean;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module "@earendil-works/pi-coding-agent" {
|
|
9
|
+
interface ExtensionUIContext {
|
|
10
|
+
requestRender?: () => void;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {};
|
|
@@ -260,6 +260,7 @@ class JsonRpcLspClient {
|
|
|
260
260
|
private readonly child: ChildProcessWithoutNullStreams;
|
|
261
261
|
private stderr = "";
|
|
262
262
|
private exited = false;
|
|
263
|
+
private terminating = false;
|
|
263
264
|
private readonly exitWaiters: Array<() => void> = [];
|
|
264
265
|
|
|
265
266
|
constructor(child: ChildProcessWithoutNullStreams) {
|
|
@@ -295,17 +296,21 @@ class JsonRpcLspClient {
|
|
|
295
296
|
|
|
296
297
|
async shutdown(): Promise<void> {
|
|
297
298
|
if (this.exited) return;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
299
|
+
if (!this.terminating) {
|
|
300
|
+
try {
|
|
301
|
+
await this.request("shutdown", null, SHUTDOWN_TIMEOUT_MS);
|
|
302
|
+
this.notify("exit", null);
|
|
303
|
+
} catch {
|
|
304
|
+
this.kill();
|
|
305
|
+
}
|
|
303
306
|
}
|
|
304
307
|
await this.waitForExit(SHUTDOWN_TIMEOUT_MS);
|
|
305
308
|
}
|
|
306
309
|
|
|
307
310
|
kill(): void {
|
|
308
|
-
if (
|
|
311
|
+
if (this.exited || this.terminating) return;
|
|
312
|
+
this.terminating = true;
|
|
313
|
+
this.child.kill("SIGTERM");
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
stderrTail(): string {
|
|
@@ -367,7 +372,7 @@ class JsonRpcLspClient {
|
|
|
367
372
|
private failProtocol(error: Error): void {
|
|
368
373
|
if (this.exited) return;
|
|
369
374
|
this.rejectPending(error);
|
|
370
|
-
this.
|
|
375
|
+
this.kill();
|
|
371
376
|
}
|
|
372
377
|
|
|
373
378
|
private waitForExit(timeoutMs: number): Promise<void> {
|