@pellux/goodvibes-tui 2.0.21 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -1
- package/README.md +30 -29
- package/docs/README.md +8 -5
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +28 -19
- package/src/cli/management.ts +5 -2
- package/src/cli/tui-startup.ts +34 -39
- package/src/core/agent-view-render.ts +157 -0
- package/src/core/approval-alert.ts +49 -11
- package/src/core/bookmark-navigation.ts +7 -1
- package/src/core/budget-breach-notifier.ts +27 -8
- package/src/core/compaction-receipt.ts +1 -1
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-display-only.ts +86 -0
- package/src/core/conversation-event-navigation.ts +27 -0
- package/src/core/conversation-line-cache.ts +216 -447
- package/src/core/conversation-render-context.ts +20 -27
- package/src/core/conversation-rendering.ts +157 -575
- package/src/core/conversation-splash-state.ts +6 -1
- package/src/core/conversation-types.ts +35 -0
- package/src/core/conversation-usage.ts +12 -0
- package/src/core/conversation.ts +130 -131
- package/src/core/long-task-notifier.ts +100 -31
- package/src/core/notices.ts +75 -0
- package/src/core/pairing-tailscale-gateway.ts +1 -1
- package/src/core/process-output-log.ts +143 -0
- package/src/core/rewind-receipt.ts +1 -1
- package/src/core/session-focus.ts +190 -0
- package/src/core/session-resume-core.ts +23 -81
- package/src/core/stream-event-wiring.ts +31 -5
- package/src/core/system-message-noise.ts +2 -2
- package/src/core/system-message-router.ts +16 -18
- package/src/core/turn-event-wiring.ts +272 -33
- package/src/core/work-tree-focus.ts +290 -0
- package/src/core/work-tree-fold-store.ts +143 -0
- package/src/core/work-tree-model.ts +799 -0
- package/src/core/work-tree-render.ts +384 -0
- package/src/core/work-tree-sources.ts +184 -0
- package/src/core/work-tree-wiring.ts +176 -0
- package/src/core/workstream-notification.ts +36 -6
- package/src/core/wrfc-notice-titles.ts +37 -0
- package/src/input/agents-modal-text.ts +119 -0
- package/src/input/agents-modal.ts +699 -0
- package/src/input/bookmark-modal.ts +33 -9
- package/src/input/changes-git.ts +251 -0
- package/src/input/changes-modal.ts +629 -0
- package/src/input/changes-model.ts +125 -0
- package/src/input/command-palette-types.ts +41 -0
- package/src/input/command-palette.ts +272 -0
- package/src/input/command-reference.ts +2 -2
- package/src/input/command-registry.ts +77 -35
- package/src/input/commands/acp-runtime.ts +19 -9
- package/src/input/commands/channel-runtime.ts +2 -2
- package/src/input/commands/ci-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/config.ts +7 -1
- package/src/input/commands/context-window.ts +25 -19
- package/src/input/commands/control-room-runtime.ts +18 -18
- package/src/input/commands/conversation-runtime.ts +0 -1
- package/src/input/commands/cost-runtime.ts +29 -20
- package/src/input/commands/diff-runtime.ts +35 -181
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -7
- package/src/input/commands/fresh-conversation.ts +86 -0
- package/src/input/commands/git-runtime.ts +5 -22
- package/src/input/commands/graph-runtime.ts +1 -1
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +8 -9
- package/src/input/commands/hooks-runtime.ts +2 -2
- package/src/input/commands/hosted-runtime.ts +3 -3
- package/src/input/commands/incident-runtime.ts +3 -3
- package/src/input/commands/intelligence-runtime.ts +6 -9
- package/src/input/commands/knowledge.ts +2 -2
- package/src/input/commands/legacy-panel-command.ts +57 -0
- package/src/input/commands/local-auth-runtime.ts +8 -9
- package/src/input/commands/local-provider-runtime.ts +7 -4
- package/src/input/commands/local-runtime.ts +12 -15
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup.ts +13 -19
- package/src/input/commands/memory.ts +1 -1
- package/src/input/commands/notify-runtime.ts +9 -0
- package/src/input/commands/operator-runtime.ts +10 -12
- package/src/input/commands/permissions-provenance.ts +3 -3
- package/src/input/commands/planning-runtime.ts +7 -7
- package/src/input/commands/platform-sandbox-runtime.ts +2 -2
- package/src/input/commands/platform-services-runtime.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +2 -2
- package/src/input/commands/policy.ts +3 -3
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/review-runtime.ts +39 -94
- package/src/input/commands/rewind-runtime.ts +81 -108
- package/src/input/commands/runtime-services.ts +15 -17
- package/src/input/commands/services-runtime.ts +2 -2
- package/src/input/commands/session-content.ts +21 -5
- package/src/input/commands/session-workflow.ts +13 -38
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/shell-core.ts +19 -4
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/status-runtime.ts +13 -0
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +2 -8
- package/src/input/commands/work-plan-runtime.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +24 -47
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands/worktree-runtime.ts +3 -3
- package/src/input/commands.ts +3 -1
- package/src/input/composer-editor.ts +44 -0
- package/src/input/concealed-input.ts +1 -1
- package/src/input/config-modal-types.ts +20 -8
- package/src/input/config-modal.ts +106 -105
- package/src/input/confirm-dialog-types.ts +28 -0
- package/src/input/confirm-dialog.ts +70 -0
- package/src/input/delete-key-policy.ts +3 -3
- package/src/input/feed-context-factory.ts +15 -16
- package/src/input/handler-command-route.ts +7 -42
- package/src/input/handler-content-actions.ts +16 -15
- package/src/input/handler-feed-routes.ts +26 -211
- package/src/input/handler-feed.ts +68 -95
- package/src/input/handler-interactions.ts +5 -3
- package/src/input/handler-modal-routes.ts +215 -216
- package/src/input/handler-modal-stack.ts +81 -11
- package/src/input/handler-modal-token-routes.ts +4 -0
- package/src/input/handler-onboarding.ts +0 -1
- package/src/input/handler-picker-routes.ts +65 -107
- package/src/input/handler-session-view-route.ts +149 -0
- package/src/input/handler-shortcuts.ts +16 -154
- package/src/input/handler-types.ts +4 -2
- package/src/input/handler-ui-state.ts +45 -15
- package/src/input/handler-work-tree-route.ts +123 -0
- package/src/input/handler.ts +25 -70
- package/src/input/keybindings.ts +16 -71
- package/src/input/masked-entry-modal.ts +117 -0
- package/src/input/mcp-workspace.ts +48 -17
- package/src/input/model-picker-filter.ts +8 -0
- package/src/input/model-picker-open.ts +99 -0
- package/src/input/model-picker-types.ts +1 -1
- package/src/input/model-picker.ts +92 -9
- package/src/input/notifications-modal.ts +123 -0
- package/src/input/overlay-filter.ts +26 -0
- package/src/input/profile-picker-modal.ts +37 -8
- package/src/input/search.ts +3 -3
- package/src/input/selection-modal-queue.ts +2 -0
- package/src/input/selection-modal.ts +28 -16
- package/src/input/session-picker-modal.ts +33 -8
- package/src/input/settings-modal-activation.ts +7 -2
- package/src/input/settings-modal-data.ts +11 -6
- package/src/input/settings-modal-types.ts +1 -1
- package/src/input/settings-modal.ts +20 -20
- package/src/input/submission-intent.ts +1 -1
- package/src/input/submission-router.ts +4 -9
- package/src/input/surface-modal-host.ts +110 -0
- package/src/input/theme-settings-actions.ts +82 -0
- package/src/input/usage-modal.ts +183 -0
- package/src/input/views.ts +97 -0
- package/src/main.ts +175 -165
- package/src/permissions/hunk-selection.ts +1 -1
- package/src/permissions/prompt-card.ts +511 -0
- package/src/permissions/prompt.ts +59 -622
- package/src/renderer/agents-modal.ts +310 -0
- package/src/renderer/autocomplete-overlay.ts +42 -220
- package/src/renderer/back-to-bottom.ts +80 -0
- package/src/renderer/block-actions-overlay.ts +30 -48
- package/src/renderer/bookmark-modal.ts +48 -79
- package/src/renderer/changes-modal.ts +325 -0
- package/src/renderer/code-block.ts +184 -106
- package/src/renderer/command-palette.ts +93 -0
- package/src/renderer/composer-fragments.ts +4 -4
- package/src/renderer/composer.ts +146 -0
- package/src/renderer/compositor.ts +26 -160
- package/src/renderer/config-modal.ts +185 -81
- package/src/renderer/confirm-dialog.ts +50 -0
- package/src/renderer/context-inspector.ts +116 -154
- package/src/renderer/context-status-hint.ts +4 -4
- package/src/renderer/conversation-layout.ts +20 -0
- package/src/renderer/conversation-overlays.ts +94 -84
- package/src/renderer/conversation-surface.ts +19 -38
- package/src/renderer/diff-tint.ts +98 -0
- package/src/renderer/file-picker-overlay.ts +63 -90
- package/src/renderer/footer-targets.ts +38 -0
- package/src/renderer/git-status.ts +3 -4
- package/src/renderer/header-line.ts +140 -117
- package/src/renderer/help-overlay.ts +133 -219
- package/src/renderer/history-search-overlay.ts +15 -12
- package/src/renderer/lane-graph/bead.ts +800 -0
- package/src/renderer/lane-graph/glyphs.ts +134 -0
- package/src/renderer/lane-graph/layout.ts +248 -0
- package/src/renderer/lane-graph/paint.ts +537 -0
- package/src/renderer/lane-graph/semantic-memo.ts +77 -0
- package/src/renderer/layout-engine.ts +2 -57
- package/src/renderer/layout.ts +7 -13
- package/src/renderer/markdown-inline.ts +2 -1
- package/src/renderer/markdown-table.ts +17 -12
- package/src/renderer/markdown.ts +92 -34
- package/src/renderer/masked-entry-modal.ts +78 -0
- package/src/renderer/mcp-workspace.ts +166 -203
- package/src/renderer/model-workspace.ts +381 -436
- package/src/renderer/notifications-modal.ts +116 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
- package/src/renderer/process-view.ts +192 -0
- package/src/renderer/profile-picker-modal.ts +58 -110
- package/src/renderer/search-overlay.ts +6 -3
- package/src/renderer/selection-modal-overlay.ts +87 -327
- package/src/renderer/semantic-diff.ts +2 -2
- package/src/renderer/session-chips.ts +102 -0
- package/src/renderer/session-picker-modal.ts +177 -297
- package/src/renderer/settings-modal-connections.ts +14 -23
- package/src/renderer/settings-modal-context.ts +367 -0
- package/src/renderer/settings-modal-helpers.ts +6 -11
- package/src/renderer/settings-modal.ts +322 -726
- package/src/renderer/shell-surface.ts +277 -175
- package/src/renderer/shortcuts-overlay.ts +217 -0
- package/src/renderer/status-line.ts +475 -0
- package/src/renderer/surface-compose.ts +174 -0
- package/src/renderer/surface-kit-extra.ts +303 -0
- package/src/renderer/surface-kit-list.ts +366 -0
- package/src/renderer/surface-kit-parts.ts +360 -0
- package/src/renderer/surface-kit.ts +568 -0
- package/src/renderer/syntax-highlighter.ts +280 -197
- package/src/renderer/syntax-theme.ts +51 -0
- package/src/renderer/system-message.ts +0 -1
- package/src/renderer/term-caps.ts +42 -1
- package/src/renderer/terminal-bg-probe.ts +116 -19
- package/src/renderer/terminal-palette-probe.ts +322 -0
- package/src/renderer/terminal-palette-reader.ts +183 -0
- package/src/renderer/terminal-palette.ts +58 -0
- package/src/renderer/theme-mode-config.ts +16 -1
- package/src/renderer/theme.ts +234 -207
- package/src/renderer/thinking.ts +0 -1
- package/src/renderer/throbber.ts +188 -0
- package/src/renderer/toast-center.ts +119 -0
- package/src/renderer/ui-factory.ts +39 -602
- package/src/renderer/ui-primitives.ts +8 -25
- package/src/renderer/usage-modal.ts +353 -0
- package/src/renderer/user-message.ts +58 -0
- package/src/renderer/voice-capture-chip.ts +53 -0
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +38 -89
- package/src/runtime/bootstrap-core.ts +13 -15
- package/src/runtime/bootstrap-hook-bridge.ts +12 -21
- package/src/runtime/bootstrap-notifier-sync.ts +17 -1
- package/src/runtime/bootstrap-shell.ts +16 -50
- package/src/runtime/bootstrap.ts +14 -14
- package/src/runtime/client/fleet-union.ts +5 -5
- package/src/runtime/client/hosted-exit.ts +2 -2
- package/src/runtime/client/hosted-sessions.ts +1 -1
- package/src/runtime/client/spine-adoption.ts +1 -1
- package/src/runtime/context-accounting-source.ts +5 -4
- package/src/runtime/index.ts +1 -0
- package/src/runtime/notification-dispatch.ts +102 -29
- package/src/runtime/recovery-offer-wiring.ts +4 -1
- package/src/runtime/recovery-prompt.ts +31 -15
- package/src/runtime/runtime-services-types.ts +10 -6
- package/src/runtime/services.ts +17 -12
- package/src/runtime/session-continuity-hints.ts +2 -5
- package/src/runtime/session-inbound-inputs.ts +1 -1
- package/src/runtime/session-resume-liveness-confirm.ts +14 -2
- package/src/runtime/startup-typeahead-gate.ts +103 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
- package/src/runtime/store/domains/index.ts +0 -8
- package/src/runtime/store/selectors/index.ts +0 -28
- package/src/runtime/store/state.ts +0 -8
- package/src/runtime/ui-services.ts +4 -6
- package/src/runtime/usage-tracker.ts +348 -0
- package/src/runtime/wrfc-persistence.ts +4 -4
- package/src/shell/blocking-input.ts +92 -2
- package/src/shell/recovery-input-helpers.ts +1 -1
- package/src/shell/session-views.ts +539 -0
- package/src/shell/status-report.ts +86 -0
- package/src/shell/transcript-scroll.ts +68 -0
- package/src/shell/ui-openers.ts +84 -172
- package/src/shell/view-openers.ts +205 -0
- package/src/tools/wrfc-agent-guard.ts +22 -2
- package/src/utils/format-duration.ts +4 -8
- package/src/utils/splash-lines.ts +9 -1
- package/src/utils/terminal-width.ts +56 -0
- package/src/verification/live-verifier.ts +0 -10
- package/src/version.ts +1 -1
- package/src/{panels → views}/agent-inspector-shared.ts +2 -2
- package/src/{panels → views}/builtin-modals.ts +22 -21
- package/src/views/builtin-views.ts +135 -0
- package/src/{panels → views}/confirm-state.ts +9 -9
- package/src/{panels → views}/diff-review-model.ts +10 -3
- package/src/{panels → views}/eval-registry.ts +1 -1
- package/src/{panels → views}/fleet-acts.ts +49 -85
- package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
- package/src/{panels → views}/fleet-gateway.ts +5 -5
- package/src/{panels → views}/fleet-observed-render.ts +16 -16
- package/src/{panels → views}/fleet-read-model.ts +9 -9
- package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
- package/src/{panels → views}/fleet-spawn.ts +33 -49
- package/src/{panels → views}/fleet-steer.ts +12 -12
- package/src/{panels → views}/fleet-stop.ts +4 -4
- package/src/{panels → views}/fleet-transcript.ts +26 -27
- package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
- package/src/{panels → views}/hosted-session-feed.ts +6 -6
- package/src/views/modal-surface-registry.ts +41 -0
- package/src/{panels → views}/modals/devices-modal.ts +2 -2
- package/src/{panels → views}/modals/hooks-modal.ts +13 -13
- package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
- package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
- package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
- package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
- package/src/{panels → views}/modals/memory-modal.ts +8 -8
- package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
- package/src/{panels → views}/modals/pairing-modal.ts +9 -9
- package/src/{panels → views}/modals/planning-modal.ts +15 -20
- package/src/{panels → views}/modals/plugins-modal.ts +6 -6
- package/src/{panels → views}/modals/policy-modal.ts +13 -5
- package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
- package/src/{panels → views}/modals/remote-modal.ts +3 -3
- package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
- package/src/{panels → views}/modals/security-modal.ts +22 -16
- package/src/{panels → views}/modals/services-modal.ts +3 -3
- package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
- package/src/{panels → views}/modals/skills-modal.ts +9 -9
- package/src/{panels → views}/modals/subscription-modal.ts +3 -3
- package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
- package/src/views/notifications-feed.ts +314 -0
- package/src/{panels → views}/polish-core.ts +38 -36
- package/src/{panels → views}/polish-tables.ts +35 -26
- package/src/{panels → views}/polish.ts +101 -95
- package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
- package/src/{panels → views}/provider-health-routes.ts +14 -14
- package/src/{panels → views}/search-focus.ts +8 -8
- package/src/{panels → views}/session-maintenance.ts +11 -11
- package/src/views/skills-discovery.ts +159 -0
- package/src/views/view-deps.ts +80 -0
- package/src/views/view-panel-adapter.ts +53 -0
- package/src/{panels → views}/workstream-graph-render.ts +1 -1
- package/src/core/conversation-fold.ts +0 -101
- package/src/core/conversation-turn-structure.ts +0 -464
- package/src/core/conversation-user-receipts.ts +0 -42
- package/src/input/commands/operator-panel-runtime.ts +0 -186
- package/src/input/panel-integration-actions.ts +0 -33
- package/src/input/panel-mouse-geometry.ts +0 -97
- package/src/input/panel-paste-flood-guard.ts +0 -86
- package/src/panels/base-panel.ts +0 -301
- package/src/panels/builtin/agent.ts +0 -27
- package/src/panels/builtin/development.ts +0 -92
- package/src/panels/builtin/knowledge.ts +0 -12
- package/src/panels/builtin/operations.ts +0 -177
- package/src/panels/builtin/session.ts +0 -81
- package/src/panels/builtin/shared.ts +0 -357
- package/src/panels/builtin-panels.ts +0 -28
- package/src/panels/cost-tracker-panel.ts +0 -746
- package/src/panels/diff-panel.ts +0 -797
- package/src/panels/diff-review-panel.ts +0 -395
- package/src/panels/expandable-list-panel.ts +0 -189
- package/src/panels/fleet-deep-link.ts +0 -31
- package/src/panels/fleet-panel.ts +0 -799
- package/src/panels/git-panel.ts +0 -800
- package/src/panels/hosted-session-panel.ts +0 -221
- package/src/panels/index.ts +0 -21
- package/src/panels/local-auth-panel.ts +0 -478
- package/src/panels/notifications-feed.ts +0 -127
- package/src/panels/notifications-panel.ts +0 -160
- package/src/panels/panel-confirm-overlay.ts +0 -72
- package/src/panels/panel-manager.ts +0 -791
- package/src/panels/plugins-panel.ts +0 -371
- package/src/panels/scrollable-list-panel.ts +0 -498
- package/src/panels/skills-panel.ts +0 -454
- package/src/panels/token-budget-panel.ts +0 -688
- package/src/panels/types.ts +0 -191
- package/src/panels/wrfc-panel-format.ts +0 -133
- package/src/renderer/diff-view.ts +0 -134
- package/src/renderer/fleet-tab-strip.ts +0 -56
- package/src/renderer/footer-tips.ts +0 -53
- package/src/renderer/fullscreen-primitives.ts +0 -146
- package/src/renderer/fullscreen-workspace.ts +0 -194
- package/src/renderer/hint-grammar.ts +0 -52
- package/src/renderer/modal-factory.ts +0 -480
- package/src/renderer/overlay-box.ts +0 -154
- package/src/renderer/overlay-viewport.ts +0 -104
- package/src/renderer/panel-composite.ts +0 -145
- package/src/renderer/panel-workspace-bar.ts +0 -54
- package/src/renderer/process-indicator.ts +0 -163
- package/src/renderer/tab-strip.ts +0 -222
- package/src/renderer/text-layout.ts +0 -31
- package/src/renderer/tool-call.ts +0 -276
- package/src/renderer/tool-result-expanded-lines.ts +0 -48
- package/src/renderer/tool-result-summary.ts +0 -219
- package/src/runtime/diagnostics/panels/index.ts +0 -21
- package/src/runtime/diagnostics/panels/ops.ts +0 -156
- package/src/runtime/perf/panel-contracts.ts +0 -32
- package/src/runtime/store/domains/panels.ts +0 -117
- package/src/verification/verification-ledger.ts +0 -707
- /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
- /package/src/{panels → views}/modals/modal-theme.ts +0 -0
- /package/src/{panels → views}/provider-health-domains.ts +0 -0
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-acts.ts
|
|
3
3
|
//
|
|
4
|
-
// The
|
|
5
|
-
// conflict row, and the worktree discard all act from the
|
|
6
|
-
// no id ever typed. This controller owns that flow
|
|
7
|
-
// the
|
|
8
|
-
// pick-mode input, and the pick-mode render here.
|
|
4
|
+
// The waiting-on-human ACTS in the Agents modal: the flagged pick row, the
|
|
5
|
+
// flagged conflict row, and the worktree discard all act from the selected row
|
|
6
|
+
// with no id ever typed. This controller owns that flow; the Agents modal
|
|
7
|
+
// delegates the trigger keys and the pick-mode input here and draws pickView().
|
|
9
8
|
//
|
|
10
9
|
// • Pick , the flagged workstream row (needsAttention 'pick') opens a
|
|
11
10
|
// candidate picker (best-of-N held attempts, from fleet.attempts.list);
|
|
12
|
-
// ↑↓ chooses the winner, its diff
|
|
11
|
+
// ↑↓ chooses the winner, Enter shows its diff in a Changes preview and
|
|
13
12
|
// drives fleet.attempts.pick preview (confirm:false) -> confirm (confirm:true)
|
|
14
|
-
// through the
|
|
15
|
-
//
|
|
13
|
+
// through the question on that preview. No group/candidate id is ever
|
|
14
|
+
// typed; they come from the node and the selection.
|
|
16
15
|
// • Conflict, the flagged work-item row (needsAttention 'conflict') runs
|
|
17
16
|
// fleet.conflicts.resolve and hands the STAMPED resolution session id to the
|
|
18
17
|
// shared one-key jump/attach affordance (the CI fix-session machinery). On
|
|
@@ -25,17 +24,9 @@
|
|
|
25
24
|
import type { ProcessNode } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
26
25
|
import type { WorkItem } from '@pellux/goodvibes-sdk/platform/orchestration';
|
|
27
26
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
28
|
-
import
|
|
29
|
-
import {
|
|
30
|
-
import { appendSteerText } from './fleet-tabs.ts';
|
|
27
|
+
import { isViewSearchBackspace, isViewSearchCancel, isViewSearchCommit, isViewSearchPrintable } from './search-focus.ts';
|
|
28
|
+
import { appendSteerText } from './fleet-session-tabs.ts';
|
|
31
29
|
import { isObservedExternalNode, observedKindLabel, type ObservedNode } from './fleet-observed-render.ts';
|
|
32
|
-
import {
|
|
33
|
-
buildKeyboardHints,
|
|
34
|
-
buildPanelWorkspace,
|
|
35
|
-
DEFAULT_PANEL_PALETTE,
|
|
36
|
-
type PanelPalette,
|
|
37
|
-
} from './polish.ts';
|
|
38
|
-
import { buildPanelLine } from './polish.ts';
|
|
39
30
|
import { formatAgentCost } from './agent-inspector-shared.ts';
|
|
40
31
|
import { fleetNodeAttention } from './fleet-read-model.ts';
|
|
41
32
|
import {
|
|
@@ -48,11 +39,11 @@ import {
|
|
|
48
39
|
type FleetHeldMergeGroup,
|
|
49
40
|
} from './fleet-gateway.ts';
|
|
50
41
|
|
|
51
|
-
/** The
|
|
42
|
+
/** The Changes-preview surface the pick act reuses: show a diff, ask over it, close it. */
|
|
52
43
|
export interface FleetDiffSurface {
|
|
53
|
-
/**
|
|
44
|
+
/** Show this unified diff in a Changes preview (title labels the candidate). */
|
|
54
45
|
show(title: string, unifiedDiff: string): void;
|
|
55
|
-
/**
|
|
46
|
+
/** Ask a yes/no question over the preview (or in a confirm dialog when none is open). */
|
|
56
47
|
armConfirm(opts: {
|
|
57
48
|
readonly id: string;
|
|
58
49
|
readonly label: string;
|
|
@@ -60,14 +51,14 @@ export interface FleetDiffSurface {
|
|
|
60
51
|
readonly onConfirm: () => void | Promise<void>;
|
|
61
52
|
readonly onCancel?: () => void;
|
|
62
53
|
}): void;
|
|
63
|
-
/** Close the
|
|
54
|
+
/** Close the preview. */
|
|
64
55
|
close(): void;
|
|
65
56
|
}
|
|
66
57
|
|
|
67
58
|
export interface FleetActsDeps {
|
|
68
59
|
/** Resolve a live gateway per act (so a daemon that comes up mid-session is seen); honest unavailable reason otherwise. */
|
|
69
60
|
readonly resolveGateway: () => FleetGatewayResolution;
|
|
70
|
-
/** The
|
|
61
|
+
/** The Changes preview surface (candidate diffs + the pick confirm). */
|
|
71
62
|
readonly diffSurface: FleetDiffSurface;
|
|
72
63
|
/** Surface a result/receipt/error line to the operator (system message, high priority). */
|
|
73
64
|
readonly notify: (message: string) => void;
|
|
@@ -85,7 +76,15 @@ interface PickMode {
|
|
|
85
76
|
selectedHeldIndex: number;
|
|
86
77
|
}
|
|
87
78
|
|
|
88
|
-
|
|
79
|
+
/** The candidate picker, as data the Agents modal draws. */
|
|
80
|
+
export interface FleetPickView {
|
|
81
|
+
readonly title: string;
|
|
82
|
+
readonly candidates: ReadonlyArray<{ readonly label: string; readonly detail: string; readonly selected: boolean }>;
|
|
83
|
+
/** The selected candidate's unified diff, or null when it has none. */
|
|
84
|
+
readonly diff: string | null;
|
|
85
|
+
/** The model's advisory proposal, or null. */
|
|
86
|
+
readonly proposal: string | null;
|
|
87
|
+
}
|
|
89
88
|
|
|
90
89
|
function shortId(id: string): string {
|
|
91
90
|
return id.length > 10 ? `${id.slice(0, 10)}…` : id;
|
|
@@ -120,7 +119,7 @@ export class FleetActs {
|
|
|
120
119
|
/**
|
|
121
120
|
* Dispatch an act-trigger key pressed on the tree while `node` is selected.
|
|
122
121
|
* Returns true when consumed (an act fired), false to fall through to the
|
|
123
|
-
*
|
|
122
|
+
* Agents modal's ordinary key handling (so Enter still attaches an agent tab, etc.).
|
|
124
123
|
*/
|
|
125
124
|
public handleTreeKey(key: string, node: ProcessNode): boolean {
|
|
126
125
|
if (key === 'enter' || key === 'return') {
|
|
@@ -172,10 +171,10 @@ export class FleetActs {
|
|
|
172
171
|
/** Input while the observed-steer composer is open (mirrors the tab steer composer). */
|
|
173
172
|
public handleObservedSteerInput(key: string): boolean {
|
|
174
173
|
if (!this.observedSteer) return false;
|
|
175
|
-
if (
|
|
176
|
-
if (
|
|
177
|
-
if (
|
|
178
|
-
if (key.length === 1 && (
|
|
174
|
+
if (isViewSearchCancel(key)) { this.observedSteer = null; this.deps.markDirty(); return true; }
|
|
175
|
+
if (isViewSearchCommit(key)) { void this.submitObservedSteer(); return true; }
|
|
176
|
+
if (isViewSearchBackspace(key)) { this.observedSteer.draft = this.observedSteer.draft.slice(0, -1); this.deps.markDirty(); return true; }
|
|
177
|
+
if (key.length === 1 && (isViewSearchPrintable(key) || key === '\r' || key === '\n')) {
|
|
179
178
|
this.observedSteer.draft = appendSteerText(this.observedSteer.draft, key);
|
|
180
179
|
this.deps.markDirty();
|
|
181
180
|
return true;
|
|
@@ -203,11 +202,11 @@ export class FleetActs {
|
|
|
203
202
|
}
|
|
204
203
|
}
|
|
205
204
|
|
|
206
|
-
// ── Task-graph posture (in-
|
|
205
|
+
// ── Task-graph posture (in-view edges/pool under a workstream) ───────────
|
|
207
206
|
|
|
208
207
|
/**
|
|
209
208
|
* Lazily fetch the task graph (fleet.graph.get) for a selected workstream row
|
|
210
|
-
* so the in-
|
|
209
|
+
* so the in-view detail can render its edges/pool posture WITHOUT opening
|
|
211
210
|
* /graph. Idempotent: fetches once per node (cache + in-flight guard), quiet
|
|
212
211
|
* on an unavailable daemon (caches null rather than nagging every frame). A
|
|
213
212
|
* no-op for any non-workstream node.
|
|
@@ -248,7 +247,6 @@ export class FleetActs {
|
|
|
248
247
|
}
|
|
249
248
|
if (!group) { this.deps.notify('No ready best-of-N group on this workstream; every attempt must settle first.'); return; }
|
|
250
249
|
this.pick = { workstreamNodeId: node.id, group, selectedHeldIndex: 0 };
|
|
251
|
-
this.showSelectedDiff();
|
|
252
250
|
this.deps.markDirty();
|
|
253
251
|
}
|
|
254
252
|
|
|
@@ -259,13 +257,11 @@ export class FleetActs {
|
|
|
259
257
|
if (key === 'escape' || key === 'esc') { this.pick = null; this.deps.markDirty(); return true; }
|
|
260
258
|
if (key === 'up' || key === 'k') {
|
|
261
259
|
this.pick.selectedHeldIndex = (this.pick.selectedHeldIndex - 1 + held.length) % held.length;
|
|
262
|
-
this.showSelectedDiff();
|
|
263
260
|
this.deps.markDirty();
|
|
264
261
|
return true;
|
|
265
262
|
}
|
|
266
263
|
if (key === 'down' || key === 'j') {
|
|
267
264
|
this.pick.selectedHeldIndex = (this.pick.selectedHeldIndex + 1) % held.length;
|
|
268
|
-
this.showSelectedDiff();
|
|
269
265
|
this.deps.markDirty();
|
|
270
266
|
return true;
|
|
271
267
|
}
|
|
@@ -273,18 +269,9 @@ export class FleetActs {
|
|
|
273
269
|
return true; // absorb every other key while the picker owns the view
|
|
274
270
|
}
|
|
275
271
|
|
|
276
|
-
private showSelectedDiff(): void {
|
|
277
|
-
if (!this.pick) return;
|
|
278
|
-
const cand = heldCandidates(this.pick.group)[this.pick.selectedHeldIndex];
|
|
279
|
-
if (!cand) return;
|
|
280
|
-
const diff = cand.diff?.unifiedDiff?.trim();
|
|
281
|
-
if (diff) this.deps.diffSurface.show(cand.title, cand.diff!.unifiedDiff);
|
|
282
|
-
else this.deps.diffSurface.show(cand.title, '@@ pick @@\n (no diff to preview for this candidate)');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
272
|
/**
|
|
286
|
-
* Drive fleet.attempts.pick preview (confirm:false) then, behind the
|
|
287
|
-
*
|
|
273
|
+
* Drive fleet.attempts.pick preview (confirm:false) then, behind the question
|
|
274
|
+
* on the candidate's Changes preview, confirm (confirm:true). No id is typed, the group id and
|
|
288
275
|
* the winner item id both come from the picker state.
|
|
289
276
|
*/
|
|
290
277
|
private async confirmSelectedPick(): Promise<void> {
|
|
@@ -394,48 +381,25 @@ export class FleetActs {
|
|
|
394
381
|
return true;
|
|
395
382
|
}
|
|
396
383
|
|
|
397
|
-
// ──
|
|
384
|
+
// ── Pick mode, as data ────────────────────────────────────────────────────
|
|
398
385
|
|
|
399
|
-
/** The candidate picker
|
|
400
|
-
public
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const { group } = this.pick;
|
|
386
|
+
/** The candidate picker while pick mode is active, else null. */
|
|
387
|
+
public pickView(): FleetPickView | null {
|
|
388
|
+
if (!this.pick) return null;
|
|
389
|
+
const { group, selectedHeldIndex } = this.pick;
|
|
404
390
|
const held = heldCandidates(group);
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
: 'unpriced';
|
|
418
|
-
lines.push(buildPanelLine(width, [
|
|
419
|
-
[selected ? ' ▸ ' : ' ', selected ? P.info : P.dim],
|
|
420
|
-
[`${cand.attemptIndex + 1}. `, P.label],
|
|
421
|
-
[cand.title, selected ? P.value : P.dim],
|
|
422
|
-
[`, ${files}, ${cost}`, P.dim],
|
|
423
|
-
]));
|
|
424
|
-
});
|
|
425
|
-
if (group.judgment?.proposedWinnerItemId) {
|
|
426
|
-
const proposed = held.find((c) => c.itemId === group.judgment!.proposedWinnerItemId);
|
|
427
|
-
lines.push(buildPanelLine(width, [['', P.dim]]));
|
|
428
|
-
lines.push(buildPanelLine(width, [
|
|
429
|
-
[' judge proposal (model, advisory): ', P.label],
|
|
430
|
-
[proposed ? `attempt ${proposed.attemptIndex + 1}` : shortId(group.judgment.proposedWinnerItemId), P.info],
|
|
431
|
-
]));
|
|
432
|
-
}
|
|
433
|
-
const footerLines = [buildKeyboardHints(width, [
|
|
434
|
-
{ keys: '↑↓', label: 'choose' },
|
|
435
|
-
{ keys: 'Enter', label: 'pick (confirm)' },
|
|
436
|
-
{ keys: 'Esc', label: 'cancel' },
|
|
437
|
-
], P)];
|
|
438
|
-
return buildPanelWorkspace(width, height, { title: 'Fleet: Pick winner', sections: [{ lines }], footerLines, palette: P });
|
|
391
|
+
const proposedId = group.judgment?.proposedWinnerItemId;
|
|
392
|
+
const proposed = proposedId ? held.find((c) => c.itemId === proposedId) : undefined;
|
|
393
|
+
return {
|
|
394
|
+
title: group.sourceTitle,
|
|
395
|
+
candidates: held.map((cand, index) => {
|
|
396
|
+
const files = cand.diff ? `${cand.diff.files.length} file${cand.diff.files.length === 1 ? '' : 's'}` : 'no diff';
|
|
397
|
+
const cost = cand.usage.costUsd !== null && cand.usage.costState !== 'unpriced' ? formatAgentCost(cand.usage.costUsd) : 'unpriced';
|
|
398
|
+
return { label: `${cand.attemptIndex + 1}. ${cand.title}`, detail: `${files} · ${cost}`, selected: index === selectedHeldIndex };
|
|
399
|
+
}),
|
|
400
|
+
proposal: proposedId ? (proposed ? `attempt ${proposed.attemptIndex + 1}` : shortId(proposedId)) : null,
|
|
401
|
+
diff: held[selectedHeldIndex]?.diff?.unifiedDiff?.trim() ? held[selectedHeldIndex]!.diff!.unifiedDiff : null,
|
|
402
|
+
};
|
|
439
403
|
}
|
|
440
404
|
|
|
441
405
|
private requireGateway(): FleetGateway | null {
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// fleet-
|
|
2
|
+
// fleet-format.ts
|
|
3
3
|
//
|
|
4
|
-
// Pure column-layout + cell-formatting helpers for
|
|
5
|
-
// Extracted from
|
|
6
|
-
// 800-line architecture cap; these are stateless functions the
|
|
4
|
+
// Pure column-layout + cell-formatting helpers for the Agents modal's tree rows.
|
|
5
|
+
// Extracted from src/input/agents-modal.ts (file-size hygiene) to hold that file under the
|
|
6
|
+
// 800-line architecture cap; these are stateless functions the Agents modal's
|
|
7
7
|
// renderItem/renderDetail call, with no `this` dependency.
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
import type { ProcessCostState, ProcessNode, ProcessReviewSummary, ProcessUsage } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
11
11
|
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
12
12
|
import { formatAgentCost } from './agent-inspector-shared.ts';
|
|
13
|
-
import {
|
|
13
|
+
import { buildViewLine, DEFAULT_VIEW_PALETTE, type ColumnSpec, type ViewPalette } from './polish.ts';
|
|
14
14
|
import { fleetAttentionText, fleetNodeAttention, fleetStallMarker, fleetUsageTokens, hasFleetCost, type FleetStateTone } from './fleet-read-model.ts';
|
|
15
15
|
import { fleetStateDisplay } from './fleet-stop.ts';
|
|
16
16
|
import { formatElapsed } from '../utils/format-elapsed.ts';
|
|
17
17
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
18
|
-
import { conflictFilesFromRaw, formatWorkItemIsolationDetailFromRaw } from './fleet-
|
|
18
|
+
import { conflictFilesFromRaw, formatWorkItemIsolationDetailFromRaw } from './fleet-worktree-detail.ts';
|
|
19
19
|
import { wrapText } from '../utils/terminal-width.ts';
|
|
20
20
|
import { buildAlignedRow } from './polish.ts';
|
|
21
21
|
import { fleetKindTag } from './fleet-read-model.ts';
|
|
22
22
|
import { steerBadgeGlyph, steerBadgeTone } from './fleet-steer.ts';
|
|
23
|
-
import type { SteerBadge } from './fleet-tabs.ts';
|
|
23
|
+
import type { SteerBadge } from './fleet-session-tabs.ts';
|
|
24
24
|
import type { FleetTreeRow } from './fleet-read-model.ts';
|
|
25
25
|
import { isObservedExternalNode, renderObservedDetailLines, renderObservedRowLine } from './fleet-observed-render.ts';
|
|
26
26
|
import { renderPoolSummary, type WorkstreamGraphSnapshot } from './workstream-graph-render.ts';
|
|
27
27
|
|
|
28
28
|
// Column widths for the tree row layout. `label` absorbs whatever width is
|
|
29
29
|
// left over after the fixed columns + gaps; on hostile (narrow) widths the
|
|
30
|
-
// trailing columns simply clip (buildAlignedRow/
|
|
30
|
+
// trailing columns simply clip (buildAlignedRow/buildSelectableViewLine stop
|
|
31
31
|
// writing once a cell would overflow the row) rather than throwing or
|
|
32
32
|
// wrapping, the tree stays readable, just denser.
|
|
33
33
|
const KIND_W = 8;
|
|
@@ -51,12 +51,12 @@ export function planColumns(width: number): ColumnSpec[] {
|
|
|
51
51
|
];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export function toneColor(tone: FleetStateTone, palette:
|
|
54
|
+
export function toneColor(tone: FleetStateTone, palette: ViewPalette): string {
|
|
55
55
|
switch (tone) {
|
|
56
|
-
case 'active': return palette.info ??
|
|
57
|
-
case 'success': return palette.good ??
|
|
58
|
-
case 'failure': return palette.bad ??
|
|
59
|
-
case 'warn': return palette.warn ??
|
|
56
|
+
case 'active': return palette.info ?? DEFAULT_VIEW_PALETTE.info;
|
|
57
|
+
case 'success': return palette.good ?? DEFAULT_VIEW_PALETTE.good;
|
|
58
|
+
case 'failure': return palette.bad ?? DEFAULT_VIEW_PALETTE.bad;
|
|
59
|
+
case 'warn': return palette.warn ?? DEFAULT_VIEW_PALETTE.warn;
|
|
60
60
|
case 'muted': return palette.dim;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -75,7 +75,7 @@ export function formatFleetCost(costUsd: number | null | undefined, costState: P
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* One fleet tree row. Pure (extracted from
|
|
78
|
+
* One fleet tree row. Pure (extracted from the Agents modal's row renderer for the
|
|
79
79
|
* 800-line cap). Two display-only overrides in priority order: an in-flight
|
|
80
80
|
* `stopping` wins the glyph/label/activity, then a `blocked`-on-user node gets
|
|
81
81
|
* the ⚑ badge + 'blocked on you' in the activity slot. Otherwise a queued-steer
|
|
@@ -87,7 +87,7 @@ export function renderFleetRowLine(
|
|
|
87
87
|
stopping: boolean,
|
|
88
88
|
blocked: boolean,
|
|
89
89
|
badge: SteerBadge | null,
|
|
90
|
-
palette:
|
|
90
|
+
palette: ViewPalette = DEFAULT_VIEW_PALETTE,
|
|
91
91
|
): Line {
|
|
92
92
|
const C = palette;
|
|
93
93
|
const node = row.node;
|
|
@@ -133,7 +133,7 @@ export function renderFleetRowLine(
|
|
|
133
133
|
: badge
|
|
134
134
|
? steerBadgeTone(badge.status, C)
|
|
135
135
|
: stallMarker
|
|
136
|
-
? (C.warn ??
|
|
136
|
+
? (C.warn ?? DEFAULT_VIEW_PALETTE.warn)
|
|
137
137
|
: C.dim;
|
|
138
138
|
|
|
139
139
|
return buildAlignedRow(
|
|
@@ -153,7 +153,7 @@ export function renderFleetRowLine(
|
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
155
|
* The selected-node detail block under the fleet tree. Pure (extracted from
|
|
156
|
-
*
|
|
156
|
+
* the Agents modal's detail renderer to hold that file under the 800-line architecture
|
|
157
157
|
* cap). `stopping`/`blocked` are display-only overrides the caller derives from
|
|
158
158
|
* the stop tracker + node state, mirror the tree row exactly so the literal
|
|
159
159
|
* 'state' text never claims a past-tense outcome mid-write, and a
|
|
@@ -166,13 +166,13 @@ export function renderFleetRowLine(
|
|
|
166
166
|
* whether it was verified, the evidence, and (when present) how it was
|
|
167
167
|
* exercised, matching what the webui's review detail surfaces.
|
|
168
168
|
*/
|
|
169
|
-
export function renderReviewLines(review: ProcessReviewSummary, width: number, palette:
|
|
169
|
+
export function renderReviewLines(review: ProcessReviewSummary, width: number, palette: ViewPalette = DEFAULT_VIEW_PALETTE): Line[] {
|
|
170
170
|
const C = palette;
|
|
171
171
|
const passTone = C.good ?? C.info;
|
|
172
|
-
const failTone = C.bad ?? C.warn ??
|
|
172
|
+
const failTone = C.bad ?? C.warn ?? DEFAULT_VIEW_PALETTE.warn;
|
|
173
173
|
const cyclesLabel = review.cycles === 1 ? '1 cycle' : `${review.cycles} cycles`;
|
|
174
174
|
const lines: Line[] = [
|
|
175
|
-
|
|
175
|
+
buildViewLine(width, [
|
|
176
176
|
[' review ', C.label],
|
|
177
177
|
[review.passed ? '✓ passed' : '✗ not passed', review.passed ? passTone : failTone],
|
|
178
178
|
[' score ', C.label], [String(review.score), C.value],
|
|
@@ -181,25 +181,25 @@ export function renderReviewLines(review: ProcessReviewSummary, width: number, p
|
|
|
181
181
|
];
|
|
182
182
|
if (review.checklist.length === 0) {
|
|
183
183
|
// Empty checklist is itself a gate failure, say so honestly, don't hide it.
|
|
184
|
-
lines.push(
|
|
184
|
+
lines.push(buildViewLine(width, [[' ', C.dim], ['(the reviewer emitted no acceptance checklist; a gate failure)', failTone]]));
|
|
185
185
|
return lines;
|
|
186
186
|
}
|
|
187
187
|
for (const it of review.checklist) {
|
|
188
188
|
const mark = it.verified ? '[verified] ' : '[unverified]';
|
|
189
189
|
const markTone = it.verified ? passTone : failTone;
|
|
190
190
|
wrapText(it.item, Math.max(1, width - 16)).forEach((seg, i) => {
|
|
191
|
-
lines.push(
|
|
191
|
+
lines.push(buildViewLine(width, i === 0
|
|
192
192
|
? [[' ', C.dim], [`${mark} `, markTone], [seg, C.value]]
|
|
193
193
|
: [[' ', C.dim], [seg, C.value]]));
|
|
194
194
|
});
|
|
195
195
|
if (it.evidence) {
|
|
196
196
|
for (const seg of wrapText(`evidence: ${it.evidence}`, Math.max(1, width - 6))) {
|
|
197
|
-
lines.push(
|
|
197
|
+
lines.push(buildViewLine(width, [[' ', C.dim], [seg, C.dim]]));
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
if (it.howExercised) {
|
|
201
201
|
for (const seg of wrapText(`exercised: ${it.howExercised}`, Math.max(1, width - 6))) {
|
|
202
|
-
lines.push(
|
|
202
|
+
lines.push(buildViewLine(width, [[' ', C.dim], [seg, C.dim]]));
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
}
|
|
@@ -212,22 +212,22 @@ export function renderReviewLines(review: ProcessReviewSummary, width: number, p
|
|
|
212
212
|
* only via /graph, /graph still works). Fetched lazily and cached by fleet-acts;
|
|
213
213
|
* rendered only once a snapshot is in hand. An empty graph states so honestly.
|
|
214
214
|
*/
|
|
215
|
-
export function renderGraphPostureLines(graph: WorkstreamGraphSnapshot, width: number, palette:
|
|
215
|
+
export function renderGraphPostureLines(graph: WorkstreamGraphSnapshot, width: number, palette: ViewPalette = DEFAULT_VIEW_PALETTE): Line[] {
|
|
216
216
|
const C = palette;
|
|
217
217
|
const lines: Line[] = [
|
|
218
|
-
|
|
218
|
+
buildViewLine(width, [[' graph ', C.label], [renderPoolSummary(graph.pool), C.value]]),
|
|
219
219
|
];
|
|
220
220
|
if (graph.edges.length === 0) {
|
|
221
|
-
lines.push(
|
|
221
|
+
lines.push(buildViewLine(width, [[' ', C.dim], ['no dependency edges', C.dim]]));
|
|
222
222
|
return lines;
|
|
223
223
|
}
|
|
224
224
|
const titleById = new Map(graph.nodes.map((n) => [n.id, n.title]));
|
|
225
|
-
lines.push(
|
|
225
|
+
lines.push(buildViewLine(width, [[' edges ', C.label], [`${graph.edges.length} dependency link(s)`, C.value]]));
|
|
226
226
|
for (const edge of graph.edges) {
|
|
227
227
|
const from = titleById.get(edge.from) ?? edge.from;
|
|
228
228
|
const to = titleById.get(edge.to) ?? edge.to;
|
|
229
229
|
for (const seg of wrapText(`${from} → ${to}`, Math.max(1, width - 5))) {
|
|
230
|
-
lines.push(
|
|
230
|
+
lines.push(buildViewLine(width, [[' ', C.dim], [seg, C.dim]]));
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
return lines;
|
|
@@ -238,7 +238,7 @@ export function renderFleetDetailLines(
|
|
|
238
238
|
width: number,
|
|
239
239
|
stopping: boolean,
|
|
240
240
|
blocked: boolean,
|
|
241
|
-
palette:
|
|
241
|
+
palette: ViewPalette = DEFAULT_VIEW_PALETTE,
|
|
242
242
|
observedSteerDraft: string | null = null,
|
|
243
243
|
graphSnapshot: WorkstreamGraphSnapshot | null = null,
|
|
244
244
|
): Line[] {
|
|
@@ -257,7 +257,7 @@ export function renderFleetDetailLines(
|
|
|
257
257
|
? fleetAttentionText(fleetNodeAttention(node) ?? { reason: 'approval' })
|
|
258
258
|
: disp.label;
|
|
259
259
|
|
|
260
|
-
const line1 =
|
|
260
|
+
const line1 = buildViewLine(width, [
|
|
261
261
|
[' ', C.dim],
|
|
262
262
|
[disp.glyph, color],
|
|
263
263
|
[` ${node.kind}`, C.dim],
|
|
@@ -268,7 +268,7 @@ export function renderFleetDetailLines(
|
|
|
268
268
|
[' elapsed ', C.label],
|
|
269
269
|
[formatElapsed(node.elapsedMs), C.value],
|
|
270
270
|
]);
|
|
271
|
-
const line2 =
|
|
271
|
+
const line2 = buildViewLine(width, [
|
|
272
272
|
[' model ', C.label],
|
|
273
273
|
[node.model ?? 'unknown', C.info],
|
|
274
274
|
[' tokens ', C.label],
|
|
@@ -284,20 +284,20 @@ export function renderFleetDetailLines(
|
|
|
284
284
|
? `${node.headline.text}${stallMarker ? ` · ${stallMarker}` : ''}`
|
|
285
285
|
: stallMarker;
|
|
286
286
|
const headlineLine = headlineText
|
|
287
|
-
? [
|
|
287
|
+
? [buildViewLine(width, [
|
|
288
288
|
[' headline ', C.label],
|
|
289
|
-
[truncateDisplay(headlineText, Math.max(0, width - 11)), stallMarker ? (C.warn ??
|
|
289
|
+
[truncateDisplay(headlineText, Math.max(0, width - 11)), stallMarker ? (C.warn ?? DEFAULT_VIEW_PALETTE.warn) : C.value],
|
|
290
290
|
])]
|
|
291
291
|
: [];
|
|
292
292
|
const activityText = node.currentActivity
|
|
293
293
|
? `${node.currentActivity.kind}: ${node.currentActivity.text}`
|
|
294
294
|
: '(no recent activity)';
|
|
295
|
-
const line3 =
|
|
295
|
+
const line3 = buildViewLine(width, [
|
|
296
296
|
[' activity ', C.label],
|
|
297
297
|
[truncateDisplay(activityText, Math.max(0, width - 11)), C.dim],
|
|
298
298
|
]);
|
|
299
299
|
// Approval history attaches here once session tabs land.
|
|
300
|
-
const line4 =
|
|
300
|
+
const line4 = buildViewLine(width, [[' approvals ', C.label], ['—', C.dim]]);
|
|
301
301
|
const isolationDetail = node.kind === 'work-item' ? formatWorkItemIsolationDetailFromRaw(node.raw) : null;
|
|
302
302
|
// A merge-conflict row shows its STRUCTURED conflicting-path list, wrapped so a
|
|
303
303
|
// long path is fully readable, never clipped (STEP 4: the conflict row acts,
|
|
@@ -305,13 +305,13 @@ export function renderFleetDetailLines(
|
|
|
305
305
|
const conflictFiles = node.kind === 'work-item' ? conflictFilesFromRaw(node.raw) : null;
|
|
306
306
|
const conflictLines: Line[] = conflictFiles
|
|
307
307
|
? [
|
|
308
|
-
|
|
308
|
+
buildViewLine(width, [[' conflicts ', C.label], [`${conflictFiles.length} file(s): press Enter to resolve`, C.warn ?? DEFAULT_VIEW_PALETTE.warn]]),
|
|
309
309
|
// Prefix is 3 spaces of indent + a 2-col bullet/continuation marker, so
|
|
310
310
|
// wrap the path at width-5 to keep the composed line within `width`
|
|
311
311
|
// (a hard-wrapped long path is fully readable, never clipped).
|
|
312
312
|
...conflictFiles.flatMap((file) =>
|
|
313
313
|
wrapText(file, Math.max(1, width - 5)).map((segment, i) =>
|
|
314
|
-
|
|
314
|
+
buildViewLine(width, [[' ', C.dim], [i === 0 ? `• ${segment}` : ` ${segment}`, C.value]]),
|
|
315
315
|
),
|
|
316
316
|
),
|
|
317
317
|
]
|
|
@@ -320,7 +320,7 @@ export function renderFleetDetailLines(
|
|
|
320
320
|
// (served on node.review; absent before any review, never an empty shell).
|
|
321
321
|
const reviewLines = node.review ? renderReviewLines(node.review, width, C) : [];
|
|
322
322
|
// The task graph's edges/pool posture for a workstream row (fetched + cached
|
|
323
|
-
// by fleet-acts); rendered in-
|
|
323
|
+
// by fleet-acts); rendered in-view under the chain, /graph still available.
|
|
324
324
|
const graphLines = graphSnapshot ? renderGraphPostureLines(graphSnapshot, width, C) : [];
|
|
325
|
-
return [line1, line2, ...headlineLine, line3, line4, ...(isolationDetail ? [
|
|
325
|
+
return [line1, line2, ...headlineLine, line3, line4, ...(isolationDetail ? [buildViewLine(width, [[' isolation ', C.label], [isolationDetail, C.dim]])] : []), ...conflictLines, ...reviewLines, ...graphLines];
|
|
326
326
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-gateway.ts
|
|
3
3
|
//
|
|
4
|
-
// The async, daemon-backed verb surface the
|
|
4
|
+
// The async, daemon-backed verb surface the Agents modal's waiting-on-human acts
|
|
5
5
|
// drive, a best-of-N winner pick (fleet.attempts.list / fleet.attempts.pick),
|
|
6
6
|
// a merge-conflict resolution (fleet.conflicts.resolve), and a worktree discard
|
|
7
7
|
// (worktrees.discard). These are gateway verbs with no named facade on the
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
// go over the generic operator invoke path (operator-rpc.ts's resolveOperatorRpc
|
|
10
10
|
// -> sdk.operator.invoke), reaching the SAME daemon the command layer does.
|
|
11
11
|
//
|
|
12
|
-
// The
|
|
12
|
+
// The view never types an id: it derives the workstream/work-item id from the
|
|
13
13
|
// selected node's namespaced id (workstream:<id> / work-item:<id>, the SDK's own
|
|
14
14
|
// fleet-adapter id scheme) and drives the verbs from there. The interface is
|
|
15
|
-
// injectable so the
|
|
15
|
+
// injectable so the Agents modal's act flow round-trips against a mocked daemon in
|
|
16
16
|
// tests; the live builder (createFleetGateway) is wired in builtin/operations.ts.
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@ export type FleetGraphSnapshot = OperatorMethodOutput<'fleet.graph.get'>;
|
|
|
38
38
|
export type FleetObservedSteerResult = OperatorMethodOutput<'fleet.observed.steer'>;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* The narrow async verb surface the
|
|
41
|
+
* The narrow async verb surface the Agents modal's acts drive. Every method is a
|
|
42
42
|
* real daemon round-trip in production (createFleetGateway) and a mocked shape
|
|
43
43
|
* in tests. `armFixSessionAttach` is the ONLY sync member, it hands a spawned
|
|
44
44
|
* session id to the shared one-key jump affordance (the CI fix-session
|
|
@@ -97,7 +97,7 @@ export interface FleetGatewayDeps {
|
|
|
97
97
|
/**
|
|
98
98
|
* Build the live Fleet gateway over the generic operator invoke path, the same
|
|
99
99
|
* daemon resolution the command layer uses (resolveOperatorRpc). Returns an
|
|
100
|
-
* honest unavailable reason when no daemon is reachable, so the
|
|
100
|
+
* honest unavailable reason when no daemon is reachable, so the Agents modal acts can
|
|
101
101
|
* refuse cleanly instead of throwing into the render loop.
|
|
102
102
|
*/
|
|
103
103
|
export function createFleetGateway(deps: FleetGatewayDeps): FleetGatewayResolution {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// fleet-observed-render.ts, the
|
|
2
|
+
// fleet-observed-render.ts, the Agents modal rows for observed FOREIGN coding
|
|
3
3
|
// agents (kind 'observed-external').
|
|
4
4
|
//
|
|
5
5
|
// goodvibes did NOT spawn these, they are externally-launched Claude Code /
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// FIRST job is visibility ("it's in claude or codex"): goodvibes never presents
|
|
8
8
|
// itself as the foreign session's cockpit. So an observed row:
|
|
9
9
|
// - shows its honest external kind + a liveness-derived state (active/quiet),
|
|
10
|
-
// - is NEVER counted in any of the
|
|
10
|
+
// - is NEVER counted in any of the Agents modal's own-fleet counts,
|
|
11
11
|
// - carries NO stop affordance, ever,
|
|
12
12
|
// - offers steer as a DRILL-IN only (in the detail view), and only where a
|
|
13
13
|
// real channel exists; where steer.kind is 'none' the detail states why.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import type { ProcessNode, ProcessObserved, ObservedAgentKind } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
17
17
|
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
18
|
-
import {
|
|
18
|
+
import { buildViewLine, buildSearchInputLine, DEFAULT_VIEW_PALETTE, type ViewPalette } from './polish.ts';
|
|
19
19
|
import { truncateDisplay, wrapText } from '../utils/terminal-width.ts';
|
|
20
20
|
|
|
21
21
|
/** An observed-external node carries its foreign-agent facts on `observed`. */
|
|
@@ -47,7 +47,7 @@ function livenessGlyph(state: ProcessObserved['liveness']['state']): string {
|
|
|
47
47
|
* no stop marker, just the glyph, an "observed" tag, the session label, its
|
|
48
48
|
* external kind, and its liveness state.
|
|
49
49
|
*/
|
|
50
|
-
export function renderObservedRowLine(node: ObservedNode, width: number, palette:
|
|
50
|
+
export function renderObservedRowLine(node: ObservedNode, width: number, palette: ViewPalette = DEFAULT_VIEW_PALETTE): Line {
|
|
51
51
|
const C = palette;
|
|
52
52
|
const observed = node.observed;
|
|
53
53
|
const glyph = livenessGlyph(observed.liveness.state);
|
|
@@ -56,9 +56,9 @@ export function renderObservedRowLine(node: ObservedNode, width: number, palette
|
|
|
56
56
|
// overflows the row (the tell is the honesty signal, it must survive).
|
|
57
57
|
const tell = `${kindLabel} · ${observed.liveness.state}`;
|
|
58
58
|
const labelBudget = Math.max(4, width - tell.length - 14);
|
|
59
|
-
return
|
|
59
|
+
return buildViewLine(width, [
|
|
60
60
|
[' ', C.dim],
|
|
61
|
-
[glyph, observed.liveness.state === 'active' ? (C.info ??
|
|
61
|
+
[glyph, observed.liveness.state === 'active' ? (C.info ?? DEFAULT_VIEW_PALETTE.info) : C.dim],
|
|
62
62
|
[' observed ', C.dim],
|
|
63
63
|
[truncateDisplay(node.label, labelBudget), C.value],
|
|
64
64
|
[' ', C.dim],
|
|
@@ -79,13 +79,13 @@ export function renderObservedRowLine(node: ObservedNode, width: number, palette
|
|
|
79
79
|
* "s: steer" hint. A channel-less row never shows an input (owner ruling), its
|
|
80
80
|
* detail states the honest reason and stops there.
|
|
81
81
|
*/
|
|
82
|
-
export function renderObservedDetailLines(node: ObservedNode, width: number, palette:
|
|
82
|
+
export function renderObservedDetailLines(node: ObservedNode, width: number, palette: ViewPalette = DEFAULT_VIEW_PALETTE, steerDraft: string | null = null): Line[] {
|
|
83
83
|
const C = palette;
|
|
84
84
|
const observed = node.observed;
|
|
85
85
|
const lines: Line[] = [];
|
|
86
|
-
lines.push(
|
|
86
|
+
lines.push(buildViewLine(width, [
|
|
87
87
|
[' ', C.dim],
|
|
88
|
-
[livenessGlyph(observed.liveness.state), observed.liveness.state === 'active' ? (C.info ??
|
|
88
|
+
[livenessGlyph(observed.liveness.state), observed.liveness.state === 'active' ? (C.info ?? DEFAULT_VIEW_PALETTE.info) : C.dim],
|
|
89
89
|
[' observed ', C.dim],
|
|
90
90
|
[observedKindLabel(observed.externalKind), C.value],
|
|
91
91
|
[' pid ', C.label],
|
|
@@ -94,11 +94,11 @@ export function renderObservedDetailLines(node: ObservedNode, width: number, pal
|
|
|
94
94
|
[observed.liveness.state, C.value],
|
|
95
95
|
]));
|
|
96
96
|
if (observed.cwd) {
|
|
97
|
-
lines.push(
|
|
97
|
+
lines.push(buildViewLine(width, [[' cwd ', C.label], [truncateDisplay(observed.cwd, Math.max(0, width - 6)), C.dim]]));
|
|
98
98
|
}
|
|
99
99
|
// Liveness meaning, verbatim, honest about what 'quiet' can and cannot tell.
|
|
100
100
|
for (const segment of wrapText(observed.liveness.detail, Math.max(1, width - 2))) {
|
|
101
|
-
lines.push(
|
|
101
|
+
lines.push(buildViewLine(width, [[' ', C.dim], [segment, C.dim]]));
|
|
102
102
|
}
|
|
103
103
|
// Steer is a drill-in: a real channel is named here (a message sent from this
|
|
104
104
|
// detail travels over it, fleet.observed.steer), and where none exists the
|
|
@@ -107,22 +107,22 @@ export function renderObservedDetailLines(node: ObservedNode, width: number, pal
|
|
|
107
107
|
const steerText = observed.steer.kind === 'tmux'
|
|
108
108
|
? `available via tmux pane ${observed.steer.paneId} (send-keys)`
|
|
109
109
|
: `unavailable: ${observed.steer.reason}`;
|
|
110
|
-
const steerFg = observed.steer.kind === 'tmux' ? (C.info ??
|
|
110
|
+
const steerFg = observed.steer.kind === 'tmux' ? (C.info ?? DEFAULT_VIEW_PALETTE.info) : C.dim;
|
|
111
111
|
const steerSegments = wrapText(steerText, Math.max(1, width - 8));
|
|
112
112
|
steerSegments.forEach((segment, i) => {
|
|
113
|
-
lines.push(
|
|
113
|
+
lines.push(buildViewLine(width, [[i === 0 ? ' steer ' : ' ', C.label], [segment, steerFg]]));
|
|
114
114
|
});
|
|
115
115
|
// Drill-in compose input: only where a real channel exists. An active draft
|
|
116
116
|
// renders the input line + send/cancel hint; otherwise a discoverability hint.
|
|
117
117
|
if (observed.steer.kind === 'tmux') {
|
|
118
118
|
if (steerDraft !== null) {
|
|
119
119
|
lines.push(buildSearchInputLine(width, ' send ', `${steerDraft}_`, C, { active: true, bg: C.inputBg, valueColor: C.info }));
|
|
120
|
-
lines.push(
|
|
120
|
+
lines.push(buildViewLine(width, [[' ', C.label], ['Enter: send Esc: cancel', C.dim]]));
|
|
121
121
|
} else {
|
|
122
|
-
lines.push(
|
|
122
|
+
lines.push(buildViewLine(width, [[' ', C.label], ['s: steer this session', C.dim]]));
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
// Visibility, not a cockpit: there is no stop here, by design.
|
|
126
|
-
lines.push(
|
|
126
|
+
lines.push(buildViewLine(width, [[' stop ', C.label], ['not offered: goodvibes only observes this foreign session', C.dim]]));
|
|
127
127
|
return lines;
|
|
128
128
|
}
|