@selesai/code 0.8.12 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/core/agent-session.js +41 -29
- package/dist/core/resource-loader.d.ts +1 -1
- package/dist/core/resource-loader.js +2 -2
- package/dist/defaults/models.json +15 -3
- package/dist/defaults/settings.json +10 -8
- package/dist/extensions/model-prompt-injector/config.json +6 -0
- package/dist/extensions/package.json +2 -1
- package/dist/extensions/pi-subagents/README.md +4 -4
- package/dist/extensions/pi-subagents/docs/configuration.md +3 -54
- package/dist/extensions/pi-subagents/docs/extension-api.md +4 -15
- package/dist/extensions/pi-subagents/docs/missions.md +2 -39
- package/dist/extensions/pi-subagents/docs/observability.md +2 -42
- package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -2
- package/dist/extensions/pi-subagents/docs/watchdog.md +0 -57
- package/dist/extensions/pi-subagents/docs/workflows.md +2 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +4 -49
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +1 -2
- package/dist/extensions/pi-subagents/src/extension/config.ts +3 -15
- package/dist/extensions/pi-subagents/src/extension/index.ts +1 -53
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +4 -10
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +1 -2
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +1 -12
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -2
- package/dist/extensions/pi-subagents/src/policy/authority.ts +0 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +33 -102
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -2
- package/dist/extensions/pi-subagents/src/shared/types.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +4 -58
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +2 -8
- package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +1 -2
- package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +16 -3
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -95
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +0 -15
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +0 -59
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +0 -59
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +0 -47
- package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +0 -8
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +21 -1
- package/dist/modes/interactive/components/model-selector.d.ts +10 -1
- package/dist/modes/interactive/components/model-selector.js +10 -6
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.js +3 -0
- package/dist/modes/interactive/interactive-mode.js +7 -0
- package/dist/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/skills/pi-subagents/references/execution-controls.md +2 -32
- package/dist/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/dist/skills/pi-subagents/references/prompting-and-roles.md +3 -3
- package/docs/custom-provider.md +1 -0
- package/package.json +3 -3
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +0 -803
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +0 -691
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +0 -1314
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +0 -2
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +0 -17
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +0 -57
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +0 -16
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +0 -18
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +0 -9
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +0 -20
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +0 -96
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +0 -119
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +0 -85
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +0 -620
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +0 -5
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +0 -56
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +0 -32
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +0 -49
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +0 -93
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +0 -128
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +0 -7
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +0 -38
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +0 -9
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +0 -29
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +0 -2
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +0 -22
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +0 -37
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +0 -34
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +0 -12
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +0 -23
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +0 -171
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +0 -1263
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +0 -20
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +0 -54
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +0 -22
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +0 -13
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +0 -358
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +0 -77
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +0 -512
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +0 -43
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +0 -121
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +0 -82
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +0 -17
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +0 -7
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +0 -210
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +0 -54
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +0 -4
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +0 -55
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +0 -2
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +0 -5
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +0 -30
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +0 -206
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +0 -106
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +0 -19
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +0 -356
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +0 -297
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +0 -60
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +0 -18
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +0 -28
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +0 -68
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +0 -106
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +0 -51
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +0 -6
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +0 -35
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +0 -37
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +0 -40
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +0 -37
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +0 -52
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +0 -35
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +0 -24
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +0 -14
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +0 -70
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +0 -26
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +0 -36
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +0 -19
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +0 -61
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +0 -23
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +0 -42
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +0 -35
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +0 -12
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +0 -33
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +0 -9
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +0 -34
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +0 -22
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +0 -34
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +0 -16
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +0 -16
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +0 -102
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +0 -14
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +0 -21
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +0 -19
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +0 -20
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +0 -23
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +0 -12
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +0 -22
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +0 -23
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +0 -26
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +0 -25
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +0 -32
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +0 -34
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +0 -13
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +0 -52
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +0 -54
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +0 -33
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -42
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +0 -23
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +0 -95
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +0 -77
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -17
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +0 -28
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +0 -11
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +0 -11
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +0 -63
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +0 -94
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +0 -23
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +0 -30
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +0 -14
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +0 -24
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +0 -394
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +0 -12
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +0 -29
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +0 -120
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +0 -28
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +0 -17
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +0 -246
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +0 -20
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +0 -457
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +0 -8
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +0 -2
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +0 -22
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +0 -18
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +0 -11
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +0 -11
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +0 -4
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +0 -4
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +0 -5
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +0 -8
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +0 -5
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +0 -26
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +0 -7
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +0 -10
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +0 -18
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +0 -30
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +0 -2
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +0 -16
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +0 -9
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +0 -31
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +0 -21
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +0 -31
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +0 -5
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +0 -12
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +0 -15
- package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +0 -3
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +0 -1004
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +0 -1511
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +0 -634
- package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +0 -67
|
@@ -1,1314 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
4
|
-
import { getMarkdownTheme, type ExtensionContext } from "@selesai/code";
|
|
5
|
-
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
6
|
-
import { snapshotExternalRuns, type ExternalRun } from "../api/external-runs.ts";
|
|
7
|
-
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
|
|
8
|
-
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../shared/formatters.ts";
|
|
9
|
-
import { DIRS, type AsyncJobState, type Details, type FleetKeybindingAction, type FleetKeybindingsConfig, type ForegroundChildControl, type ForegroundResumeChild, type ForegroundResumeRun, type ForegroundRunControl, type SubagentState } from "../shared/types.ts";
|
|
10
|
-
import { decodeUtf8Tail } from "../shared/utf8.ts";
|
|
11
|
-
import { readStatus } from "../shared/utils.ts";
|
|
12
|
-
import { formatAsyncRunTranscript } from "../runs/background/fleet-view.ts";
|
|
13
|
-
import { listAsyncRuns, type AsyncRunSummary } from "../runs/background/async-status.ts";
|
|
14
|
-
import { steerAsyncRun } from "../runs/foreground/async-steering-action.ts";
|
|
15
|
-
import type { SteerDeliveryMode } from "../runs/background/control-channel.ts";
|
|
16
|
-
import { stopAsyncRun } from "../runs/foreground/async-stop-action.ts";
|
|
17
|
-
import { contextModeBadge, contextModeLabel } from "../runs/shared/context-mode.ts";
|
|
18
|
-
import { FLEET_STATUS_WIDGET_KEY } from "./fleet-status.ts";
|
|
19
|
-
import { readFleetTranscript, renderFleetTranscript, type FleetTranscript } from "./fleet-transcript.ts";
|
|
20
|
-
import { handleHerdrInspectorAction } from "../inspectors/herdr/actions.ts";
|
|
21
|
-
import { getLivePromptAudit, type LivePromptAudit, type PromptAuditView } from "../runs/foreground/prompt-audit.ts";
|
|
22
|
-
|
|
23
|
-
const REFRESH_MS = 750;
|
|
24
|
-
const MIN_REFRESH_MS = 250;
|
|
25
|
-
const MAX_RECENT_ASYNC_RUNS = 20;
|
|
26
|
-
const MAX_FLEET_HISTORY_CANDIDATES = 100;
|
|
27
|
-
const TRANSCRIPT_LINES = 200;
|
|
28
|
-
const OUTPUT_TAIL_BYTES = 64 * 1024;
|
|
29
|
-
const PROMPT_AUDIT_SUMMARY_WIDTH = 160;
|
|
30
|
-
|
|
31
|
-
export const DEFAULT_FLEET_KEYBINDINGS: Record<FleetKeybindingAction, string[]> = {
|
|
32
|
-
close: ["escape", "ctrl+c", "q"],
|
|
33
|
-
scrollUp: ["K"],
|
|
34
|
-
scrollDown: ["J"],
|
|
35
|
-
selectUp: ["up", "k"],
|
|
36
|
-
selectDown: ["down", "j"],
|
|
37
|
-
selectFirst: ["home"],
|
|
38
|
-
selectLast: ["end"],
|
|
39
|
-
pageUp: ["pageUp"],
|
|
40
|
-
pageDown: ["pageDown"],
|
|
41
|
-
refresh: ["r", "R"],
|
|
42
|
-
steer: ["s"],
|
|
43
|
-
inspect: ["H"],
|
|
44
|
-
stop: ["D"],
|
|
45
|
-
toggleTools: ["x", "X", "ctrl+o"],
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
type ResolvedFleetKeybindings = Record<FleetKeybindingAction, string[]>;
|
|
49
|
-
|
|
50
|
-
export function resolveFleetKeybindings(config: FleetKeybindingsConfig | undefined): ResolvedFleetKeybindings {
|
|
51
|
-
return Object.fromEntries(
|
|
52
|
-
Object.entries(DEFAULT_FLEET_KEYBINDINGS).map(([action, defaults]) => [action, config?.[action as FleetKeybindingAction] ?? defaults]),
|
|
53
|
-
) as ResolvedFleetKeybindings;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function matchesFleetBinding(data: string, binding: string): boolean {
|
|
57
|
-
const key = /^[A-Z]$/.test(binding) ? `shift+${binding.toLowerCase()}` : binding;
|
|
58
|
-
return matchesKey(data, key as Parameters<typeof matchesKey>[1]);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function matchesFleetAction(data: string, bindings: ResolvedFleetKeybindings, action: FleetKeybindingAction): boolean {
|
|
62
|
-
return bindings[action].some((binding) => matchesFleetBinding(data, binding));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function bindingLabel(bindings: ResolvedFleetKeybindings, action: FleetKeybindingAction): string {
|
|
66
|
-
return bindings[action]
|
|
67
|
-
.map((binding) => binding === "up" ? "↑" : binding === "down" ? "↓" : binding === "escape" ? "Esc" : binding === "return" ? "Enter" : binding)
|
|
68
|
-
.join("/");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
type Theme = ExtensionContext["ui"]["theme"];
|
|
72
|
-
type FleetTui = {
|
|
73
|
-
terminal?: { rows: number };
|
|
74
|
-
requestRender(): void;
|
|
75
|
-
};
|
|
76
|
-
type AsyncStep = AsyncRunSummary["steps"][number];
|
|
77
|
-
|
|
78
|
-
export type FleetItem = (
|
|
79
|
-
| { key: string; kind: "foreground-active"; runId: string; index?: number; agent: string; state: "running"; updatedAt: number; control: ForegroundRunControl; activeChild?: ForegroundChildControl }
|
|
80
|
-
| { key: string; kind: "foreground-recent"; runId: string; index: number; agent: string; state: ForegroundResumeChild["status"]; updatedAt: number; run: ForegroundResumeRun; child: ForegroundResumeChild }
|
|
81
|
-
| { key: string; kind: "async"; runId: string; index?: number; agent: string; state: string; updatedAt: number; run: AsyncRunSummary; step?: AsyncStep }
|
|
82
|
-
| { key: string; kind: "external"; runId: string; agent: string; state: ExternalRun["state"]; updatedAt: number; run: ExternalRun }
|
|
83
|
-
) & { description?: string };
|
|
84
|
-
|
|
85
|
-
export interface FleetSnapshot {
|
|
86
|
-
items: FleetItem[];
|
|
87
|
-
error?: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface FleetActionResult {
|
|
91
|
-
text: string;
|
|
92
|
-
isError?: boolean;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface FleetActionHandlers {
|
|
96
|
-
steer(input: { runId: string; asyncDir: string; index?: number; message: string; mode: SteerDeliveryMode }): Promise<FleetActionResult>;
|
|
97
|
-
stop(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult> | FleetActionResult;
|
|
98
|
-
inspect?(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult>;
|
|
99
|
-
redoPrompt?(input: { runId: string; index: number; guidance: string; control?: ForegroundRunControl }): Promise<FleetActionResult>;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface FleetViewOptions {
|
|
103
|
-
asyncDirRoot?: string;
|
|
104
|
-
resultsDir?: string;
|
|
105
|
-
refreshMs?: number;
|
|
106
|
-
initialKey?: string;
|
|
107
|
-
markdownTheme?: MarkdownTheme;
|
|
108
|
-
fleetKeybindings?: FleetKeybindingsConfig;
|
|
109
|
-
actions?: FleetActionHandlers;
|
|
110
|
-
copyText?: (text: string) => Promise<void> | void;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {
|
|
114
|
-
return !currentSessionId || sessionId === currentSessionId;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function trackedJobSummary(job: AsyncJobState): AsyncRunSummary {
|
|
118
|
-
const startedAt = job.startedAt ?? job.updatedAt ?? Date.now();
|
|
119
|
-
return {
|
|
120
|
-
id: job.asyncId,
|
|
121
|
-
asyncDir: job.asyncDir,
|
|
122
|
-
...(job.sessionId ? { sessionId: job.sessionId } : {}),
|
|
123
|
-
state: job.status,
|
|
124
|
-
activityState: job.activityState,
|
|
125
|
-
lastActivityAt: job.lastActivityAt,
|
|
126
|
-
currentTool: job.currentTool,
|
|
127
|
-
currentToolStartedAt: job.currentToolStartedAt,
|
|
128
|
-
currentPath: job.currentPath,
|
|
129
|
-
turnCount: job.turnCount,
|
|
130
|
-
toolCount: job.toolCount,
|
|
131
|
-
steering: job.steering,
|
|
132
|
-
mode: job.mode ?? "single",
|
|
133
|
-
...(job.context ? { context: job.context } : {}),
|
|
134
|
-
...(job.cwd ? { cwd: job.cwd } : {}),
|
|
135
|
-
startedAt,
|
|
136
|
-
...(job.updatedAt !== undefined ? { lastUpdate: job.updatedAt } : {}),
|
|
137
|
-
...(job.timeoutMs !== undefined ? { timeoutMs: job.timeoutMs } : {}),
|
|
138
|
-
...(job.deadlineAt !== undefined ? { deadlineAt: job.deadlineAt } : {}),
|
|
139
|
-
...(job.timedOut !== undefined ? { timedOut: job.timedOut } : {}),
|
|
140
|
-
...(job.stopped !== undefined ? { stopped: job.stopped } : {}),
|
|
141
|
-
...(job.turnBudget ? { turnBudget: job.turnBudget } : {}),
|
|
142
|
-
...(job.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: job.turnBudgetExceeded } : {}),
|
|
143
|
-
...(job.wrapUpRequested !== undefined ? { wrapUpRequested: job.wrapUpRequested } : {}),
|
|
144
|
-
...(job.currentStep !== undefined ? { currentStep: job.currentStep } : {}),
|
|
145
|
-
...(job.chainStepCount !== undefined ? { chainStepCount: job.chainStepCount } : {}),
|
|
146
|
-
...(job.parallelGroups?.length ? { parallelGroups: job.parallelGroups } : {}),
|
|
147
|
-
steps: (job.steps ?? job.agents?.map((agent, index) => ({ agent, index, status: job.status === "queued" ? "pending" as const : job.status })) ?? []).map((step, index) => ({
|
|
148
|
-
...step,
|
|
149
|
-
index: step.index ?? index,
|
|
150
|
-
})),
|
|
151
|
-
...(job.sessionDir ? { sessionDir: job.sessionDir } : {}),
|
|
152
|
-
...(job.outputFile ? { outputFile: job.outputFile } : {}),
|
|
153
|
-
...(job.totalTokens ? { totalTokens: job.totalTokens } : {}),
|
|
154
|
-
...(job.sessionFile ? { sessionFile: job.sessionFile } : {}),
|
|
155
|
-
...(job.nestedChildren?.length ? { nestedChildren: job.nestedChildren } : {}),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function asyncItems(run: AsyncRunSummary, description?: string): FleetItem[] {
|
|
160
|
-
const updatedAt = run.lastUpdate ?? run.endedAt ?? run.startedAt;
|
|
161
|
-
if (run.steps.length === 0 || run.mode === "workflow") {
|
|
162
|
-
return [{ key: `async:${run.id}`, kind: "async", runId: run.id, agent: run.mode, state: run.state, updatedAt, run, ...(description ? { description } : {}) }];
|
|
163
|
-
}
|
|
164
|
-
return run.steps.map((step) => ({
|
|
165
|
-
key: `async:${run.id}:${step.index}`,
|
|
166
|
-
kind: "async" as const,
|
|
167
|
-
runId: run.id,
|
|
168
|
-
index: step.index,
|
|
169
|
-
agent: step.label ? `${step.label} (${step.agent})` : step.agent,
|
|
170
|
-
state: step.status,
|
|
171
|
-
updatedAt: step.lastActivityAt ?? updatedAt,
|
|
172
|
-
run,
|
|
173
|
-
step,
|
|
174
|
-
...(description ? { description } : {}),
|
|
175
|
-
}));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function orderFleetAsyncRuns(runs: AsyncRunSummary[], terminalLimit: number): AsyncRunSummary[] {
|
|
179
|
-
const updatedAt = (run: AsyncRunSummary) => run.lastUpdate ?? run.endedAt ?? run.startedAt;
|
|
180
|
-
const byNewest = (left: AsyncRunSummary, right: AsyncRunSummary) => updatedAt(right) - updatedAt(left);
|
|
181
|
-
const active = runs.filter((run) => run.state === "queued" || run.state === "running").sort(byNewest);
|
|
182
|
-
const terminal = runs.filter((run) => run.state !== "queued" && run.state !== "running").sort(byNewest);
|
|
183
|
-
return [...active, ...terminal.slice(0, terminalLimit)];
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function collectFleetSnapshot(
|
|
187
|
-
state: SubagentState,
|
|
188
|
-
options: { asyncDirRoot?: string; resultsDir?: string; limit?: number } = {},
|
|
189
|
-
): FleetSnapshot {
|
|
190
|
-
const items: FleetItem[] = [];
|
|
191
|
-
const activeForegroundIds = new Set<string>();
|
|
192
|
-
const trackedJobs = state.fleetJobs ?? state.asyncJobs;
|
|
193
|
-
const workflowParentIds = new Set([...trackedJobs.values()]
|
|
194
|
-
.filter((job) => job.mode === "workflow" && belongsToCurrentSession(job.sessionId, state.currentSessionId))
|
|
195
|
-
.map((job) => job.asyncId));
|
|
196
|
-
for (const control of [...state.foregroundControls.values()].sort((left, right) => right.updatedAt - left.updatedAt)) {
|
|
197
|
-
activeForegroundIds.add(control.runId);
|
|
198
|
-
if (control.parentWorkflowRunId && workflowParentIds.has(control.parentWorkflowRunId)) continue;
|
|
199
|
-
if (control.activeChildren) {
|
|
200
|
-
for (const child of [...control.activeChildren.values()].sort((left, right) => left.index - right.index)) {
|
|
201
|
-
items.push({
|
|
202
|
-
key: `foreground-active:${control.runId}:${child.index}`,
|
|
203
|
-
kind: "foreground-active",
|
|
204
|
-
runId: control.runId,
|
|
205
|
-
index: child.index,
|
|
206
|
-
agent: child.agent,
|
|
207
|
-
state: "running",
|
|
208
|
-
updatedAt: child.updatedAt,
|
|
209
|
-
control,
|
|
210
|
-
activeChild: child,
|
|
211
|
-
...(child.description ? { description: child.description } : {}),
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
items.push({
|
|
217
|
-
key: `foreground-active:${control.runId}:${control.currentIndex ?? 0}`,
|
|
218
|
-
kind: "foreground-active",
|
|
219
|
-
runId: control.runId,
|
|
220
|
-
...(control.currentIndex !== undefined ? { index: control.currentIndex } : {}),
|
|
221
|
-
agent: control.currentAgent ?? control.mode,
|
|
222
|
-
state: "running",
|
|
223
|
-
updatedAt: control.updatedAt,
|
|
224
|
-
control,
|
|
225
|
-
...(control.description ? { description: control.description } : {}),
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
let error: string | undefined;
|
|
230
|
-
try {
|
|
231
|
-
let runs: AsyncRunSummary[];
|
|
232
|
-
const descriptions = new Map<string, string>();
|
|
233
|
-
const tracked = [...trackedJobs.values()]
|
|
234
|
-
.filter((job) => belongsToCurrentSession(job.sessionId, state.currentSessionId));
|
|
235
|
-
const byUpdate = (left: AsyncJobState, right: AsyncJobState) => (right.updatedAt ?? right.startedAt ?? 0) - (left.updatedAt ?? left.startedAt ?? 0);
|
|
236
|
-
const active = tracked.filter((job) => job.status === "queued" || job.status === "running").sort(byUpdate);
|
|
237
|
-
const recent = tracked.filter((job) => job.status !== "queued" && job.status !== "running").sort(byUpdate).slice(0, options.limit ?? MAX_RECENT_ASYNC_RUNS);
|
|
238
|
-
const trackedRuns: AsyncRunSummary[] = [];
|
|
239
|
-
for (const job of [...active, ...recent]) {
|
|
240
|
-
try {
|
|
241
|
-
trackedRuns.push(trackedJobSummary(job));
|
|
242
|
-
if (job.description) descriptions.set(job.asyncId, job.description);
|
|
243
|
-
} catch (cause) {
|
|
244
|
-
error = `Failed to inspect async run '${job.asyncId}': ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
if (options.asyncDirRoot !== undefined) {
|
|
248
|
-
const trackedIds = new Set(trackedRuns.map((run) => run.id));
|
|
249
|
-
const history = listAsyncRuns(options.asyncDirRoot, {
|
|
250
|
-
...(state.sessionLineage?.length ? { sessionIds: state.sessionLineage } : state.currentSessionId ? { sessionId: state.currentSessionId } : {}),
|
|
251
|
-
entryLimit: MAX_FLEET_HISTORY_CANDIDATES,
|
|
252
|
-
resultsDir: options.resultsDir ?? DIRS.results,
|
|
253
|
-
reconcile: false,
|
|
254
|
-
}).filter((run) => !trackedIds.has(run.id));
|
|
255
|
-
runs = [...trackedRuns, ...history];
|
|
256
|
-
} else {
|
|
257
|
-
runs = trackedRuns;
|
|
258
|
-
}
|
|
259
|
-
for (const run of orderFleetAsyncRuns(runs, options.limit ?? MAX_RECENT_ASYNC_RUNS)) {
|
|
260
|
-
items.push(...asyncItems(run, descriptions.get(run.id)));
|
|
261
|
-
}
|
|
262
|
-
} catch (cause) {
|
|
263
|
-
error = cause instanceof Error ? cause.message : String(cause);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (state.currentSessionId) {
|
|
267
|
-
try {
|
|
268
|
-
for (const run of snapshotExternalRuns(state.currentSessionId, { ignoreMalformed: true, onMalformedRecord: (message) => console.warn(`[pi-subagents] Removed ${message}`) })) {
|
|
269
|
-
items.push({
|
|
270
|
-
key: `external:${run.id}`,
|
|
271
|
-
kind: "external",
|
|
272
|
-
runId: run.id,
|
|
273
|
-
agent: run.label,
|
|
274
|
-
state: run.state,
|
|
275
|
-
updatedAt: run.updatedAt ?? run.endedAt ?? run.startedAt,
|
|
276
|
-
run,
|
|
277
|
-
...(run.currentAction ? { description: run.currentAction } : {}),
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
} catch (cause) {
|
|
281
|
-
const message = `Failed to inspect external jobs: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
282
|
-
error = error ? `${error}; ${message}` : message;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const recentForeground = [...(state.foregroundRuns?.values() ?? [])]
|
|
287
|
-
.filter((run) => belongsToCurrentSession(run.sessionId, state.currentSessionId) && !activeForegroundIds.has(run.runId))
|
|
288
|
-
.sort((left, right) => right.updatedAt - left.updatedAt);
|
|
289
|
-
for (const run of recentForeground) {
|
|
290
|
-
for (const child of run.children) {
|
|
291
|
-
items.push({
|
|
292
|
-
key: `foreground-recent:${run.runId}:${child.index}`,
|
|
293
|
-
kind: "foreground-recent",
|
|
294
|
-
runId: run.runId,
|
|
295
|
-
index: child.index,
|
|
296
|
-
agent: child.agent,
|
|
297
|
-
state: child.status,
|
|
298
|
-
updatedAt: child.updatedAt ?? run.updatedAt,
|
|
299
|
-
run,
|
|
300
|
-
child,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return { items, ...(error ? { error } : {}) };
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function visibleWorkflowParentKeyForForegroundKey(state: SubagentState, key: string, items: FleetItem[]): string | undefined {
|
|
308
|
-
for (const control of state.foregroundControls.values()) {
|
|
309
|
-
if (!control.parentWorkflowRunId) continue;
|
|
310
|
-
let matches = false;
|
|
311
|
-
if (control.activeChildren) {
|
|
312
|
-
for (const child of control.activeChildren.values()) {
|
|
313
|
-
if (`foreground-active:${control.runId}:${child.index}` === key) {
|
|
314
|
-
matches = true;
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
} else {
|
|
319
|
-
matches = `foreground-active:${control.runId}:${control.currentIndex ?? 0}` === key;
|
|
320
|
-
}
|
|
321
|
-
if (!matches) continue;
|
|
322
|
-
const parentKey = `async:${control.parentWorkflowRunId}`;
|
|
323
|
-
return items.some((item) => item.key === parentKey) ? parentKey : undefined;
|
|
324
|
-
}
|
|
325
|
-
return undefined;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function statusGlyph(item: FleetItem, theme: Theme): string {
|
|
329
|
-
if (item.state === "running") return theme.fg("accent", "●");
|
|
330
|
-
if (item.state === "queued" || item.state === "pending") return theme.fg("muted", "◦");
|
|
331
|
-
if (item.state === "complete" || item.state === "completed") return theme.fg("success", "✓");
|
|
332
|
-
if (item.state === "paused" || item.state === "stopped" || item.state === "detached") return theme.fg("warning", "■");
|
|
333
|
-
return theme.fg("error", "✗");
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function foregroundPromptAuditCount(item: Extract<FleetItem, { kind: "foreground-active" }>, state: SubagentState): number {
|
|
337
|
-
if (!state.currentSessionId || item.control.sessionId !== state.currentSessionId) return 0;
|
|
338
|
-
if (!item.control.activeChildren) return getLivePromptAudit(item.control, item.index ?? 0) ? 1 : 0;
|
|
339
|
-
return [...item.control.activeChildren.keys()].filter((index) => getLivePromptAudit(item.control, index)).length;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function authoredPromptSummary(text: string): string | undefined {
|
|
343
|
-
const summary = text.replace(/\s+/g, " ").trim();
|
|
344
|
-
return summary ? truncateToWidth(summary, PROMPT_AUDIT_SUMMARY_WIDTH, "…") : undefined;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function foregroundAuthoredPromptSummary(item: Extract<FleetItem, { kind: "foreground-active" }>, state: SubagentState): string | undefined {
|
|
348
|
-
if (!state.currentSessionId || item.control.sessionId !== state.currentSessionId) return undefined;
|
|
349
|
-
const prompt = getLivePromptAudit(item.control, item.index ?? 0);
|
|
350
|
-
return prompt ? authoredPromptSummary(prompt.authoredTask) : undefined;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function promptAuditText(prompt: LivePromptAudit, view: PromptAuditView): string {
|
|
354
|
-
switch (view) {
|
|
355
|
-
case "authored": return prompt.authoredTask;
|
|
356
|
-
case "runtime": return prompt.runtimeAdditions;
|
|
357
|
-
case "effective": return prompt.finalEffectivePrompt;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function promptAuditViewLabel(view: PromptAuditView): string {
|
|
362
|
-
switch (view) {
|
|
363
|
-
case "authored": return "[1] Authored task";
|
|
364
|
-
case "runtime": return "[2] Runtime additions";
|
|
365
|
-
case "effective": return "[3] Final effective prompt";
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-active" }>, state: SubagentState): string[] {
|
|
370
|
-
const { control } = item;
|
|
371
|
-
const live = item.activeChild ?? control;
|
|
372
|
-
const modelThinking = formatModelThinking(live.model, live.thinking);
|
|
373
|
-
const promptAuditCount = foregroundPromptAuditCount(item, state);
|
|
374
|
-
const promptSummary = foregroundAuthoredPromptSummary(item, state);
|
|
375
|
-
const lines = [
|
|
376
|
-
`Run: ${item.runId}`,
|
|
377
|
-
"Source: foreground",
|
|
378
|
-
`State: running`,
|
|
379
|
-
`Mode: ${control.mode}`,
|
|
380
|
-
control.parentWorkflowRunId ? `Workflow child of: ${control.parentWorkflowRunId}${control.workflowKey ? ` (${control.workflowKey})` : ""}` : undefined,
|
|
381
|
-
control.sourceRunId ? `Redo source: ${control.sourceRunId}` : undefined,
|
|
382
|
-
control.supersededByRunId ? `Superseded by: ${control.supersededByRunId}` : undefined,
|
|
383
|
-
item.index !== undefined ? `Child: ${item.index} (${item.agent})` : `Agent: ${item.agent}`,
|
|
384
|
-
modelThinking ? `Model: ${modelThinking}` : undefined,
|
|
385
|
-
promptSummary ? `Task: ${promptSummary}` : undefined,
|
|
386
|
-
`Started: ${new Date(live.startedAt).toISOString()}`,
|
|
387
|
-
live.currentTool ? `Current tool: ${live.currentTool}${live.currentPath ? ` · ${shortenPath(live.currentPath)}` : ""}` : undefined,
|
|
388
|
-
live.turnCount !== undefined ? `Turns: ${live.turnCount}` : undefined,
|
|
389
|
-
live.toolCount !== undefined ? `Tools: ${live.toolCount}` : undefined,
|
|
390
|
-
live.tokens !== undefined ? `Tokens: ${formatTokens(live.tokens)}` : undefined,
|
|
391
|
-
promptAuditCount > 0 ? `Prompt audit: ${promptAuditCount} live · 3 views · p opens` : undefined,
|
|
392
|
-
"",
|
|
393
|
-
"Transcript",
|
|
394
|
-
"Live foreground output remains in the expanded subagent tool result. Persisted output and session paths appear here after the child settles.",
|
|
395
|
-
];
|
|
396
|
-
return lines.filter((line): line is string => line !== undefined);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function pathWithin(base: string, candidate: string): boolean {
|
|
400
|
-
const resolvedBase = path.resolve(base);
|
|
401
|
-
const resolvedCandidate = path.resolve(candidate);
|
|
402
|
-
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function trustedFileTail(filePath: string, trustedRoots: string[]): { text?: string; warning?: string; unavailable?: string } {
|
|
406
|
-
const resolvedPath = path.resolve(filePath);
|
|
407
|
-
if (trustedRoots.length === 0 || !trustedRoots.some((root) => pathWithin(root, resolvedPath))) return { warning: `output artifact is outside trusted roots: ${filePath}` };
|
|
408
|
-
let stat: fs.Stats;
|
|
409
|
-
try {
|
|
410
|
-
stat = fs.lstatSync(resolvedPath);
|
|
411
|
-
} catch (error) {
|
|
412
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return { unavailable: `output artifact unavailable: ${filePath}` };
|
|
413
|
-
return { warning: `output artifact could not be inspected: ${error instanceof Error ? error.message : String(error)}` };
|
|
414
|
-
}
|
|
415
|
-
if (stat.isSymbolicLink()) return { warning: `output artifact refused a symlink: ${filePath}` };
|
|
416
|
-
if (!stat.isFile()) return { warning: `output artifact is not a file: ${filePath}` };
|
|
417
|
-
try {
|
|
418
|
-
const realPath = fs.realpathSync(resolvedPath);
|
|
419
|
-
const realRoots = trustedRoots.filter((root) => fs.existsSync(root)).map((root) => fs.realpathSync(root));
|
|
420
|
-
if (!realRoots.some((root) => pathWithin(root, realPath))) return { warning: `output artifact resolves outside trusted roots: ${filePath}` };
|
|
421
|
-
const fd = fs.openSync(realPath, "r");
|
|
422
|
-
try {
|
|
423
|
-
const bytes = Math.min(stat.size, OUTPUT_TAIL_BYTES);
|
|
424
|
-
const buffer = Buffer.alloc(bytes);
|
|
425
|
-
fs.readSync(fd, buffer, 0, bytes, stat.size - bytes);
|
|
426
|
-
return { text: decodeUtf8Tail(buffer) };
|
|
427
|
-
} finally {
|
|
428
|
-
fs.closeSync(fd);
|
|
429
|
-
}
|
|
430
|
-
} catch (error) {
|
|
431
|
-
return { warning: `output artifact could not be read: ${error instanceof Error ? error.message : String(error)}` };
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function foregroundRecentOutputLines(item: Extract<FleetItem, { kind: "foreground-recent" }>, state: SubagentState): string[] {
|
|
436
|
-
const outputPath = item.child.artifactPaths?.outputPath ?? item.child.savedOutputPath;
|
|
437
|
-
const output = item.child.finalOutput
|
|
438
|
-
? { text: item.child.finalOutput }
|
|
439
|
-
: outputPath
|
|
440
|
-
? trustedFileTail(path.isAbsolute(outputPath) ? outputPath : path.resolve(item.run.cwd, outputPath), uniquePaths([
|
|
441
|
-
fleetArtifactsRoot(state, item.run.cwd),
|
|
442
|
-
fleetArtifactsRoot(state, state.baseCwd),
|
|
443
|
-
]))
|
|
444
|
-
: undefined;
|
|
445
|
-
if (output?.warning) return [`(${output.warning})`];
|
|
446
|
-
if (output?.unavailable) return [`(${output.unavailable})`];
|
|
447
|
-
const outputLines = (output?.text ?? "").split(/\r?\n/).filter((line) => line.trim()).slice(-TRANSCRIPT_LINES);
|
|
448
|
-
return outputLines.length ? outputLines : ["(no recovered output available)"];
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function foregroundRecentDetail(item: Extract<FleetItem, { kind: "foreground-recent" }>, state: SubagentState): string[] {
|
|
452
|
-
const { child, run } = item;
|
|
453
|
-
const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
|
|
454
|
-
const modelThinking = formatModelThinking(child.model, child.thinking);
|
|
455
|
-
const lines = [
|
|
456
|
-
`Run: ${item.runId}`,
|
|
457
|
-
"Source: foreground",
|
|
458
|
-
`State: ${child.status}`,
|
|
459
|
-
`Mode: ${run.mode}`,
|
|
460
|
-
`Child: ${child.index} (${child.agent})${contextModeLabel(child.context) ? ` ${contextModeLabel(child.context)}` : ""}`,
|
|
461
|
-
modelThinking ? `Model: ${modelThinking}` : undefined,
|
|
462
|
-
`Updated: ${new Date(child.updatedAt ?? run.updatedAt).toISOString()}`,
|
|
463
|
-
outputPath ? `Output: ${outputPath}` : undefined,
|
|
464
|
-
child.sessionFile ? `Session: ${child.sessionFile}` : undefined,
|
|
465
|
-
child.transcriptPath ? `Transcript file: ${child.transcriptPath}` : undefined,
|
|
466
|
-
child.error ? `Error: ${child.error}` : undefined,
|
|
467
|
-
child.outputSaveError ? `Output warning: ${child.outputSaveError}` : undefined,
|
|
468
|
-
child.transcriptError ? `Transcript warning: ${child.transcriptError}` : undefined,
|
|
469
|
-
"",
|
|
470
|
-
"Result transcript tail",
|
|
471
|
-
];
|
|
472
|
-
lines.push(...foregroundRecentOutputLines(item, state));
|
|
473
|
-
return lines.filter((line): line is string => line !== undefined);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function externalElapsedEnd(run: ExternalRun): number {
|
|
477
|
-
const terminal = run.state !== "queued" && run.state !== "running";
|
|
478
|
-
return terminal ? (run.endedAt ?? run.updatedAt ?? Date.now()) : Date.now();
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
function asyncDetail(item: Extract<FleetItem, { kind: "async" }>): string[] {
|
|
482
|
-
const status = readStatus(item.run.asyncDir);
|
|
483
|
-
if (status) {
|
|
484
|
-
return formatAsyncRunTranscript(status, item.run.asyncDir, { index: item.index, lines: TRANSCRIPT_LINES }).split("\n");
|
|
485
|
-
}
|
|
486
|
-
const outputPath = item.index !== undefined ? path.join(item.run.asyncDir, `output-${item.index}.log`) : undefined;
|
|
487
|
-
return [
|
|
488
|
-
`Run: ${item.runId}`,
|
|
489
|
-
"Source: async",
|
|
490
|
-
`State: ${item.state}`,
|
|
491
|
-
`Mode: ${item.run.mode}${contextModeLabel(item.run.context) ? ` ${contextModeLabel(item.run.context)}` : ""}`,
|
|
492
|
-
item.index !== undefined ? `Child: ${item.index} (${item.agent})${contextModeLabel(item.step?.context) ? ` ${contextModeLabel(item.step?.context)}` : ""}` : `Agent: ${item.agent}${contextModeLabel(item.run.context) ? ` ${contextModeLabel(item.run.context)}` : ""}`,
|
|
493
|
-
outputPath ? `Output: ${outputPath}` : undefined,
|
|
494
|
-
item.step?.sessionFile ? `Session: ${item.step.sessionFile}` : item.run.sessionFile ? `Session: ${item.run.sessionFile}` : undefined,
|
|
495
|
-
"",
|
|
496
|
-
"Transcript",
|
|
497
|
-
"(status is no longer available)",
|
|
498
|
-
].filter((line): line is string => line !== undefined);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function externalDetail(item: Extract<FleetItem, { kind: "external" }>): string[] {
|
|
502
|
-
return [
|
|
503
|
-
`Run: ${item.runId}`,
|
|
504
|
-
"Source: external · display-only",
|
|
505
|
-
`Owner: ${item.run.source}`,
|
|
506
|
-
`State: ${item.state}`,
|
|
507
|
-
`Started: ${new Date(item.run.startedAt).toISOString()}`,
|
|
508
|
-
item.run.updatedAt !== undefined ? `Updated: ${new Date(item.run.updatedAt).toISOString()}` : undefined,
|
|
509
|
-
item.run.endedAt !== undefined ? `Ended: ${new Date(item.run.endedAt).toISOString()}` : undefined,
|
|
510
|
-
`Elapsed: ${formatDuration(Math.max(0, externalElapsedEnd(item.run) - item.run.startedAt))}`,
|
|
511
|
-
item.run.currentAction ? `Current action: ${item.run.currentAction}` : undefined,
|
|
512
|
-
item.run.reportPath ? `Report path: ${item.run.reportPath}` : undefined,
|
|
513
|
-
item.run.transcriptPath ? `Transcript path: ${item.run.transcriptPath}` : undefined,
|
|
514
|
-
"",
|
|
515
|
-
"Preview",
|
|
516
|
-
item.run.preview ?? "(no preview supplied)",
|
|
517
|
-
"",
|
|
518
|
-
"The owning extension controls execution, persistence, cancellation, and results.",
|
|
519
|
-
].filter((line): line is string => line !== undefined);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function detailLines(item: FleetItem | undefined, error: string | undefined, state: SubagentState): string[] {
|
|
523
|
-
if (!item) return [error ? `Fleet scan failed: ${error}` : "No current-session Fleet jobs.", "", "New jobs appear here automatically while this inspector remains open."];
|
|
524
|
-
const lines = item.kind === "foreground-active"
|
|
525
|
-
? foregroundActiveDetail(item, state)
|
|
526
|
-
: item.kind === "foreground-recent"
|
|
527
|
-
? foregroundRecentDetail(item, state)
|
|
528
|
-
: item.kind === "external"
|
|
529
|
-
? externalDetail(item)
|
|
530
|
-
: asyncDetail(item);
|
|
531
|
-
if (error) lines.unshift(`Fleet scan warning: ${error}`, "");
|
|
532
|
-
return lines;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function isActionableAsyncState(state: string): boolean {
|
|
536
|
-
return state === "running" || state === "queued" || state === "pending";
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function firstToolResultText(result: AgentToolResult<Details> | null, fallback: string): FleetActionResult {
|
|
540
|
-
if (!result) return { text: fallback, isError: true };
|
|
541
|
-
const text = result.content.find((item) => item.type === "text")?.text ?? fallback;
|
|
542
|
-
return { text, ...(result.isError ? { isError: true } : {}) };
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function uniquePaths(values: Array<string | undefined>): string[] {
|
|
546
|
-
return [...new Set(values.filter((value): value is string => Boolean(value)).map((value) => path.resolve(value)))];
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function fleetArtifactsRoot(state: SubagentState, cwd: string): string {
|
|
550
|
-
return getArtifactsDir(
|
|
551
|
-
state.parentSessionFile ?? null,
|
|
552
|
-
cwd,
|
|
553
|
-
state.artifactDirPreference,
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function transcriptTarget(item: FleetItem, state: SubagentState): { path: string; trustedRoots: string[] } | undefined {
|
|
558
|
-
if (item.kind === "external") return undefined;
|
|
559
|
-
if (item.kind === "foreground-active") {
|
|
560
|
-
const artifactsRoot = fleetArtifactsRoot(state, item.control.cwd ?? state.baseCwd);
|
|
561
|
-
return {
|
|
562
|
-
path: getArtifactPaths(artifactsRoot, item.runId, item.agent, item.index ?? 0).transcriptPath,
|
|
563
|
-
trustedRoots: [artifactsRoot],
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
if (item.kind === "foreground-recent") {
|
|
567
|
-
if (!item.child.transcriptPath) return undefined;
|
|
568
|
-
const transcriptPath = path.isAbsolute(item.child.transcriptPath)
|
|
569
|
-
? item.child.transcriptPath
|
|
570
|
-
: path.resolve(item.run.cwd, item.child.transcriptPath);
|
|
571
|
-
return {
|
|
572
|
-
path: transcriptPath,
|
|
573
|
-
trustedRoots: uniquePaths([
|
|
574
|
-
fleetArtifactsRoot(state, item.run.cwd),
|
|
575
|
-
fleetArtifactsRoot(state, state.baseCwd),
|
|
576
|
-
]),
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
const step = item.step ?? (item.run.steps.length === 1 ? item.run.steps[0] : undefined);
|
|
580
|
-
if (!step?.transcriptPath) return undefined;
|
|
581
|
-
const transcriptPath = path.isAbsolute(step.transcriptPath)
|
|
582
|
-
? step.transcriptPath
|
|
583
|
-
: path.resolve(item.run.asyncDir, step.transcriptPath);
|
|
584
|
-
const trackedJob = state.fleetJobs?.get(item.runId) ?? state.asyncJobs.get(item.runId);
|
|
585
|
-
return {
|
|
586
|
-
path: transcriptPath,
|
|
587
|
-
trustedRoots: uniquePaths([
|
|
588
|
-
item.run.asyncDir,
|
|
589
|
-
fleetArtifactsRoot(state, state.baseCwd),
|
|
590
|
-
trackedJob?.cwd ? fleetArtifactsRoot(state, trackedJob.cwd) : undefined,
|
|
591
|
-
item.run.sessionFile ? getArtifactsDir(item.run.sessionFile, item.run.cwd ?? state.baseCwd, state.artifactDirPreference) : undefined,
|
|
592
|
-
]),
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function itemContext(item: FleetItem): string | undefined {
|
|
597
|
-
if (item.kind === "async") return contextModeLabel(item.step?.context ?? item.run.context);
|
|
598
|
-
if (item.kind === "foreground-recent") return contextModeLabel(item.child.context);
|
|
599
|
-
return undefined;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
function itemMode(item: FleetItem): string {
|
|
603
|
-
if (item.kind === "external") return "display-only";
|
|
604
|
-
return item.kind === "foreground-active" ? item.control.mode : item.run.mode;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function itemSource(item: FleetItem): string {
|
|
608
|
-
if (item.kind === "external") return `external · ${item.run.source}`;
|
|
609
|
-
if (item.kind === "async") return "background";
|
|
610
|
-
return item.kind === "foreground-active" ? "foreground · live" : "foreground · recent";
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
function itemStats(item: FleetItem): string[] {
|
|
614
|
-
let model: string | undefined;
|
|
615
|
-
let tokens: number | undefined;
|
|
616
|
-
let tools: number | undefined;
|
|
617
|
-
let durationMs: number | undefined;
|
|
618
|
-
if (item.kind === "foreground-active") {
|
|
619
|
-
const live = item.activeChild ?? item.control;
|
|
620
|
-
model = formatModelThinking(live.model, live.thinking) || undefined;
|
|
621
|
-
tokens = live.tokens;
|
|
622
|
-
tools = live.toolCount;
|
|
623
|
-
durationMs = Math.max(0, Date.now() - live.startedAt);
|
|
624
|
-
} else if (item.kind === "foreground-recent") {
|
|
625
|
-
model = formatModelThinking(item.child.model, item.child.thinking) || undefined;
|
|
626
|
-
tokens = item.child.tokens;
|
|
627
|
-
tools = item.child.toolCount;
|
|
628
|
-
} else if (item.kind === "external") {
|
|
629
|
-
durationMs = Math.max(0, externalElapsedEnd(item.run) - item.run.startedAt);
|
|
630
|
-
} else {
|
|
631
|
-
model = formatModelThinking(item.step?.model, item.step?.thinking) || undefined;
|
|
632
|
-
tokens = item.step?.tokens?.total ?? (item.index === undefined ? item.run.totalTokens?.total : undefined);
|
|
633
|
-
tools = item.step?.toolCount ?? (item.index === undefined ? item.run.toolCount : undefined);
|
|
634
|
-
const terminalRun = item.state !== "queued" && item.state !== "running" && item.state !== "pending";
|
|
635
|
-
const endTime = item.run.endedAt ?? (terminalRun ? item.run.lastUpdate : undefined) ?? Date.now();
|
|
636
|
-
durationMs = item.step?.durationMs ?? Math.max(0, endTime - item.run.startedAt);
|
|
637
|
-
}
|
|
638
|
-
return [
|
|
639
|
-
model,
|
|
640
|
-
tokens !== undefined ? `${formatTokens(tokens)} tok` : undefined,
|
|
641
|
-
tools !== undefined ? `${tools} tool${tools === 1 ? "" : "s"}` : undefined,
|
|
642
|
-
durationMs !== undefined ? formatDuration(durationMs) : undefined,
|
|
643
|
-
].filter((value): value is string => Boolean(value));
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function structuredHeader(item: FleetItem, width: number, theme: Theme, conversationState: string, promptSummary?: string): string[] {
|
|
647
|
-
const lines: string[] = [];
|
|
648
|
-
lines.push(rightAligned(` ${statusGlyph(item, theme)} ${theme.bold(item.agent)}`, theme.fg("dim", item.state), width));
|
|
649
|
-
const child = "index" in item && item.index !== undefined ? ` · child ${item.index + 1}` : "";
|
|
650
|
-
const context = itemContext(item);
|
|
651
|
-
const identity = `${itemSource(item)} · ${item.runId.slice(0, 8)}${child} · ${itemMode(item)}${context ? ` ${context}` : ""}`;
|
|
652
|
-
lines.push(` ${theme.fg("dim", identity)}`);
|
|
653
|
-
const stats = itemStats(item);
|
|
654
|
-
if (stats.length) lines.push(` ${theme.fg("muted", stats.join(" · "))}`);
|
|
655
|
-
if (promptSummary) lines.push(` ${theme.fg("muted", `Task: ${promptSummary}`)}`);
|
|
656
|
-
lines.push(`${theme.fg("accent", "Conversation")} ${theme.fg("dim", `· ${conversationState}`)}`);
|
|
657
|
-
return lines.map((line) => truncateToWidth(line, width));
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
function fit(text: string, width: number): string {
|
|
661
|
-
const clipped = truncateToWidth(text, Math.max(0, width));
|
|
662
|
-
return clipped + " ".repeat(Math.max(0, width - visibleWidth(clipped)));
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
function rightAligned(left: string, right: string, width: number): string {
|
|
666
|
-
const rightWidth = visibleWidth(right);
|
|
667
|
-
const leftWidth = Math.max(0, width - rightWidth - 1);
|
|
668
|
-
return fit(left, leftWidth) + " ".repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
interface FleetDetailSections {
|
|
672
|
-
header: string[];
|
|
673
|
-
body: string[];
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
interface FleetTranscriptCache {
|
|
677
|
-
path: string;
|
|
678
|
-
fingerprint: string;
|
|
679
|
-
width: number;
|
|
680
|
-
expandedTools: boolean;
|
|
681
|
-
transcript: FleetTranscript;
|
|
682
|
-
body: string[];
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function transcriptFingerprint(filePath: string): string {
|
|
686
|
-
try {
|
|
687
|
-
const stat = fs.statSync(filePath);
|
|
688
|
-
return `${stat.size}:${stat.mtimeMs}`;
|
|
689
|
-
} catch {
|
|
690
|
-
return "missing";
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
export class SubagentFleetComponent implements Component {
|
|
695
|
-
private snapshot: FleetSnapshot = { items: [] };
|
|
696
|
-
private selected = 0;
|
|
697
|
-
private selectedKey: string | undefined;
|
|
698
|
-
private detailScroll = 0;
|
|
699
|
-
private detailAutoFollow = true;
|
|
700
|
-
private detailLineCount = 0;
|
|
701
|
-
private detailViewportHeight = 8;
|
|
702
|
-
private bodyHeight = 8;
|
|
703
|
-
private expandedTools = false;
|
|
704
|
-
private promptAuditOpen = false;
|
|
705
|
-
private promptAuditView: PromptAuditView = "authored";
|
|
706
|
-
private actionNotice: FleetActionResult | undefined;
|
|
707
|
-
private steerDraft: string | undefined;
|
|
708
|
-
private redoGuidanceDraft: string | undefined;
|
|
709
|
-
private steerMode: SteerDeliveryMode = "steer";
|
|
710
|
-
private stopConfirming = false;
|
|
711
|
-
private actionBusy = false;
|
|
712
|
-
private transcriptCache: FleetTranscriptCache | undefined;
|
|
713
|
-
private disposed = false;
|
|
714
|
-
private refreshTimer: ReturnType<typeof setTimeout> | undefined;
|
|
715
|
-
private readonly refreshMs: number;
|
|
716
|
-
private readonly tui: FleetTui;
|
|
717
|
-
private readonly theme: Theme;
|
|
718
|
-
private readonly markdownTheme: MarkdownTheme;
|
|
719
|
-
private readonly state: SubagentState;
|
|
720
|
-
private readonly done: (result: undefined) => void;
|
|
721
|
-
private readonly options: FleetViewOptions;
|
|
722
|
-
private readonly keybindings: ResolvedFleetKeybindings;
|
|
723
|
-
|
|
724
|
-
constructor(
|
|
725
|
-
tui: FleetTui,
|
|
726
|
-
theme: Theme,
|
|
727
|
-
state: SubagentState,
|
|
728
|
-
done: (result: undefined) => void,
|
|
729
|
-
options: FleetViewOptions = {},
|
|
730
|
-
) {
|
|
731
|
-
this.tui = tui;
|
|
732
|
-
this.theme = theme;
|
|
733
|
-
this.markdownTheme = options.markdownTheme ?? getMarkdownTheme();
|
|
734
|
-
this.state = state;
|
|
735
|
-
this.done = done;
|
|
736
|
-
this.options = options;
|
|
737
|
-
this.keybindings = resolveFleetKeybindings(options.fleetKeybindings);
|
|
738
|
-
this.refreshMs = Math.max(MIN_REFRESH_MS, options.refreshMs ?? REFRESH_MS);
|
|
739
|
-
this.selectedKey = options.initialKey;
|
|
740
|
-
this.refresh();
|
|
741
|
-
this.scheduleRefresh();
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
private scheduleRefresh(): void {
|
|
745
|
-
if (this.disposed || this.refreshTimer) return;
|
|
746
|
-
this.refreshTimer = setTimeout(() => {
|
|
747
|
-
this.refreshTimer = undefined;
|
|
748
|
-
if (this.disposed) return;
|
|
749
|
-
try {
|
|
750
|
-
this.invalidate();
|
|
751
|
-
this.tui.requestRender();
|
|
752
|
-
} finally {
|
|
753
|
-
this.scheduleRefresh();
|
|
754
|
-
}
|
|
755
|
-
}, this.refreshMs);
|
|
756
|
-
this.refreshTimer.unref?.();
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
private stopRefresh(): void {
|
|
760
|
-
this.disposed = true;
|
|
761
|
-
if (this.refreshTimer) clearTimeout(this.refreshTimer);
|
|
762
|
-
this.refreshTimer = undefined;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
private refresh(): void {
|
|
766
|
-
const previousKey = this.snapshot.items[this.selected]?.key ?? this.selectedKey;
|
|
767
|
-
this.snapshot = collectFleetSnapshot(this.state, this.options);
|
|
768
|
-
let preserved = previousKey ? this.snapshot.items.findIndex((item) => item.key === previousKey) : -1;
|
|
769
|
-
if (preserved < 0 && previousKey) {
|
|
770
|
-
const parentKey = visibleWorkflowParentKeyForForegroundKey(this.state, previousKey, this.snapshot.items);
|
|
771
|
-
if (parentKey) preserved = this.snapshot.items.findIndex((item) => item.key === parentKey);
|
|
772
|
-
}
|
|
773
|
-
this.selected = preserved >= 0 ? preserved : Math.min(this.selected, Math.max(0, this.snapshot.items.length - 1));
|
|
774
|
-
this.selectedKey = this.snapshot.items[this.selected]?.key;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
private moveSelection(delta: number): void {
|
|
778
|
-
if (this.snapshot.items.length === 0) return;
|
|
779
|
-
this.selected = Math.max(0, Math.min(this.snapshot.items.length - 1, this.selected + delta));
|
|
780
|
-
this.selectedKey = this.snapshot.items[this.selected]?.key;
|
|
781
|
-
this.detailAutoFollow = true;
|
|
782
|
-
this.resetActionInput();
|
|
783
|
-
this.tui.requestRender();
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
private selectedPromptAudit(): LivePromptAudit | undefined {
|
|
787
|
-
const item = this.snapshot.items[this.selected];
|
|
788
|
-
if (item?.kind !== "foreground-active") return undefined;
|
|
789
|
-
if (!this.state.currentSessionId || item.control.sessionId !== this.state.currentSessionId) return undefined;
|
|
790
|
-
return getLivePromptAudit(item.control, item.index ?? 0);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
private promptAuditItems(): Array<{ item: Extract<FleetItem, { kind: "foreground-active" }>; prompt: LivePromptAudit }> {
|
|
794
|
-
const selected = this.snapshot.items[this.selected];
|
|
795
|
-
if (selected?.kind !== "foreground-active" || !this.state.currentSessionId || selected.control.sessionId !== this.state.currentSessionId) return [];
|
|
796
|
-
return this.snapshot.items.flatMap((item) => {
|
|
797
|
-
if (item.kind !== "foreground-active" || item.control !== selected.control) return [];
|
|
798
|
-
const prompt = getLivePromptAudit(item.control, item.index ?? 0);
|
|
799
|
-
return prompt ? [{ item, prompt }] : [];
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
private selectedPromptText(): string | undefined {
|
|
804
|
-
const prompt = this.selectedPromptAudit();
|
|
805
|
-
return prompt ? promptAuditText(prompt, this.promptAuditView) : undefined;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
private movePromptSelection(delta: number): void {
|
|
809
|
-
const items = this.promptAuditItems();
|
|
810
|
-
if (items.length === 0) return;
|
|
811
|
-
const currentKey = this.snapshot.items[this.selected]?.key;
|
|
812
|
-
const current = Math.max(0, items.findIndex(({ item }) => item.key === currentKey));
|
|
813
|
-
const target = items[Math.max(0, Math.min(items.length - 1, current + delta))]?.item;
|
|
814
|
-
if (!target) return;
|
|
815
|
-
this.selected = this.snapshot.items.findIndex((item) => item.key === target.key);
|
|
816
|
-
this.selectedKey = target.key;
|
|
817
|
-
this.detailScroll = 0;
|
|
818
|
-
this.detailAutoFollow = false;
|
|
819
|
-
this.tui.requestRender();
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
private resetActionInput(): void {
|
|
823
|
-
this.steerDraft = undefined;
|
|
824
|
-
this.redoGuidanceDraft = undefined;
|
|
825
|
-
this.steerMode = "steer";
|
|
826
|
-
this.stopConfirming = false;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
private selectedAsyncAction(): { item: Extract<FleetItem, { kind: "async" }> } | { reason: string } {
|
|
830
|
-
const item = this.snapshot.items[this.selected];
|
|
831
|
-
if (!item) return { reason: "No child is selected." };
|
|
832
|
-
if (item.kind === "external") return { reason: "External jobs are display-only and remain controlled by their owning extension." };
|
|
833
|
-
if (item.kind !== "async") return { reason: "Fleet controls are available for current-session top-level async runs only." };
|
|
834
|
-
if (!isActionableAsyncState(item.run.state) || !isActionableAsyncState(item.state)) return { reason: `Selected child is ${item.state}; controls require a running or queued async child.` };
|
|
835
|
-
return { item };
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
private selectedHerdrInspectAction(): { runId: string; asyncDir: string; index?: number } | { reason: string } {
|
|
839
|
-
const item = this.snapshot.items[this.selected];
|
|
840
|
-
if (!item) return { reason: "No child is selected." };
|
|
841
|
-
if (item.kind === "external") return { reason: "External jobs are display-only and have no Herdr controls." };
|
|
842
|
-
if (item.kind === "async") {
|
|
843
|
-
if (!isActionableAsyncState(item.run.state) || !isActionableAsyncState(item.state)) return { reason: `Selected child is ${item.state}; controls require a running or queued async child.` };
|
|
844
|
-
return { runId: item.runId, asyncDir: item.run.asyncDir, ...(item.index !== undefined ? { index: item.index } : {}) };
|
|
845
|
-
}
|
|
846
|
-
if (item.kind !== "foreground-active" || !item.control.parentWorkflowRunId) return { reason: "Fleet controls are available for current-session top-level async runs only." };
|
|
847
|
-
const parent = this.state.asyncJobs.get(item.control.parentWorkflowRunId) ?? this.state.fleetJobs?.get(item.control.parentWorkflowRunId);
|
|
848
|
-
if (!parent || !isActionableAsyncState(parent.status)) return { reason: "The parent workflow is no longer available for Herdr inspection." };
|
|
849
|
-
return { runId: parent.asyncId, asyncDir: parent.asyncDir };
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
private actionLines(): string[] {
|
|
853
|
-
const lines: string[] = [];
|
|
854
|
-
if (this.actionBusy) lines.push(this.theme.fg("accent", "Action pending..."));
|
|
855
|
-
if (this.steerDraft !== undefined) {
|
|
856
|
-
lines.push(this.theme.fg("accent", `Steer message (${this.steerMode}): ${this.steerDraft}${this.theme.fg("dim", "▌")}`));
|
|
857
|
-
lines.push(this.theme.fg("dim", "Enter sends · Tab changes mode · Esc cancels · Backspace edits"));
|
|
858
|
-
} else if (this.redoGuidanceDraft !== undefined) {
|
|
859
|
-
lines.push(this.theme.fg("accent", `Redo guidance: ${this.redoGuidanceDraft}${this.theme.fg("dim", "▌")}`));
|
|
860
|
-
lines.push(this.theme.fg("dim", "Enter rewrites and reruns · Esc cancels · Backspace edits"));
|
|
861
|
-
} else if (this.stopConfirming) {
|
|
862
|
-
const selected = this.snapshot.items[this.selected];
|
|
863
|
-
lines.push(this.theme.fg("warning", `Confirm stop for async run ${selected?.runId ?? "selected run"}?`));
|
|
864
|
-
lines.push(this.theme.fg("dim", "Stop ends the run; use interrupt for a resumable pause. Enter/Y confirms · N returns · Esc cancels"));
|
|
865
|
-
} else if (this.actionNotice) {
|
|
866
|
-
lines.push(this.theme.fg(this.actionNotice.isError ? "error" : "success", this.actionNotice.text));
|
|
867
|
-
}
|
|
868
|
-
return lines;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
private withActionLines(body: string[]): string[] {
|
|
872
|
-
const actionLines = this.actionLines();
|
|
873
|
-
return actionLines.length ? [...actionLines, "", ...body] : body;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
private setActionNotice(result: FleetActionResult): void {
|
|
877
|
-
this.actionNotice = result;
|
|
878
|
-
this.resetActionInput();
|
|
879
|
-
this.detailAutoFollow = false;
|
|
880
|
-
this.detailScroll = 0;
|
|
881
|
-
this.refresh();
|
|
882
|
-
this.tui.requestRender();
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
private runAction(action: () => Promise<FleetActionResult>): void {
|
|
886
|
-
if (this.actionBusy) return;
|
|
887
|
-
this.actionBusy = true;
|
|
888
|
-
this.actionNotice = undefined;
|
|
889
|
-
this.tui.requestRender();
|
|
890
|
-
void action()
|
|
891
|
-
.then((result) => this.setActionNotice(result))
|
|
892
|
-
.catch((error) => this.setActionNotice({ text: error instanceof Error ? error.message : String(error), isError: true }))
|
|
893
|
-
.finally(() => {
|
|
894
|
-
this.actionBusy = false;
|
|
895
|
-
if (!this.disposed) this.tui.requestRender();
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
private scrollDetail(delta: number): void {
|
|
900
|
-
const maxScroll = Math.max(0, this.detailLineCount - this.detailViewportHeight);
|
|
901
|
-
this.detailScroll = Math.max(0, Math.min(maxScroll, this.detailScroll + delta));
|
|
902
|
-
this.detailAutoFollow = this.detailScroll >= maxScroll;
|
|
903
|
-
this.tui.requestRender();
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
handleInput(data: string): void {
|
|
907
|
-
if (this.promptAuditOpen && this.redoGuidanceDraft !== undefined) {
|
|
908
|
-
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
909
|
-
this.resetActionInput();
|
|
910
|
-
this.tui.requestRender();
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
if (matchesKey(data, "return") || data === "\r" || data === "\n") {
|
|
914
|
-
const guidance = this.redoGuidanceDraft.trim();
|
|
915
|
-
if (!guidance) {
|
|
916
|
-
this.setActionNotice({ text: "Redo guidance cannot be empty.", isError: true });
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
const selected = this.snapshot.items[this.selected];
|
|
920
|
-
if (selected?.kind !== "foreground-active" || !this.options.actions?.redoPrompt) {
|
|
921
|
-
this.setActionNotice({ text: "Redo is unavailable for this prompt.", isError: true });
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
this.runAction(() => this.options.actions!.redoPrompt!({ runId: selected.runId, index: selected.index ?? 0, guidance, control: selected.control }));
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
if (matchesKey(data, "backspace") || data === "\x7f") {
|
|
928
|
-
this.redoGuidanceDraft = this.redoGuidanceDraft.slice(0, -1);
|
|
929
|
-
this.tui.requestRender();
|
|
930
|
-
return;
|
|
931
|
-
}
|
|
932
|
-
if (data.length === 1 && data >= " " && data !== "\x7f") {
|
|
933
|
-
this.redoGuidanceDraft += data;
|
|
934
|
-
this.tui.requestRender();
|
|
935
|
-
}
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
if (this.promptAuditOpen) {
|
|
939
|
-
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
940
|
-
this.promptAuditOpen = false;
|
|
941
|
-
this.detailAutoFollow = true;
|
|
942
|
-
this.tui.requestRender();
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
if (data === "j") return this.movePromptSelection(1);
|
|
946
|
-
if (data === "k") return this.movePromptSelection(-1);
|
|
947
|
-
if (data === "1" || data === "2" || data === "3") {
|
|
948
|
-
this.promptAuditView = data === "1" ? "authored" : data === "2" ? "runtime" : "effective";
|
|
949
|
-
this.detailScroll = 0;
|
|
950
|
-
this.tui.requestRender();
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
if (data === "g") {
|
|
954
|
-
const prompt = this.selectedPromptAudit();
|
|
955
|
-
if (!prompt) this.setActionNotice({ text: "Prompt Audit is available only for a live child owned by this session.", isError: true });
|
|
956
|
-
else if (!prompt.rerun) this.setActionNotice({ text: "Redo is not safe for this prompt in this slice.", isError: true });
|
|
957
|
-
else if (!this.options.actions?.redoPrompt) this.setActionNotice({ text: "Redo controls are unavailable in this context.", isError: true });
|
|
958
|
-
else {
|
|
959
|
-
this.actionNotice = undefined;
|
|
960
|
-
this.redoGuidanceDraft = "";
|
|
961
|
-
this.detailAutoFollow = false;
|
|
962
|
-
this.detailScroll = 0;
|
|
963
|
-
this.tui.requestRender();
|
|
964
|
-
}
|
|
965
|
-
return;
|
|
966
|
-
}
|
|
967
|
-
if (data === "c") {
|
|
968
|
-
const text = this.selectedPromptText();
|
|
969
|
-
if (!text) this.setActionNotice({ text: "No prompt view is available to copy.", isError: true });
|
|
970
|
-
else if (!this.options.copyText) this.setActionNotice({ text: "Clipboard is unavailable in this context.", isError: true });
|
|
971
|
-
else this.runAction(async () => {
|
|
972
|
-
await this.options.copyText!(text);
|
|
973
|
-
return { text: "Copied visible prompt view." };
|
|
974
|
-
});
|
|
975
|
-
return;
|
|
976
|
-
}
|
|
977
|
-
if (matchesFleetAction(data, this.keybindings, "scrollUp")) return this.scrollDetail(-1);
|
|
978
|
-
if (matchesFleetAction(data, this.keybindings, "scrollDown")) return this.scrollDetail(1);
|
|
979
|
-
if (matchesFleetAction(data, this.keybindings, "pageUp")) return this.scrollDetail(-this.detailViewportHeight);
|
|
980
|
-
if (matchesFleetAction(data, this.keybindings, "pageDown")) return this.scrollDetail(this.detailViewportHeight);
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
if (this.steerDraft !== undefined) {
|
|
984
|
-
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
985
|
-
this.resetActionInput();
|
|
986
|
-
this.tui.requestRender();
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
if (matchesKey(data, "return") || data === "\r" || data === "\n") {
|
|
990
|
-
const message = this.steerDraft.trim();
|
|
991
|
-
if (!message) {
|
|
992
|
-
this.setActionNotice({ text: "Steer message cannot be empty.", isError: true });
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
const target = this.selectedAsyncAction();
|
|
996
|
-
if ("reason" in target || !this.options.actions) {
|
|
997
|
-
this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
this.runAction(() => this.options.actions!.steer({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}), message, mode: this.steerMode }));
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
if (matchesKey(data, "tab") || data === "\t") {
|
|
1004
|
-
const modes: SteerDeliveryMode[] = ["steer", "follow_up", "auto"];
|
|
1005
|
-
this.steerMode = modes[(modes.indexOf(this.steerMode) + 1) % modes.length]!;
|
|
1006
|
-
this.tui.requestRender();
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1009
|
-
if (matchesKey(data, "backspace") || data === "\x7f") {
|
|
1010
|
-
this.steerDraft = this.steerDraft.slice(0, -1);
|
|
1011
|
-
this.tui.requestRender();
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
if (data.length === 1 && data >= " " && data !== "\x7f") {
|
|
1015
|
-
this.steerDraft += data;
|
|
1016
|
-
this.tui.requestRender();
|
|
1017
|
-
}
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
if (this.stopConfirming) {
|
|
1021
|
-
if (matchesKey(data, "return") || data.toLowerCase() === "y") {
|
|
1022
|
-
const target = this.selectedAsyncAction();
|
|
1023
|
-
if ("reason" in target || !this.options.actions) {
|
|
1024
|
-
this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
1025
|
-
return;
|
|
1026
|
-
}
|
|
1027
|
-
this.runAction(() => Promise.resolve(this.options.actions!.stop({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}) })));
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || data.toLowerCase() === "n" || matchesKey(data, "backspace")) {
|
|
1031
|
-
this.resetActionInput();
|
|
1032
|
-
this.tui.requestRender();
|
|
1033
|
-
}
|
|
1034
|
-
return;
|
|
1035
|
-
}
|
|
1036
|
-
if (matchesFleetAction(data, this.keybindings, "close")) {
|
|
1037
|
-
this.stopRefresh();
|
|
1038
|
-
this.done(undefined);
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (matchesFleetAction(data, this.keybindings, "scrollUp")) return this.scrollDetail(-1);
|
|
1042
|
-
if (matchesFleetAction(data, this.keybindings, "scrollDown")) return this.scrollDetail(1);
|
|
1043
|
-
if (matchesFleetAction(data, this.keybindings, "selectUp")) return this.moveSelection(-1);
|
|
1044
|
-
if (matchesFleetAction(data, this.keybindings, "selectDown")) return this.moveSelection(1);
|
|
1045
|
-
if (matchesFleetAction(data, this.keybindings, "selectFirst")) return this.moveSelection(-this.snapshot.items.length);
|
|
1046
|
-
if (matchesFleetAction(data, this.keybindings, "selectLast")) return this.moveSelection(this.snapshot.items.length);
|
|
1047
|
-
if (matchesFleetAction(data, this.keybindings, "pageUp")) return this.scrollDetail(-this.detailViewportHeight);
|
|
1048
|
-
if (matchesFleetAction(data, this.keybindings, "pageDown")) return this.scrollDetail(this.detailViewportHeight);
|
|
1049
|
-
if (matchesFleetAction(data, this.keybindings, "refresh")) {
|
|
1050
|
-
this.transcriptCache = undefined;
|
|
1051
|
-
this.refresh();
|
|
1052
|
-
this.tui.requestRender();
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
if (data === "p") {
|
|
1056
|
-
if (!this.selectedPromptAudit()) this.setActionNotice({ text: "Prompt Audit is available only for a live child owned by this session.", isError: true });
|
|
1057
|
-
else {
|
|
1058
|
-
this.promptAuditOpen = true;
|
|
1059
|
-
this.promptAuditView = "authored";
|
|
1060
|
-
this.detailScroll = 0;
|
|
1061
|
-
this.detailAutoFollow = false;
|
|
1062
|
-
this.actionNotice = undefined;
|
|
1063
|
-
this.tui.requestRender();
|
|
1064
|
-
}
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
if (matchesFleetAction(data, this.keybindings, "steer")) {
|
|
1068
|
-
const target = this.selectedAsyncAction();
|
|
1069
|
-
if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
1070
|
-
else {
|
|
1071
|
-
this.actionNotice = undefined;
|
|
1072
|
-
this.steerDraft = "";
|
|
1073
|
-
this.detailAutoFollow = false;
|
|
1074
|
-
this.detailScroll = 0;
|
|
1075
|
-
this.tui.requestRender();
|
|
1076
|
-
}
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
if (matchesFleetAction(data, this.keybindings, "inspect")) {
|
|
1080
|
-
const target = this.selectedHerdrInspectAction();
|
|
1081
|
-
if ("reason" in target || !this.options.actions?.inspect) this.setActionNotice({ text: "reason" in target ? target.reason : "Herdr inspector controls are unavailable in this context.", isError: true });
|
|
1082
|
-
else this.runAction(() => this.options.actions!.inspect!(target));
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
if (matchesFleetAction(data, this.keybindings, "stop")) {
|
|
1086
|
-
const target = this.selectedAsyncAction();
|
|
1087
|
-
if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
1088
|
-
else {
|
|
1089
|
-
this.actionNotice = undefined;
|
|
1090
|
-
this.stopConfirming = true;
|
|
1091
|
-
this.detailAutoFollow = false;
|
|
1092
|
-
this.detailScroll = 0;
|
|
1093
|
-
this.tui.requestRender();
|
|
1094
|
-
}
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
if (matchesFleetAction(data, this.keybindings, "toggleTools")) {
|
|
1098
|
-
this.expandedTools = !this.expandedTools;
|
|
1099
|
-
this.transcriptCache = undefined;
|
|
1100
|
-
this.tui.requestRender();
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
private rosterLines(width: number): string[] {
|
|
1105
|
-
if (this.snapshot.items.length === 0) return [this.theme.fg("dim", "No tracked children")];
|
|
1106
|
-
const start = Math.max(0, Math.min(this.selected - this.bodyHeight + 1, Math.max(0, this.snapshot.items.length - this.bodyHeight)));
|
|
1107
|
-
return this.snapshot.items.slice(start, start + this.bodyHeight).map((item, offset) => {
|
|
1108
|
-
const index = start + offset;
|
|
1109
|
-
const marker = index === this.selected ? this.theme.fg("accent", "›") : " ";
|
|
1110
|
-
const context = item.kind === "async" ? contextModeBadge(this.theme, item.step?.context ?? item.run.context) : item.kind === "foreground-recent" ? contextModeBadge(this.theme, item.child.context) : "";
|
|
1111
|
-
const agent = index === this.selected ? this.theme.bold(item.agent) : item.agent;
|
|
1112
|
-
const identity = item.runId.slice(0, 8);
|
|
1113
|
-
const left = `${marker} ${statusGlyph(item, this.theme)} ${agent}${context} ${this.theme.fg("dim", `· ${identity}`)}`;
|
|
1114
|
-
return rightAligned(left, this.theme.fg("dim", item.state), width);
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
private renderedTranscript(target: { path: string; trustedRoots: string[] }, width: number): { transcript: FleetTranscript; body: string[] } {
|
|
1119
|
-
const fingerprint = `${target.trustedRoots.join("\0")}|${transcriptFingerprint(target.path)}`;
|
|
1120
|
-
if (this.transcriptCache
|
|
1121
|
-
&& this.transcriptCache.path === target.path
|
|
1122
|
-
&& this.transcriptCache.fingerprint === fingerprint
|
|
1123
|
-
&& this.transcriptCache.width === width
|
|
1124
|
-
&& this.transcriptCache.expandedTools === this.expandedTools) {
|
|
1125
|
-
return { transcript: this.transcriptCache.transcript, body: [...this.transcriptCache.body] };
|
|
1126
|
-
}
|
|
1127
|
-
const transcript = readFleetTranscript(target.path, { trustedRoots: target.trustedRoots });
|
|
1128
|
-
const body = transcript.events.length > 0
|
|
1129
|
-
? renderFleetTranscript(transcript, width, this.theme, this.markdownTheme, { expandedTools: this.expandedTools })
|
|
1130
|
-
: [];
|
|
1131
|
-
this.transcriptCache = { path: target.path, fingerprint, width, expandedTools: this.expandedTools, transcript, body };
|
|
1132
|
-
return { transcript, body: [...body] };
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
private promptAuditDetail(width: number): FleetDetailSections {
|
|
1136
|
-
const selected = this.snapshot.items[this.selected];
|
|
1137
|
-
const prompt = this.selectedPromptAudit();
|
|
1138
|
-
const items = this.promptAuditItems();
|
|
1139
|
-
const selectedPosition = selected ? items.findIndex(({ item }) => item.key === selected.key) : -1;
|
|
1140
|
-
const live = selected?.kind === "foreground-active" ? selected.activeChild ?? selected.control : undefined;
|
|
1141
|
-
const viewLabel = promptAuditViewLabel(this.promptAuditView);
|
|
1142
|
-
const promptText = this.selectedPromptText()
|
|
1143
|
-
?? this.theme.fg("muted", "Selected prompt unavailable.");
|
|
1144
|
-
const raw = [
|
|
1145
|
-
this.theme.bold("Prompt Audit"),
|
|
1146
|
-
this.theme.fg("dim", "Retention: live memory only · no storage"),
|
|
1147
|
-
selected?.kind === "foreground-active" ? `Run: ${selected.runId}${selected.index !== undefined ? ` · Child: ${selected.index}` : ""} · Agent: ${selected.agent}` : "Selected prompt unavailable",
|
|
1148
|
-
live ? `Started: ${new Date(live.startedAt).toISOString()}` : undefined,
|
|
1149
|
-
live ? `Model: ${formatModelThinking(live.model, live.thinking) || "default"}` : undefined,
|
|
1150
|
-
prompt?.cwd ? `Cwd: ${prompt.cwd}` : undefined,
|
|
1151
|
-
prompt?.outputPath ? `Output: ${prompt.outputPath}` : undefined,
|
|
1152
|
-
this.theme.fg("dim", `Live children: ${items.map(({ item }) => item.agent).join(", ") || "none"}`),
|
|
1153
|
-
this.theme.fg("dim", selectedPosition >= 0 ? `Selected: ${selectedPosition + 1}/${items.length}` : "Selected prompt unavailable"),
|
|
1154
|
-
"",
|
|
1155
|
-
this.theme.fg("accent", viewLabel),
|
|
1156
|
-
promptText,
|
|
1157
|
-
];
|
|
1158
|
-
const body = raw.filter((line): line is string => line !== undefined).flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width)));
|
|
1159
|
-
return { header: [], body: this.withActionLines(body) };
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
private wrappedDetail(width: number): FleetDetailSections {
|
|
1163
|
-
if (this.promptAuditOpen) return this.promptAuditDetail(width);
|
|
1164
|
-
const selected = this.snapshot.items[this.selected];
|
|
1165
|
-
let transcriptWarning: string | undefined;
|
|
1166
|
-
if (selected) {
|
|
1167
|
-
const target = transcriptTarget(selected, this.state);
|
|
1168
|
-
if (target) {
|
|
1169
|
-
const { transcript, body } = this.renderedTranscript(target, width);
|
|
1170
|
-
transcriptWarning = transcript.warning;
|
|
1171
|
-
if (transcript.events.length > 0) {
|
|
1172
|
-
if (this.snapshot.error) body.unshift(this.theme.fg("warning", `Fleet scan warning: ${this.snapshot.error}`), "");
|
|
1173
|
-
const latest = transcript.events.at(-1);
|
|
1174
|
-
const conversationState = latest?.kind === "assistant"
|
|
1175
|
-
? "assistant response"
|
|
1176
|
-
: latest?.kind === "user"
|
|
1177
|
-
? "supervisor message"
|
|
1178
|
-
: latest?.kind === "tool"
|
|
1179
|
-
? `${latest.name} · ${latest.status}`
|
|
1180
|
-
: "activity";
|
|
1181
|
-
const promptSummary = selected.kind === "foreground-active" ? foregroundAuthoredPromptSummary(selected, this.state) : undefined;
|
|
1182
|
-
return { header: structuredHeader(selected, width, this.theme, conversationState, promptSummary), body: this.withActionLines(body) };
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
const raw = detailLines(selected, this.snapshot.error, this.state);
|
|
1188
|
-
if (transcriptWarning) raw.unshift(`Transcript preview warning: ${transcriptWarning}`, "");
|
|
1189
|
-
const lines: string[] = [];
|
|
1190
|
-
for (const line of raw) {
|
|
1191
|
-
const styled = /^(Run|State|Mode|Source|Child|Agent|Model|Task):/.test(line)
|
|
1192
|
-
? this.theme.bold(line)
|
|
1193
|
-
: /^(Transcript|Result transcript tail)/.test(line)
|
|
1194
|
-
? this.theme.fg("accent", line)
|
|
1195
|
-
: /^(Output|Session|Transcript file|Artifacts):/.test(line)
|
|
1196
|
-
? this.theme.fg("muted", line)
|
|
1197
|
-
: /^Transcript preview warning:/.test(line)
|
|
1198
|
-
? this.theme.fg("warning", line)
|
|
1199
|
-
: line;
|
|
1200
|
-
const wrapped = wrapTextWithAnsi(styled, Math.max(1, width));
|
|
1201
|
-
lines.push(...(wrapped.length ? wrapped : [""]));
|
|
1202
|
-
}
|
|
1203
|
-
return { header: [], body: this.withActionLines(lines) };
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
render(width: number): string[] {
|
|
1207
|
-
if (width < 36) return [truncateToWidth("Subagent fleet needs at least 36 columns. Esc closes.", width)];
|
|
1208
|
-
const innerWidth = width - 2;
|
|
1209
|
-
const rows = this.tui.terminal?.rows ?? 32;
|
|
1210
|
-
this.bodyHeight = Math.max(2, Math.floor(rows * 0.85) - 6);
|
|
1211
|
-
const rosterWidth = Math.max(22, Math.min(46, Math.floor((innerWidth - 1) * 0.38)));
|
|
1212
|
-
const detailWidth = Math.max(1, innerWidth - rosterWidth - 1);
|
|
1213
|
-
const roster = this.rosterLines(rosterWidth);
|
|
1214
|
-
const detail = this.wrappedDetail(detailWidth);
|
|
1215
|
-
const detailHeader = detail.header.slice(0, Math.max(0, this.bodyHeight - 1));
|
|
1216
|
-
this.detailViewportHeight = Math.max(1, this.bodyHeight - detailHeader.length);
|
|
1217
|
-
this.detailLineCount = detail.body.length;
|
|
1218
|
-
const maxDetailScroll = Math.max(0, detail.body.length - this.detailViewportHeight);
|
|
1219
|
-
if (this.detailAutoFollow) this.detailScroll = maxDetailScroll;
|
|
1220
|
-
else if (this.detailScroll > maxDetailScroll) this.detailScroll = maxDetailScroll;
|
|
1221
|
-
const visibleDetails = [
|
|
1222
|
-
...detailHeader,
|
|
1223
|
-
...detail.body.slice(this.detailScroll, this.detailScroll + this.detailViewportHeight),
|
|
1224
|
-
];
|
|
1225
|
-
const lines = [this.theme.fg("border", `╭${"─".repeat(innerWidth)}╮`)];
|
|
1226
|
-
const selected = this.snapshot.items[this.selected];
|
|
1227
|
-
const title = selected?.kind === "external"
|
|
1228
|
-
? ` ${this.theme.bold("Fleet inspector")} ${this.theme.fg("dim", "· external display-only")}`
|
|
1229
|
-
: ` ${this.theme.bold("Subagent fleet inspector")} ${this.theme.fg("dim", "· live controls")}`;
|
|
1230
|
-
const selectedStatus = selected
|
|
1231
|
-
? `${statusGlyph(selected, this.theme)} ${selected.agent} · ${selected.state} `
|
|
1232
|
-
: this.theme.fg("dim", "no children ");
|
|
1233
|
-
lines.push(this.theme.fg("border", "│") + rightAligned(title, selectedStatus, innerWidth) + this.theme.fg("border", "│"));
|
|
1234
|
-
lines.push(this.theme.fg("border", `├${"─".repeat(rosterWidth)}┬${"─".repeat(detailWidth)}┤`));
|
|
1235
|
-
for (let index = 0; index < this.bodyHeight; index++) {
|
|
1236
|
-
lines.push(
|
|
1237
|
-
this.theme.fg("border", "│")
|
|
1238
|
-
+ fit(roster[index] ?? "", rosterWidth)
|
|
1239
|
-
+ this.theme.fg("border", "│")
|
|
1240
|
-
+ fit(visibleDetails[index] ?? "", detailWidth)
|
|
1241
|
-
+ this.theme.fg("border", "│"),
|
|
1242
|
-
);
|
|
1243
|
-
}
|
|
1244
|
-
lines.push(this.theme.fg("border", `├${"─".repeat(rosterWidth)}┴${"─".repeat(detailWidth)}┤`));
|
|
1245
|
-
const position = this.snapshot.items.length ? `${this.selected + 1}/${this.snapshot.items.length}` : "0/0";
|
|
1246
|
-
const footer = this.promptAuditOpen
|
|
1247
|
-
? ` j/k child · 1/2/3 view · g redo with guidance · c copy · Esc close Prompt Audit · ${position}`
|
|
1248
|
-
: selected?.kind === "external"
|
|
1249
|
-
? ` ${bindingLabel(this.keybindings, "selectUp")}/${bindingLabel(this.keybindings, "selectDown")} job · display-only · ${bindingLabel(this.keybindings, "refresh")} refresh · ${bindingLabel(this.keybindings, "close")} close · ${position}`
|
|
1250
|
-
: ` ${bindingLabel(this.keybindings, "selectUp")}/${bindingLabel(this.keybindings, "selectDown")} agent · p Prompt Audit · ${bindingLabel(this.keybindings, "inspect")} Herdr · ${bindingLabel(this.keybindings, "steer")} steer · ${bindingLabel(this.keybindings, "stop")} stop · ${bindingLabel(this.keybindings, "toggleTools")} tools · ${bindingLabel(this.keybindings, "refresh")} refresh · ${bindingLabel(this.keybindings, "close")} close · ${position}`;
|
|
1251
|
-
lines.push(this.theme.fg("border", "│") + fit(this.theme.fg("dim", footer), innerWidth) + this.theme.fg("border", "│"));
|
|
1252
|
-
lines.push(this.theme.fg("border", `╰${"─".repeat(innerWidth)}╯`));
|
|
1253
|
-
return lines.map((line) => truncateToWidth(line, width));
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
invalidate(): void {
|
|
1257
|
-
this.transcriptCache = undefined;
|
|
1258
|
-
this.refresh();
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
dispose(): void {
|
|
1262
|
-
this.stopRefresh();
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentState, options: FleetViewOptions = {}): Promise<void> {
|
|
1267
|
-
const wasOpen = state.fleetInspectorOpen === true;
|
|
1268
|
-
state.fleetInspectorOpen = true;
|
|
1269
|
-
if (typeof ctx.ui.setWidget === "function") ctx.ui.setWidget(FLEET_STATUS_WIDGET_KEY, undefined);
|
|
1270
|
-
const copyText = options.copyText ?? (async (text: string) => {
|
|
1271
|
-
const module = await import("@selesai/code");
|
|
1272
|
-
const copyToClipboard = (module as { copyToClipboard?: (value: string) => Promise<void> | void }).copyToClipboard;
|
|
1273
|
-
if (!copyToClipboard) throw new Error("Clipboard is unavailable in this Pi version.");
|
|
1274
|
-
await copyToClipboard(text);
|
|
1275
|
-
});
|
|
1276
|
-
const actions = options.actions ?? {
|
|
1277
|
-
steer: async (input: { runId: string; asyncDir: string; index?: number; message: string; mode: SteerDeliveryMode }) => firstToolResultText(await steerAsyncRun({
|
|
1278
|
-
state,
|
|
1279
|
-
runId: input.runId,
|
|
1280
|
-
...(input.index !== undefined ? { index: input.index } : {}),
|
|
1281
|
-
message: input.message,
|
|
1282
|
-
mode: input.mode,
|
|
1283
|
-
location: { asyncDir: input.asyncDir, resolvedId: input.runId } as Parameters<typeof steerAsyncRun>[0]["location"],
|
|
1284
|
-
}), `Failed to steer async run ${input.runId}.`),
|
|
1285
|
-
stop: (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(stopAsyncRun(state, input.runId, undefined, { asyncDir: input.asyncDir, resolvedId: input.runId }), `Failed to stop async run ${input.runId}.`),
|
|
1286
|
-
inspect: async (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(await handleHerdrInspectorAction("inspector.open", {
|
|
1287
|
-
id: input.runId,
|
|
1288
|
-
dir: input.asyncDir,
|
|
1289
|
-
...(input.index !== undefined ? { index: input.index } : {}),
|
|
1290
|
-
}, {
|
|
1291
|
-
state,
|
|
1292
|
-
sessionRoots: state.trustedSessionRoots,
|
|
1293
|
-
cwd: state.baseCwd,
|
|
1294
|
-
...(state.authorityPolicy ? { authorityPolicy: state.authorityPolicy } : {}),
|
|
1295
|
-
...(state.missionStoreConfig ? { missions: state.missionStoreConfig } : {}),
|
|
1296
|
-
}), `Failed to open Herdr inspector for async run ${input.runId}.`),
|
|
1297
|
-
redoPrompt: async (input: { runId: string; index: number; guidance: string; control?: ForegroundRunControl }) => {
|
|
1298
|
-
const control = input.control ?? state.foregroundControls.get(input.runId);
|
|
1299
|
-
if (!control?.promptAuditRedo) return { text: "Redo is not available for this live child.", isError: true };
|
|
1300
|
-
return control.promptAuditRedo(input.index, input.guidance);
|
|
1301
|
-
},
|
|
1302
|
-
} satisfies FleetActionHandlers;
|
|
1303
|
-
try {
|
|
1304
|
-
await ctx.ui.custom<undefined>(
|
|
1305
|
-
(tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, { ...options, actions, copyText }),
|
|
1306
|
-
{
|
|
1307
|
-
overlay: true,
|
|
1308
|
-
overlayOptions: { anchor: "center", width: "95%", minWidth: 60, maxHeight: "85%", margin: 1 },
|
|
1309
|
-
},
|
|
1310
|
-
);
|
|
1311
|
-
} finally {
|
|
1312
|
-
state.fleetInspectorOpen = wasOpen;
|
|
1313
|
-
}
|
|
1314
|
-
}
|