@bastani/atomic 0.8.13 → 0.8.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/host-html-template.ts +1 -1
- package/dist/builtin/mcp/init.ts +15 -2
- package/dist/builtin/mcp/mcp-callback-server.ts +10 -9
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/ui-session.ts +9 -6
- package/dist/builtin/subagents/CHANGELOG.md +8 -1
- package/dist/builtin/subagents/README.md +39 -32
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
- package/dist/builtin/subagents/src/agents/agent-management.ts +6 -1
- package/dist/builtin/subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/builtin/subagents/src/agents/agents.ts +44 -19
- package/dist/builtin/subagents/src/extension/config.ts +16 -0
- package/dist/builtin/subagents/src/extension/fanout-child.ts +246 -0
- package/dist/builtin/subagents/src/extension/index.ts +466 -603
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +6 -4
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +109 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +124 -19
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +41 -6
- package/dist/builtin/subagents/src/runs/background/async-resume.ts +28 -15
- package/dist/builtin/subagents/src/runs/background/async-status.ts +60 -30
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +111 -54
- package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +83 -0
- package/dist/builtin/subagents/src/runs/background/run-status.ts +79 -3
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +46 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +66 -14
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +10 -3
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +14 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +320 -23
- package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +23 -1
- package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +369 -0
- package/dist/builtin/subagents/src/runs/shared/nested-events.ts +935 -0
- package/dist/builtin/subagents/src/runs/shared/nested-path.ts +52 -0
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +115 -0
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +82 -9
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/single-output.ts +12 -2
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +32 -10
- package/dist/builtin/subagents/src/runs/shared/worktree.ts +3 -2
- package/dist/builtin/subagents/src/shared/artifacts.ts +0 -1
- package/dist/builtin/subagents/src/shared/types.ts +96 -1
- package/dist/builtin/subagents/src/shared/utils.ts +10 -2
- package/dist/builtin/subagents/src/slash/slash-commands.ts +468 -625
- package/dist/builtin/subagents/src/tui/render.ts +1227 -2093
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +24 -0
- package/dist/builtin/workflows/README.md +28 -11
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +323 -40
- package/dist/builtin/workflows/builtin/ralph.ts +362 -176
- package/dist/builtin/workflows/package.json +2 -5
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/skills/skill-creator/LICENSE.txt +202 -0
- package/dist/builtin/workflows/skills/skill-creator/SKILL.md +489 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/analyzer.md +274 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/comparator.md +202 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/grader.md +223 -0
- package/dist/builtin/workflows/skills/skill-creator/assets/eval_review.html +146 -0
- package/dist/builtin/workflows/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/dist/builtin/workflows/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/dist/builtin/workflows/skills/skill-creator/references/schemas.md +430 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/__init__.py +0 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/generate_report.py +326 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/improve_description.py +247 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/package_skill.py +136 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/run_eval.py +310 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/run_loop.py +328 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/utils.py +47 -0
- package/dist/builtin/workflows/src/extension/index.ts +869 -93
- package/dist/builtin/workflows/src/extension/render-call.ts +34 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +126 -21
- package/dist/builtin/workflows/src/extension/runtime.ts +91 -3
- package/dist/builtin/workflows/src/extension/wiring.ts +38 -12
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +62 -5
- package/dist/builtin/workflows/src/runs/background/runner.ts +3 -3
- package/dist/builtin/workflows/src/runs/background/status.ts +42 -8
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +410 -95
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +5 -2
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +8 -0
- package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +6 -4
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +3 -2
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +138 -5
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +30 -0
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +78 -120
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +193 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +26 -1
- package/dist/builtin/workflows/src/shared/store.ts +145 -17
- package/dist/builtin/workflows/src/shared/timing.ts +6 -2
- package/dist/builtin/workflows/src/shared/workflow-failures.ts +375 -0
- package/dist/builtin/workflows/src/tui/chat-surface.ts +68 -17
- package/dist/builtin/workflows/src/tui/connectors.ts +2 -2
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +24 -26
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +4 -8
- package/dist/builtin/workflows/src/tui/graph-view.ts +17 -14
- package/dist/builtin/workflows/src/tui/header.ts +38 -0
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +161 -238
- package/dist/builtin/workflows/src/tui/inline-form-editor.ts +68 -73
- package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -3
- package/dist/builtin/workflows/src/tui/inline-form-store.ts +2 -1
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +1 -3
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +286 -399
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +11 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +2 -1
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -1
- package/dist/builtin/workflows/src/tui/prompt-card.ts +46 -19
- package/dist/builtin/workflows/src/tui/run-detail.ts +63 -80
- package/dist/builtin/workflows/src/tui/session-confirm.ts +9 -3
- package/dist/builtin/workflows/src/tui/session-picker.ts +19 -16
- package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +88 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +368 -879
- package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +67 -75
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +50 -12
- package/dist/builtin/workflows/src/tui/submit-pane.ts +164 -0
- package/dist/builtin/workflows/src/tui/switcher.ts +27 -4
- package/dist/builtin/workflows/src/tui/text-helpers.ts +98 -4
- package/dist/builtin/workflows/src/tui/widget.ts +90 -68
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +23 -2
- package/dist/builtin/workflows/src/tui/workflow-list.ts +44 -68
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -3
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -10
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -1
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +16 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +8 -9
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +8 -6
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.js +6 -3
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +12 -29
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +8 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +145 -58
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +6 -20
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +38 -31
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +9 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +32 -24
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +8 -15
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +8 -22
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +5 -4
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js +34 -11
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +1 -0
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.js +1 -0
- package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -2
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.js +26 -9
- package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.d.ts +4 -0
- package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +9 -6
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +28 -7
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js +4 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +56 -53
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +3 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +8 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +3 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +44 -81
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +27 -12
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +2 -3
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +3 -3
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -5
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +2 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +11 -4
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +2 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +39 -21
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +9 -8
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +12 -2
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +20 -35
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/chat-input-actions.d.ts +24 -0
- package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -0
- package/dist/modes/interactive/chat-input-actions.js +179 -0
- package/dist/modes/interactive/chat-input-actions.js.map +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +14 -3
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +157 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-session-host.js +1007 -0
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +14 -5
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +29 -4
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +18 -67
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/child-process.d.ts +1 -0
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +8 -0
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +3 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js +14 -8
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +124 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +7 -27
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +75 -115
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/paths.d.ts +16 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +49 -7
- package/dist/utils/paths.js.map +1 -1
- package/docs/changelog.mdx +29 -0
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +1 -1
- package/docs/docs.json +98 -143
- package/docs/extensions.md +29 -16
- package/docs/favicon.svg +29 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/images/workflow-command.png +0 -0
- package/docs/images/workflow-graph.png +0 -0
- package/docs/images/workflow-input-picker.png +0 -0
- package/docs/images/workflow-list.png +0 -0
- package/docs/index.md +10 -1
- package/docs/logo.svg +59 -0
- package/docs/packages.md +3 -3
- package/docs/providers.md +1 -1
- package/docs/quickstart.md +98 -2
- package/docs/rpc.md +8 -8
- package/docs/sdk.md +23 -12
- package/docs/sessions.md +1 -1
- package/docs/skills.md +15 -1
- package/docs/termux.md +11 -1
- package/docs/themes.md +6 -6
- package/docs/tui.md +18 -18
- package/docs/usage.md +1 -1
- package/docs/workflows.md +172 -2
- package/examples/extensions/subagent/index.ts +2 -1
- package/package.json +6 -6
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/SKILL.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/element-attributes.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/playwright-tests.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/request-mocking.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/running-code.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/session-management.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/spec-driven-testing.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/storage-state.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/test-generation.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/tracing.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/video-recording.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/SKILL.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/deep-modules.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/interface-design.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/mocking.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/refactoring.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/tests.md +0 -0
|
@@ -5,52 +5,28 @@
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
7
7
|
import { getMarkdownTheme, type ExtensionContext } from "@bastani/atomic";
|
|
8
|
+
import { Container, Markdown, Spacer, Text, visibleWidth, type Component } from "@earendil-works/pi-tui";
|
|
8
9
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type AsyncJobState,
|
|
19
|
-
type AsyncJobStep,
|
|
20
|
-
type AsyncParallelGroupStatus,
|
|
21
|
-
type Details,
|
|
22
|
-
MAX_WIDGET_JOBS,
|
|
23
|
-
WIDGET_KEY,
|
|
10
|
+
type AgentProgress,
|
|
11
|
+
type AsyncJobState,
|
|
12
|
+
type AsyncJobStep,
|
|
13
|
+
type AsyncParallelGroupStatus,
|
|
14
|
+
type Details,
|
|
15
|
+
type NestedRunSummary,
|
|
16
|
+
type NestedStepSummary,
|
|
17
|
+
MAX_WIDGET_JOBS,
|
|
18
|
+
WIDGET_KEY,
|
|
24
19
|
} from "../shared/types.ts";
|
|
25
|
-
import {
|
|
26
|
-
formatTokens,
|
|
27
|
-
formatUsage,
|
|
28
|
-
formatDuration,
|
|
29
|
-
formatModelThinking,
|
|
30
|
-
formatToolCall,
|
|
31
|
-
shortenPath,
|
|
32
|
-
} from "../shared/formatters.ts";
|
|
20
|
+
import { formatTokens, formatUsage, formatDuration, formatModelThinking, formatToolCall, shortenPath } from "../shared/formatters.ts";
|
|
33
21
|
import { getDisplayItems, getSingleResultOutput } from "../shared/utils.ts";
|
|
34
22
|
import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
formatActivityLabel,
|
|
38
|
-
formatAgentRunningLabel,
|
|
39
|
-
formatParallelOutcome,
|
|
40
|
-
} from "../shared/status-format.ts";
|
|
23
|
+
import { formatNestedAggregate } from "../runs/shared/nested-render.ts";
|
|
24
|
+
import { aggregateStepStatus, formatActivityLabel, formatAgentRunningLabel, formatParallelOutcome } from "../shared/status-format.ts";
|
|
41
25
|
|
|
42
26
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
43
27
|
|
|
44
|
-
type RenderRequestingContext = ExtensionContext & {
|
|
45
|
-
ui: ExtensionContext["ui"] & { requestRender?: () => void };
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
function requestRender(ctx: ExtensionContext): void {
|
|
49
|
-
(ctx as RenderRequestingContext).ui.requestRender?.();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
28
|
function getTermWidth(): number {
|
|
53
|
-
|
|
29
|
+
return process.stdout.columns || 120;
|
|
54
30
|
}
|
|
55
31
|
|
|
56
32
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
@@ -65,2160 +41,1318 @@ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
|
65
41
|
* Uses Intl.Segmenter for proper Unicode/emoji handling (not char-by-char).
|
|
66
42
|
*/
|
|
67
43
|
function truncLine(text: string, maxWidth: number): string {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
44
|
+
if (visibleWidth(text) <= maxWidth) return text;
|
|
45
|
+
|
|
46
|
+
const targetWidth = maxWidth - 1;
|
|
47
|
+
let result = "";
|
|
48
|
+
let currentWidth = 0;
|
|
49
|
+
let activeStyles: string[] = [];
|
|
50
|
+
let i = 0;
|
|
51
|
+
|
|
52
|
+
while (i < text.length) {
|
|
53
|
+
const ansiMatch = text.slice(i).match(/^\x1b\[[0-9;]*m/);
|
|
54
|
+
if (ansiMatch) {
|
|
55
|
+
const code = ansiMatch[0];
|
|
56
|
+
result += code;
|
|
57
|
+
|
|
58
|
+
if (code === "\x1b[0m" || code === "\x1b[m") {
|
|
59
|
+
activeStyles = [];
|
|
60
|
+
} else {
|
|
61
|
+
activeStyles.push(code);
|
|
62
|
+
}
|
|
63
|
+
i += code.length;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let end = i;
|
|
68
|
+
while (end < text.length && !text.slice(end).match(/^\x1b\[[0-9;]*m/)) {
|
|
69
|
+
end++;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const textPortion = text.slice(i, end);
|
|
73
|
+
for (const seg of segmenter.segment(textPortion)) {
|
|
74
|
+
const grapheme = seg.segment;
|
|
75
|
+
const graphemeWidth = visibleWidth(grapheme);
|
|
76
|
+
|
|
77
|
+
if (currentWidth + graphemeWidth > targetWidth) {
|
|
78
|
+
return result + activeStyles.join("") + "…";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
result += grapheme;
|
|
82
|
+
currentWidth += graphemeWidth;
|
|
83
|
+
}
|
|
84
|
+
i = end;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result + activeStyles.join("") + "…";
|
|
112
88
|
}
|
|
113
89
|
|
|
114
90
|
const RUNNING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
115
91
|
const STATIC_RUNNING_GLYPH = "●";
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
let runningAnimationFrame = 0;
|
|
120
|
-
let widgetTimer: ReturnType<typeof setInterval> | undefined;
|
|
121
|
-
let latestWidgetCtx: ExtensionContext | undefined;
|
|
122
|
-
let latestWidgetJobs: AsyncJobState[] = [];
|
|
123
|
-
|
|
124
|
-
const resultAnimationTimers = new Map<
|
|
125
|
-
ReturnType<typeof setInterval>,
|
|
126
|
-
ResultAnimationContext["state"]
|
|
127
|
-
>();
|
|
128
|
-
|
|
129
|
-
type ProgressSeedSource = Partial<
|
|
130
|
-
Pick<
|
|
131
|
-
AgentProgress,
|
|
132
|
-
| "index"
|
|
133
|
-
| "toolCount"
|
|
134
|
-
| "tokens"
|
|
135
|
-
| "durationMs"
|
|
136
|
-
| "lastActivityAt"
|
|
137
|
-
| "currentToolStartedAt"
|
|
138
|
-
| "turnCount"
|
|
139
|
-
>
|
|
140
|
-
>;
|
|
92
|
+
|
|
93
|
+
type ProgressSeedSource = Partial<Pick<AgentProgress, "index" | "toolCount" | "tokens" | "durationMs" | "lastActivityAt" | "currentToolStartedAt" | "turnCount">>;
|
|
141
94
|
|
|
142
95
|
function runningSeed(...values: Array<number | undefined>): number | undefined {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
96
|
+
let seed: number | undefined;
|
|
97
|
+
for (const value of values) {
|
|
98
|
+
if (value === undefined || !Number.isFinite(value)) continue;
|
|
99
|
+
seed = (seed ?? 0) + Math.trunc(value);
|
|
100
|
+
}
|
|
101
|
+
return seed;
|
|
149
102
|
}
|
|
150
103
|
|
|
151
104
|
function runningGlyph(seed?: number): string {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return RUNNING_FRAMES[Math.abs(animatedSeed) % RUNNING_FRAMES.length]!;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function progressRunningSeed(
|
|
158
|
-
progress: ProgressSeedSource | undefined,
|
|
159
|
-
): number | undefined {
|
|
160
|
-
if (!progress) return undefined;
|
|
161
|
-
return runningSeed(
|
|
162
|
-
progress.index,
|
|
163
|
-
progress.toolCount,
|
|
164
|
-
progress.tokens,
|
|
165
|
-
progress.durationMs,
|
|
166
|
-
progress.lastActivityAt,
|
|
167
|
-
progress.currentToolStartedAt,
|
|
168
|
-
progress.turnCount,
|
|
169
|
-
);
|
|
105
|
+
if (seed === undefined) return STATIC_RUNNING_GLYPH;
|
|
106
|
+
return RUNNING_FRAMES[Math.abs(seed) % RUNNING_FRAMES.length]!;
|
|
170
107
|
}
|
|
171
108
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
109
|
+
function progressRunningSeed(progress: ProgressSeedSource | undefined): number | undefined {
|
|
110
|
+
if (!progress) return undefined;
|
|
111
|
+
return runningSeed(
|
|
112
|
+
progress.index,
|
|
113
|
+
progress.toolCount,
|
|
114
|
+
progress.tokens,
|
|
115
|
+
progress.durationMs,
|
|
116
|
+
progress.lastActivityAt,
|
|
117
|
+
progress.currentToolStartedAt,
|
|
118
|
+
progress.turnCount,
|
|
119
|
+
);
|
|
175
120
|
}
|
|
176
121
|
|
|
177
122
|
interface LegacyResultAnimationContext {
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function isStaleExtensionContextError(error: unknown): boolean {
|
|
182
|
-
return error instanceof Error && error.message.includes(STALE_EXTENSION_CONTEXT_MESSAGE);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function resultIsRunning(result: AgentToolResult<Details>): boolean {
|
|
186
|
-
return Boolean(
|
|
187
|
-
result.details?.progress?.some((entry) => entry.status === "running") ||
|
|
188
|
-
result.details?.results.some((entry) => entry.progress?.status === "running"),
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function stopResultAnimation(context: LegacyResultAnimationContext): void {
|
|
193
|
-
const timer = context.state.subagentResultAnimationTimer;
|
|
194
|
-
if (!timer) return;
|
|
195
|
-
clearInterval(timer);
|
|
196
|
-
resultAnimationTimers.delete(timer);
|
|
197
|
-
context.state.subagentResultAnimationTimer = undefined;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function clearLegacyResultAnimationTimer(
|
|
201
|
-
context: LegacyResultAnimationContext,
|
|
202
|
-
): void {
|
|
203
|
-
stopResultAnimation(context);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function syncResultAnimation(
|
|
207
|
-
result: AgentToolResult<Details>,
|
|
208
|
-
context: ResultAnimationContext,
|
|
209
|
-
): void {
|
|
210
|
-
if (!resultIsRunning(result)) {
|
|
211
|
-
stopResultAnimation(context);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if (context.state.subagentResultAnimationTimer) return;
|
|
215
|
-
const timer = setInterval(() => {
|
|
216
|
-
runningAnimationFrame = (runningAnimationFrame + 1) % RUNNING_FRAMES.length;
|
|
217
|
-
try {
|
|
218
|
-
context.invalidate();
|
|
219
|
-
} catch (error) {
|
|
220
|
-
if (!isStaleExtensionContextError(error)) throw error;
|
|
221
|
-
stopResultAnimation(context);
|
|
222
|
-
}
|
|
223
|
-
}, RUNNING_ANIMATION_MS);
|
|
224
|
-
timer.unref?.();
|
|
225
|
-
context.state.subagentResultAnimationTimer = timer;
|
|
226
|
-
resultAnimationTimers.set(timer, context.state);
|
|
123
|
+
state: { subagentResultAnimationTimer?: ReturnType<typeof setInterval> };
|
|
227
124
|
}
|
|
228
125
|
|
|
229
|
-
export function
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
resultAnimationTimers.clear();
|
|
126
|
+
export function clearLegacyResultAnimationTimer(context: LegacyResultAnimationContext): void {
|
|
127
|
+
const timer = context.state.subagentResultAnimationTimer;
|
|
128
|
+
if (!timer) return;
|
|
129
|
+
clearInterval(timer);
|
|
130
|
+
context.state.subagentResultAnimationTimer = undefined;
|
|
235
131
|
}
|
|
236
132
|
|
|
237
133
|
function extractOutputTarget(task: string): string | undefined {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
134
|
+
const writeToMatch = task.match(/\[Write to:\s*([^\]\n]+)\]/i);
|
|
135
|
+
if (writeToMatch?.[1]?.trim()) return writeToMatch[1].trim();
|
|
136
|
+
const findingsMatch = task.match(/Write your findings to:\s*(\S+)/i);
|
|
137
|
+
if (findingsMatch?.[1]?.trim()) return findingsMatch[1].trim();
|
|
138
|
+
const outputMatch = task.match(/[Oo]utput(?:\s+to)?\s*:\s*(\S+)/i);
|
|
139
|
+
if (outputMatch?.[1]?.trim()) return outputMatch[1].trim();
|
|
140
|
+
return undefined;
|
|
245
141
|
}
|
|
246
142
|
|
|
247
|
-
function hasEmptyTextOutputWithoutOutputTarget(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
): boolean {
|
|
251
|
-
if (output.trim()) return false;
|
|
252
|
-
return !extractOutputTarget(task);
|
|
143
|
+
function hasEmptyTextOutputWithoutOutputTarget(task: string, output: string): boolean {
|
|
144
|
+
if (output.trim()) return false;
|
|
145
|
+
return !extractOutputTarget(task);
|
|
253
146
|
}
|
|
254
147
|
|
|
255
148
|
function getToolCallLines(
|
|
256
|
-
|
|
257
|
-
|
|
149
|
+
result: Pick<Details["results"][number], "messages" | "toolCalls">,
|
|
150
|
+
expanded: boolean,
|
|
258
151
|
): string[] {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
return (
|
|
273
|
-
result.toolCalls?.map((toolCall) =>
|
|
274
|
-
expanded ? toolCall.expandedText : toolCall.text,
|
|
275
|
-
) ?? []
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function snapshotNowForProgress(
|
|
280
|
-
progress: Pick<
|
|
281
|
-
AgentProgress,
|
|
282
|
-
"currentToolStartedAt" | "durationMs" | "lastActivityAt"
|
|
283
|
-
>,
|
|
284
|
-
): number | undefined {
|
|
285
|
-
if (
|
|
286
|
-
progress.currentToolStartedAt !== undefined &&
|
|
287
|
-
progress.durationMs !== undefined
|
|
288
|
-
)
|
|
289
|
-
return progress.currentToolStartedAt + progress.durationMs;
|
|
290
|
-
return progress.lastActivityAt;
|
|
152
|
+
if (result.messages) {
|
|
153
|
+
return getDisplayItems(result.messages)
|
|
154
|
+
.filter((item): item is { type: "tool"; name: string; args: Record<string, unknown> } => item.type === "tool")
|
|
155
|
+
.map((item) => formatToolCall(item.name, item.args, expanded));
|
|
156
|
+
}
|
|
157
|
+
return result.toolCalls?.map((toolCall) => expanded ? toolCall.expandedText : toolCall.text) ?? [];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
function snapshotNowForProgress(progress: Pick<AgentProgress, "currentToolStartedAt" | "durationMs" | "lastActivityAt">): number | undefined {
|
|
162
|
+
if (progress.currentToolStartedAt !== undefined && progress.durationMs !== undefined) return progress.currentToolStartedAt + progress.durationMs;
|
|
163
|
+
return progress.lastActivityAt;
|
|
291
164
|
}
|
|
292
165
|
|
|
293
166
|
function formatCurrentToolLine(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
availableWidth: number,
|
|
299
|
-
expanded: boolean,
|
|
300
|
-
snapshotNow?: number,
|
|
301
|
-
): string | undefined {
|
|
302
|
-
if (!progress.currentTool) return undefined;
|
|
303
|
-
const maxToolArgsLen = Math.max(50, availableWidth - 20);
|
|
304
|
-
const toolArgsPreview = progress.currentToolArgs
|
|
305
|
-
? expanded || progress.currentToolArgs.length <= maxToolArgsLen
|
|
306
|
-
? progress.currentToolArgs
|
|
307
|
-
: `${progress.currentToolArgs.slice(0, maxToolArgsLen)}...`
|
|
308
|
-
: "";
|
|
309
|
-
const durationSuffix =
|
|
310
|
-
progress.currentToolStartedAt !== undefined && snapshotNow !== undefined
|
|
311
|
-
? ` | ${formatDuration(Math.max(0, snapshotNow - progress.currentToolStartedAt))}`
|
|
312
|
-
: "";
|
|
313
|
-
return toolArgsPreview
|
|
314
|
-
? `${progress.currentTool}: ${toolArgsPreview}${durationSuffix}`
|
|
315
|
-
: `${progress.currentTool}${durationSuffix}`;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function buildLiveStatusLine(
|
|
319
|
-
progress: Pick<AgentProgress, "activityState" | "lastActivityAt">,
|
|
320
|
-
snapshotNow?: number,
|
|
167
|
+
progress: Pick<AgentProgress, "currentTool" | "currentToolArgs" | "currentToolStartedAt">,
|
|
168
|
+
availableWidth: number,
|
|
169
|
+
expanded: boolean,
|
|
170
|
+
snapshotNow?: number,
|
|
321
171
|
): string | undefined {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
172
|
+
if (!progress.currentTool) return undefined;
|
|
173
|
+
const maxToolArgsLen = Math.max(50, availableWidth - 20);
|
|
174
|
+
const toolArgsPreview = progress.currentToolArgs
|
|
175
|
+
? (expanded || progress.currentToolArgs.length <= maxToolArgsLen
|
|
176
|
+
? progress.currentToolArgs
|
|
177
|
+
: `${progress.currentToolArgs.slice(0, maxToolArgsLen)}...`)
|
|
178
|
+
: "";
|
|
179
|
+
const durationSuffix = progress.currentToolStartedAt !== undefined && snapshotNow !== undefined
|
|
180
|
+
? ` | ${formatDuration(Math.max(0, snapshotNow - progress.currentToolStartedAt))}`
|
|
181
|
+
: "";
|
|
182
|
+
return toolArgsPreview
|
|
183
|
+
? `${progress.currentTool}: ${toolArgsPreview}${durationSuffix}`
|
|
184
|
+
: `${progress.currentTool}${durationSuffix}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildLiveStatusLine(progress: Pick<AgentProgress, "activityState" | "lastActivityAt">, snapshotNow?: number): string | undefined {
|
|
188
|
+
if (progress.lastActivityAt !== undefined && snapshotNow !== undefined) return formatActivityLabel(progress.lastActivityAt, progress.activityState, snapshotNow);
|
|
189
|
+
if (progress.activityState === "needs_attention") return "needs attention";
|
|
190
|
+
if (progress.activityState === "active_long_running") return "active but long-running";
|
|
191
|
+
if (progress.lastActivityAt !== undefined) return "active";
|
|
192
|
+
return undefined;
|
|
333
193
|
}
|
|
334
194
|
|
|
335
195
|
function themeBold(theme: Theme, text: string): string {
|
|
336
|
-
|
|
196
|
+
return ((theme as { bold?: (value: string) => string }).bold?.(text)) ?? text;
|
|
337
197
|
}
|
|
338
198
|
|
|
339
199
|
function statJoin(theme: Theme, parts: string[]): string {
|
|
340
|
-
|
|
341
|
-
.filter(Boolean)
|
|
342
|
-
.map((part) => theme.fg("dim", part))
|
|
343
|
-
.join(` ${theme.fg("dim", "·")} `);
|
|
200
|
+
return parts.filter(Boolean).map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "·")} `);
|
|
344
201
|
}
|
|
345
202
|
|
|
346
203
|
function formatTokenStat(tokens: number): string {
|
|
347
|
-
|
|
204
|
+
return `${formatTokens(tokens)} token`;
|
|
348
205
|
}
|
|
349
206
|
|
|
350
207
|
function formatToolUseStat(count: number): string {
|
|
351
|
-
|
|
208
|
+
return `${count} tool use${count === 1 ? "" : "s"}`;
|
|
352
209
|
}
|
|
353
210
|
|
|
354
|
-
function formatProgressStats(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (!progress) return "";
|
|
362
|
-
const parts: string[] = [];
|
|
363
|
-
if (progress.toolCount > 0) parts.push(formatToolUseStat(progress.toolCount));
|
|
364
|
-
if (progress.tokens > 0) parts.push(formatTokenStat(progress.tokens));
|
|
365
|
-
if (includeDuration && progress.durationMs > 0)
|
|
366
|
-
parts.push(formatDuration(progress.durationMs));
|
|
367
|
-
return statJoin(theme, parts);
|
|
211
|
+
function formatProgressStats(theme: Theme, progress: Pick<AgentProgress, "toolCount" | "tokens" | "durationMs"> | undefined, includeDuration = true): string {
|
|
212
|
+
if (!progress) return "";
|
|
213
|
+
const parts: string[] = [];
|
|
214
|
+
if (progress.toolCount > 0) parts.push(formatToolUseStat(progress.toolCount));
|
|
215
|
+
if (progress.tokens > 0) parts.push(formatTokenStat(progress.tokens));
|
|
216
|
+
if (includeDuration && progress.durationMs > 0) parts.push(formatDuration(progress.durationMs));
|
|
217
|
+
return statJoin(theme, parts);
|
|
368
218
|
}
|
|
369
219
|
|
|
370
220
|
function firstOutputLine(text: string): string {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
): string {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return `Error: ${result.error ?? (firstOutputLine(output) || `exit ${result.exitCode}`)}`;
|
|
390
|
-
if (hasEmptyTextOutputWithoutOutputTarget(result.task, output))
|
|
391
|
-
return "Done (no text output)";
|
|
392
|
-
return "Done";
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function resultGlyph(
|
|
396
|
-
result: Details["results"][number],
|
|
397
|
-
output: string,
|
|
398
|
-
theme: Theme,
|
|
399
|
-
running = result.progress?.status === "running",
|
|
400
|
-
seed = progressRunningSeed(result.progress ?? result.progressSummary),
|
|
401
|
-
): string {
|
|
402
|
-
if (running) return theme.fg("accent", runningGlyph(seed));
|
|
403
|
-
if (result.detached) return theme.fg("warning", "■");
|
|
404
|
-
if (result.interrupted) return theme.fg("warning", "■");
|
|
405
|
-
if (result.exitCode !== 0) return theme.fg("error", "✗");
|
|
406
|
-
if (hasEmptyTextOutputWithoutOutputTarget(result.task, output))
|
|
407
|
-
return theme.fg("warning", "✓");
|
|
408
|
-
return theme.fg("success", "✓");
|
|
221
|
+
return text.split("\n").find((line) => line.trim())?.trim() ?? "";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function resultStatusLine(result: Details["results"][number], output: string): string {
|
|
225
|
+
if (result.detached) return result.detachedReason ? `Detached: ${result.detachedReason}` : "Detached";
|
|
226
|
+
if (result.interrupted) return "Paused";
|
|
227
|
+
if (result.exitCode !== 0) return `Error: ${result.error ?? (firstOutputLine(output) || `exit ${result.exitCode}`)}`;
|
|
228
|
+
if (hasEmptyTextOutputWithoutOutputTarget(result.task, output)) return "Done (no text output)";
|
|
229
|
+
return "Done";
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function resultGlyph(result: Details["results"][number], output: string, theme: Theme, running = result.progress?.status === "running", seed = progressRunningSeed(result.progress ?? result.progressSummary)): string {
|
|
233
|
+
if (running) return theme.fg("accent", runningGlyph(seed));
|
|
234
|
+
if (result.detached) return theme.fg("warning", "■");
|
|
235
|
+
if (result.interrupted) return theme.fg("warning", "■");
|
|
236
|
+
if (result.exitCode !== 0) return theme.fg("error", "✗");
|
|
237
|
+
if (hasEmptyTextOutputWithoutOutputTarget(result.task, output)) return theme.fg("warning", "✓");
|
|
238
|
+
return theme.fg("success", "✓");
|
|
409
239
|
}
|
|
410
240
|
|
|
411
241
|
function compactCurrentActivity(progress: AgentProgress): string {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
formatCurrentToolLine(progress, getTermWidth() - 4, false, snapshotNow) ??
|
|
415
|
-
buildLiveStatusLine(progress, snapshotNow) ??
|
|
416
|
-
"thinking…"
|
|
417
|
-
);
|
|
242
|
+
const snapshotNow = snapshotNowForProgress(progress);
|
|
243
|
+
return formatCurrentToolLine(progress, getTermWidth() - 4, false, snapshotNow) ?? buildLiveStatusLine(progress, snapshotNow) ?? "thinking…";
|
|
418
244
|
}
|
|
419
245
|
|
|
420
246
|
export function widgetRenderKey(job: AsyncJobState): string {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
247
|
+
return JSON.stringify({
|
|
248
|
+
asyncDir: job.asyncDir,
|
|
249
|
+
status: job.status,
|
|
250
|
+
activityState: job.activityState,
|
|
251
|
+
lastActivityAt: job.lastActivityAt,
|
|
252
|
+
currentTool: job.currentTool,
|
|
253
|
+
currentToolStartedAt: job.currentToolStartedAt,
|
|
254
|
+
currentPath: job.currentPath,
|
|
255
|
+
turnCount: job.turnCount,
|
|
256
|
+
toolCount: job.toolCount,
|
|
257
|
+
mode: job.mode,
|
|
258
|
+
agents: job.agents,
|
|
259
|
+
currentStep: job.currentStep,
|
|
260
|
+
chainStepCount: job.chainStepCount,
|
|
261
|
+
parallelGroups: job.parallelGroups,
|
|
262
|
+
steps: job.steps,
|
|
263
|
+
nestedChildren: job.nestedChildren,
|
|
264
|
+
stepsTotal: job.stepsTotal,
|
|
265
|
+
runningSteps: job.runningSteps,
|
|
266
|
+
completedSteps: job.completedSteps,
|
|
267
|
+
activeParallelGroup: job.activeParallelGroup,
|
|
268
|
+
startedAt: job.startedAt,
|
|
269
|
+
updatedAt: job.updatedAt,
|
|
270
|
+
totalTokens: job.totalTokens,
|
|
271
|
+
});
|
|
445
272
|
}
|
|
446
273
|
|
|
447
274
|
function formatWidgetAgents(agents: string[]): string {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
return `${agents.slice(0, 2).join(", ")} +${agents.length - 2} more`;
|
|
453
|
-
return agents.join(", ");
|
|
275
|
+
const distinct = [...new Set(agents)];
|
|
276
|
+
if (distinct.length === 1 && agents.length > 1) return `${distinct[0]} ×${agents.length}`;
|
|
277
|
+
if (agents.length > 3) return `${agents.slice(0, 2).join(", ")} +${agents.length - 2} more`;
|
|
278
|
+
return agents.join(", ");
|
|
454
279
|
}
|
|
455
280
|
|
|
456
281
|
function widgetJobName(job: AsyncJobState): string {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
282
|
+
if (job.mode === "parallel") return "parallel";
|
|
283
|
+
if (job.mode === "chain") return "chain";
|
|
284
|
+
if (job.mode === "single" && job.agents?.length === 1) return job.agents[0]!;
|
|
285
|
+
if (job.agents?.length) return formatWidgetAgents(job.agents);
|
|
286
|
+
return job.mode ?? "subagent";
|
|
462
287
|
}
|
|
463
288
|
|
|
464
289
|
function widgetActivity(job: AsyncJobState): string {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
)
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
step.lastActivityAt,
|
|
500
|
-
step.currentToolStartedAt,
|
|
501
|
-
step.durationMs,
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function widgetStepsRunningSeed(
|
|
506
|
-
steps: Array<NonNullable<AsyncJobState["steps"]>[number]> | undefined,
|
|
507
|
-
): number | undefined {
|
|
508
|
-
let seed: number | undefined;
|
|
509
|
-
for (const [index, step] of (steps ?? []).entries())
|
|
510
|
-
seed = runningSeed(seed, widgetStepRunningSeed(step, index));
|
|
511
|
-
return seed;
|
|
290
|
+
const facts: string[] = [];
|
|
291
|
+
if (job.currentTool && job.currentToolStartedAt !== undefined && job.updatedAt !== undefined) facts.push(`${job.currentTool} ${formatDuration(Math.max(0, job.updatedAt - job.currentToolStartedAt))}`);
|
|
292
|
+
else if (job.currentTool) facts.push(job.currentTool);
|
|
293
|
+
if (job.currentPath) facts.push(shortenPath(job.currentPath));
|
|
294
|
+
if (job.turnCount !== undefined) facts.push(`${job.turnCount} turns`);
|
|
295
|
+
if (job.toolCount !== undefined) facts.push(`${job.toolCount} tools`);
|
|
296
|
+
const activity = buildLiveStatusLine(job, job.updatedAt);
|
|
297
|
+
if (activity && facts.length) return `${activity} · ${facts.join(" · ")}`;
|
|
298
|
+
if (activity) return activity;
|
|
299
|
+
if (facts.length) return facts.join(" · ");
|
|
300
|
+
if (job.status === "running") return "thinking…";
|
|
301
|
+
if (job.status === "queued") return "queued…";
|
|
302
|
+
if (job.status === "paused") return "Paused";
|
|
303
|
+
if (job.status === "failed") return "Failed";
|
|
304
|
+
return "Done";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function widgetStepRunningSeed(step: NonNullable<AsyncJobState["steps"]>[number], fallbackIndex?: number): number | undefined {
|
|
308
|
+
return runningSeed(
|
|
309
|
+
fallbackIndex,
|
|
310
|
+
step.index,
|
|
311
|
+
step.toolCount,
|
|
312
|
+
step.turnCount,
|
|
313
|
+
step.tokens?.total,
|
|
314
|
+
step.lastActivityAt,
|
|
315
|
+
step.currentToolStartedAt,
|
|
316
|
+
step.durationMs,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function widgetStepsRunningSeed(steps: Array<NonNullable<AsyncJobState["steps"]>[number]> | undefined): number | undefined {
|
|
321
|
+
let seed: number | undefined;
|
|
322
|
+
for (const [index, step] of (steps ?? []).entries()) seed = runningSeed(seed, widgetStepRunningSeed(step, index));
|
|
323
|
+
return seed;
|
|
512
324
|
}
|
|
513
325
|
|
|
514
326
|
function widgetJobRunningSeed(job: AsyncJobState): number | undefined {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
327
|
+
return runningSeed(
|
|
328
|
+
job.updatedAt,
|
|
329
|
+
job.lastActivityAt,
|
|
330
|
+
job.toolCount,
|
|
331
|
+
job.turnCount,
|
|
332
|
+
job.totalTokens?.total,
|
|
333
|
+
job.currentStep,
|
|
334
|
+
job.runningSteps,
|
|
335
|
+
job.completedSteps,
|
|
336
|
+
widgetStepsRunningSeed(job.steps),
|
|
337
|
+
);
|
|
526
338
|
}
|
|
527
339
|
|
|
528
340
|
function widgetJobsRunningSeed(jobs: AsyncJobState[]): number | undefined {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
341
|
+
let seed: number | undefined;
|
|
342
|
+
for (const job of jobs) seed = runningSeed(seed, widgetJobRunningSeed(job));
|
|
343
|
+
return seed;
|
|
532
344
|
}
|
|
533
345
|
|
|
534
346
|
function widgetStatusGlyph(job: AsyncJobState, theme: Theme): string {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
)
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
)) {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
job,
|
|
624
|
-
theme,
|
|
625
|
-
step,
|
|
626
|
-
"Step",
|
|
627
|
-
span.stepIndex + 1,
|
|
628
|
-
total,
|
|
629
|
-
expanded,
|
|
630
|
-
width,
|
|
631
|
-
),
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
return lines;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function widgetParallelAgentDetails(
|
|
638
|
-
job: AsyncJobState,
|
|
639
|
-
theme: Theme,
|
|
640
|
-
): string[] {
|
|
641
|
-
if (!job.steps?.length) return [];
|
|
642
|
-
if (job.mode !== "parallel" && job.mode !== "chain") return [];
|
|
643
|
-
if (
|
|
644
|
-
job.mode === "chain" &&
|
|
645
|
-
!job.activeParallelGroup &&
|
|
646
|
-
job.parallelGroups?.length
|
|
647
|
-
)
|
|
648
|
-
return widgetChainDetails(job, theme);
|
|
649
|
-
const total = job.stepsTotal ?? job.steps.length;
|
|
650
|
-
return job.steps.map((step, index) => {
|
|
651
|
-
const marker = index === job.steps!.length - 1 ? "└" : "├";
|
|
652
|
-
const activity = widgetStepActivity(step, job.updatedAt);
|
|
653
|
-
const itemTitle =
|
|
654
|
-
job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
655
|
-
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
656
|
-
return ` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`;
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
function parseParallelGroupAgentCount(
|
|
661
|
-
label: string | undefined,
|
|
662
|
-
): number | undefined {
|
|
663
|
-
if (!label || !label.startsWith("[") || !label.endsWith("]"))
|
|
664
|
-
return undefined;
|
|
665
|
-
const inner = label.slice(1, -1).trim();
|
|
666
|
-
if (!inner) return 0;
|
|
667
|
-
return inner
|
|
668
|
-
.split("+")
|
|
669
|
-
.map((part) => part.trim())
|
|
670
|
-
.filter(Boolean).length;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function isChainParallelGroupActive(
|
|
674
|
-
details: Pick<Details, "mode" | "chainAgents" | "currentStepIndex">,
|
|
675
|
-
): boolean {
|
|
676
|
-
if (details.mode !== "chain") return false;
|
|
677
|
-
if (details.currentStepIndex === undefined) return false;
|
|
678
|
-
const currentLabel = details.chainAgents?.[details.currentStepIndex];
|
|
679
|
-
return parseParallelGroupAgentCount(currentLabel) !== undefined;
|
|
347
|
+
if (job.status === "running") return theme.fg("accent", runningGlyph(widgetJobRunningSeed(job)));
|
|
348
|
+
if (job.status === "queued") return theme.fg("muted", "◦");
|
|
349
|
+
if (job.status === "complete") return theme.fg("success", "✓");
|
|
350
|
+
if (job.status === "paused") return theme.fg("warning", "■");
|
|
351
|
+
return theme.fg("error", "✗");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function widgetStepGlyph(status: AsyncJobStep["status"], theme: Theme, seed?: number): string {
|
|
355
|
+
if (status === "running") return theme.fg("accent", runningGlyph(seed));
|
|
356
|
+
if (status === "complete" || status === "completed") return theme.fg("success", "✓");
|
|
357
|
+
if (status === "failed") return theme.fg("error", "✗");
|
|
358
|
+
if (status === "paused") return theme.fg("warning", "■");
|
|
359
|
+
return theme.fg("muted", "◦");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function widgetStepStatus(status: AsyncJobStep["status"], theme: Theme): string {
|
|
363
|
+
if (status === "running") return theme.fg("accent", "running");
|
|
364
|
+
if (status === "complete" || status === "completed") return theme.fg("success", "complete");
|
|
365
|
+
if (status === "failed") return theme.fg("error", "failed");
|
|
366
|
+
if (status === "paused") return theme.fg("warning", "paused");
|
|
367
|
+
return theme.fg("dim", status);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function widgetStepActivity(step: NonNullable<AsyncJobState["steps"]>[number], snapshotNow?: number): string {
|
|
371
|
+
const facts: string[] = [];
|
|
372
|
+
if (step.currentTool && step.currentToolStartedAt !== undefined && snapshotNow !== undefined) facts.push(`${step.currentTool} ${formatDuration(Math.max(0, snapshotNow - step.currentToolStartedAt))}`);
|
|
373
|
+
else if (step.currentTool) facts.push(step.currentTool);
|
|
374
|
+
if (step.currentPath) facts.push(shortenPath(step.currentPath));
|
|
375
|
+
if (step.turnCount !== undefined) facts.push(`${step.turnCount} turns`);
|
|
376
|
+
if (step.toolCount !== undefined) facts.push(`${step.toolCount} tools`);
|
|
377
|
+
if (step.tokens?.total) facts.push(formatTokenStat(step.tokens.total));
|
|
378
|
+
const activity = buildLiveStatusLine(step, snapshotNow);
|
|
379
|
+
if (activity && facts.length) return `${activity} · ${facts.join(" · ")}`;
|
|
380
|
+
if (activity) return activity;
|
|
381
|
+
return facts.join(" · ");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
function widgetChainDetails(job: AsyncJobState, theme: Theme, expanded = false, width = getTermWidth()): string[] {
|
|
386
|
+
if (!job.steps?.length) return [];
|
|
387
|
+
const total = job.chainStepCount ?? job.steps.length;
|
|
388
|
+
const lines: string[] = [];
|
|
389
|
+
for (const span of buildAsyncChainStepSpans(total, job.steps.length, job.parallelGroups)) {
|
|
390
|
+
const steps = job.steps.slice(span.start, span.start + span.count);
|
|
391
|
+
if (span.isParallel) {
|
|
392
|
+
const status = aggregateStepStatus(steps);
|
|
393
|
+
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))}`);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const step = steps[0];
|
|
397
|
+
if (!step) {
|
|
398
|
+
lines.push(` ${theme.fg("dim", `◦ Step ${span.stepIndex + 1}/${total}: pending`)}`);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, "Step", span.stepIndex + 1, total, expanded, width));
|
|
402
|
+
}
|
|
403
|
+
return lines;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function widgetParallelAgentDetails(job: AsyncJobState, theme: Theme, expanded = false, width = getTermWidth()): string[] {
|
|
407
|
+
if (!job.steps?.length) return [];
|
|
408
|
+
if (job.mode !== "parallel" && job.mode !== "chain") return [];
|
|
409
|
+
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length) return widgetChainDetails(job, theme, expanded, width);
|
|
410
|
+
const total = job.stepsTotal ?? job.steps.length;
|
|
411
|
+
const lines: string[] = [];
|
|
412
|
+
for (const [index, step] of job.steps.entries()) {
|
|
413
|
+
const marker = index === job.steps.length - 1 ? "└" : "├";
|
|
414
|
+
const activity = widgetStepActivity(step, job.updatedAt);
|
|
415
|
+
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
416
|
+
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
417
|
+
lines.push(` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`);
|
|
418
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt, expanded ? 8 : 1)) lines.push(` ${nestedLine}`);
|
|
419
|
+
}
|
|
420
|
+
return lines;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function parseParallelGroupAgentCount(label: string | undefined): number | undefined {
|
|
424
|
+
if (!label || !label.startsWith("[") || !label.endsWith("]")) return undefined;
|
|
425
|
+
const inner = label.slice(1, -1).trim();
|
|
426
|
+
if (!inner) return 0;
|
|
427
|
+
return inner.split("+").map((part) => part.trim()).filter(Boolean).length;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function isChainParallelGroupActive(details: Pick<Details, "mode" | "chainAgents" | "currentStepIndex">): boolean {
|
|
431
|
+
if (details.mode !== "chain") return false;
|
|
432
|
+
if (details.currentStepIndex === undefined) return false;
|
|
433
|
+
const currentLabel = details.chainAgents?.[details.currentStepIndex];
|
|
434
|
+
return parseParallelGroupAgentCount(currentLabel) !== undefined;
|
|
680
435
|
}
|
|
681
436
|
|
|
682
437
|
interface ChainStepSpan {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
function buildChainStepSpans(
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
718
|
-
const group = parallelGroups.find(
|
|
719
|
-
(candidate) => candidate.stepIndex === stepIndex,
|
|
720
|
-
);
|
|
721
|
-
if (group) {
|
|
722
|
-
spans.push({
|
|
723
|
-
stepIndex,
|
|
724
|
-
start: group.start,
|
|
725
|
-
count: group.count,
|
|
726
|
-
isParallel: true,
|
|
727
|
-
});
|
|
728
|
-
flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
spans.push({
|
|
732
|
-
stepIndex,
|
|
733
|
-
start: flatIndex,
|
|
734
|
-
count: flatIndex < stepCount ? 1 : 0,
|
|
735
|
-
isParallel: false,
|
|
736
|
-
});
|
|
737
|
-
flatIndex++;
|
|
738
|
-
}
|
|
739
|
-
return spans;
|
|
438
|
+
stepIndex: number;
|
|
439
|
+
start: number;
|
|
440
|
+
count: number;
|
|
441
|
+
isParallel: boolean;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function buildChainStepSpans(chainAgents: string[] | undefined): ChainStepSpan[] {
|
|
445
|
+
if (!chainAgents?.length) return [];
|
|
446
|
+
const spans: ChainStepSpan[] = [];
|
|
447
|
+
let start = 0;
|
|
448
|
+
for (let stepIndex = 0; stepIndex < chainAgents.length; stepIndex++) {
|
|
449
|
+
const label = chainAgents[stepIndex]!;
|
|
450
|
+
const parsedCount = parseParallelGroupAgentCount(label);
|
|
451
|
+
const count = parsedCount ?? 1;
|
|
452
|
+
spans.push({ stepIndex, start, count, isParallel: parsedCount !== undefined });
|
|
453
|
+
start += count;
|
|
454
|
+
}
|
|
455
|
+
return spans;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function buildAsyncChainStepSpans(total: number, stepCount: number, parallelGroups: AsyncParallelGroupStatus[] = []): ChainStepSpan[] {
|
|
459
|
+
const spans: ChainStepSpan[] = [];
|
|
460
|
+
let flatIndex = 0;
|
|
461
|
+
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
462
|
+
const group = parallelGroups.find((candidate) => candidate.stepIndex === stepIndex);
|
|
463
|
+
if (group) {
|
|
464
|
+
spans.push({ stepIndex, start: group.start, count: group.count, isParallel: true });
|
|
465
|
+
flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
spans.push({ stepIndex, start: flatIndex, count: flatIndex < stepCount ? 1 : 0, isParallel: false });
|
|
469
|
+
flatIndex++;
|
|
470
|
+
}
|
|
471
|
+
return spans;
|
|
740
472
|
}
|
|
741
473
|
|
|
742
474
|
function isDoneResult(result: Details["results"][number]): boolean {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
475
|
+
const status = result.progress?.status;
|
|
476
|
+
if (status === "completed") return true;
|
|
477
|
+
if (status === "running" || status === "pending") return false;
|
|
478
|
+
if (result.interrupted || result.detached) return false;
|
|
479
|
+
return result.exitCode === 0;
|
|
748
480
|
}
|
|
749
481
|
|
|
750
482
|
interface MultiProgressLabel {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
function buildMultiProgressLabel(
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
activeParallelGroup,
|
|
860
|
-
groupStartIndex: groupStart,
|
|
861
|
-
groupEndIndex: groupEnd,
|
|
862
|
-
showActiveGroupOnly: true,
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if (details.mode === "chain" && details.chainAgents?.length) {
|
|
867
|
-
const totalCount = details.totalSteps ?? details.chainAgents.length;
|
|
868
|
-
const doneLogical = stepSpans.filter((span) => {
|
|
869
|
-
for (let index = span.start; index < span.start + span.count; index++) {
|
|
870
|
-
const progressEntry = details.progress?.find(
|
|
871
|
-
(progress) => progress.index === index,
|
|
872
|
-
);
|
|
873
|
-
const resultEntry =
|
|
874
|
-
details.results.find((result) => result.progress?.index === index) ??
|
|
875
|
-
details.results[index];
|
|
876
|
-
if (
|
|
877
|
-
progressEntry?.status === "running" ||
|
|
878
|
-
progressEntry?.status === "pending"
|
|
879
|
-
)
|
|
880
|
-
return false;
|
|
881
|
-
if (resultEntry && !isDoneResult(resultEntry)) return false;
|
|
882
|
-
}
|
|
883
|
-
return true;
|
|
884
|
-
}).length;
|
|
885
|
-
const currentStep =
|
|
886
|
-
details.currentStepIndex !== undefined
|
|
887
|
-
? details.currentStepIndex + 1
|
|
888
|
-
: Math.min(totalCount, doneLogical + (hasRunning ? 1 : 0));
|
|
889
|
-
const headerLabel = hasRunning
|
|
890
|
-
? `step ${currentStep}/${totalCount}`
|
|
891
|
-
: `step ${doneLogical}/${totalCount}`;
|
|
892
|
-
return {
|
|
893
|
-
headerLabel,
|
|
894
|
-
itemTitle,
|
|
895
|
-
totalCount,
|
|
896
|
-
hasParallelInChain,
|
|
897
|
-
activeParallelGroup,
|
|
898
|
-
groupStartIndex: 0,
|
|
899
|
-
groupEndIndex: details.results.length,
|
|
900
|
-
showActiveGroupOnly: false,
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
const totalCount = details.totalSteps ?? details.results.length;
|
|
905
|
-
const currentStep =
|
|
906
|
-
details.currentStepIndex !== undefined
|
|
907
|
-
? details.currentStepIndex + 1
|
|
908
|
-
: Math.min(
|
|
909
|
-
totalCount,
|
|
910
|
-
details.results.filter(isDoneResult).length + (hasRunning ? 1 : 0),
|
|
911
|
-
);
|
|
912
|
-
const done = details.results.filter(isDoneResult).length;
|
|
913
|
-
const headerLabel = hasRunning
|
|
914
|
-
? `step ${currentStep}/${totalCount}`
|
|
915
|
-
: `step ${done}/${totalCount}`;
|
|
916
|
-
return {
|
|
917
|
-
headerLabel,
|
|
918
|
-
itemTitle,
|
|
919
|
-
totalCount,
|
|
920
|
-
hasParallelInChain,
|
|
921
|
-
activeParallelGroup,
|
|
922
|
-
groupStartIndex: 0,
|
|
923
|
-
groupEndIndex: details.results.length,
|
|
924
|
-
showActiveGroupOnly: false,
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
function resultRowLabel(
|
|
929
|
-
details: Pick<Details, "mode" | "chainAgents">,
|
|
930
|
-
label: MultiProgressLabel,
|
|
931
|
-
resultIndex: number,
|
|
932
|
-
stepNumber: number,
|
|
933
|
-
): string {
|
|
934
|
-
if (details.mode === "chain" && label.hasParallelInChain) {
|
|
935
|
-
const span = buildChainStepSpans(details.chainAgents).find(
|
|
936
|
-
(candidate) =>
|
|
937
|
-
resultIndex >= candidate.start &&
|
|
938
|
-
resultIndex < candidate.start + candidate.count,
|
|
939
|
-
);
|
|
940
|
-
if (span?.isParallel)
|
|
941
|
-
return `Agent ${resultIndex - span.start + 1}/${span.count}`;
|
|
942
|
-
if (span) return `Step ${span.stepIndex + 1}`;
|
|
943
|
-
}
|
|
944
|
-
if (label.itemTitle === "Agent") {
|
|
945
|
-
const localStepNumber = label.activeParallelGroup
|
|
946
|
-
? Math.max(1, stepNumber - label.groupStartIndex)
|
|
947
|
-
: stepNumber;
|
|
948
|
-
return `Agent ${localStepNumber}/${label.totalCount}`;
|
|
949
|
-
}
|
|
950
|
-
return `Step ${stepNumber}`;
|
|
483
|
+
headerLabel: string;
|
|
484
|
+
itemTitle: "Step" | "Agent";
|
|
485
|
+
totalCount: number;
|
|
486
|
+
hasParallelInChain: boolean;
|
|
487
|
+
activeParallelGroup: boolean;
|
|
488
|
+
groupStartIndex: number;
|
|
489
|
+
groupEndIndex: number;
|
|
490
|
+
showActiveGroupOnly: boolean;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "progress" | "totalSteps" | "currentStepIndex" | "chainAgents">, hasRunning: boolean): MultiProgressLabel {
|
|
494
|
+
const stepSpans = buildChainStepSpans(details.chainAgents);
|
|
495
|
+
const hasParallelInChain = details.mode === "chain" && stepSpans.some((span) => span.isParallel);
|
|
496
|
+
const activeParallelGroup = isChainParallelGroupActive(details);
|
|
497
|
+
const itemTitle: "Step" | "Agent" = details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
|
|
498
|
+
|
|
499
|
+
if (details.mode === "parallel") {
|
|
500
|
+
const totalCount = details.totalSteps ?? details.results.length;
|
|
501
|
+
const statuses = new Array(totalCount).fill("pending") as Array<"pending" | "running" | "completed" | "failed" | "detached">;
|
|
502
|
+
for (const progress of details.progress ?? []) {
|
|
503
|
+
if (progress.index >= 0 && progress.index < totalCount) statuses[progress.index] = progress.status;
|
|
504
|
+
}
|
|
505
|
+
for (let i = 0; i < details.results.length; i++) {
|
|
506
|
+
const result = details.results[i]!;
|
|
507
|
+
const progressFromArray = details.progress?.find((progress) => progress.index === i)
|
|
508
|
+
|| details.progress?.find((progress) => progress.agent === result.agent && progress.status === "running");
|
|
509
|
+
const index = result.progress?.index ?? progressFromArray?.index ?? i;
|
|
510
|
+
if (index < 0 || index >= totalCount) continue;
|
|
511
|
+
const status = result.progress?.status
|
|
512
|
+
?? (result.interrupted || result.detached
|
|
513
|
+
? "detached"
|
|
514
|
+
: result.exitCode === 0
|
|
515
|
+
? "completed"
|
|
516
|
+
: "failed");
|
|
517
|
+
statuses[index] = status;
|
|
518
|
+
}
|
|
519
|
+
const running = statuses.filter((status) => status === "running").length;
|
|
520
|
+
const done = statuses.filter((status) => status === "completed").length;
|
|
521
|
+
const headerLabel = hasRunning
|
|
522
|
+
? `${formatAgentRunningLabel(running)} · ${done}/${totalCount} done`
|
|
523
|
+
: `${done}/${totalCount} done`;
|
|
524
|
+
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: totalCount, showActiveGroupOnly: false };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (activeParallelGroup) {
|
|
528
|
+
const currentStepIndex = details.currentStepIndex!;
|
|
529
|
+
const span = stepSpans[currentStepIndex];
|
|
530
|
+
const groupSize = span?.count ?? 1;
|
|
531
|
+
const groupStart = span?.start ?? 0;
|
|
532
|
+
const groupEnd = groupStart + groupSize;
|
|
533
|
+
let running = 0;
|
|
534
|
+
let done = 0;
|
|
535
|
+
for (let index = groupStart; index < groupEnd; index++) {
|
|
536
|
+
const progressEntry = details.progress?.find((progress) => progress.index === index);
|
|
537
|
+
const resultEntry = details.results.find((result) => result.progress?.index === index);
|
|
538
|
+
if (progressEntry?.status === "running") {
|
|
539
|
+
running++;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (progressEntry?.status === "completed") {
|
|
543
|
+
done++;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (resultEntry && isDoneResult(resultEntry)) done++;
|
|
547
|
+
}
|
|
548
|
+
const totalSteps = details.totalSteps ?? details.chainAgents?.length ?? 1;
|
|
549
|
+
const headerLabel = hasRunning
|
|
550
|
+
? `step ${currentStepIndex + 1}/${totalSteps} · parallel group: ${formatAgentRunningLabel(running)} · ${done}/${groupSize} done`
|
|
551
|
+
: `step ${currentStepIndex + 1}/${totalSteps} · parallel group: ${done}/${groupSize} done`;
|
|
552
|
+
return { headerLabel, itemTitle, totalCount: groupSize, hasParallelInChain, activeParallelGroup, groupStartIndex: groupStart, groupEndIndex: groupEnd, showActiveGroupOnly: true };
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (details.mode === "chain" && details.chainAgents?.length) {
|
|
556
|
+
const totalCount = details.totalSteps ?? details.chainAgents.length;
|
|
557
|
+
const doneLogical = stepSpans.filter((span) => {
|
|
558
|
+
for (let index = span.start; index < span.start + span.count; index++) {
|
|
559
|
+
const progressEntry = details.progress?.find((progress) => progress.index === index);
|
|
560
|
+
const resultEntry = details.results.find((result) => result.progress?.index === index) ?? details.results[index];
|
|
561
|
+
if (progressEntry?.status === "running" || progressEntry?.status === "pending") return false;
|
|
562
|
+
if (resultEntry && !isDoneResult(resultEntry)) return false;
|
|
563
|
+
}
|
|
564
|
+
return true;
|
|
565
|
+
}).length;
|
|
566
|
+
const currentStep = details.currentStepIndex !== undefined ? details.currentStepIndex + 1 : Math.min(totalCount, doneLogical + (hasRunning ? 1 : 0));
|
|
567
|
+
const headerLabel = hasRunning ? `step ${currentStep}/${totalCount}` : `step ${doneLogical}/${totalCount}`;
|
|
568
|
+
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const totalCount = details.totalSteps ?? details.results.length;
|
|
572
|
+
const currentStep = details.currentStepIndex !== undefined ? details.currentStepIndex + 1 : Math.min(totalCount, details.results.filter(isDoneResult).length + (hasRunning ? 1 : 0));
|
|
573
|
+
const done = details.results.filter(isDoneResult).length;
|
|
574
|
+
const headerLabel = hasRunning ? `step ${currentStep}/${totalCount}` : `step ${done}/${totalCount}`;
|
|
575
|
+
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function resultRowLabel(details: Pick<Details, "mode" | "chainAgents">, label: MultiProgressLabel, resultIndex: number, stepNumber: number): string {
|
|
579
|
+
if (details.mode === "chain" && label.hasParallelInChain) {
|
|
580
|
+
const span = buildChainStepSpans(details.chainAgents).find((candidate) => resultIndex >= candidate.start && resultIndex < candidate.start + candidate.count);
|
|
581
|
+
if (span?.isParallel) return `Agent ${resultIndex - span.start + 1}/${span.count}`;
|
|
582
|
+
if (span) return `Step ${span.stepIndex + 1}`;
|
|
583
|
+
}
|
|
584
|
+
if (label.itemTitle === "Agent") {
|
|
585
|
+
const localStepNumber = label.activeParallelGroup
|
|
586
|
+
? Math.max(1, stepNumber - label.groupStartIndex)
|
|
587
|
+
: stepNumber;
|
|
588
|
+
return `Agent ${localStepNumber}/${label.totalCount}`;
|
|
589
|
+
}
|
|
590
|
+
return `Step ${stepNumber}`;
|
|
951
591
|
}
|
|
952
592
|
|
|
953
593
|
function widgetStats(job: AsyncJobState, theme: Theme): string {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
function
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
)
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
594
|
+
const parts: string[] = [];
|
|
595
|
+
const stepsTotal = job.stepsTotal ?? (job.agents?.length ?? 1);
|
|
596
|
+
if (job.activeParallelGroup) {
|
|
597
|
+
const running = job.runningSteps ?? (job.status === "running" ? 1 : 0);
|
|
598
|
+
const done = job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
|
|
599
|
+
if (job.mode === "parallel") {
|
|
600
|
+
if (job.status === "running" && running > 0) parts.push(formatAgentRunningLabel(running));
|
|
601
|
+
if (stepsTotal > 0) parts.push(`${done}/${stepsTotal} done`);
|
|
602
|
+
} else {
|
|
603
|
+
const activeGroup = job.currentStep !== undefined
|
|
604
|
+
? job.parallelGroups?.find((group) => job.currentStep! >= group.start && job.currentStep! < group.start + group.count)
|
|
605
|
+
: job.parallelGroups?.find((group) => group.start === 0);
|
|
606
|
+
const logicalStep = activeGroup?.stepIndex ?? job.currentStep ?? 0;
|
|
607
|
+
const total = job.chainStepCount ?? stepsTotal;
|
|
608
|
+
const groupParts = [`${done}/${stepsTotal} done`];
|
|
609
|
+
if (job.status === "running" && running > 0) groupParts.unshift(formatAgentRunningLabel(running));
|
|
610
|
+
parts.push(`step ${logicalStep + 1}/${total} · parallel group: ${groupParts.join(" · ")}`);
|
|
611
|
+
}
|
|
612
|
+
} else if (job.currentStep !== undefined) {
|
|
613
|
+
if (job.mode === "chain" && job.parallelGroups?.length) {
|
|
614
|
+
const total = job.chainStepCount ?? stepsTotal;
|
|
615
|
+
parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`);
|
|
616
|
+
} else {
|
|
617
|
+
parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
|
|
618
|
+
}
|
|
619
|
+
} else if (stepsTotal > 1) {
|
|
620
|
+
parts.push(`steps ${stepsTotal}`);
|
|
621
|
+
}
|
|
622
|
+
if (job.toolCount !== undefined) parts.push(formatToolUseStat(job.toolCount));
|
|
623
|
+
if (job.totalTokens?.total) parts.push(formatTokenStat(job.totalTokens.total));
|
|
624
|
+
if (job.startedAt !== undefined && job.updatedAt !== undefined) parts.push(formatDuration(Math.max(0, job.updatedAt - job.startedAt)));
|
|
625
|
+
return statJoin(theme, parts);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function widgetStepStats(theme: Theme, step: NonNullable<AsyncJobState["steps"]>[number]): string {
|
|
629
|
+
return statJoin(theme, [
|
|
630
|
+
step.turnCount !== undefined ? `${step.turnCount} turns` : "",
|
|
631
|
+
step.toolCount !== undefined ? formatToolUseStat(step.toolCount) : "",
|
|
632
|
+
step.tokens?.total ? formatTokenStat(step.tokens.total) : "",
|
|
633
|
+
step.durationMs !== undefined ? formatDuration(step.durationMs) : "",
|
|
634
|
+
]);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function modelThinkingBadge(theme: Theme, model?: string, thinking?: string): string {
|
|
638
|
+
const label = formatModelThinking(model, thinking);
|
|
639
|
+
return label ? theme.fg("dim", ` (${label})`) : "";
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function widgetStepActivityLine(step: NonNullable<AsyncJobState["steps"]>[number], width: number, expanded: boolean, snapshotNow?: number): string {
|
|
643
|
+
const toolLine = formatCurrentToolLine(step, width, expanded, snapshotNow);
|
|
644
|
+
if (toolLine) return toolLine;
|
|
645
|
+
const activity = buildLiveStatusLine(step, snapshotNow);
|
|
646
|
+
if (activity) return activity;
|
|
647
|
+
if (step.status === "running") return "thinking…";
|
|
648
|
+
return "";
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function widgetOutputPath(job: AsyncJobState, step: NonNullable<AsyncJobState["steps"]>[number]): string | undefined {
|
|
652
|
+
if (typeof step.index !== "number") return undefined;
|
|
653
|
+
return path.join(job.asyncDir, `output-${step.index}.log`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function nestedRunName(run: NestedRunSummary): string {
|
|
657
|
+
if (run.agent) return run.agent;
|
|
658
|
+
if (run.agents?.length) return formatWidgetAgents(run.agents);
|
|
659
|
+
return run.id;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function nestedStatusGlyph(state: NestedRunSummary["state"] | NestedStepSummary["status"], theme: Theme, seed?: number): string {
|
|
663
|
+
if (state === "running") return theme.fg("accent", runningGlyph(seed));
|
|
664
|
+
if (state === "complete" || state === "completed") return theme.fg("success", "✓");
|
|
665
|
+
if (state === "failed") return theme.fg("error", "✗");
|
|
666
|
+
if (state === "paused") return theme.fg("warning", "■");
|
|
667
|
+
return theme.fg("muted", "◦");
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function nestedRunSeed(run: NestedRunSummary): number | undefined {
|
|
671
|
+
return runningSeed(run.lastUpdate, run.lastActivityAt, run.currentStep, run.toolCount, run.turnCount, run.totalTokens?.total, run.currentToolStartedAt);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function nestedActivity(input: Pick<NestedRunSummary | NestedStepSummary, "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount">, state: NestedRunSummary["state"] | NestedStepSummary["status"], snapshotNow?: number): string {
|
|
675
|
+
const facts: string[] = [];
|
|
676
|
+
if (input.currentTool && input.currentToolStartedAt !== undefined && snapshotNow !== undefined) facts.push(`${input.currentTool} ${formatDuration(Math.max(0, snapshotNow - input.currentToolStartedAt))}`);
|
|
677
|
+
else if (input.currentTool) facts.push(input.currentTool);
|
|
678
|
+
if (input.currentPath) facts.push(shortenPath(input.currentPath));
|
|
679
|
+
if (input.turnCount !== undefined) facts.push(`${input.turnCount} turns`);
|
|
680
|
+
if (input.toolCount !== undefined) facts.push(`${input.toolCount} tools`);
|
|
681
|
+
const activity = buildLiveStatusLine(input, snapshotNow);
|
|
682
|
+
if (activity && facts.length) return `${activity} · ${facts.join(" · ")}`;
|
|
683
|
+
if (activity) return activity;
|
|
684
|
+
if (facts.length) return facts.join(" · ");
|
|
685
|
+
if (state === "running") return "thinking…";
|
|
686
|
+
if (state === "queued" || state === "pending") return "queued…";
|
|
687
|
+
if (state === "paused") return "Paused";
|
|
688
|
+
if (state === "failed") return "Failed";
|
|
689
|
+
return "Done";
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function formatNestedWidgetLines(children: NestedRunSummary[] | undefined, theme: Theme, width: number, expanded: boolean, snapshotNow?: number, lineBudget = expanded ? 12 : 1): string[] {
|
|
693
|
+
if (!children?.length || lineBudget <= 0) return [];
|
|
694
|
+
if (!expanded) {
|
|
695
|
+
const aggregate = formatNestedAggregate(children);
|
|
696
|
+
return aggregate ? [theme.fg("dim", `↳ ${aggregate}`)] : [];
|
|
697
|
+
}
|
|
698
|
+
const lines: string[] = [];
|
|
699
|
+
const maxDepth = 2;
|
|
700
|
+
const append = (items: NestedRunSummary[] | undefined, depth: number, prefix: string): void => {
|
|
701
|
+
if (!items?.length || lines.length >= lineBudget) return;
|
|
702
|
+
if (depth > maxDepth) {
|
|
703
|
+
const aggregate = formatNestedAggregate(items);
|
|
704
|
+
if (aggregate && lines.length < lineBudget) lines.push(theme.fg("dim", `${prefix}↳ ${aggregate}`));
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
for (let index = 0; index < items.length; index++) {
|
|
708
|
+
const child = items[index]!;
|
|
709
|
+
if (lines.length >= lineBudget) {
|
|
710
|
+
const aggregate = formatNestedAggregate(items.slice(index));
|
|
711
|
+
if (aggregate) lines[lines.length - 1] = theme.fg("dim", `${prefix}↳ ${aggregate}`);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const activity = nestedActivity(child, child.state, snapshotNow ?? child.lastUpdate);
|
|
715
|
+
const error = child.error ? ` · ${child.error}` : "";
|
|
716
|
+
lines.push(theme.fg("dim", `${prefix}↳ ${nestedStatusGlyph(child.state, theme, nestedRunSeed(child))} ${nestedRunName(child)} · ${child.state} · ${activity}${error}`));
|
|
717
|
+
if (depth === maxDepth) {
|
|
718
|
+
const aggregate = formatNestedAggregate([...(child.steps?.flatMap((step) => step.children ?? []) ?? []), ...(child.children ?? [])]);
|
|
719
|
+
if (aggregate && lines.length < lineBudget) lines.push(theme.fg("dim", `${prefix} ↳ ${aggregate}`));
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
for (const step of child.steps ?? []) {
|
|
723
|
+
if (lines.length >= lineBudget) return;
|
|
724
|
+
lines.push(theme.fg("dim", `${prefix} ↳ ${nestedStatusGlyph(step.status, theme)} ${step.agent} · ${step.status} · ${nestedActivity(step, step.status, snapshotNow ?? child.lastUpdate)}`));
|
|
725
|
+
append(step.children, depth + 1, `${prefix} `);
|
|
726
|
+
}
|
|
727
|
+
append(child.children, depth + 1, `${prefix} `);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
append(children, 0, "");
|
|
731
|
+
return lines.map((line) => truncLine(line, width));
|
|
1043
732
|
}
|
|
1044
733
|
|
|
1045
734
|
function foregroundStyleWidgetStepLines(
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
): string[] {
|
|
1055
|
-
const status = widgetStepStatus(step.status, theme);
|
|
1056
|
-
const stats = widgetStepStats(theme, step);
|
|
1057
|
-
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1058
|
-
const lines = [
|
|
1059
|
-
` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1))} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1060
|
-
];
|
|
1061
|
-
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
1062
|
-
if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
|
|
1063
|
-
if (step.status === "running") {
|
|
1064
|
-
if (!expanded)
|
|
1065
|
-
lines.push(` ${theme.fg("accent", "Press ctrl+o for live detail")}`);
|
|
1066
|
-
const output = widgetOutputPath(job, step);
|
|
1067
|
-
if (output)
|
|
1068
|
-
lines.push(` ${theme.fg("dim", `output: ${shortenPath(output)}`)}`);
|
|
1069
|
-
if (expanded) {
|
|
1070
|
-
const liveStatus = buildLiveStatusLine(step, job.updatedAt);
|
|
1071
|
-
if (liveStatus && liveStatus !== activity)
|
|
1072
|
-
lines.push(` ${theme.fg("accent", liveStatus)}`);
|
|
1073
|
-
for (const tool of step.recentTools?.slice(-3) ?? []) {
|
|
1074
|
-
const maxArgsLen = Math.max(40, width - 30);
|
|
1075
|
-
const argsPreview =
|
|
1076
|
-
tool.args.length <= maxArgsLen
|
|
1077
|
-
? tool.args
|
|
1078
|
-
: `${tool.args.slice(0, maxArgsLen)}...`;
|
|
1079
|
-
lines.push(
|
|
1080
|
-
` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`,
|
|
1081
|
-
);
|
|
1082
|
-
}
|
|
1083
|
-
for (const line of step.recentOutput?.slice(-5) ?? []) {
|
|
1084
|
-
lines.push(` ${theme.fg("dim", line)}`);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
return lines;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
function foregroundStyleWidgetDetails(
|
|
1092
|
-
job: AsyncJobState,
|
|
1093
|
-
theme: Theme,
|
|
1094
|
-
expanded: boolean,
|
|
1095
|
-
width: number,
|
|
1096
|
-
): string[] {
|
|
1097
|
-
if (!job.steps?.length)
|
|
1098
|
-
return [` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`];
|
|
1099
|
-
if (
|
|
1100
|
-
job.mode === "chain" &&
|
|
1101
|
-
!job.activeParallelGroup &&
|
|
1102
|
-
job.parallelGroups?.length
|
|
1103
|
-
)
|
|
1104
|
-
return widgetChainDetails(job, theme, expanded, width);
|
|
1105
|
-
const total = job.stepsTotal ?? job.steps.length;
|
|
1106
|
-
const itemTitle =
|
|
1107
|
-
job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
1108
|
-
const lines: string[] = [];
|
|
1109
|
-
for (const [index, step] of job.steps.entries()) {
|
|
1110
|
-
lines.push(
|
|
1111
|
-
...foregroundStyleWidgetStepLines(
|
|
1112
|
-
job,
|
|
1113
|
-
theme,
|
|
1114
|
-
step,
|
|
1115
|
-
itemTitle,
|
|
1116
|
-
index + 1,
|
|
1117
|
-
total,
|
|
1118
|
-
expanded,
|
|
1119
|
-
width,
|
|
1120
|
-
),
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
return lines;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
function buildSingleWidgetLines(
|
|
1127
|
-
job: AsyncJobState,
|
|
1128
|
-
theme: Theme,
|
|
1129
|
-
width: number,
|
|
1130
|
-
expanded: boolean,
|
|
735
|
+
job: AsyncJobState,
|
|
736
|
+
theme: Theme,
|
|
737
|
+
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
738
|
+
itemTitle: "Agent" | "Step",
|
|
739
|
+
index: number,
|
|
740
|
+
total: number,
|
|
741
|
+
expanded: boolean,
|
|
742
|
+
width: number,
|
|
1131
743
|
): string[] {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
):
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
function buildWidgetComponent(
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
continue;
|
|
1310
|
-
}
|
|
1311
|
-
const stats = widgetStats(job, theme);
|
|
1312
|
-
items.push([
|
|
1313
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1314
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1315
|
-
...widgetParallelAgentDetails(job, theme),
|
|
1316
|
-
]);
|
|
1317
|
-
slots--;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
if (queued.length > 0 && slots > 0) {
|
|
1321
|
-
items.push([
|
|
1322
|
-
`${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`,
|
|
1323
|
-
]);
|
|
1324
|
-
queuedSummaryShown = true;
|
|
1325
|
-
slots--;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
for (const job of finished) {
|
|
1329
|
-
if (slots <= 0) {
|
|
1330
|
-
hiddenFinished++;
|
|
1331
|
-
continue;
|
|
1332
|
-
}
|
|
1333
|
-
const stats = widgetStats(job, theme);
|
|
1334
|
-
items.push([
|
|
1335
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1336
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1337
|
-
...widgetParallelAgentDetails(job, theme),
|
|
1338
|
-
]);
|
|
1339
|
-
slots--;
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
const hiddenQueued =
|
|
1343
|
-
queued.length > 0 && !queuedSummaryShown ? queued.length : 0;
|
|
1344
|
-
const hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
1345
|
-
if (hiddenTotal > 0) {
|
|
1346
|
-
const parts: string[] = [];
|
|
1347
|
-
if (hiddenRunning > 0) parts.push(`${hiddenRunning} running`);
|
|
1348
|
-
if (hiddenQueued > 0) parts.push(`${hiddenQueued} queued`);
|
|
1349
|
-
if (hiddenFinished > 0) parts.push(`${hiddenFinished} finished`);
|
|
1350
|
-
items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
for (let i = 0; i < items.length; i++) {
|
|
1354
|
-
const item = items[i]!;
|
|
1355
|
-
const last = i === items.length - 1;
|
|
1356
|
-
const branch = last ? "└─" : "├─";
|
|
1357
|
-
const continuation = last ? " " : "│ ";
|
|
1358
|
-
lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
|
|
1359
|
-
for (const detail of item.slice(1)) {
|
|
1360
|
-
lines.push(
|
|
1361
|
-
truncLine(`${theme.fg("dim", continuation)} ${detail}`, width),
|
|
1362
|
-
);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
return lines;
|
|
744
|
+
const status = widgetStepStatus(step.status, theme);
|
|
745
|
+
const stats = widgetStepStats(theme, step);
|
|
746
|
+
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
747
|
+
const lines = [` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index - 1))} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`];
|
|
748
|
+
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
749
|
+
if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
|
|
750
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, expanded, job.updatedAt)) {
|
|
751
|
+
lines.push(` ${nestedLine}`);
|
|
752
|
+
}
|
|
753
|
+
if (step.status === "running") {
|
|
754
|
+
if (!expanded) lines.push(` ${theme.fg("accent", "Press ctrl+o for live detail")}`);
|
|
755
|
+
const output = widgetOutputPath(job, step);
|
|
756
|
+
if (output) lines.push(` ${theme.fg("dim", `output: ${shortenPath(output)}`)}`);
|
|
757
|
+
if (expanded) {
|
|
758
|
+
const liveStatus = buildLiveStatusLine(step, job.updatedAt);
|
|
759
|
+
if (liveStatus && liveStatus !== activity) lines.push(` ${theme.fg("accent", liveStatus)}`);
|
|
760
|
+
for (const tool of step.recentTools?.slice(-3) ?? []) {
|
|
761
|
+
const maxArgsLen = Math.max(40, width - 30);
|
|
762
|
+
const argsPreview = tool.args.length <= maxArgsLen ? tool.args : `${tool.args.slice(0, maxArgsLen)}...`;
|
|
763
|
+
lines.push(` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`);
|
|
764
|
+
}
|
|
765
|
+
for (const line of step.recentOutput?.slice(-5) ?? []) {
|
|
766
|
+
lines.push(` ${theme.fg("dim", line)}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return lines;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded: boolean, width: number): string[] {
|
|
774
|
+
if (!job.steps?.length) return [
|
|
775
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
776
|
+
...formatNestedWidgetLines(job.nestedChildren, theme, width, expanded, job.updatedAt).map((line) => ` ${line}`),
|
|
777
|
+
];
|
|
778
|
+
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length) return widgetChainDetails(job, theme, expanded, width);
|
|
779
|
+
const total = job.stepsTotal ?? job.steps.length;
|
|
780
|
+
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
781
|
+
const lines: string[] = [];
|
|
782
|
+
for (const [index, step] of job.steps.entries()) {
|
|
783
|
+
lines.push(...foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index + 1, total, expanded, width));
|
|
784
|
+
}
|
|
785
|
+
const attached = new Set(job.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
786
|
+
const unattached = job.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
787
|
+
for (const nestedLine of formatNestedWidgetLines(unattached, theme, width, expanded, job.updatedAt)) {
|
|
788
|
+
lines.push(` ${nestedLine}`);
|
|
789
|
+
}
|
|
790
|
+
return lines;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function buildSingleWidgetLines(job: AsyncJobState, theme: Theme, width: number, expanded: boolean): string[] {
|
|
794
|
+
const stats = widgetStats(job, theme);
|
|
795
|
+
const count = job.mode === "chain" ? job.chainStepCount : job.stepsTotal ?? job.agents?.length ?? job.steps?.length;
|
|
796
|
+
const mode = widgetJobName(job);
|
|
797
|
+
const title = `async subagent ${mode}${count && count > 1 ? ` (${count})` : ""}`;
|
|
798
|
+
return [
|
|
799
|
+
`${theme.fg("toolTitle", themeBold(theme, title))} ${theme.fg("dim", "· background")}`,
|
|
800
|
+
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, mode)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
801
|
+
...foregroundStyleWidgetDetails(job, theme, expanded, width),
|
|
802
|
+
].map((line) => truncLine(line, width));
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function compactSingleWidgetLines(job: AsyncJobState, theme: Theme, width: number): string[] {
|
|
806
|
+
const fullLines = buildSingleWidgetLines(job, theme, width, false);
|
|
807
|
+
if (fullLines.length <= 10 || !job.steps?.length || (job.mode !== "parallel" && !job.activeParallelGroup)) return fullLines;
|
|
808
|
+
|
|
809
|
+
const total = job.stepsTotal ?? job.steps.length;
|
|
810
|
+
const itemTitle = job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
811
|
+
const lines = fullLines.slice(0, 2);
|
|
812
|
+
for (const [index, step] of job.steps.entries()) {
|
|
813
|
+
const status = widgetStepStatus(step.status, theme);
|
|
814
|
+
const activity = widgetStepActivityLine(step, width, false, job.updatedAt);
|
|
815
|
+
const stepStats = widgetStepStats(theme, step);
|
|
816
|
+
const activitySuffix = activity ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}` : "";
|
|
817
|
+
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
818
|
+
lines.push(` ${widgetStepGlyph(step.status, theme, widgetStepRunningSeed(step, index))} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`);
|
|
819
|
+
for (const nestedLine of formatNestedWidgetLines(step.children, theme, width, false, job.updatedAt)) lines.push(` ${nestedLine}`);
|
|
820
|
+
}
|
|
821
|
+
if (job.steps.some((step) => step.status === "running")) lines.push(theme.fg("accent", " Press ctrl+o for live detail"));
|
|
822
|
+
return lines.map((line) => truncLine(line, width));
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function fitWidgetLineBudget(lines: string[], theme: Theme, width: number, expanded: boolean): string[] {
|
|
826
|
+
const rows = process.stdout.rows || 30;
|
|
827
|
+
const budget = expanded
|
|
828
|
+
? Math.max(12, Math.min(24, Math.floor(rows * 0.55)))
|
|
829
|
+
: Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
|
|
830
|
+
if (lines.length <= budget) return lines;
|
|
831
|
+
const visibleLines = Math.max(1, budget - 1);
|
|
832
|
+
const hiddenCount = lines.length - visibleLines;
|
|
833
|
+
const hint = expanded
|
|
834
|
+
? `… ${hiddenCount} live-detail lines hidden`
|
|
835
|
+
: `… ${hiddenCount} lines hidden · ctrl+o expands`;
|
|
836
|
+
return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function buildWidgetComponent(jobs: AsyncJobState[], expanded: boolean): (_tui: unknown, theme: Theme) => Component {
|
|
840
|
+
return (_tui, theme) => {
|
|
841
|
+
const width = getTermWidth();
|
|
842
|
+
const lines = expanded
|
|
843
|
+
? buildWidgetLines(jobs, theme, width, true)
|
|
844
|
+
: jobs.length === 1
|
|
845
|
+
? compactSingleWidgetLines(jobs[0]!, theme, width)
|
|
846
|
+
: buildWidgetLines(jobs, theme, width, false);
|
|
847
|
+
const container = new Container();
|
|
848
|
+
for (const line of fitWidgetLineBudget(lines, theme, width, expanded)) container.addChild(new Text(line, 1, 0));
|
|
849
|
+
return container;
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
export function buildWidgetLines(jobs: AsyncJobState[], theme: Theme, width = getTermWidth(), expanded = false): string[] {
|
|
854
|
+
if (jobs.length === 0) return [];
|
|
855
|
+
if (jobs.length === 1) return buildSingleWidgetLines(jobs[0]!, theme, width, expanded);
|
|
856
|
+
const running = jobs.filter((job) => job.status === "running");
|
|
857
|
+
const queued = jobs.filter((job) => job.status === "queued");
|
|
858
|
+
const finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued");
|
|
859
|
+
|
|
860
|
+
const lines: string[] = [];
|
|
861
|
+
const hasActive = running.length > 0 || queued.length > 0;
|
|
862
|
+
const headerGlyph = running.length > 0 ? runningGlyph(widgetJobsRunningSeed(running)) : hasActive ? "●" : "○";
|
|
863
|
+
lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "· background")}`, width));
|
|
864
|
+
|
|
865
|
+
const items: string[][] = [];
|
|
866
|
+
let hiddenRunning = 0;
|
|
867
|
+
let hiddenFinished = 0;
|
|
868
|
+
let queuedSummaryShown = false;
|
|
869
|
+
let slots = MAX_WIDGET_JOBS;
|
|
870
|
+
|
|
871
|
+
for (const job of running) {
|
|
872
|
+
if (slots <= 0) { hiddenRunning++; continue; }
|
|
873
|
+
const stats = widgetStats(job, theme);
|
|
874
|
+
items.push([
|
|
875
|
+
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
876
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
877
|
+
...widgetParallelAgentDetails(job, theme, expanded, width),
|
|
878
|
+
]);
|
|
879
|
+
slots--;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (queued.length > 0 && slots > 0) {
|
|
883
|
+
items.push([`${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`]);
|
|
884
|
+
queuedSummaryShown = true;
|
|
885
|
+
slots--;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
for (const job of finished) {
|
|
889
|
+
if (slots <= 0) { hiddenFinished++; continue; }
|
|
890
|
+
const stats = widgetStats(job, theme);
|
|
891
|
+
items.push([
|
|
892
|
+
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
893
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
894
|
+
...widgetParallelAgentDetails(job, theme, expanded, width),
|
|
895
|
+
]);
|
|
896
|
+
slots--;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0;
|
|
900
|
+
const hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
901
|
+
if (hiddenTotal > 0) {
|
|
902
|
+
const parts: string[] = [];
|
|
903
|
+
if (hiddenRunning > 0) parts.push(`${hiddenRunning} running`);
|
|
904
|
+
if (hiddenQueued > 0) parts.push(`${hiddenQueued} queued`);
|
|
905
|
+
if (hiddenFinished > 0) parts.push(`${hiddenFinished} finished`);
|
|
906
|
+
items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
for (let i = 0; i < items.length; i++) {
|
|
910
|
+
const item = items[i]!;
|
|
911
|
+
const last = i === items.length - 1;
|
|
912
|
+
const branch = last ? "└─" : "├─";
|
|
913
|
+
const continuation = last ? " " : "│ ";
|
|
914
|
+
lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
|
|
915
|
+
for (const detail of item.slice(1)) {
|
|
916
|
+
lines.push(truncLine(`${theme.fg("dim", continuation)} ${detail}`, width));
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
return lines;
|
|
1367
921
|
}
|
|
1368
922
|
|
|
1369
923
|
/**
|
|
1370
924
|
* Render the async jobs widget
|
|
1371
925
|
*/
|
|
1372
|
-
export function renderWidget(
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
`${resultGlyph(r, output, theme, isRunning)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1416
|
-
width,
|
|
1417
|
-
),
|
|
1418
|
-
0,
|
|
1419
|
-
0,
|
|
1420
|
-
),
|
|
1421
|
-
);
|
|
1422
|
-
|
|
1423
|
-
if (isRunning && r.progress) {
|
|
1424
|
-
const progressSnapshotNow = snapshotNowForProgress(r.progress);
|
|
1425
|
-
const activity = compactCurrentActivity(r.progress);
|
|
1426
|
-
c.addChild(
|
|
1427
|
-
new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0),
|
|
1428
|
-
);
|
|
1429
|
-
const liveStatus = buildLiveStatusLine(r.progress, progressSnapshotNow);
|
|
1430
|
-
if (liveStatus && liveStatus !== activity)
|
|
1431
|
-
c.addChild(
|
|
1432
|
-
new Text(truncLine(theme.fg("dim", ` ${liveStatus}`), width), 0, 0),
|
|
1433
|
-
);
|
|
1434
|
-
c.addChild(
|
|
1435
|
-
new Text(
|
|
1436
|
-
truncLine(theme.fg("accent", " Press ctrl+o for live detail"), width),
|
|
1437
|
-
0,
|
|
1438
|
-
0,
|
|
1439
|
-
),
|
|
1440
|
-
);
|
|
1441
|
-
if (r.artifactPaths)
|
|
1442
|
-
c.addChild(
|
|
1443
|
-
new Text(
|
|
1444
|
-
truncLine(
|
|
1445
|
-
theme.fg(
|
|
1446
|
-
"dim",
|
|
1447
|
-
` output: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1448
|
-
),
|
|
1449
|
-
width,
|
|
1450
|
-
),
|
|
1451
|
-
0,
|
|
1452
|
-
0,
|
|
1453
|
-
),
|
|
1454
|
-
);
|
|
1455
|
-
return c;
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
c.addChild(
|
|
1459
|
-
new Text(
|
|
1460
|
-
truncLine(theme.fg("dim", ` ⎿ ${resultStatusLine(r, output)}`), width),
|
|
1461
|
-
0,
|
|
1462
|
-
0,
|
|
1463
|
-
),
|
|
1464
|
-
);
|
|
1465
|
-
const preview = firstOutputLine(output);
|
|
1466
|
-
if (
|
|
1467
|
-
preview &&
|
|
1468
|
-
r.exitCode === 0 &&
|
|
1469
|
-
!hasEmptyTextOutputWithoutOutputTarget(r.task, output)
|
|
1470
|
-
) {
|
|
1471
|
-
c.addChild(
|
|
1472
|
-
new Text(truncLine(theme.fg("dim", ` ${preview}`), width), 0, 0),
|
|
1473
|
-
);
|
|
1474
|
-
}
|
|
1475
|
-
if (r.sessionFile)
|
|
1476
|
-
c.addChild(
|
|
1477
|
-
new Text(
|
|
1478
|
-
truncLine(
|
|
1479
|
-
theme.fg("dim", ` session: ${shortenPath(r.sessionFile)}`),
|
|
1480
|
-
width,
|
|
1481
|
-
),
|
|
1482
|
-
0,
|
|
1483
|
-
0,
|
|
1484
|
-
),
|
|
1485
|
-
);
|
|
1486
|
-
if (r.artifactPaths)
|
|
1487
|
-
c.addChild(
|
|
1488
|
-
new Text(
|
|
1489
|
-
truncLine(
|
|
1490
|
-
theme.fg(
|
|
1491
|
-
"dim",
|
|
1492
|
-
` output: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1493
|
-
),
|
|
1494
|
-
width,
|
|
1495
|
-
),
|
|
1496
|
-
0,
|
|
1497
|
-
0,
|
|
1498
|
-
),
|
|
1499
|
-
);
|
|
1500
|
-
if (r.truncation?.artifactPath)
|
|
1501
|
-
c.addChild(
|
|
1502
|
-
new Text(
|
|
1503
|
-
truncLine(
|
|
1504
|
-
theme.fg(
|
|
1505
|
-
"dim",
|
|
1506
|
-
` full output: ${shortenPath(r.truncation.artifactPath)}`,
|
|
1507
|
-
),
|
|
1508
|
-
width,
|
|
1509
|
-
),
|
|
1510
|
-
0,
|
|
1511
|
-
0,
|
|
1512
|
-
),
|
|
1513
|
-
);
|
|
1514
|
-
return c;
|
|
926
|
+
export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[]): void {
|
|
927
|
+
if (jobs.length === 0) {
|
|
928
|
+
if (ctx.hasUI) ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (!ctx.hasUI) return;
|
|
932
|
+
ctx.ui.setWidget(WIDGET_KEY, buildWidgetComponent(jobs, ctx.ui.getToolsExpanded?.() ?? false));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function renderSingleCompact(d: Details, r: Details["results"][number], theme: Theme): Component {
|
|
936
|
+
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
937
|
+
const progress = r.progress || r.progressSummary;
|
|
938
|
+
const isRunning = r.progress?.status === "running";
|
|
939
|
+
const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
940
|
+
const stats = statJoin(theme, [
|
|
941
|
+
r.usage?.turns ? `⟳ ${r.usage.turns}` : "",
|
|
942
|
+
formatProgressStats(theme, progress),
|
|
943
|
+
]);
|
|
944
|
+
const c = new Container();
|
|
945
|
+
const width = getTermWidth() - 4;
|
|
946
|
+
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
947
|
+
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
948
|
+
|
|
949
|
+
if (isRunning && r.progress) {
|
|
950
|
+
const progressSnapshotNow = snapshotNowForProgress(r.progress);
|
|
951
|
+
const activity = compactCurrentActivity(r.progress);
|
|
952
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0));
|
|
953
|
+
const liveStatus = buildLiveStatusLine(r.progress, progressSnapshotNow);
|
|
954
|
+
if (liveStatus && liveStatus !== activity) c.addChild(new Text(truncLine(theme.fg("dim", ` ${liveStatus}`), width), 0, 0));
|
|
955
|
+
c.addChild(new Text(truncLine(theme.fg("accent", " Press ctrl+o for live detail"), width), 0, 0));
|
|
956
|
+
if (r.artifactPaths) c.addChild(new Text(truncLine(theme.fg("dim", ` output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0));
|
|
957
|
+
return c;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
|
|
961
|
+
const preview = firstOutputLine(output);
|
|
962
|
+
if (preview && r.exitCode === 0 && !hasEmptyTextOutputWithoutOutputTarget(r.task, output)) {
|
|
963
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ${preview}`), width), 0, 0));
|
|
964
|
+
}
|
|
965
|
+
if (r.sessionFile) c.addChild(new Text(truncLine(theme.fg("dim", ` session: ${shortenPath(r.sessionFile)}`), width), 0, 0));
|
|
966
|
+
if (r.artifactPaths) c.addChild(new Text(truncLine(theme.fg("dim", ` output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0));
|
|
967
|
+
if (r.truncation?.artifactPath) c.addChild(new Text(truncLine(theme.fg("dim", ` full output: ${shortenPath(r.truncation.artifactPath)}`), width), 0, 0));
|
|
968
|
+
return c;
|
|
1515
969
|
}
|
|
1516
970
|
|
|
1517
971
|
function renderMultiCompact(d: Details, theme: Theme): Component {
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
:
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
const r = d.results[i];
|
|
1589
|
-
const fallbackLabel = itemTitle.toLowerCase();
|
|
1590
|
-
const rowNumber = multiLabel.showActiveGroupOnly
|
|
1591
|
-
? i - multiLabel.groupStartIndex + 1
|
|
1592
|
-
: i + 1;
|
|
1593
|
-
const agentName = useResultsDirectly
|
|
1594
|
-
? r?.agent || `${fallbackLabel}-${rowNumber}`
|
|
1595
|
-
: d.chainAgents![i] || r?.agent || `${fallbackLabel}-${rowNumber}`;
|
|
1596
|
-
if (!r) {
|
|
1597
|
-
c.addChild(
|
|
1598
|
-
new Text(
|
|
1599
|
-
truncLine(
|
|
1600
|
-
theme.fg(
|
|
1601
|
-
"dim",
|
|
1602
|
-
` ◦ ${itemTitle} ${rowNumber}: ${agentName} · pending`,
|
|
1603
|
-
),
|
|
1604
|
-
width,
|
|
1605
|
-
),
|
|
1606
|
-
0,
|
|
1607
|
-
0,
|
|
1608
|
-
),
|
|
1609
|
-
);
|
|
1610
|
-
continue;
|
|
1611
|
-
}
|
|
1612
|
-
const output = getSingleResultOutput(r);
|
|
1613
|
-
const progressFromArray =
|
|
1614
|
-
d.progress?.find((p) => p.index === i) ||
|
|
1615
|
-
d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1616
|
-
const liveProgress = r.progress || progressFromArray;
|
|
1617
|
-
const rProg = liveProgress || r.progressSummary;
|
|
1618
|
-
const rRunning = liveProgress?.status === "running";
|
|
1619
|
-
const rPending = liveProgress?.status === "pending";
|
|
1620
|
-
const stepNumber =
|
|
1621
|
-
r.progress?.index !== undefined
|
|
1622
|
-
? r.progress.index + 1
|
|
1623
|
-
: progressFromArray?.index !== undefined
|
|
1624
|
-
? progressFromArray.index + 1
|
|
1625
|
-
: i + 1;
|
|
1626
|
-
const stepStats = formatProgressStats(theme, rProg);
|
|
1627
|
-
const glyph = rPending
|
|
1628
|
-
? theme.fg("dim", "◦")
|
|
1629
|
-
: resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg));
|
|
1630
|
-
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1631
|
-
const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
|
|
1632
|
-
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1633
|
-
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1634
|
-
if (rRunning && liveProgress) {
|
|
1635
|
-
const activity = compactCurrentActivity(liveProgress);
|
|
1636
|
-
c.addChild(
|
|
1637
|
-
new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0),
|
|
1638
|
-
);
|
|
1639
|
-
c.addChild(
|
|
1640
|
-
new Text(
|
|
1641
|
-
truncLine(
|
|
1642
|
-
theme.fg("accent", " Press ctrl+o for live detail"),
|
|
1643
|
-
width,
|
|
1644
|
-
),
|
|
1645
|
-
0,
|
|
1646
|
-
0,
|
|
1647
|
-
),
|
|
1648
|
-
);
|
|
1649
|
-
} else if (
|
|
1650
|
-
!rPending &&
|
|
1651
|
-
(r.exitCode !== 0 ||
|
|
1652
|
-
r.interrupted ||
|
|
1653
|
-
r.detached ||
|
|
1654
|
-
hasEmptyTextOutputWithoutOutputTarget(r.task, output))
|
|
1655
|
-
) {
|
|
1656
|
-
c.addChild(
|
|
1657
|
-
new Text(
|
|
1658
|
-
truncLine(
|
|
1659
|
-
theme.fg(
|
|
1660
|
-
r.exitCode !== 0 ? "error" : "dim",
|
|
1661
|
-
` ⎿ ${resultStatusLine(r, output)}`,
|
|
1662
|
-
),
|
|
1663
|
-
width,
|
|
1664
|
-
),
|
|
1665
|
-
0,
|
|
1666
|
-
0,
|
|
1667
|
-
),
|
|
1668
|
-
);
|
|
1669
|
-
}
|
|
1670
|
-
const outputTarget = extractOutputTarget(r.task);
|
|
1671
|
-
if (outputTarget)
|
|
1672
|
-
c.addChild(
|
|
1673
|
-
new Text(
|
|
1674
|
-
truncLine(theme.fg("dim", ` output: ${outputTarget}`), width),
|
|
1675
|
-
0,
|
|
1676
|
-
0,
|
|
1677
|
-
),
|
|
1678
|
-
);
|
|
1679
|
-
if (r.artifactPaths)
|
|
1680
|
-
c.addChild(
|
|
1681
|
-
new Text(
|
|
1682
|
-
truncLine(
|
|
1683
|
-
theme.fg(
|
|
1684
|
-
"dim",
|
|
1685
|
-
` output: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1686
|
-
),
|
|
1687
|
-
width,
|
|
1688
|
-
),
|
|
1689
|
-
0,
|
|
1690
|
-
0,
|
|
1691
|
-
),
|
|
1692
|
-
);
|
|
1693
|
-
}
|
|
1694
|
-
if (d.artifacts)
|
|
1695
|
-
c.addChild(
|
|
1696
|
-
new Text(
|
|
1697
|
-
truncLine(
|
|
1698
|
-
theme.fg("dim", ` artifacts: ${shortenPath(d.artifacts.dir)}`),
|
|
1699
|
-
width,
|
|
1700
|
-
),
|
|
1701
|
-
0,
|
|
1702
|
-
0,
|
|
1703
|
-
),
|
|
1704
|
-
);
|
|
1705
|
-
return c;
|
|
972
|
+
const hasRunning = d.progress?.some((p) => p.status === "running")
|
|
973
|
+
|| d.results.some((r) => r.progress?.status === "running");
|
|
974
|
+
const failed = d.results.some((r) => r.exitCode !== 0 && r.progress?.status !== "running");
|
|
975
|
+
const paused = d.results.some((r) => (r.interrupted || r.detached) && r.progress?.status !== "running");
|
|
976
|
+
let totalSummary = d.progressSummary;
|
|
977
|
+
if (!totalSummary) {
|
|
978
|
+
let sawProgress = false;
|
|
979
|
+
const summary = { toolCount: 0, tokens: 0, durationMs: 0 };
|
|
980
|
+
for (const r of d.results) {
|
|
981
|
+
const prog = r.progress || r.progressSummary;
|
|
982
|
+
if (!prog) continue;
|
|
983
|
+
sawProgress = true;
|
|
984
|
+
summary.toolCount += prog.toolCount;
|
|
985
|
+
summary.tokens += prog.tokens;
|
|
986
|
+
summary.durationMs = d.mode === "chain" ? summary.durationMs + prog.durationMs : Math.max(summary.durationMs, prog.durationMs);
|
|
987
|
+
}
|
|
988
|
+
if (sawProgress) totalSummary = summary;
|
|
989
|
+
}
|
|
990
|
+
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
991
|
+
const itemTitle = multiLabel.itemTitle;
|
|
992
|
+
const stats = statJoin(theme, [multiLabel.headerLabel, formatProgressStats(theme, totalSummary)]);
|
|
993
|
+
const glyph = hasRunning
|
|
994
|
+
? theme.fg("accent", runningGlyph(runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex)))
|
|
995
|
+
: failed
|
|
996
|
+
? theme.fg("error", "✗")
|
|
997
|
+
: paused
|
|
998
|
+
? theme.fg("warning", "■")
|
|
999
|
+
: theme.fg("success", "✓");
|
|
1000
|
+
const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1001
|
+
const c = new Container();
|
|
1002
|
+
const width = getTermWidth() - 4;
|
|
1003
|
+
c.addChild(new Text(truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1004
|
+
|
|
1005
|
+
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
1006
|
+
const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
|
|
1007
|
+
const displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : (useResultsDirectly ? d.results.length : d.chainAgents!.length);
|
|
1008
|
+
for (let i = displayStart; i < displayEnd; i++) {
|
|
1009
|
+
const r = d.results[i];
|
|
1010
|
+
const fallbackLabel = itemTitle.toLowerCase();
|
|
1011
|
+
const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
|
|
1012
|
+
const agentName = useResultsDirectly ? (r?.agent || `${fallbackLabel}-${rowNumber}`) : (d.chainAgents![i] || r?.agent || `${fallbackLabel}-${rowNumber}`);
|
|
1013
|
+
if (!r) {
|
|
1014
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${itemTitle} ${rowNumber}: ${agentName} · pending`), width), 0, 0));
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
const output = getSingleResultOutput(r);
|
|
1018
|
+
const progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1019
|
+
const rProg = (r.progress || progressFromArray || r.progressSummary) as AgentProgress | undefined;
|
|
1020
|
+
const rRunning = rProg && "status" in rProg && rProg.status === "running";
|
|
1021
|
+
const rPending = rProg && "status" in rProg && rProg.status === "pending";
|
|
1022
|
+
const stepNumber = r.progress?.index !== undefined ? r.progress.index + 1 : progressFromArray?.index !== undefined ? progressFromArray.index + 1 : i + 1;
|
|
1023
|
+
const stepStats = formatProgressStats(theme, rProg);
|
|
1024
|
+
const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg));
|
|
1025
|
+
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1026
|
+
const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
|
|
1027
|
+
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1028
|
+
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1029
|
+
if (rRunning && rProg && "status" in rProg) {
|
|
1030
|
+
const activity = compactCurrentActivity(rProg);
|
|
1031
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0));
|
|
1032
|
+
c.addChild(new Text(truncLine(theme.fg("accent", " Press ctrl+o for live detail"), width), 0, 0));
|
|
1033
|
+
} else if (!rPending && (r.exitCode !== 0 || r.interrupted || r.detached || hasEmptyTextOutputWithoutOutputTarget(r.task, output))) {
|
|
1034
|
+
c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` ⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
|
|
1035
|
+
}
|
|
1036
|
+
const outputTarget = extractOutputTarget(r.task);
|
|
1037
|
+
if (outputTarget) c.addChild(new Text(truncLine(theme.fg("dim", ` output: ${outputTarget}`), width), 0, 0));
|
|
1038
|
+
if (r.artifactPaths) c.addChild(new Text(truncLine(theme.fg("dim", ` output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0));
|
|
1039
|
+
}
|
|
1040
|
+
if (d.artifacts) c.addChild(new Text(truncLine(theme.fg("dim", ` artifacts: ${shortenPath(d.artifacts.dir)}`), width), 0, 0));
|
|
1041
|
+
return c;
|
|
1706
1042
|
}
|
|
1707
1043
|
|
|
1708
1044
|
/**
|
|
1709
1045
|
* Render a subagent result
|
|
1710
1046
|
*/
|
|
1711
1047
|
export function renderSubagentResult(
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1048
|
+
result: AgentToolResult<Details>,
|
|
1049
|
+
options: { expanded: boolean },
|
|
1050
|
+
theme: Theme,
|
|
1715
1051
|
): Component {
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
: 0;
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
if (!r) {
|
|
2023
|
-
c.addChild(
|
|
2024
|
-
new Text(
|
|
2025
|
-
fit(theme.fg("dim", ` ${itemTitle} ${rowNumber}: ${agentName}`)),
|
|
2026
|
-
0,
|
|
2027
|
-
0,
|
|
2028
|
-
),
|
|
2029
|
-
);
|
|
2030
|
-
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
2031
|
-
c.addChild(new Spacer(1));
|
|
2032
|
-
continue;
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
const progressFromArray =
|
|
2036
|
-
d.progress?.find((p) => p.index === i) ||
|
|
2037
|
-
d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
2038
|
-
const liveProgress = r.progress || progressFromArray;
|
|
2039
|
-
const rProg = liveProgress || r.progressSummary;
|
|
2040
|
-
const rRunning = liveProgress?.status === "running";
|
|
2041
|
-
const stepNumber =
|
|
2042
|
-
typeof liveProgress?.index === "number" ? liveProgress.index + 1 : i + 1;
|
|
2043
|
-
|
|
2044
|
-
const resultOutput = getSingleResultOutput(r);
|
|
2045
|
-
const statusIcon = rRunning
|
|
2046
|
-
? theme.fg("warning", "running")
|
|
2047
|
-
: r.exitCode !== 0
|
|
2048
|
-
? theme.fg("error", "failed")
|
|
2049
|
-
: hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput)
|
|
2050
|
-
? theme.fg("warning", "warning")
|
|
2051
|
-
: theme.fg("success", "done");
|
|
2052
|
-
const stats = rProg
|
|
2053
|
-
? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}`
|
|
2054
|
-
: "";
|
|
2055
|
-
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
2056
|
-
const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
|
|
2057
|
-
const stepHeader = rRunning
|
|
2058
|
-
? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats}`
|
|
2059
|
-
: `${statusIcon} ${stepLabel}: ${theme.bold(r.agent)}${modelDisplay}${stats}`;
|
|
2060
|
-
const toolCallLines = getToolCallLines(r, expanded);
|
|
2061
|
-
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
2062
|
-
|
|
2063
|
-
const taskMaxLen = Math.max(20, w - 12);
|
|
2064
|
-
const taskPreview =
|
|
2065
|
-
expanded || r.task.length <= taskMaxLen
|
|
2066
|
-
? r.task
|
|
2067
|
-
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
2068
|
-
c.addChild(
|
|
2069
|
-
new Text(fit(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0),
|
|
2070
|
-
);
|
|
2071
|
-
|
|
2072
|
-
const outputTarget = extractOutputTarget(r.task);
|
|
2073
|
-
if (outputTarget) {
|
|
2074
|
-
c.addChild(
|
|
2075
|
-
new Text(fit(theme.fg("dim", ` output: ${outputTarget}`)), 0, 0),
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
if (r.skills?.length) {
|
|
2080
|
-
c.addChild(
|
|
2081
|
-
new Text(
|
|
2082
|
-
fit(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)),
|
|
2083
|
-
0,
|
|
2084
|
-
0,
|
|
2085
|
-
),
|
|
2086
|
-
);
|
|
2087
|
-
}
|
|
2088
|
-
if (r.skillsWarning) {
|
|
2089
|
-
c.addChild(
|
|
2090
|
-
new Text(
|
|
2091
|
-
fit(theme.fg("warning", ` Warning: ${r.skillsWarning}`)),
|
|
2092
|
-
0,
|
|
2093
|
-
0,
|
|
2094
|
-
),
|
|
2095
|
-
);
|
|
2096
|
-
}
|
|
2097
|
-
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
2098
|
-
c.addChild(
|
|
2099
|
-
new Text(
|
|
2100
|
-
fit(
|
|
2101
|
-
theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" → ")}`),
|
|
2102
|
-
),
|
|
2103
|
-
0,
|
|
2104
|
-
0,
|
|
2105
|
-
),
|
|
2106
|
-
);
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
if (rRunning && liveProgress) {
|
|
2110
|
-
if (liveProgress.skills?.length) {
|
|
2111
|
-
c.addChild(
|
|
2112
|
-
new Text(
|
|
2113
|
-
fit(
|
|
2114
|
-
theme.fg(
|
|
2115
|
-
"accent",
|
|
2116
|
-
` skills: ${liveProgress.skills.join(", ")}`,
|
|
2117
|
-
),
|
|
2118
|
-
),
|
|
2119
|
-
0,
|
|
2120
|
-
0,
|
|
2121
|
-
),
|
|
2122
|
-
);
|
|
2123
|
-
}
|
|
2124
|
-
const progressSnapshotNow = snapshotNowForProgress(liveProgress);
|
|
2125
|
-
const toolLine = formatCurrentToolLine(
|
|
2126
|
-
liveProgress,
|
|
2127
|
-
w,
|
|
2128
|
-
expanded,
|
|
2129
|
-
progressSnapshotNow,
|
|
2130
|
-
);
|
|
2131
|
-
if (toolLine) {
|
|
2132
|
-
c.addChild(
|
|
2133
|
-
new Text(fit(theme.fg("warning", ` > ${toolLine}`)), 0, 0),
|
|
2134
|
-
);
|
|
2135
|
-
}
|
|
2136
|
-
const liveStatusLine = buildLiveStatusLine(
|
|
2137
|
-
liveProgress,
|
|
2138
|
-
progressSnapshotNow,
|
|
2139
|
-
);
|
|
2140
|
-
if (liveStatusLine) {
|
|
2141
|
-
c.addChild(
|
|
2142
|
-
new Text(fit(theme.fg("accent", ` ${liveStatusLine}`)), 0, 0),
|
|
2143
|
-
);
|
|
2144
|
-
}
|
|
2145
|
-
c.addChild(
|
|
2146
|
-
new Text(
|
|
2147
|
-
fit(theme.fg("accent", " Press ctrl+o for live detail")),
|
|
2148
|
-
0,
|
|
2149
|
-
0,
|
|
2150
|
-
),
|
|
2151
|
-
);
|
|
2152
|
-
if (r.artifactPaths) {
|
|
2153
|
-
c.addChild(
|
|
2154
|
-
new Text(
|
|
2155
|
-
fit(
|
|
2156
|
-
theme.fg(
|
|
2157
|
-
"dim",
|
|
2158
|
-
` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
2159
|
-
),
|
|
2160
|
-
),
|
|
2161
|
-
0,
|
|
2162
|
-
0,
|
|
2163
|
-
),
|
|
2164
|
-
);
|
|
2165
|
-
}
|
|
2166
|
-
if (liveProgress.recentTools?.length) {
|
|
2167
|
-
for (const t of liveProgress.recentTools.slice(-3)) {
|
|
2168
|
-
const maxArgsLen = Math.max(40, w - 30);
|
|
2169
|
-
const argsPreview =
|
|
2170
|
-
expanded || t.args.length <= maxArgsLen
|
|
2171
|
-
? t.args
|
|
2172
|
-
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
2173
|
-
c.addChild(
|
|
2174
|
-
new Text(
|
|
2175
|
-
fit(theme.fg("dim", ` ${t.tool}: ${argsPreview}`)),
|
|
2176
|
-
0,
|
|
2177
|
-
0,
|
|
2178
|
-
),
|
|
2179
|
-
);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
const recentLines = (liveProgress.recentOutput ?? []).slice(-5);
|
|
2183
|
-
for (const line of recentLines) {
|
|
2184
|
-
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
if (!rRunning && r.artifactPaths) {
|
|
2189
|
-
c.addChild(
|
|
2190
|
-
new Text(
|
|
2191
|
-
fit(
|
|
2192
|
-
theme.fg(
|
|
2193
|
-
"dim",
|
|
2194
|
-
` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
2195
|
-
),
|
|
2196
|
-
),
|
|
2197
|
-
0,
|
|
2198
|
-
0,
|
|
2199
|
-
),
|
|
2200
|
-
);
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
if (expanded && !rRunning) {
|
|
2204
|
-
for (const line of toolCallLines) {
|
|
2205
|
-
c.addChild(new Text(fit(theme.fg("muted", ` ${line}`)), 0, 0));
|
|
2206
|
-
}
|
|
2207
|
-
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
c.addChild(new Spacer(1));
|
|
2211
|
-
}
|
|
2212
|
-
|
|
2213
|
-
if (d.artifacts) {
|
|
2214
|
-
c.addChild(new Spacer(1));
|
|
2215
|
-
c.addChild(
|
|
2216
|
-
new Text(
|
|
2217
|
-
fit(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`)),
|
|
2218
|
-
0,
|
|
2219
|
-
0,
|
|
2220
|
-
),
|
|
2221
|
-
);
|
|
2222
|
-
}
|
|
2223
|
-
return c;
|
|
1052
|
+
const d = result.details;
|
|
1053
|
+
if (!d || !d.results.length) {
|
|
1054
|
+
const t = result.content[0];
|
|
1055
|
+
const text = t?.type === "text" ? t.text : "(no output)";
|
|
1056
|
+
const contextPrefix = d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "";
|
|
1057
|
+
return new Text(truncLine(`${contextPrefix}${text}`, getTermWidth() - 4), 0, 0);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const expanded = options.expanded;
|
|
1061
|
+
const mdTheme = getMarkdownTheme();
|
|
1062
|
+
|
|
1063
|
+
if (d.mode === "single" && d.results.length === 1) {
|
|
1064
|
+
const r = d.results[0];
|
|
1065
|
+
if (!expanded) return renderSingleCompact(d, r, theme);
|
|
1066
|
+
const isRunning = r.progress?.status === "running";
|
|
1067
|
+
const icon = isRunning
|
|
1068
|
+
? theme.fg("warning", "running")
|
|
1069
|
+
: r.detached
|
|
1070
|
+
? theme.fg("warning", "detached")
|
|
1071
|
+
: r.exitCode === 0
|
|
1072
|
+
? theme.fg("success", "ok")
|
|
1073
|
+
: theme.fg("error", "failed");
|
|
1074
|
+
const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1075
|
+
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
1076
|
+
|
|
1077
|
+
const progressInfo = isRunning && r.progress
|
|
1078
|
+
? ` | ${r.progress.toolCount} tools, ${formatTokens(r.progress.tokens)} tok, ${formatDuration(r.progress.durationMs)}`
|
|
1079
|
+
: r.progressSummary
|
|
1080
|
+
? ` | ${r.progressSummary.toolCount} tools, ${formatTokens(r.progressSummary.tokens)} tok, ${formatDuration(r.progressSummary.durationMs)}`
|
|
1081
|
+
: "";
|
|
1082
|
+
|
|
1083
|
+
const w = getTermWidth() - 4;
|
|
1084
|
+
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
1085
|
+
const toolCallLines = getToolCallLines(r, expanded);
|
|
1086
|
+
const c = new Container();
|
|
1087
|
+
c.addChild(new Text(fit(`${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${contextBadge}${progressInfo}`), 0, 0));
|
|
1088
|
+
c.addChild(new Spacer(1));
|
|
1089
|
+
const taskMaxLen = Math.max(20, w - 8);
|
|
1090
|
+
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
1091
|
+
? r.task
|
|
1092
|
+
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
1093
|
+
c.addChild(
|
|
1094
|
+
new Text(fit(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0),
|
|
1095
|
+
);
|
|
1096
|
+
c.addChild(new Spacer(1));
|
|
1097
|
+
|
|
1098
|
+
if (isRunning && r.progress) {
|
|
1099
|
+
const progressSnapshotNow = snapshotNowForProgress(r.progress);
|
|
1100
|
+
const toolLine = formatCurrentToolLine(r.progress, w, expanded, progressSnapshotNow);
|
|
1101
|
+
if (toolLine) {
|
|
1102
|
+
c.addChild(new Text(fit(theme.fg("warning", `> ${toolLine}`)), 0, 0));
|
|
1103
|
+
}
|
|
1104
|
+
const liveStatusLine = buildLiveStatusLine(r.progress, progressSnapshotNow);
|
|
1105
|
+
if (liveStatusLine) {
|
|
1106
|
+
c.addChild(new Text(fit(theme.fg("accent", liveStatusLine)), 0, 0));
|
|
1107
|
+
}
|
|
1108
|
+
c.addChild(new Text(fit(theme.fg("accent", "Press ctrl+o for live detail")), 0, 0));
|
|
1109
|
+
if (r.artifactPaths) {
|
|
1110
|
+
c.addChild(new Text(fit(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
1111
|
+
}
|
|
1112
|
+
if (r.progress.recentTools?.length) {
|
|
1113
|
+
for (const t of r.progress.recentTools.slice(-3)) {
|
|
1114
|
+
const maxArgsLen = Math.max(40, w - 24);
|
|
1115
|
+
const argsPreview = expanded || t.args.length <= maxArgsLen
|
|
1116
|
+
? t.args
|
|
1117
|
+
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
1118
|
+
c.addChild(new Text(fit(theme.fg("dim", `${t.tool}: ${argsPreview}`)), 0, 0));
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
for (const line of (r.progress.recentOutput ?? []).slice(-5)) {
|
|
1122
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
1123
|
+
}
|
|
1124
|
+
if (toolLine || liveStatusLine || r.progress.recentTools?.length || r.progress.recentOutput?.length || r.artifactPaths) {
|
|
1125
|
+
c.addChild(new Spacer(1));
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
if (expanded) {
|
|
1130
|
+
for (const line of toolCallLines) {
|
|
1131
|
+
c.addChild(new Text(fit(theme.fg("muted", line)), 0, 0));
|
|
1132
|
+
}
|
|
1133
|
+
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (output) c.addChild(new Markdown(output, 0, 0, mdTheme));
|
|
1137
|
+
c.addChild(new Spacer(1));
|
|
1138
|
+
if (r.skills?.length) {
|
|
1139
|
+
c.addChild(new Text(fit(theme.fg("dim", `Skills: ${r.skills.join(", ")}`)), 0, 0));
|
|
1140
|
+
}
|
|
1141
|
+
if (r.skillsWarning) {
|
|
1142
|
+
c.addChild(new Text(fit(theme.fg("warning", `Warning: ${r.skillsWarning}`)), 0, 0));
|
|
1143
|
+
}
|
|
1144
|
+
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
1145
|
+
c.addChild(new Text(fit(theme.fg("dim", `Fallbacks: ${r.attemptedModels.join(" → ")}`)), 0, 0));
|
|
1146
|
+
}
|
|
1147
|
+
c.addChild(new Text(fit(theme.fg("dim", formatUsage(r.usage, r.model))), 0, 0));
|
|
1148
|
+
if (r.sessionFile) {
|
|
1149
|
+
c.addChild(new Text(fit(theme.fg("dim", `Session: ${shortenPath(r.sessionFile)}`)), 0, 0));
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (!isRunning && r.artifactPaths) {
|
|
1153
|
+
c.addChild(new Spacer(1));
|
|
1154
|
+
c.addChild(new Text(fit(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
1155
|
+
}
|
|
1156
|
+
return c;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
if (!expanded) return renderMultiCompact(d, theme);
|
|
1160
|
+
|
|
1161
|
+
const hasRunning = d.progress?.some((p) => p.status === "running")
|
|
1162
|
+
|| d.results.some((r) => r.progress?.status === "running");
|
|
1163
|
+
const ok = d.results.filter((r) => r.progress?.status === "completed" || (r.exitCode === 0 && r.progress?.status !== "running")).length;
|
|
1164
|
+
const hasEmptyWithoutTarget = d.results.some((r) =>
|
|
1165
|
+
r.exitCode === 0
|
|
1166
|
+
&& r.progress?.status !== "running"
|
|
1167
|
+
&& hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r)),
|
|
1168
|
+
);
|
|
1169
|
+
const icon = hasRunning
|
|
1170
|
+
? theme.fg("warning", "running")
|
|
1171
|
+
: hasEmptyWithoutTarget
|
|
1172
|
+
? theme.fg("warning", "warning")
|
|
1173
|
+
: ok === d.results.length
|
|
1174
|
+
? theme.fg("success", "ok")
|
|
1175
|
+
: theme.fg("error", "failed");
|
|
1176
|
+
|
|
1177
|
+
const totalSummary =
|
|
1178
|
+
d.progressSummary ||
|
|
1179
|
+
d.results.reduce(
|
|
1180
|
+
(acc, r) => {
|
|
1181
|
+
const prog = r.progress || r.progressSummary;
|
|
1182
|
+
if (prog) {
|
|
1183
|
+
acc.toolCount += prog.toolCount;
|
|
1184
|
+
acc.tokens += prog.tokens;
|
|
1185
|
+
acc.durationMs =
|
|
1186
|
+
d.mode === "chain"
|
|
1187
|
+
? acc.durationMs + prog.durationMs
|
|
1188
|
+
: Math.max(acc.durationMs, prog.durationMs);
|
|
1189
|
+
}
|
|
1190
|
+
return acc;
|
|
1191
|
+
},
|
|
1192
|
+
{ toolCount: 0, tokens: 0, durationMs: 0 },
|
|
1193
|
+
);
|
|
1194
|
+
|
|
1195
|
+
const summaryStr =
|
|
1196
|
+
totalSummary.toolCount || totalSummary.tokens
|
|
1197
|
+
? ` | ${totalSummary.toolCount} tools, ${formatTokens(totalSummary.tokens)} tok, ${formatDuration(totalSummary.durationMs)}`
|
|
1198
|
+
: "";
|
|
1199
|
+
|
|
1200
|
+
const modeLabel = d.mode;
|
|
1201
|
+
const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1202
|
+
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
1203
|
+
const itemTitle = multiLabel.itemTitle;
|
|
1204
|
+
|
|
1205
|
+
const chainVis = d.chainAgents?.length && !multiLabel.hasParallelInChain
|
|
1206
|
+
? d.chainAgents
|
|
1207
|
+
.map((agent, i) => {
|
|
1208
|
+
const result = d.results[i];
|
|
1209
|
+
const isFailed = result && result.exitCode !== 0 && result.progress?.status !== "running";
|
|
1210
|
+
const isComplete = result && result.exitCode === 0 && result.progress?.status !== "running";
|
|
1211
|
+
const isEmptyWithoutTarget = Boolean(result)
|
|
1212
|
+
&& Boolean(isComplete)
|
|
1213
|
+
&& hasEmptyTextOutputWithoutOutputTarget(result.task, getSingleResultOutput(result));
|
|
1214
|
+
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
1215
|
+
const stepIcon = isFailed
|
|
1216
|
+
? theme.fg("error", "failed")
|
|
1217
|
+
: isEmptyWithoutTarget
|
|
1218
|
+
? theme.fg("warning", "warning")
|
|
1219
|
+
: isComplete
|
|
1220
|
+
? theme.fg("success", "done")
|
|
1221
|
+
: isCurrent && hasRunning
|
|
1222
|
+
? theme.fg("warning", "running")
|
|
1223
|
+
: theme.fg("dim", "pending");
|
|
1224
|
+
return `${stepIcon} ${agent}`;
|
|
1225
|
+
})
|
|
1226
|
+
.join(theme.fg("dim", " → "))
|
|
1227
|
+
: null;
|
|
1228
|
+
|
|
1229
|
+
const w = getTermWidth() - 4;
|
|
1230
|
+
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
1231
|
+
const c = new Container();
|
|
1232
|
+
c.addChild(
|
|
1233
|
+
new Text(
|
|
1234
|
+
fit(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} · ${multiLabel.headerLabel}${summaryStr}`),
|
|
1235
|
+
0,
|
|
1236
|
+
0,
|
|
1237
|
+
),
|
|
1238
|
+
);
|
|
1239
|
+
if (chainVis) {
|
|
1240
|
+
c.addChild(new Text(fit(` ${chainVis}`), 0, 0));
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
1244
|
+
const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
|
|
1245
|
+
const displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : (useResultsDirectly ? d.results.length : d.chainAgents!.length);
|
|
1246
|
+
|
|
1247
|
+
c.addChild(new Spacer(1));
|
|
1248
|
+
|
|
1249
|
+
for (let i = displayStart; i < displayEnd; i++) {
|
|
1250
|
+
const r = d.results[i];
|
|
1251
|
+
const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
|
|
1252
|
+
const agentName = useResultsDirectly
|
|
1253
|
+
? (r?.agent || `step-${rowNumber}`)
|
|
1254
|
+
: (d.chainAgents![i] || r?.agent || `step-${rowNumber}`);
|
|
1255
|
+
|
|
1256
|
+
if (!r) {
|
|
1257
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${itemTitle} ${rowNumber}: ${agentName}`)), 0, 0));
|
|
1258
|
+
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
1259
|
+
c.addChild(new Spacer(1));
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const progressFromArray = d.progress?.find((p) => p.index === i)
|
|
1264
|
+
|| d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1265
|
+
const rProg = (r.progress || progressFromArray || r.progressSummary) as AgentProgress | undefined;
|
|
1266
|
+
const rRunning = rProg?.status === "running";
|
|
1267
|
+
const stepNumber = typeof rProg?.index === "number" ? rProg.index + 1 : i + 1;
|
|
1268
|
+
|
|
1269
|
+
const resultOutput = getSingleResultOutput(r);
|
|
1270
|
+
const statusIcon = rRunning
|
|
1271
|
+
? theme.fg("warning", "running")
|
|
1272
|
+
: r.exitCode !== 0
|
|
1273
|
+
? theme.fg("error", "failed")
|
|
1274
|
+
: hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput)
|
|
1275
|
+
? theme.fg("warning", "warning")
|
|
1276
|
+
: theme.fg("success", "done");
|
|
1277
|
+
const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
|
|
1278
|
+
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1279
|
+
const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
|
|
1280
|
+
const stepHeader = rRunning
|
|
1281
|
+
? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats}`
|
|
1282
|
+
: `${statusIcon} ${stepLabel}: ${theme.bold(r.agent)}${modelDisplay}${stats}`;
|
|
1283
|
+
const toolCallLines = getToolCallLines(r, expanded);
|
|
1284
|
+
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
1285
|
+
|
|
1286
|
+
const taskMaxLen = Math.max(20, w - 12);
|
|
1287
|
+
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
1288
|
+
? r.task
|
|
1289
|
+
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
1290
|
+
c.addChild(new Text(fit(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0));
|
|
1291
|
+
|
|
1292
|
+
const outputTarget = extractOutputTarget(r.task);
|
|
1293
|
+
if (outputTarget) {
|
|
1294
|
+
c.addChild(new Text(fit(theme.fg("dim", ` output: ${outputTarget}`)), 0, 0));
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
if (r.skills?.length) {
|
|
1298
|
+
c.addChild(new Text(fit(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0));
|
|
1299
|
+
}
|
|
1300
|
+
if (r.skillsWarning) {
|
|
1301
|
+
c.addChild(new Text(fit(theme.fg("warning", ` Warning: ${r.skillsWarning}`)), 0, 0));
|
|
1302
|
+
}
|
|
1303
|
+
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
1304
|
+
c.addChild(new Text(fit(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" → ")}`)), 0, 0));
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
if (rRunning && rProg) {
|
|
1308
|
+
if (rProg.skills?.length) {
|
|
1309
|
+
c.addChild(new Text(fit(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`)), 0, 0));
|
|
1310
|
+
}
|
|
1311
|
+
const progressSnapshotNow = snapshotNowForProgress(rProg);
|
|
1312
|
+
const toolLine = formatCurrentToolLine(rProg, w, expanded, progressSnapshotNow);
|
|
1313
|
+
if (toolLine) {
|
|
1314
|
+
c.addChild(new Text(fit(theme.fg("warning", ` > ${toolLine}`)), 0, 0));
|
|
1315
|
+
}
|
|
1316
|
+
const liveStatusLine = buildLiveStatusLine(rProg, progressSnapshotNow);
|
|
1317
|
+
if (liveStatusLine) {
|
|
1318
|
+
c.addChild(new Text(fit(theme.fg("accent", ` ${liveStatusLine}`)), 0, 0));
|
|
1319
|
+
}
|
|
1320
|
+
c.addChild(new Text(fit(theme.fg("accent", " Press ctrl+o for live detail")), 0, 0));
|
|
1321
|
+
if (r.artifactPaths) {
|
|
1322
|
+
c.addChild(new Text(fit(theme.fg("dim", ` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
1323
|
+
}
|
|
1324
|
+
if (rProg.recentTools?.length) {
|
|
1325
|
+
for (const t of rProg.recentTools.slice(-3)) {
|
|
1326
|
+
const maxArgsLen = Math.max(40, w - 30);
|
|
1327
|
+
const argsPreview = expanded || t.args.length <= maxArgsLen
|
|
1328
|
+
? t.args
|
|
1329
|
+
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
1330
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${t.tool}: ${argsPreview}`)), 0, 0));
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
const recentLines = (rProg.recentOutput ?? []).slice(-5);
|
|
1334
|
+
for (const line of recentLines) {
|
|
1335
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
if (!rRunning && r.artifactPaths) {
|
|
1340
|
+
c.addChild(new Text(fit(theme.fg("dim", ` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
if (expanded && !rRunning) {
|
|
1344
|
+
for (const line of toolCallLines) {
|
|
1345
|
+
c.addChild(new Text(fit(theme.fg("muted", ` ${line}`)), 0, 0));
|
|
1346
|
+
}
|
|
1347
|
+
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
c.addChild(new Spacer(1));
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
if (d.artifacts) {
|
|
1354
|
+
c.addChild(new Spacer(1));
|
|
1355
|
+
c.addChild(new Text(fit(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`)), 0, 0));
|
|
1356
|
+
}
|
|
1357
|
+
return c;
|
|
2224
1358
|
}
|