@duckmind/dm-windows-x64 0.61.4 → 0.61.9
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +211 -67
- package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
- package/extensions/dm-subagents/agents/claude-code.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
- package/extensions/dm-subagents/agents/codex-exec.md +15 -0
- package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
- package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
- package/extensions/dm-subagents/agents/delegate.md +3 -2
- package/extensions/dm-subagents/agents/oracle.md +10 -5
- package/extensions/dm-subagents/agents/researcher.md +2 -2
- package/extensions/dm-subagents/agents/reviewer.md +17 -7
- package/extensions/dm-subagents/agents/scout.md +5 -5
- package/extensions/dm-subagents/agents/worker.md +6 -2
- package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
- package/extensions/dm-subagents/index.js +4 -0
- package/extensions/dm-subagents/inspector-runner.mjs +10 -0
- package/extensions/dm-subagents/install.mjs +3 -2
- package/extensions/dm-subagents/package.json +2 -2
- package/extensions/dm-subagents/prompts/council.md +60 -0
- package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
- package/extensions/dm-subagents/prompts/review-loop.md +13 -7
- package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
- package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
- package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
- package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
- package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
- package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
- package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
- package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
- package/extensions/dm-subagents/src/agents/agents.js +1447 -299
- package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
- package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
- package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
- package/extensions/dm-subagents/src/agents/identity.js +1 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
- package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
- package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
- package/extensions/dm-subagents/src/agents/skills.js +52 -35
- package/extensions/dm-subagents/src/api/agents.js +6 -0
- package/extensions/dm-subagents/src/api/background-work.js +151 -0
- package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
- package/extensions/dm-subagents/src/api/control-channel.js +3 -0
- package/extensions/dm-subagents/src/api/delegation.js +5 -0
- package/extensions/dm-subagents/src/api/dm-args.js +3 -0
- package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
- package/extensions/dm-subagents/src/api/external-runs.js +233 -0
- package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
- package/extensions/dm-subagents/src/api/preflight.js +322 -0
- package/extensions/dm-subagents/src/api/project-panes.js +11 -0
- package/extensions/dm-subagents/src/api/shared-types.js +4 -0
- package/extensions/dm-subagents/src/extension/config.js +175 -0
- package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
- package/extensions/dm-subagents/src/extension/doctor.js +71 -17
- package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
- package/extensions/dm-subagents/src/extension/index.js +711 -265
- package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
- package/extensions/dm-subagents/src/extension/rpc.js +427 -22
- package/extensions/dm-subagents/src/extension/schemas.js +158 -65
- package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
- package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
- package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
- package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
- package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
- package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
- package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
- package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
- package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
- package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
- package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
- package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
- package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
- package/extensions/dm-subagents/src/missions/actions.js +394 -0
- package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
- package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
- package/extensions/dm-subagents/src/missions/store.js +548 -0
- package/extensions/dm-subagents/src/missions/types.js +9 -0
- package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
- package/extensions/dm-subagents/src/policy/authority.js +37 -0
- package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
- package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
- package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
- package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
- package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
- package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
- package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
- package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
- package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
- package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
- package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
- package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
- package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
- package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
- package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
- package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
- package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
- package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
- package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
- package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
- package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
- package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
- package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
- package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
- package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
- package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
- package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
- package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
- package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
- package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
- package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
- package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
- package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
- package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
- package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
- package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
- package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
- package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
- package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
- package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
- package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
- package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
- package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
- package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
- package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
- package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
- package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
- package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
- package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
- package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
- package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
- package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
- package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
- package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
- package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
- package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
- package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
- package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
- package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
- package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
- package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
- package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
- package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
- package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
- package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
- package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
- package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
- package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
- package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
- package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
- package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
- package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
- package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
- package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
- package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
- package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
- package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
- package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
- package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
- package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
- package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
- package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
- package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
- package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
- package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
- package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
- package/extensions/dm-subagents/src/shared/display-text.js +142 -0
- package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
- package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
- package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
- package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
- package/extensions/dm-subagents/src/shared/formatters.js +21 -7
- package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
- package/extensions/dm-subagents/src/shared/model-info.js +10 -5
- package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
- package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
- package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
- package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
- package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
- package/extensions/dm-subagents/src/shared/settings.js +39 -47
- package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
- package/extensions/dm-subagents/src/shared/status-format.js +9 -2
- package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
- package/extensions/dm-subagents/src/shared/types.js +47 -7
- package/extensions/dm-subagents/src/shared/utf8.js +12 -0
- package/extensions/dm-subagents/src/shared/utils.js +151 -131
- package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
- package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
- package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
- package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
- package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
- package/extensions/dm-subagents/src/slash/selector.js +101 -0
- package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
- package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
- package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
- package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
- package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
- package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
- package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
- package/extensions/dm-subagents/src/tui/render.js +1511 -261
- package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
- package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
- package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
- package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
- package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
- package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
- package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
- package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
- package/extensions/dm-subagents/src/watchdog/render.js +54 -0
- package/extensions/dm-subagents/src/watchdog/review.js +251 -0
- package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
- package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
- package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
- package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
- package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
- package/extensions/dm-subagents/src/watchdog/types.js +29 -0
- package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
- package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
- package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
- package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
- package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
- package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
- package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
- package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
- package/package.json +4 -3
- package/extensions/dm-fff/package.json +0 -21
- package/extensions/dm-fff/src/index.js +0 -691
- package/extensions/dm-fff/src/query.js +0 -60
- package/extensions/dm-subagents/agents/context-builder.md +0 -46
- package/extensions/dm-subagents/agents/planner.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
- package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
- package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
|
@@ -1,26 +1,74 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { getMarkdownTheme, keyText } from "@duckmind/dm-coding-agent";
|
|
3
4
|
import { Container, Markdown, Spacer, Text, visibleWidth } from "@duckmind/dm-tui";
|
|
4
5
|
import {
|
|
5
6
|
MAX_WIDGET_JOBS,
|
|
7
|
+
WIDGET_ANIMATION_INTERVAL_MS,
|
|
6
8
|
WIDGET_KEY
|
|
7
9
|
} from "../shared/types.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
+
import { previewDisplayText, sanitizeDisplayText, truncateDisplayText } from "../shared/display-text.js";
|
|
11
|
+
import { FLEET_OPEN_SHORTCUT, formatShortcutLabel } from "../shared/shortcuts.js";
|
|
12
|
+
import { formatContextUsage, formatTokens, formatUsage, formatDuration, formatModelThinking, formatToolCall, formatTokenUsage, shortenPath } from "../shared/formatters.js";
|
|
13
|
+
import { getDisplayItems, getSingleResultOutput, PROMPT_REDACTED } from "../shared/utils.js";
|
|
10
14
|
import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.js";
|
|
11
15
|
import { formatNestedAggregate } from "../runs/shared/nested-render.js";
|
|
12
16
|
import { aggregateStepStatus, formatActivityLabel, formatAgentRunningLabel, formatParallelOutcome } from "../shared/status-format.js";
|
|
17
|
+
import { contextModeBadge, contextModePrefix } from "../runs/shared/context-mode.js";
|
|
18
|
+
import { shouldSuppressSingleStep, stripRepeatedAgentPrefix, withDuplicateLabelDiscriminators } from "./render-helpers.js";
|
|
19
|
+
import { buildWorkflowChatProgressRows } from "../workflows/chat-progress.js";
|
|
20
|
+
import { formatWorkflowPreflight, formatWorkflowPreflightPlanSummary, formatWorkflowPreflightWarningSummary, formatWorkflowPreflightWarnings } from "../workflows/workflow-preflight.js";
|
|
21
|
+
import { encodeAsyncStatusSnapshotWidget } from "../runs/background/async-status-snapshot.js";
|
|
22
|
+
import { projectAsyncWorkflowRows } from "../runs/shared/async-status-projection.js";
|
|
23
|
+
import { hostStepReportName, hostStepVerdictLabel } from "../runs/shared/host-step-status.js";
|
|
24
|
+
import { workflowGraphStageNodes } from "../runs/shared/workflow-graph.js";
|
|
25
|
+
function resolveMainWindowRenderLayout(config) {
|
|
26
|
+
return {
|
|
27
|
+
horizontalSpacing: config?.horizontalSpacing ?? 2,
|
|
28
|
+
...config?.compactResultMaxLines !== undefined ? { compactResultMaxLines: config.compactResultMaxLines } : {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function mainWindowIndent(layout, level) {
|
|
32
|
+
return " ".repeat(Math.max(0, layout.horizontalSpacing * level));
|
|
33
|
+
}
|
|
34
|
+
function capCompactMainWindowResult(component, layout, theme, enabled) {
|
|
35
|
+
const maxLines = layout.compactResultMaxLines;
|
|
36
|
+
if (!enabled || maxLines === undefined)
|
|
37
|
+
return component;
|
|
38
|
+
const capped = new Container;
|
|
39
|
+
capped.render = (width) => {
|
|
40
|
+
const lines = component.render(width);
|
|
41
|
+
if (lines.length <= maxLines)
|
|
42
|
+
return lines;
|
|
43
|
+
const visibleRows = maxLines === 1 ? 1 : maxLines - 1;
|
|
44
|
+
const hiddenCount = lines.length - visibleRows;
|
|
45
|
+
const hint = theme.fg("accent", `… ${hiddenCount} rows hidden · ${liveDetailKeyText()} expands`);
|
|
46
|
+
if (maxLines === 1)
|
|
47
|
+
return [truncLine(`${lines[0] ?? ""} ${hint}`, width)];
|
|
48
|
+
return [...lines.slice(0, visibleRows), truncLine(hint, width)];
|
|
49
|
+
};
|
|
50
|
+
return capped;
|
|
51
|
+
}
|
|
13
52
|
function liveDetailKeyText() {
|
|
14
53
|
return keyText("app.tools.expand");
|
|
15
54
|
}
|
|
16
|
-
function liveDetailHintText() {
|
|
17
|
-
return `Press ${liveDetailKeyText()} for live detail`;
|
|
55
|
+
export function liveDetailHintText() {
|
|
56
|
+
return `Press ${liveDetailKeyText()} for live detail · ${formatShortcutLabel(FLEET_OPEN_SHORTCUT)} Fleet`;
|
|
57
|
+
}
|
|
58
|
+
function foregroundSingleHintText(shortcut) {
|
|
59
|
+
if (!shortcut)
|
|
60
|
+
return liveDetailHintText();
|
|
61
|
+
const label = formatShortcutLabel(shortcut);
|
|
62
|
+
return `${liveDetailHintText()} · ${label} to run in background`;
|
|
18
63
|
}
|
|
19
64
|
function getTermWidth() {
|
|
20
65
|
return process.stdout.columns || 120;
|
|
21
66
|
}
|
|
22
67
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
23
|
-
|
|
68
|
+
const ansiStylePattern = /\x1b\[[0-9;]*m/y;
|
|
69
|
+
export function truncLine(text, maxWidth) {
|
|
70
|
+
if (maxWidth <= 0)
|
|
71
|
+
return "";
|
|
24
72
|
if (visibleWidth(text) <= maxWidth)
|
|
25
73
|
return text;
|
|
26
74
|
const targetWidth = maxWidth - 1;
|
|
@@ -29,7 +77,8 @@ function truncLine(text, maxWidth) {
|
|
|
29
77
|
let activeStyles = [];
|
|
30
78
|
let i = 0;
|
|
31
79
|
while (i < text.length) {
|
|
32
|
-
|
|
80
|
+
ansiStylePattern.lastIndex = i;
|
|
81
|
+
const ansiMatch = ansiStylePattern.exec(text);
|
|
33
82
|
if (ansiMatch) {
|
|
34
83
|
const code = ansiMatch[0];
|
|
35
84
|
result += code;
|
|
@@ -41,14 +90,15 @@ function truncLine(text, maxWidth) {
|
|
|
41
90
|
i += code.length;
|
|
42
91
|
continue;
|
|
43
92
|
}
|
|
44
|
-
let end = i;
|
|
45
|
-
|
|
46
|
-
end
|
|
47
|
-
|
|
93
|
+
let end = text.indexOf("\x1B[", i);
|
|
94
|
+
if (end === i)
|
|
95
|
+
end = text.indexOf("\x1B[", i + 2);
|
|
96
|
+
if (end === -1)
|
|
97
|
+
end = text.length;
|
|
48
98
|
const textPortion = text.slice(i, end);
|
|
49
99
|
for (const seg of segmenter.segment(textPortion)) {
|
|
50
100
|
const grapheme = seg.segment;
|
|
51
|
-
const graphemeWidth = visibleWidth(grapheme);
|
|
101
|
+
const graphemeWidth = grapheme === "\x1B" ? 0 : visibleWidth(grapheme);
|
|
52
102
|
if (currentWidth + graphemeWidth > targetWidth) {
|
|
53
103
|
return result + activeStyles.join("") + "…";
|
|
54
104
|
}
|
|
@@ -74,6 +124,8 @@ function wrapPlainText(text, maxWidth) {
|
|
|
74
124
|
for (const seg of segmenter.segment(rawLine)) {
|
|
75
125
|
const grapheme = seg.segment;
|
|
76
126
|
const graphemeWidth = visibleWidth(grapheme);
|
|
127
|
+
if (graphemeWidth > maxWidth)
|
|
128
|
+
continue;
|
|
77
129
|
if (currentWidth > 0 && currentWidth + graphemeWidth > maxWidth) {
|
|
78
130
|
lines.push(current);
|
|
79
131
|
current = grapheme;
|
|
@@ -101,7 +153,13 @@ function runningSeed(...values) {
|
|
|
101
153
|
function runningGlyph(seed) {
|
|
102
154
|
if (seed === undefined)
|
|
103
155
|
return STATIC_RUNNING_GLYPH;
|
|
104
|
-
|
|
156
|
+
const clock = Math.floor(Date.now() / 125);
|
|
157
|
+
return RUNNING_FRAMES[Math.abs(seed + clock) % RUNNING_FRAMES.length];
|
|
158
|
+
}
|
|
159
|
+
function animatedSeed(seed, frame) {
|
|
160
|
+
if (frame === undefined)
|
|
161
|
+
return seed;
|
|
162
|
+
return (seed ?? 0) + frame;
|
|
105
163
|
}
|
|
106
164
|
function progressRunningSeed(progress) {
|
|
107
165
|
if (!progress)
|
|
@@ -138,16 +196,487 @@ function getToolCallLines(result, expanded) {
|
|
|
138
196
|
}
|
|
139
197
|
return result.toolCalls?.map((toolCall) => expanded ? toolCall.expandedText : toolCall.text) ?? [];
|
|
140
198
|
}
|
|
199
|
+
const ansiEscapePattern = /\x1b\[[0-9;]*m/g;
|
|
200
|
+
const noisyStatusPatterns = [
|
|
201
|
+
/^(?:i|we)\s+(?:will|need|can|should|am|are)\b/i,
|
|
202
|
+
/^i(?:'m|’m| am)\b/i,
|
|
203
|
+
/\bso i (?:will|need|can)\b/i,
|
|
204
|
+
/^(?:checking|fetching|reading|inspecting|verifying|collecting|confirming|polling)\b/i,
|
|
205
|
+
/^(?:async\s+subagent\s+)?[\w.-]+\s*·\s*(?:step|agent)\s+\d+\/\d+\s*·/i,
|
|
206
|
+
/^(?:Step|Agent)\s+\d+\/\d+:\s+[\w.-]+\s*·\s*(?:running|queued|pending|complete|completed)\b/i,
|
|
207
|
+
/^(?:async\s+subagent\s+)?[\w.-]+(?:\s+\[(?:fresh|fork|mixed)\])?\s*·\s*(?:running|queued|pending|complete|completed|done)\b/i,
|
|
208
|
+
/^Press\s+\S+\s+for\s+live\s+detail$/i,
|
|
209
|
+
/^output:\s+.+\/async-subagent-runs\//i
|
|
210
|
+
];
|
|
211
|
+
const liveOutputWordSignalPattern = /\b(?:access denied|denied|error|exception|fail(?:ed|ure)?|fatal|panic|rejected|timeout|timed out|unable|warning)\b/i;
|
|
212
|
+
const liveOutputCodeSignalPattern = /\bE[A-Z0-9_]{2,}\b/;
|
|
213
|
+
function oneLine(text) {
|
|
214
|
+
return text.replace(ansiEscapePattern, "").replace(/\s+/g, " ").trim();
|
|
215
|
+
}
|
|
216
|
+
const COMPACT_TASK_MAX_CHARS = 96;
|
|
217
|
+
function childDisplayName(result, fallback = "subagent") {
|
|
218
|
+
return result?.sessionName?.trim() || result?.agent || fallback;
|
|
219
|
+
}
|
|
220
|
+
export function compactTaskText(task, label) {
|
|
221
|
+
const taskText = task?.trim();
|
|
222
|
+
const labelText = label?.trim();
|
|
223
|
+
const normalizedTask = taskText && taskText !== PROMPT_REDACTED ? oneLine(taskText) : "";
|
|
224
|
+
const normalizedLabel = labelText && labelText !== PROMPT_REDACTED ? oneLine(labelText) : "";
|
|
225
|
+
const normalized = normalizedLabel && normalizedTask && normalizedLabel !== normalizedTask ? `${normalizedLabel} — ${normalizedTask}` : normalizedLabel || normalizedTask;
|
|
226
|
+
if (!normalized)
|
|
227
|
+
return;
|
|
228
|
+
return previewDisplayText(normalized, COMPACT_TASK_MAX_CHARS);
|
|
229
|
+
}
|
|
230
|
+
const LANE_VALUE_MAX_CHARS = 48;
|
|
231
|
+
function boundedLaneValue(value, maxChars = LANE_VALUE_MAX_CHARS) {
|
|
232
|
+
if (!value?.trim() || value.trim() === PROMPT_REDACTED)
|
|
233
|
+
return;
|
|
234
|
+
return previewDisplayText(oneLine(value), maxChars);
|
|
235
|
+
}
|
|
236
|
+
function workflowNodeStepStatus(status) {
|
|
237
|
+
switch (status) {
|
|
238
|
+
case "completed":
|
|
239
|
+
return "completed";
|
|
240
|
+
case "detached":
|
|
241
|
+
return "paused";
|
|
242
|
+
default:
|
|
243
|
+
return status;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function workflowNodeStatusLabel(status) {
|
|
247
|
+
switch (status) {
|
|
248
|
+
case "completed":
|
|
249
|
+
return "complete";
|
|
250
|
+
case "detached":
|
|
251
|
+
return "paused";
|
|
252
|
+
default:
|
|
253
|
+
return status;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function workflowStepPriority(step, currentNodeId) {
|
|
257
|
+
const isCurrent = step.workflowKey === currentNodeId;
|
|
258
|
+
if (step.status === "running" || isCurrent && step.status !== "complete" && step.status !== "completed")
|
|
259
|
+
return 0;
|
|
260
|
+
const gate = laneGate(step);
|
|
261
|
+
if (step.status === "failed" || step.status === "partial" || step.status === "paused" || step.status === "stopped" || step.status === "rejected" || step.toolBudgetBlocked === true || step.turnBudgetExceeded === true || step.activityState === "needs_attention" || step.watchdog?.phase === "stale" || gate !== undefined)
|
|
262
|
+
return 1;
|
|
263
|
+
if (step.status === "pending")
|
|
264
|
+
return 2;
|
|
265
|
+
return 3;
|
|
266
|
+
}
|
|
267
|
+
function workflowWidgetSteps(job, planned = job.mode === "workflow" ? workflowGraphStageNodes(job.workflowGraph) : []) {
|
|
268
|
+
const loaded = job.steps ?? [];
|
|
269
|
+
if (planned.length === 0)
|
|
270
|
+
return loaded;
|
|
271
|
+
const loadedIndexesByKey = new Map;
|
|
272
|
+
for (const [index, step] of loaded.entries()) {
|
|
273
|
+
if (!step.workflowKey)
|
|
274
|
+
continue;
|
|
275
|
+
const indexes = loadedIndexesByKey.get(step.workflowKey) ?? [];
|
|
276
|
+
indexes.push(index);
|
|
277
|
+
loadedIndexesByKey.set(step.workflowKey, indexes);
|
|
278
|
+
}
|
|
279
|
+
const consumed = new Set;
|
|
280
|
+
const entries = [];
|
|
281
|
+
for (const [order, node] of planned.entries()) {
|
|
282
|
+
const loadedIndex = loadedIndexesByKey.get(node.id)?.find((index) => !consumed.has(index));
|
|
283
|
+
if (loadedIndex !== undefined) {
|
|
284
|
+
consumed.add(loadedIndex);
|
|
285
|
+
const loadedStep = loaded[loadedIndex];
|
|
286
|
+
entries.push({
|
|
287
|
+
step: {
|
|
288
|
+
...loadedStep,
|
|
289
|
+
index: node.flatIndex ?? loadedStep.index ?? order,
|
|
290
|
+
agent: loadedStep.agent || node.agent || job.agents?.[0] || "workflow",
|
|
291
|
+
phase: loadedStep.phase ?? node.phase,
|
|
292
|
+
label: loadedStep.label ?? node.label,
|
|
293
|
+
workflowKey: loadedStep.workflowKey ?? node.id,
|
|
294
|
+
...loadedStep.outputName === undefined && node.outputName !== undefined ? { outputName: node.outputName } : {},
|
|
295
|
+
...loadedStep.structured === undefined && node.structured !== undefined ? { structured: node.structured } : {},
|
|
296
|
+
...loadedStep.error === undefined && node.error !== undefined ? { error: node.error } : {}
|
|
297
|
+
},
|
|
298
|
+
order
|
|
299
|
+
});
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
entries.push({
|
|
303
|
+
step: {
|
|
304
|
+
index: node.flatIndex ?? order,
|
|
305
|
+
agent: node.agent ?? job.agents?.[0] ?? "workflow",
|
|
306
|
+
status: workflowNodeStepStatus(node.status),
|
|
307
|
+
workflowKey: node.id,
|
|
308
|
+
label: node.label,
|
|
309
|
+
...node.phase ? { phase: node.phase } : {},
|
|
310
|
+
...node.outputName ? { outputName: node.outputName } : {},
|
|
311
|
+
...node.structured !== undefined ? { structured: node.structured } : {},
|
|
312
|
+
...node.error ? { error: node.error } : {}
|
|
313
|
+
},
|
|
314
|
+
order
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
for (const [index, step] of loaded.entries()) {
|
|
318
|
+
if (!consumed.has(index))
|
|
319
|
+
entries.push({ step, order: planned.length + index });
|
|
320
|
+
}
|
|
321
|
+
entries.sort((left, right) => workflowStepPriority(left.step, job.workflowGraph?.currentNodeId) - workflowStepPriority(right.step, job.workflowGraph?.currentNodeId) || left.order - right.order);
|
|
322
|
+
return entries.map(({ step }) => step);
|
|
323
|
+
}
|
|
324
|
+
function workflowStageProgress(job, stages = job.mode === "workflow" ? workflowGraphStageNodes(job.workflowGraph) : []) {
|
|
325
|
+
if (job.mode !== "workflow")
|
|
326
|
+
return;
|
|
327
|
+
if (stages.length === 0)
|
|
328
|
+
return;
|
|
329
|
+
const currentId = job.workflowGraph?.currentNodeId;
|
|
330
|
+
const currentIndex = currentId ? stages.findIndex((stage) => stage.id === currentId) : -1;
|
|
331
|
+
if (currentIndex >= 0 && stages[currentIndex]?.status !== "completed")
|
|
332
|
+
return { total: stages.length, current: currentIndex };
|
|
333
|
+
const runningIndex = stages.findIndex((stage) => stage.status === "running");
|
|
334
|
+
if (runningIndex >= 0)
|
|
335
|
+
return { total: stages.length, current: runningIndex };
|
|
336
|
+
const indexedStage = job.currentStep !== undefined && job.currentStep >= 0 ? stages[job.currentStep] : undefined;
|
|
337
|
+
if (indexedStage && indexedStage.status !== "completed")
|
|
338
|
+
return { total: stages.length, current: job.currentStep };
|
|
339
|
+
if (stages.every((stage) => stage.status === "completed"))
|
|
340
|
+
return { total: stages.length, current: stages.length - 1 };
|
|
341
|
+
return { total: stages.length };
|
|
342
|
+
}
|
|
343
|
+
function buildWorkflowWidgetProjection(job) {
|
|
344
|
+
const stages = job.mode === "workflow" ? workflowGraphStageNodes(job.workflowGraph) : [];
|
|
345
|
+
const stageProgress = workflowStageProgress(job, stages);
|
|
346
|
+
const steps = workflowWidgetSteps(job, stages);
|
|
347
|
+
const projection = { stages, steps };
|
|
348
|
+
if (stageProgress)
|
|
349
|
+
projection.stageProgress = stageProgress;
|
|
350
|
+
if (stages.length)
|
|
351
|
+
projection.plannedKeys = new Set(stages.map((node) => node.id));
|
|
352
|
+
return projection;
|
|
353
|
+
}
|
|
354
|
+
function workflowWidgetProjectionLookup() {
|
|
355
|
+
const projections = new WeakMap;
|
|
356
|
+
return (job) => {
|
|
357
|
+
const cached = projections.get(job);
|
|
358
|
+
if (cached)
|
|
359
|
+
return cached;
|
|
360
|
+
const projection = buildWorkflowWidgetProjection(job);
|
|
361
|
+
projections.set(job, projection);
|
|
362
|
+
return projection;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function laneStepForJob(job, steps = workflowWidgetSteps(job)) {
|
|
366
|
+
if (steps.length === 0)
|
|
367
|
+
return;
|
|
368
|
+
const graphCurrent = job.workflowGraph?.currentNodeId;
|
|
369
|
+
if (graphCurrent) {
|
|
370
|
+
const current = steps.find((step) => step.workflowKey === graphCurrent);
|
|
371
|
+
if (current)
|
|
372
|
+
return current;
|
|
373
|
+
}
|
|
374
|
+
if (job.currentStep !== undefined) {
|
|
375
|
+
const current = steps[job.currentStep];
|
|
376
|
+
if (current && (current.index === undefined || current.index === job.currentStep))
|
|
377
|
+
return current;
|
|
378
|
+
const indexedCurrent = steps.find((step) => step.index === job.currentStep);
|
|
379
|
+
if (indexedCurrent)
|
|
380
|
+
return indexedCurrent;
|
|
381
|
+
return steps[0];
|
|
382
|
+
}
|
|
383
|
+
return steps.find((step) => step.status === "running") ?? steps.find((step) => step.status === "pending") ?? steps.at(-1);
|
|
384
|
+
}
|
|
385
|
+
function laneTraceForJob(job) {
|
|
386
|
+
return Array.isArray(job.workflow?.trace) ? job.workflow.trace.at(-1) : undefined;
|
|
387
|
+
}
|
|
388
|
+
function laneGate(step) {
|
|
389
|
+
const review = step?.review?.status ?? step?.acceptance?.reviewResult?.status;
|
|
390
|
+
if (review === "blockers")
|
|
391
|
+
return "review blockers";
|
|
392
|
+
if (review === "review-required")
|
|
393
|
+
return "review required";
|
|
394
|
+
if (review === "reviewed")
|
|
395
|
+
return "reviewed";
|
|
396
|
+
const acceptance = step?.acceptance?.status;
|
|
397
|
+
if (acceptance === "review-required")
|
|
398
|
+
return "acceptance review";
|
|
399
|
+
if (acceptance === "checked" || acceptance === "verified" || acceptance === "accepted")
|
|
400
|
+
return "acceptance";
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
function laneNextAction(state, step, output, gate) {
|
|
404
|
+
if (step?.watchdog?.phase === "stale")
|
|
405
|
+
return "inspect stale state";
|
|
406
|
+
if (step?.toolBudgetBlocked === true || step?.turnBudgetExceeded === true)
|
|
407
|
+
return "inspect blocked state";
|
|
408
|
+
if (gate === "review blockers")
|
|
409
|
+
return "resolve review blockers";
|
|
410
|
+
if (gate === "review required" || gate === "acceptance review")
|
|
411
|
+
return "review output";
|
|
412
|
+
if (step?.activityState === "needs_attention")
|
|
413
|
+
return "inspect attention";
|
|
414
|
+
if (state === "queued" || state === "pending")
|
|
415
|
+
return "await launch";
|
|
416
|
+
if (state === "failed" || state === "rejected")
|
|
417
|
+
return "inspect failure";
|
|
418
|
+
if (state === "partial")
|
|
419
|
+
return "inspect partial state";
|
|
420
|
+
if (state === "paused")
|
|
421
|
+
return "inspect paused state";
|
|
422
|
+
if (state === "stopped")
|
|
423
|
+
return "inspect stopped state";
|
|
424
|
+
if ((state === "complete" || state === "completed") && gate === "reviewed")
|
|
425
|
+
return "ready";
|
|
426
|
+
if ((state === "complete" || state === "completed") && output)
|
|
427
|
+
return "inspect output";
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
function isTerminalLaneState(state) {
|
|
431
|
+
return state !== "queued" && state !== "running";
|
|
432
|
+
}
|
|
433
|
+
export function projectAsyncLane(job, ...args) {
|
|
434
|
+
const selectedStep = args.length === 0 ? laneStepForJob(job) : args[0];
|
|
435
|
+
const trace = laneTraceForJob(job);
|
|
436
|
+
const workspace = job.cwd ? boundedLaneValue(shortenPath(job.cwd)) : undefined;
|
|
437
|
+
const label = compactTaskText(selectedStep?.description, selectedStep?.label) ?? boundedLaneValue(trace?.label) ?? (workspace ? undefined : boundedLaneValue(selectedStep?.workflowKey ?? job.workflowKey));
|
|
438
|
+
const role = boundedLaneValue(selectedStep?.agent ?? trace?.agent ?? job.agents?.[0] ?? widgetJobName(job), 32) ?? "subagent";
|
|
439
|
+
const phase = boundedLaneValue(selectedStep?.phase ?? trace?.phase);
|
|
440
|
+
const gate = laneGate(selectedStep);
|
|
441
|
+
const output = boundedLaneValue(selectedStep?.outputName);
|
|
442
|
+
const ref = boundedLaneValue(selectedStep?.workflowKey ?? job.workflowKey ?? job.asyncId.slice(0, 8), 24) ?? job.asyncId.slice(0, 8);
|
|
443
|
+
const chips = [
|
|
444
|
+
selectedStep?.context,
|
|
445
|
+
selectedStep?.structured ? "structured" : undefined,
|
|
446
|
+
selectedStep?.activityState === "active_long_running" ? "long-running" : undefined,
|
|
447
|
+
selectedStep?.activityState === "needs_attention" ? "attention" : undefined,
|
|
448
|
+
selectedStep?.watchdog?.phase === "stale" ? "stale" : undefined,
|
|
449
|
+
selectedStep?.toolBudgetBlocked === true || selectedStep?.turnBudgetExceeded === true ? "blocked" : undefined
|
|
450
|
+
].filter((chip) => Boolean(chip));
|
|
451
|
+
const state = isTerminalLaneState(job.status) ? job.status : selectedStep?.status ?? job.status;
|
|
452
|
+
const next = laneNextAction(state, selectedStep, output, gate);
|
|
453
|
+
if (!label && !phase && !gate && !output && !selectedStep?.workflowKey && !job.workflowKey && !trace?.label && !trace?.phase)
|
|
454
|
+
return;
|
|
455
|
+
return { ...label ? { label } : {}, role, ...phase ? { phase } : {}, state, ...gate ? { gate } : {}, ...next ? { next } : {}, ...output ? { output } : {}, ...workspace ? { workspace } : {}, ref, chips };
|
|
456
|
+
}
|
|
457
|
+
function laneStateLabel(state, theme) {
|
|
458
|
+
if (state === "running")
|
|
459
|
+
return theme.fg("accent", "running");
|
|
460
|
+
if (state === "queued" || state === "pending")
|
|
461
|
+
return theme.fg("muted", state);
|
|
462
|
+
if (state === "complete" || state === "completed")
|
|
463
|
+
return theme.fg("success", "complete");
|
|
464
|
+
if (state === "failed" || state === "rejected")
|
|
465
|
+
return theme.fg("error", state === "rejected" ? "rejected" : "failed");
|
|
466
|
+
return theme.fg("warning", state);
|
|
467
|
+
}
|
|
468
|
+
function formatLaneProjection(lane, theme) {
|
|
469
|
+
const label = boundedLaneValue(lane.label, 56);
|
|
470
|
+
const identity = [label, lane.role ? `role:${lane.role}` : undefined].filter(Boolean).join(" · ");
|
|
471
|
+
return `${identity ? theme.bold(identity) : theme.bold(lane.role)} · ${laneStateLabel(lane.state, theme)}`;
|
|
472
|
+
}
|
|
473
|
+
function formatLaneChip(chip, theme) {
|
|
474
|
+
const text = `[${chip}]`;
|
|
475
|
+
if (chip === "blocked")
|
|
476
|
+
return theme.fg("error", text);
|
|
477
|
+
if (chip === "stale")
|
|
478
|
+
return theme.fg("warning", text);
|
|
479
|
+
return text;
|
|
480
|
+
}
|
|
481
|
+
function formatLaneProjectionDetails(lane, theme) {
|
|
482
|
+
const details = [
|
|
483
|
+
lane.phase ? `phase:${lane.phase}` : undefined,
|
|
484
|
+
lane.gate ? `gate:${lane.gate}` : undefined,
|
|
485
|
+
lane.next ? `next:${lane.next}` : undefined,
|
|
486
|
+
lane.output ? `out:${lane.output}` : undefined,
|
|
487
|
+
lane.workspace ? `workspace:${lane.workspace}` : lane.ref ? `ref:${lane.ref}` : undefined
|
|
488
|
+
].filter(Boolean);
|
|
489
|
+
const chips = lane.chips.map((chip) => formatLaneChip(chip, theme));
|
|
490
|
+
return [...details.length ? [theme.fg("dim", details.join(" · "))] : [], ...chips].join(" · ") || undefined;
|
|
491
|
+
}
|
|
492
|
+
function formatLaneProjectionLines(lane, theme, indent) {
|
|
493
|
+
const details = formatLaneProjectionDetails(lane, theme);
|
|
494
|
+
return [
|
|
495
|
+
`${indent}${formatLaneProjection(lane, theme)}`,
|
|
496
|
+
...details ? [`${indent} ${details}`] : []
|
|
497
|
+
];
|
|
498
|
+
}
|
|
499
|
+
function laneRenderKey(job, projection) {
|
|
500
|
+
const selectedStep = projection ? laneStepForJob(job, projection.steps) : laneStepForJob(job);
|
|
501
|
+
const lane = projectAsyncLane(job, selectedStep);
|
|
502
|
+
return lane ? [lane.label, lane.role, lane.phase, lane.state, lane.gate, lane.next, lane.output, lane.workspace, lane.ref, lane.chips] : undefined;
|
|
503
|
+
}
|
|
504
|
+
function widgetLaneDetailLines(job, theme, projection) {
|
|
505
|
+
if (job.steps?.length && (job.mode === "parallel" || job.mode === "chain"))
|
|
506
|
+
return [];
|
|
507
|
+
const selectedStep = projection ? laneStepForJob(job, projection.steps) : laneStepForJob(job);
|
|
508
|
+
const lane = projectAsyncLane(job, selectedStep);
|
|
509
|
+
return lane ? formatLaneProjectionLines(lane, theme, " ") : [];
|
|
510
|
+
}
|
|
511
|
+
function workflowPreflightLines(job, expanded = false) {
|
|
512
|
+
if (job.mode !== "workflow" || !job.preflight)
|
|
513
|
+
return [];
|
|
514
|
+
if (!expanded) {
|
|
515
|
+
const warning = formatWorkflowPreflightWarningSummary(job.workflow?.preflightWarnings, { indent: " ", hint: "expand for debug" });
|
|
516
|
+
return [
|
|
517
|
+
formatWorkflowPreflightPlanSummary(job.preflight, { indent: " " }),
|
|
518
|
+
...warning ? [warning] : []
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
return [
|
|
522
|
+
...formatWorkflowPreflight(job.preflight, { indent: " " }).split(`
|
|
523
|
+
`),
|
|
524
|
+
...job.workflow?.preflightWarnings ? formatWorkflowPreflightWarnings(job.workflow.preflightWarnings, { indent: " " }).split(`
|
|
525
|
+
`) : []
|
|
526
|
+
];
|
|
527
|
+
}
|
|
528
|
+
function workflowLabelForResult(details, resultIndex) {
|
|
529
|
+
const flatIndex = foregroundResultIndex(details, resultIndex);
|
|
530
|
+
const visit = (nodes) => {
|
|
531
|
+
for (const node of nodes) {
|
|
532
|
+
if (node.flatIndex === flatIndex && node.label.trim())
|
|
533
|
+
return node.label;
|
|
534
|
+
const nested = node.children ? visit(node.children) : undefined;
|
|
535
|
+
if (nested)
|
|
536
|
+
return nested;
|
|
537
|
+
}
|
|
538
|
+
return;
|
|
539
|
+
};
|
|
540
|
+
return details.workflowGraph ? visit(details.workflowGraph.nodes) : undefined;
|
|
541
|
+
}
|
|
542
|
+
function foregroundProgressForResult(details, resultIndex) {
|
|
543
|
+
const result = details.results[resultIndex];
|
|
544
|
+
if (result?.progress)
|
|
545
|
+
return result.progress;
|
|
546
|
+
const index = result?.index ?? resultIndex;
|
|
547
|
+
const indexed = details.progress?.find((progress) => progress.index === index);
|
|
548
|
+
if (indexed)
|
|
549
|
+
return indexed;
|
|
550
|
+
return result?.agent ? details.progress?.find((progress) => progress.agent === result.agent && progress.status === "running") : undefined;
|
|
551
|
+
}
|
|
552
|
+
function foregroundResultIndex(details, resultIndex) {
|
|
553
|
+
const result = details.results[resultIndex];
|
|
554
|
+
if (typeof result?.index === "number")
|
|
555
|
+
return result.index;
|
|
556
|
+
return foregroundProgressForResult(details, resultIndex)?.index ?? resultIndex;
|
|
557
|
+
}
|
|
558
|
+
function foregroundResultDisplayName(details, resultIndex, result, fallback) {
|
|
559
|
+
const progress = foregroundProgressForResult(details, resultIndex);
|
|
560
|
+
const agent = normalizedParallelDisplayText(result?.agent) ?? normalizedParallelDisplayText(progress?.agent);
|
|
561
|
+
const workflowLabel = normalizedParallelDisplayText(workflowLabelForResult(details, resultIndex));
|
|
562
|
+
if (workflowLabel)
|
|
563
|
+
return compactTaskText(undefined, workflowLabel) ?? workflowLabel;
|
|
564
|
+
const sessionName = normalizedParallelDisplayText(result?.sessionName) ?? normalizedParallelDisplayText(progress?.sessionName);
|
|
565
|
+
if (sessionName) {
|
|
566
|
+
const sessionTask = stripRepeatedAgentPrefix(sessionName, agent);
|
|
567
|
+
if (sessionTask && sessionTask !== PROMPT_REDACTED && sessionTask.toLowerCase() !== agent?.toLowerCase()) {
|
|
568
|
+
return compactTaskText(sessionTask) ?? sessionTask;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return compactTaskText(result?.task) ?? compactTaskText(progress?.task) ?? agent ?? fallback;
|
|
572
|
+
}
|
|
573
|
+
function foregroundSingleDisplayName(result) {
|
|
574
|
+
return normalizedParallelDisplayText(result?.agent) ?? normalizedParallelDisplayText(result?.sessionName) ?? compactTaskText(result?.task) ?? "subagent";
|
|
575
|
+
}
|
|
576
|
+
function hasLiveOutputSignal(line) {
|
|
577
|
+
const clean = oneLine(line);
|
|
578
|
+
return liveOutputWordSignalPattern.test(clean) || liveOutputCodeSignalPattern.test(clean);
|
|
579
|
+
}
|
|
580
|
+
function isNoisyStatusLine(line) {
|
|
581
|
+
const clean = oneLine(line);
|
|
582
|
+
return clean.length > 0 && clean.length <= 240 && !hasLiveOutputSignal(clean) && noisyStatusPatterns.some((pattern) => pattern.test(clean));
|
|
583
|
+
}
|
|
584
|
+
function latestActivityText(line) {
|
|
585
|
+
return oneLine(line).replace(/^i (?:will|can|need to|am going to)\s+/i, "").replace(/^i(?:'m|’m| am)\s+/i, "");
|
|
586
|
+
}
|
|
587
|
+
function progressUpdateSummary(lines) {
|
|
588
|
+
const counts = new Map;
|
|
589
|
+
for (const line of lines)
|
|
590
|
+
counts.set(line.toLowerCase(), (counts.get(line.toLowerCase()) ?? 0) + 1);
|
|
591
|
+
const exactRepeatCount = Math.max(...counts.values());
|
|
592
|
+
const latest = latestActivityText(lines[lines.length - 1]);
|
|
593
|
+
const repeat = exactRepeatCount > 1 ? ` · repeated ${exactRepeatCount}×` : "";
|
|
594
|
+
return `↻ ${lines.length} progress updates${repeat} · latest: ${latest}`;
|
|
595
|
+
}
|
|
596
|
+
function compactRecentOutputLines(recentOutput) {
|
|
597
|
+
const lines = [];
|
|
598
|
+
const noisyLines = [];
|
|
599
|
+
const otherLines = [];
|
|
600
|
+
for (const rawLine of recentOutput ?? []) {
|
|
601
|
+
const line = oneLine(rawLine);
|
|
602
|
+
if (!line || line === "(running...)")
|
|
603
|
+
continue;
|
|
604
|
+
lines.push(line);
|
|
605
|
+
(isNoisyStatusLine(line) ? noisyLines : otherLines).push(line);
|
|
606
|
+
}
|
|
607
|
+
if (noisyLines.length >= 4 && !otherLines.some(hasLiveOutputSignal)) {
|
|
608
|
+
if (otherLines.length === 0) {
|
|
609
|
+
return [
|
|
610
|
+
progressUpdateSummary(noisyLines),
|
|
611
|
+
"pattern: repeated short status lines"
|
|
612
|
+
];
|
|
613
|
+
}
|
|
614
|
+
const visibleTail = otherLines.slice(-3);
|
|
615
|
+
const hiddenSignals = otherLines.slice(0, -3).filter(hasLiveOutputSignal);
|
|
616
|
+
return [
|
|
617
|
+
progressUpdateSummary(noisyLines),
|
|
618
|
+
...hiddenSignals.length > 0 ? [`… ${hiddenSignals.length} older signal ${hiddenSignals.length === 1 ? "line" : "lines"}: ${hiddenSignals.at(-1)}`] : [],
|
|
619
|
+
...visibleTail
|
|
620
|
+
].slice(0, 5);
|
|
621
|
+
}
|
|
622
|
+
if (lines.length <= 5)
|
|
623
|
+
return lines;
|
|
624
|
+
const tail = lines.slice(-5);
|
|
625
|
+
const hiddenSignals = lines.slice(0, -5).filter(hasLiveOutputSignal);
|
|
626
|
+
if (hiddenSignals.length === 0)
|
|
627
|
+
return tail;
|
|
628
|
+
return [
|
|
629
|
+
`… ${hiddenSignals.length} older signal ${hiddenSignals.length === 1 ? "line" : "lines"}: ${hiddenSignals.at(-1)}`,
|
|
630
|
+
...lines.slice(-4)
|
|
631
|
+
];
|
|
632
|
+
}
|
|
633
|
+
function compactWorkflowError(error) {
|
|
634
|
+
const outputMatch = error.match(/(?:^|\n)Output:\s*([\s\S]+)/);
|
|
635
|
+
if (!outputMatch)
|
|
636
|
+
return oneLine(error);
|
|
637
|
+
const prefix = oneLine(error.slice(0, outputMatch.index)).replace(/:$/, "") || "Failed";
|
|
638
|
+
const outputLines = outputMatch[1].split(/\r?\n/).map(oneLine).filter(Boolean);
|
|
639
|
+
const allOutputLinesAreNoisy = outputLines.length > 0 && outputLines.every(isNoisyStatusLine);
|
|
640
|
+
const latest = outputLines.at(-1);
|
|
641
|
+
return allOutputLinesAreNoisy && latest ? `${prefix} · latest: ${latestActivityText(latest)}` : `${prefix} · ${oneLine(outputMatch[1] ?? "")}`;
|
|
642
|
+
}
|
|
643
|
+
const WORKFLOW_LIVE_ROW_LIMIT = 8;
|
|
644
|
+
function visibleWorkflowRows(rows) {
|
|
645
|
+
if (rows.length <= WORKFLOW_LIVE_ROW_LIMIT)
|
|
646
|
+
return { rows, hiddenRows: 0 };
|
|
647
|
+
const selected = new Set;
|
|
648
|
+
const add = (row) => {
|
|
649
|
+
if (selected.size >= WORKFLOW_LIVE_ROW_LIMIT || selected.has(row.key))
|
|
650
|
+
return;
|
|
651
|
+
selected.add(row.key);
|
|
652
|
+
};
|
|
653
|
+
for (const row of [...rows].reverse()) {
|
|
654
|
+
if (row.state === "failed" || row.state === "detached")
|
|
655
|
+
add(row);
|
|
656
|
+
}
|
|
657
|
+
for (const row of [...rows].reverse())
|
|
658
|
+
add(row);
|
|
659
|
+
return {
|
|
660
|
+
rows: rows.filter((row) => selected.has(row.key)),
|
|
661
|
+
hiddenRows: rows.length - selected.size
|
|
662
|
+
};
|
|
663
|
+
}
|
|
141
664
|
function snapshotNowForProgress(progress) {
|
|
142
665
|
if (progress.currentToolStartedAt !== undefined && progress.durationMs !== undefined)
|
|
143
666
|
return progress.currentToolStartedAt + progress.durationMs;
|
|
144
667
|
return progress.lastActivityAt;
|
|
145
668
|
}
|
|
669
|
+
function renderToolArgsPreview(value, maxLength, expanded) {
|
|
670
|
+
const normalized = sanitizeDisplayText(value);
|
|
671
|
+
if (expanded || normalized.length <= maxLength)
|
|
672
|
+
return normalized;
|
|
673
|
+
return `${truncateDisplayText(normalized, maxLength)}...`;
|
|
674
|
+
}
|
|
146
675
|
function formatCurrentToolLine(progress, availableWidth, expanded, snapshotNow) {
|
|
147
676
|
if (!progress.currentTool)
|
|
148
677
|
return;
|
|
149
678
|
const maxToolArgsLen = Math.max(50, availableWidth - 20);
|
|
150
|
-
const toolArgsPreview = progress.currentToolArgs ?
|
|
679
|
+
const toolArgsPreview = progress.currentToolArgs ? renderToolArgsPreview(progress.currentToolArgs, maxToolArgsLen, expanded) : "";
|
|
151
680
|
const durationSuffix = progress.currentToolStartedAt !== undefined && snapshotNow !== undefined ? ` | ${formatDuration(Math.max(0, snapshotNow - progress.currentToolStartedAt))}` : "";
|
|
152
681
|
return toolArgsPreview ? `${progress.currentTool}: ${toolArgsPreview}${durationSuffix}` : `${progress.currentTool}${durationSuffix}`;
|
|
153
682
|
}
|
|
@@ -205,6 +734,8 @@ function firstOutputLine(text) {
|
|
|
205
734
|
function resultStatusLine(result, output) {
|
|
206
735
|
if (result.detached)
|
|
207
736
|
return result.detachedReason ? `Detached: ${result.detachedReason}` : "Detached";
|
|
737
|
+
if (result.stopped)
|
|
738
|
+
return "Stopped";
|
|
208
739
|
if (result.interrupted)
|
|
209
740
|
return "Paused";
|
|
210
741
|
if (result.exitCode !== 0)
|
|
@@ -215,27 +746,171 @@ function resultStatusLine(result, output) {
|
|
|
215
746
|
return "Done (no text output)";
|
|
216
747
|
return "Done";
|
|
217
748
|
}
|
|
218
|
-
function
|
|
219
|
-
if (running) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return theme.fg("accent", runningGlyph(seed));
|
|
749
|
+
function semanticResultPresentation(input) {
|
|
750
|
+
if (input.running) {
|
|
751
|
+
const glyph = input.frame !== undefined ? runningGlyph((input.seed ?? 0) + input.frame) : runningGlyph(input.seed);
|
|
752
|
+
return { glyph, label: "running", tone: "accent" };
|
|
223
753
|
}
|
|
224
|
-
if (
|
|
225
|
-
return
|
|
226
|
-
if (
|
|
227
|
-
return
|
|
228
|
-
if (
|
|
229
|
-
return
|
|
230
|
-
if (
|
|
231
|
-
return
|
|
232
|
-
|
|
754
|
+
if (input.detached)
|
|
755
|
+
return { glyph: "■", label: "detached", tone: "warning" };
|
|
756
|
+
if (input.stopped)
|
|
757
|
+
return { glyph: "■", label: "stopped", tone: "warning" };
|
|
758
|
+
if (input.interrupted)
|
|
759
|
+
return { glyph: "■", label: "paused", tone: "warning" };
|
|
760
|
+
if (input.failed)
|
|
761
|
+
return { glyph: "✗", label: "failed", tone: "error" };
|
|
762
|
+
if (input.partial)
|
|
763
|
+
return { glyph: "■", label: "partial", tone: "warning" };
|
|
764
|
+
return { glyph: "✓", label: "completed", tone: input.completedWithoutOutput ? "warning" : "success" };
|
|
765
|
+
}
|
|
766
|
+
function hasTerminalResultFlag(result) {
|
|
767
|
+
return Boolean(result.detached || result.stopped || result.interrupted);
|
|
768
|
+
}
|
|
769
|
+
function hasTerminalResult(result) {
|
|
770
|
+
if (hasTerminalResultFlag(result))
|
|
771
|
+
return true;
|
|
772
|
+
const status = result.progress?.status;
|
|
773
|
+
if (status === "running" || status === "pending")
|
|
774
|
+
return false;
|
|
775
|
+
return result.exitCode !== undefined;
|
|
776
|
+
}
|
|
777
|
+
function isResultRunning(result, status = result.progress?.status) {
|
|
778
|
+
return status === "running" && !hasTerminalResultFlag(result);
|
|
779
|
+
}
|
|
780
|
+
function detailsHaveRunningResult(details) {
|
|
781
|
+
return details.progress?.some((progress) => {
|
|
782
|
+
if (progress.status !== "running")
|
|
783
|
+
return false;
|
|
784
|
+
const result = details.results.find((entry) => entry.progress?.index === progress.index) ?? details.results[progress.index];
|
|
785
|
+
return !result || !hasTerminalResultFlag(result);
|
|
786
|
+
}) || details.results.some((result) => isResultRunning(result)) || workflowGraphHasStatus(details, ["running"]);
|
|
787
|
+
}
|
|
788
|
+
function resultPresentation(result, output, running = isResultRunning(result), seed = progressRunningSeed(result.progress ?? result.progressSummary), frame) {
|
|
789
|
+
return semanticResultPresentation({
|
|
790
|
+
running,
|
|
791
|
+
detached: result.detached,
|
|
792
|
+
stopped: result.stopped,
|
|
793
|
+
interrupted: result.interrupted,
|
|
794
|
+
failed: result.exitCode !== 0,
|
|
795
|
+
completedWithoutOutput: hasEmptyTextOutputWithoutOutputTarget(result.task, output),
|
|
796
|
+
seed,
|
|
797
|
+
frame
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
function resultGlyph(result, output, theme, running = isResultRunning(result), seed = progressRunningSeed(result.progress ?? result.progressSummary), frame) {
|
|
801
|
+
const presentation = resultPresentation(result, output, running, seed, frame);
|
|
802
|
+
return theme.fg(presentation.tone, presentation.glyph);
|
|
803
|
+
}
|
|
804
|
+
function styledResultPresentation(presentation, theme) {
|
|
805
|
+
return {
|
|
806
|
+
glyph: theme.fg(presentation.tone, presentation.glyph),
|
|
807
|
+
label: theme.fg(presentation.tone, presentation.label)
|
|
808
|
+
};
|
|
233
809
|
}
|
|
234
810
|
function compactCurrentActivity(progress) {
|
|
235
811
|
const snapshotNow = snapshotNowForProgress(progress);
|
|
236
812
|
return formatCurrentToolLine(progress, getTermWidth() - 4, false, snapshotNow) ?? buildLiveStatusLine(progress, snapshotNow) ?? "thinking…";
|
|
237
813
|
}
|
|
238
|
-
|
|
814
|
+
function textDigest(text) {
|
|
815
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
816
|
+
}
|
|
817
|
+
function displayTextRenderKey(text) {
|
|
818
|
+
const normalized = sanitizeDisplayText(text);
|
|
819
|
+
return [normalized.length, textDigest(normalized)];
|
|
820
|
+
}
|
|
821
|
+
function expandedStepActivityRenderKey(step) {
|
|
822
|
+
return [
|
|
823
|
+
step.recentTools?.slice(-3).map((tool) => [tool.tool, displayTextRenderKey(tool.args), tool.endMs]),
|
|
824
|
+
compactRecentOutputLines(step.recentOutput).map(displayTextRenderKey)
|
|
825
|
+
];
|
|
826
|
+
}
|
|
827
|
+
function widgetStepRenderKey(step, index, expanded = false) {
|
|
828
|
+
return [
|
|
829
|
+
step.index ?? index,
|
|
830
|
+
step.agent,
|
|
831
|
+
step.sessionName,
|
|
832
|
+
step.workflowKey,
|
|
833
|
+
step.phase,
|
|
834
|
+
step.label,
|
|
835
|
+
step.status,
|
|
836
|
+
step.activityState,
|
|
837
|
+
step.lastActivityAt,
|
|
838
|
+
step.currentTool,
|
|
839
|
+
step.currentToolArgs,
|
|
840
|
+
step.currentToolStartedAt,
|
|
841
|
+
step.currentPath,
|
|
842
|
+
step.turnCount,
|
|
843
|
+
step.toolCount,
|
|
844
|
+
step.startedAt,
|
|
845
|
+
step.endedAt,
|
|
846
|
+
step.durationMs,
|
|
847
|
+
step.tokens?.total,
|
|
848
|
+
step.model,
|
|
849
|
+
step.thinking,
|
|
850
|
+
step.context,
|
|
851
|
+
step.description,
|
|
852
|
+
step.outputName,
|
|
853
|
+
step.structured,
|
|
854
|
+
step.acceptance?.status,
|
|
855
|
+
step.acceptance?.reviewResult?.status,
|
|
856
|
+
step.review?.status,
|
|
857
|
+
step.toolBudgetBlocked,
|
|
858
|
+
step.turnBudgetExceeded,
|
|
859
|
+
step.timedOut,
|
|
860
|
+
step.stopped,
|
|
861
|
+
step.execution?.status,
|
|
862
|
+
step.execution?.error,
|
|
863
|
+
step.execution?.timedOut,
|
|
864
|
+
step.execution?.interrupted,
|
|
865
|
+
step.execution?.stopped,
|
|
866
|
+
step.execution?.detached,
|
|
867
|
+
step.watchdog?.phase,
|
|
868
|
+
step.error,
|
|
869
|
+
expanded ? expandedStepActivityRenderKey(step) : undefined,
|
|
870
|
+
nestedRenderKey(step.children, expanded)
|
|
871
|
+
];
|
|
872
|
+
}
|
|
873
|
+
function nestedRenderKey(children, expanded = false) {
|
|
874
|
+
return (children ?? []).map((child) => [
|
|
875
|
+
child.id,
|
|
876
|
+
child.state,
|
|
877
|
+
child.agent,
|
|
878
|
+
child.sessionName,
|
|
879
|
+
child.model,
|
|
880
|
+
child.thinking,
|
|
881
|
+
child.activityState,
|
|
882
|
+
child.lastActivityAt,
|
|
883
|
+
child.currentTool,
|
|
884
|
+
child.currentToolStartedAt,
|
|
885
|
+
child.currentPath,
|
|
886
|
+
child.turnCount,
|
|
887
|
+
child.toolCount,
|
|
888
|
+
child.startedAt,
|
|
889
|
+
child.endedAt,
|
|
890
|
+
child.lastUpdate,
|
|
891
|
+
child.error,
|
|
892
|
+
child.totalTokens?.total,
|
|
893
|
+
child.steps?.map((step, index) => widgetStepRenderKey({ ...step, agent: step.agent, status: step.status }, index, expanded)),
|
|
894
|
+
nestedRenderKey(child.children, expanded)
|
|
895
|
+
]);
|
|
896
|
+
}
|
|
897
|
+
function hostStepRenderKey(row) {
|
|
898
|
+
return [
|
|
899
|
+
row.kind,
|
|
900
|
+
row.name,
|
|
901
|
+
row.state,
|
|
902
|
+
row.provider,
|
|
903
|
+
row.role,
|
|
904
|
+
row.verdict,
|
|
905
|
+
row.reasonCode,
|
|
906
|
+
row.detail,
|
|
907
|
+
row.target,
|
|
908
|
+
row.freshness,
|
|
909
|
+
row.reportPath
|
|
910
|
+
];
|
|
911
|
+
}
|
|
912
|
+
export function widgetRenderKey(job, expanded = false) {
|
|
913
|
+
const projection = buildWorkflowWidgetProjection(job);
|
|
239
914
|
return JSON.stringify({
|
|
240
915
|
asyncDir: job.asyncDir,
|
|
241
916
|
status: job.status,
|
|
@@ -251,14 +926,24 @@ export function widgetRenderKey(job) {
|
|
|
251
926
|
currentStep: job.currentStep,
|
|
252
927
|
chainStepCount: job.chainStepCount,
|
|
253
928
|
parallelGroups: job.parallelGroups,
|
|
254
|
-
|
|
255
|
-
|
|
929
|
+
workflowHostSteps: projectAsyncWorkflowRows([], job.hostSteps).map(hostStepRenderKey),
|
|
930
|
+
workflowGraph: job.mode === "workflow" && job.workflowGraph ? {
|
|
931
|
+
currentNodeId: job.workflowGraph.currentNodeId,
|
|
932
|
+
stages: projection.stages.map((node) => [node.id, node.status, node.agent, node.phase, node.label, node.flatIndex, node.outputName, node.structured, node.error])
|
|
933
|
+
} : undefined,
|
|
934
|
+
preflight: expanded ? job.preflight : job.preflight ? formatWorkflowPreflightPlanSummary(job.preflight) : undefined,
|
|
935
|
+
preflightWarnings: expanded ? job.workflow?.preflightWarnings : job.workflow?.preflightWarnings?.length || undefined,
|
|
936
|
+
steps: job.steps?.map((step, index) => widgetStepRenderKey(step, index, expanded)),
|
|
937
|
+
nestedChildren: nestedRenderKey(job.nestedChildren, expanded),
|
|
938
|
+
lane: laneRenderKey(job, projection),
|
|
256
939
|
stepsTotal: job.stepsTotal,
|
|
257
940
|
runningSteps: job.runningSteps,
|
|
258
941
|
completedSteps: job.completedSteps,
|
|
259
942
|
activeParallelGroup: job.activeParallelGroup,
|
|
260
943
|
startedAt: job.startedAt,
|
|
261
944
|
updatedAt: job.updatedAt,
|
|
945
|
+
timedOut: job.timedOut,
|
|
946
|
+
stopped: job.stopped,
|
|
262
947
|
totalTokens: job.totalTokens
|
|
263
948
|
});
|
|
264
949
|
}
|
|
@@ -281,6 +966,38 @@ function widgetJobName(job) {
|
|
|
281
966
|
return formatWidgetAgents(job.agents);
|
|
282
967
|
return job.mode ?? "subagent";
|
|
283
968
|
}
|
|
969
|
+
function isSingleChildAsyncJob(job) {
|
|
970
|
+
return job.mode === "single" && job.steps?.length === 1 && shouldSuppressSingleStep(job.chainStepCount, job.stepsTotal);
|
|
971
|
+
}
|
|
972
|
+
function isCompletedWidgetStepStatus(status) {
|
|
973
|
+
return status === "complete" || status === "completed";
|
|
974
|
+
}
|
|
975
|
+
function singleChildAgentName(job, step) {
|
|
976
|
+
return job.agents?.length === 1 ? job.agents[0] : step.agent || widgetJobName(job);
|
|
977
|
+
}
|
|
978
|
+
function hasSingleChildDetailEvidence(job, step) {
|
|
979
|
+
return Boolean(step.error?.trim() || step.execution?.error?.trim() || step.phase?.trim() || step.label?.trim() || step.workflowKey?.trim() || step.outputName?.trim() || step.lane || job.workflowKey?.trim() || job.lane || laneTraceForJob(job)?.phase?.trim() || laneTraceForJob(job)?.label?.trim() || step.timedOut || step.stopped || step.execution?.timedOut || step.execution?.interrupted || step.execution?.stopped || step.execution?.detached || job.timedOut || job.stopped || ["failed", "partial", "paused", "stopped", "detached"].includes(step.execution?.status ?? "") || step.acceptance?.status === "rejected" || step.acceptance?.reviewResult?.status === "blockers" || step.review?.status === "blockers");
|
|
980
|
+
}
|
|
981
|
+
function shouldCollapseSingleChildDetails(job, step) {
|
|
982
|
+
if (!isSingleChildAsyncJob(job) || hasSingleChildDetailEvidence(job, step))
|
|
983
|
+
return false;
|
|
984
|
+
if (job.status === "running")
|
|
985
|
+
return step.status === "running";
|
|
986
|
+
if (job.status === "complete")
|
|
987
|
+
return isCompletedWidgetStepStatus(step.status);
|
|
988
|
+
return false;
|
|
989
|
+
}
|
|
990
|
+
function singleChildTask(job, step) {
|
|
991
|
+
const task = compactTaskText(step.description, step.label);
|
|
992
|
+
if (task)
|
|
993
|
+
return task;
|
|
994
|
+
const sessionName = step.sessionName?.trim();
|
|
995
|
+
if (!sessionName)
|
|
996
|
+
return;
|
|
997
|
+
const agentName = singleChildAgentName(job, step);
|
|
998
|
+
const sessionTask = stripRepeatedAgentPrefix(sessionName, agentName);
|
|
999
|
+
return sessionTask === agentName ? undefined : compactTaskText(sessionTask);
|
|
1000
|
+
}
|
|
284
1001
|
function widgetActivity(job) {
|
|
285
1002
|
const facts = [];
|
|
286
1003
|
if (job.currentTool && job.currentToolStartedAt !== undefined && job.updatedAt !== undefined)
|
|
@@ -306,6 +1023,10 @@ function widgetActivity(job) {
|
|
|
306
1023
|
return "queued…";
|
|
307
1024
|
if (job.status === "paused")
|
|
308
1025
|
return "Paused";
|
|
1026
|
+
if (job.status === "stopped")
|
|
1027
|
+
return "Stopped";
|
|
1028
|
+
if (job.status === "partial")
|
|
1029
|
+
return "Partial";
|
|
309
1030
|
if (job.status === "failed")
|
|
310
1031
|
return "Failed";
|
|
311
1032
|
return "Done";
|
|
@@ -328,26 +1049,30 @@ function widgetJobsRunningSeed(jobs) {
|
|
|
328
1049
|
seed = runningSeed(seed, widgetJobRunningSeed(job));
|
|
329
1050
|
return seed;
|
|
330
1051
|
}
|
|
331
|
-
function widgetStatusGlyph(job, theme) {
|
|
1052
|
+
function widgetStatusGlyph(job, theme, frame) {
|
|
332
1053
|
if (job.status === "running")
|
|
333
|
-
return theme.fg("accent", runningGlyph(widgetJobRunningSeed(job)));
|
|
1054
|
+
return theme.fg("accent", runningGlyph(animatedSeed(widgetJobRunningSeed(job), frame)));
|
|
334
1055
|
if (job.status === "queued")
|
|
335
1056
|
return theme.fg("muted", "◦");
|
|
336
1057
|
if (job.status === "complete")
|
|
337
1058
|
return theme.fg("success", "✓");
|
|
338
1059
|
if (job.status === "paused")
|
|
339
1060
|
return theme.fg("warning", "■");
|
|
1061
|
+
if (job.status === "stopped")
|
|
1062
|
+
return theme.fg("warning", "■");
|
|
340
1063
|
return theme.fg("error", "✗");
|
|
341
1064
|
}
|
|
342
|
-
function widgetStepGlyph(status, theme, seed) {
|
|
1065
|
+
function widgetStepGlyph(status, theme, seed, frame) {
|
|
343
1066
|
if (status === "running")
|
|
344
|
-
return theme.fg("accent", runningGlyph(seed));
|
|
1067
|
+
return theme.fg("accent", runningGlyph(animatedSeed(seed, frame)));
|
|
345
1068
|
if (status === "complete" || status === "completed")
|
|
346
1069
|
return theme.fg("success", "✓");
|
|
347
1070
|
if (status === "failed")
|
|
348
1071
|
return theme.fg("error", "✗");
|
|
349
1072
|
if (status === "paused")
|
|
350
1073
|
return theme.fg("warning", "■");
|
|
1074
|
+
if (status === "stopped")
|
|
1075
|
+
return theme.fg("warning", "■");
|
|
351
1076
|
return theme.fg("muted", "◦");
|
|
352
1077
|
}
|
|
353
1078
|
function widgetStepStatus(status, theme) {
|
|
@@ -359,6 +1084,8 @@ function widgetStepStatus(status, theme) {
|
|
|
359
1084
|
return theme.fg("error", "failed");
|
|
360
1085
|
if (status === "paused")
|
|
361
1086
|
return theme.fg("warning", "paused");
|
|
1087
|
+
if (status === "stopped")
|
|
1088
|
+
return theme.fg("warning", "stopped");
|
|
362
1089
|
return theme.fg("dim", status);
|
|
363
1090
|
}
|
|
364
1091
|
function widgetStepActivity(step, snapshotNow) {
|
|
@@ -374,7 +1101,7 @@ function widgetStepActivity(step, snapshotNow) {
|
|
|
374
1101
|
if (step.toolCount !== undefined)
|
|
375
1102
|
facts.push(`${step.toolCount} tools`);
|
|
376
1103
|
if (step.tokens?.total)
|
|
377
|
-
facts.push(
|
|
1104
|
+
facts.push(formatTokenUsage(step.tokens, "token"));
|
|
378
1105
|
const activity = buildLiveStatusLine(step, snapshotNow);
|
|
379
1106
|
if (activity && facts.length)
|
|
380
1107
|
return `${activity} · ${facts.join(" · ")}`;
|
|
@@ -382,7 +1109,7 @@ function widgetStepActivity(step, snapshotNow) {
|
|
|
382
1109
|
return activity;
|
|
383
1110
|
return facts.join(" · ");
|
|
384
1111
|
}
|
|
385
|
-
function widgetChainDetails(job, theme, expanded = false, width = getTermWidth()) {
|
|
1112
|
+
function widgetChainDetails(job, theme, expanded = false, width = getTermWidth(), frame) {
|
|
386
1113
|
if (!job.steps?.length)
|
|
387
1114
|
return [];
|
|
388
1115
|
const total = job.chainStepCount ?? job.steps.length;
|
|
@@ -390,8 +1117,7 @@ function widgetChainDetails(job, theme, expanded = false, width = getTermWidth()
|
|
|
390
1117
|
for (const span of buildAsyncChainStepSpans(total, job.steps.length, job.parallelGroups)) {
|
|
391
1118
|
const steps = job.steps.slice(span.start, span.start + span.count);
|
|
392
1119
|
if (span.isParallel) {
|
|
393
|
-
|
|
394
|
-
lines.push(` ${widgetStepGlyph(status, theme, widgetStepsRunningSeed(steps))} Step ${span.stepIndex + 1}/${total}: ${themeBold(theme, "parallel group")} ${theme.fg("dim", "·")} ${theme.fg("dim", formatParallelOutcome(steps, span.count))}`);
|
|
1120
|
+
lines.push(...parallelWidgetGroupDetails(job, theme, { steps, total: span.count, stepIndex: span.stepIndex, chainTotal: total }, expanded, width, frame, false));
|
|
395
1121
|
continue;
|
|
396
1122
|
}
|
|
397
1123
|
const step = steps[0];
|
|
@@ -399,17 +1125,20 @@ function widgetChainDetails(job, theme, expanded = false, width = getTermWidth()
|
|
|
399
1125
|
lines.push(` ${theme.fg("dim", `◦ Step ${span.stepIndex + 1}/${total}: pending`)}`);
|
|
400
1126
|
continue;
|
|
401
1127
|
}
|
|
402
|
-
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, "Step", span.stepIndex + 1, total, expanded, width));
|
|
1128
|
+
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, "Step", span.stepIndex + 1, total, expanded, width, frame));
|
|
403
1129
|
}
|
|
404
1130
|
return lines;
|
|
405
1131
|
}
|
|
406
|
-
function widgetParallelAgentDetails(job, theme, expanded = false, width = getTermWidth()) {
|
|
1132
|
+
function widgetParallelAgentDetails(job, theme, expanded = false, width = getTermWidth(), frame) {
|
|
407
1133
|
if (!job.steps?.length)
|
|
408
1134
|
return [];
|
|
409
1135
|
if (job.mode !== "parallel" && job.mode !== "chain")
|
|
410
1136
|
return [];
|
|
411
1137
|
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length)
|
|
412
|
-
return widgetChainDetails(job, theme, expanded, width);
|
|
1138
|
+
return widgetChainDetails(job, theme, expanded, width, frame);
|
|
1139
|
+
const group = activeParallelWidgetGroup(job);
|
|
1140
|
+
if (group)
|
|
1141
|
+
return parallelWidgetGroupDetails(job, theme, group, expanded, width, frame, Boolean(job.activeParallelGroup));
|
|
413
1142
|
const total = job.stepsTotal ?? job.steps.length;
|
|
414
1143
|
const lines = [];
|
|
415
1144
|
for (const [index, step] of job.steps.entries()) {
|
|
@@ -417,8 +1146,13 @@ function widgetParallelAgentDetails(job, theme, expanded = false, width = getTer
|
|
|
417
1146
|
const activity = widgetStepActivity(step, job.updatedAt);
|
|
418
1147
|
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
419
1148
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
420
|
-
|
|
421
|
-
|
|
1149
|
+
const label = compactTaskText(step.description, step.label);
|
|
1150
|
+
const display = step.sessionName?.trim() || (label ? `${label} (${step.agent})` : step.agent);
|
|
1151
|
+
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}` : ""}`)}`);
|
|
1152
|
+
const lane = projectAsyncLane(job, step);
|
|
1153
|
+
if (lane)
|
|
1154
|
+
lines.push(...formatLaneProjectionLines(lane, theme, " "));
|
|
1155
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 6))
|
|
422
1156
|
lines.push(` ${nestedLine}`);
|
|
423
1157
|
}
|
|
424
1158
|
return lines;
|
|
@@ -466,18 +1200,16 @@ function buildChainStepSpans(details) {
|
|
|
466
1200
|
}
|
|
467
1201
|
return spans;
|
|
468
1202
|
}
|
|
469
|
-
function isChainParallelGroupActive(details) {
|
|
470
|
-
if (details.mode !== "chain")
|
|
471
|
-
return false;
|
|
472
|
-
if (details.currentStepIndex === undefined)
|
|
473
|
-
return false;
|
|
474
|
-
return buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
475
|
-
}
|
|
476
1203
|
function buildAsyncChainStepSpans(total, stepCount, parallelGroups = []) {
|
|
1204
|
+
const groupsByStep = new Map;
|
|
1205
|
+
for (const group of parallelGroups) {
|
|
1206
|
+
if (!groupsByStep.has(group.stepIndex))
|
|
1207
|
+
groupsByStep.set(group.stepIndex, group);
|
|
1208
|
+
}
|
|
477
1209
|
const spans = [];
|
|
478
1210
|
let flatIndex = 0;
|
|
479
1211
|
for (let stepIndex = 0;stepIndex < total; stepIndex++) {
|
|
480
|
-
const group =
|
|
1212
|
+
const group = groupsByStep.get(stepIndex);
|
|
481
1213
|
if (group) {
|
|
482
1214
|
spans.push({ stepIndex, start: group.start, count: group.count, isParallel: true });
|
|
483
1215
|
flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
@@ -488,6 +1220,93 @@ function buildAsyncChainStepSpans(total, stepCount, parallelGroups = []) {
|
|
|
488
1220
|
}
|
|
489
1221
|
return spans;
|
|
490
1222
|
}
|
|
1223
|
+
function normalizedParallelDisplayText(value) {
|
|
1224
|
+
if (!value?.trim())
|
|
1225
|
+
return;
|
|
1226
|
+
const normalized = oneLine(value);
|
|
1227
|
+
return normalized && normalized !== PROMPT_REDACTED ? normalized : undefined;
|
|
1228
|
+
}
|
|
1229
|
+
function parallelWidgetStepDisplayName(step) {
|
|
1230
|
+
const agent = normalizedParallelDisplayText(step.agent);
|
|
1231
|
+
const explicitLabel = normalizedParallelDisplayText(step.label);
|
|
1232
|
+
if (explicitLabel) {
|
|
1233
|
+
const label = compactTaskText(step.description, explicitLabel) ?? explicitLabel;
|
|
1234
|
+
return agent ? `${label} (${agent})` : label;
|
|
1235
|
+
}
|
|
1236
|
+
const sessionName = normalizedParallelDisplayText(step.sessionName);
|
|
1237
|
+
if (sessionName) {
|
|
1238
|
+
const sessionTask = stripRepeatedAgentPrefix(sessionName, agent);
|
|
1239
|
+
if (sessionTask && sessionTask !== PROMPT_REDACTED && sessionTask.toLowerCase() !== agent?.toLowerCase())
|
|
1240
|
+
return sessionTask;
|
|
1241
|
+
}
|
|
1242
|
+
return compactTaskText(step.description) ?? agent ?? "subagent";
|
|
1243
|
+
}
|
|
1244
|
+
function parallelWidgetStepPriority(step) {
|
|
1245
|
+
if (step.status === "running" || step.activityState === "needs_attention")
|
|
1246
|
+
return 0;
|
|
1247
|
+
switch (step.status) {
|
|
1248
|
+
case "failed":
|
|
1249
|
+
case "rejected":
|
|
1250
|
+
case "partial":
|
|
1251
|
+
case "stopped":
|
|
1252
|
+
case "paused":
|
|
1253
|
+
return 1;
|
|
1254
|
+
case "complete":
|
|
1255
|
+
case "completed":
|
|
1256
|
+
return 2;
|
|
1257
|
+
default:
|
|
1258
|
+
return 3;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
function parallelWidgetStepRows(steps, total, prioritizeActive) {
|
|
1262
|
+
const indexed = steps.map((step, index) => ({ step, index, displayName: parallelWidgetStepDisplayName(step) }));
|
|
1263
|
+
if (prioritizeActive)
|
|
1264
|
+
indexed.sort((left, right) => parallelWidgetStepPriority(left.step) - parallelWidgetStepPriority(right.step) || left.index - right.index);
|
|
1265
|
+
return withDuplicateLabelDiscriminators(indexed, total).map(({ displayName, ...row }) => row);
|
|
1266
|
+
}
|
|
1267
|
+
function activeParallelWidgetGroup(job) {
|
|
1268
|
+
const steps = job.steps ?? [];
|
|
1269
|
+
if (!steps.length)
|
|
1270
|
+
return;
|
|
1271
|
+
if (job.mode === "parallel")
|
|
1272
|
+
return { steps, total: job.stepsTotal ?? steps.length };
|
|
1273
|
+
if (job.mode !== "chain" || !job.activeParallelGroup)
|
|
1274
|
+
return;
|
|
1275
|
+
const chainTotal = job.chainStepCount ?? job.stepsTotal ?? steps.length;
|
|
1276
|
+
const spans = buildAsyncChainStepSpans(chainTotal, steps.length, job.parallelGroups);
|
|
1277
|
+
const currentStep = job.currentStep;
|
|
1278
|
+
const activeSpan = currentStep === undefined ? spans.find((span) => span.isParallel) : spans.find((span) => span.isParallel && currentStep >= span.start && currentStep < span.start + span.count);
|
|
1279
|
+
return {
|
|
1280
|
+
steps,
|
|
1281
|
+
total: activeSpan?.count ?? job.stepsTotal ?? steps.length,
|
|
1282
|
+
stepIndex: activeSpan?.stepIndex,
|
|
1283
|
+
chainTotal
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
function parallelWidgetGroupHeader(group, theme, frame) {
|
|
1287
|
+
const status = aggregateStepStatus(group.steps);
|
|
1288
|
+
const label = group.stepIndex !== undefined && group.chainTotal !== undefined ? `Step ${group.stepIndex + 1}/${group.chainTotal}: parallel group` : "parallel group";
|
|
1289
|
+
return ` ${widgetStepGlyph(status, theme, widgetStepsRunningSeed(group.steps), frame)} ${themeBold(theme, label)} ${theme.fg("dim", "·")} ${theme.fg("dim", formatParallelOutcome(group.steps, group.total))}`;
|
|
1290
|
+
}
|
|
1291
|
+
function parallelWidgetGroupDetails(job, theme, group, expanded, width, frame, prioritizeActive) {
|
|
1292
|
+
const lines = [parallelWidgetGroupHeader(group, theme, frame)];
|
|
1293
|
+
const rows = parallelWidgetStepRows(group.steps, group.total, prioritizeActive);
|
|
1294
|
+
for (const [rowIndex, row] of rows.entries()) {
|
|
1295
|
+
const marker = rowIndex === rows.length - 1 && rows.length >= group.total ? "└─" : "├─";
|
|
1296
|
+
lines.push(...foregroundStyleWidgetStepLines(job, theme, row.step, "Agent", row.index + 1, group.total, expanded, width, frame, {
|
|
1297
|
+
rowLabel: row.rowLabel,
|
|
1298
|
+
rowIndent: " ",
|
|
1299
|
+
detailIndent: " ",
|
|
1300
|
+
rowMarker: marker,
|
|
1301
|
+
includeCurrentPath: true
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1304
|
+
for (let index = rows.length;index < group.total; index++) {
|
|
1305
|
+
const marker = index === group.total - 1 ? "└─" : "├─";
|
|
1306
|
+
lines.push(` ${marker} ${theme.fg("muted", "◦")} ${theme.fg("dim", "pending")}`);
|
|
1307
|
+
}
|
|
1308
|
+
return lines;
|
|
1309
|
+
}
|
|
491
1310
|
function isDoneResult(result) {
|
|
492
1311
|
const status = result.progress?.status;
|
|
493
1312
|
if (status === "completed")
|
|
@@ -501,28 +1320,71 @@ function isDoneResult(result) {
|
|
|
501
1320
|
function workflowGraphHasStatus(details, statuses) {
|
|
502
1321
|
return details.workflowGraph?.nodes.some((node) => statuses.includes(node.status)) ?? false;
|
|
503
1322
|
}
|
|
1323
|
+
function chainSpanStatus(details, span) {
|
|
1324
|
+
if (span.status)
|
|
1325
|
+
return span.status;
|
|
1326
|
+
const results = details.results.slice(span.start, span.start + span.count);
|
|
1327
|
+
if (results.some((result) => isResultRunning(result)))
|
|
1328
|
+
return "running";
|
|
1329
|
+
if (results.some((result) => !hasTerminalResultFlag(result) && (result.progress?.status === "failed" || result.exitCode !== 0) && !isResultRunning(result)))
|
|
1330
|
+
return "failed";
|
|
1331
|
+
if (results.some((result) => result.stopped))
|
|
1332
|
+
return "stopped";
|
|
1333
|
+
if (results.some((result) => result.interrupted))
|
|
1334
|
+
return "paused";
|
|
1335
|
+
if (results.some((result) => result.detached || result.progress?.status === "detached"))
|
|
1336
|
+
return "detached";
|
|
1337
|
+
if (results.length < span.count)
|
|
1338
|
+
return "pending";
|
|
1339
|
+
if (results.length > 0 && results.every(isDoneResult))
|
|
1340
|
+
return "completed";
|
|
1341
|
+
return "pending";
|
|
1342
|
+
}
|
|
1343
|
+
function withDuplicateForegroundLabels(entries, total) {
|
|
1344
|
+
return withDuplicateLabelDiscriminators(entries.map((entry) => ({ ...entry, index: entry.displayIndex, displayName: entry.agentName })), total).map(({ displayName, rowLabel, ...entry }) => ({
|
|
1345
|
+
...entry,
|
|
1346
|
+
rowLabel: rowLabel === displayName ? undefined : rowLabel.slice(0, -(displayName.length + 2))
|
|
1347
|
+
}));
|
|
1348
|
+
}
|
|
504
1349
|
function buildChainRenderEntries(details, label) {
|
|
505
1350
|
if (details.mode !== "chain" || !label.hasParallelInChain || label.showActiveGroupOnly)
|
|
506
1351
|
return;
|
|
507
1352
|
const entries = [];
|
|
508
1353
|
for (const span of buildChainStepSpans(details)) {
|
|
509
|
-
if (span.isParallel
|
|
1354
|
+
if (span.isParallel) {
|
|
510
1355
|
entries.push({
|
|
511
|
-
kind: "
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
status: span.status ?? "pending",
|
|
1356
|
+
kind: "group",
|
|
1357
|
+
stepLabel: `Step ${span.stepIndex + 1}/${label.logicalStepCount}: parallel group`,
|
|
1358
|
+
groupLabel: span.label?.trim() || undefined,
|
|
1359
|
+
status: chainSpanStatus(details, span),
|
|
516
1360
|
error: span.error
|
|
517
1361
|
});
|
|
1362
|
+
const groupEntries = [];
|
|
1363
|
+
for (let index = span.start;index < span.start + span.count; index++) {
|
|
1364
|
+
const result = details.results[index];
|
|
1365
|
+
const localIndex = foregroundResultIndex(details, index);
|
|
1366
|
+
const displayIndex = localIndex >= span.start && localIndex < span.start + span.count ? localIndex - span.start : index - span.start;
|
|
1367
|
+
groupEntries.push({
|
|
1368
|
+
kind: "result",
|
|
1369
|
+
resultIndex: index,
|
|
1370
|
+
rowNumber: index - span.start + 1,
|
|
1371
|
+
agentName: foregroundResultDisplayName(details, index, result, `agent-${displayIndex + 1}`),
|
|
1372
|
+
displayIndex,
|
|
1373
|
+
isParallel: true
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
entries.push(...withDuplicateForegroundLabels(groupEntries, span.count));
|
|
518
1377
|
continue;
|
|
519
1378
|
}
|
|
520
1379
|
for (let index = span.start;index < span.start + span.count; index++) {
|
|
1380
|
+
const result = details.results[index];
|
|
521
1381
|
entries.push({
|
|
522
1382
|
kind: "result",
|
|
523
1383
|
resultIndex: index,
|
|
524
|
-
rowNumber:
|
|
525
|
-
|
|
1384
|
+
rowNumber: span.stepIndex + 1,
|
|
1385
|
+
rowLabel: resultRowLabel(label, span.stepIndex + 1),
|
|
1386
|
+
agentName: foregroundResultDisplayName(details, index, result, details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`),
|
|
1387
|
+
displayIndex: foregroundResultIndex(details, index)
|
|
526
1388
|
});
|
|
527
1389
|
}
|
|
528
1390
|
}
|
|
@@ -531,7 +1393,7 @@ function buildChainRenderEntries(details, label) {
|
|
|
531
1393
|
function buildMultiProgressLabel(details, hasRunning) {
|
|
532
1394
|
const stepSpans = buildChainStepSpans(details);
|
|
533
1395
|
const hasParallelInChain = details.mode === "chain" && stepSpans.some((span) => span.isParallel);
|
|
534
|
-
const activeParallelGroup =
|
|
1396
|
+
const activeParallelGroup = details.mode === "chain" && details.currentStepIndex !== undefined && stepSpans.some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
535
1397
|
const itemTitle = details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
|
|
536
1398
|
if (details.mode === "parallel") {
|
|
537
1399
|
const totalCount = details.totalSteps ?? details.results.length;
|
|
@@ -546,13 +1408,13 @@ function buildMultiProgressLabel(details, hasRunning) {
|
|
|
546
1408
|
const index = result.progress?.index ?? progressFromArray?.index ?? i;
|
|
547
1409
|
if (index < 0 || index >= totalCount)
|
|
548
1410
|
continue;
|
|
549
|
-
const status = result.
|
|
1411
|
+
const status = result.stopped ? "stopped" : result.interrupted || result.detached ? "detached" : result.progress?.status ?? (result.exitCode === 0 ? "completed" : "failed");
|
|
550
1412
|
statuses[index] = status;
|
|
551
1413
|
}
|
|
552
1414
|
const running = statuses.filter((status) => status === "running").length;
|
|
553
1415
|
const done = statuses.filter((status) => status === "completed").length;
|
|
554
1416
|
const headerLabel = hasRunning ? `${formatAgentRunningLabel(running)} · ${done}/${totalCount} done` : `${done}/${totalCount} done`;
|
|
555
|
-
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: totalCount, showActiveGroupOnly: false };
|
|
1417
|
+
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: totalCount, showActiveGroupOnly: false, logicalStepCount: totalCount };
|
|
556
1418
|
}
|
|
557
1419
|
if (activeParallelGroup) {
|
|
558
1420
|
const currentStepIndex = details.currentStepIndex;
|
|
@@ -564,8 +1426,8 @@ function buildMultiProgressLabel(details, hasRunning) {
|
|
|
564
1426
|
let done = 0;
|
|
565
1427
|
for (let index = groupStart;index < groupEnd; index++) {
|
|
566
1428
|
const progressEntry = details.progress?.find((progress) => progress.index === index);
|
|
567
|
-
const resultEntry = details.results.find((result) => result.progress?.index === index);
|
|
568
|
-
if (progressEntry?.status === "running") {
|
|
1429
|
+
const resultEntry = details.results.find((result) => result.progress?.index === index) ?? details.results[index];
|
|
1430
|
+
if (progressEntry?.status === "running" && (!resultEntry || !hasTerminalResultFlag(resultEntry))) {
|
|
569
1431
|
running++;
|
|
570
1432
|
continue;
|
|
571
1433
|
}
|
|
@@ -578,7 +1440,7 @@ function buildMultiProgressLabel(details, hasRunning) {
|
|
|
578
1440
|
}
|
|
579
1441
|
const totalSteps = details.totalSteps ?? details.chainAgents?.length ?? 1;
|
|
580
1442
|
const headerLabel = hasRunning ? `step ${currentStepIndex + 1}/${totalSteps} · parallel group: ${formatAgentRunningLabel(running)} · ${done}/${groupSize} done` : `step ${currentStepIndex + 1}/${totalSteps} · parallel group: ${done}/${groupSize} done`;
|
|
581
|
-
return { headerLabel, itemTitle, totalCount: groupSize, hasParallelInChain, activeParallelGroup, groupStartIndex: groupStart, groupEndIndex: groupEnd, showActiveGroupOnly: true };
|
|
1443
|
+
return { headerLabel, itemTitle, totalCount: groupSize, hasParallelInChain, activeParallelGroup, groupStartIndex: groupStart, groupEndIndex: groupEnd, showActiveGroupOnly: true, logicalStepCount: totalSteps };
|
|
582
1444
|
}
|
|
583
1445
|
if (details.mode === "chain" && details.chainAgents?.length) {
|
|
584
1446
|
const totalCount = details.totalSteps ?? details.chainAgents.length;
|
|
@@ -599,32 +1461,51 @@ function buildMultiProgressLabel(details, hasRunning) {
|
|
|
599
1461
|
}).length;
|
|
600
1462
|
const currentStep = details.currentStepIndex !== undefined ? details.currentStepIndex + 1 : Math.min(totalCount, doneLogical + (hasRunning ? 1 : 0));
|
|
601
1463
|
const headerLabel = hasRunning ? `step ${currentStep}/${totalCount}` : `step ${doneLogical}/${totalCount}`;
|
|
602
|
-
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
|
|
1464
|
+
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false, logicalStepCount: totalCount };
|
|
603
1465
|
}
|
|
604
1466
|
const totalCount = details.totalSteps ?? details.results.length;
|
|
605
1467
|
const currentStep = details.currentStepIndex !== undefined ? details.currentStepIndex + 1 : Math.min(totalCount, details.results.filter(isDoneResult).length + (hasRunning ? 1 : 0));
|
|
606
1468
|
const done = details.results.filter(isDoneResult).length;
|
|
607
1469
|
const headerLabel = hasRunning ? `step ${currentStep}/${totalCount}` : `step ${done}/${totalCount}`;
|
|
608
|
-
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
|
|
1470
|
+
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false, logicalStepCount: totalCount };
|
|
609
1471
|
}
|
|
610
|
-
function resultRowLabel(
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
1472
|
+
function resultRowLabel(label, stepNumber) {
|
|
1473
|
+
if (label.itemTitle === "Agent")
|
|
1474
|
+
return;
|
|
1475
|
+
if (shouldSuppressSingleStep(label.logicalStepCount))
|
|
1476
|
+
return;
|
|
1477
|
+
return `Step ${stepNumber}/${label.logicalStepCount}`;
|
|
1478
|
+
}
|
|
1479
|
+
function buildForegroundResultEntries(details, label, displayStart, displayEnd, useResultsDirectly) {
|
|
1480
|
+
const fallbackLabel = label.itemTitle.toLowerCase();
|
|
1481
|
+
const entries = Array.from({ length: displayEnd - displayStart }, (_, offset) => {
|
|
1482
|
+
const index = displayStart + offset;
|
|
1483
|
+
const result = details.results[index];
|
|
1484
|
+
const stableIndex = foregroundResultIndex(details, index);
|
|
1485
|
+
const rowNumber = label.showActiveGroupOnly ? index - label.groupStartIndex + 1 : stableIndex + 1;
|
|
1486
|
+
const fallbackAgent = useResultsDirectly ? result?.agent || `${fallbackLabel}-${rowNumber}` : details.chainAgents[index] || result?.agent || `${fallbackLabel}-${rowNumber}`;
|
|
1487
|
+
const displayIndex = label.activeParallelGroup && stableIndex >= label.groupStartIndex && stableIndex < label.groupEndIndex ? stableIndex - label.groupStartIndex : label.activeParallelGroup ? index - label.groupStartIndex : stableIndex;
|
|
1488
|
+
return {
|
|
1489
|
+
kind: "result",
|
|
1490
|
+
resultIndex: index,
|
|
1491
|
+
rowNumber,
|
|
1492
|
+
rowLabel: resultRowLabel(label, rowNumber),
|
|
1493
|
+
agentName: foregroundResultDisplayName(details, index, result, fallbackAgent),
|
|
1494
|
+
displayIndex
|
|
1495
|
+
};
|
|
1496
|
+
});
|
|
1497
|
+
return label.itemTitle === "Agent" ? withDuplicateForegroundLabels(entries, label.totalCount) : entries;
|
|
623
1498
|
}
|
|
624
|
-
function widgetStats(job, theme) {
|
|
1499
|
+
function widgetStats(job, theme, projection = buildWorkflowWidgetProjection(job)) {
|
|
625
1500
|
const parts = [];
|
|
626
|
-
const
|
|
627
|
-
|
|
1501
|
+
const { stageProgress } = projection;
|
|
1502
|
+
const stepsTotal = stageProgress?.total ?? job.stepsTotal ?? (job.agents?.length ?? 1);
|
|
1503
|
+
const isSingleChild = isSingleChildAsyncJob(job);
|
|
1504
|
+
if (stageProgress) {
|
|
1505
|
+
const currentStage = stageProgress.current !== undefined ? projection.stages[stageProgress.current] : undefined;
|
|
1506
|
+
const focus = currentStage ? [compactTaskText(undefined, currentStage.label) ?? boundedLaneValue(currentStage.id), currentStage.agent ? boundedLaneValue(currentStage.agent) : "", workflowNodeStatusLabel(currentStage.status)].filter(Boolean) : [];
|
|
1507
|
+
parts.push(["staged lane", stageProgress.current !== undefined ? `stage ${stageProgress.current + 1}/${stageProgress.total}` : `${stageProgress.total} stages`, ...focus].join(" · "));
|
|
1508
|
+
} else if (job.activeParallelGroup) {
|
|
628
1509
|
const running = job.runningSteps ?? (job.status === "running" ? 1 : 0);
|
|
629
1510
|
const done = job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
|
|
630
1511
|
if (job.mode === "parallel") {
|
|
@@ -642,10 +1523,10 @@ function widgetStats(job, theme) {
|
|
|
642
1523
|
parts.push(`step ${logicalStep + 1}/${total} · parallel group: ${groupParts.join(" · ")}`);
|
|
643
1524
|
}
|
|
644
1525
|
} else if (job.currentStep !== undefined) {
|
|
645
|
-
if (job.mode === "chain"
|
|
1526
|
+
if (job.mode === "chain") {
|
|
646
1527
|
const total = job.chainStepCount ?? stepsTotal;
|
|
647
|
-
parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`);
|
|
648
|
-
} else {
|
|
1528
|
+
parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups ?? []) + 1}/${total}`);
|
|
1529
|
+
} else if (!isSingleChild) {
|
|
649
1530
|
parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
|
|
650
1531
|
}
|
|
651
1532
|
} else if (stepsTotal > 1) {
|
|
@@ -654,7 +1535,7 @@ function widgetStats(job, theme) {
|
|
|
654
1535
|
if (job.toolCount !== undefined)
|
|
655
1536
|
parts.push(formatToolUseStat(job.toolCount));
|
|
656
1537
|
if (job.totalTokens?.total)
|
|
657
|
-
parts.push(
|
|
1538
|
+
parts.push(formatTokenUsage(job.totalTokens, "token"));
|
|
658
1539
|
if (job.startedAt !== undefined && job.updatedAt !== undefined)
|
|
659
1540
|
parts.push(formatDuration(Math.max(0, job.updatedAt - job.startedAt)));
|
|
660
1541
|
return statJoin(theme, parts);
|
|
@@ -663,7 +1544,7 @@ function widgetStepStats(theme, step) {
|
|
|
663
1544
|
return statJoin(theme, [
|
|
664
1545
|
step.turnCount !== undefined ? `${step.turnCount} turns` : "",
|
|
665
1546
|
step.toolCount !== undefined ? formatToolUseStat(step.toolCount) : "",
|
|
666
|
-
step.tokens
|
|
1547
|
+
step.tokens ? step.contextLimit !== undefined ? formatContextUsage(step.tokens, step.contextLimit) ?? formatTokenUsage(step.tokens, "token") : step.tokens.total ? formatTokenUsage(step.tokens, "token") : "" : "",
|
|
667
1548
|
step.durationMs !== undefined ? formatDuration(step.durationMs) : ""
|
|
668
1549
|
]);
|
|
669
1550
|
}
|
|
@@ -688,6 +1569,8 @@ function widgetOutputPath(job, step) {
|
|
|
688
1569
|
return path.join(job.asyncDir, `output-${step.index}.log`);
|
|
689
1570
|
}
|
|
690
1571
|
function nestedRunName(run) {
|
|
1572
|
+
if (run.sessionName?.trim())
|
|
1573
|
+
return run.sessionName.trim();
|
|
691
1574
|
if (run.agent)
|
|
692
1575
|
return run.agent;
|
|
693
1576
|
if (run.agents?.length)
|
|
@@ -701,13 +1584,33 @@ function nestedStatusGlyph(state, theme, seed) {
|
|
|
701
1584
|
return theme.fg("success", "✓");
|
|
702
1585
|
if (state === "failed")
|
|
703
1586
|
return theme.fg("error", "✗");
|
|
1587
|
+
if (state === "partial")
|
|
1588
|
+
return theme.fg("warning", "■");
|
|
704
1589
|
if (state === "paused")
|
|
705
1590
|
return theme.fg("warning", "■");
|
|
1591
|
+
if (state === "stopped")
|
|
1592
|
+
return theme.fg("warning", "■");
|
|
706
1593
|
return theme.fg("muted", "◦");
|
|
707
1594
|
}
|
|
708
1595
|
function nestedRunSeed(run) {
|
|
709
1596
|
return runningSeed(run.lastUpdate, run.lastActivityAt, run.currentStep, run.toolCount, run.turnCount, run.totalTokens?.total, run.currentToolStartedAt);
|
|
710
1597
|
}
|
|
1598
|
+
function formatClockTime(ms) {
|
|
1599
|
+
if (ms === undefined || !Number.isFinite(ms))
|
|
1600
|
+
return;
|
|
1601
|
+
const date = new Date(ms);
|
|
1602
|
+
const pad = (value) => value.toString().padStart(2, "0");
|
|
1603
|
+
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
1604
|
+
}
|
|
1605
|
+
function nestedRunEventTime(run) {
|
|
1606
|
+
return run.state === "running" ? run.lastActivityAt ?? run.currentToolStartedAt ?? run.lastUpdate ?? run.startedAt : run.endedAt ?? run.lastUpdate ?? run.lastActivityAt ?? run.startedAt;
|
|
1607
|
+
}
|
|
1608
|
+
function nestedStepTimestamp(step, fallback) {
|
|
1609
|
+
return formatClockTime(step.status === "running" ? step.lastActivityAt ?? step.currentToolStartedAt ?? fallback ?? step.startedAt : step.endedAt ?? step.lastActivityAt ?? fallback ?? step.startedAt);
|
|
1610
|
+
}
|
|
1611
|
+
function nestedTimestampPrefix(timestamp) {
|
|
1612
|
+
return timestamp ? `[${timestamp}] ` : "";
|
|
1613
|
+
}
|
|
711
1614
|
function nestedActivity(input, state, snapshotNow) {
|
|
712
1615
|
const facts = [];
|
|
713
1616
|
if (input.currentTool && input.currentToolStartedAt !== undefined && snapshotNow !== undefined)
|
|
@@ -733,6 +1636,10 @@ function nestedActivity(input, state, snapshotNow) {
|
|
|
733
1636
|
return "queued…";
|
|
734
1637
|
if (state === "paused")
|
|
735
1638
|
return "Paused";
|
|
1639
|
+
if (state === "stopped")
|
|
1640
|
+
return "Stopped";
|
|
1641
|
+
if (state === "partial")
|
|
1642
|
+
return "Partial";
|
|
736
1643
|
if (state === "failed")
|
|
737
1644
|
return "Failed";
|
|
738
1645
|
return "Done";
|
|
@@ -741,8 +1648,53 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
|
|
|
741
1648
|
if (!children?.length || lineBudget <= 0)
|
|
742
1649
|
return [];
|
|
743
1650
|
if (!expanded) {
|
|
744
|
-
const
|
|
745
|
-
|
|
1651
|
+
const rows = [];
|
|
1652
|
+
const maxLeaves = 4;
|
|
1653
|
+
const maxLines = Math.min(6, lineBudget);
|
|
1654
|
+
let leaves = 0;
|
|
1655
|
+
let overflow = 0;
|
|
1656
|
+
const appendLeaf = (step, prefix, fallback) => {
|
|
1657
|
+
if (leaves >= maxLeaves) {
|
|
1658
|
+
overflow++;
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
leaves++;
|
|
1662
|
+
const state = "status" in step ? step.status : step.state;
|
|
1663
|
+
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
1664
|
+
const activity = nestedActivity(step, state, snapshotNow ?? fallback);
|
|
1665
|
+
const timestamp = "status" in step ? nestedStepTimestamp(step, fallback) : formatClockTime(nestedRunEventTime(step));
|
|
1666
|
+
const error = step.error ? ` · ${step.error}` : "";
|
|
1667
|
+
const name = "status" in step ? childDisplayName(step) : nestedRunName(step);
|
|
1668
|
+
rows.push({
|
|
1669
|
+
prefix,
|
|
1670
|
+
text: `${nestedTimestampPrefix(timestamp)}${nestedStatusGlyph(state, theme)} ${name} · ${state}${modelThinking ? ` · ${modelThinking}` : ""}${activity ? ` · ${activity}` : ""}${error}`
|
|
1671
|
+
});
|
|
1672
|
+
};
|
|
1673
|
+
for (const child of children) {
|
|
1674
|
+
const steps = child.mode === "parallel" || child.mode === "chain" ? child.steps ?? [] : [];
|
|
1675
|
+
if (steps.length > 0) {
|
|
1676
|
+
const ownerModelThinking = formatModelThinking(child.model, child.thinking);
|
|
1677
|
+
const ownerActivity = nestedActivity(child, child.state, snapshotNow ?? child.lastUpdate);
|
|
1678
|
+
const ownerError = child.error ? ` · ${child.error}` : "";
|
|
1679
|
+
rows.push({
|
|
1680
|
+
prefix: "↳ ",
|
|
1681
|
+
text: `OWNER ${nestedStatusGlyph(child.state, theme, nestedRunSeed(child))} ${nestedRunName(child)} · ${child.state}${ownerModelThinking ? ` · ${ownerModelThinking}` : ""}${ownerActivity ? ` · ${ownerActivity}` : ""}${ownerError}`
|
|
1682
|
+
});
|
|
1683
|
+
for (const step of steps)
|
|
1684
|
+
appendLeaf(step, "↳ │ ", child.lastUpdate);
|
|
1685
|
+
} else {
|
|
1686
|
+
appendLeaf(child, "↳ ", child.lastUpdate);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
if (overflow > 0)
|
|
1690
|
+
rows.push({ prefix: "↳ ", text: `… +${overflow} more nested leaves` });
|
|
1691
|
+
const visibleRows = rows.length <= maxLines ? rows : overflow > 0 ? [...rows.slice(0, Math.max(0, maxLines - 1)), rows.at(-1)] : rows.slice(0, maxLines);
|
|
1692
|
+
return visibleRows.map((row, index) => {
|
|
1693
|
+
const marker = index === visibleRows.length - 1 ? "└─" : "├─";
|
|
1694
|
+
const prefix = row.prefix;
|
|
1695
|
+
const text = row.text.startsWith("OWNER ") ? row.text.slice("OWNER ".length) : row.text;
|
|
1696
|
+
return truncLine(theme.fg("dim", `${prefix}${marker} ${text}`), width);
|
|
1697
|
+
});
|
|
746
1698
|
}
|
|
747
1699
|
const lines = [];
|
|
748
1700
|
const maxDepth = 2;
|
|
@@ -765,7 +1717,8 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
|
|
|
765
1717
|
}
|
|
766
1718
|
const activity = nestedActivity(child, child.state, snapshotNow ?? child.lastUpdate);
|
|
767
1719
|
const error = child.error ? ` · ${child.error}` : "";
|
|
768
|
-
|
|
1720
|
+
const modelThinking = formatModelThinking(child.model, child.thinking);
|
|
1721
|
+
lines.push(theme.fg("dim", `${prefix}↳ ${nestedTimestampPrefix(formatClockTime(nestedRunEventTime(child)))}${nestedStatusGlyph(child.state, theme, nestedRunSeed(child))} ${nestedRunName(child)} · ${child.state}${modelThinking ? ` · ${modelThinking}` : ""} · ${activity}${error}`));
|
|
769
1722
|
if (depth === maxDepth) {
|
|
770
1723
|
const aggregate = formatNestedAggregate([...child.steps?.flatMap((step) => step.children ?? []) ?? [], ...child.children ?? []]);
|
|
771
1724
|
if (aggregate && lines.length < lineBudget)
|
|
@@ -775,7 +1728,8 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
|
|
|
775
1728
|
for (const step of child.steps ?? []) {
|
|
776
1729
|
if (lines.length >= lineBudget)
|
|
777
1730
|
return;
|
|
778
|
-
|
|
1731
|
+
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
1732
|
+
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)}`));
|
|
779
1733
|
append(step.children, depth + 1, `${prefix} `);
|
|
780
1734
|
}
|
|
781
1735
|
append(child.children, depth + 1, `${prefix} `);
|
|
@@ -784,88 +1738,160 @@ function formatNestedWidgetLines(children, theme, width, expanded, snapshotNow,
|
|
|
784
1738
|
append(children, 0, "");
|
|
785
1739
|
return lines.map((line) => truncLine(line, width));
|
|
786
1740
|
}
|
|
787
|
-
function foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index, total, expanded, width) {
|
|
1741
|
+
function foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index, total, expanded, width, frame, options) {
|
|
1742
|
+
const rowIndent = options?.rowIndent ?? " ";
|
|
1743
|
+
const detailIndent = options?.detailIndent ?? " ";
|
|
788
1744
|
const status = widgetStepStatus(step.status, theme);
|
|
789
1745
|
const stats = widgetStepStats(theme, step);
|
|
790
1746
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
791
|
-
const
|
|
1747
|
+
const collapseDetails = shouldCollapseSingleChildDetails(job, step);
|
|
1748
|
+
const displayName = collapseDetails ? singleChildAgentName(job, step) : childDisplayName(step);
|
|
1749
|
+
const stageName = itemTitle === "Stage" ? compactTaskText(undefined, step.label) ?? boundedLaneValue(step.workflowKey) ?? displayName : undefined;
|
|
1750
|
+
const stageIdentity = stageName && stageName !== displayName ? `${stageName} (${displayName})` : stageName ?? displayName;
|
|
1751
|
+
const rowLabel = collapseDetails ? displayName : options?.rowLabel ?? `${itemTitle} ${index}/${total}: ${itemTitle === "Stage" ? stageIdentity : displayName}`;
|
|
1752
|
+
const rowMarker = options?.rowMarker ? `${options.rowMarker} ` : "";
|
|
1753
|
+
const lines = [`${rowIndent}${rowMarker}${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1), frame)} ${themeBold(theme, rowLabel)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
|
|
1754
|
+
const lane = projectAsyncLane(job, step);
|
|
1755
|
+
if (lane)
|
|
1756
|
+
lines.push(...formatLaneProjectionLines(lane, theme, detailIndent));
|
|
1757
|
+
const task = collapseDetails ? singleChildTask(job, step) : compactTaskText(step.description, step.label);
|
|
1758
|
+
if (task)
|
|
1759
|
+
lines.push(`${detailIndent}${theme.fg("dim", `task: ${task}`)}`);
|
|
792
1760
|
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
lines.push(
|
|
1761
|
+
const currentPath = options?.includeCurrentPath && step.currentPath ? shortenPath(step.currentPath) : undefined;
|
|
1762
|
+
const activityWithPath = currentPath && activity && !activity.includes(currentPath) ? `${activity} · ${currentPath}` : activity ?? currentPath;
|
|
1763
|
+
if (activityWithPath)
|
|
1764
|
+
lines.push(`${detailIndent}${theme.fg("dim", `⎿ ${activityWithPath}`)}`);
|
|
1765
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
1766
|
+
lines.push(`${detailIndent}${nestedLine}`);
|
|
797
1767
|
}
|
|
1768
|
+
const error = step.error?.trim() || step.execution?.error?.trim();
|
|
1769
|
+
if (error)
|
|
1770
|
+
lines.push(`${detailIndent}${theme.fg("error", `error: ${oneLine(error)}`)}`);
|
|
798
1771
|
if (step.status === "running") {
|
|
799
1772
|
if (!expanded)
|
|
800
|
-
lines.push(
|
|
1773
|
+
lines.push(`${detailIndent}${theme.fg("accent", liveDetailHintText())}`);
|
|
801
1774
|
const output = widgetOutputPath(job, step);
|
|
802
1775
|
if (output)
|
|
803
|
-
lines.push(
|
|
1776
|
+
lines.push(`${detailIndent}${theme.fg("dim", `output: ${shortenPath(output)}`)}`);
|
|
804
1777
|
if (expanded) {
|
|
805
1778
|
const liveStatus = buildLiveStatusLine(step, job.updatedAt);
|
|
806
1779
|
if (liveStatus && liveStatus !== activity)
|
|
807
|
-
lines.push(
|
|
1780
|
+
lines.push(`${detailIndent}${theme.fg("accent", liveStatus)}`);
|
|
808
1781
|
for (const tool of step.recentTools?.slice(-3) ?? []) {
|
|
809
1782
|
const maxArgsLen = Math.max(40, width - 30);
|
|
810
|
-
const argsPreview = tool.args
|
|
811
|
-
lines.push(
|
|
1783
|
+
const argsPreview = renderToolArgsPreview(tool.args, maxArgsLen, expanded);
|
|
1784
|
+
lines.push(`${detailIndent} ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`);
|
|
812
1785
|
}
|
|
813
|
-
for (const line of step.recentOutput
|
|
814
|
-
lines.push(
|
|
1786
|
+
for (const line of compactRecentOutputLines(step.recentOutput)) {
|
|
1787
|
+
lines.push(`${detailIndent} ${theme.fg("dim", line)}`);
|
|
815
1788
|
}
|
|
816
1789
|
}
|
|
817
1790
|
}
|
|
818
1791
|
return lines;
|
|
819
1792
|
}
|
|
820
|
-
function
|
|
821
|
-
|
|
1793
|
+
function hostStepWidgetLines(job, theme, indent) {
|
|
1794
|
+
const rows = projectAsyncWorkflowRows([], job.hostSteps).filter((row) => row.kind);
|
|
1795
|
+
const visible = rows.slice(0, 8);
|
|
1796
|
+
const lines = visible.map((row) => {
|
|
1797
|
+
const state = hostStepVerdictLabel(row.state, row.verdict);
|
|
1798
|
+
const glyph = state === "running" ? theme.fg("accent", "●") : state === "pending" ? theme.fg("muted", "◦") : state === "pass" ? theme.fg("success", "✓") : state === "fail" || state === "error" ? theme.fg("error", "✗") : theme.fg("warning", "■");
|
|
1799
|
+
const details = [
|
|
1800
|
+
row.provider ? `provider:${row.provider}` : undefined,
|
|
1801
|
+
row.role ? `role:${row.role}` : undefined,
|
|
1802
|
+
row.target,
|
|
1803
|
+
row.detail,
|
|
1804
|
+
row.reasonCode ? `reason:${row.reasonCode}` : undefined,
|
|
1805
|
+
row.freshness?.stale ? "stale" : row.freshness?.observedRef ? `ref:${row.freshness.observedRef}` : undefined,
|
|
1806
|
+
row.reportPath ? `out:${hostStepReportName(row.reportPath)}` : undefined
|
|
1807
|
+
].filter(Boolean).join(" · ");
|
|
1808
|
+
return `${indent}${glyph} ${row.kind}: ${row.name} · ${state}${details ? ` · ${details}` : ""}`;
|
|
1809
|
+
});
|
|
1810
|
+
if (rows.length > visible.length)
|
|
1811
|
+
lines.push(`${indent}${theme.fg("dim", `… +${rows.length - visible.length} host steps hidden`)}`);
|
|
1812
|
+
return lines;
|
|
1813
|
+
}
|
|
1814
|
+
function foregroundStyleWidgetDetails(job, theme, expanded, width, frame, projection = buildWorkflowWidgetProjection(job)) {
|
|
1815
|
+
const { steps } = projection;
|
|
1816
|
+
if (!steps.length) {
|
|
1817
|
+
const lane = projectAsyncLane(job, laneStepForJob(job, steps));
|
|
822
1818
|
return [
|
|
1819
|
+
...workflowPreflightLines(job, expanded),
|
|
1820
|
+
...lane ? formatLaneProjectionLines(lane, theme, " ") : [],
|
|
1821
|
+
...hostStepWidgetLines(job, theme, " "),
|
|
823
1822
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
824
|
-
...formatNestedWidgetLines(job.nestedChildren, theme, width, expanded, job.updatedAt).map((line) => ` ${line}`)
|
|
1823
|
+
...formatNestedWidgetLines(job.nestedChildren, theme, width, expanded, job.updatedAt, expanded ? 12 : 6).map((line) => ` ${line}`)
|
|
825
1824
|
];
|
|
1825
|
+
}
|
|
826
1826
|
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length)
|
|
827
|
-
return widgetChainDetails(job, theme, expanded, width);
|
|
828
|
-
const
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1827
|
+
return widgetChainDetails(job, theme, expanded, width, frame);
|
|
1828
|
+
const lines = workflowPreflightLines(job, expanded);
|
|
1829
|
+
const group = activeParallelWidgetGroup(job);
|
|
1830
|
+
if (group) {
|
|
1831
|
+
lines.push(...parallelWidgetGroupDetails(job, theme, group, expanded, width, frame, Boolean(job.activeParallelGroup)));
|
|
1832
|
+
} else {
|
|
1833
|
+
const { stageProgress, plannedKeys } = projection;
|
|
1834
|
+
const total = job.mode === "chain" ? job.chainStepCount ?? job.stepsTotal ?? steps.length : stageProgress?.total ?? job.stepsTotal ?? steps.length;
|
|
1835
|
+
const stageTotal = stageProgress?.total ?? total;
|
|
1836
|
+
const extraStepCount = plannedKeys ? steps.filter((step) => step.workflowKey === undefined || !plannedKeys.has(step.workflowKey)).length : total;
|
|
1837
|
+
let extraStepIndex = 0;
|
|
1838
|
+
for (const [index, step] of steps.entries()) {
|
|
1839
|
+
const isPlannedStage = plannedKeys !== undefined && step.workflowKey !== undefined && plannedKeys.has(step.workflowKey);
|
|
1840
|
+
const itemTitle = isPlannedStage ? "Stage" : "Step";
|
|
1841
|
+
const displayIndex = isPlannedStage ? (step.index ?? index) + 1 : plannedKeys ? ++extraStepIndex : index + 1;
|
|
1842
|
+
const displayTotal = isPlannedStage ? stageTotal : extraStepCount;
|
|
1843
|
+
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, itemTitle, displayIndex, displayTotal, expanded, width, frame));
|
|
1844
|
+
}
|
|
833
1845
|
}
|
|
834
|
-
|
|
1846
|
+
lines.push(...hostStepWidgetLines(job, theme, " "));
|
|
1847
|
+
const attached = new Set(steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
835
1848
|
const unattached = job.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
836
|
-
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt)) {
|
|
1849
|
+
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt, expanded ? 12 : 6)) {
|
|
837
1850
|
lines.push(` ${nestedLine}`);
|
|
838
1851
|
}
|
|
839
1852
|
return lines;
|
|
840
1853
|
}
|
|
841
|
-
function buildSingleWidgetLines(job, theme, width, expanded) {
|
|
842
|
-
const stats = widgetStats(job, theme);
|
|
843
|
-
const count = job.mode === "chain" ? job.chainStepCount : job.stepsTotal ?? job.agents?.length ?? job.steps?.length;
|
|
1854
|
+
function buildSingleWidgetLines(job, theme, width, expanded, frame, projection = buildWorkflowWidgetProjection(job)) {
|
|
1855
|
+
const stats = widgetStats(job, theme, projection);
|
|
1856
|
+
const count = job.mode === "chain" ? job.chainStepCount : projection.stageProgress?.total ?? job.stepsTotal ?? job.agents?.length ?? job.steps?.length;
|
|
844
1857
|
const mode = widgetJobName(job);
|
|
845
|
-
const title = `async subagent ${mode}${count && count > 1 ? ` (${count})` : ""}`;
|
|
1858
|
+
const title = isSingleChildAsyncJob(job) ? "async subagent" : `async subagent ${mode}${count && count > 1 ? ` (${count})` : ""}`;
|
|
1859
|
+
const collapseDetails = job.steps?.length === 1 && shouldCollapseSingleChildDetails(job, job.steps[0]);
|
|
1860
|
+
const summary = `${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, mode)}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`;
|
|
846
1861
|
return [
|
|
847
1862
|
`${theme.fg("toolTitle", themeBold(theme, title))} ${theme.fg("dim", "· background")}`,
|
|
848
|
-
|
|
849
|
-
...foregroundStyleWidgetDetails(job, theme, expanded, width)
|
|
1863
|
+
...collapseDetails ? [] : [summary],
|
|
1864
|
+
...foregroundStyleWidgetDetails(job, theme, expanded, width, frame, projection)
|
|
850
1865
|
].map((line) => truncLine(line, width));
|
|
851
1866
|
}
|
|
852
|
-
function compactSingleWidgetLines(job, theme, width) {
|
|
853
|
-
const fullLines = buildSingleWidgetLines(job, theme, width, false);
|
|
1867
|
+
function compactSingleWidgetLines(job, theme, width, frame, projection = buildWorkflowWidgetProjection(job)) {
|
|
1868
|
+
const fullLines = buildSingleWidgetLines(job, theme, width, false, frame, projection);
|
|
854
1869
|
if (fullLines.length <= 10 || !job.steps?.length || job.mode !== "parallel" && !job.activeParallelGroup)
|
|
855
1870
|
return fullLines;
|
|
856
|
-
const
|
|
857
|
-
|
|
1871
|
+
const group = activeParallelWidgetGroup(job);
|
|
1872
|
+
if (!group)
|
|
1873
|
+
return fullLines;
|
|
1874
|
+
const rows = parallelWidgetStepRows(group.steps, group.total, Boolean(job.activeParallelGroup));
|
|
858
1875
|
const lines = fullLines.slice(0, 2);
|
|
859
|
-
|
|
1876
|
+
lines.push(parallelWidgetGroupHeader(group, theme, frame));
|
|
1877
|
+
for (const [rowIndex, row] of rows.entries()) {
|
|
1878
|
+
const step = row.step;
|
|
860
1879
|
const status = widgetStepStatus(step.status, theme);
|
|
861
1880
|
const activity = widgetStepActivityLine(step, width, false, job.updatedAt);
|
|
862
1881
|
const stepStats = widgetStepStats(theme, step);
|
|
863
1882
|
const activitySuffix = activity ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}` : "";
|
|
864
1883
|
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1884
|
+
const task = compactTaskText(step.description, step.label);
|
|
1885
|
+
const taskSuffix = task ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", `task: ${task}`)}` : "";
|
|
1886
|
+
const marker = rowIndex === rows.length - 1 && rowIndex >= group.total - 1 ? "└─" : "├─";
|
|
1887
|
+
lines.push(` ${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, row.index), frame)} ${themeBold(theme, row.rowLabel)}${contextModeBadge(theme, step.context)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${taskSuffix}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
|
|
1888
|
+
const lane = projectAsyncLane(job, step);
|
|
1889
|
+
if (lane)
|
|
1890
|
+
lines.push(...formatLaneProjectionLines(lane, theme, " "));
|
|
1891
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt, 6))
|
|
1892
|
+
lines.push(` ${nestedLine}`);
|
|
868
1893
|
}
|
|
1894
|
+
lines.push(...hostStepWidgetLines(job, theme, " "));
|
|
869
1895
|
if (job.steps.some((step) => step.status === "running"))
|
|
870
1896
|
lines.push(theme.fg("accent", ` ${liveDetailHintText()}`));
|
|
871
1897
|
return lines.map((line) => truncLine(line, width));
|
|
@@ -894,13 +1920,14 @@ function widgetHeaderCounts(jobs) {
|
|
|
894
1920
|
queued: jobs.filter((job) => job.status === "queued"),
|
|
895
1921
|
complete: jobs.filter((job) => job.status === "complete"),
|
|
896
1922
|
failed: jobs.filter((job) => job.status === "failed"),
|
|
897
|
-
paused: jobs.filter((job) => job.status === "paused")
|
|
1923
|
+
paused: jobs.filter((job) => job.status === "paused"),
|
|
1924
|
+
stopped: jobs.filter((job) => job.status === "stopped")
|
|
898
1925
|
};
|
|
899
1926
|
}
|
|
900
|
-
function buildSingleLineWidgetLines(jobs, theme, width) {
|
|
1927
|
+
function buildSingleLineWidgetLines(jobs, theme, width, frame) {
|
|
901
1928
|
const counts = widgetHeaderCounts(jobs);
|
|
902
1929
|
const hasActive = counts.running.length > 0 || counts.queued.length > 0;
|
|
903
|
-
const glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "●" : "○";
|
|
1930
|
+
const glyph = counts.running.length > 0 ? runningGlyph(animatedSeed(widgetJobsRunningSeed(counts.running), frame)) : hasActive ? "●" : "○";
|
|
904
1931
|
const parts = [];
|
|
905
1932
|
if (counts.running.length > 0)
|
|
906
1933
|
parts.push(`${counts.running.length}/${jobs.length} running`);
|
|
@@ -908,6 +1935,8 @@ function buildSingleLineWidgetLines(jobs, theme, width) {
|
|
|
908
1935
|
parts.push(`${counts.queued.length} queued`);
|
|
909
1936
|
if (counts.failed.length > 0)
|
|
910
1937
|
parts.push(`${counts.failed.length} failed`);
|
|
1938
|
+
if (counts.stopped.length > 0)
|
|
1939
|
+
parts.push(`${counts.stopped.length} stopped`);
|
|
911
1940
|
if (counts.paused.length > 0)
|
|
912
1941
|
parts.push(`${counts.paused.length} paused`);
|
|
913
1942
|
if (!hasActive && counts.complete.length > 0)
|
|
@@ -969,10 +1998,10 @@ function selectProgressiveJobKeys(jobs, previousKeys, bodyRows) {
|
|
|
969
1998
|
}
|
|
970
1999
|
return selected;
|
|
971
2000
|
}
|
|
972
|
-
function progressiveHeaderLine(jobs, theme, width) {
|
|
2001
|
+
function progressiveHeaderLine(jobs, theme, width, frame) {
|
|
973
2002
|
const counts = widgetHeaderCounts(jobs);
|
|
974
2003
|
const hasActive = counts.running.length > 0 || counts.queued.length > 0;
|
|
975
|
-
const glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "●" : "○";
|
|
2004
|
+
const glyph = counts.running.length > 0 ? runningGlyph(animatedSeed(widgetJobsRunningSeed(counts.running), frame)) : hasActive ? "●" : "○";
|
|
976
2005
|
const parts = [];
|
|
977
2006
|
if (counts.running.length > 0)
|
|
978
2007
|
parts.push(formatAgentRunningLabel(counts.running.length));
|
|
@@ -981,6 +2010,8 @@ function progressiveHeaderLine(jobs, theme, width) {
|
|
|
981
2010
|
if (!hasActive) {
|
|
982
2011
|
if (counts.failed.length > 0)
|
|
983
2012
|
parts.push(`${counts.failed.length} failed`);
|
|
2013
|
+
if (counts.stopped.length > 0)
|
|
2014
|
+
parts.push(`${counts.stopped.length} stopped`);
|
|
984
2015
|
if (counts.paused.length > 0)
|
|
985
2016
|
parts.push(`${counts.paused.length} paused`);
|
|
986
2017
|
if (counts.complete.length > 0)
|
|
@@ -988,17 +2019,22 @@ function progressiveHeaderLine(jobs, theme, width) {
|
|
|
988
2019
|
}
|
|
989
2020
|
return truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "·")} ${theme.fg("dim", parts.join(", ") || `${jobs.length} total`)}`, width);
|
|
990
2021
|
}
|
|
991
|
-
function progressiveJobLine(job, theme, width) {
|
|
992
|
-
const stats = widgetStats(job, theme);
|
|
2022
|
+
function progressiveJobLine(job, theme, width, frame, projection = buildWorkflowWidgetProjection(job)) {
|
|
2023
|
+
const stats = widgetStats(job, theme, projection);
|
|
993
2024
|
const activity = widgetActivity(job);
|
|
994
2025
|
const status = job.status === "complete" ? "done" : job.status;
|
|
2026
|
+
const lane = projectAsyncLane(job, laneStepForJob(job, projection.steps));
|
|
2027
|
+
const laneSummary = lane ? [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(" · ") : "";
|
|
2028
|
+
const laneSignals = lane ? [lane.next ? `next:${lane.next}` : undefined, ...lane.chips.map((chip) => formatLaneChip(chip, theme))].filter(Boolean).join(" · ") : "";
|
|
995
2029
|
const parts = [
|
|
996
|
-
themeBold(theme, widgetJobName(job)),
|
|
2030
|
+
`${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}`,
|
|
997
2031
|
theme.fg("dim", status),
|
|
2032
|
+
laneSignals ? laneSignals : "",
|
|
2033
|
+
laneSummary ? theme.fg("dim", laneSummary) : "",
|
|
998
2034
|
stats,
|
|
999
2035
|
activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : ""
|
|
1000
2036
|
].filter(Boolean);
|
|
1001
|
-
return truncLine(` ${widgetStatusGlyph(job, theme)} ${parts.join(` ${theme.fg("dim", "·")} `)}`, width);
|
|
2037
|
+
return truncLine(` ${widgetStatusGlyph(job, theme, frame)} ${parts.join(` ${theme.fg("dim", "·")} `)}`, width);
|
|
1002
2038
|
}
|
|
1003
2039
|
function progressiveHiddenLine(hiddenJobs, theme, width) {
|
|
1004
2040
|
const counts = widgetHeaderCounts(hiddenJobs);
|
|
@@ -1007,15 +2043,15 @@ function progressiveHiddenLine(hiddenJobs, theme, width) {
|
|
|
1007
2043
|
parts.push(`${counts.running.length} running`);
|
|
1008
2044
|
if (counts.queued.length > 0)
|
|
1009
2045
|
parts.push(`${counts.queued.length} queued`);
|
|
1010
|
-
const finished = counts.complete.length + counts.failed.length + counts.paused.length;
|
|
2046
|
+
const finished = counts.complete.length + counts.failed.length + counts.paused.length + counts.stopped.length;
|
|
1011
2047
|
if (finished > 0)
|
|
1012
2048
|
parts.push(`${finished} finished`);
|
|
1013
2049
|
return truncLine(theme.fg("dim", ` +${hiddenJobs.length} more${parts.length ? ` (${parts.join(", ")})` : ""}`), width);
|
|
1014
2050
|
}
|
|
1015
|
-
function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKeys) {
|
|
2051
|
+
function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKeys, frame, projectionFor = workflowWidgetProjectionLookup()) {
|
|
1016
2052
|
const rowCount = Math.max(1, lockedRows);
|
|
1017
2053
|
if (rowCount === 1)
|
|
1018
|
-
return { lines: buildSingleLineWidgetLines(jobs, theme, width), visibleJobKeys: [] };
|
|
2054
|
+
return { lines: buildSingleLineWidgetLines(jobs, theme, width, frame), visibleJobKeys: [] };
|
|
1019
2055
|
const bodyRows = rowCount - 1;
|
|
1020
2056
|
let visibleJobKeys = selectProgressiveJobKeys(jobs, previousKeys, bodyRows);
|
|
1021
2057
|
const jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job]));
|
|
@@ -1028,8 +2064,8 @@ function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKey
|
|
|
1028
2064
|
hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job)));
|
|
1029
2065
|
}
|
|
1030
2066
|
const lines = [
|
|
1031
|
-
progressiveHeaderLine(jobs, theme, width),
|
|
1032
|
-
...visibleJobs.map((job) => progressiveJobLine(job, theme, width))
|
|
2067
|
+
progressiveHeaderLine(jobs, theme, width, frame),
|
|
2068
|
+
...visibleJobs.map((job) => progressiveJobLine(job, theme, width, frame, projectionFor(job)))
|
|
1033
2069
|
];
|
|
1034
2070
|
if (hiddenJobs.length > 0 && lines.length < rowCount)
|
|
1035
2071
|
lines.push(progressiveHiddenLine(hiddenJobs, theme, width));
|
|
@@ -1040,6 +2076,12 @@ function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKey
|
|
|
1040
2076
|
function collapsedWidgetLineBudget(rows) {
|
|
1041
2077
|
return Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
|
|
1042
2078
|
}
|
|
2079
|
+
function paddedWidgetLine(line, width) {
|
|
2080
|
+
if (width <= 2)
|
|
2081
|
+
return " ".repeat(Math.max(0, width));
|
|
2082
|
+
const text = ` ${truncLine(line, width - 2)} `;
|
|
2083
|
+
return `${text}${" ".repeat(Math.max(0, width - visibleWidth(text)))}`;
|
|
2084
|
+
}
|
|
1043
2085
|
function fitWidgetLineBudget(lines, theme, width, expanded) {
|
|
1044
2086
|
const rows = process.stdout.rows || 30;
|
|
1045
2087
|
const budget = expanded ? Math.max(12, Math.min(24, Math.floor(rows * 0.55))) : collapsedWidgetLineBudget(rows);
|
|
@@ -1050,74 +2092,98 @@ function fitWidgetLineBudget(lines, theme, width, expanded) {
|
|
|
1050
2092
|
const hint = expanded ? `… ${hiddenCount} live-detail lines hidden` : `… ${hiddenCount} lines hidden · ${liveDetailKeyText()} expands`;
|
|
1051
2093
|
return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
|
|
1052
2094
|
}
|
|
1053
|
-
function fitAdaptiveWidgetLines(jobs,
|
|
2095
|
+
function fitAdaptiveWidgetLines(jobs, buildLines, theme, width, expanded, frame, projectionFor) {
|
|
1054
2096
|
if (expanded) {
|
|
1055
2097
|
resetWidgetLayoutSession();
|
|
1056
|
-
return fitWidgetLineBudget(
|
|
2098
|
+
return fitWidgetLineBudget(buildLines(), theme, width, true);
|
|
1057
2099
|
}
|
|
1058
2100
|
const hasMatchingSession = widgetSessionMatches(expanded);
|
|
1059
2101
|
const rows = currentTerminalRows();
|
|
1060
2102
|
const columns = currentTerminalColumns();
|
|
1061
2103
|
const availableRows = estimateAvailableWidgetRows();
|
|
1062
2104
|
if (hasMatchingSession && widgetLayoutSession?.tier === "single-line") {
|
|
1063
|
-
return buildSingleLineWidgetLines(jobs, theme, width);
|
|
2105
|
+
return buildSingleLineWidgetLines(jobs, theme, width, frame);
|
|
1064
2106
|
}
|
|
1065
2107
|
if (hasMatchingSession && widgetLayoutSession?.tier === "progressive" && widgetLayoutSession.lockedRows !== undefined) {
|
|
1066
|
-
const rendered = buildProgressiveWidgetLines(jobs, theme, width, widgetLayoutSession.lockedRows, widgetLayoutSession.visibleJobKeys);
|
|
2108
|
+
const rendered = buildProgressiveWidgetLines(jobs, theme, width, widgetLayoutSession.lockedRows, widgetLayoutSession.visibleJobKeys, frame, projectionFor);
|
|
1067
2109
|
widgetLayoutSession.visibleJobKeys = rendered.visibleJobKeys;
|
|
1068
2110
|
return rendered.lines;
|
|
1069
2111
|
}
|
|
2112
|
+
const lines = buildLines();
|
|
1070
2113
|
if (lines.length <= availableRows) {
|
|
1071
2114
|
widgetLayoutSession = { expanded, rows, columns, tier: "full", visibleJobKeys: [] };
|
|
1072
2115
|
return fitWidgetLineBudget(lines, theme, width, false);
|
|
1073
2116
|
}
|
|
2117
|
+
if (availableRows > 2 && jobs.length === 1 && projectionFor?.(jobs[0]).stageProgress) {
|
|
2118
|
+
widgetLayoutSession = { expanded, rows, columns, tier: "full", visibleJobKeys: [] };
|
|
2119
|
+
return fitWidgetLineBudget(lines, theme, width, false);
|
|
2120
|
+
}
|
|
1074
2121
|
if (availableRows <= 2) {
|
|
1075
2122
|
widgetLayoutSession = { expanded, rows, columns, tier: "single-line", visibleJobKeys: [] };
|
|
1076
|
-
return buildSingleLineWidgetLines(jobs, theme, width);
|
|
2123
|
+
return buildSingleLineWidgetLines(jobs, theme, width, frame);
|
|
1077
2124
|
}
|
|
1078
2125
|
const lockedRows = Math.min(availableRows, collapsedWidgetLineBudget(rows));
|
|
1079
|
-
const rendered = buildProgressiveWidgetLines(jobs, theme, width, lockedRows, []);
|
|
2126
|
+
const rendered = buildProgressiveWidgetLines(jobs, theme, width, lockedRows, [], frame, projectionFor);
|
|
1080
2127
|
widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows, visibleJobKeys: rendered.visibleJobKeys };
|
|
1081
2128
|
return rendered.lines;
|
|
1082
2129
|
}
|
|
1083
|
-
function buildWidgetComponent(jobs,
|
|
2130
|
+
function buildWidgetComponent(jobs, isExpanded) {
|
|
1084
2131
|
return (_tui, theme) => {
|
|
1085
|
-
const width = getTermWidth();
|
|
1086
|
-
const lines = expanded ? buildWidgetLines(jobs, theme, width, true) : jobs.length === 1 ? compactSingleWidgetLines(jobs[0], theme, width) : buildWidgetLines(jobs, theme, width, false);
|
|
1087
2132
|
const container = new Container;
|
|
1088
|
-
|
|
1089
|
-
|
|
2133
|
+
let cachedRenderWidth;
|
|
2134
|
+
let cachedFrame;
|
|
2135
|
+
let cachedExpanded;
|
|
2136
|
+
let cachedLines;
|
|
2137
|
+
container.render = (renderWidth) => {
|
|
2138
|
+
const frame = Math.floor(Date.now() / WIDGET_ANIMATION_INTERVAL_MS);
|
|
2139
|
+
const expanded = isExpanded();
|
|
2140
|
+
if (cachedLines && cachedRenderWidth === renderWidth && cachedFrame === frame && cachedExpanded === expanded)
|
|
2141
|
+
return cachedLines;
|
|
2142
|
+
const width = Math.max(0, renderWidth - 2);
|
|
2143
|
+
const projectionFor = workflowWidgetProjectionLookup();
|
|
2144
|
+
const buildLines = () => expanded ? buildWidgetLinesWithProjection(jobs, theme, width, true, frame, projectionFor) : jobs.length === 1 ? compactSingleWidgetLines(jobs[0], theme, width, frame, projectionFor(jobs[0])) : buildWidgetLinesWithProjection(jobs, theme, width, false, frame, projectionFor);
|
|
2145
|
+
cachedRenderWidth = renderWidth;
|
|
2146
|
+
cachedFrame = frame;
|
|
2147
|
+
cachedExpanded = expanded;
|
|
2148
|
+
cachedLines = fitAdaptiveWidgetLines(jobs, buildLines, theme, width, expanded, frame, projectionFor).map((line) => paddedWidgetLine(line, renderWidth));
|
|
2149
|
+
return cachedLines;
|
|
2150
|
+
};
|
|
1090
2151
|
return container;
|
|
1091
2152
|
};
|
|
1092
2153
|
}
|
|
1093
|
-
|
|
2154
|
+
function buildWidgetLinesWithProjection(jobs, theme, width = getTermWidth(), expanded = false, frame, projectionFor = workflowWidgetProjectionLookup()) {
|
|
1094
2155
|
if (jobs.length === 0)
|
|
1095
2156
|
return [];
|
|
1096
2157
|
if (jobs.length === 1)
|
|
1097
|
-
return buildSingleWidgetLines(jobs[0], theme, width, expanded);
|
|
2158
|
+
return buildSingleWidgetLines(jobs[0], theme, width, expanded, frame, projectionFor(jobs[0]));
|
|
1098
2159
|
const running = jobs.filter((job) => job.status === "running");
|
|
1099
2160
|
const queued = jobs.filter((job) => job.status === "queued");
|
|
1100
2161
|
const finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued");
|
|
1101
2162
|
const lines = [];
|
|
1102
2163
|
const hasActive = running.length > 0 || queued.length > 0;
|
|
1103
|
-
const headerGlyph = running.length > 0 ? runningGlyph(widgetJobsRunningSeed(running)) : hasActive ? "●" : "○";
|
|
2164
|
+
const headerGlyph = running.length > 0 ? runningGlyph(animatedSeed(widgetJobsRunningSeed(running), frame)) : hasActive ? "●" : "○";
|
|
1104
2165
|
lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "· background")}`, width));
|
|
1105
2166
|
const items = [];
|
|
1106
2167
|
let hiddenRunning = 0;
|
|
1107
2168
|
let hiddenFinished = 0;
|
|
1108
2169
|
let queuedSummaryShown = false;
|
|
1109
2170
|
let slots = MAX_WIDGET_JOBS;
|
|
2171
|
+
const appendJob = (job) => {
|
|
2172
|
+
const projection = projectionFor(job);
|
|
2173
|
+
const stats = widgetStats(job, theme, projection);
|
|
2174
|
+
items.push([
|
|
2175
|
+
`${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
2176
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
2177
|
+
...widgetLaneDetailLines(job, theme, projection),
|
|
2178
|
+
...widgetParallelAgentDetails(job, theme, expanded, width, frame)
|
|
2179
|
+
]);
|
|
2180
|
+
};
|
|
1110
2181
|
for (const job of running) {
|
|
1111
2182
|
if (slots <= 0) {
|
|
1112
2183
|
hiddenRunning++;
|
|
1113
2184
|
continue;
|
|
1114
2185
|
}
|
|
1115
|
-
|
|
1116
|
-
items.push([
|
|
1117
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1118
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1119
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
1120
|
-
]);
|
|
2186
|
+
appendJob(job);
|
|
1121
2187
|
slots--;
|
|
1122
2188
|
}
|
|
1123
2189
|
if (queued.length > 0 && slots > 0) {
|
|
@@ -1130,12 +2196,7 @@ export function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded =
|
|
|
1130
2196
|
hiddenFinished++;
|
|
1131
2197
|
continue;
|
|
1132
2198
|
}
|
|
1133
|
-
|
|
1134
|
-
items.push([
|
|
1135
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1136
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1137
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
1138
|
-
]);
|
|
2199
|
+
appendJob(job);
|
|
1139
2200
|
slots--;
|
|
1140
2201
|
}
|
|
1141
2202
|
const hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0;
|
|
@@ -1162,6 +2223,9 @@ export function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded =
|
|
|
1162
2223
|
}
|
|
1163
2224
|
return lines;
|
|
1164
2225
|
}
|
|
2226
|
+
export function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = false, frame) {
|
|
2227
|
+
return buildWidgetLinesWithProjection(jobs, theme, width, expanded, frame);
|
|
2228
|
+
}
|
|
1165
2229
|
export function renderWidget(ctx, jobs) {
|
|
1166
2230
|
if (jobs.length === 0) {
|
|
1167
2231
|
resetWidgetLayoutSession();
|
|
@@ -1171,50 +2235,151 @@ export function renderWidget(ctx, jobs) {
|
|
|
1171
2235
|
}
|
|
1172
2236
|
if (!ctx.hasUI)
|
|
1173
2237
|
return;
|
|
1174
|
-
|
|
2238
|
+
if (ctx.mode === "rpc") {
|
|
2239
|
+
ctx.ui.setWidget(WIDGET_KEY, encodeAsyncStatusSnapshotWidget(jobs));
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
ctx.ui.setWidget(WIDGET_KEY, buildWidgetComponent(jobs, () => ctx.ui.getToolsExpanded?.() ?? false));
|
|
1175
2243
|
}
|
|
1176
|
-
function renderSingleCompact(d, r, theme, frame) {
|
|
2244
|
+
function renderSingleCompact(d, r, theme, layout, frame, foregroundDetachShortcut) {
|
|
1177
2245
|
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
1178
2246
|
const progress = r.progress || r.progressSummary;
|
|
1179
|
-
const isRunning = r
|
|
1180
|
-
const contextBadge =
|
|
2247
|
+
const isRunning = isResultRunning(r);
|
|
2248
|
+
const contextBadge = contextModeBadge(theme, r.context ?? d.context);
|
|
1181
2249
|
const stats = statJoin(theme, [
|
|
1182
2250
|
r.usage?.turns ? `⟳ ${r.usage.turns}` : "",
|
|
1183
2251
|
formatProgressStats(theme, progress)
|
|
1184
2252
|
]);
|
|
1185
2253
|
const c = new Container;
|
|
1186
2254
|
const width = getTermWidth() - 4;
|
|
1187
|
-
const
|
|
1188
|
-
|
|
2255
|
+
const detailIndent = mainWindowIndent(layout, 1);
|
|
2256
|
+
const continuationIndent = mainWindowIndent(layout, 2) + (layout.horizontalSpacing > 0 ? " " : "");
|
|
2257
|
+
const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
|
|
2258
|
+
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(foregroundSingleDisplayName(r)))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1189
2259
|
if (isRunning && r.progress) {
|
|
2260
|
+
const task = compactTaskText(r.task);
|
|
2261
|
+
if (task)
|
|
2262
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}task: ${task}`), width), 0, 0));
|
|
1190
2263
|
const progressSnapshotNow = snapshotNowForProgress(r.progress);
|
|
1191
2264
|
const activity = compactCurrentActivity(r.progress);
|
|
1192
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2265
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}⎿ ${activity}`), width), 0, 0));
|
|
1193
2266
|
const liveStatus = buildLiveStatusLine(r.progress, progressSnapshotNow);
|
|
1194
2267
|
if (liveStatus && liveStatus !== activity)
|
|
1195
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
1196
|
-
|
|
2268
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${continuationIndent}${liveStatus}`), width), 0, 0));
|
|
2269
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, width, false, progressSnapshotNow)) {
|
|
2270
|
+
c.addChild(new Text(truncLine(`${detailIndent}${nestedLine}`, width), 0, 0));
|
|
2271
|
+
}
|
|
2272
|
+
c.addChild(new Text(truncLine(theme.fg("accent", `${detailIndent}${foregroundSingleHintText(foregroundDetachShortcut)}`), width), 0, 0));
|
|
1197
2273
|
if (r.artifactPaths)
|
|
1198
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2274
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0));
|
|
1199
2275
|
return c;
|
|
1200
2276
|
}
|
|
1201
|
-
|
|
2277
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, width, false, r.progress?.lastActivityAt)) {
|
|
2278
|
+
c.addChild(new Text(truncLine(`${detailIndent}${nestedLine}`, width), 0, 0));
|
|
2279
|
+
}
|
|
2280
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
|
|
1202
2281
|
const preview = firstOutputLine(output);
|
|
1203
2282
|
if (preview && r.exitCode === 0 && !hasEmptyTextOutputWithoutOutputTarget(r.task, output)) {
|
|
1204
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2283
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${continuationIndent}${preview}`), width), 0, 0));
|
|
1205
2284
|
}
|
|
1206
2285
|
if (r.sessionFile)
|
|
1207
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2286
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}session: ${shortenPath(r.sessionFile)}`), width), 0, 0));
|
|
1208
2287
|
if (r.artifactPaths)
|
|
1209
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2288
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0));
|
|
1210
2289
|
if (r.truncation?.artifactPath)
|
|
1211
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2290
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}full output: ${shortenPath(r.truncation.artifactPath)}`), width), 0, 0));
|
|
2291
|
+
return c;
|
|
2292
|
+
}
|
|
2293
|
+
function workflowRowGlyph(row, theme, frame) {
|
|
2294
|
+
if (row.state === "planned")
|
|
2295
|
+
return theme.fg("muted", "◦");
|
|
2296
|
+
if (row.state === "running")
|
|
2297
|
+
return theme.fg("accent", runningGlyph(frame));
|
|
2298
|
+
if (row.state === "complete")
|
|
2299
|
+
return theme.fg("success", "✓");
|
|
2300
|
+
if (row.state === "detached" || row.state === "stopped")
|
|
2301
|
+
return theme.fg("warning", "■");
|
|
2302
|
+
return theme.fg("error", "✗");
|
|
2303
|
+
}
|
|
2304
|
+
function workflowRowStateLabel(row, theme) {
|
|
2305
|
+
const label = (row.state === "complete" ? "complete" : row.state).padEnd(8);
|
|
2306
|
+
if (row.state === "planned")
|
|
2307
|
+
return theme.fg("dim", label);
|
|
2308
|
+
if (row.state === "running")
|
|
2309
|
+
return theme.fg("accent", label);
|
|
2310
|
+
if (row.state === "complete")
|
|
2311
|
+
return theme.fg("success", label);
|
|
2312
|
+
if (row.state === "detached" || row.state === "stopped")
|
|
2313
|
+
return theme.fg("warning", label);
|
|
2314
|
+
return theme.fg("error", label);
|
|
2315
|
+
}
|
|
2316
|
+
function workflowOverallState(rows, hasTerminalValue, isError) {
|
|
2317
|
+
if (rows.some((row) => row.state === "failed"))
|
|
2318
|
+
return "failed";
|
|
2319
|
+
if (rows.some((row) => row.state === "detached"))
|
|
2320
|
+
return "paused";
|
|
2321
|
+
if (isError)
|
|
2322
|
+
return "failed";
|
|
2323
|
+
if (rows.length > 0 && rows.every((row) => row.state === "complete") || hasTerminalValue)
|
|
2324
|
+
return "complete";
|
|
2325
|
+
return "running";
|
|
2326
|
+
}
|
|
2327
|
+
function renderWorkflowChatProgress(d, result, theme, layout, frame, expanded = false) {
|
|
2328
|
+
const workflow = d.workflow;
|
|
2329
|
+
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace, d.preflight) : d.preflight ? buildWorkflowChatProgressRows([], d.preflight) : [];
|
|
2330
|
+
const state = workflowOverallState(rows, workflow?.value !== undefined, result.isError);
|
|
2331
|
+
const glyph = state === "running" ? theme.fg("accent", runningGlyph(frame)) : state === "complete" ? theme.fg("success", "✓") : state === "paused" ? theme.fg("warning", "■") : theme.fg("error", "✗");
|
|
2332
|
+
const width = getTermWidth() - 4;
|
|
2333
|
+
const runId = d.runId ? d.runId.slice(0, 12) : "workflow";
|
|
2334
|
+
const repoLabel = d.chatProgress?.repoLabel ?? (d.chatProgress?.repoRelation === "same" ? "same repo" : "other repo");
|
|
2335
|
+
const phase = rows.find((row) => row.state === "running" && row.phase)?.phase ?? [...rows].reverse().find((row) => row.phase)?.phase;
|
|
2336
|
+
const c = new Container;
|
|
2337
|
+
const rowIndent = mainWindowIndent(layout, 1);
|
|
2338
|
+
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));
|
|
2339
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Repo ${repoLabel}`), width), 0, 0));
|
|
2340
|
+
if (d.preflight)
|
|
2341
|
+
c.addChild(new Text(truncLine(theme.fg("dim", formatWorkflowPreflightPlanSummary(d.preflight, { indent: rowIndent })), width), 0, 0));
|
|
2342
|
+
if (phase)
|
|
2343
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Phase ${phase}`), width), 0, 0));
|
|
2344
|
+
if (rows.length === 0) {
|
|
2345
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}◦ waiting for workflow child launches`), width), 0, 0));
|
|
2346
|
+
return c;
|
|
2347
|
+
}
|
|
2348
|
+
const visible = visibleWorkflowRows(rows);
|
|
2349
|
+
if (visible.hiddenRows > 0)
|
|
2350
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}… ${visible.hiddenRows} older workflow rows hidden`), width), 0, 0));
|
|
2351
|
+
for (const row of visible.rows) {
|
|
2352
|
+
const status = workflowRowStateLabel(row, theme);
|
|
2353
|
+
const label = row.label && row.label !== row.key ? ` ${oneLine(row.label)}` : "";
|
|
2354
|
+
const duration = row.durationMs !== undefined ? ` ${theme.fg("dim", `· ${formatDuration(row.durationMs)}`)}` : "";
|
|
2355
|
+
const run = row.runId ? ` ${theme.fg("dim", `[${row.runId.slice(0, 8)}]`)}` : "";
|
|
2356
|
+
const error = row.error ? ` ${theme.fg(row.state === "detached" ? "warning" : "error", `· ${compactWorkflowError(row.error)}`)}` : "";
|
|
2357
|
+
const hints = expanded && row.preflight ? [
|
|
2358
|
+
row.preflight.mode ? `mode:${row.preflight.mode}` : undefined,
|
|
2359
|
+
row.preflight.decision ? `decision:${row.preflight.decision}` : undefined,
|
|
2360
|
+
row.preflight.claims?.length ? `claims:${row.preflight.claims.join(",")}` : undefined,
|
|
2361
|
+
row.preflight.expectedOutput ? `expected:${row.preflight.expectedOutput}` : undefined,
|
|
2362
|
+
row.preflight.independence ? `independence:${row.preflight.independence}` : undefined
|
|
2363
|
+
].filter((value) => Boolean(value)).join(" · ") : "";
|
|
2364
|
+
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));
|
|
2365
|
+
}
|
|
2366
|
+
if (workflow?.preflightWarnings?.length) {
|
|
2367
|
+
const warningLines = expanded ? formatWorkflowPreflightWarnings(workflow.preflightWarnings, { indent: rowIndent }).split(`
|
|
2368
|
+
`) : [formatWorkflowPreflightWarningSummary(workflow.preflightWarnings, { indent: rowIndent, hint: "expand for debug" })];
|
|
2369
|
+
for (const warningLine of warningLines)
|
|
2370
|
+
c.addChild(new Text(truncLine(theme.fg("warning", warningLine), width), 0, 0));
|
|
2371
|
+
}
|
|
2372
|
+
if (workflow?.emits.length)
|
|
2373
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Emits ${workflow.emits.length}`), width), 0, 0));
|
|
1212
2374
|
return c;
|
|
1213
2375
|
}
|
|
1214
|
-
function renderMultiCompact(d, theme, frame) {
|
|
1215
|
-
const hasRunning =
|
|
1216
|
-
const
|
|
1217
|
-
const
|
|
2376
|
+
function renderMultiCompact(d, theme, layout, frame) {
|
|
2377
|
+
const hasRunning = detailsHaveRunningResult(d);
|
|
2378
|
+
const detached = d.results.some((r) => r.detached) || workflowGraphHasStatus(d, ["detached"]);
|
|
2379
|
+
const stopped = d.results.some((r) => r.stopped) || workflowGraphHasStatus(d, ["stopped"]);
|
|
2380
|
+
const failed = d.results.some((r) => !hasTerminalResultFlag(r) && r.exitCode !== 0 && !isResultRunning(r)) || workflowGraphHasStatus(d, ["failed"]);
|
|
2381
|
+
const paused = d.results.some((r) => r.interrupted) || workflowGraphHasStatus(d, ["paused"]);
|
|
2382
|
+
const partial = workflowGraphHasStatus(d, ["partial"]);
|
|
1218
2383
|
let totalSummary = d.progressSummary;
|
|
1219
2384
|
if (!totalSummary) {
|
|
1220
2385
|
let sawProgress = false;
|
|
@@ -1234,29 +2399,36 @@ function renderMultiCompact(d, theme, frame) {
|
|
|
1234
2399
|
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
1235
2400
|
const itemTitle = multiLabel.itemTitle;
|
|
1236
2401
|
const stats = statJoin(theme, [multiLabel.headerLabel, formatProgressStats(theme, totalSummary), formatTotalCostStat(d.totalCost)]);
|
|
1237
|
-
const
|
|
1238
|
-
|
|
2402
|
+
const aggregatePresentation = semanticResultPresentation({
|
|
2403
|
+
running: hasRunning,
|
|
2404
|
+
detached,
|
|
2405
|
+
stopped,
|
|
2406
|
+
interrupted: paused,
|
|
2407
|
+
failed,
|
|
2408
|
+
partial,
|
|
2409
|
+
seed: runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex),
|
|
2410
|
+
frame
|
|
2411
|
+
});
|
|
2412
|
+
const glyph = theme.fg(aggregatePresentation.tone, aggregatePresentation.glyph);
|
|
2413
|
+
const contextBadge = contextModeBadge(theme, d.context);
|
|
1239
2414
|
const c = new Container;
|
|
1240
2415
|
const width = getTermWidth() - 4;
|
|
2416
|
+
const rowIndent = mainWindowIndent(layout, 1);
|
|
2417
|
+
const detailIndent = mainWindowIndent(layout, 2);
|
|
1241
2418
|
c.addChild(new Text(truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1242
2419
|
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
1243
2420
|
const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
|
|
1244
2421
|
const displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : useResultsDirectly ? d.results.length : d.chainAgents.length;
|
|
1245
2422
|
const chainEntries = buildChainRenderEntries(d, multiLabel);
|
|
1246
|
-
const renderEntries = chainEntries ??
|
|
1247
|
-
const i = displayStart + offset;
|
|
1248
|
-
const r = d.results[i];
|
|
1249
|
-
const fallbackLabel = itemTitle.toLowerCase();
|
|
1250
|
-
const rowNumber = multiLabel.showActiveGroupOnly ? i - multiLabel.groupStartIndex + 1 : i + 1;
|
|
1251
|
-
return { kind: "result", resultIndex: i, rowNumber, agentName: useResultsDirectly ? r?.agent || `${fallbackLabel}-${rowNumber}` : d.chainAgents[i] || r?.agent || `${fallbackLabel}-${rowNumber}` };
|
|
1252
|
-
});
|
|
2423
|
+
const renderEntries = chainEntries ?? buildForegroundResultEntries(d, multiLabel, displayStart, displayEnd, useResultsDirectly);
|
|
1253
2424
|
for (const entry of renderEntries) {
|
|
1254
|
-
if (entry.kind === "
|
|
2425
|
+
if (entry.kind === "group") {
|
|
1255
2426
|
const glyph = widgetStepGlyph(entry.status, theme);
|
|
1256
2427
|
const statusLabel = widgetStepStatus(entry.status, theme);
|
|
1257
|
-
|
|
2428
|
+
const groupLabel = entry.groupLabel ? ` (${compactTaskText(undefined, entry.groupLabel) ?? entry.groupLabel})` : "";
|
|
2429
|
+
c.addChild(new Text(truncLine(`${rowIndent}${glyph} ${entry.stepLabel}${groupLabel} ${theme.fg("dim", "·")} ${statusLabel}`, width), 0, 0));
|
|
1258
2430
|
if (entry.error)
|
|
1259
|
-
c.addChild(new Text(truncLine(theme.fg("error",
|
|
2431
|
+
c.addChild(new Text(truncLine(theme.fg("error", `${detailIndent}⎿ Error: ${entry.error}`), width), 0, 0));
|
|
1260
2432
|
continue;
|
|
1261
2433
|
}
|
|
1262
2434
|
const i = entry.resultIndex;
|
|
@@ -1264,49 +2436,96 @@ function renderMultiCompact(d, theme, frame) {
|
|
|
1264
2436
|
const rowNumber = entry.rowNumber;
|
|
1265
2437
|
const agentName = entry.agentName;
|
|
1266
2438
|
if (!r) {
|
|
1267
|
-
const pendingLabel =
|
|
1268
|
-
|
|
2439
|
+
const pendingLabel = entry.rowLabel ?? (entry.isParallel ? "" : `${itemTitle} ${rowNumber}`);
|
|
2440
|
+
const labelPrefix = pendingLabel ? `${pendingLabel}: ` : "";
|
|
2441
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}◦ ${labelPrefix}${agentName} · pending`), width), 0, 0));
|
|
1269
2442
|
continue;
|
|
1270
2443
|
}
|
|
1271
2444
|
const output = getSingleResultOutput(r);
|
|
1272
|
-
const progressFromArray = d
|
|
2445
|
+
const progressFromArray = foregroundProgressForResult(d, i);
|
|
1273
2446
|
const rProg = r.progress || progressFromArray || r.progressSummary;
|
|
1274
|
-
const rRunning = rProg && "status" in rProg && rProg.status
|
|
2447
|
+
const rRunning = rProg && "status" in rProg && isResultRunning(r, rProg.status);
|
|
1275
2448
|
const rPending = rProg && "status" in rProg && rProg.status === "pending";
|
|
1276
|
-
const stepNumber = r.progress?.index !== undefined ? r.progress.index + 1 : progressFromArray?.index !== undefined ? progressFromArray.index + 1 : i + 1;
|
|
1277
2449
|
const stepStats = formatProgressStats(theme, rProg);
|
|
1278
2450
|
const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), frame);
|
|
1279
2451
|
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1280
|
-
const stepLabel =
|
|
1281
|
-
const
|
|
1282
|
-
|
|
2452
|
+
const stepLabel = entry.rowLabel;
|
|
2453
|
+
const rowProgressModel = rProg && "status" in rProg ? rProg : undefined;
|
|
2454
|
+
const rowModelDisplay = modelThinkingBadge(theme, r.model ?? rowProgressModel?.model, r.thinking ?? rowProgressModel?.thinking);
|
|
2455
|
+
const labelPrefix = stepLabel ? `${stepLabel}: ` : "";
|
|
2456
|
+
const line = `${glyph} ${labelPrefix}${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
2457
|
+
c.addChild(new Text(truncLine(`${rowIndent}${line}`, width), 0, 0));
|
|
2458
|
+
if (rRunning || rPending) {
|
|
2459
|
+
const task = compactTaskText(r.task, workflowLabelForResult(d, i));
|
|
2460
|
+
if (task)
|
|
2461
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}task: ${task}`), width), 0, 0));
|
|
2462
|
+
}
|
|
1283
2463
|
if (rRunning && rProg && "status" in rProg) {
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
c.addChild(new Text(truncLine(theme.fg("
|
|
2464
|
+
const liveProgress = rProg;
|
|
2465
|
+
const activity = compactCurrentActivity(liveProgress);
|
|
2466
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}⎿ ${activity}`), width), 0, 0));
|
|
2467
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, width, false, snapshotNowForProgress(liveProgress))) {
|
|
2468
|
+
c.addChild(new Text(truncLine(`${detailIndent}${nestedLine}`, width), 0, 0));
|
|
2469
|
+
}
|
|
2470
|
+
c.addChild(new Text(truncLine(theme.fg("accent", `${detailIndent}${liveDetailHintText()}`), width), 0, 0));
|
|
1287
2471
|
} else if (!rPending && (r.exitCode !== 0 || r.interrupted || r.detached || hasEmptyTextOutputWithoutOutputTarget(r.task, output))) {
|
|
1288
|
-
c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim",
|
|
2472
|
+
c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", `${detailIndent}⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
|
|
2473
|
+
}
|
|
2474
|
+
if (!rRunning && !rPending) {
|
|
2475
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, width, false, r.progress?.lastActivityAt)) {
|
|
2476
|
+
c.addChild(new Text(truncLine(`${detailIndent}${nestedLine}`, width), 0, 0));
|
|
2477
|
+
}
|
|
1289
2478
|
}
|
|
1290
2479
|
const outputTarget = extractOutputTarget(r.task);
|
|
1291
2480
|
if (outputTarget)
|
|
1292
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2481
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}output: ${outputTarget}`), width), 0, 0));
|
|
1293
2482
|
if (r.artifactPaths)
|
|
1294
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2483
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${detailIndent}output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0));
|
|
1295
2484
|
}
|
|
1296
2485
|
if (d.artifacts)
|
|
1297
|
-
c.addChild(new Text(truncLine(theme.fg("dim",
|
|
2486
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}artifacts: ${shortenPath(d.artifacts.dir)}`), width), 0, 0));
|
|
1298
2487
|
return c;
|
|
1299
2488
|
}
|
|
1300
|
-
export function
|
|
2489
|
+
export function renderSubagentSummary(result, options, theme) {
|
|
2490
|
+
const details = result.details;
|
|
2491
|
+
const results = details?.results ?? [];
|
|
2492
|
+
const hasSingleTerminalResult = results.length === 1 && hasTerminalResult(results[0]);
|
|
2493
|
+
const hasOnlyTerminalResults = results.length > 0 && results.every(hasTerminalResult);
|
|
2494
|
+
const running = !hasSingleTerminalResult && !hasOnlyTerminalResults && (options.isPartial === true || Boolean(details?.asyncId && details.mode !== "management") || Boolean(details && detailsHaveRunningResult(details)));
|
|
2495
|
+
const stopped = results.some((entry) => entry.stopped) || Boolean(details && workflowGraphHasStatus(details, ["stopped"]));
|
|
2496
|
+
const paused = results.some((entry) => entry.interrupted || entry.detached) || Boolean(details && workflowGraphHasStatus(details, ["paused", "detached"]));
|
|
2497
|
+
const failed = result.isError === true || results.some((entry) => !hasTerminalResultFlag(entry) && entry.exitCode !== 0 && !isResultRunning(entry)) || Boolean(details && workflowGraphHasStatus(details, ["failed"]));
|
|
2498
|
+
const partial = Boolean(details && workflowGraphHasStatus(details, ["partial"]));
|
|
2499
|
+
const state = running ? "running" : failed ? "failed" : stopped ? "stopped" : paused ? "paused" : partial ? "partial" : "completed";
|
|
2500
|
+
const glyph = state === "running" ? theme.fg("accent", STATIC_RUNNING_GLYPH) : state === "completed" ? theme.fg("success", "✓") : state === "failed" ? theme.fg("error", "✗") : theme.fg("warning", "■");
|
|
2501
|
+
const label = details?.mode === "single" && results.length === 1 ? foregroundSingleDisplayName(results[0]) : details?.mode || "subagent";
|
|
2502
|
+
return new Text(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), 0, 0);
|
|
2503
|
+
}
|
|
2504
|
+
export function renderSubagentResult(result, options, theme, frame, rendererConfig, foregroundDetachShortcut) {
|
|
2505
|
+
const layout = resolveMainWindowRenderLayout(rendererConfig);
|
|
2506
|
+
const compact = (component) => capCompactMainWindowResult(component, layout, theme, !options.expanded);
|
|
1301
2507
|
const d = result.details;
|
|
2508
|
+
if (d?.mode === "workflow" && d.chatProgress?.mode === "live-card" && d.workflow?.value === undefined && (!result.isError || (d.workflow?.trace.length ?? 0) > 0)) {
|
|
2509
|
+
return compact(renderWorkflowChatProgress(d, result, theme, options.expanded ? resolveMainWindowRenderLayout() : layout, frame, options.expanded));
|
|
2510
|
+
}
|
|
1302
2511
|
if (!d || !d.results.length) {
|
|
1303
2512
|
const t = result.content[0];
|
|
1304
2513
|
const text = t?.type === "text" ? t.text : "(no output)";
|
|
1305
|
-
const contextPrefix = d?.context
|
|
2514
|
+
const contextPrefix = contextModePrefix(theme, d?.context);
|
|
1306
2515
|
const width = getTermWidth() - 4;
|
|
1307
2516
|
if (!text.includes(`
|
|
1308
2517
|
`))
|
|
1309
|
-
return new Text(truncLine(`${contextPrefix}${text}`, width), 0, 0);
|
|
2518
|
+
return compact(new Text(truncLine(`${contextPrefix}${text}`, width), 0, 0));
|
|
2519
|
+
if (d && !options.expanded && !result.isError) {
|
|
2520
|
+
const lines = text.split(/\r?\n/);
|
|
2521
|
+
const firstNonEmptyLine = lines.find((line) => line.trim())?.trim() || "(no output)";
|
|
2522
|
+
const compactLine = d.mode === "workflow" && d.preflight ? formatWorkflowPreflightPlanSummary(d.preflight) : firstNonEmptyLine;
|
|
2523
|
+
const c = new Container;
|
|
2524
|
+
const detailIndent = mainWindowIndent(layout, 1);
|
|
2525
|
+
c.addChild(new Text(truncLine(`${contextPrefix}${compactLine} · ${lines.length} lines`, width), 0, 0));
|
|
2526
|
+
c.addChild(new Text(truncLine(theme.fg("accent", `${detailIndent}Press ${liveDetailKeyText()} for full output`), width), 0, 0));
|
|
2527
|
+
return compact(c);
|
|
2528
|
+
}
|
|
1310
2529
|
const c = new Container;
|
|
1311
2530
|
const wrapped = wrapPlainText(`${contextPrefix}${text}`, width);
|
|
1312
2531
|
for (const line of wrapped)
|
|
@@ -1317,25 +2536,34 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1317
2536
|
const mdTheme = getMarkdownTheme();
|
|
1318
2537
|
if (d.mode === "single" && d.results.length === 1) {
|
|
1319
2538
|
const r = d.results[0];
|
|
2539
|
+
const detachableShortcut = d.asyncId || d.background ? undefined : foregroundDetachShortcut;
|
|
2540
|
+
if (!r)
|
|
2541
|
+
return compact(renderMultiCompact(d, theme, layout, frame));
|
|
1320
2542
|
if (!expanded)
|
|
1321
|
-
return renderSingleCompact(d, r, theme, frame);
|
|
1322
|
-
const isRunning = r
|
|
1323
|
-
const
|
|
1324
|
-
const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
2543
|
+
return compact(renderSingleCompact(d, r, theme, layout, frame, detachableShortcut));
|
|
2544
|
+
const isRunning = isResultRunning(r);
|
|
2545
|
+
const contextBadge = contextModeBadge(theme, r.context ?? d.context);
|
|
1325
2546
|
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
2547
|
+
const presentation = styledResultPresentation(resultPresentation(r, output, isRunning, undefined, frame), theme);
|
|
1326
2548
|
const progressInfo = isRunning && r.progress ? ` | ${r.progress.toolCount} tools, ${formatTokens(r.progress.tokens)} tok, ${formatDuration(r.progress.durationMs)}` : r.progressSummary ? ` | ${r.progressSummary.toolCount} tools, ${formatTokens(r.progressSummary.tokens)} tok, ${formatDuration(r.progressSummary.durationMs)}` : "";
|
|
1327
2549
|
const w = getTermWidth() - 4;
|
|
1328
2550
|
const fit = (text) => expanded ? text : truncLine(text, w);
|
|
1329
2551
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
1330
2552
|
const c = new Container;
|
|
1331
|
-
c.addChild(new Text(fit(`${
|
|
2553
|
+
c.addChild(new Text(fit(`${presentation.glyph} ${theme.fg("toolTitle", theme.bold(foregroundSingleDisplayName(r)))}${contextBadge}${progressInfo} ${theme.fg("dim", "·")} ${presentation.label}`), 0, 0));
|
|
1332
2554
|
c.addChild(new Spacer(1));
|
|
1333
2555
|
const taskMaxLen = Math.max(20, w - 8);
|
|
1334
2556
|
const taskPreview = expanded || r.task.length <= taskMaxLen ? r.task : `${r.task.slice(0, taskMaxLen)}...`;
|
|
1335
2557
|
c.addChild(new Text(fit(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0));
|
|
1336
2558
|
c.addChild(new Spacer(1));
|
|
2559
|
+
if (!isRunning && (r.exitCode !== 0 || r.interrupted || r.detached || r.stopped)) {
|
|
2560
|
+
c.addChild(new Text(fit(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` ⎿ ${resultStatusLine(r, output)}`)), 0, 0));
|
|
2561
|
+
}
|
|
1337
2562
|
if (isRunning && r.progress) {
|
|
1338
2563
|
const progressSnapshotNow = snapshotNowForProgress(r.progress);
|
|
2564
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, w, true, progressSnapshotNow, 12)) {
|
|
2565
|
+
c.addChild(new Text(fit(` ${nestedLine}`), 0, 0));
|
|
2566
|
+
}
|
|
1339
2567
|
const toolLine = formatCurrentToolLine(r.progress, w, expanded, progressSnapshotNow);
|
|
1340
2568
|
if (toolLine) {
|
|
1341
2569
|
c.addChild(new Text(fit(theme.fg("warning", `> ${toolLine}`)), 0, 0));
|
|
@@ -1344,23 +2572,27 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1344
2572
|
if (liveStatusLine) {
|
|
1345
2573
|
c.addChild(new Text(fit(theme.fg("accent", liveStatusLine)), 0, 0));
|
|
1346
2574
|
}
|
|
1347
|
-
c.addChild(new Text(fit(theme.fg("accent",
|
|
2575
|
+
c.addChild(new Text(fit(theme.fg("accent", foregroundSingleHintText(detachableShortcut))), 0, 0));
|
|
1348
2576
|
if (r.artifactPaths) {
|
|
1349
2577
|
c.addChild(new Text(fit(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
1350
2578
|
}
|
|
1351
2579
|
if (r.progress.recentTools?.length) {
|
|
1352
2580
|
for (const t of r.progress.recentTools.slice(-3)) {
|
|
1353
2581
|
const maxArgsLen = Math.max(40, w - 24);
|
|
1354
|
-
const argsPreview =
|
|
2582
|
+
const argsPreview = renderToolArgsPreview(t.args, maxArgsLen, expanded);
|
|
1355
2583
|
c.addChild(new Text(fit(theme.fg("dim", `${t.tool}: ${argsPreview}`)), 0, 0));
|
|
1356
2584
|
}
|
|
1357
2585
|
}
|
|
1358
|
-
for (const line of (r.progress.recentOutput
|
|
2586
|
+
for (const line of compactRecentOutputLines(r.progress.recentOutput)) {
|
|
1359
2587
|
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
1360
2588
|
}
|
|
1361
2589
|
if (toolLine || liveStatusLine || r.progress.recentTools?.length || r.progress.recentOutput?.length || r.artifactPaths) {
|
|
1362
2590
|
c.addChild(new Spacer(1));
|
|
1363
2591
|
}
|
|
2592
|
+
} else {
|
|
2593
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, w, true, r.progress?.lastActivityAt, 8)) {
|
|
2594
|
+
c.addChild(new Text(fit(` ${nestedLine}`), 0, 0));
|
|
2595
|
+
}
|
|
1364
2596
|
}
|
|
1365
2597
|
if (expanded) {
|
|
1366
2598
|
for (const line of toolCallLines) {
|
|
@@ -1392,13 +2624,24 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1392
2624
|
return c;
|
|
1393
2625
|
}
|
|
1394
2626
|
if (!expanded)
|
|
1395
|
-
return renderMultiCompact(d, theme, frame);
|
|
1396
|
-
const hasRunning =
|
|
1397
|
-
const
|
|
1398
|
-
const
|
|
1399
|
-
const
|
|
1400
|
-
const
|
|
1401
|
-
const
|
|
2627
|
+
return compact(renderMultiCompact(d, theme, layout, frame));
|
|
2628
|
+
const hasRunning = detailsHaveRunningResult(d);
|
|
2629
|
+
const detached = d.results.some((r) => r.detached) || workflowGraphHasStatus(d, ["detached"]);
|
|
2630
|
+
const stopped = d.results.some((r) => r.stopped) || workflowGraphHasStatus(d, ["stopped"]);
|
|
2631
|
+
const failed = d.results.some((r) => !hasTerminalResultFlag(r) && r.exitCode !== 0 && !isResultRunning(r)) || workflowGraphHasStatus(d, ["failed"]);
|
|
2632
|
+
const paused = d.results.some((r) => r.interrupted) || workflowGraphHasStatus(d, ["paused"]);
|
|
2633
|
+
const partial = workflowGraphHasStatus(d, ["partial"]);
|
|
2634
|
+
const completedWithoutOutput = d.results.some((r) => !hasTerminalResultFlag(r) && r.exitCode === 0 && !isResultRunning(r) && hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r)));
|
|
2635
|
+
const presentation = styledResultPresentation(semanticResultPresentation({
|
|
2636
|
+
running: hasRunning,
|
|
2637
|
+
detached,
|
|
2638
|
+
stopped,
|
|
2639
|
+
interrupted: paused,
|
|
2640
|
+
failed,
|
|
2641
|
+
partial,
|
|
2642
|
+
completedWithoutOutput,
|
|
2643
|
+
frame
|
|
2644
|
+
}), theme);
|
|
1402
2645
|
const totalSummary = d.progressSummary || d.results.reduce((acc, r) => {
|
|
1403
2646
|
const prog = r.progress || r.progressSummary;
|
|
1404
2647
|
if (prog) {
|
|
@@ -1414,22 +2657,21 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1414
2657
|
].filter(Boolean);
|
|
1415
2658
|
const summaryStr = summaryParts.length ? ` | ${summaryParts.join(", ")}` : "";
|
|
1416
2659
|
const modeLabel = d.mode;
|
|
1417
|
-
const contextBadge = d.context
|
|
2660
|
+
const contextBadge = contextModeBadge(theme, d.context);
|
|
1418
2661
|
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
1419
2662
|
const itemTitle = multiLabel.itemTitle;
|
|
1420
2663
|
const chainVis = d.chainAgents?.length && !multiLabel.hasParallelInChain ? d.chainAgents.map((agent, i) => {
|
|
1421
2664
|
const result = d.results[i];
|
|
1422
|
-
const
|
|
1423
|
-
const isComplete = result && result.exitCode === 0 && result.progress?.status !== "running";
|
|
1424
|
-
const isEmptyWithoutTarget = Boolean(result) && Boolean(isComplete) && hasEmptyTextOutputWithoutOutputTarget(result.task, getSingleResultOutput(result));
|
|
2665
|
+
const displayName = foregroundResultDisplayName(d, i, result, agent);
|
|
1425
2666
|
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
1426
|
-
const
|
|
1427
|
-
|
|
2667
|
+
const stepPresentation = result ? styledResultPresentation(resultPresentation(result, getSingleResultOutput(result), isCurrent && hasRunning && !hasTerminalResultFlag(result)), theme) : undefined;
|
|
2668
|
+
const stepStatus = stepPresentation ? `${stepPresentation.glyph} ${stepPresentation.label}` : theme.fg("dim", "◦ pending");
|
|
2669
|
+
return `${stepStatus} ${displayName}${contextModeBadge(theme, result?.context)}`;
|
|
1428
2670
|
}).join(theme.fg("dim", " → ")) : null;
|
|
1429
2671
|
const w = getTermWidth() - 4;
|
|
1430
2672
|
const fit = (text) => expanded ? text : truncLine(text, w);
|
|
1431
2673
|
const c = new Container;
|
|
1432
|
-
c.addChild(new Text(fit(`${
|
|
2674
|
+
c.addChild(new Text(fit(`${presentation.glyph} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} · ${multiLabel.headerLabel}${summaryStr} ${theme.fg("dim", "·")} ${presentation.label}`), 0, 0));
|
|
1433
2675
|
if (chainVis) {
|
|
1434
2676
|
c.addChild(new Text(fit(` ${chainVis}`), 0, 0));
|
|
1435
2677
|
}
|
|
@@ -1437,17 +2679,13 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1437
2679
|
const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
|
|
1438
2680
|
const displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : useResultsDirectly ? d.results.length : d.chainAgents.length;
|
|
1439
2681
|
const chainEntries = buildChainRenderEntries(d, multiLabel);
|
|
1440
|
-
const renderEntries = chainEntries ??
|
|
1441
|
-
const i = displayStart + offset;
|
|
1442
|
-
const r = d.results[i];
|
|
1443
|
-
const rowNumber = multiLabel.showActiveGroupOnly ? i - multiLabel.groupStartIndex + 1 : i + 1;
|
|
1444
|
-
return { kind: "result", resultIndex: i, rowNumber, agentName: useResultsDirectly ? r?.agent || `step-${rowNumber}` : d.chainAgents[i] || r?.agent || `step-${rowNumber}` };
|
|
1445
|
-
});
|
|
2682
|
+
const renderEntries = chainEntries ?? buildForegroundResultEntries(d, multiLabel, displayStart, displayEnd, useResultsDirectly);
|
|
1446
2683
|
c.addChild(new Spacer(1));
|
|
1447
2684
|
for (const entry of renderEntries) {
|
|
1448
|
-
if (entry.kind === "
|
|
2685
|
+
if (entry.kind === "group") {
|
|
1449
2686
|
const statusLabel = widgetStepStatus(entry.status, theme);
|
|
1450
|
-
|
|
2687
|
+
const groupLabel = entry.groupLabel ? ` (${compactTaskText(undefined, entry.groupLabel) ?? entry.groupLabel})` : "";
|
|
2688
|
+
c.addChild(new Text(fit(` ${statusLabel} ${entry.stepLabel}${groupLabel}`), 0, 0));
|
|
1451
2689
|
c.addChild(new Text(theme.fg(entry.status === "failed" ? "error" : "dim", ` status: ${entry.status}`), 0, 0));
|
|
1452
2690
|
if (entry.error)
|
|
1453
2691
|
c.addChild(new Text(theme.fg("error", ` error: ${entry.error}`), 0, 0));
|
|
@@ -1459,22 +2697,24 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1459
2697
|
const rowNumber = entry.rowNumber;
|
|
1460
2698
|
const agentName = entry.agentName;
|
|
1461
2699
|
if (!r) {
|
|
1462
|
-
const pendingLabel =
|
|
1463
|
-
|
|
2700
|
+
const pendingLabel = entry.rowLabel ?? (entry.isParallel ? "" : `${itemTitle} ${rowNumber}`);
|
|
2701
|
+
const labelPrefix = pendingLabel ? `${pendingLabel}: ` : "";
|
|
2702
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${labelPrefix}${agentName}`)), 0, 0));
|
|
1464
2703
|
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
1465
2704
|
c.addChild(new Spacer(1));
|
|
1466
2705
|
continue;
|
|
1467
2706
|
}
|
|
1468
|
-
const progressFromArray = d
|
|
2707
|
+
const progressFromArray = foregroundProgressForResult(d, i);
|
|
1469
2708
|
const rProg = r.progress || progressFromArray || r.progressSummary;
|
|
1470
|
-
const rRunning = rProg?.status
|
|
1471
|
-
const stepNumber = typeof rProg?.index === "number" ? rProg.index + 1 : i + 1;
|
|
2709
|
+
const rRunning = isResultRunning(r, rProg?.status);
|
|
1472
2710
|
const resultOutput = getSingleResultOutput(r);
|
|
1473
|
-
const
|
|
2711
|
+
const rowPresentation = styledResultPresentation(resultPresentation(r, resultOutput, rRunning, progressRunningSeed(rProg), frame), theme);
|
|
1474
2712
|
const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
|
|
1475
|
-
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1476
|
-
const stepLabel =
|
|
1477
|
-
const
|
|
2713
|
+
const modelDisplay = modelThinkingBadge(theme, r.model ?? rProg?.model, r.thinking ?? rProg?.thinking);
|
|
2714
|
+
const stepLabel = entry.rowLabel;
|
|
2715
|
+
const contextBadge = contextModeBadge(theme, r.context);
|
|
2716
|
+
const labelPrefix = stepLabel ? `${stepLabel}: ` : "";
|
|
2717
|
+
const stepHeader = rRunning ? `${rowPresentation.glyph} ${labelPrefix}${theme.bold(theme.fg("warning", agentName))}${contextBadge}${modelDisplay}${stats} ${theme.fg("dim", "·")} ${rowPresentation.label}` : `${rowPresentation.glyph} ${labelPrefix}${theme.bold(agentName)}${contextBadge}${modelDisplay}${stats} ${theme.fg("dim", "·")} ${rowPresentation.label}`;
|
|
1478
2718
|
const toolCallLines = getToolCallLines(r, expanded);
|
|
1479
2719
|
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
1480
2720
|
const taskMaxLen = Math.max(20, w - 12);
|
|
@@ -1484,6 +2724,9 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1484
2724
|
if (outputTarget) {
|
|
1485
2725
|
c.addChild(new Text(fit(theme.fg("dim", ` output: ${outputTarget}`)), 0, 0));
|
|
1486
2726
|
}
|
|
2727
|
+
if (!rRunning && (r.exitCode !== 0 || r.interrupted || r.detached || r.stopped)) {
|
|
2728
|
+
c.addChild(new Text(fit(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` ⎿ ${resultStatusLine(r, resultOutput)}`)), 0, 0));
|
|
2729
|
+
}
|
|
1487
2730
|
if (r.skills?.length) {
|
|
1488
2731
|
c.addChild(new Text(fit(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0));
|
|
1489
2732
|
}
|
|
@@ -1506,6 +2749,9 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1506
2749
|
if (liveStatusLine) {
|
|
1507
2750
|
c.addChild(new Text(fit(theme.fg("accent", ` ${liveStatusLine}`)), 0, 0));
|
|
1508
2751
|
}
|
|
2752
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, w, true, progressSnapshotNow, 8)) {
|
|
2753
|
+
c.addChild(new Text(fit(` ${nestedLine}`), 0, 0));
|
|
2754
|
+
}
|
|
1509
2755
|
c.addChild(new Text(fit(theme.fg("accent", ` ${liveDetailHintText()}`)), 0, 0));
|
|
1510
2756
|
if (r.artifactPaths) {
|
|
1511
2757
|
c.addChild(new Text(fit(theme.fg("dim", ` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
@@ -1513,15 +2759,19 @@ export function renderSubagentResult(result, options, theme, frame) {
|
|
|
1513
2759
|
if (rProg.recentTools?.length) {
|
|
1514
2760
|
for (const t of rProg.recentTools.slice(-3)) {
|
|
1515
2761
|
const maxArgsLen = Math.max(40, w - 30);
|
|
1516
|
-
const argsPreview =
|
|
2762
|
+
const argsPreview = renderToolArgsPreview(t.args, maxArgsLen, expanded);
|
|
1517
2763
|
c.addChild(new Text(fit(theme.fg("dim", ` ${t.tool}: ${argsPreview}`)), 0, 0));
|
|
1518
2764
|
}
|
|
1519
2765
|
}
|
|
1520
|
-
const
|
|
1521
|
-
for (const line of recentLines) {
|
|
2766
|
+
for (const line of compactRecentOutputLines(rProg.recentOutput)) {
|
|
1522
2767
|
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
1523
2768
|
}
|
|
1524
2769
|
}
|
|
2770
|
+
if (!rRunning) {
|
|
2771
|
+
for (const nestedLine of formatNestedWidgetLines(r.children, theme, w, true, r.progress?.lastActivityAt, 8)) {
|
|
2772
|
+
c.addChild(new Text(fit(` ${nestedLine}`), 0, 0));
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
1525
2775
|
if (!rRunning && r.artifactPaths) {
|
|
1526
2776
|
c.addChild(new Text(fit(theme.fg("dim", ` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
1527
2777
|
}
|