pi-subagents 0.58.0 → 0.59.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 +65 -0
- package/docs/agents.md +5 -3
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +1 -1
- package/docs/models.md +24 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +89 -4
- package/docs/workflows.md +93 -2
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +1 -1
- package/skills/pi-subagents/SKILL.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +5 -1
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +24 -3
- package/src/agents/agent-management.ts +5 -17
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +67 -26
- package/src/agents/runtime-agent-registry.ts +9 -16
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +6 -9
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +15 -2
- package/src/extension/schemas.ts +35 -10
- package/src/extension/tool-description.ts +18 -3
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +50 -35
- package/src/runs/background/async-job-tracker.ts +14 -12
- package/src/runs/background/async-resume.ts +23 -25
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +66 -7
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +158 -6
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +2 -0
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +18 -8
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +346 -357
- package/src/runs/background/subagent-wait.ts +58 -10
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +22 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +114 -120
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +478 -239
- package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +463 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/mcp-config-sources.ts +42 -6
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
- package/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +20 -2
- package/src/runs/shared/single-output.ts +27 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -4
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +5 -2
- package/src/shared/launch-contract.ts +1 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +198 -29
- package/src/shared/utils.ts +35 -55
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/tui/fleet-status.ts +125 -74
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render.ts +316 -27
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +230 -0
- package/src/workflows/scripted-workflow.ts +452 -38
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +43 -4
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
package/src/tui/render.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
type AsyncJobStep,
|
|
14
14
|
type AsyncParallelGroupStatus,
|
|
15
15
|
type Details,
|
|
16
|
+
type HostStepState,
|
|
17
|
+
type HostStepVerdict,
|
|
16
18
|
type NestedRunSummary,
|
|
17
19
|
type NestedStepSummary,
|
|
18
20
|
type WorkflowNodeStatus,
|
|
@@ -30,7 +32,10 @@ import { formatNestedAggregate } from "../runs/shared/nested-render.ts";
|
|
|
30
32
|
import { aggregateStepStatus, formatActivityLabel, formatAgentRunningLabel, formatParallelOutcome } from "../shared/status-format.ts";
|
|
31
33
|
import { contextModeBadge, contextModePrefix } from "../runs/shared/context-mode.ts";
|
|
32
34
|
import { buildWorkflowChatProgressRows, type WorkflowChatProgressRow } from "../workflows/chat-progress.ts";
|
|
35
|
+
import { formatWorkflowPreflight, formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary, formatWorkflowPreflightWarnings } from "../workflows/workflow-preflight.ts";
|
|
33
36
|
import { encodeAsyncStatusSnapshotWidget } from "../runs/background/async-status-snapshot.ts";
|
|
37
|
+
import { projectAsyncWorkflowRows, type AsyncStatusWorkflowRow } from "../runs/shared/async-status-projection.ts";
|
|
38
|
+
import { hostStepReportName, hostStepVerdictLabel } from "../runs/shared/host-step-status.ts";
|
|
34
39
|
|
|
35
40
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
36
41
|
|
|
@@ -266,6 +271,12 @@ function oneLine(text: string): string {
|
|
|
266
271
|
|
|
267
272
|
const COMPACT_TASK_MAX_CHARS = 96;
|
|
268
273
|
|
|
274
|
+
/** Display label for a child run: the derived session name (agent + task
|
|
275
|
+
* excerpt) when the launcher provided one, else the bare agent name. */
|
|
276
|
+
function childDisplayName(result: { agent?: string; sessionName?: string } | undefined, fallback = "subagent"): string {
|
|
277
|
+
return result?.sessionName?.trim() || result?.agent || fallback;
|
|
278
|
+
}
|
|
279
|
+
|
|
269
280
|
export function compactTaskText(task: string | undefined, label?: string): string | undefined {
|
|
270
281
|
const taskText = task?.trim();
|
|
271
282
|
const labelText = label?.trim();
|
|
@@ -278,6 +289,170 @@ export function compactTaskText(task: string | undefined, label?: string): strin
|
|
|
278
289
|
return previewDisplayText(normalized, COMPACT_TASK_MAX_CHARS);
|
|
279
290
|
}
|
|
280
291
|
|
|
292
|
+
export interface AsyncLaneProjection {
|
|
293
|
+
label?: string;
|
|
294
|
+
role: string;
|
|
295
|
+
phase?: string;
|
|
296
|
+
state: AsyncJobState["status"] | AsyncJobStep["status"];
|
|
297
|
+
gate?: string;
|
|
298
|
+
next?: string;
|
|
299
|
+
output?: string;
|
|
300
|
+
workspace?: string;
|
|
301
|
+
ref: string;
|
|
302
|
+
chips: string[];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const LANE_VALUE_MAX_CHARS = 48;
|
|
306
|
+
|
|
307
|
+
function boundedLaneValue(value: string | undefined, maxChars = LANE_VALUE_MAX_CHARS): string | undefined {
|
|
308
|
+
if (!value?.trim() || value.trim() === PROMPT_REDACTED) return undefined;
|
|
309
|
+
return previewDisplayText(oneLine(value), maxChars);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function laneStepForJob(job: AsyncJobState): AsyncJobStep | undefined {
|
|
313
|
+
const steps = job.steps ?? [];
|
|
314
|
+
if (steps.length === 0) return undefined;
|
|
315
|
+
if (job.currentStep !== undefined) {
|
|
316
|
+
const current = steps[job.currentStep];
|
|
317
|
+
if (current && (current.index === undefined || current.index === job.currentStep)) return current;
|
|
318
|
+
// Active parallel groups retain flat indices while exposing only the group slice.
|
|
319
|
+
const indexedCurrent = steps.find((step) => step.index === job.currentStep);
|
|
320
|
+
if (indexedCurrent) return indexedCurrent;
|
|
321
|
+
return steps[0];
|
|
322
|
+
}
|
|
323
|
+
return steps.find((step) => step.status === "running")
|
|
324
|
+
?? steps.find((step) => step.status === "pending")
|
|
325
|
+
?? steps.at(-1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function laneTraceForJob(job: AsyncJobState): NonNullable<AsyncJobState["workflow"]>["trace"][number] | undefined {
|
|
329
|
+
return Array.isArray(job.workflow?.trace) ? job.workflow.trace.at(-1) : undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function laneGate(step: AsyncJobStep | undefined): string | undefined {
|
|
333
|
+
const review = step?.review?.status ?? step?.acceptance?.reviewResult?.status;
|
|
334
|
+
if (review === "blockers") return "review blockers";
|
|
335
|
+
if (review === "review-required") return "review required";
|
|
336
|
+
if (review === "reviewed") return "reviewed";
|
|
337
|
+
const acceptance = step?.acceptance?.status;
|
|
338
|
+
if (acceptance === "review-required") return "acceptance review";
|
|
339
|
+
if (acceptance === "checked" || acceptance === "verified" || acceptance === "accepted") return "acceptance";
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function laneNextAction(state: AsyncLaneProjection["state"], step: AsyncJobStep | undefined, output: string | undefined, gate: string | undefined): string | undefined {
|
|
344
|
+
if (step?.watchdog?.phase === "stale") return "inspect stale state";
|
|
345
|
+
if (step?.toolBudgetBlocked === true || step?.turnBudgetExceeded === true) return "inspect blocked state";
|
|
346
|
+
if (gate === "review blockers") return "resolve review blockers";
|
|
347
|
+
if (gate === "review required" || gate === "acceptance review") return "review output";
|
|
348
|
+
if (step?.activityState === "needs_attention") return "inspect attention";
|
|
349
|
+
if (state === "queued" || state === "pending") return "await launch";
|
|
350
|
+
if (state === "failed" || state === "rejected") return "inspect failure";
|
|
351
|
+
if (state === "partial") return "inspect partial state";
|
|
352
|
+
if (state === "paused") return "inspect paused state";
|
|
353
|
+
if (state === "stopped") return "inspect stopped state";
|
|
354
|
+
if ((state === "complete" || state === "completed") && gate === "reviewed") return "ready";
|
|
355
|
+
if ((state === "complete" || state === "completed") && output) return "inspect output";
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function isTerminalLaneState(state: AsyncJobState["status"]): boolean {
|
|
360
|
+
return state !== "queued" && state !== "running";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Project already-loaded async status facts into one bounded, render-only lane row. */
|
|
364
|
+
export function projectAsyncLane(job: AsyncJobState, selectedStep = laneStepForJob(job)): AsyncLaneProjection | undefined {
|
|
365
|
+
const trace = laneTraceForJob(job);
|
|
366
|
+
const workspace = job.cwd ? boundedLaneValue(shortenPath(job.cwd)) : undefined;
|
|
367
|
+
const label = compactTaskText(selectedStep?.description, selectedStep?.label)
|
|
368
|
+
?? boundedLaneValue(trace?.label)
|
|
369
|
+
?? (workspace ? undefined : boundedLaneValue(selectedStep?.workflowKey ?? job.workflowKey));
|
|
370
|
+
const role = boundedLaneValue(selectedStep?.agent ?? trace?.agent ?? job.agents?.[0] ?? widgetJobName(job), 32) ?? "subagent";
|
|
371
|
+
const phase = boundedLaneValue(selectedStep?.phase ?? trace?.phase);
|
|
372
|
+
const gate = laneGate(selectedStep);
|
|
373
|
+
const output = boundedLaneValue(selectedStep?.outputName);
|
|
374
|
+
const ref = boundedLaneValue(selectedStep?.workflowKey ?? job.workflowKey ?? job.asyncId.slice(0, 8), 24) ?? job.asyncId.slice(0, 8);
|
|
375
|
+
const chips = [
|
|
376
|
+
selectedStep?.context,
|
|
377
|
+
selectedStep?.structured ? "structured" : undefined,
|
|
378
|
+
selectedStep?.activityState === "active_long_running" ? "long-running" : undefined,
|
|
379
|
+
selectedStep?.activityState === "needs_attention" ? "attention" : undefined,
|
|
380
|
+
selectedStep?.watchdog?.phase === "stale" ? "stale" : undefined,
|
|
381
|
+
selectedStep?.toolBudgetBlocked === true || selectedStep?.turnBudgetExceeded === true ? "blocked" : undefined,
|
|
382
|
+
].filter((chip): chip is string => Boolean(chip));
|
|
383
|
+
const state = isTerminalLaneState(job.status) ? job.status : selectedStep?.status ?? job.status;
|
|
384
|
+
const next = laneNextAction(state, selectedStep, output, gate);
|
|
385
|
+
if (!label && !phase && !gate && !output && !selectedStep?.workflowKey && !job.workflowKey && !trace?.label && !trace?.phase) return undefined;
|
|
386
|
+
return { ...(label ? { label } : {}), role, ...(phase ? { phase } : {}), state, ...(gate ? { gate } : {}), ...(next ? { next } : {}), ...(output ? { output } : {}), ...(workspace ? { workspace } : {}), ref, chips };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function laneStateLabel(state: AsyncLaneProjection["state"], theme: Theme): string {
|
|
390
|
+
if (state === "running") return theme.fg("accent", "running");
|
|
391
|
+
if (state === "queued" || state === "pending") return theme.fg("muted", state);
|
|
392
|
+
if (state === "complete" || state === "completed") return theme.fg("success", "complete");
|
|
393
|
+
if (state === "failed" || state === "rejected") return theme.fg("error", state === "rejected" ? "rejected" : "failed");
|
|
394
|
+
return theme.fg("warning", state);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function formatLaneProjection(lane: AsyncLaneProjection, theme: Theme): string {
|
|
398
|
+
const label = boundedLaneValue(lane.label, 56);
|
|
399
|
+
const identity = [label, lane.role ? `role:${lane.role}` : undefined].filter(Boolean).join(" · ");
|
|
400
|
+
return `${identity ? theme.bold(identity) : theme.bold(lane.role)} · ${laneStateLabel(lane.state, theme)}`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function formatLaneChip(chip: string, theme: Theme): string {
|
|
404
|
+
const text = `[${chip}]`;
|
|
405
|
+
if (chip === "blocked") return theme.fg("error", text);
|
|
406
|
+
if (chip === "stale") return theme.fg("warning", text);
|
|
407
|
+
return text;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function formatLaneProjectionDetails(lane: AsyncLaneProjection, theme: Theme): string | undefined {
|
|
411
|
+
const details = [
|
|
412
|
+
lane.phase ? `phase:${lane.phase}` : undefined,
|
|
413
|
+
lane.gate ? `gate:${lane.gate}` : undefined,
|
|
414
|
+
lane.next ? `next:${lane.next}` : undefined,
|
|
415
|
+
lane.output ? `out:${lane.output}` : undefined,
|
|
416
|
+
lane.workspace ? `workspace:${lane.workspace}` : lane.ref ? `ref:${lane.ref}` : undefined,
|
|
417
|
+
].filter(Boolean);
|
|
418
|
+
const chips = lane.chips.map((chip) => formatLaneChip(chip, theme));
|
|
419
|
+
return [...(details.length ? [theme.fg("dim", details.join(" · "))] : []), ...chips].join(" · ") || undefined;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function formatLaneProjectionLines(lane: AsyncLaneProjection, theme: Theme, indent: string): string[] {
|
|
423
|
+
const details = formatLaneProjectionDetails(lane, theme);
|
|
424
|
+
return [
|
|
425
|
+
`${indent}${formatLaneProjection(lane, theme)}`,
|
|
426
|
+
...(details ? [`${indent} ${details}`] : []),
|
|
427
|
+
];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function laneRenderKey(job: AsyncJobState): unknown {
|
|
431
|
+
const lane = projectAsyncLane(job);
|
|
432
|
+
return lane ? [lane.label, lane.role, lane.phase, lane.state, lane.gate, lane.next, lane.output, lane.workspace, lane.ref, lane.chips] : undefined;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function widgetLaneDetailLines(job: AsyncJobState, theme: Theme): string[] {
|
|
436
|
+
if (job.steps?.length && (job.mode === "parallel" || job.mode === "chain")) return [];
|
|
437
|
+
const lane = projectAsyncLane(job);
|
|
438
|
+
return lane ? formatLaneProjectionLines(lane, theme, " ") : [];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function workflowPreflightLines(job: AsyncJobState, expanded = false): string[] {
|
|
442
|
+
if (job.mode !== "workflow" || !job.preflight) return [];
|
|
443
|
+
if (!expanded) {
|
|
444
|
+
const warning = formatWorkflowPreflightWarningSummary(job.workflow?.preflightWarnings, { indent: " ", hint: "expand for debug" });
|
|
445
|
+
return [
|
|
446
|
+
formatWorkflowPreflightPlanSummary(job.preflight, { indent: " " }),
|
|
447
|
+
...(warning ? [warning] : []),
|
|
448
|
+
];
|
|
449
|
+
}
|
|
450
|
+
return [
|
|
451
|
+
...formatWorkflowPreflight(job.preflight, { indent: " " }).split("\n"),
|
|
452
|
+
...(job.workflow?.preflightWarnings ? formatWorkflowPreflightWarnings(job.workflow.preflightWarnings, { indent: " " }).split("\n") : []),
|
|
453
|
+
];
|
|
454
|
+
}
|
|
455
|
+
|
|
281
456
|
function workflowLabelForResult(details: Details, resultIndex: number): string | undefined {
|
|
282
457
|
const flatIndex = details.results[resultIndex]?.progress?.index ?? resultIndex;
|
|
283
458
|
const visit = (nodes: NonNullable<Details["workflowGraph"]>["nodes"]): string | undefined => {
|
|
@@ -474,7 +649,7 @@ function resultStatusLine(result: Details["results"][number], output: string): s
|
|
|
474
649
|
|
|
475
650
|
type ResultPresentation = {
|
|
476
651
|
glyph: string;
|
|
477
|
-
label: "running" | "detached" | "stopped" | "paused" | "failed" | "completed";
|
|
652
|
+
label: "running" | "detached" | "stopped" | "paused" | "failed" | "partial" | "completed";
|
|
478
653
|
tone: "accent" | "warning" | "error" | "success";
|
|
479
654
|
};
|
|
480
655
|
|
|
@@ -484,6 +659,7 @@ function semanticResultPresentation(input: {
|
|
|
484
659
|
stopped?: boolean;
|
|
485
660
|
interrupted?: boolean;
|
|
486
661
|
failed?: boolean;
|
|
662
|
+
partial?: boolean;
|
|
487
663
|
completedWithoutOutput?: boolean;
|
|
488
664
|
seed?: number;
|
|
489
665
|
frame?: number;
|
|
@@ -496,6 +672,7 @@ function semanticResultPresentation(input: {
|
|
|
496
672
|
if (input.stopped) return { glyph: "■", label: "stopped", tone: "warning" };
|
|
497
673
|
if (input.interrupted) return { glyph: "■", label: "paused", tone: "warning" };
|
|
498
674
|
if (input.failed) return { glyph: "✗", label: "failed", tone: "error" };
|
|
675
|
+
if (input.partial) return { glyph: "■", label: "partial", tone: "warning" };
|
|
499
676
|
return { glyph: "✓", label: "completed", tone: input.completedWithoutOutput ? "warning" : "success" };
|
|
500
677
|
}
|
|
501
678
|
|
|
@@ -574,6 +751,7 @@ function widgetStepRenderKey(step: AsyncJobStep, index: number, expanded = false
|
|
|
574
751
|
return [
|
|
575
752
|
step.index ?? index,
|
|
576
753
|
step.agent,
|
|
754
|
+
step.sessionName,
|
|
577
755
|
step.workflowKey,
|
|
578
756
|
step.phase,
|
|
579
757
|
step.label,
|
|
@@ -593,6 +771,15 @@ function widgetStepRenderKey(step: AsyncJobStep, index: number, expanded = false
|
|
|
593
771
|
step.model,
|
|
594
772
|
step.thinking,
|
|
595
773
|
step.context,
|
|
774
|
+
step.description,
|
|
775
|
+
step.outputName,
|
|
776
|
+
step.structured,
|
|
777
|
+
step.acceptance?.status,
|
|
778
|
+
step.acceptance?.reviewResult?.status,
|
|
779
|
+
step.review?.status,
|
|
780
|
+
step.toolBudgetBlocked,
|
|
781
|
+
step.turnBudgetExceeded,
|
|
782
|
+
step.watchdog?.phase,
|
|
596
783
|
step.error,
|
|
597
784
|
expanded ? expandedStepActivityRenderKey(step) : undefined,
|
|
598
785
|
nestedRenderKey(step.children, expanded),
|
|
@@ -604,6 +791,7 @@ function nestedRenderKey(children: NestedRunSummary[] | undefined, expanded = fa
|
|
|
604
791
|
child.id,
|
|
605
792
|
child.state,
|
|
606
793
|
child.agent,
|
|
794
|
+
child.sessionName,
|
|
607
795
|
child.model,
|
|
608
796
|
child.thinking,
|
|
609
797
|
child.activityState,
|
|
@@ -623,6 +811,22 @@ function nestedRenderKey(children: NestedRunSummary[] | undefined, expanded = fa
|
|
|
623
811
|
]);
|
|
624
812
|
}
|
|
625
813
|
|
|
814
|
+
function hostStepRenderKey(row: AsyncStatusWorkflowRow): unknown[] {
|
|
815
|
+
return [
|
|
816
|
+
row.kind,
|
|
817
|
+
row.name,
|
|
818
|
+
row.state,
|
|
819
|
+
row.provider,
|
|
820
|
+
row.role,
|
|
821
|
+
row.verdict,
|
|
822
|
+
row.reasonCode,
|
|
823
|
+
row.detail,
|
|
824
|
+
row.target,
|
|
825
|
+
row.freshness,
|
|
826
|
+
row.reportPath,
|
|
827
|
+
];
|
|
828
|
+
}
|
|
829
|
+
|
|
626
830
|
export function widgetRenderKey(job: AsyncJobState, expanded = false): string {
|
|
627
831
|
return JSON.stringify({
|
|
628
832
|
asyncDir: job.asyncDir,
|
|
@@ -639,8 +843,12 @@ export function widgetRenderKey(job: AsyncJobState, expanded = false): string {
|
|
|
639
843
|
currentStep: job.currentStep,
|
|
640
844
|
chainStepCount: job.chainStepCount,
|
|
641
845
|
parallelGroups: job.parallelGroups,
|
|
846
|
+
workflowHostSteps: projectAsyncWorkflowRows([], job.hostSteps).map(hostStepRenderKey),
|
|
847
|
+
preflight: expanded ? job.preflight : job.preflight ? formatWorkflowPreflightPlanSummary(job.preflight) : undefined,
|
|
848
|
+
preflightWarnings: expanded ? job.workflow?.preflightWarnings : job.workflow?.preflightWarnings?.length || undefined,
|
|
642
849
|
steps: job.steps?.map((step, index) => widgetStepRenderKey(step, index, expanded)),
|
|
643
850
|
nestedChildren: nestedRenderKey(job.nestedChildren, expanded),
|
|
851
|
+
lane: laneRenderKey(job),
|
|
644
852
|
stepsTotal: job.stepsTotal,
|
|
645
853
|
runningSteps: job.runningSteps,
|
|
646
854
|
completedSteps: job.completedSteps,
|
|
@@ -681,6 +889,7 @@ function widgetActivity(job: AsyncJobState): string {
|
|
|
681
889
|
if (job.status === "queued") return "queued…";
|
|
682
890
|
if (job.status === "paused") return "Paused";
|
|
683
891
|
if (job.status === "stopped") return "Stopped";
|
|
892
|
+
if (job.status === "partial") return "Partial";
|
|
684
893
|
if (job.status === "failed") return "Failed";
|
|
685
894
|
return "Done";
|
|
686
895
|
}
|
|
@@ -799,8 +1008,10 @@ function widgetParallelAgentDetails(job: AsyncJobState, theme: Theme, expanded =
|
|
|
799
1008
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
800
1009
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
801
1010
|
const label = compactTaskText(step.description, step.label);
|
|
802
|
-
const display = label ? `${label} (${step.agent})` : step.agent;
|
|
1011
|
+
const display = step.sessionName?.trim() || (label ? `${label} (${step.agent})` : step.agent);
|
|
803
1012
|
lines.push(` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index), frame)} ${itemTitle} ${index + 1}/${total}: ${display} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`);
|
|
1013
|
+
const lane = projectAsyncLane(job, step);
|
|
1014
|
+
if (lane) lines.push(...formatLaneProjectionLines(lane, theme, " "));
|
|
804
1015
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 6)) lines.push(` ${nestedLine}`);
|
|
805
1016
|
}
|
|
806
1017
|
return lines;
|
|
@@ -926,12 +1137,13 @@ function buildChainRenderEntries(details: Details, label: MultiProgressLabel): C
|
|
|
926
1137
|
continue;
|
|
927
1138
|
}
|
|
928
1139
|
for (let index = span.start; index < span.start + span.count; index++) {
|
|
1140
|
+
const result = details.results[index];
|
|
929
1141
|
entries.push({
|
|
930
1142
|
kind: "result",
|
|
931
1143
|
resultIndex: index,
|
|
932
1144
|
rowNumber: index + 1,
|
|
933
1145
|
rowLabel: span.isParallel ? `Agent ${index - span.start + 1}/${span.count}` : `Step ${span.stepIndex + 1}`,
|
|
934
|
-
agentName:
|
|
1146
|
+
agentName: result?.sessionName?.trim() || result?.agent || details.chainAgents?.[span.stepIndex] || `step-${span.stepIndex + 1}`,
|
|
935
1147
|
});
|
|
936
1148
|
}
|
|
937
1149
|
}
|
|
@@ -1116,6 +1328,7 @@ function widgetOutputPath(job: AsyncJobState, step: NonNullable<AsyncJobState["s
|
|
|
1116
1328
|
}
|
|
1117
1329
|
|
|
1118
1330
|
function nestedRunName(run: NestedRunSummary): string {
|
|
1331
|
+
if (run.sessionName?.trim()) return run.sessionName.trim();
|
|
1119
1332
|
if (run.agent) return run.agent;
|
|
1120
1333
|
if (run.agents?.length) return formatWidgetAgents(run.agents);
|
|
1121
1334
|
return run.id;
|
|
@@ -1125,6 +1338,7 @@ function nestedStatusGlyph(state: NestedRunSummary["state"] | NestedStepSummary[
|
|
|
1125
1338
|
if (state === "running") return theme.fg("accent", runningGlyph(seed));
|
|
1126
1339
|
if (state === "complete" || state === "completed") return theme.fg("success", "✓");
|
|
1127
1340
|
if (state === "failed") return theme.fg("error", "✗");
|
|
1341
|
+
if (state === "partial") return theme.fg("warning", "■");
|
|
1128
1342
|
if (state === "paused") return theme.fg("warning", "■");
|
|
1129
1343
|
if (state === "stopped") return theme.fg("warning", "■");
|
|
1130
1344
|
return theme.fg("muted", "◦");
|
|
@@ -1172,6 +1386,7 @@ function nestedActivity(input: Pick<NestedRunSummary | NestedStepSummary, "activ
|
|
|
1172
1386
|
if (state === "queued" || state === "pending") return "queued…";
|
|
1173
1387
|
if (state === "paused") return "Paused";
|
|
1174
1388
|
if (state === "stopped") return "Stopped";
|
|
1389
|
+
if (state === "partial") return "Partial";
|
|
1175
1390
|
if (state === "failed") return "Failed";
|
|
1176
1391
|
return "Done";
|
|
1177
1392
|
}
|
|
@@ -1196,7 +1411,7 @@ function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme
|
|
|
1196
1411
|
const activity = nestedActivity(step, state, snapshotNow ?? fallback);
|
|
1197
1412
|
const timestamp = "status" in step ? nestedStepTimestamp(step, fallback) : formatClockTime(nestedRunEventTime(step));
|
|
1198
1413
|
const error = step.error ? ` · ${step.error}` : "";
|
|
1199
|
-
const name = "status" in step ? step
|
|
1414
|
+
const name = "status" in step ? childDisplayName(step) : nestedRunName(step);
|
|
1200
1415
|
rows.push({
|
|
1201
1416
|
prefix,
|
|
1202
1417
|
text: `${nestedTimestampPrefix(timestamp)}${nestedStatusGlyph(state, theme)} ${name} · ${state}${modelThinking ? ` · ${modelThinking}` : ""}${activity ? ` · ${activity}` : ""}${error}`,
|
|
@@ -1258,7 +1473,7 @@ function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme
|
|
|
1258
1473
|
for (const step of child.steps ?? []) {
|
|
1259
1474
|
if (lines.length >= lineBudget) return;
|
|
1260
1475
|
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
1261
|
-
lines.push(theme.fg("dim", `${prefix} ↳ ${nestedTimestampPrefix(nestedStepTimestamp(step, child.lastUpdate))}${nestedStatusGlyph(step.status, theme)} ${step
|
|
1476
|
+
lines.push(theme.fg("dim", `${prefix} ↳ ${nestedTimestampPrefix(nestedStepTimestamp(step, child.lastUpdate))}${nestedStatusGlyph(step.status, theme)} ${childDisplayName(step)} · ${step.status}${modelThinking ? ` · ${modelThinking}` : ""} · ${nestedActivity(step, step.status, snapshotNow ?? child.lastUpdate)}`));
|
|
1262
1477
|
append(step.children, depth + 1, `${prefix} `);
|
|
1263
1478
|
}
|
|
1264
1479
|
append(child.children, depth + 1, `${prefix} `);
|
|
@@ -1282,7 +1497,9 @@ function foregroundStyleWidgetStepLines(
|
|
|
1282
1497
|
const status = widgetStepStatus(step.status, theme);
|
|
1283
1498
|
const stats = widgetStepStats(theme, step);
|
|
1284
1499
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1285
|
-
const lines = [` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1), frame)} ${itemTitle} ${index}/${total}: ${themeBold(theme, step
|
|
1500
|
+
const lines = [` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1), frame)} ${itemTitle} ${index}/${total}: ${themeBold(theme, childDisplayName(step))}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
|
|
1501
|
+
const lane = projectAsyncLane(job, step);
|
|
1502
|
+
if (lane) lines.push(...formatLaneProjectionLines(lane, theme, " "));
|
|
1286
1503
|
const task = compactTaskText(step.description, step.label);
|
|
1287
1504
|
if (task) lines.push(` ${theme.fg("dim", `task: ${task}`)}`);
|
|
1288
1505
|
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
@@ -1310,18 +1527,50 @@ function foregroundStyleWidgetStepLines(
|
|
|
1310
1527
|
return lines;
|
|
1311
1528
|
}
|
|
1312
1529
|
|
|
1530
|
+
function hostStepWidgetLines(job: AsyncJobState, theme: Theme, indent: string): string[] {
|
|
1531
|
+
const rows = projectAsyncWorkflowRows([], job.hostSteps).filter((row) => row.kind);
|
|
1532
|
+
const visible = rows.slice(0, 8);
|
|
1533
|
+
const lines = visible.map((row) => {
|
|
1534
|
+
const state = hostStepVerdictLabel(row.state as HostStepState, row.verdict as HostStepVerdict | undefined);
|
|
1535
|
+
const glyph = state === "running" ? theme.fg("accent", "●")
|
|
1536
|
+
: state === "pending" ? theme.fg("muted", "◦")
|
|
1537
|
+
: state === "pass" ? theme.fg("success", "✓")
|
|
1538
|
+
: state === "fail" || state === "error" ? theme.fg("error", "✗")
|
|
1539
|
+
: theme.fg("warning", "■");
|
|
1540
|
+
const details = [
|
|
1541
|
+
row.provider ? `provider:${row.provider}` : undefined,
|
|
1542
|
+
row.role ? `role:${row.role}` : undefined,
|
|
1543
|
+
row.target,
|
|
1544
|
+
row.detail,
|
|
1545
|
+
row.reasonCode ? `reason:${row.reasonCode}` : undefined,
|
|
1546
|
+
row.freshness?.stale ? "stale" : row.freshness?.observedRef ? `ref:${row.freshness.observedRef}` : undefined,
|
|
1547
|
+
row.reportPath ? `out:${hostStepReportName(row.reportPath)}` : undefined,
|
|
1548
|
+
].filter(Boolean).join(" · ");
|
|
1549
|
+
return `${indent}${glyph} ${row.kind}: ${row.name} · ${state}${details ? ` · ${details}` : ""}`;
|
|
1550
|
+
});
|
|
1551
|
+
if (rows.length > visible.length) lines.push(`${indent}${theme.fg("dim", `… +${rows.length - visible.length} host steps hidden`)}`);
|
|
1552
|
+
return lines;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1313
1555
|
function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded: boolean, width: number, frame?: number): string[] {
|
|
1314
|
-
if (!job.steps?.length)
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1556
|
+
if (!job.steps?.length) {
|
|
1557
|
+
const lane = projectAsyncLane(job);
|
|
1558
|
+
return [
|
|
1559
|
+
...workflowPreflightLines(job, expanded),
|
|
1560
|
+
...(lane ? formatLaneProjectionLines(lane, theme, " ") : []),
|
|
1561
|
+
...hostStepWidgetLines(job, theme, " "),
|
|
1562
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1563
|
+
...formatNestedWidgetLines(job.nestedChildren, theme, width, expanded, job.updatedAt, expanded ? 12 : 6).map((line) => ` ${line}`),
|
|
1564
|
+
];
|
|
1565
|
+
}
|
|
1318
1566
|
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length) return widgetChainDetails(job, theme, expanded, width, frame);
|
|
1319
1567
|
const total = job.stepsTotal ?? job.steps.length;
|
|
1320
1568
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
1321
|
-
const lines: string[] =
|
|
1569
|
+
const lines: string[] = workflowPreflightLines(job, expanded);
|
|
1322
1570
|
for (const [index, step] of job.steps.entries()) {
|
|
1323
1571
|
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index + 1, total, expanded, width, frame));
|
|
1324
1572
|
}
|
|
1573
|
+
lines.push(...hostStepWidgetLines(job, theme, " "));
|
|
1325
1574
|
const attached = new Set(job.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
1326
1575
|
const unattached = job.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
1327
1576
|
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
@@ -1357,9 +1606,12 @@ function compactSingleWidgetLines(job: AsyncJobState, theme: Theme, width: numbe
|
|
|
1357
1606
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1358
1607
|
const task = compactTaskText(step.description, step.label);
|
|
1359
1608
|
const taskSuffix = task ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", `task: ${task}`)}` : "";
|
|
1360
|
-
lines.push(` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index), frame)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step
|
|
1609
|
+
lines.push(` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index), frame)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, childDisplayName(step))}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${taskSuffix}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
|
|
1610
|
+
const lane = projectAsyncLane(job, step);
|
|
1611
|
+
if (lane) lines.push(...formatLaneProjectionLines(lane, theme, " "));
|
|
1361
1612
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt, 6)) lines.push(` ${nestedLine}`);
|
|
1362
1613
|
}
|
|
1614
|
+
lines.push(...hostStepWidgetLines(job, theme, " "));
|
|
1363
1615
|
if (job.steps.some((step) => step.status === "running")) lines.push(theme.fg("accent", ` ${liveDetailHintText()}`));
|
|
1364
1616
|
return lines.map((line) => truncLine(line, width));
|
|
1365
1617
|
}
|
|
@@ -1496,9 +1748,18 @@ function progressiveJobLine(job: AsyncJobState, theme: Theme, width: number, fra
|
|
|
1496
1748
|
const stats = widgetStats(job, theme);
|
|
1497
1749
|
const activity = widgetActivity(job);
|
|
1498
1750
|
const status = job.status === "complete" ? "done" : job.status;
|
|
1751
|
+
const lane = projectAsyncLane(job);
|
|
1752
|
+
const laneSummary = lane
|
|
1753
|
+
? [lane.label ?? lane.role, lane.phase ? `phase:${lane.phase}` : undefined, lane.output ? `out:${lane.output}` : undefined, lane.workspace ? `workspace:${lane.workspace}` : `ref:${lane.ref}`].filter(Boolean).join(" · ")
|
|
1754
|
+
: "";
|
|
1755
|
+
const laneSignals = lane
|
|
1756
|
+
? [lane.next ? `next:${lane.next}` : undefined, ...lane.chips.map((chip) => formatLaneChip(chip, theme))].filter(Boolean).join(" · ")
|
|
1757
|
+
: "";
|
|
1499
1758
|
const parts = [
|
|
1500
1759
|
`${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}`,
|
|
1501
1760
|
theme.fg("dim", status),
|
|
1761
|
+
laneSignals ? laneSignals : "",
|
|
1762
|
+
laneSummary ? theme.fg("dim", laneSummary) : "",
|
|
1502
1763
|
stats,
|
|
1503
1764
|
activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : "",
|
|
1504
1765
|
].filter(Boolean);
|
|
@@ -1644,6 +1905,7 @@ export function buildWidgetLines(jobs: AsyncJobState[], theme: Theme, width = ge
|
|
|
1644
1905
|
items.push([
|
|
1645
1906
|
`${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1646
1907
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1908
|
+
...widgetLaneDetailLines(job, theme),
|
|
1647
1909
|
...widgetParallelAgentDetails(job, theme, expanded, width, frame),
|
|
1648
1910
|
]);
|
|
1649
1911
|
slots--;
|
|
@@ -1661,6 +1923,7 @@ export function buildWidgetLines(jobs: AsyncJobState[], theme: Theme, width = ge
|
|
|
1661
1923
|
items.push([
|
|
1662
1924
|
`${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1663
1925
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1926
|
+
...widgetLaneDetailLines(job, theme),
|
|
1664
1927
|
...widgetParallelAgentDetails(job, theme, expanded, width, frame),
|
|
1665
1928
|
]);
|
|
1666
1929
|
slots--;
|
|
@@ -1728,7 +1991,7 @@ function renderSingleCompact(
|
|
|
1728
1991
|
const detailIndent = mainWindowIndent(layout, 1);
|
|
1729
1992
|
const continuationIndent = mainWindowIndent(layout, 2) + (layout.horizontalSpacing > 0 ? " " : "");
|
|
1730
1993
|
const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
|
|
1731
|
-
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(r
|
|
1994
|
+
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(childDisplayName(r)))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1732
1995
|
|
|
1733
1996
|
if (isRunning && r.progress) {
|
|
1734
1997
|
const task = compactTaskText(r.task);
|
|
@@ -1761,6 +2024,7 @@ function renderSingleCompact(
|
|
|
1761
2024
|
}
|
|
1762
2025
|
|
|
1763
2026
|
function workflowRowGlyph(row: WorkflowChatProgressRow, theme: Theme, frame?: number): string {
|
|
2027
|
+
if (row.state === "planned") return theme.fg("muted", "◦");
|
|
1764
2028
|
if (row.state === "running") return theme.fg("accent", runningGlyph(frame));
|
|
1765
2029
|
if (row.state === "complete") return theme.fg("success", "✓");
|
|
1766
2030
|
if (row.state === "detached" || row.state === "stopped") return theme.fg("warning", "■");
|
|
@@ -1769,6 +2033,7 @@ function workflowRowGlyph(row: WorkflowChatProgressRow, theme: Theme, frame?: nu
|
|
|
1769
2033
|
|
|
1770
2034
|
function workflowRowStateLabel(row: WorkflowChatProgressRow, theme: Theme): string {
|
|
1771
2035
|
const label = (row.state === "complete" ? "complete" : row.state).padEnd(8);
|
|
2036
|
+
if (row.state === "planned") return theme.fg("dim", label);
|
|
1772
2037
|
if (row.state === "running") return theme.fg("accent", label);
|
|
1773
2038
|
if (row.state === "complete") return theme.fg("success", label);
|
|
1774
2039
|
if (row.state === "detached" || row.state === "stopped") return theme.fg("warning", label);
|
|
@@ -1783,9 +2048,9 @@ function workflowOverallState(rows: WorkflowChatProgressRow[], hasTerminalValue:
|
|
|
1783
2048
|
return "running";
|
|
1784
2049
|
}
|
|
1785
2050
|
|
|
1786
|
-
function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>, theme: Theme, layout: MainWindowRenderLayout, frame?: number): Component {
|
|
2051
|
+
function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>, theme: Theme, layout: MainWindowRenderLayout, frame?: number, expanded = false): Component {
|
|
1787
2052
|
const workflow = d.workflow;
|
|
1788
|
-
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace) : [];
|
|
2053
|
+
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace, d.preflight) : d.preflight ? buildWorkflowChatProgressRows([], d.preflight) : [];
|
|
1789
2054
|
const state = workflowOverallState(rows, workflow?.value !== undefined, result.isError);
|
|
1790
2055
|
const glyph = state === "running" ? theme.fg("accent", runningGlyph(frame)) : state === "complete" ? theme.fg("success", "✓") : state === "paused" ? theme.fg("warning", "■") : theme.fg("error", "✗");
|
|
1791
2056
|
const width = getTermWidth() - 4;
|
|
@@ -1796,6 +2061,7 @@ function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>
|
|
|
1796
2061
|
const rowIndent = mainWindowIndent(layout, 1);
|
|
1797
2062
|
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));
|
|
1798
2063
|
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Repo ${repoLabel}`), width), 0, 0));
|
|
2064
|
+
if (d.preflight) c.addChild(new Text(truncLine(theme.fg("dim", formatWorkflowPreflightPlanSummary(d.preflight, { indent: rowIndent })), width), 0, 0));
|
|
1799
2065
|
if (phase) c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Phase ${phase}`), width), 0, 0));
|
|
1800
2066
|
if (rows.length === 0) {
|
|
1801
2067
|
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}◦ waiting for workflow child launches`), width), 0, 0));
|
|
@@ -1809,7 +2075,20 @@ function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>
|
|
|
1809
2075
|
const duration = row.durationMs !== undefined ? ` ${theme.fg("dim", `· ${formatDuration(row.durationMs)}`)}` : "";
|
|
1810
2076
|
const run = row.runId ? ` ${theme.fg("dim", `[${row.runId.slice(0, 8)}]`)}` : "";
|
|
1811
2077
|
const error = row.error ? ` ${theme.fg(row.state === "detached" ? "warning" : "error", `· ${compactWorkflowError(row.error)}`)}` : "";
|
|
1812
|
-
|
|
2078
|
+
const hints = expanded && row.preflight ? [
|
|
2079
|
+
row.preflight.mode ? `mode:${row.preflight.mode}` : undefined,
|
|
2080
|
+
row.preflight.decision ? `decision:${row.preflight.decision}` : undefined,
|
|
2081
|
+
row.preflight.claims?.length ? `claims:${row.preflight.claims.join(",")}` : undefined,
|
|
2082
|
+
row.preflight.expectedOutput ? `expected:${row.preflight.expectedOutput}` : undefined,
|
|
2083
|
+
row.preflight.independence ? `independence:${row.preflight.independence}` : undefined,
|
|
2084
|
+
].filter((value): value is string => Boolean(value)).join(" · ") : "";
|
|
2085
|
+
c.addChild(new Text(truncLine(`${rowIndent}${workflowRowGlyph(row, theme, frame)} ${status} ${theme.bold(row.key)}${label}${run}${duration}${error}${hints ? ` ${theme.fg("dim", `· ${hints}`)}` : ""}`, width), 0, 0));
|
|
2086
|
+
}
|
|
2087
|
+
if (workflow?.preflightWarnings?.length) {
|
|
2088
|
+
const warningLines = expanded
|
|
2089
|
+
? formatWorkflowPreflightWarnings(workflow.preflightWarnings, { indent: rowIndent }).split("\n")
|
|
2090
|
+
: [formatWorkflowPreflightWarningSummary(workflow.preflightWarnings, { indent: rowIndent, hint: "expand for debug" })];
|
|
2091
|
+
for (const warningLine of warningLines) c.addChild(new Text(truncLine(theme.fg("warning", warningLine), width), 0, 0));
|
|
1813
2092
|
}
|
|
1814
2093
|
if (workflow?.emits.length) c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Emits ${workflow.emits.length}`), width), 0, 0));
|
|
1815
2094
|
return c;
|
|
@@ -1825,6 +2104,7 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1825
2104
|
|| workflowGraphHasStatus(d, ["failed"]);
|
|
1826
2105
|
const paused = d.results.some((r) => r.interrupted)
|
|
1827
2106
|
|| workflowGraphHasStatus(d, ["paused"]);
|
|
2107
|
+
const partial = workflowGraphHasStatus(d, ["partial"]);
|
|
1828
2108
|
let totalSummary = d.progressSummary;
|
|
1829
2109
|
if (!totalSummary) {
|
|
1830
2110
|
let sawProgress = false;
|
|
@@ -1848,6 +2128,7 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1848
2128
|
stopped,
|
|
1849
2129
|
interrupted: paused,
|
|
1850
2130
|
failed,
|
|
2131
|
+
partial,
|
|
1851
2132
|
seed: runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex),
|
|
1852
2133
|
frame,
|
|
1853
2134
|
});
|
|
@@ -1857,7 +2138,6 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1857
2138
|
const width = getTermWidth() - 4;
|
|
1858
2139
|
const rowIndent = mainWindowIndent(layout, 1);
|
|
1859
2140
|
const detailIndent = mainWindowIndent(layout, 2);
|
|
1860
|
-
const continuationIndent = mainWindowIndent(layout, 3) + (layout.horizontalSpacing > 0 ? " " : "");
|
|
1861
2141
|
c.addChild(new Text(truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1862
2142
|
|
|
1863
2143
|
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
@@ -1869,7 +2149,8 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1869
2149
|
const r = d.results[i];
|
|
1870
2150
|
const fallbackLabel = itemTitle.toLowerCase();
|
|
1871
2151
|
const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
|
|
1872
|
-
|
|
2152
|
+
const fallbackAgent = useResultsDirectly ? (r?.agent || `${fallbackLabel}-${rowNumber}`) : (d.chainAgents![i] || r?.agent || `${fallbackLabel}-${rowNumber}`);
|
|
2153
|
+
return { kind: "result", resultIndex: i, rowNumber, agentName: r?.sessionName?.trim() || fallbackAgent };
|
|
1873
2154
|
});
|
|
1874
2155
|
for (const entry of renderEntries) {
|
|
1875
2156
|
if (entry.kind === "placeholder") {
|
|
@@ -1951,7 +2232,8 @@ export function renderSubagentSummary(
|
|
|
1951
2232
|
const failed = result.isError === true
|
|
1952
2233
|
|| results.some((entry) => !hasTerminalResultFlag(entry) && entry.exitCode !== 0 && !isResultRunning(entry))
|
|
1953
2234
|
|| Boolean(details && workflowGraphHasStatus(details, ["failed"]));
|
|
1954
|
-
const
|
|
2235
|
+
const partial = Boolean(details && workflowGraphHasStatus(details, ["partial"]));
|
|
2236
|
+
const state = running ? "running" : failed ? "failed" : stopped ? "stopped" : paused ? "paused" : partial ? "partial" : "completed";
|
|
1955
2237
|
const glyph = state === "running"
|
|
1956
2238
|
? theme.fg("accent", STATIC_RUNNING_GLYPH)
|
|
1957
2239
|
: state === "completed"
|
|
@@ -1960,7 +2242,7 @@ export function renderSubagentSummary(
|
|
|
1960
2242
|
? theme.fg("error", "✗")
|
|
1961
2243
|
: theme.fg("warning", "■");
|
|
1962
2244
|
const label = details?.mode === "single" && results.length === 1
|
|
1963
|
-
? results[0]
|
|
2245
|
+
? childDisplayName(results[0])
|
|
1964
2246
|
: details?.mode || "subagent";
|
|
1965
2247
|
return new Text(
|
|
1966
2248
|
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),
|
|
@@ -1984,7 +2266,7 @@ export function renderSubagentResult(
|
|
|
1984
2266
|
const compact = (component: Component): Component => capCompactMainWindowResult(component, layout, theme, !options.expanded);
|
|
1985
2267
|
const d = result.details;
|
|
1986
2268
|
if (d?.mode === "workflow" && d.chatProgress?.mode === "live-card" && d.workflow?.value === undefined && (!result.isError || (d.workflow?.trace.length ?? 0) > 0)) {
|
|
1987
|
-
return compact(renderWorkflowChatProgress(d, result, theme, options.expanded ? resolveMainWindowRenderLayout() : layout, frame));
|
|
2269
|
+
return compact(renderWorkflowChatProgress(d, result, theme, options.expanded ? resolveMainWindowRenderLayout() : layout, frame, options.expanded));
|
|
1988
2270
|
}
|
|
1989
2271
|
if (!d || !d.results.length) {
|
|
1990
2272
|
const t = result.content[0];
|
|
@@ -1995,9 +2277,12 @@ export function renderSubagentResult(
|
|
|
1995
2277
|
if (d && !options.expanded && !result.isError) {
|
|
1996
2278
|
const lines = text.split(/\r?\n/);
|
|
1997
2279
|
const firstNonEmptyLine = lines.find((line) => line.trim())?.trim() || "(no output)";
|
|
2280
|
+
const compactLine = d.mode === "workflow" && d.preflight
|
|
2281
|
+
? formatWorkflowPreflightPlanSummary(d.preflight)
|
|
2282
|
+
: firstNonEmptyLine;
|
|
1998
2283
|
const c = new Container();
|
|
1999
2284
|
const detailIndent = mainWindowIndent(layout, 1);
|
|
2000
|
-
c.addChild(new Text(truncLine(`${contextPrefix}${
|
|
2285
|
+
c.addChild(new Text(truncLine(`${contextPrefix}${compactLine} · ${lines.length} lines`, width), 0, 0));
|
|
2001
2286
|
c.addChild(new Text(truncLine(theme.fg("accent", `${detailIndent}Press ${liveDetailKeyText()} for full output`), width), 0, 0));
|
|
2002
2287
|
return compact(c);
|
|
2003
2288
|
}
|
|
@@ -2030,7 +2315,7 @@ export function renderSubagentResult(
|
|
|
2030
2315
|
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
2031
2316
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
2032
2317
|
const c = new Container();
|
|
2033
|
-
c.addChild(new Text(fit(`${presentation.glyph} ${theme.fg("toolTitle", theme.bold(r
|
|
2318
|
+
c.addChild(new Text(fit(`${presentation.glyph} ${theme.fg("toolTitle", theme.bold(childDisplayName(r)))}${contextBadge}${progressInfo} ${theme.fg("dim", "·")} ${presentation.label}`), 0, 0));
|
|
2034
2319
|
c.addChild(new Spacer(1));
|
|
2035
2320
|
const taskMaxLen = Math.max(20, w - 8);
|
|
2036
2321
|
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
@@ -2118,6 +2403,7 @@ export function renderSubagentResult(
|
|
|
2118
2403
|
|| workflowGraphHasStatus(d, ["failed"]);
|
|
2119
2404
|
const paused = d.results.some((r) => r.interrupted)
|
|
2120
2405
|
|| workflowGraphHasStatus(d, ["paused"]);
|
|
2406
|
+
const partial = workflowGraphHasStatus(d, ["partial"]);
|
|
2121
2407
|
const completedWithoutOutput = d.results.some((r) =>
|
|
2122
2408
|
!hasTerminalResultFlag(r)
|
|
2123
2409
|
&& r.exitCode === 0
|
|
@@ -2130,6 +2416,7 @@ export function renderSubagentResult(
|
|
|
2130
2416
|
stopped,
|
|
2131
2417
|
interrupted: paused,
|
|
2132
2418
|
failed,
|
|
2419
|
+
partial,
|
|
2133
2420
|
completedWithoutOutput,
|
|
2134
2421
|
frame,
|
|
2135
2422
|
}), theme);
|
|
@@ -2169,6 +2456,7 @@ export function renderSubagentResult(
|
|
|
2169
2456
|
? d.chainAgents
|
|
2170
2457
|
.map((agent, i) => {
|
|
2171
2458
|
const result = d.results[i];
|
|
2459
|
+
const displayName = result?.sessionName?.trim() || agent;
|
|
2172
2460
|
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
2173
2461
|
const stepPresentation = result
|
|
2174
2462
|
? styledResultPresentation(resultPresentation(result, getSingleResultOutput(result), isCurrent && hasRunning && !hasTerminalResultFlag(result)), theme)
|
|
@@ -2176,7 +2464,7 @@ export function renderSubagentResult(
|
|
|
2176
2464
|
const stepStatus = stepPresentation
|
|
2177
2465
|
? `${stepPresentation.glyph} ${stepPresentation.label}`
|
|
2178
2466
|
: theme.fg("dim", "◦ pending");
|
|
2179
|
-
return `${stepStatus} ${
|
|
2467
|
+
return `${stepStatus} ${displayName}${contextModeBadge(theme, result?.context)}`;
|
|
2180
2468
|
})
|
|
2181
2469
|
.join(theme.fg("dim", " → "))
|
|
2182
2470
|
: null;
|
|
@@ -2203,7 +2491,8 @@ export function renderSubagentResult(
|
|
|
2203
2491
|
const i = displayStart + offset;
|
|
2204
2492
|
const r = d.results[i];
|
|
2205
2493
|
const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
|
|
2206
|
-
|
|
2494
|
+
const fallbackAgent = useResultsDirectly ? (r?.agent || `step-${rowNumber}`) : (d.chainAgents![i] || r?.agent || `step-${rowNumber}`);
|
|
2495
|
+
return { kind: "result", resultIndex: i, rowNumber, agentName: r?.sessionName?.trim() || fallbackAgent };
|
|
2207
2496
|
});
|
|
2208
2497
|
|
|
2209
2498
|
c.addChild(new Spacer(1));
|
|
@@ -2243,8 +2532,8 @@ export function renderSubagentResult(
|
|
|
2243
2532
|
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
2244
2533
|
const contextBadge = contextModeBadge(theme, r.context);
|
|
2245
2534
|
const stepHeader = rRunning
|
|
2246
|
-
? `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(theme.fg("warning", r
|
|
2247
|
-
: `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(r
|
|
2535
|
+
? `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(theme.fg("warning", childDisplayName(r)))}${contextBadge}${modelDisplay}${stats} ${theme.fg("dim", "·")} ${rowPresentation.label}`
|
|
2536
|
+
: `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(childDisplayName(r))}${contextBadge}${modelDisplay}${stats} ${theme.fg("dim", "·")} ${rowPresentation.label}`;
|
|
2248
2537
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
2249
2538
|
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
2250
2539
|
|