pi-subagents 0.57.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 +104 -0
- package/docs/agents.md +18 -9
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +40 -1
- package/docs/models.md +27 -4
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +92 -6
- package/docs/workflows.md +115 -0
- 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 +3 -1
- package/skills/pi-subagents/references/execution-controls.md +6 -2
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +26 -5
- package/src/agents/agent-management.ts +28 -17
- package/src/agents/agent-serializer.ts +9 -2
- package/src/agents/agents.ts +103 -27
- package/src/agents/runtime-agent-events.ts +70 -0
- package/src/agents/runtime-agent-registry.ts +19 -18
- package/src/api/agents.ts +10 -5
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +32 -10
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +66 -27
- package/src/extension/public-execution.ts +15 -2
- package/src/extension/rpc.ts +35 -14
- package/src/extension/schemas.ts +37 -12
- package/src/extension/tool-description.ts +25 -6
- package/src/integrations/herdr-status.ts +5 -0
- 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 +78 -46
- package/src/runs/background/async-job-tracker.ts +14 -12
- package/src/runs/background/async-resume.ts +29 -27
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +68 -7
- package/src/runs/background/chain-append.ts +8 -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 +184 -9
- package/src/runs/background/result-delivery-ownership.ts +45 -0
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +29 -10
- 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 +389 -350
- 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 +134 -121
- 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 +527 -257
- package/src/runs/foreground/workflow-detach-reconcile.ts +130 -120
- 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 +5 -4
- package/src/runs/shared/dynamic-fanout.ts +3 -3
- package/src/runs/shared/fast-mode-extension.ts +5 -5
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/launch-cwd.ts +16 -0
- package/src/runs/shared/long-running-guard.ts +2 -1
- package/src/runs/shared/mcp-config-sources.ts +422 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
- package/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/src/runs/shared/model-exclusions.ts +12 -0
- package/src/runs/shared/model-fallback.ts +25 -6
- 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 +15 -1
- package/src/runs/shared/pi-args.ts +81 -8
- package/src/runs/shared/single-output.ts +44 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +98 -13
- 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 +11 -2
- package/src/shared/launch-contract.ts +5 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +240 -30
- package/src/shared/utils.ts +41 -84
- 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/slash/subagents-admin.ts +3 -0
- package/src/tui/fleet-status.ts +125 -74
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render.ts +330 -33
- 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 +496 -35
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +79 -5
- 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,
|
|
@@ -23,14 +25,17 @@ import {
|
|
|
23
25
|
} from "../shared/types.ts";
|
|
24
26
|
import { previewDisplayText, sanitizeDisplayText, truncateDisplayText } from "../shared/display-text.ts";
|
|
25
27
|
import { FLEET_OPEN_SHORTCUT, formatShortcutLabel } from "../shared/shortcuts.ts";
|
|
26
|
-
import { formatTokens, formatUsage, formatDuration, formatModelThinking, formatToolCall, formatTokenUsage, shortenPath } from "../shared/formatters.ts";
|
|
28
|
+
import { formatContextUsage, formatTokens, formatUsage, formatDuration, formatModelThinking, formatToolCall, formatTokenUsage, shortenPath } from "../shared/formatters.ts";
|
|
27
29
|
import { getDisplayItems, getSingleResultOutput, PROMPT_REDACTED } from "../shared/utils.ts";
|
|
28
30
|
import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
|
|
29
31
|
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,14 +271,188 @@ 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
|
-
const
|
|
271
|
-
|
|
272
|
-
const
|
|
281
|
+
const taskText = task?.trim();
|
|
282
|
+
const labelText = label?.trim();
|
|
283
|
+
const normalizedTask = taskText && taskText !== PROMPT_REDACTED ? oneLine(taskText) : "";
|
|
284
|
+
const normalizedLabel = labelText && labelText !== PROMPT_REDACTED ? oneLine(labelText) : "";
|
|
285
|
+
const normalized = normalizedLabel && normalizedTask && normalizedLabel !== normalizedTask
|
|
286
|
+
? `${normalizedLabel} — ${normalizedTask}`
|
|
287
|
+
: normalizedLabel || normalizedTask;
|
|
273
288
|
if (!normalized) return undefined;
|
|
274
289
|
return previewDisplayText(normalized, COMPACT_TASK_MAX_CHARS);
|
|
275
290
|
}
|
|
276
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
|
+
|
|
277
456
|
function workflowLabelForResult(details: Details, resultIndex: number): string | undefined {
|
|
278
457
|
const flatIndex = details.results[resultIndex]?.progress?.index ?? resultIndex;
|
|
279
458
|
const visit = (nodes: NonNullable<Details["workflowGraph"]>["nodes"]): string | undefined => {
|
|
@@ -470,7 +649,7 @@ function resultStatusLine(result: Details["results"][number], output: string): s
|
|
|
470
649
|
|
|
471
650
|
type ResultPresentation = {
|
|
472
651
|
glyph: string;
|
|
473
|
-
label: "running" | "detached" | "stopped" | "paused" | "failed" | "completed";
|
|
652
|
+
label: "running" | "detached" | "stopped" | "paused" | "failed" | "partial" | "completed";
|
|
474
653
|
tone: "accent" | "warning" | "error" | "success";
|
|
475
654
|
};
|
|
476
655
|
|
|
@@ -480,6 +659,7 @@ function semanticResultPresentation(input: {
|
|
|
480
659
|
stopped?: boolean;
|
|
481
660
|
interrupted?: boolean;
|
|
482
661
|
failed?: boolean;
|
|
662
|
+
partial?: boolean;
|
|
483
663
|
completedWithoutOutput?: boolean;
|
|
484
664
|
seed?: number;
|
|
485
665
|
frame?: number;
|
|
@@ -492,6 +672,7 @@ function semanticResultPresentation(input: {
|
|
|
492
672
|
if (input.stopped) return { glyph: "■", label: "stopped", tone: "warning" };
|
|
493
673
|
if (input.interrupted) return { glyph: "■", label: "paused", tone: "warning" };
|
|
494
674
|
if (input.failed) return { glyph: "✗", label: "failed", tone: "error" };
|
|
675
|
+
if (input.partial) return { glyph: "■", label: "partial", tone: "warning" };
|
|
495
676
|
return { glyph: "✓", label: "completed", tone: input.completedWithoutOutput ? "warning" : "success" };
|
|
496
677
|
}
|
|
497
678
|
|
|
@@ -570,6 +751,7 @@ function widgetStepRenderKey(step: AsyncJobStep, index: number, expanded = false
|
|
|
570
751
|
return [
|
|
571
752
|
step.index ?? index,
|
|
572
753
|
step.agent,
|
|
754
|
+
step.sessionName,
|
|
573
755
|
step.workflowKey,
|
|
574
756
|
step.phase,
|
|
575
757
|
step.label,
|
|
@@ -589,6 +771,15 @@ function widgetStepRenderKey(step: AsyncJobStep, index: number, expanded = false
|
|
|
589
771
|
step.model,
|
|
590
772
|
step.thinking,
|
|
591
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,
|
|
592
783
|
step.error,
|
|
593
784
|
expanded ? expandedStepActivityRenderKey(step) : undefined,
|
|
594
785
|
nestedRenderKey(step.children, expanded),
|
|
@@ -600,6 +791,7 @@ function nestedRenderKey(children: NestedRunSummary[] | undefined, expanded = fa
|
|
|
600
791
|
child.id,
|
|
601
792
|
child.state,
|
|
602
793
|
child.agent,
|
|
794
|
+
child.sessionName,
|
|
603
795
|
child.model,
|
|
604
796
|
child.thinking,
|
|
605
797
|
child.activityState,
|
|
@@ -619,6 +811,22 @@ function nestedRenderKey(children: NestedRunSummary[] | undefined, expanded = fa
|
|
|
619
811
|
]);
|
|
620
812
|
}
|
|
621
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
|
+
|
|
622
830
|
export function widgetRenderKey(job: AsyncJobState, expanded = false): string {
|
|
623
831
|
return JSON.stringify({
|
|
624
832
|
asyncDir: job.asyncDir,
|
|
@@ -635,8 +843,12 @@ export function widgetRenderKey(job: AsyncJobState, expanded = false): string {
|
|
|
635
843
|
currentStep: job.currentStep,
|
|
636
844
|
chainStepCount: job.chainStepCount,
|
|
637
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,
|
|
638
849
|
steps: job.steps?.map((step, index) => widgetStepRenderKey(step, index, expanded)),
|
|
639
850
|
nestedChildren: nestedRenderKey(job.nestedChildren, expanded),
|
|
851
|
+
lane: laneRenderKey(job),
|
|
640
852
|
stepsTotal: job.stepsTotal,
|
|
641
853
|
runningSteps: job.runningSteps,
|
|
642
854
|
completedSteps: job.completedSteps,
|
|
@@ -677,6 +889,7 @@ function widgetActivity(job: AsyncJobState): string {
|
|
|
677
889
|
if (job.status === "queued") return "queued…";
|
|
678
890
|
if (job.status === "paused") return "Paused";
|
|
679
891
|
if (job.status === "stopped") return "Stopped";
|
|
892
|
+
if (job.status === "partial") return "Partial";
|
|
680
893
|
if (job.status === "failed") return "Failed";
|
|
681
894
|
return "Done";
|
|
682
895
|
}
|
|
@@ -794,9 +1007,11 @@ function widgetParallelAgentDetails(job: AsyncJobState, theme: Theme, expanded =
|
|
|
794
1007
|
const activity = widgetStepActivity(step, job.updatedAt);
|
|
795
1008
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
796
1009
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
797
|
-
const label = compactTaskText(
|
|
798
|
-
const display = label ? `${label} (${step.agent})` : step.agent;
|
|
1010
|
+
const label = compactTaskText(step.description, step.label);
|
|
1011
|
+
const display = step.sessionName?.trim() || (label ? `${label} (${step.agent})` : step.agent);
|
|
799
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, " "));
|
|
800
1015
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 6)) lines.push(` ${nestedLine}`);
|
|
801
1016
|
}
|
|
802
1017
|
return lines;
|
|
@@ -922,12 +1137,13 @@ function buildChainRenderEntries(details: Details, label: MultiProgressLabel): C
|
|
|
922
1137
|
continue;
|
|
923
1138
|
}
|
|
924
1139
|
for (let index = span.start; index < span.start + span.count; index++) {
|
|
1140
|
+
const result = details.results[index];
|
|
925
1141
|
entries.push({
|
|
926
1142
|
kind: "result",
|
|
927
1143
|
resultIndex: index,
|
|
928
1144
|
rowNumber: index + 1,
|
|
929
1145
|
rowLabel: span.isParallel ? `Agent ${index - span.start + 1}/${span.count}` : `Step ${span.stepIndex + 1}`,
|
|
930
|
-
agentName:
|
|
1146
|
+
agentName: result?.sessionName?.trim() || result?.agent || details.chainAgents?.[span.stepIndex] || `step-${span.stepIndex + 1}`,
|
|
931
1147
|
});
|
|
932
1148
|
}
|
|
933
1149
|
}
|
|
@@ -1083,7 +1299,11 @@ function widgetStepStats(theme: Theme, step: NonNullable<AsyncJobState["steps"]>
|
|
|
1083
1299
|
return statJoin(theme, [
|
|
1084
1300
|
step.turnCount !== undefined ? `${step.turnCount} turns` : "",
|
|
1085
1301
|
step.toolCount !== undefined ? formatToolUseStat(step.toolCount) : "",
|
|
1086
|
-
step.tokens
|
|
1302
|
+
step.tokens
|
|
1303
|
+
? step.contextLimit !== undefined
|
|
1304
|
+
? formatContextUsage(step.tokens, step.contextLimit) ?? formatTokenUsage(step.tokens, "token")
|
|
1305
|
+
: step.tokens.total ? formatTokenUsage(step.tokens, "token") : ""
|
|
1306
|
+
: "",
|
|
1087
1307
|
step.durationMs !== undefined ? formatDuration(step.durationMs) : "",
|
|
1088
1308
|
]);
|
|
1089
1309
|
}
|
|
@@ -1108,6 +1328,7 @@ function widgetOutputPath(job: AsyncJobState, step: NonNullable<AsyncJobState["s
|
|
|
1108
1328
|
}
|
|
1109
1329
|
|
|
1110
1330
|
function nestedRunName(run: NestedRunSummary): string {
|
|
1331
|
+
if (run.sessionName?.trim()) return run.sessionName.trim();
|
|
1111
1332
|
if (run.agent) return run.agent;
|
|
1112
1333
|
if (run.agents?.length) return formatWidgetAgents(run.agents);
|
|
1113
1334
|
return run.id;
|
|
@@ -1117,6 +1338,7 @@ function nestedStatusGlyph(state: NestedRunSummary["state"] | NestedStepSummary[
|
|
|
1117
1338
|
if (state === "running") return theme.fg("accent", runningGlyph(seed));
|
|
1118
1339
|
if (state === "complete" || state === "completed") return theme.fg("success", "✓");
|
|
1119
1340
|
if (state === "failed") return theme.fg("error", "✗");
|
|
1341
|
+
if (state === "partial") return theme.fg("warning", "■");
|
|
1120
1342
|
if (state === "paused") return theme.fg("warning", "■");
|
|
1121
1343
|
if (state === "stopped") return theme.fg("warning", "■");
|
|
1122
1344
|
return theme.fg("muted", "◦");
|
|
@@ -1164,6 +1386,7 @@ function nestedActivity(input: Pick<NestedRunSummary | NestedStepSummary, "activ
|
|
|
1164
1386
|
if (state === "queued" || state === "pending") return "queued…";
|
|
1165
1387
|
if (state === "paused") return "Paused";
|
|
1166
1388
|
if (state === "stopped") return "Stopped";
|
|
1389
|
+
if (state === "partial") return "Partial";
|
|
1167
1390
|
if (state === "failed") return "Failed";
|
|
1168
1391
|
return "Done";
|
|
1169
1392
|
}
|
|
@@ -1188,7 +1411,7 @@ function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme
|
|
|
1188
1411
|
const activity = nestedActivity(step, state, snapshotNow ?? fallback);
|
|
1189
1412
|
const timestamp = "status" in step ? nestedStepTimestamp(step, fallback) : formatClockTime(nestedRunEventTime(step));
|
|
1190
1413
|
const error = step.error ? ` · ${step.error}` : "";
|
|
1191
|
-
const name = "status" in step ? step
|
|
1414
|
+
const name = "status" in step ? childDisplayName(step) : nestedRunName(step);
|
|
1192
1415
|
rows.push({
|
|
1193
1416
|
prefix,
|
|
1194
1417
|
text: `${nestedTimestampPrefix(timestamp)}${nestedStatusGlyph(state, theme)} ${name} · ${state}${modelThinking ? ` · ${modelThinking}` : ""}${activity ? ` · ${activity}` : ""}${error}`,
|
|
@@ -1250,7 +1473,7 @@ function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme
|
|
|
1250
1473
|
for (const step of child.steps ?? []) {
|
|
1251
1474
|
if (lines.length >= lineBudget) return;
|
|
1252
1475
|
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
1253
|
-
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)}`));
|
|
1254
1477
|
append(step.children, depth + 1, `${prefix} `);
|
|
1255
1478
|
}
|
|
1256
1479
|
append(child.children, depth + 1, `${prefix} `);
|
|
@@ -1274,7 +1497,9 @@ function foregroundStyleWidgetStepLines(
|
|
|
1274
1497
|
const status = widgetStepStatus(step.status, theme);
|
|
1275
1498
|
const stats = widgetStepStats(theme, step);
|
|
1276
1499
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1277
|
-
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, " "));
|
|
1278
1503
|
const task = compactTaskText(step.description, step.label);
|
|
1279
1504
|
if (task) lines.push(` ${theme.fg("dim", `task: ${task}`)}`);
|
|
1280
1505
|
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
@@ -1302,18 +1527,50 @@ function foregroundStyleWidgetStepLines(
|
|
|
1302
1527
|
return lines;
|
|
1303
1528
|
}
|
|
1304
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
|
+
|
|
1305
1555
|
function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded: boolean, width: number, frame?: number): string[] {
|
|
1306
|
-
if (!job.steps?.length)
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
+
}
|
|
1310
1566
|
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length) return widgetChainDetails(job, theme, expanded, width, frame);
|
|
1311
1567
|
const total = job.stepsTotal ?? job.steps.length;
|
|
1312
1568
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
1313
|
-
const lines: string[] =
|
|
1569
|
+
const lines: string[] = workflowPreflightLines(job, expanded);
|
|
1314
1570
|
for (const [index, step] of job.steps.entries()) {
|
|
1315
1571
|
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index + 1, total, expanded, width, frame));
|
|
1316
1572
|
}
|
|
1573
|
+
lines.push(...hostStepWidgetLines(job, theme, " "));
|
|
1317
1574
|
const attached = new Set(job.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
1318
1575
|
const unattached = job.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
1319
1576
|
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
@@ -1349,9 +1606,12 @@ function compactSingleWidgetLines(job: AsyncJobState, theme: Theme, width: numbe
|
|
|
1349
1606
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1350
1607
|
const task = compactTaskText(step.description, step.label);
|
|
1351
1608
|
const taskSuffix = task ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", `task: ${task}`)}` : "";
|
|
1352
|
-
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, " "));
|
|
1353
1612
|
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt, 6)) lines.push(` ${nestedLine}`);
|
|
1354
1613
|
}
|
|
1614
|
+
lines.push(...hostStepWidgetLines(job, theme, " "));
|
|
1355
1615
|
if (job.steps.some((step) => step.status === "running")) lines.push(theme.fg("accent", ` ${liveDetailHintText()}`));
|
|
1356
1616
|
return lines.map((line) => truncLine(line, width));
|
|
1357
1617
|
}
|
|
@@ -1488,9 +1748,18 @@ function progressiveJobLine(job: AsyncJobState, theme: Theme, width: number, fra
|
|
|
1488
1748
|
const stats = widgetStats(job, theme);
|
|
1489
1749
|
const activity = widgetActivity(job);
|
|
1490
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
|
+
: "";
|
|
1491
1758
|
const parts = [
|
|
1492
1759
|
`${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}`,
|
|
1493
1760
|
theme.fg("dim", status),
|
|
1761
|
+
laneSignals ? laneSignals : "",
|
|
1762
|
+
laneSummary ? theme.fg("dim", laneSummary) : "",
|
|
1494
1763
|
stats,
|
|
1495
1764
|
activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : "",
|
|
1496
1765
|
].filter(Boolean);
|
|
@@ -1636,6 +1905,7 @@ export function buildWidgetLines(jobs: AsyncJobState[], theme: Theme, width = ge
|
|
|
1636
1905
|
items.push([
|
|
1637
1906
|
`${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1638
1907
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1908
|
+
...widgetLaneDetailLines(job, theme),
|
|
1639
1909
|
...widgetParallelAgentDetails(job, theme, expanded, width, frame),
|
|
1640
1910
|
]);
|
|
1641
1911
|
slots--;
|
|
@@ -1653,6 +1923,7 @@ export function buildWidgetLines(jobs: AsyncJobState[], theme: Theme, width = ge
|
|
|
1653
1923
|
items.push([
|
|
1654
1924
|
`${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1655
1925
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1926
|
+
...widgetLaneDetailLines(job, theme),
|
|
1656
1927
|
...widgetParallelAgentDetails(job, theme, expanded, width, frame),
|
|
1657
1928
|
]);
|
|
1658
1929
|
slots--;
|
|
@@ -1720,7 +1991,7 @@ function renderSingleCompact(
|
|
|
1720
1991
|
const detailIndent = mainWindowIndent(layout, 1);
|
|
1721
1992
|
const continuationIndent = mainWindowIndent(layout, 2) + (layout.horizontalSpacing > 0 ? " " : "");
|
|
1722
1993
|
const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
|
|
1723
|
-
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));
|
|
1724
1995
|
|
|
1725
1996
|
if (isRunning && r.progress) {
|
|
1726
1997
|
const task = compactTaskText(r.task);
|
|
@@ -1753,6 +2024,7 @@ function renderSingleCompact(
|
|
|
1753
2024
|
}
|
|
1754
2025
|
|
|
1755
2026
|
function workflowRowGlyph(row: WorkflowChatProgressRow, theme: Theme, frame?: number): string {
|
|
2027
|
+
if (row.state === "planned") return theme.fg("muted", "◦");
|
|
1756
2028
|
if (row.state === "running") return theme.fg("accent", runningGlyph(frame));
|
|
1757
2029
|
if (row.state === "complete") return theme.fg("success", "✓");
|
|
1758
2030
|
if (row.state === "detached" || row.state === "stopped") return theme.fg("warning", "■");
|
|
@@ -1761,6 +2033,7 @@ function workflowRowGlyph(row: WorkflowChatProgressRow, theme: Theme, frame?: nu
|
|
|
1761
2033
|
|
|
1762
2034
|
function workflowRowStateLabel(row: WorkflowChatProgressRow, theme: Theme): string {
|
|
1763
2035
|
const label = (row.state === "complete" ? "complete" : row.state).padEnd(8);
|
|
2036
|
+
if (row.state === "planned") return theme.fg("dim", label);
|
|
1764
2037
|
if (row.state === "running") return theme.fg("accent", label);
|
|
1765
2038
|
if (row.state === "complete") return theme.fg("success", label);
|
|
1766
2039
|
if (row.state === "detached" || row.state === "stopped") return theme.fg("warning", label);
|
|
@@ -1775,9 +2048,9 @@ function workflowOverallState(rows: WorkflowChatProgressRow[], hasTerminalValue:
|
|
|
1775
2048
|
return "running";
|
|
1776
2049
|
}
|
|
1777
2050
|
|
|
1778
|
-
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 {
|
|
1779
2052
|
const workflow = d.workflow;
|
|
1780
|
-
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace) : [];
|
|
2053
|
+
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace, d.preflight) : d.preflight ? buildWorkflowChatProgressRows([], d.preflight) : [];
|
|
1781
2054
|
const state = workflowOverallState(rows, workflow?.value !== undefined, result.isError);
|
|
1782
2055
|
const glyph = state === "running" ? theme.fg("accent", runningGlyph(frame)) : state === "complete" ? theme.fg("success", "✓") : state === "paused" ? theme.fg("warning", "■") : theme.fg("error", "✗");
|
|
1783
2056
|
const width = getTermWidth() - 4;
|
|
@@ -1788,6 +2061,7 @@ function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>
|
|
|
1788
2061
|
const rowIndent = mainWindowIndent(layout, 1);
|
|
1789
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));
|
|
1790
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));
|
|
1791
2065
|
if (phase) c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Phase ${phase}`), width), 0, 0));
|
|
1792
2066
|
if (rows.length === 0) {
|
|
1793
2067
|
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}◦ waiting for workflow child launches`), width), 0, 0));
|
|
@@ -1801,7 +2075,20 @@ function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>
|
|
|
1801
2075
|
const duration = row.durationMs !== undefined ? ` ${theme.fg("dim", `· ${formatDuration(row.durationMs)}`)}` : "";
|
|
1802
2076
|
const run = row.runId ? ` ${theme.fg("dim", `[${row.runId.slice(0, 8)}]`)}` : "";
|
|
1803
2077
|
const error = row.error ? ` ${theme.fg(row.state === "detached" ? "warning" : "error", `· ${compactWorkflowError(row.error)}`)}` : "";
|
|
1804
|
-
|
|
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));
|
|
1805
2092
|
}
|
|
1806
2093
|
if (workflow?.emits.length) c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Emits ${workflow.emits.length}`), width), 0, 0));
|
|
1807
2094
|
return c;
|
|
@@ -1817,6 +2104,7 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1817
2104
|
|| workflowGraphHasStatus(d, ["failed"]);
|
|
1818
2105
|
const paused = d.results.some((r) => r.interrupted)
|
|
1819
2106
|
|| workflowGraphHasStatus(d, ["paused"]);
|
|
2107
|
+
const partial = workflowGraphHasStatus(d, ["partial"]);
|
|
1820
2108
|
let totalSummary = d.progressSummary;
|
|
1821
2109
|
if (!totalSummary) {
|
|
1822
2110
|
let sawProgress = false;
|
|
@@ -1840,6 +2128,7 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1840
2128
|
stopped,
|
|
1841
2129
|
interrupted: paused,
|
|
1842
2130
|
failed,
|
|
2131
|
+
partial,
|
|
1843
2132
|
seed: runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex),
|
|
1844
2133
|
frame,
|
|
1845
2134
|
});
|
|
@@ -1849,7 +2138,6 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1849
2138
|
const width = getTermWidth() - 4;
|
|
1850
2139
|
const rowIndent = mainWindowIndent(layout, 1);
|
|
1851
2140
|
const detailIndent = mainWindowIndent(layout, 2);
|
|
1852
|
-
const continuationIndent = mainWindowIndent(layout, 3) + (layout.horizontalSpacing > 0 ? " " : "");
|
|
1853
2141
|
c.addChild(new Text(truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1854
2142
|
|
|
1855
2143
|
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
@@ -1861,7 +2149,8 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
1861
2149
|
const r = d.results[i];
|
|
1862
2150
|
const fallbackLabel = itemTitle.toLowerCase();
|
|
1863
2151
|
const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
|
|
1864
|
-
|
|
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 };
|
|
1865
2154
|
});
|
|
1866
2155
|
for (const entry of renderEntries) {
|
|
1867
2156
|
if (entry.kind === "placeholder") {
|
|
@@ -1943,7 +2232,8 @@ export function renderSubagentSummary(
|
|
|
1943
2232
|
const failed = result.isError === true
|
|
1944
2233
|
|| results.some((entry) => !hasTerminalResultFlag(entry) && entry.exitCode !== 0 && !isResultRunning(entry))
|
|
1945
2234
|
|| Boolean(details && workflowGraphHasStatus(details, ["failed"]));
|
|
1946
|
-
const
|
|
2235
|
+
const partial = Boolean(details && workflowGraphHasStatus(details, ["partial"]));
|
|
2236
|
+
const state = running ? "running" : failed ? "failed" : stopped ? "stopped" : paused ? "paused" : partial ? "partial" : "completed";
|
|
1947
2237
|
const glyph = state === "running"
|
|
1948
2238
|
? theme.fg("accent", STATIC_RUNNING_GLYPH)
|
|
1949
2239
|
: state === "completed"
|
|
@@ -1952,7 +2242,7 @@ export function renderSubagentSummary(
|
|
|
1952
2242
|
? theme.fg("error", "✗")
|
|
1953
2243
|
: theme.fg("warning", "■");
|
|
1954
2244
|
const label = details?.mode === "single" && results.length === 1
|
|
1955
|
-
? results[0]
|
|
2245
|
+
? childDisplayName(results[0])
|
|
1956
2246
|
: details?.mode || "subagent";
|
|
1957
2247
|
return new Text(
|
|
1958
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),
|
|
@@ -1976,7 +2266,7 @@ export function renderSubagentResult(
|
|
|
1976
2266
|
const compact = (component: Component): Component => capCompactMainWindowResult(component, layout, theme, !options.expanded);
|
|
1977
2267
|
const d = result.details;
|
|
1978
2268
|
if (d?.mode === "workflow" && d.chatProgress?.mode === "live-card" && d.workflow?.value === undefined && (!result.isError || (d.workflow?.trace.length ?? 0) > 0)) {
|
|
1979
|
-
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));
|
|
1980
2270
|
}
|
|
1981
2271
|
if (!d || !d.results.length) {
|
|
1982
2272
|
const t = result.content[0];
|
|
@@ -1987,9 +2277,12 @@ export function renderSubagentResult(
|
|
|
1987
2277
|
if (d && !options.expanded && !result.isError) {
|
|
1988
2278
|
const lines = text.split(/\r?\n/);
|
|
1989
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;
|
|
1990
2283
|
const c = new Container();
|
|
1991
2284
|
const detailIndent = mainWindowIndent(layout, 1);
|
|
1992
|
-
c.addChild(new Text(truncLine(`${contextPrefix}${
|
|
2285
|
+
c.addChild(new Text(truncLine(`${contextPrefix}${compactLine} · ${lines.length} lines`, width), 0, 0));
|
|
1993
2286
|
c.addChild(new Text(truncLine(theme.fg("accent", `${detailIndent}Press ${liveDetailKeyText()} for full output`), width), 0, 0));
|
|
1994
2287
|
return compact(c);
|
|
1995
2288
|
}
|
|
@@ -2022,7 +2315,7 @@ export function renderSubagentResult(
|
|
|
2022
2315
|
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
2023
2316
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
2024
2317
|
const c = new Container();
|
|
2025
|
-
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));
|
|
2026
2319
|
c.addChild(new Spacer(1));
|
|
2027
2320
|
const taskMaxLen = Math.max(20, w - 8);
|
|
2028
2321
|
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
@@ -2110,6 +2403,7 @@ export function renderSubagentResult(
|
|
|
2110
2403
|
|| workflowGraphHasStatus(d, ["failed"]);
|
|
2111
2404
|
const paused = d.results.some((r) => r.interrupted)
|
|
2112
2405
|
|| workflowGraphHasStatus(d, ["paused"]);
|
|
2406
|
+
const partial = workflowGraphHasStatus(d, ["partial"]);
|
|
2113
2407
|
const completedWithoutOutput = d.results.some((r) =>
|
|
2114
2408
|
!hasTerminalResultFlag(r)
|
|
2115
2409
|
&& r.exitCode === 0
|
|
@@ -2122,6 +2416,7 @@ export function renderSubagentResult(
|
|
|
2122
2416
|
stopped,
|
|
2123
2417
|
interrupted: paused,
|
|
2124
2418
|
failed,
|
|
2419
|
+
partial,
|
|
2125
2420
|
completedWithoutOutput,
|
|
2126
2421
|
frame,
|
|
2127
2422
|
}), theme);
|
|
@@ -2161,6 +2456,7 @@ export function renderSubagentResult(
|
|
|
2161
2456
|
? d.chainAgents
|
|
2162
2457
|
.map((agent, i) => {
|
|
2163
2458
|
const result = d.results[i];
|
|
2459
|
+
const displayName = result?.sessionName?.trim() || agent;
|
|
2164
2460
|
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
2165
2461
|
const stepPresentation = result
|
|
2166
2462
|
? styledResultPresentation(resultPresentation(result, getSingleResultOutput(result), isCurrent && hasRunning && !hasTerminalResultFlag(result)), theme)
|
|
@@ -2168,7 +2464,7 @@ export function renderSubagentResult(
|
|
|
2168
2464
|
const stepStatus = stepPresentation
|
|
2169
2465
|
? `${stepPresentation.glyph} ${stepPresentation.label}`
|
|
2170
2466
|
: theme.fg("dim", "◦ pending");
|
|
2171
|
-
return `${stepStatus} ${
|
|
2467
|
+
return `${stepStatus} ${displayName}${contextModeBadge(theme, result?.context)}`;
|
|
2172
2468
|
})
|
|
2173
2469
|
.join(theme.fg("dim", " → "))
|
|
2174
2470
|
: null;
|
|
@@ -2195,7 +2491,8 @@ export function renderSubagentResult(
|
|
|
2195
2491
|
const i = displayStart + offset;
|
|
2196
2492
|
const r = d.results[i];
|
|
2197
2493
|
const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
|
|
2198
|
-
|
|
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 };
|
|
2199
2496
|
});
|
|
2200
2497
|
|
|
2201
2498
|
c.addChild(new Spacer(1));
|
|
@@ -2235,8 +2532,8 @@ export function renderSubagentResult(
|
|
|
2235
2532
|
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
2236
2533
|
const contextBadge = contextModeBadge(theme, r.context);
|
|
2237
2534
|
const stepHeader = rRunning
|
|
2238
|
-
? `${rowPresentation.glyph} ${stepLabel}: ${theme.bold(theme.fg("warning", r
|
|
2239
|
-
: `${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}`;
|
|
2240
2537
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
2241
2538
|
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
2242
2539
|
|