@pellux/goodvibes-tui 2.0.20 → 2.1.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 +70 -0
- package/README.md +16 -16
- package/docs/README.md +4 -4
- package/docs/foundation-artifacts/operator-contract.json +59 -4
- package/package.json +16 -6
- 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-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/verification-ledger.ts +18 -16
- 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/{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,11 +1,11 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-read-model.ts
|
|
3
3
|
//
|
|
4
|
-
// (
|
|
4
|
+
// (Agents modal tree), pure, testable read-model over the SDK's live
|
|
5
5
|
// process registry (@pellux/goodvibes-sdk/platform/runtime/fleet, landed on
|
|
6
|
-
// SDK main as). No
|
|
6
|
+
// SDK main as). No rendering dependency: this module owns the
|
|
7
7
|
// flat ProcessNode[] -> tree transformation, the honest cost/token
|
|
8
|
-
// aggregates, and the state->glyph/tone mapping that
|
|
8
|
+
// aggregates, and the state->glyph/tone mapping that the Agents modal renders.
|
|
9
9
|
//
|
|
10
10
|
// Design notes:
|
|
11
11
|
// - The brief predates the real SDK API and sketched a bespoke
|
|
@@ -55,7 +55,7 @@ export interface FleetTreeRow {
|
|
|
55
55
|
readonly hasChildren: boolean;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/** Aggregate snapshot the
|
|
58
|
+
/** Aggregate snapshot the Agents modal renders from. */
|
|
59
59
|
export interface FleetSnapshot {
|
|
60
60
|
readonly rows: readonly FleetTreeRow[];
|
|
61
61
|
/** Sum of costUsd across nodes with a real (non-unpriced) reading; null when none do. */
|
|
@@ -68,7 +68,7 @@ export interface FleetSnapshot {
|
|
|
68
68
|
* Ids of the nodes that are "blocked on me", waiting on a user approval or
|
|
69
69
|
* user input right now (see isBlockedOnUserState), in display (row) order.
|
|
70
70
|
* This is a DERIVED view over the same node states the rows already carry,
|
|
71
|
-
* never a second source of truth: the
|
|
71
|
+
* never a second source of truth: the Agents modal reads it to badge/jump, it is
|
|
72
72
|
* not a store. Empty when nothing is waiting on the operator.
|
|
73
73
|
*/
|
|
74
74
|
readonly blockedNodeIds: readonly string[];
|
|
@@ -85,9 +85,9 @@ export type FleetStateTone = 'active' | 'success' | 'failure' | 'warn' | 'muted'
|
|
|
85
85
|
// contract's STATE_GLYPHS (@pellux/goodvibes-sdk/platform/presentation), that
|
|
86
86
|
// one is a 4-state semantic alias (good/warn/bad/info) shared by the TUI and
|
|
87
87
|
// agent renderers; this one is a 12-state ProcessNode taxonomy specific to the
|
|
88
|
-
//
|
|
88
|
+
// Agents modal tree (thinking/executing-tool/awaiting-approval/streaming/
|
|
89
89
|
// stalled/retrying/done/failed/killed/interrupted/idle/queued/paused), and no
|
|
90
|
-
// other surface (checked: the agent has no
|
|
90
|
+
// other surface (checked: the agent has no Agents modal renderer) consumes it
|
|
91
91
|
// today. Per the presentation-parity brief, a table only moves to the SDK
|
|
92
92
|
// contract when 2+ surfaces need it, this one stays TUI-renderer-local.
|
|
93
93
|
const STATE_GLYPHS: Record<ProcessState, string> = {
|
|
@@ -176,7 +176,7 @@ const KIND_TAGS: Record<ProcessKind, string> = {
|
|
|
176
176
|
// that goodvibes did NOT spawn, observed read-only from OS signals. The row's
|
|
177
177
|
// first job is visibility ("it's in claude or codex"); it is never counted in
|
|
178
178
|
// any our-fleet count, never carries a stop affordance, and steer is drill-in
|
|
179
|
-
// only. See ObservedProcess handling in fleet-
|
|
179
|
+
// only. See ObservedProcess handling in fleet-format.ts.
|
|
180
180
|
'observed-external': 'observed',
|
|
181
181
|
};
|
|
182
182
|
|
|
@@ -525,7 +525,7 @@ export function buildFleetSnapshot(nodes: readonly ProcessNode[], capturedAt: nu
|
|
|
525
525
|
if (isFleetRunningLeaf(node)) runningCount++;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
-
// Blocked ids in display (row) order so the
|
|
528
|
+
// Blocked ids in display (row) order so the Agents modal's jump key steps through
|
|
529
529
|
// them top-to-bottom exactly as they appear on screen.
|
|
530
530
|
const blockedNodeIds = rows
|
|
531
531
|
.filter((row) => isBlockedOnUserNode(row.node))
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// fleet-tabs.ts
|
|
2
|
+
// fleet-session-tabs.ts
|
|
3
3
|
//
|
|
4
|
-
// Pure, testable tab-state model for
|
|
5
|
-
// session tabs. No
|
|
4
|
+
// Pure, testable tab-state model for the Agents modal's
|
|
5
|
+
// session tabs. No rendering dependency, mirroring the
|
|
6
6
|
// fleet-read-model.ts convention: this module owns attach/detach/switch as
|
|
7
7
|
// pure state transitions over a small immutable FleetTabsState, so the tab
|
|
8
|
-
// lifecycle is unit-testable without a live registry or a rendered
|
|
8
|
+
// lifecycle is unit-testable without a live registry or a rendered view.
|
|
9
9
|
//
|
|
10
|
-
// Tab-index convention: index 0 is always the fleet TREE (the
|
|
10
|
+
// Tab-index convention: index 0 is always the fleet TREE (the Agents modal's root
|
|
11
11
|
// view, never stored in `tabs`); `activeTabIndex` 1..N addresses
|
|
12
12
|
// `tabs[0..N-1]`. This mirrors the brief's "tree is the root/home tab"
|
|
13
13
|
// framing while keeping the array itself simple (only attached tabs).
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// attaching to (a transcript for an agent; a live member summary for a
|
|
17
17
|
// chain, see fleet-transcript.ts). workflow/trigger/schedule/watcher/
|
|
18
18
|
// background-process/wrfc-subtask nodes have no transcript and are never
|
|
19
|
-
// attachable (
|
|
19
|
+
// attachable (the Agents modal shows a status message instead, matching the
|
|
20
20
|
// existing i/K "not supported" convention).
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
|
|
@@ -39,9 +39,9 @@ export function isAttachableFleetKind(kind: ProcessKind): kind is FleetAttachabl
|
|
|
39
39
|
* event matching this badge's `messageId`). `dropped` is a TUI-side
|
|
40
40
|
* inference: the SDK emits no "expired"/"cancelled" signal for a queued
|
|
41
41
|
* steer, so if the target node goes terminal (done/failed/killed/
|
|
42
|
-
* interrupted) while the badge is still `queued`,
|
|
42
|
+
* interrupted) while the badge is still `queued`, the Agents modal resolves it to
|
|
43
43
|
* `dropped` itself rather than leaving the badge hanging forever (see
|
|
44
|
-
*
|
|
44
|
+
* src/input/agents-modal.ts reconcileSteerBadges, cross-WO note: the SDK engineer
|
|
45
45
|
* confirmed no dropped signal exists).
|
|
46
46
|
*/
|
|
47
47
|
export type SteerBadgeStatus = 'queued' | 'consumed' | 'dropped';
|
|
@@ -52,7 +52,7 @@ export interface SteerBadge {
|
|
|
52
52
|
readonly status: SteerBadgeStatus;
|
|
53
53
|
/** Present for 'dropped', a one-line honest explanation shown in the tab. */
|
|
54
54
|
readonly note?: string;
|
|
55
|
-
/** epoch ms when status left 'queued' (consumed or dropped), drives
|
|
55
|
+
/** epoch ms when status left 'queued' (consumed or dropped), drives the Agents modal's linger-then-clear tick. */
|
|
56
56
|
readonly resolvedAt?: number;
|
|
57
57
|
/**
|
|
58
58
|
* epoch ms when status entered 'queued' (set once, at submit time). Drives
|
|
@@ -119,10 +119,10 @@ export interface FleetTab {
|
|
|
119
119
|
ledgerLoadStarted: boolean;
|
|
120
120
|
/**
|
|
121
121
|
* The one-line steer composer's in-progress text, or `null`
|
|
122
|
-
* when not composing. Mirrors git
|
|
123
|
-
* convention (
|
|
122
|
+
* when not composing. Mirrors the git view's `commitMessage` mutable-slot
|
|
123
|
+
* convention (the Agents modal's text-capture check gates on this being
|
|
124
124
|
* non-null so a burst/paste lands here char-by-char, never as tree/tab
|
|
125
|
-
* hotkeys, see
|
|
125
|
+
* hotkeys, see the Agents modal's steer input handler).
|
|
126
126
|
*/
|
|
127
127
|
steerDraft: string | null;
|
|
128
128
|
/** This tab's most recent steer message's lifecycle, or null. */
|
|
@@ -173,7 +173,7 @@ export function activeFleetTab(state: FleetTabsState): FleetTab | null {
|
|
|
173
173
|
* Attach a node as a tab (Enter on an attachable tree row). Re-focuses an
|
|
174
174
|
* already-open tab for the same node instead of creating a duplicate.
|
|
175
175
|
* Returns `state` unchanged if `node.kind` is not attachable, callers
|
|
176
|
-
* (
|
|
176
|
+
* (the Agents modal) are expected to guard with `isAttachableFleetKind` first and
|
|
177
177
|
* report a status message rather than silently doing nothing; this
|
|
178
178
|
* function's own no-op fallback is defense in depth.
|
|
179
179
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-spawn.ts
|
|
3
3
|
//
|
|
4
|
-
// The
|
|
4
|
+
// The Agents modal's ACP spawn affordance: 'n' lists the
|
|
5
5
|
// third-party coding agents the daemon discovered (acp.agents.list, read-only,
|
|
6
6
|
// quiet when none), you pick one, then pick a working directory from the known
|
|
7
7
|
// candidates (the registered workspaces + the current dir, no free-text path
|
|
@@ -13,24 +13,16 @@
|
|
|
13
13
|
// never a hung row: the daemon bounds the handshake and returns a 'failed'
|
|
14
14
|
// record, and this controller reports its three honest fields.
|
|
15
15
|
//
|
|
16
|
-
// The controller owns the
|
|
17
|
-
// FleetActs' pick mode)
|
|
18
|
-
//
|
|
16
|
+
// The controller owns the Agents modal's input while a pick is in flight
|
|
17
|
+
// (mirrors FleetActs' pick mode); the modal draws spawnView(). The gateway is
|
|
18
|
+
// injectable so the flow round-trips
|
|
19
19
|
// against a mocked daemon in tests; the live builder is createAcpSpawnGateway.
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
23
23
|
import type { OperatorMethodOutput } from '@pellux/goodvibes-sdk';
|
|
24
24
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
25
|
-
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
26
25
|
import { resolveOperatorRpc } from '../input/commands/operator-rpc.ts';
|
|
27
|
-
import {
|
|
28
|
-
buildKeyboardHints,
|
|
29
|
-
buildPanelWorkspace,
|
|
30
|
-
buildPanelLine,
|
|
31
|
-
DEFAULT_PANEL_PALETTE,
|
|
32
|
-
type PanelPalette,
|
|
33
|
-
} from './polish.ts';
|
|
34
26
|
|
|
35
27
|
/** One discovered third-party ACP agent (acp.agents.list). */
|
|
36
28
|
export type AcpDiscoveredAgent = OperatorMethodOutput<'acp.agents.list'>['agents'][number];
|
|
@@ -111,7 +103,15 @@ interface DirStep {
|
|
|
111
103
|
}
|
|
112
104
|
type SpawnMode = AgentStep | DirStep | null;
|
|
113
105
|
|
|
114
|
-
|
|
106
|
+
/** The spawn picker, as data. */
|
|
107
|
+
export interface FleetSpawnView {
|
|
108
|
+
readonly step: 'agent' | 'dir';
|
|
109
|
+
readonly title: string;
|
|
110
|
+
readonly intro: string;
|
|
111
|
+
readonly options: ReadonlyArray<{ readonly label: string; readonly detail: string; readonly selected: boolean }>;
|
|
112
|
+
/** A create round-trip is in flight. */
|
|
113
|
+
readonly creating: boolean;
|
|
114
|
+
}
|
|
115
115
|
|
|
116
116
|
export class FleetSpawn {
|
|
117
117
|
private mode: SpawnMode = null;
|
|
@@ -218,44 +218,28 @@ export class FleetSpawn {
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
/** The spawn picker
|
|
222
|
-
public
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
const lines: Line[] = [];
|
|
226
|
-
if (this.creating) {
|
|
227
|
-
lines.push(buildPanelLine(width, [[' Hosting the agent…', P.dim]]));
|
|
228
|
-
return buildPanelWorkspace(width, height, { title: 'Fleet: Host an agent', sections: [{ lines }], footerLines: [], palette: P });
|
|
229
|
-
}
|
|
221
|
+
/** The spawn picker as data the Agents modal draws, or null when it is not open. */
|
|
222
|
+
public spawnView(): FleetSpawnView | null {
|
|
223
|
+
if (!this.mode) return null;
|
|
224
|
+
if (this.creating) return { step: this.mode.step, title: 'Hosting the agent…', intro: '', options: [], creating: true };
|
|
230
225
|
if (this.mode.step === 'agent') {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
]));
|
|
240
|
-
});
|
|
241
|
-
} else {
|
|
242
|
-
lines.push(buildPanelLine(width, [[` Directory for ${this.mode.agent.title}: a known dir, no path to type.`, P.dim]]));
|
|
243
|
-
lines.push(buildPanelLine(width, [['', P.dim]]));
|
|
244
|
-
this.mode.candidates.forEach((cand, i) => {
|
|
245
|
-
const selected = i === (this.mode as DirStep).index;
|
|
246
|
-
lines.push(buildPanelLine(width, [
|
|
247
|
-
[selected ? ' ▸ ' : ' ', selected ? P.info : P.dim],
|
|
248
|
-
[cand.label, selected ? P.value : P.dim],
|
|
249
|
-
[`, ${cand.path}`, P.dim],
|
|
250
|
-
]));
|
|
251
|
-
});
|
|
226
|
+
const index = this.mode.index;
|
|
227
|
+
return {
|
|
228
|
+
step: 'agent',
|
|
229
|
+
title: 'Host an agent',
|
|
230
|
+
intro: 'Host a third-party coding agent here: pick one, then a directory.',
|
|
231
|
+
options: this.mode.agents.map((agent, i) => ({ label: agent.title, detail: agent.binaryPath, selected: i === index })),
|
|
232
|
+
creating: false,
|
|
233
|
+
};
|
|
252
234
|
}
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
235
|
+
const index = this.mode.index;
|
|
236
|
+
return {
|
|
237
|
+
step: 'dir',
|
|
238
|
+
title: this.mode.agent.title,
|
|
239
|
+
intro: `Where ${this.mode.agent.title} runs: a known directory, no path to type.`,
|
|
240
|
+
options: this.mode.candidates.map((cand, i) => ({ label: cand.label, detail: cand.path, selected: i === index })),
|
|
241
|
+
creating: false,
|
|
242
|
+
};
|
|
259
243
|
}
|
|
260
244
|
|
|
261
245
|
private requireGateway(): AcpSpawnGateway | null {
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
// fleet-steer.ts
|
|
3
3
|
//
|
|
4
4
|
// Steer-badge rendering helpers and the "dropped inference"
|
|
5
|
-
// reconciliation pass, split out of
|
|
5
|
+
// reconciliation pass, split out of src/input/agents-modal.ts to keep that file under
|
|
6
6
|
// the architecture line cap (see check-architecture.ts's 800-line gate).
|
|
7
|
-
// Pure functions only;
|
|
8
|
-
// state itself (see fleet-tabs.ts's SteerBadge doc) and calls into this
|
|
7
|
+
// Pure functions only; the Agents modal still owns the mutable FleetTab.steerBadge
|
|
8
|
+
// state itself (see fleet-session-tabs.ts's SteerBadge doc) and calls into this
|
|
9
9
|
// module rather than duplicating the logic inline.
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
|
|
12
12
|
import { STEER_TTL_MS, type ProcessNode } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
13
13
|
import { isTerminalProcessState } from './fleet-read-model.ts';
|
|
14
|
-
import type { FleetTab, SteerBadge, SteerBadgeStatus } from './fleet-tabs.ts';
|
|
15
|
-
import {
|
|
14
|
+
import type { FleetTab, SteerBadge, SteerBadgeStatus } from './fleet-session-tabs.ts';
|
|
15
|
+
import { buildViewLine, DEFAULT_VIEW_PALETTE, type ViewPalette } from './polish.ts';
|
|
16
16
|
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
17
17
|
|
|
18
18
|
/** Linger before an auto-resolved (consumed/dropped) steer badge is cleared from the tab. */
|
|
@@ -26,11 +26,11 @@ export function steerBadgeGlyph(status: SteerBadgeStatus): string {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export function steerBadgeTone(status: SteerBadgeStatus, palette:
|
|
29
|
+
export function steerBadgeTone(status: SteerBadgeStatus, palette: ViewPalette): string {
|
|
30
30
|
switch (status) {
|
|
31
|
-
case 'queued': return palette.warn ??
|
|
32
|
-
case 'consumed': return palette.good ??
|
|
33
|
-
case 'dropped': return palette.bad ??
|
|
31
|
+
case 'queued': return palette.warn ?? DEFAULT_VIEW_PALETTE.warn;
|
|
32
|
+
case 'consumed': return palette.good ?? DEFAULT_VIEW_PALETTE.good;
|
|
33
|
+
case 'dropped': return palette.bad ?? DEFAULT_VIEW_PALETTE.bad;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -50,7 +50,7 @@ export function steerRefusalMessage(reason: string, siblingLabels: readonly stri
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/** One-line honest status for a tab's steer badge; the queued line names the target and points at the ⧗ delivery badge (WO item 4). */
|
|
53
|
-
export function renderSteerBadgeLine(badge: SteerBadge, width: number, palette:
|
|
53
|
+
export function renderSteerBadgeLine(badge: SteerBadge, width: number, palette: ViewPalette, targetLabel?: string): Line {
|
|
54
54
|
const glyph = steerBadgeGlyph(badge.status);
|
|
55
55
|
const tone = steerBadgeTone(badge.status, palette);
|
|
56
56
|
const forTarget = targetLabel ? ` for ${targetLabel}` : '';
|
|
@@ -59,7 +59,7 @@ export function renderSteerBadgeLine(badge: SteerBadge, width: number, palette:
|
|
|
59
59
|
: badge.status === 'consumed'
|
|
60
60
|
? 'steer consumed'
|
|
61
61
|
: `steer dropped: ${badge.note ?? 'the target ended before delivery'}`;
|
|
62
|
-
return
|
|
62
|
+
return buildViewLine(width, [
|
|
63
63
|
[' ', palette.dim],
|
|
64
64
|
[glyph, tone],
|
|
65
65
|
[` ${label}`, palette.dim],
|
|
@@ -75,7 +75,7 @@ export function renderSteerBadgeLine(badge: SteerBadge, width: number, palette:
|
|
|
75
75
|
* its short linger so a tab doesn't accumulate stale indicators.
|
|
76
76
|
*
|
|
77
77
|
* Mutates `tab.steerBadge` in place (same mutable-slot convention as
|
|
78
|
-
* `FleetTab.ledgerEntries`, see
|
|
78
|
+
* `FleetTab.ledgerEntries`, see src/input/agents-modal.ts's ensureLedgerLoaded).
|
|
79
79
|
* Returns true when anything changed, so the caller knows whether to mark
|
|
80
80
|
* itself dirty.
|
|
81
81
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-stop.ts
|
|
3
3
|
//
|
|
4
|
-
// d1, the "stopping…" write-window overlay, split out of
|
|
4
|
+
// d1, the "stopping…" write-window overlay, split out of src/input/agents-modal.ts
|
|
5
5
|
// to keep that file under the architecture 800-line gate (same rationale as
|
|
6
|
-
// fleet-steer.ts).
|
|
6
|
+
// fleet-steer.ts). The Agents modal owns nothing here beyond a FleetStopTracker
|
|
7
7
|
// instance; all the state->display logic lives in these pure helpers.
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
|
|
@@ -76,7 +76,7 @@ export function fleetStateDisplay(state: ProcessState, stopping: boolean, blocke
|
|
|
76
76
|
return { glyph: fleetStateGlyph(state), label: state, tone: fleetStateTone(state) };
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/** The narrow action + side-effect surface `toggleFleetPause` needs from
|
|
79
|
+
/** The narrow action + side-effect surface `toggleFleetPause` needs from the Agents modal. */
|
|
80
80
|
export interface FleetPauseDeps {
|
|
81
81
|
readonly interrupt: (id: string) => boolean;
|
|
82
82
|
readonly resume: (id: string) => boolean;
|
|
@@ -196,7 +196,7 @@ export function countDescendantStats(rows: readonly FleetTreeRow[], nodeId: stri
|
|
|
196
196
|
/**
|
|
197
197
|
* Build the confirm-overlay arm options for a cascade Kill of one node, the
|
|
198
198
|
* label carries the descendant count so the operator sees what a confirm takes
|
|
199
|
-
* down. Extracted from
|
|
199
|
+
* down. Extracted from the Agents modal to keep that file under the 800-line cap.
|
|
200
200
|
*/
|
|
201
201
|
export function fleetKillConfirmArgs(
|
|
202
202
|
node: ProcessNode,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-transcript.ts
|
|
3
3
|
//
|
|
4
|
-
// Renders the content of an attached
|
|
4
|
+
// Renders the content of an attached Agents modal
|
|
5
5
|
// session tab. Three sources, chosen by what the SDK can actually provide
|
|
6
6
|
// (see the design brief's "central reality check", a full-fidelity live
|
|
7
7
|
// transcript needs a ConversationMessageSnapshot[] history source, which
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// - 'unavailable' , a terminal agent whose snapshot came back empty
|
|
20
20
|
// (evicted past the retention bound, or an agent kind, e.g. the WRFC
|
|
21
21
|
// owner, that never registered a live conversation source at all).
|
|
22
|
-
//
|
|
22
|
+
// the Agents modal degrades to the on-disk event ledger (renderFleetLedgerFallback)
|
|
23
23
|
// for this case; this module never fabricates transcript content.
|
|
24
24
|
//
|
|
25
25
|
// A 'wrfc-chain' tab has no single conversation of its own (it coordinates
|
|
@@ -33,7 +33,7 @@ import { createEmptyLine, type Line } from '@pellux/goodvibes-sdk/platform/types
|
|
|
33
33
|
import type { MessageLineCache } from '../core/conversation-line-cache.ts';
|
|
34
34
|
import type { ConversationRenderContext } from '../core/conversation-rendering.ts';
|
|
35
35
|
import { renderConversationEventLine, renderConversationNotice } from '../renderer/conversation-surface.ts';
|
|
36
|
-
import {
|
|
36
|
+
import { buildViewLine, DEFAULT_VIEW_PALETTE, type ViewPalette } from './polish.ts';
|
|
37
37
|
import { formatElapsed } from '../utils/format-elapsed.ts';
|
|
38
38
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
39
39
|
import {
|
|
@@ -44,16 +44,16 @@ import {
|
|
|
44
44
|
type FleetTreeRow,
|
|
45
45
|
} from './fleet-read-model.ts';
|
|
46
46
|
|
|
47
|
-
/** Duplicated from
|
|
48
|
-
* cross-importing,
|
|
49
|
-
* importing back would form a cycle. See
|
|
47
|
+
/** Duplicated from src/input/agents-modal.ts's private helper (same tiny mapping) rather than
|
|
48
|
+
* cross-importing, src/input/agents-modal.ts imports THIS module to render tab content, so
|
|
49
|
+
* importing back would form a cycle. See src/input/agents-modal.ts's own toneColor for the
|
|
50
50
|
* canonical copy; keep both in sync if the tone set ever changes. */
|
|
51
|
-
function toneColor(tone: FleetStateTone, palette:
|
|
51
|
+
function toneColor(tone: FleetStateTone, palette: ViewPalette): string {
|
|
52
52
|
switch (tone) {
|
|
53
|
-
case 'active': return palette.info ??
|
|
54
|
-
case 'success': return palette.good ??
|
|
55
|
-
case 'failure': return palette.bad ??
|
|
56
|
-
case 'warn': return palette.warn ??
|
|
53
|
+
case 'active': return palette.info ?? DEFAULT_VIEW_PALETTE.info;
|
|
54
|
+
case 'success': return palette.good ?? DEFAULT_VIEW_PALETTE.good;
|
|
55
|
+
case 'failure': return palette.bad ?? DEFAULT_VIEW_PALETTE.bad;
|
|
56
|
+
case 'warn': return palette.warn ?? DEFAULT_VIEW_PALETTE.warn;
|
|
57
57
|
case 'muted': return palette.dim;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -83,11 +83,10 @@ export interface FleetTranscriptRender {
|
|
|
83
83
|
const FROZEN_TRANSCRIPT_NOTICE = 'Read-only: this agent finished; not a live view.';
|
|
84
84
|
|
|
85
85
|
function renderFrozenTranscriptNotice(width: number): Line[] {
|
|
86
|
-
const palette =
|
|
86
|
+
const palette = DEFAULT_VIEW_PALETTE;
|
|
87
87
|
return renderConversationNotice(FROZEN_TRANSCRIPT_NOTICE, width, {
|
|
88
|
-
accent: palette.warn ??
|
|
88
|
+
accent: palette.warn ?? DEFAULT_VIEW_PALETTE.warn,
|
|
89
89
|
text: palette.dim,
|
|
90
|
-
dim: true,
|
|
91
90
|
});
|
|
92
91
|
}
|
|
93
92
|
|
|
@@ -115,7 +114,7 @@ export function renderFleetAgentTranscript(
|
|
|
115
114
|
): FleetTranscriptRender {
|
|
116
115
|
if (snapshot.length === 0) {
|
|
117
116
|
if (isTerminal) return { mode: 'unavailable', lines: [] };
|
|
118
|
-
return { mode: 'live', lines: [
|
|
117
|
+
return { mode: 'live', lines: [buildViewLine(width, [[' (no messages yet)', DEFAULT_VIEW_PALETTE.dim]])] };
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
const historyLines: Line[] = [];
|
|
@@ -174,17 +173,17 @@ export function renderFleetChainSummary(
|
|
|
174
173
|
width: number,
|
|
175
174
|
chainDoneOrAbsent: boolean,
|
|
176
175
|
): Line[] {
|
|
177
|
-
const C =
|
|
176
|
+
const C = DEFAULT_VIEW_PALETTE;
|
|
178
177
|
if (memberRows.length === 0) {
|
|
179
178
|
const message = chainDoneOrAbsent
|
|
180
179
|
? ' chain completed: members no longer tracked'
|
|
181
180
|
: ' (no member agents yet)';
|
|
182
|
-
return [
|
|
181
|
+
return [buildViewLine(width, [[message, C.dim]])];
|
|
183
182
|
}
|
|
184
183
|
return memberRows.map((row) => {
|
|
185
184
|
const node = row.node;
|
|
186
185
|
const color = toneColor(fleetStateTone(node.state), C);
|
|
187
|
-
return
|
|
186
|
+
return buildViewLine(width, [
|
|
188
187
|
[' ', C.dim],
|
|
189
188
|
[fleetStateGlyph(node.state), color],
|
|
190
189
|
[` ${fleetKindTag(node.kind)} `, C.dim],
|
|
@@ -224,7 +223,7 @@ function formatLedgerDuration(ms: unknown): string {
|
|
|
224
223
|
return typeof ms === 'number' ? ` (${formatElapsed(ms)})` : '';
|
|
225
224
|
}
|
|
226
225
|
|
|
227
|
-
function renderLedgerEntry(width: number, entry: Record<string, unknown>, palette:
|
|
226
|
+
function renderLedgerEntry(width: number, entry: Record<string, unknown>, palette: ViewPalette): Line {
|
|
228
227
|
const type = typeof entry['type'] === 'string' ? entry['type'] : 'unknown';
|
|
229
228
|
switch (type) {
|
|
230
229
|
case 'meta': {
|
|
@@ -256,7 +255,7 @@ function renderLedgerEntry(width: number, entry: Record<string, unknown>, palett
|
|
|
256
255
|
case 'tool_execution': {
|
|
257
256
|
const toolName = typeof entry['toolName'] === 'string' ? entry['toolName'] : 'tool';
|
|
258
257
|
const success = entry['success'] !== false;
|
|
259
|
-
const bad = palette.bad ??
|
|
258
|
+
const bad = palette.bad ?? DEFAULT_VIEW_PALETTE.bad;
|
|
260
259
|
// the writer already truncates this to 500 chars (session.ts's
|
|
261
260
|
// resultPreview field), passed through verbatim (collapsed to one
|
|
262
261
|
// line), never re-summarized or fabricated, then tail-truncated again
|
|
@@ -276,9 +275,9 @@ function renderLedgerEntry(width: number, entry: Record<string, unknown>, palett
|
|
|
276
275
|
}
|
|
277
276
|
case 'session_end': {
|
|
278
277
|
const status = typeof entry['status'] === 'string' ? entry['status'] : 'ended';
|
|
279
|
-
const good = palette.good ??
|
|
280
|
-
const bad = palette.bad ??
|
|
281
|
-
const warn = palette.warn ??
|
|
278
|
+
const good = palette.good ?? DEFAULT_VIEW_PALETTE.good;
|
|
279
|
+
const bad = palette.bad ?? DEFAULT_VIEW_PALETTE.bad;
|
|
280
|
+
const warn = palette.warn ?? DEFAULT_VIEW_PALETTE.warn;
|
|
282
281
|
const tone = status === 'completed' ? good : status === 'failed' ? bad : warn;
|
|
283
282
|
return renderConversationEventLine(width, { marker: '■', markerFg: tone, label: 'session', labelFg: tone }, [
|
|
284
283
|
{ text: `${status}${formatLedgerDuration(entry['durationMs'])}`, fg: tone },
|
|
@@ -334,14 +333,14 @@ export function renderFleetLedgerFallback(
|
|
|
334
333
|
width: number,
|
|
335
334
|
height: number,
|
|
336
335
|
): Line[] {
|
|
337
|
-
const palette =
|
|
336
|
+
const palette = DEFAULT_VIEW_PALETTE;
|
|
338
337
|
const notice = renderConversationNotice(
|
|
339
338
|
'Read-only. Full transcript unavailable for this agent; showing its activity log instead.',
|
|
340
339
|
width,
|
|
341
|
-
{ accent: palette.warn, text: palette.dim
|
|
340
|
+
{ accent: palette.warn, text: palette.dim },
|
|
342
341
|
);
|
|
343
342
|
const body = entries.length === 0
|
|
344
|
-
? [
|
|
343
|
+
? [buildViewLine(width, [[' (no activity recorded)', palette.dim]])]
|
|
345
344
|
: entries.map((entry) => renderLedgerEntry(width, entry, palette));
|
|
346
345
|
const budgetHeight = Math.max(0, height - notice.length);
|
|
347
346
|
const visible = body.length > budgetHeight
|
|
@@ -352,5 +351,5 @@ export function renderFleetLedgerFallback(
|
|
|
352
351
|
|
|
353
352
|
/** A blank tab-content placeholder (e.g. while a ledger load is in flight). */
|
|
354
353
|
export function renderFleetTranscriptLoading(width: number): Line[] {
|
|
355
|
-
return [
|
|
354
|
+
return [buildViewLine(width, [[' Loading…', DEFAULT_VIEW_PALETTE.dim]]), createEmptyLine(width)];
|
|
356
355
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// fleet-
|
|
2
|
+
// fleet-worktree-detail.ts
|
|
3
3
|
//
|
|
4
|
-
// NET-NEW helper (not moved code, it was authored here because
|
|
4
|
+
// NET-NEW helper (not moved code, it was authored here because src/input/agents-modal.ts
|
|
5
5
|
// sits at the architecture check's 800-line cap, scripts/check-architecture.ts):
|
|
6
|
-
// a single pure helper for the
|
|
7
|
-
// new
|
|
6
|
+
// a single pure helper for the Agents modal's per-work-item detail view, so a
|
|
7
|
+
// new view feature doesn't have to fight that cap inline.
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
import type { WorkItem } from '@pellux/goodvibes-sdk/platform/orchestration';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Per-item worktree-isolation detail text for the
|
|
14
|
-
* node detail (
|
|
13
|
+
* Per-item worktree-isolation detail text for the Agents modal's expanded
|
|
14
|
+
* node detail (src/input/agents-modal.ts renderDetail), or null for a shared-isolation
|
|
15
15
|
* item (or any node that isn't a work item). Inferred from the item's OWN
|
|
16
16
|
* fields (worktreePath/worktreeBranch/mergeState) rather than a
|
|
17
17
|
* workstream-level flag, adaptWorkItem's ProcessNode.raw carries
|
|
@@ -41,7 +41,7 @@ export function formatWorkItemIsolationDetail(item: WorkItem): string | null {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/** `node.raw` for a 'work-item' ProcessNode is `{ item, workstreamId }` (unknown to the
|
|
44
|
+
/** `node.raw` for a 'work-item' ProcessNode is `{ item, workstreamId }` (unknown to the Agents modal's own types), this narrows without src/input/agents-modal.ts needing its own WorkItem import at the call site. Non-work-item nodes (raw is undefined/some other shape) fall through to null. */
|
|
45
45
|
export function formatWorkItemIsolationDetailFromRaw(raw: unknown): string | null {
|
|
46
46
|
const item = (raw as { item?: WorkItem } | undefined)?.item;
|
|
47
47
|
return item ? formatWorkItemIsolationDetail(item) : null;
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* session it is attached to.
|
|
4
4
|
*
|
|
5
5
|
* One place holds it because two surfaces read it: the `/hosted` command family
|
|
6
|
-
* (which prints status and drives the verbs) and the Hosted Session
|
|
6
|
+
* (which prints status and drives the verbs) and the Hosted Session view
|
|
7
7
|
* (which renders the conversation). The same shared-feed shape
|
|
8
8
|
* notifications-feed.ts already uses in this repo, a module-level instance a
|
|
9
|
-
* command writes and a
|
|
10
|
-
* through the command context and the
|
|
9
|
+
* command writes and a view subscribes to, rather than threading a service
|
|
10
|
+
* through the command context and the Hosted Session view deps for one feature.
|
|
11
11
|
*
|
|
12
12
|
* ── What is a fact here, and what is a rendering ──────────────────────────
|
|
13
13
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* The ROWS are this terminal's rendering of the transcript: the history
|
|
20
20
|
* `attach` backfilled, plus what the live `turn` and `tools` frames have said
|
|
21
|
-
* since. A row that is still being streamed is marked `streaming` so the
|
|
21
|
+
* since. A row that is still being streamed is marked `streaming` so the Hosted Session view
|
|
22
22
|
* can show it growing rather than waiting for the turn to end, the same thing
|
|
23
23
|
* a local turn does.
|
|
24
24
|
*
|
|
@@ -57,7 +57,7 @@ export interface HostedRunningToolCall {
|
|
|
57
57
|
readonly startedAt: number;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
/** Everything the
|
|
60
|
+
/** Everything the Hosted Session view renders and the command reports. */
|
|
61
61
|
export interface HostedSessionFeedState {
|
|
62
62
|
readonly record: HostedSessionRecord | null;
|
|
63
63
|
readonly rows: readonly HostedSessionRow[];
|
|
@@ -340,7 +340,7 @@ export class HostedSessionFeed {
|
|
|
340
340
|
|
|
341
341
|
let sharedFeed: HostedSessionFeed | null = null;
|
|
342
342
|
|
|
343
|
-
/** The one feed the `/hosted` command writes and the Hosted Session
|
|
343
|
+
/** The one feed the `/hosted` command writes and the Hosted Session view reads. */
|
|
344
344
|
export function getSharedHostedSessionFeed(): HostedSessionFeed {
|
|
345
345
|
sharedFeed ??= new HostedSessionFeed();
|
|
346
346
|
return sharedFeed;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modal-surface-registry.ts, the config-modal surfaces by name.
|
|
3
|
+
*
|
|
4
|
+
* Every settings-style modal (services, providers, plugins, hooks, memory and
|
|
5
|
+
* the rest) is a ConfigModalSurface registered here once at startup, with the
|
|
6
|
+
* old names that should open it (for example 'providers' and 'accounts' both
|
|
7
|
+
* open 'providers-modal'). The config-modal host resolves a name through
|
|
8
|
+
* getModalSurface; ctx.openModal and the view router (views.ts) resolve an old
|
|
9
|
+
* name through getModalRedirect first.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ConfigModalSurface, ConfigModalSurfaceRegistry } from '../input/config-modal-types.ts';
|
|
13
|
+
|
|
14
|
+
export class ModalSurfaceRegistry implements ConfigModalSurfaceRegistry {
|
|
15
|
+
private readonly surfaces = new Map<string, ConfigModalSurface>();
|
|
16
|
+
private readonly redirects = new Map<string, string>();
|
|
17
|
+
|
|
18
|
+
/** Register (or replace) the surface for its name. */
|
|
19
|
+
registerModalSurface(surface: ConfigModalSurface): void {
|
|
20
|
+
this.surfaces.set(surface.name, surface);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Make an old name open a modal (e.g. 'accounts' opens 'providers-modal'). */
|
|
24
|
+
registerModalRedirect(name: string, modalName: string): void {
|
|
25
|
+
this.redirects.set(name, modalName);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getModalSurface(name: string): ConfigModalSurface | undefined {
|
|
29
|
+
return this.surfaces.get(name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The modal an old name opens, or undefined. */
|
|
33
|
+
getModalRedirect(name: string): string | undefined {
|
|
34
|
+
return this.redirects.get(name);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Every old name that opens a modal, with the modal it opens. */
|
|
38
|
+
listRedirects(): ReadonlyArray<readonly [string, string]> {
|
|
39
|
+
return [...this.redirects.entries()];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -62,7 +62,7 @@ class DevicesModalSurface implements ConfigModalSurface {
|
|
|
62
62
|
const now = this.deps.now?.() ?? Date.now();
|
|
63
63
|
const tokens = this.deps.pairingTokens.list();
|
|
64
64
|
const rows: ConfigModalRow[] = [];
|
|
65
|
-
rows.push(infoRow('intro', 'Devices paired to this daemon. Each has its own token; revoke one without affecting the others.', {
|
|
65
|
+
rows.push(infoRow('intro', 'Devices paired to this daemon. Each has its own token; revoke one without affecting the others.', { fg: MODAL_TONES.dim }));
|
|
66
66
|
|
|
67
67
|
if (tokens.length === 0) {
|
|
68
68
|
rows.push(infoRow('empty', 'No devices paired yet: run /pair (or open the pairing QR) to add one.'));
|
|
@@ -78,7 +78,7 @@ class DevicesModalSurface implements ConfigModalSurface {
|
|
|
78
78
|
legacyRevoked
|
|
79
79
|
? 'Legacy shared token: revoked.'
|
|
80
80
|
: 'Legacy shared token: active; migrate a device off it (m), then revoke it (s).',
|
|
81
|
-
legacyRevoked ? {
|
|
81
|
+
legacyRevoked ? { fg: MODAL_TONES.dim } : { fg: MODAL_TONES.reasoning },
|
|
82
82
|
));
|
|
83
83
|
|
|
84
84
|
return {
|