@pellux/goodvibes-tui 2.0.14 → 2.0.16
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/.goodvibes/GOODVIBES.md +1 -1
- package/.goodvibes/agents/reviewer.md +8 -8
- package/.goodvibes/skills/add-provider/SKILL.md +22 -22
- package/CHANGELOG.md +49 -0
- package/README.md +64 -44
- package/docs/README.md +6 -6
- package/docs/foundation-artifacts/README.md +5 -5
- package/docs/foundation-artifacts/operator-contract.json +123 -123
- package/package.json +12 -10
- package/scripts/postinstall.js +3 -3
- package/src/audio/activation-sound.ts +7 -7
- package/src/audio/capture.ts +7 -7
- package/src/audio/onnxruntime-assets.d.ts +1 -1
- package/src/audio/player.ts +4 -4
- package/src/audio/spoken-turn-wiring.ts +4 -4
- package/src/audio/voice-capture-wiring.ts +5 -5
- package/src/audio/voice-input-session.ts +3 -3
- package/src/audio/wake-inference.ts +13 -8
- package/src/audio/wake-runtime.ts +10 -10
- package/src/cli/completions/generate.ts +2 -2
- package/src/cli/doctor.ts +13 -13
- package/src/cli/ensure-goodvibes-gitignore.ts +3 -3
- package/src/cli/entrypoint.ts +3 -3
- package/src/cli/help.ts +6 -6
- package/src/cli/hooks-command.ts +4 -4
- package/src/cli/hooks-report.ts +4 -4
- package/src/cli/launch-auto-update.ts +17 -17
- package/src/cli/management-commands.ts +8 -8
- package/src/cli/management-utils.ts +3 -3
- package/src/cli/management.ts +1 -1
- package/src/cli/plugin-bundles-command.ts +10 -10
- package/src/cli/plugin-command.ts +9 -9
- package/src/cli/service-posture.ts +1 -1
- package/src/cli/status.ts +10 -10
- package/src/cli/surface-command.ts +4 -4
- package/src/cli/tui-startup.ts +23 -23
- package/src/config/run-daemon-config-migration.ts +3 -3
- package/src/config/secret-config.ts +10 -10
- package/src/config/surface.ts +1 -1
- package/src/config/tui-extension-settings.ts +8 -8
- package/src/core/active-model-identity.ts +7 -7
- package/src/core/approval-alert.ts +5 -5
- package/src/core/bookmark-navigation.ts +3 -3
- package/src/core/budget-breach-notifier.ts +6 -6
- package/src/core/compaction-receipt.ts +8 -8
- package/src/core/composer-state.ts +2 -2
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-event-navigation.ts +2 -2
- package/src/core/conversation-fold.ts +9 -9
- package/src/core/conversation-line-cache.ts +14 -14
- package/src/core/conversation-render-context.ts +6 -6
- package/src/core/conversation-rendering.ts +27 -30
- package/src/core/conversation-search-expansion.ts +6 -6
- package/src/core/conversation-splash-state.ts +3 -3
- package/src/core/conversation-turn-structure.ts +13 -13
- package/src/core/conversation-types.ts +3 -3
- package/src/core/conversation-user-receipts.ts +5 -5
- package/src/core/conversation.ts +17 -17
- package/src/core/format-user-error.ts +5 -5
- package/src/core/long-task-notifier.ts +9 -9
- package/src/core/memory-diagnostics-gateway.ts +8 -8
- package/src/core/memory-provenance.ts +5 -5
- package/src/core/model-routing-chip.ts +3 -3
- package/src/core/pairing-tailscale-gateway.ts +3 -3
- package/src/core/permission-mode.ts +5 -5
- package/src/core/power-chip-source.ts +3 -3
- package/src/core/power-status.ts +8 -8
- package/src/core/rewind-receipt.ts +6 -6
- package/src/core/session-resume-core.ts +8 -8
- package/src/core/stream-event-wiring.ts +36 -36
- package/src/core/stream-stall-watchdog.ts +4 -4
- package/src/core/system-message-noise.ts +7 -7
- package/src/core/system-message-router.ts +10 -10
- package/src/core/terminal-notifier.ts +5 -5
- package/src/core/turn-budget-outcome.ts +5 -5
- package/src/core/turn-cancellation.ts +3 -3
- package/src/core/turn-event-wiring.ts +16 -16
- package/src/core/voice-capture-status.ts +2 -2
- package/src/core/voice-provision-gateway.ts +12 -12
- package/src/core/voice-provision-status.ts +19 -19
- package/src/core/voice-stt-gateway.ts +6 -6
- package/src/core/wake-provision-runner.ts +7 -7
- package/src/core/wake-provision-status.ts +17 -17
- package/src/core/workstream-notification.ts +8 -8
- package/src/export/gist-uploader.ts +7 -7
- package/src/input/autocomplete.ts +3 -3
- package/src/input/bookmark-modal.ts +1 -1
- package/src/input/command-args-hint.ts +2 -2
- package/src/input/command-reference.ts +9 -9
- package/src/input/command-registry.ts +32 -32
- package/src/input/commands/acp-runtime.ts +6 -6
- package/src/input/commands/calendar-runtime.ts +9 -9
- package/src/input/commands/channel-pairing.ts +11 -11
- package/src/input/commands/channel-runtime.ts +1 -1
- package/src/input/commands/checkin-runtime.ts +4 -4
- package/src/input/commands/checkpoint-runtime.ts +4 -4
- package/src/input/commands/ci-runtime.ts +4 -4
- package/src/input/commands/cluster-runtime.ts +3 -3
- package/src/input/commands/codebase-runtime.ts +16 -16
- package/src/input/commands/config.ts +6 -6
- package/src/input/commands/connection-status.ts +12 -12
- package/src/input/commands/context-window.ts +2 -2
- package/src/input/commands/cost-attribution-format.ts +4 -4
- package/src/input/commands/cost-runtime.ts +2 -2
- package/src/input/commands/devices-runtime.ts +7 -7
- package/src/input/commands/diff-runtime.ts +2 -2
- package/src/input/commands/editor-runtime.ts +1 -1
- package/src/input/commands/eval.ts +4 -4
- package/src/input/commands/experience-runtime.ts +1 -1
- package/src/input/commands/git-runtime.ts +3 -3
- package/src/input/commands/graph-runtime.ts +3 -3
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-metrics-format.ts +2 -2
- package/src/input/commands/health-runtime.ts +8 -8
- package/src/input/commands/hosted-runtime.ts +13 -13
- package/src/input/commands/image-runtime.ts +6 -6
- package/src/input/commands/intelligence-runtime.ts +2 -2
- package/src/input/commands/local-auth-runtime.ts +3 -3
- package/src/input/commands/local-provider-runtime.ts +3 -3
- package/src/input/commands/local-runtime.ts +7 -7
- package/src/input/commands/mail-runtime.ts +9 -9
- package/src/input/commands/mcp-runtime.ts +1 -1
- package/src/input/commands/memory.ts +13 -13
- package/src/input/commands/notify-runtime.ts +1 -1
- package/src/input/commands/operator-panel-runtime.ts +8 -8
- package/src/input/commands/operator-rpc.ts +3 -3
- package/src/input/commands/operator-runtime.ts +3 -3
- package/src/input/commands/palette-runtime.ts +1 -1
- package/src/input/commands/payment-card-intake.ts +17 -17
- package/src/input/commands/permissions-provenance.ts +7 -7
- package/src/input/commands/permissions-runtime.ts +1 -1
- package/src/input/commands/planning-runtime.ts +19 -19
- package/src/input/commands/platform-services-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +6 -6
- package/src/input/commands/power-runtime.ts +6 -6
- package/src/input/commands/principals-runtime.ts +3 -3
- package/src/input/commands/product-runtime.ts +3 -3
- package/src/input/commands/profile-render.ts +13 -13
- package/src/input/commands/profile-runtime.ts +13 -13
- package/src/input/commands/profile-types.ts +13 -13
- package/src/input/commands/provider-accounts-runtime.ts +5 -5
- package/src/input/commands/provider.ts +10 -10
- package/src/input/commands/qrcode-runtime.ts +3 -3
- package/src/input/commands/queue-runtime.ts +6 -6
- package/src/input/commands/recall-files-config.ts +4 -4
- package/src/input/commands/recall-files-git.ts +3 -3
- package/src/input/commands/recall-files.ts +12 -12
- package/src/input/commands/recall-query.ts +3 -3
- package/src/input/commands/recall-review.ts +3 -3
- package/src/input/commands/recall-shared.ts +1 -1
- package/src/input/commands/relay-runtime.ts +5 -5
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/review-runtime.ts +8 -8
- package/src/input/commands/rewind-runtime.ts +21 -21
- package/src/input/commands/runtime-services.ts +1 -2
- package/src/input/commands/schedule-nl.ts +2 -2
- package/src/input/commands/schedule-runtime.ts +2 -2
- package/src/input/commands/secret-runtime.ts +3 -3
- package/src/input/commands/session-content.ts +7 -7
- package/src/input/commands/session-picker-filter.ts +3 -3
- package/src/input/commands/session-workflow.ts +10 -10
- package/src/input/commands/session.ts +15 -15
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +4 -4
- package/src/input/commands/shell-core.ts +11 -11
- package/src/input/commands/tasks-runtime.ts +4 -4
- package/src/input/commands/test-runtime.ts +6 -6
- package/src/input/commands/tts-runtime.ts +4 -4
- package/src/input/commands/update-runtime.ts +27 -27
- package/src/input/commands/websearch-runtime.ts +2 -2
- package/src/input/commands/work-plan-runtime.ts +2 -2
- package/src/input/commands/workstream-attempt-dependents.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +16 -16
- package/src/input/commands/workstream-runtime.ts +36 -36
- package/src/input/commands/worktree-runtime.ts +6 -6
- package/src/input/composer-capture.ts +5 -5
- package/src/input/concealed-input.ts +2 -2
- package/src/input/config-modal-types.ts +16 -16
- package/src/input/config-modal.ts +30 -30
- package/src/input/daemon-owned-settings-descriptions.ts +4 -4
- package/src/input/delete-key-policy.ts +1 -1
- package/src/input/exec-env-scrub-config.ts +7 -7
- package/src/input/feature-unit-layout.ts +5 -5
- package/src/input/feed-context-factory.ts +38 -38
- package/src/input/file-picker.ts +1 -1
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +8 -8
- package/src/input/handler-feed-routes.ts +13 -13
- package/src/input/handler-feed.ts +31 -31
- package/src/input/handler-interactions.ts +2 -2
- package/src/input/handler-modal-routes.ts +13 -13
- package/src/input/handler-modal-stack.ts +1 -1
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +6 -6
- package/src/input/handler-picker-routes.ts +7 -7
- package/src/input/handler-prompt-buffer.ts +1 -1
- package/src/input/handler-shortcuts.ts +12 -12
- package/src/input/handler-types.ts +3 -3
- package/src/input/handler-ui-state.ts +1 -1
- package/src/input/handler.ts +9 -9
- package/src/input/input-history.ts +1 -1
- package/src/input/keybindings.ts +13 -13
- package/src/input/kill-ring.ts +7 -11
- package/src/input/model-picker-filter.ts +3 -3
- package/src/input/model-picker-items.ts +5 -5
- package/src/input/model-picker-types.ts +2 -2
- package/src/input/model-picker.ts +18 -18
- package/src/input/onboarding/onboarding-feature-units.ts +9 -13
- package/src/input/onboarding/onboarding-provider-key-fields.ts +3 -3
- package/src/input/onboarding/onboarding-runtime-status.ts +3 -3
- package/src/input/onboarding/onboarding-wizard-apply.ts +6 -6
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +4 -4
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +6 -6
- package/src/input/onboarding/onboarding-wizard-steps.ts +2 -2
- package/src/input/panel-integration-actions.ts +3 -3
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +9 -9
- package/src/input/payments-config.ts +15 -15
- package/src/input/profile-picker-modal.ts +1 -1
- package/src/input/provider-key-intake.ts +9 -9
- package/src/input/relay-settings-descriptions.ts +3 -3
- package/src/input/sandbox-exec-config.ts +9 -9
- package/src/input/search.ts +18 -18
- package/src/input/selection-modal-queue.ts +10 -10
- package/src/input/selection-modal.ts +2 -2
- package/src/input/session-picker-modal.ts +7 -7
- package/src/input/settings-modal-activation.ts +1 -1
- package/src/input/settings-modal-adjustment.ts +2 -2
- package/src/input/settings-modal-commit.ts +6 -6
- package/src/input/settings-modal-connections.ts +2 -2
- package/src/input/settings-modal-data.ts +29 -29
- package/src/input/settings-modal-mutations.ts +7 -7
- package/src/input/settings-modal-reset.ts +3 -3
- package/src/input/settings-modal-search.ts +6 -6
- package/src/input/settings-modal-secrets.ts +3 -3
- package/src/input/settings-modal-types.ts +16 -16
- package/src/input/settings-modal.ts +4 -4
- package/src/input/voice-local-settings.ts +3 -3
- package/src/input/worktree-setup-config.ts +8 -8
- package/src/main.ts +15 -15
- package/src/panels/agent-inspector-shared.ts +5 -5
- package/src/panels/base-panel.ts +3 -3
- package/src/panels/builtin/agent.ts +3 -3
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +12 -12
- package/src/panels/builtin/session.ts +8 -8
- package/src/panels/builtin/shared.ts +6 -6
- package/src/panels/builtin-modals.ts +8 -8
- package/src/panels/confirm-state.ts +5 -5
- package/src/panels/cost-tracker-panel.ts +22 -22
- package/src/panels/diff-panel.ts +20 -20
- package/src/panels/diff-review-model.ts +5 -5
- package/src/panels/diff-review-panel.ts +5 -5
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/expandable-list-panel.ts +5 -5
- package/src/panels/fleet-acts.ts +20 -20
- package/src/panels/fleet-deep-link.ts +5 -5
- package/src/panels/fleet-gateway.ts +12 -12
- package/src/panels/fleet-observed-render.ts +11 -11
- package/src/panels/fleet-panel-format.ts +14 -14
- package/src/panels/fleet-panel-worktree-detail.ts +8 -8
- package/src/panels/fleet-panel.ts +34 -34
- package/src/panels/fleet-read-model.ts +49 -49
- package/src/panels/fleet-spawn.ts +15 -15
- package/src/panels/fleet-steer.ts +7 -7
- package/src/panels/fleet-stop.ts +5 -5
- package/src/panels/fleet-tabs.ts +14 -14
- package/src/panels/fleet-transcript.ts +26 -26
- package/src/panels/git-panel.ts +8 -8
- package/src/panels/hosted-session-feed.ts +13 -13
- package/src/panels/hosted-session-panel.ts +7 -7
- package/src/panels/index.ts +1 -1
- package/src/panels/local-auth-panel.ts +15 -15
- package/src/panels/memory-consolidation-gateway.ts +8 -8
- package/src/panels/modals/devices-modal.ts +8 -8
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +5 -5
- package/src/panels/modals/knowledge-modal.ts +2 -2
- package/src/panels/modals/local-auth-modal.ts +4 -4
- package/src/panels/modals/marketplace-modal.ts +3 -3
- package/src/panels/modals/memory-modal.ts +19 -19
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +3 -3
- package/src/panels/modals/pairing-modal.ts +19 -19
- package/src/panels/modals/planning-modal.ts +11 -11
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +4 -4
- package/src/panels/modals/provider-health-modal.ts +1 -1
- package/src/panels/modals/remote-modal.ts +1 -1
- package/src/panels/modals/sandbox-modal.ts +2 -2
- package/src/panels/modals/security-modal.ts +4 -4
- package/src/panels/modals/services-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +4 -4
- package/src/panels/notifications-feed.ts +6 -6
- package/src/panels/notifications-panel.ts +6 -6
- package/src/panels/panel-confirm-overlay.ts +4 -4
- package/src/panels/panel-manager.ts +13 -13
- package/src/panels/plugins-panel.ts +6 -6
- package/src/panels/polish-core.ts +2 -2
- package/src/panels/polish-tables.ts +2 -2
- package/src/panels/polish.ts +1 -1
- package/src/panels/project-planning-answer-actions.ts +3 -3
- package/src/panels/scrollable-list-panel.ts +15 -15
- package/src/panels/search-focus.ts +1 -1
- package/src/panels/session-maintenance.ts +1 -1
- package/src/panels/skills-panel.ts +3 -3
- package/src/panels/token-budget-panel.ts +11 -11
- package/src/panels/types.ts +14 -14
- package/src/panels/workstream-graph-render.ts +5 -5
- package/src/panels/wrfc-panel-format.ts +1 -1
- package/src/permissions/broker-approval-card.ts +13 -13
- package/src/permissions/hunk-selection.ts +13 -13
- package/src/permissions/prompt.ts +22 -22
- package/src/permissions/sandbox-exec-gate.ts +5 -5
- package/src/providers/provider-classification.ts +1 -1
- package/src/providers/reasoning-effort-surface.ts +12 -12
- package/src/renderer/ansi-sanitize.ts +5 -6
- package/src/renderer/autocomplete-overlay.ts +5 -5
- package/src/renderer/block-actions-overlay.ts +1 -1
- package/src/renderer/bookmark-modal.ts +1 -1
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +4 -4
- package/src/renderer/compaction-preview.ts +5 -5
- package/src/renderer/compaction-quality.ts +5 -5
- package/src/renderer/composer-fragments.ts +6 -6
- package/src/renderer/compositor.ts +9 -9
- package/src/renderer/config-modal.ts +3 -3
- package/src/renderer/context-inspector.ts +1 -1
- package/src/renderer/context-status-hint.ts +10 -10
- package/src/renderer/conversation-layout.ts +2 -2
- package/src/renderer/conversation-overlays.ts +2 -2
- package/src/renderer/conversation-surface.ts +6 -6
- package/src/renderer/diff.ts +4 -4
- package/src/renderer/fleet-tab-strip.ts +2 -2
- package/src/renderer/footer-tips.ts +4 -4
- package/src/renderer/fullscreen-primitives.ts +1 -1
- package/src/renderer/git-status.ts +5 -5
- package/src/renderer/header-line.ts +5 -5
- package/src/renderer/help-overlay.ts +5 -5
- package/src/renderer/hint-grammar.ts +2 -2
- package/src/renderer/layout.ts +1 -1
- package/src/renderer/markdown-inline.ts +3 -3
- package/src/renderer/markdown-table.ts +7 -7
- package/src/renderer/modal-factory.ts +3 -3
- package/src/renderer/model-picker-overlay.ts +2 -2
- package/src/renderer/model-workspace.ts +7 -7
- package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
- package/src/renderer/overlay-box.ts +1 -1
- package/src/renderer/panel-composite.ts +5 -5
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +8 -8
- package/src/renderer/profile-picker-modal.ts +3 -3
- package/src/renderer/prompt-content-width.ts +1 -1
- package/src/renderer/search-overlay.ts +3 -3
- package/src/renderer/selection-modal-overlay.ts +6 -6
- package/src/renderer/semantic-diff.ts +6 -7
- package/src/renderer/session-picker-modal.ts +24 -24
- package/src/renderer/settings-modal-connections.ts +3 -3
- package/src/renderer/settings-modal-helpers.ts +16 -16
- package/src/renderer/settings-modal.ts +10 -10
- package/src/renderer/shell-surface.ts +8 -8
- package/src/renderer/status-glyphs.ts +6 -6
- package/src/renderer/syntax-highlighter.ts +2 -3
- package/src/renderer/system-message.ts +3 -3
- package/src/renderer/tab-strip.ts +2 -2
- package/src/renderer/term-caps.ts +12 -12
- package/src/renderer/terminal-bg-probe.ts +10 -10
- package/src/renderer/terminal-escapes.ts +1 -1
- package/src/renderer/theme-mode-config.ts +3 -3
- package/src/renderer/theme.ts +34 -34
- package/src/renderer/thinking.ts +1 -1
- package/src/renderer/tool-call.ts +5 -5
- package/src/renderer/tool-result-expanded-lines.ts +3 -3
- package/src/renderer/tool-result-summary.ts +6 -6
- package/src/renderer/turn-injection.ts +9 -9
- package/src/renderer/ui-factory.ts +33 -33
- package/src/renderer/ui-primitives.ts +4 -4
- package/src/runtime/bootstrap-command-parts.ts +3 -3
- package/src/runtime/bootstrap-core.ts +21 -21
- package/src/runtime/bootstrap-hook-bridge.ts +8 -8
- package/src/runtime/bootstrap-shell.ts +9 -9
- package/src/runtime/bootstrap.ts +16 -16
- package/src/runtime/client/approval-updates.ts +3 -3
- package/src/runtime/client/build-floors.ts +7 -7
- package/src/runtime/client/daemon-attach-handshake.ts +5 -5
- package/src/runtime/client/fleet-union.ts +7 -7
- package/src/runtime/client/host-service-status.ts +2 -2
- package/src/runtime/client/hosted-exit.ts +4 -4
- package/src/runtime/client/hosted-roster.ts +4 -4
- package/src/runtime/client/hosted-session-stream.ts +5 -5
- package/src/runtime/client/hosted-sessions.ts +8 -8
- package/src/runtime/client/operator-endpoint.ts +14 -14
- package/src/runtime/client/spine-adoption.ts +8 -8
- package/src/runtime/context-accounting-source.ts +4 -4
- package/src/runtime/context.ts +2 -2
- package/src/runtime/conversation-rewind-port.ts +6 -6
- package/src/runtime/credential-composition.ts +2 -2
- package/src/runtime/daemon-attach-notices.ts +6 -6
- package/src/runtime/diagnostics/panels/index.ts +1 -1
- package/src/runtime/diagnostics/panels/ops.ts +2 -2
- package/src/runtime/disposal-wiring.ts +6 -6
- package/src/runtime/index.ts +11 -11
- package/src/runtime/install-self-check-startup.ts +4 -4
- package/src/runtime/install-self-check.ts +6 -6
- package/src/runtime/interaction-seams.ts +4 -4
- package/src/runtime/knowledge-services.ts +2 -2
- package/src/runtime/legacy-daemon-migration.ts +37 -37
- package/src/runtime/legacy-daemon-reconcile.ts +30 -30
- package/src/runtime/memory-governance-services.ts +3 -3
- package/src/runtime/memory-spine-transport.ts +3 -3
- package/src/runtime/notification-dispatch.ts +4 -4
- package/src/runtime/onboarding/apply.ts +1 -1
- package/src/runtime/onboarding/derivation.ts +1 -1
- package/src/runtime/onboarding/fast-path.ts +7 -7
- package/src/runtime/onboarding/progress.ts +2 -2
- package/src/runtime/onboarding/provider-key-capture.ts +4 -4
- package/src/runtime/onboarding/snapshot.ts +2 -2
- package/src/runtime/onboarding/state.ts +4 -4
- package/src/runtime/onboarding/types.ts +4 -4
- package/src/runtime/orchestrator-core-services.ts +5 -5
- package/src/runtime/path-shadow-startup.ts +3 -3
- package/src/runtime/perf/panel-contracts.ts +1 -1
- package/src/runtime/power-keepawake-remote.ts +6 -6
- package/src/runtime/process-lifecycle.ts +14 -14
- package/src/runtime/provider-fallback.ts +3 -3
- package/src/runtime/recovery-autosave.ts +3 -3
- package/src/runtime/recovery-decisions.ts +19 -19
- package/src/runtime/recovery-offer-wiring.ts +4 -4
- package/src/runtime/recovery-prompt.ts +20 -20
- package/src/runtime/relay-reachability-bridge.ts +3 -3
- package/src/runtime/release-artifacts.ts +1 -1
- package/src/runtime/resume-notice.ts +20 -20
- package/src/runtime/runtime-services-types.ts +20 -20
- package/src/runtime/services.ts +20 -20
- package/src/runtime/session-continuity-hints.ts +1 -1
- package/src/runtime/session-inbound-inputs.ts +12 -12
- package/src/runtime/session-resume-liveness-confirm.ts +4 -4
- package/src/runtime/session-spine-transport.ts +3 -3
- package/src/runtime/store/domains/panels.ts +2 -2
- package/src/runtime/store/domains/ui-perf.ts +2 -2
- package/src/runtime/store/index.ts +1 -1
- package/src/runtime/store/selectors/index.ts +1 -1
- package/src/runtime/store/state.ts +2 -2
- package/src/runtime/ui-services.ts +3 -3
- package/src/runtime/workspace-checkpointing.ts +3 -3
- package/src/runtime/wrfc-persistence.ts +15 -15
- package/src/scripts/process-messages.ts +1 -1
- package/src/shell/blocking-input.ts +9 -9
- package/src/shell/recovery-input-helpers.ts +2 -2
- package/src/shell/retry-affordance.ts +9 -9
- package/src/shell/ui-openers.ts +14 -14
- package/src/shell/voice-capture-shell.ts +3 -3
- package/src/tools/wrfc-agent-guard.ts +4 -4
- package/src/utils/browser.ts +2 -2
- package/src/utils/format-duration.ts +3 -3
- package/src/utils/format-number.ts +2 -2
- package/src/utils/splash-lines.ts +4 -4
- package/src/utils/terminal-width.ts +6 -6
- package/src/verification/verification-ledger.ts +25 -25
- package/src/version.ts +3 -3
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-panel.ts
|
|
3
3
|
//
|
|
4
|
-
|
|
4
|
+
//, FleetPanel: the live unified observability tree. Renders the
|
|
5
5
|
// registered process fleet (agents incl. WRFC roles, WRFC chains/subtasks,
|
|
6
6
|
// workflow-tool FSMs, watchers, background processes) as a depth-first tree
|
|
7
7
|
// with a selected-row detail region. Session tabs (Part C): Enter on an
|
|
8
8
|
// attachable node (agent/wrfc-chain) opens a tab with that process's
|
|
9
|
-
// transcript/member summary
|
|
9
|
+
// transcript/member summary, see fleet-tabs.ts + fleet-transcript.ts. This
|
|
10
10
|
// file owns input dispatch and layout; fleet-deep-link.ts owns the
|
|
11
11
|
// cross-panel jump target (item 4).
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
@@ -53,14 +53,14 @@ const C = DEFAULT_PANEL_PALETTE;
|
|
|
53
53
|
export interface FleetActionCallbacks {
|
|
54
54
|
/** Graceful interruption (AgentManager.cancel / trigger-schedule disable, via the registry). */
|
|
55
55
|
readonly interrupt: (id: string) => boolean;
|
|
56
|
-
/** Re-arm a `paused` node (disabled trigger/schedule or automation job)
|
|
56
|
+
/** Re-arm a `paused` node (disabled trigger/schedule or automation job), interrupt()'s inverse. Honest false when not paused / non-resumable. */
|
|
57
57
|
readonly resume: (id: string) => boolean;
|
|
58
58
|
/** Hard stop, optionally cascading to killable descendants. Returns the node ids acted on. */
|
|
59
59
|
readonly kill: (id: string, opts: { readonly cascade: boolean }) => readonly string[];
|
|
60
60
|
/**
|
|
61
|
-
* Fetch an agent's conversation history
|
|
61
|
+
* Fetch an agent's conversation history, live (running) or
|
|
62
62
|
* frozen (just-completed, still in the SDK's retention ring). Empty array
|
|
63
|
-
* means "unavailable" (evicted, or never registered)
|
|
63
|
+
* means "unavailable" (evicted, or never registered), FleetPanel degrades
|
|
64
64
|
* to the on-disk ledger fallback in that case, never fabricating content.
|
|
65
65
|
*/
|
|
66
66
|
readonly getConversationSnapshot: (agentId: string) => readonly ConversationMessageSnapshot[];
|
|
@@ -69,7 +69,7 @@ export interface FleetActionCallbacks {
|
|
|
69
69
|
/**
|
|
70
70
|
* Queue a human message for a live in-process agent (or a
|
|
71
71
|
* wrfc-subtask's current live member agent), delivered at the target's
|
|
72
|
-
* next turn boundary
|
|
72
|
+
* next turn boundary, never mid-token. Honest refusal
|
|
73
73
|
* (`{queued:false,reason}`) for anything that cannot take mid-run input
|
|
74
74
|
* (terminal nodes, non-agent kinds, the wrfc-chain coordinator itself).
|
|
75
75
|
*/
|
|
@@ -112,7 +112,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
112
112
|
actions: Partial<FleetActionCallbacks> = {},
|
|
113
113
|
private readonly configManager: ConfigManager | null = null,
|
|
114
114
|
// The waiting-on-human acts (pick / conflict / discard). Null in a runtime
|
|
115
|
-
// with no daemon gateway wired
|
|
115
|
+
// with no daemon gateway wired, those keys then fall through honestly.
|
|
116
116
|
private readonly acts: FleetActs | null = null,
|
|
117
117
|
// The ACP spawn affordance ('n' hosts a discovered third-party agent); null with no gateway wired.
|
|
118
118
|
private readonly spawn: FleetSpawn | null = null,
|
|
@@ -128,7 +128,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
128
128
|
this.markDirty();
|
|
129
129
|
});
|
|
130
130
|
// The honest "the agent actually consumed this steer at
|
|
131
|
-
// its turn boundary" signal
|
|
131
|
+
// its turn boundary" signal, see fleet-tabs.ts's SteerBadgeStatus doc.
|
|
132
132
|
// A read-model without a runtimeBus dep never invokes this (graceful
|
|
133
133
|
// no-op), same degrade as steer()/steerable without a messageBus dep.
|
|
134
134
|
this.unsubConsumed = readModel.subscribeConsumed((event) => {
|
|
@@ -184,7 +184,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
|
-
* True while the steer composer on the active tab is open
|
|
187
|
+
* True while the steer composer on the active tab is open,
|
|
188
188
|
* every character of a burst (paste, or several fast-typed chars in one
|
|
189
189
|
* stdin chunk) must land in the draft one at a time rather than being
|
|
190
190
|
* routed away as panel hotkeys or exploded to the main composer. See
|
|
@@ -231,21 +231,21 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
231
231
|
|
|
232
232
|
protected override getEmptyStateMessage(): string {
|
|
233
233
|
return this.viewMode === 'archived'
|
|
234
|
-
? ' Archive is empty
|
|
234
|
+
? ' Archive is empty: press a on a finished process (or A for all) to move it here.'
|
|
235
235
|
: ' No processes tracked yet.';
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
/**
|
|
239
239
|
* (cross-restart honesty): a restart starts from an empty `AgentManager` Map
|
|
240
240
|
* (no bridge from a prior registry), so a completed process from a previous
|
|
241
|
-
* session can never reappear here
|
|
241
|
+
* session can never reappear here, a real, permanent limitation, documented
|
|
242
242
|
* rather than implying "nothing has ever run" or that a restart brings it back.
|
|
243
243
|
*/
|
|
244
244
|
protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
245
245
|
return [
|
|
246
246
|
{
|
|
247
247
|
command: 'previous sessions',
|
|
248
|
-
summary: "not shown here
|
|
248
|
+
summary: "not shown here: this list resets on TUI restart; each agent's .goodvibes/tui/sessions/<id>.jsonl event log still persists on disk",
|
|
249
249
|
},
|
|
250
250
|
];
|
|
251
251
|
}
|
|
@@ -281,7 +281,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
281
281
|
* Re-locate `selectedNodeId` in the current snapshot and repoint
|
|
282
282
|
* `selectedIndex` at it, so the selection follows a specific process across
|
|
283
283
|
* adds/removes/reorders instead of drifting onto whatever row occupies the old
|
|
284
|
-
* index. When the anchored node is gone (pruned)
|
|
284
|
+
* index. When the anchored node is gone (pruned), or no anchor exists yet,
|
|
285
285
|
* falls back to clampSelection() (nearest valid index) and re-anchors there.
|
|
286
286
|
*/
|
|
287
287
|
private reanchorSelection(): void {
|
|
@@ -308,7 +308,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
308
308
|
if (idx < 0) { this.setError('node no longer present.'); return; }
|
|
309
309
|
// A successful reveal must be VISIBLE: with a session tab active, render()
|
|
310
310
|
// routes to the tab view and the selection change would be silently
|
|
311
|
-
// swallowed (batch refutation finding 1)
|
|
311
|
+
// swallowed (batch refutation finding 1), return to the tree first
|
|
312
312
|
// (tabs kept; only the active view switches).
|
|
313
313
|
this.tabsState = { tabs: this.tabsState.tabs, activeTabIndex: 0 };
|
|
314
314
|
this.clearError(); this.selectedIndex = idx; this.selectedNodeId = target.id; this.markDirty();
|
|
@@ -394,7 +394,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
394
394
|
// Once the steer composer is open on the active tab, it
|
|
395
395
|
// owns EVERY key (same full-priority-while-composing contract as
|
|
396
396
|
// git-panel.ts's commitMessage entry) until Enter (submit) or Esc
|
|
397
|
-
// (cancel)
|
|
397
|
+
// (cancel), a burst/paste and single hotkeys like 'j'/'s'/'[' all land
|
|
398
398
|
// in the draft, never as tree/tab navigation (focus rule).
|
|
399
399
|
const composingTab = activeFleetTab(this.tabsState);
|
|
400
400
|
if (composingTab && composingTab.steerDraft !== null) {
|
|
@@ -402,9 +402,9 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
// Tab switching works from ANY view (root tree or an attached tab) once
|
|
405
|
-
// at least one tab is open
|
|
405
|
+
// at least one tab is open, [ / ] step through Tree, tab 1, tab 2, ...
|
|
406
406
|
// Ctrl+X detach is handled by interceptPanelClose(), not handleInput (it
|
|
407
|
-
// must win a global-shortcut race
|
|
407
|
+
// must win a global-shortcut race, see that method's doc comment).
|
|
408
408
|
if ((key === '[' || key === ']') && this.tabsState.tabs.length > 0) {
|
|
409
409
|
this.tabsState = stepFleetTab(this.tabsState, key === ']' ? 1 : -1);
|
|
410
410
|
this.markDirty();
|
|
@@ -422,7 +422,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
422
422
|
if (key === 's') {
|
|
423
423
|
return this.tryOpenSteerDraft(activeFleetTab(this.tabsState)!);
|
|
424
424
|
}
|
|
425
|
-
// A session tab is focused
|
|
425
|
+
// A session tab is focused, the tree isn't visible, so tree-only keys
|
|
426
426
|
// (navigate/i/K/f/Enter) don't apply here.
|
|
427
427
|
return false;
|
|
428
428
|
}
|
|
@@ -477,7 +477,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
477
477
|
// Guard: only consume on a real, non-terminal node (cockpit-panel.ts
|
|
478
478
|
// precedent) so the key falls through when there is nothing to act on.
|
|
479
479
|
if (!selected || isTerminalProcessState(selected.node.state)) return false;
|
|
480
|
-
// Not every kind can be interrupted (only 'agent' nodes ever are
|
|
480
|
+
// Not every kind can be interrupted (only 'agent' nodes ever are, see
|
|
481
481
|
// the SDK's fleet adapters: schedule/trigger/watcher/workflow/
|
|
482
482
|
// wrfc-chain/wrfc-subtask/background-process all report
|
|
483
483
|
// capabilities.interruptible: false unconditionally). Consume the key
|
|
@@ -495,7 +495,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
495
495
|
|
|
496
496
|
if (key === 'K') {
|
|
497
497
|
if (!selected || isTerminalProcessState(selected.node.state)) return false;
|
|
498
|
-
// Observed foreign agents have killable:false
|
|
498
|
+
// Observed foreign agents have killable:false, the refusal below reads
|
|
499
499
|
// honestly; goodvibes only observes them, so stop is never offered.
|
|
500
500
|
if (!selected.node.capabilities.killable) {
|
|
501
501
|
this.setError(`${selected.node.kind} does not support kill.`);
|
|
@@ -541,7 +541,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
541
541
|
}
|
|
542
542
|
|
|
543
543
|
if (key === 's') {
|
|
544
|
-
// An earlier replay fix: 's' from the TREE was silently dead
|
|
544
|
+
// An earlier replay fix: 's' from the TREE was silently dead, steering
|
|
545
545
|
// required an undiscoverable Enter-attach first. Attach-and-steer in
|
|
546
546
|
// one press for a steerable node; honest refusal otherwise.
|
|
547
547
|
if (!selected) return false;
|
|
@@ -550,10 +550,10 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
550
550
|
// acts.handleTreeKey above (opens the composer in the row's detail), so it
|
|
551
551
|
// never reaches this attach path.
|
|
552
552
|
// Closure replay note: a node that FINISHED in the hint→keypress race
|
|
553
|
-
// window must say so
|
|
553
|
+
// window must say so, "does not support steer" is misleading for an
|
|
554
554
|
// agent whose only problem is being done.
|
|
555
555
|
if (isTerminalProcessState(node.state)) {
|
|
556
|
-
this.setError(`${node.kind} already finished
|
|
556
|
+
this.setError(`${node.kind} already finished: nothing to steer.`);
|
|
557
557
|
return true;
|
|
558
558
|
}
|
|
559
559
|
if (!node.capabilities.steerable || !isAttachableFleetKind(node.kind)) {
|
|
@@ -571,14 +571,14 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
571
571
|
}
|
|
572
572
|
|
|
573
573
|
/**
|
|
574
|
-
* The `s` gate
|
|
574
|
+
* The `s` gate, mirrors the i/K capability-gating template
|
|
575
575
|
* exactly (guard on the live node existing, non-terminal, and the specific
|
|
576
576
|
* capability; consume the key and say so rather than silently no-op-ing).
|
|
577
577
|
*/
|
|
578
578
|
private tryOpenSteerDraft(tab: FleetTab): boolean {
|
|
579
579
|
const node = this.findLiveNode(tab.nodeId);
|
|
580
580
|
if (!node || isTerminalProcessState(node.state)) {
|
|
581
|
-
this.setError(`${node?.kind ?? tab.kind} already finished
|
|
581
|
+
this.setError(`${node?.kind ?? tab.kind} already finished: nothing to steer.`);
|
|
582
582
|
return true;
|
|
583
583
|
}
|
|
584
584
|
if (!node.capabilities.steerable) {
|
|
@@ -590,7 +590,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
590
590
|
return true;
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
-
/** Dispatch for the open steer composer
|
|
593
|
+
/** Dispatch for the open steer composer, mirrors git-panel.ts's handleCommitMessageInput. */
|
|
594
594
|
private handleSteerInput(tab: FleetTab, key: string): boolean {
|
|
595
595
|
if (isPanelSearchCancel(key)) {
|
|
596
596
|
tab.steerDraft = null;
|
|
@@ -617,7 +617,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
617
617
|
return true;
|
|
618
618
|
}
|
|
619
619
|
// Ordinary typing AND a pasted multi-line block's line-break characters
|
|
620
|
-
// (delivered one at a time through the same burst pipeline
|
|
620
|
+
// (delivered one at a time through the same burst pipeline, see
|
|
621
621
|
// isCapturingTextBurst) both flow through appendSteerText, which
|
|
622
622
|
// normalizes \r/\n to a collapsed space instead of corrupting the
|
|
623
623
|
// one-line field or silently dropping the content.
|
|
@@ -658,7 +658,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
658
658
|
return renderFleetRowLine(row, width, stopping, blocked, this.steerBadgeForNode(row.node.id), C);
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
-
/** A node's steer badge, looked up via its attached tab (if any)
|
|
661
|
+
/** A node's steer badge, looked up via its attached tab (if any), null when the node has no tab or no active/recent steer. */
|
|
662
662
|
private steerBadgeForNode(nodeId: string): SteerBadge | null {
|
|
663
663
|
return this.tabsState.tabs.find((t) => t.nodeId === nodeId)?.steerBadge ?? null;
|
|
664
664
|
}
|
|
@@ -686,7 +686,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
686
686
|
const snapshotRows = this.readModel.getSnapshot().rows;
|
|
687
687
|
const liveNode = snapshotRows.find((row) => row.node.id === tab.nodeId)?.node ?? null;
|
|
688
688
|
// The steer composer only ever applies to 'agent' tabs
|
|
689
|
-
// (wrfc-chain has no conversation loop of its own
|
|
689
|
+
// (wrfc-chain has no conversation loop of its own, see
|
|
690
690
|
// fleet-tabs.ts's FleetAttachableKind doc); the live-node lookup is
|
|
691
691
|
// shared with the isTerminal computation below either way.
|
|
692
692
|
const canSteer = liveNode !== null && !isTerminalProcessState(liveNode.state) && liveNode.capabilities.steerable;
|
|
@@ -714,7 +714,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
714
714
|
));
|
|
715
715
|
} else if (tab.steerBadge && !this.stopTracker.isStopping(tab.nodeId)) {
|
|
716
716
|
// A queued-steer promise ("delivers on its next turn") must not render
|
|
717
|
-
// while a stop is in flight for this node
|
|
717
|
+
// while a stop is in flight for this node, there may be no next turn
|
|
718
718
|
// (refutation finding 4). The badge reconciles to dropped/consumed once
|
|
719
719
|
// the node's terminal state lands.
|
|
720
720
|
composerLines.push(renderSteerBadgeLine(tab.steerBadge, width, palette, liveNode?.label));
|
|
@@ -756,7 +756,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
756
756
|
...(stripLine ? [{ lines: [stripLine] }] : []),
|
|
757
757
|
{ lines: contentLines },
|
|
758
758
|
];
|
|
759
|
-
return buildPanelWorkspace(width, height, { title: `Fleet
|
|
759
|
+
return buildPanelWorkspace(width, height, { title: `Fleet: ${tab.label}`, sections, footerLines, palette });
|
|
760
760
|
}
|
|
761
761
|
|
|
762
762
|
private renderTreeView(width: number, height: number): Line[] {
|
|
@@ -768,7 +768,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
768
768
|
footer.push(...this.renderDetail(selected, width));
|
|
769
769
|
}
|
|
770
770
|
|
|
771
|
-
// Capability-gated tree hints
|
|
771
|
+
// Capability-gated tree hints, only advertise i/K/p when the selected node
|
|
772
772
|
// can actually accept them (most kinds are never interruptible; nothing is
|
|
773
773
|
// killable once terminal), incl. the d2 state-dependent p pause/resume chip.
|
|
774
774
|
// Mirrors the handleInput guards above. See buildFleetTreeHints (fleet-stop.ts).
|
|
@@ -777,13 +777,13 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
777
777
|
const blockedCount = this.readModel.getSnapshot().blockedNodeIds.length;
|
|
778
778
|
const hints = buildFleetTreeHints(selected?.node, this.follow, this.tabsState.tabs.length > 0, this.viewMode, blockedCount);
|
|
779
779
|
|
|
780
|
-
// Tab strip renders only when tabs exist
|
|
780
|
+
// Tab strip renders only when tabs exist, omitting it entirely with no
|
|
781
781
|
// tabs attached keeps the pre-session-tab root-tree rendering byte-identical.
|
|
782
782
|
const stripLine = renderFleetTabStrip(this.tabsState, width);
|
|
783
783
|
const header = stripLine ? [stripLine] : undefined;
|
|
784
784
|
|
|
785
785
|
return this.renderList(width, height, {
|
|
786
|
-
title: this.viewMode === 'archived' ? 'Fleet
|
|
786
|
+
title: this.viewMode === 'archived' ? 'Fleet: Archived' : 'Fleet',
|
|
787
787
|
header,
|
|
788
788
|
footer,
|
|
789
789
|
hints,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-read-model.ts
|
|
3
3
|
//
|
|
4
|
-
// (Fleet tree panel)
|
|
4
|
+
// (Fleet tree panel), pure, testable read-model over the SDK's live
|
|
5
5
|
// process registry (@pellux/goodvibes-sdk/platform/runtime/fleet, landed on
|
|
6
6
|
// SDK main as). No BasePanel/rendering dependency: this module owns the
|
|
7
7
|
// flat ProcessNode[] -> tree transformation, the honest cost/token
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
// FleetRegistryNode/FleetRegistry adapter pair. The real
|
|
13
13
|
// createProcessRegistry() ProcessNode already carries elapsedMs,
|
|
14
14
|
// usage, costUsd, costState, and currentActivity directly, so no
|
|
15
|
-
// adapter layer is needed here
|
|
15
|
+
// adapter layer is needed here, this module consumes ProcessNode
|
|
16
16
|
// straight from the registry and only owns the tree-walk + aggregate
|
|
17
17
|
// + presentation-mapping logic the SDK does not provide.
|
|
18
18
|
// - Tree-walk shape (connectors, cycle guard, leftover pass) is ported
|
|
19
19
|
// from renderer/process-modal.ts's appendAgentSubtree/
|
|
20
20
|
// appendAgentGroupEntries, generalized from AgentRecord to ProcessNode
|
|
21
21
|
// and from WRFC-role ordering to plain startedAt ordering (ProcessNode
|
|
22
|
-
// has no role concept
|
|
22
|
+
// has no role concept, parentId alone expresses the hierarchy, and the
|
|
23
23
|
// SDK guarantees every parentId either resolves or the node is a root).
|
|
24
24
|
// - Cost/token honesty mirrors agent-inspector-shared.ts's
|
|
25
25
|
// hasReportedUsage convention: an all-zero-but-present usage object is
|
|
@@ -65,8 +65,8 @@ export interface FleetSnapshot {
|
|
|
65
65
|
/** Count of nodes in an actively-working state (see isRunningProcessState). */
|
|
66
66
|
readonly runningCount: number;
|
|
67
67
|
/**
|
|
68
|
-
* Ids of the nodes that are "blocked on me"
|
|
69
|
-
* user input right now (see isBlockedOnUserState)
|
|
68
|
+
* Ids of the nodes that are "blocked on me", waiting on a user approval or
|
|
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
71
|
* never a second source of truth: the panel reads it to badge/jump, it is
|
|
72
72
|
* not a store. Empty when nothing is waiting on the operator.
|
|
@@ -76,20 +76,20 @@ export interface FleetSnapshot {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// ---------------------------------------------------------------------------
|
|
79
|
-
// State classification
|
|
79
|
+
// State classification, glyph/tone mapping (pure, unit-testable)
|
|
80
80
|
// ---------------------------------------------------------------------------
|
|
81
81
|
|
|
82
82
|
export type FleetStateTone = 'active' | 'success' | 'failure' | 'warn' | 'muted';
|
|
83
83
|
|
|
84
84
|
// Design ruling: this is a DIFFERENT table from the SDK presentation
|
|
85
|
-
// contract's STATE_GLYPHS (@pellux/goodvibes-sdk/platform/presentation)
|
|
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
|
// Fleet tree panel (thinking/executing-tool/awaiting-approval/streaming/
|
|
89
89
|
// stalled/retrying/done/failed/killed/interrupted/idle/queued/paused), and no
|
|
90
90
|
// other surface (checked: the agent has no fleet-panel renderer) consumes it
|
|
91
91
|
// today. Per the presentation-parity brief, a table only moves to the SDK
|
|
92
|
-
// contract when 2+ surfaces need it
|
|
92
|
+
// contract when 2+ surfaces need it, this one stays TUI-renderer-local.
|
|
93
93
|
const STATE_GLYPHS: Record<ProcessState, string> = {
|
|
94
94
|
thinking: '◔',
|
|
95
95
|
'executing-tool': '●',
|
|
@@ -100,7 +100,7 @@ const STATE_GLYPHS: Record<ProcessState, string> = {
|
|
|
100
100
|
done: '✓',
|
|
101
101
|
failed: '✗',
|
|
102
102
|
killed: '⊘',
|
|
103
|
-
// A distinct TERMINAL outcome from 'killed'
|
|
103
|
+
// A distinct TERMINAL outcome from 'killed',
|
|
104
104
|
// both come from AgentManager.cancel(), but a graceful interrupt request
|
|
105
105
|
// ('the operator asked nicely') is display-distinguishable from a hard
|
|
106
106
|
// kill. '◌' verified free against every other glyph in this table.
|
|
@@ -108,7 +108,7 @@ const STATE_GLYPHS: Record<ProcessState, string> = {
|
|
|
108
108
|
idle: '·',
|
|
109
109
|
queued: '…',
|
|
110
110
|
// SDK behavior: schedules/triggers/automation jobs report 'paused' when
|
|
111
|
-
// disabled (previously mislabeled 'killed'). NOT terminal
|
|
111
|
+
// disabled (previously mislabeled 'killed'). NOT terminal, resumable via
|
|
112
112
|
// ProcessRegistry.resume() (the full pause/resume UI lives in
|
|
113
113
|
// fleet-stop.ts). '❚' verified free against every other glyph in this table.
|
|
114
114
|
paused: '❚',
|
|
@@ -124,7 +124,7 @@ const STATE_TONES: Record<ProcessState, FleetStateTone> = {
|
|
|
124
124
|
done: 'success',
|
|
125
125
|
failed: 'failure',
|
|
126
126
|
killed: 'muted',
|
|
127
|
-
// 'warn' (amber), not 'muted' like killed
|
|
127
|
+
// 'warn' (amber), not 'muted' like killed, an interrupt is a deliberate
|
|
128
128
|
// operator action worth noticing, not a passive/neutral outcome.
|
|
129
129
|
interrupted: 'warn',
|
|
130
130
|
idle: 'muted',
|
|
@@ -133,13 +133,13 @@ const STATE_TONES: Record<ProcessState, FleetStateTone> = {
|
|
|
133
133
|
paused: 'muted',
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
-
/** Terminal states
|
|
136
|
+
/** Terminal states, interrupt/kill are not offered; not counted as running. */
|
|
137
137
|
const TERMINAL_STATES = new Set<ProcessState>(['done', 'failed', 'killed', 'interrupted']);
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
140
|
* States that mean "this node is waiting on ME right now" via the state field
|
|
141
141
|
* alone. The registry now also publishes the canonical `needsAttention`
|
|
142
|
-
* projection (reason 'approval' | 'input'
|
|
142
|
+
* projection (reason 'approval' | 'input', see fleetNodeAttention below),
|
|
143
143
|
* which every surface prefers; this set remains only as the fallback mapping
|
|
144
144
|
* for nodes without the projection, so nothing regresses on older registries.
|
|
145
145
|
*/
|
|
@@ -164,16 +164,16 @@ const KIND_TAGS: Record<ProcessKind, string> = {
|
|
|
164
164
|
phase: 'phase',
|
|
165
165
|
'work-item': 'item',
|
|
166
166
|
// The repo source-tree code index (adapters/code-index.ts).
|
|
167
|
-
// A single leaf node
|
|
168
|
-
// ROLLUP_KINDS below)
|
|
167
|
+
// A single leaf node, never a rollup of other flat-list nodes (see
|
|
168
|
+
// ROLLUP_KINDS below), so no other list in this module needs updating.
|
|
169
169
|
'code-index': 'index',
|
|
170
170
|
// A hosted third-party coding agent (Claude Code / Codex / opencode) spawned
|
|
171
|
-
// over ACP (adapters/acp-host.ts). A plain leaf row
|
|
171
|
+
// over ACP (adapters/acp-host.ts). A plain leaf row, steer/stop/attention
|
|
172
172
|
// ride on its node capabilities and the SDK's needsAttention projection, so
|
|
173
173
|
// this glyph/label is the only read-model special-casing it needs.
|
|
174
174
|
'acp-agent': 'acp',
|
|
175
175
|
// An externally-launched foreign coding agent (Claude Code / Codex / opencode)
|
|
176
|
-
// that goodvibes did NOT spawn
|
|
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
179
|
// only. See ObservedProcess handling in fleet-panel-format.ts.
|
|
@@ -200,14 +200,14 @@ export function isRunningProcessState(state: ProcessState): boolean {
|
|
|
200
200
|
return RUNNING_STATES.has(state);
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
/** True when a node is waiting on a user approval or user input right now
|
|
203
|
+
/** True when a node is waiting on a user approval or user input right now, "blocked on me". */
|
|
204
204
|
export function isBlockedOnUserState(state: ProcessState): boolean {
|
|
205
205
|
return BLOCKED_ON_USER_STATES.has(state);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
/**
|
|
209
209
|
* The node's waiting-on-human classification, preferring the SDK's canonical
|
|
210
|
-
* `needsAttention` projection (reason 'approval' | 'input'
|
|
210
|
+
* `needsAttention` projection (reason 'approval' | 'input', broader than the
|
|
211
211
|
* awaiting-approval state alone: it also covers a session waiting on user
|
|
212
212
|
* input). Falls back to the state-derived mapping for registries that predate
|
|
213
213
|
* the projection, so nothing regresses. This is THE membership every
|
|
@@ -228,11 +228,11 @@ export function isBlockedOnUserNode(node: ProcessNode): boolean {
|
|
|
228
228
|
* The literal badge text for a waiting-on-human node, by reason. Every reason
|
|
229
229
|
* is a first-class member of the ONE waiting-on-human state class (same ⚑
|
|
230
230
|
* glyph, same jump key, same count) but names WHY in its own words:
|
|
231
|
-
* - 'approval'
|
|
232
|
-
* - 'input'
|
|
233
|
-
* - 'pick'
|
|
231
|
+
* - 'approval', a tool call is held for an approve/deny decision.
|
|
232
|
+
* - 'input' , the node is otherwise blocked on operator input.
|
|
233
|
+
* - 'pick' , a best-of-N attempt group is ready and only a human's
|
|
234
234
|
* winner pick advances it.
|
|
235
|
-
* - 'conflict'
|
|
235
|
+
* - 'conflict', a merge conflict needs a human resolution before the work
|
|
236
236
|
* can land.
|
|
237
237
|
* The pick/conflict wording is deliberately DISTINCT from the bare
|
|
238
238
|
* 'blocked on you' so an operator reads the required act, not just the state.
|
|
@@ -248,7 +248,7 @@ export function fleetAttentionText(attention: ProcessAttention): string {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
/**
|
|
251
|
-
* The stall marker for a row
|
|
251
|
+
* The stall marker for a row, 'quiet Nm' (or 'quiet NhMm' past an hour),
|
|
252
252
|
* derived from the read-model's stall tell (pure timestamp comparison on the
|
|
253
253
|
* registry side; a live node whose last observable output is older than the
|
|
254
254
|
* threshold). Null when the node is not stalled-quiet.
|
|
@@ -290,12 +290,12 @@ export function hasFleetCost(costUsd: number | null | undefined, costState: Proc
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
// ---------------------------------------------------------------------------
|
|
293
|
-
// Tree builder
|
|
293
|
+
// Tree builder, pure, testable (ported tree-walk shape from process-modal.ts)
|
|
294
294
|
// ---------------------------------------------------------------------------
|
|
295
295
|
|
|
296
296
|
/**
|
|
297
297
|
* Sibling comparator. `blockedSubtree` is the set of node ids whose own subtree
|
|
298
|
-
* contains a node blocked on the user (see collectBlockedSubtrees)
|
|
298
|
+
* contains a node blocked on the user (see collectBlockedSubtrees), those
|
|
299
299
|
* siblings sort to the TOP of their level so a blocked node (and the family
|
|
300
300
|
* that leads to it) floats up the tree, at every depth, without breaking the
|
|
301
301
|
* parent/child structure. Ties fall back to startedAt then id, unchanged.
|
|
@@ -367,7 +367,7 @@ function appendSubtree(
|
|
|
367
367
|
|
|
368
368
|
/**
|
|
369
369
|
* Flatten a ProcessNode[] into a stable depth-first FleetTreeRow[] list.
|
|
370
|
-
* Root-level rows (depth 0) carry an empty treePrefix (multi-root forest
|
|
370
|
+
* Root-level rows (depth 0) carry an empty treePrefix (multi-root forest,
|
|
371
371
|
* each top-level process family renders without a connector to its
|
|
372
372
|
* unrelated siblings); connectors appear starting at depth 1. Defensive
|
|
373
373
|
* cycle guard: a self-referencing or looping parentId chain is walked once
|
|
@@ -391,7 +391,7 @@ export function buildFleetRows(nodes: readonly ProcessNode[]): FleetTreeRow[] {
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
// Blocked-on-user nodes (and the families that lead to them) float to the top
|
|
394
|
-
// at every level
|
|
394
|
+
// at every level, see makeCompareNodes / collectBlockedSubtrees. Derived from
|
|
395
395
|
// the live node states on this same pass; never a stored flag.
|
|
396
396
|
const blockedSubtree = collectBlockedSubtrees(nodes, childrenByParent);
|
|
397
397
|
const compare = makeCompareNodes(blockedSubtree);
|
|
@@ -416,11 +416,11 @@ export function buildFleetRows(nodes: readonly ProcessNode[]): FleetTreeRow[] {
|
|
|
416
416
|
/**
|
|
417
417
|
* Kinds whose usage/costUsd (and, for 'running', whose derived state) are the
|
|
418
418
|
* SDK's OWN rollup of *other* nodes that ALSO appear individually in the flat
|
|
419
|
-
* list
|
|
419
|
+
* list, summing across every flat node would therefore double-count them.
|
|
420
420
|
* Per registry.ts assemble() + wrfc.ts adaptChain/adaptSubtask:
|
|
421
421
|
* - 'wrfc-chain': usage/costUsd = sum over the chain's member agents
|
|
422
422
|
* (chain.allAgentIds minus the owner) via adaptChain's aggregateCost/
|
|
423
|
-
* sumUsage
|
|
423
|
+
* sumUsage, those member agents are pushed onto the flat node list as
|
|
424
424
|
* their own 'agent' nodes too. The chain's derived ProcessState ('running'
|
|
425
425
|
* while any phase is active) likewise just mirrors its members' states.
|
|
426
426
|
* - 'wrfc-subtask': never carries usage/cost (adaptSubtask always sets
|
|
@@ -431,32 +431,32 @@ export function buildFleetRows(nodes: readonly ProcessNode[]): FleetTreeRow[] {
|
|
|
431
431
|
* nonzero cost/token reading (defense in depth against a future adapter
|
|
432
432
|
* change, not load-bearing).
|
|
433
433
|
*
|
|
434
|
-
* Orchestration-engine additions
|
|
434
|
+
* Orchestration-engine additions, verified against adapters/orchestration.ts:
|
|
435
435
|
* - 'workstream': adaptWorkstream SUMS every work-item's usage/costUsd
|
|
436
436
|
* exactly once (sumWorkItemUsage/aggregateWorkItemCost), the same
|
|
437
|
-
* "rollup of nodes that also appear individually" shape as adaptChain
|
|
437
|
+
* "rollup of nodes that also appear individually" shape as adaptChain,
|
|
438
438
|
* each work item ALSO appears as its own 'work-item' node in the flat
|
|
439
439
|
* list, so summing over every flat node would double-count. Its derived
|
|
440
440
|
* state likewise mirrors its items' states, so it is excluded from
|
|
441
441
|
* runningCount for the same non-double-counting reason as 'wrfc-chain'.
|
|
442
442
|
* - 'phase': adaptPhase reports NO usage/cost (mirrors adaptSubtask's
|
|
443
|
-
* "report nothing" choice exactly
|
|
443
|
+
* "report nothing" choice exactly, usage: undefined, costUsd: null,
|
|
444
444
|
* costState: 'unpriced') and its 'running' state mirrors whichever
|
|
445
445
|
* work-item currently occupies it. Same defense-in-depth inclusion as
|
|
446
446
|
* 'wrfc-subtask': never contributes today, excluded anyway in case a
|
|
447
447
|
* future adapter change gives it one.
|
|
448
448
|
* - 'work-item' is deliberately NOT in this set: unlike a phase, a work
|
|
449
449
|
* item carries its OWN direct usage/cost (item.usage, cumulative across
|
|
450
|
-
* every phase it has visited)
|
|
450
|
+
* every phase it has visited), it is the leaf contributor, the
|
|
451
451
|
* 'wrfc-subtask'-for-capabilities analogue but the 'agent'-for-usage
|
|
452
452
|
* analogue. Excluding it would silently zero out real cost/token totals.
|
|
453
453
|
*
|
|
454
|
-
* 'code-index' is deliberately NOT in this set either
|
|
454
|
+
* 'code-index' is deliberately NOT in this set either,
|
|
455
455
|
* adaptCodeIndex yields exactly one standalone ProcessNode per registry
|
|
456
456
|
* (never a parent whose children ALSO appear individually in the flat
|
|
457
457
|
* list), so it is a leaf like 'agent'/'work-item', not a grouping construct.
|
|
458
458
|
* It reports no usage/cost (an index build has no LLM turn), so it never
|
|
459
|
-
* contributes to totalCost/totalTokens regardless
|
|
459
|
+
* contributes to totalCost/totalTokens regardless, its running state DOES
|
|
460
460
|
* count toward runningCount while building, which is correct: it is a real,
|
|
461
461
|
* distinct unit of work, not an arithmetic sum of other rows.
|
|
462
462
|
*/
|
|
@@ -465,11 +465,11 @@ const ROLLUP_KINDS = new Set<ProcessKind>(['wrfc-chain', 'wrfc-subtask', 'workst
|
|
|
465
465
|
/**
|
|
466
466
|
* True for the WRFC owner agent's node specifically: an 'agent'-kind node
|
|
467
467
|
* whose source AgentRecord has wrfcRole === 'owner'. The owner runs no LLM
|
|
468
|
-
* turn itself
|
|
468
|
+
* turn itself, at chain completion the SDK backfills owner.usage from
|
|
469
469
|
* aggregateChainUsage(chain) (wrfc-controller.ts completeOwnerAgent), which
|
|
470
470
|
* is the SAME phase-children total already carried by the chain node AND by
|
|
471
471
|
* each phase-child agent's own node. Detected via the opaque `raw` field
|
|
472
|
-
* (the AgentRecord) since ProcessNode itself has no wrfcRole
|
|
472
|
+
* (the AgentRecord) since ProcessNode itself has no wrfcRole, this is the
|
|
473
473
|
* one place fleet-read-model reaches into `raw` for aggregation honesty
|
|
474
474
|
* rather than drill-down display.
|
|
475
475
|
*/
|
|
@@ -483,7 +483,7 @@ function isWrfcOwnerAgentNode(node: ProcessNode): boolean {
|
|
|
483
483
|
* Nodes whose usage/costUsd would double-count against other nodes already
|
|
484
484
|
* in the same flat list (see ROLLUP_KINDS / isWrfcOwnerAgentNode above).
|
|
485
485
|
* Excluded from totalCost/totalTokens. NOT excluded from runningCount by
|
|
486
|
-
* this predicate alone
|
|
486
|
+
* this predicate alone, see isFleetRunningLeaf.
|
|
487
487
|
*/
|
|
488
488
|
function isFleetAggregateRollupNode(node: ProcessNode): boolean {
|
|
489
489
|
return ROLLUP_KINDS.has(node.kind) || isWrfcOwnerAgentNode(node);
|
|
@@ -492,14 +492,14 @@ function isFleetAggregateRollupNode(node: ProcessNode): boolean {
|
|
|
492
492
|
/**
|
|
493
493
|
* Nodes that count toward runningCount. Excludes ROLLUP_KINDS (a running
|
|
494
494
|
* wrfc-chain/wrfc-subtask reflects the very same unit of work as its running
|
|
495
|
-
* member agent, which is already counted) but
|
|
496
|
-
* exclusion above
|
|
495
|
+
* member agent, which is already counted) but, unlike the cost/token
|
|
496
|
+
* exclusion above, does NOT exclude the WRFC owner agent: the owner is a
|
|
497
497
|
* real, distinct supervising process (not an arithmetic sum of other rows),
|
|
498
498
|
* so counting it as "running" alongside its phase children is not a double
|
|
499
499
|
* count in the way summing its rolled-up usage/cost would be.
|
|
500
500
|
*/
|
|
501
501
|
function isFleetRunningLeaf(node: ProcessNode): boolean {
|
|
502
|
-
// Observed foreign agents are NEVER counted in our own fleet counts
|
|
502
|
+
// Observed foreign agents are NEVER counted in our own fleet counts, they
|
|
503
503
|
// are externally-launched sessions goodvibes only watches, not work it runs.
|
|
504
504
|
return node.kind !== 'observed-external' && !ROLLUP_KINDS.has(node.kind) && isRunningProcessState(node.state);
|
|
505
505
|
}
|
|
@@ -535,7 +535,7 @@ export function buildFleetSnapshot(nodes: readonly ProcessNode[], capturedAt: nu
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
/**
|
|
538
|
-
* The fleet's honest leaf cost total
|
|
538
|
+
* The fleet's honest leaf cost total, the SAME figure as
|
|
539
539
|
* FleetSnapshot.totalCost, but computed in one pass WITHOUT building the display
|
|
540
540
|
* rows, for cheap per-frame use (the always-visible footer). Excludes rollup
|
|
541
541
|
* aggregates (chain/subtask/workstream/phase) AND the WRFC owner (its usage is a
|
|
@@ -555,7 +555,7 @@ export function fleetLeafCostTotal(nodes: readonly ProcessNode[]): number | null
|
|
|
555
555
|
|
|
556
556
|
/**
|
|
557
557
|
* The fleet cost figure for the always-visible footer: fleetLeafCostTotal over a
|
|
558
|
-
* fresh registry query, but ONLY while a fleet is live
|
|
558
|
+
* fresh registry query, but ONLY while a fleet is live, the idle single-session
|
|
559
559
|
* path must never pay the aggregate-on-read query() scan. Query failures degrade to
|
|
560
560
|
* null (honest "no fleet cost"), never a throw into the render loop.
|
|
561
561
|
*/
|
|
@@ -569,7 +569,7 @@ export function footerFleetCost(queryNodes: () => readonly ProcessNode[], hasLiv
|
|
|
569
569
|
}
|
|
570
570
|
|
|
571
571
|
// ---------------------------------------------------------------------------
|
|
572
|
-
// Read-model
|
|
572
|
+
// Read-model, two-factory shape (live / static), mirrors cockpit-read-model.ts
|
|
573
573
|
// ---------------------------------------------------------------------------
|
|
574
574
|
|
|
575
575
|
/** Payload of a `COMMUNICATION_CONSUMED` runtime-bus event, narrowed for FleetReadModel consumers. */
|
|
@@ -598,7 +598,7 @@ export interface FleetReadModel {
|
|
|
598
598
|
* Queue a human message for a live in-process agent (or a
|
|
599
599
|
* wrfc-subtask's current live member), delivered at its next turn
|
|
600
600
|
* boundary. Honest refusal (`{queued:false,reason}`) for anything that
|
|
601
|
-
* cannot take mid-run input
|
|
601
|
+
* cannot take mid-run input, see ProcessRegistry.steer's doc comment.
|
|
602
602
|
*/
|
|
603
603
|
steer(id: string, text: string): SteerResult;
|
|
604
604
|
/**
|
|
@@ -606,7 +606,7 @@ export interface FleetReadModel {
|
|
|
606
606
|
* steer at its turn boundary" signal (COMMUNICATION_CONSUMED on the
|
|
607
607
|
* runtime bus's 'communication' domain). Returns an unsubscribe function.
|
|
608
608
|
* A read-model constructed without a runtimeBus (e.g. the static factory,
|
|
609
|
-
* or a test double) never invokes the listener
|
|
609
|
+
* or a test double) never invokes the listener, graceful no-op, matching
|
|
610
610
|
* the steer()/steerable degrade-without-a-dep convention.
|
|
611
611
|
*/
|
|
612
612
|
subscribeConsumed(listener: (event: SteerConsumedEvent) => void): () => void;
|
|
@@ -630,7 +630,7 @@ export type FleetRegistryLike = Pick<ProcessRegistry, 'query' | 'subscribe' | 'i
|
|
|
630
630
|
|
|
631
631
|
/**
|
|
632
632
|
* Create a live FleetReadModel backed by the SDK's ProcessRegistry.
|
|
633
|
-
* `runtimeBus` is optional (narrowed to just `onDomain`)
|
|
633
|
+
* `runtimeBus` is optional (narrowed to just `onDomain`), without it,
|
|
634
634
|
* `subscribeConsumed` degrades to a no-op, same shape as the registry's own
|
|
635
635
|
* messageBus-optional degrade for steer()/steerable.
|
|
636
636
|
*/
|
|
@@ -685,7 +685,7 @@ export function createFleetReadModel(
|
|
|
685
685
|
};
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
-
/** Create a static FleetReadModel for tests/goldens
|
|
688
|
+
/** Create a static FleetReadModel for tests/goldens, no live registry, no timers. */
|
|
689
689
|
export function createStaticFleetReadModel(snapshot: FleetSnapshot): FleetReadModel {
|
|
690
690
|
return {
|
|
691
691
|
getSnapshot: () => snapshot,
|