@pellux/goodvibes-tui 2.0.13 → 2.0.15
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 +61 -0
- package/README.md +40 -29
- package/docs/README.md +1 -1
- 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 +9 -9
- 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 +4 -4
- 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
|
@@ -12,7 +12,7 @@ import { classifyConsolidationFetchError } from '../memory-consolidation-gateway
|
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Memory → config-modal surface (group-B port). Two tabs: 'All Records'
|
|
15
|
-
// (the full record list) and 'Review Queue' (the operator review queue)
|
|
15
|
+
// (the full record list) and 'Review Queue' (the operator review queue), the
|
|
16
16
|
// same split the panel toggled with Tab, now real host tabs. Review-state
|
|
17
17
|
// mutations (reviewed/stale/contradicted/fresh) and record delete route to the
|
|
18
18
|
// existing `/recall review` / `/recall remove` command path (charter: no
|
|
@@ -22,14 +22,14 @@ import { classifyConsolidationFetchError } from '../memory-consolidation-gateway
|
|
|
22
22
|
// "not configured" copy renders as an honest degraded state.
|
|
23
23
|
//
|
|
24
24
|
// Read path (memory-spine adoption): reads go through the spine client's
|
|
25
|
-
// `honestSearch
|
|
25
|
+
// `honestSearch`, the MemoryAccess shape, not the raw registry, so a session
|
|
26
26
|
// that has adopted an external daemon reads the SAME wire-served records a
|
|
27
27
|
// wire failure is deliberately surfaced (never a silently stale local copy).
|
|
28
28
|
// `buildView()` stays synchronous/pure per the ConfigModalSurface contract;
|
|
29
29
|
// `refresh()` is async and calls the `requestRender` callback `onOpen` hands
|
|
30
30
|
// it when the data lands. The Review Queue ranking is recomputed client-side
|
|
31
31
|
// from the same honestSearch batch (see `rankForReview` below) rather than
|
|
32
|
-
// through a second wire call
|
|
32
|
+
// through a second wire call, `reviewQueue` is not part of MemoryAccess.
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
34
|
|
|
35
35
|
/** Minimal read shape of a `MemoryRecord` this modal renders. */
|
|
@@ -67,7 +67,7 @@ export interface MemoryModalDeps {
|
|
|
67
67
|
};
|
|
68
68
|
/**
|
|
69
69
|
* Resolve the memory-consolidation-receipts gateway (memory-consolidation-gateway.ts),
|
|
70
|
-
* lazily
|
|
70
|
+
* lazily, called fresh each time the Proposals tab (re)fetches, exactly like
|
|
71
71
|
* the Fleet panel's `resolveGateway` (fleet-gateway.ts / fleet-acts.ts). Absent
|
|
72
72
|
* in a session with no daemon-backed control plane wired: the Proposals tab
|
|
73
73
|
* then renders its "unavailable" honest state without ever attempting a fetch.
|
|
@@ -75,7 +75,7 @@ export interface MemoryModalDeps {
|
|
|
75
75
|
readonly resolveConsolidationGateway?: () => MemoryConsolidationGatewayResolution;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/** The Proposals tab's fetch state
|
|
78
|
+
/** The Proposals tab's fetch state, one honest rendering per state, never a blend. */
|
|
79
79
|
type ProposalsStatus =
|
|
80
80
|
| { readonly kind: 'loading' }
|
|
81
81
|
| { readonly kind: 'unavailable'; readonly reason: string }
|
|
@@ -86,7 +86,7 @@ type ProposalsStatus =
|
|
|
86
86
|
* Client-side port of the SDK's MemoryStore.reviewQueue ranking
|
|
87
87
|
* (memory-store-helpers.ts reviewQueueScore/isReviewCandidate): all four
|
|
88
88
|
* review states are candidates, scored by state + inverse confidence (flagged
|
|
89
|
-
* states penalized), tie-broken by recency. Presentation ordering only
|
|
89
|
+
* states penalized), tie-broken by recency. Presentation ordering only, not
|
|
90
90
|
* a wire call, so exact parity with the server's own tie-breaking on ids it
|
|
91
91
|
* has never seen is not load-bearing.
|
|
92
92
|
*/
|
|
@@ -119,9 +119,9 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
119
119
|
readonly title = 'Memory';
|
|
120
120
|
private allRecords: MemoryRecordLike[] = [];
|
|
121
121
|
private reviewRecords: MemoryRecordLike[] = [];
|
|
122
|
-
/** Honest note on the last read: a wire failure (client mode) or the index-unavailable fallback reason
|
|
122
|
+
/** Honest note on the last read: a wire failure (client mode) or the index-unavailable fallback reason, never silently dropped. */
|
|
123
123
|
private loadNote: string | null = null;
|
|
124
|
-
/** The Proposals tab's fetch state
|
|
124
|
+
/** The Proposals tab's fetch state, starts 'loading' whenever a gateway is wired (see refreshProposals). */
|
|
125
125
|
private proposalsStatus: ProposalsStatus = { kind: 'loading' };
|
|
126
126
|
private requestRender: (() => void) | null = null;
|
|
127
127
|
|
|
@@ -141,7 +141,7 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
141
141
|
// Review Queue tab. A DISTINCT key from 'enter' (not shared with
|
|
142
142
|
// markReviewed): ConfigModal.resolveAction matches the FIRST action bound
|
|
143
143
|
// to a key and returns null if THAT one's enabledFor fails, rather than
|
|
144
|
-
// trying the next action bound to the same key
|
|
144
|
+
// trying the next action bound to the same key, so gating two different
|
|
145
145
|
// 'enter' actions by tabId alone would silently break one of them.
|
|
146
146
|
{ key: 'v', id: 'jumpToReviewQueue', label: 'view in review queue', enabledFor: this.proposalsGate },
|
|
147
147
|
];
|
|
@@ -175,11 +175,11 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
177
|
* Lazy fetch of memory.consolidation.receipts (memory-consolidation-gateway.ts)
|
|
178
|
-
|
|
178
|
+
*, only attempted when this surface opens or refreshes, never polled. No
|
|
179
179
|
* gateway resolver wired at all (session has no daemon-backed control
|
|
180
180
|
* plane) renders the same honest "unavailable" state as a resolved-but-
|
|
181
181
|
* refused gateway (daemon disabled / no control-plane URL) or a 501/404
|
|
182
|
-
* from an older daemon with no consolidation scheduler
|
|
182
|
+
* from an older daemon with no consolidation scheduler, three different
|
|
183
183
|
* causes, one bucket, because from the operator's seat they mean the same
|
|
184
184
|
* thing: nothing to show, and no fault of theirs.
|
|
185
185
|
*/
|
|
@@ -239,7 +239,7 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
239
239
|
// itself (the daemon sets reviewState 'contradicted' + staleReason); a
|
|
240
240
|
// cross-scope-duplicate proposal marks the record 'fresh' with NO
|
|
241
241
|
// staleReason, so without this correlation those rows are bare and
|
|
242
|
-
// unexplained (item 3)
|
|
242
|
+
// unexplained (item 3), fall back to the fetched proposal's own
|
|
243
243
|
// reason only when the record didn't already carry one.
|
|
244
244
|
const proposal = record.staleReason ? undefined : proposalByRecord.get(record.id);
|
|
245
245
|
const reasonSuffix = record.staleReason
|
|
@@ -263,12 +263,12 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
/**
|
|
266
|
-
* The Proposals tab
|
|
266
|
+
* The Proposals tab, WHAT the daemon's idle-time consolidation pass
|
|
267
267
|
* proposed (contradiction / cross-scope-duplicate / stale-delete) and is
|
|
268
268
|
* holding for human judgment, read lazily over memory.consolidation.receipts
|
|
269
269
|
* (memory-consolidation-gateway.ts). Three distinct honest states render as
|
|
270
|
-
* three distinct lines
|
|
271
|
-
* into a shared "nothing to show"
|
|
270
|
+
* three distinct lines, 'loading' / 'unavailable' / 'error' never collapse
|
|
271
|
+
* into a shared "nothing to show", plus the true empty state ('ready' with
|
|
272
272
|
* zero proposals) which is a fourth, equally honest line.
|
|
273
273
|
*/
|
|
274
274
|
private proposalsTab(): ConfigModalTab {
|
|
@@ -285,14 +285,14 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
285
285
|
}
|
|
286
286
|
const rows: ConfigModalRow[] = status.proposals.map((proposal, index) => ({
|
|
287
287
|
id: `proposal:${index}`,
|
|
288
|
-
label: `[${proposal.kind}] ${proposal.reason}
|
|
288
|
+
label: `[${proposal.kind}] ${proposal.reason}; ${proposal.ids.length} record${proposal.ids.length === 1 ? '' : 's'}: ${proposal.ids.join(', ')}`,
|
|
289
289
|
}));
|
|
290
290
|
return {
|
|
291
291
|
id: 'proposals',
|
|
292
292
|
label: 'Proposals',
|
|
293
293
|
header,
|
|
294
294
|
rows,
|
|
295
|
-
emptyText: 'No pending proposals
|
|
295
|
+
emptyText: 'No pending proposals: nothing awaiting judgment right now.',
|
|
296
296
|
};
|
|
297
297
|
}
|
|
298
298
|
|
|
@@ -333,7 +333,7 @@ class MemoryModalSurface implements ConfigModalSurface {
|
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
335
|
* The Proposals tab's "jump": move to the Review Queue tab with one of the
|
|
336
|
-
* selected proposal's affected records selected
|
|
336
|
+
* selected proposal's affected records selected, the same host mechanism
|
|
337
337
|
* (`ctx.jumpToRow`) any future in-surface jump would use. A proposal's
|
|
338
338
|
* `ids` are not guaranteed to all be inside the review queue's top-24
|
|
339
339
|
* window, so this picks the first id that IS present there rather than
|
|
@@ -361,7 +361,7 @@ export function createMemoryModalSurface(deps: MemoryModalDeps): ConfigModalSurf
|
|
|
361
361
|
|
|
362
362
|
/**
|
|
363
363
|
* Deterministic golden fixture: fixed memory records with frozen createdAt
|
|
364
|
-
* timestamps
|
|
364
|
+
* timestamps, no live registry, no wall-clock, no random ids. Promise-backed
|
|
365
365
|
* (see ecosystem-modals-golden.test.ts): `honestSearch` is async even for this
|
|
366
366
|
* in-memory fixture (matching the real MemoryAccess shape), so the factory
|
|
367
367
|
* pre-awaits the initial `onOpen` refresh before handing back the surface.
|
|
@@ -5,7 +5,7 @@ import type { ConfigModalRow } from '../../input/config-modal-types.ts';
|
|
|
5
5
|
/**
|
|
6
6
|
* Shared formatting helpers for config-modal surfaces. Row styling reuses
|
|
7
7
|
* DEFAULT_PANEL_PALETTE (the retired panels' palette) so a migrated modal reads
|
|
8
|
-
* as the same surface it replaced
|
|
8
|
+
* as the same surface it replaced, the goldens stay panel-consistent.
|
|
9
9
|
*/
|
|
10
10
|
export const PALETTE = DEFAULT_PANEL_PALETTE;
|
|
11
11
|
|
|
@@ -5,7 +5,7 @@ import { activeUiTones, registerThemeRefresh } from '../../renderer/theme.ts';
|
|
|
5
5
|
//
|
|
6
6
|
// Derived entirely from the chrome token set so the builders carry NO raw hex
|
|
7
7
|
// color literals (architecture: no-raw-hex-literal-growth). Rebuilt IN PLACE
|
|
8
|
-
// on theme flips via registerThemeRefresh
|
|
8
|
+
// on theme flips via registerThemeRefresh, the group-B surfaces read
|
|
9
9
|
// MODAL_TONES by reference at render time, so a dark-pinned module const here
|
|
10
10
|
// left them rendering dark accents on light chrome (batch refutation
|
|
11
11
|
// finding 2). Mirrors the polish-core/overlay-box/modal-factory pattern.
|
|
@@ -30,9 +30,9 @@ function buildModalTones() {
|
|
|
30
30
|
dim: T.fg.dim,
|
|
31
31
|
/** Primary text. */
|
|
32
32
|
primary: T.fg.primary,
|
|
33
|
-
/** QR module foreground
|
|
33
|
+
/** QR module foreground, high-contrast, mode-appropriate. */
|
|
34
34
|
qrDark: T.fg.inverse,
|
|
35
|
-
/** QR field background
|
|
35
|
+
/** QR field background, high-contrast, mode-appropriate. */
|
|
36
36
|
qrLight: T.fg.primary,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
@@ -12,7 +12,7 @@ import { formatPairingOffers, formatPostureCapabilities, pairingPostureNotice }
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// Device Pairing config-modal surface. Shows the web-app origin, the per-device
|
|
14
14
|
// token's name, a masked token/deep-link pair, the offer set with plain-language
|
|
15
|
-
// consequences, and a scannable QR of the `#pair=<token>` deep link
|
|
15
|
+
// consequences, and a scannable QR of the `#pair=<token>` deep link, a camera
|
|
16
16
|
// scan opens the web app already signed in. No raw JSON connection blob is ever
|
|
17
17
|
// encoded.
|
|
18
18
|
//
|
|
@@ -20,7 +20,7 @@ import { formatPairingOffers, formatPostureCapabilities, pairingPostureNotice }
|
|
|
20
20
|
// each modal open mints its OWN named per-device token, so a missing pairing
|
|
21
21
|
// service degrades honestly to an "unavailable" state instead of throwing at
|
|
22
22
|
// registration time. "new device token" clears the cache and re-pulls to mint a
|
|
23
|
-
// fresh token + QR in place
|
|
23
|
+
// fresh token + QR in place, non-destructive now, because a freshly minted
|
|
24
24
|
// token no longer rotates a shared secret out from under a live device; old
|
|
25
25
|
// tokens stay valid until explicitly revoked in the settings device surface.
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
@@ -32,14 +32,14 @@ export interface PairingModalConnectionInfo {
|
|
|
32
32
|
readonly token: string;
|
|
33
33
|
/** The per-device token's editable name. */
|
|
34
34
|
readonly tokenName: string;
|
|
35
|
-
/** The `#pair=<token>` deep link the QR encodes
|
|
35
|
+
/** The `#pair=<token>` deep link the QR encodes, opens the web app signed in. */
|
|
36
36
|
readonly deepLink: string;
|
|
37
37
|
/** The offer set this pairing carries (each declinable in the web app). */
|
|
38
38
|
readonly offers: readonly PairingHandoffOfferKind[];
|
|
39
39
|
/**
|
|
40
40
|
* The honest TLS/capability posture of the web origin the deep link opens
|
|
41
41
|
* (the SDK's describeOriginPosture / the handoff's `posture` field). The one
|
|
42
|
-
* honest LAN line and the labeled capability list both render from here
|
|
42
|
+
* honest LAN line and the labeled capability list both render from here, never
|
|
43
43
|
* from a locally-authored string. Absent when no web origin is known.
|
|
44
44
|
*/
|
|
45
45
|
readonly posture?: OriginPosture | undefined;
|
|
@@ -47,7 +47,7 @@ export interface PairingModalConnectionInfo {
|
|
|
47
47
|
export interface PairingModalControlPlaneSnapshot { readonly activeClientIds: readonly string[]; }
|
|
48
48
|
export interface PairingModalReadModel<T> { getSnapshot(): T; }
|
|
49
49
|
|
|
50
|
-
/** tailscale.get shape
|
|
50
|
+
/** tailscale.get shape, the daemon's honest tailscale detection (quiet when absent). */
|
|
51
51
|
export interface PairingTailscaleStatus {
|
|
52
52
|
readonly available: boolean;
|
|
53
53
|
readonly loggedIn: boolean;
|
|
@@ -56,7 +56,7 @@ export interface PairingTailscaleStatus {
|
|
|
56
56
|
readonly detail: string;
|
|
57
57
|
readonly lastServe?: PairingTailscaleServeReceipt | undefined;
|
|
58
58
|
}
|
|
59
|
-
/** tailscale.serve.run receipt
|
|
59
|
+
/** tailscale.serve.run receipt, the one-action serve result rendered verbatim. */
|
|
60
60
|
export interface PairingTailscaleServeReceipt {
|
|
61
61
|
readonly at: number;
|
|
62
62
|
readonly command: string;
|
|
@@ -66,14 +66,14 @@ export interface PairingTailscaleServeReceipt {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export interface PairingModalDeps {
|
|
69
|
-
/** Lazy connection-info provider
|
|
69
|
+
/** Lazy connection-info provider, returns null when the daemon/companion
|
|
70
70
|
* token cannot be resolved (honest degraded state instead of a throw). */
|
|
71
71
|
readonly getConnectionInfo: () => PairingModalConnectionInfo | null;
|
|
72
72
|
readonly controlPlaneReadModel?: PairingModalReadModel<PairingModalControlPlaneSnapshot>;
|
|
73
73
|
readonly copyToClipboard?: (text: string) => void;
|
|
74
74
|
/**
|
|
75
75
|
* Probe tailscale (tailscale.get) once on open. Absent dep, a null result, or
|
|
76
|
-
* `available:false` all keep the tailscale affordance QUIET
|
|
76
|
+
* `available:false` all keep the tailscale affordance QUIET, never a nag.
|
|
77
77
|
* Present + available ⇒ the surface offers the one-action serve.
|
|
78
78
|
*/
|
|
79
79
|
readonly probeTailscale?: () => Promise<PairingTailscaleStatus | null>;
|
|
@@ -85,7 +85,7 @@ export interface PairingModalDeps {
|
|
|
85
85
|
readonly runTailscaleServe?: () => Promise<PairingTailscaleServeReceipt | null>;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
// Fixed-width placeholder
|
|
88
|
+
// Fixed-width placeholder, deliberately NOT derived from the real secret's
|
|
89
89
|
// length (mirrors QrPanel.SECRET_MASK), so masking doesn't leak length.
|
|
90
90
|
const SECRET_MASK = '••••••••••••';
|
|
91
91
|
|
|
@@ -108,7 +108,7 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
108
108
|
{ key: 'v', id: 'toggleReveal', label: 'reveal token' },
|
|
109
109
|
{ key: 'c', id: 'copyLink', label: 'copy link', enabledFor: () => Boolean(this.deps.copyToClipboard) },
|
|
110
110
|
{ key: 'r', id: 'newToken', label: 'new device token' },
|
|
111
|
-
// The one-action serve affordance
|
|
111
|
+
// The one-action serve affordance, offered ONLY when tailscale is detected
|
|
112
112
|
// and not already serving https; a second 't' press (the standard confirm)
|
|
113
113
|
// runs tailscale.serve.run. Absence keeps this key unadvertised and inert.
|
|
114
114
|
{
|
|
@@ -141,7 +141,7 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
141
141
|
buildView(): ConfigModalView {
|
|
142
142
|
const info = this.ensureInfo();
|
|
143
143
|
if (!info) {
|
|
144
|
-
return { title: 'Device Pairing', degraded: 'Device pairing is unavailable
|
|
144
|
+
return { title: 'Device Pairing', degraded: 'Device pairing is unavailable: the pairing token service could not be resolved for this session.', tabs: [{ id: 'pairing', label: 'Pairing', rows: [] }] };
|
|
145
145
|
}
|
|
146
146
|
const { url, token, tokenName, deepLink, offers, posture } = info;
|
|
147
147
|
const displayToken = this.revealed ? token : SECRET_MASK;
|
|
@@ -149,7 +149,7 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
149
149
|
const displayLink = this.revealed ? deepLink : SECRET_MASK;
|
|
150
150
|
|
|
151
151
|
const rows: ConfigModalRow[] = [];
|
|
152
|
-
rows.push(infoRow('intro', 'Scan with the GoodVibes web app to pair this device
|
|
152
|
+
rows.push(infoRow('intro', 'Scan with the GoodVibes web app to pair this device; it opens already signed in.', { dim: true }));
|
|
153
153
|
rows.push(infoRow('url', `Web app ${url}`));
|
|
154
154
|
rows.push(infoRow('name', `Device ${tokenName}`));
|
|
155
155
|
rows.push(infoRow('token', `Token ${displayToken}`, { fg: MODAL_TONES.reasoning }));
|
|
@@ -159,13 +159,13 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
159
159
|
rows.push(infoRow('companions', `Devices connected: ${connected}`, connected > 0 ? { fg: MODAL_TONES.good } : { dim: true }));
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
// The offer set carried by this pairing
|
|
162
|
+
// The offer set carried by this pairing, each named with its plain-language
|
|
163
163
|
// consequence, each declinable in the web app.
|
|
164
164
|
if (offers.length > 0) {
|
|
165
165
|
rows.push(infoRow('offers-h', 'Offers (each declinable when you pair):', { dim: true }));
|
|
166
166
|
formatPairingOffers(offers).forEach((line, i) => rows.push(infoRow(`offer:${i}`, line)));
|
|
167
167
|
}
|
|
168
|
-
// The labeled browser-capability list
|
|
168
|
+
// The labeled browser-capability list, what the paired device actually gets
|
|
169
169
|
// over this origin (and, for a gated one, why not). Rendered from the posture,
|
|
170
170
|
// never hidden behind a dead button.
|
|
171
171
|
const capabilities = formatPostureCapabilities(posture);
|
|
@@ -173,14 +173,14 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
173
173
|
rows.push(infoRow('caps-h', 'This device will get:', { dim: true }));
|
|
174
174
|
capabilities.forEach((line, i) => rows.push(infoRow(`cap:${i}`, line)));
|
|
175
175
|
}
|
|
176
|
-
// The ONE honest LAN line
|
|
176
|
+
// The ONE honest LAN line, the SDK posture's own notice (LAN_PLAIN_HTTP_NOTICE),
|
|
177
177
|
// present only for the plain-http-on-LAN posture, never a nag elsewhere.
|
|
178
178
|
const notice = pairingPostureNotice(posture);
|
|
179
179
|
if (notice) rows.push(infoRow('posture', notice, { fg: MODAL_TONES.reasoning }));
|
|
180
|
-
// The tailscale serve affordance
|
|
180
|
+
// The tailscale serve affordance, quiet unless tailscale is detected.
|
|
181
181
|
this.appendTailscaleRows(rows);
|
|
182
182
|
|
|
183
|
-
// ASCII QR of the deep link
|
|
183
|
+
// ASCII QR of the deep link, a camera scan opens the web app already
|
|
184
184
|
// carrying the one-time token. No raw JSON blob is ever encoded. The
|
|
185
185
|
// multi-line preformatted string is split into one non-selectable row per row.
|
|
186
186
|
const qr = renderQrToString(generateQrMatrix(deepLink));
|
|
@@ -195,7 +195,7 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
|
-
* Append the tailscale section
|
|
198
|
+
* Append the tailscale section, quiet when tailscale is undetected or
|
|
199
199
|
* unavailable. When detected, the surface names the one-action serve; once a
|
|
200
200
|
* serve has produced (or tailscale already reports) an https MagicDNS URL, that
|
|
201
201
|
* URL and the serve receipt render verbatim so the encrypted path is visible.
|
|
@@ -208,7 +208,7 @@ class PairingModalSurface implements ConfigModalSurface {
|
|
|
208
208
|
rows.push(infoRow('ts-h', 'Encrypted access (Tailscale):', { dim: true }));
|
|
209
209
|
rows.push(infoRow('ts-url', ` ${httpsUrl}`, { fg: MODAL_TONES.good }));
|
|
210
210
|
} else {
|
|
211
|
-
rows.push(infoRow('ts-h', 'Tailscale detected
|
|
211
|
+
rows.push(infoRow('ts-h', 'Tailscale detected: press t to serve this app over https (MagicDNS).', { fg: MODAL_TONES.reasoning }));
|
|
212
212
|
}
|
|
213
213
|
const receipt = this.serveReceipt ?? ts.lastServe;
|
|
214
214
|
if (receipt) {
|
|
@@ -19,16 +19,16 @@ import { buildAnswerActions, isGenericRecommendation, type PlanningAnswerAction
|
|
|
19
19
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
// Project Planning → 'planning' config-modal surface (group-B port). Shows
|
|
22
|
-
// readiness/questions/decisions/task-graph/handoff
|
|
22
|
+
// readiness/questions/decisions/task-graph/handoff, read-only except choosing
|
|
23
23
|
// an answer to the current open question, approving execution, dismissing the
|
|
24
24
|
// plan, or refreshing.
|
|
25
25
|
//
|
|
26
|
-
|
|
26
|
+
//, the seams are now real:
|
|
27
27
|
// - A CANNED answer to a real open question dispatches `/plan answer <id> <text>`
|
|
28
28
|
// (records it; the open-question gap clears on the next refine).
|
|
29
29
|
// - The CUSTOM free-form typed answer (and any answer to a synthetic readiness
|
|
30
30
|
// question with no open-question record) is submitted to chat via the generic
|
|
31
|
-
// `submitInput` seam
|
|
31
|
+
// `submitInput` seam, a real model turn. ORDERING GUARD: the modal closes
|
|
32
32
|
// BEFORE the turn starts (a turn under a live modal is the modal-liveness
|
|
33
33
|
// hazard). No more `/plan <text>` reseed approximation.
|
|
34
34
|
// - Dismiss is a first-class CONFIRMED action (`d`) dispatching `/plan dismiss`,
|
|
@@ -293,7 +293,7 @@ function buildGoldenService(): PlanningModalService {
|
|
|
293
293
|
|
|
294
294
|
/**
|
|
295
295
|
* Deterministic golden fixture. All ids/timestamps are frozen literals. Because
|
|
296
|
-
* every service method is Promise-based, this helper is async
|
|
296
|
+
* every service method is Promise-based, this helper is async, it opens the
|
|
297
297
|
* surface, waits a macrotask so the fire-and-forget load resolves, then returns.
|
|
298
298
|
*/
|
|
299
299
|
export async function planningModalGoldenSurface(): Promise<ConfigModalSurface> {
|
|
@@ -107,7 +107,7 @@ class PluginsModalSurface implements ConfigModalSurface {
|
|
|
107
107
|
trustRecord?.signatureFingerprint ? `sig ${trustRecord.signatureFingerprint}` : 'unsigned',
|
|
108
108
|
caps ? `caps ${caps.requested.length}/${caps.highRisk.length}hr/${caps.blocked.length}blk` : null,
|
|
109
109
|
quarantineRecord ? `quarantine: ${quarantineRecord.reason}` : null,
|
|
110
|
-
verify ? `verify ${verify.valid ? 'VALID' : 'INVALID'}${verify.fingerprint ? ` fp=${verify.fingerprint}` : (!verify.valid && verify.reason ?
|
|
110
|
+
verify ? `verify ${verify.valid ? 'VALID' : 'INVALID'}${verify.fingerprint ? ` fp=${verify.fingerprint}` : (!verify.valid && verify.reason ? `: ${verify.reason}` : '')}` : null,
|
|
111
111
|
].filter((s): s is string => s !== null).join(' · ');
|
|
112
112
|
rows.push({
|
|
113
113
|
id: plugin.name,
|
|
@@ -19,11 +19,11 @@ import type {
|
|
|
19
19
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
// Policy → config-modal surface (group-B port). Governance CONTENT view,
|
|
22
|
-
// not a selectable list
|
|
22
|
+
// not a selectable list, current/candidate bundle state, diff, divergence
|
|
23
23
|
// gate, history, permission audit, lint findings, preflight review, and
|
|
24
24
|
// simulation samples render as non-selectable rows (the host windows/scrolls
|
|
25
25
|
// them). EVERY dispatchable key in the original panel (s/f/l/p/b) already
|
|
26
|
-
// routed to `/policy <sub>` via executeCommand
|
|
26
|
+
// routed to `/policy <sub>` via executeCommand, none were in-panel mutations;
|
|
27
27
|
// the panel's promote/rollback confirm step is dropped per charter (never fold
|
|
28
28
|
// a confirm into a modal) and the command dispatches directly. Determinism:
|
|
29
29
|
// timestamps are formatted (fmtTime/fmtRate), never read from the clock.
|
|
@@ -111,7 +111,7 @@ class PolicyModalSurface implements ConfigModalSurface {
|
|
|
111
111
|
push('No policy bundles loaded.');
|
|
112
112
|
push('Bundle: none active, none candidate. Gate: n/a.', DIM);
|
|
113
113
|
push('Get started');
|
|
114
|
-
push('/policy load
|
|
114
|
+
push('/policy load: load a policy bundle to begin governance review', DIM);
|
|
115
115
|
return { title: 'Policy And Governance', tabs: [{ id: 'governance', label: 'Governance', header, rows, emptyText: '' }] };
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -181,7 +181,7 @@ class PolicyModalSurface implements ConfigModalSurface {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
onAction(id: string, ctx: ConfigModalActionContext): void {
|
|
184
|
-
// "Rollout" in the WO brief maps to the registry's actual subcommand
|
|
184
|
+
// "Rollout" in the WO brief maps to the registry's actual subcommand,
|
|
185
185
|
// there is no separate `/policy rollout`.
|
|
186
186
|
const sub = id === 'simulate' ? 'simulate' : id === 'preflight' ? 'preflight' : id === 'lint' ? 'lint' : id === 'promote' ? 'promote' : id === 'rollback' ? 'rollback' : null;
|
|
187
187
|
if (!sub) return;
|
|
@@ -11,7 +11,7 @@ export interface ProviderRuntimeInspect {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Provider-health config-modal surface (migrated from the `provider-health`
|
|
14
|
-
* panel
|
|
14
|
+
* panel, the charter's live-modal exemplar; also the target of the `providers`
|
|
15
15
|
* and `accounts` redirects). providerRuntime.inspectAll() is async, so its
|
|
16
16
|
* result is cached and refreshed on open, on `r`, and on a 3s live tick (the
|
|
17
17
|
* panel's display-tick cadence); buildView reads the cache synchronously so
|
|
@@ -24,7 +24,7 @@ function stateTone(state: string): Tone {
|
|
|
24
24
|
* snapshot is synchronous; a subscribe() keeps live transport/heartbeat values
|
|
25
25
|
* refreshing between key presses. The panel's `Tab` connections/contracts browse
|
|
26
26
|
* toggle becomes two real tabs. `r`/Enter dispatch `/remote recover` for the
|
|
27
|
-
* selected runner
|
|
27
|
+
* selected runner, the panel's only corrective keybinding; the richer
|
|
28
28
|
* `/remote` command set (show/supervisor/cancel/dispatch/artifact/…) stays on
|
|
29
29
|
* the command front-door.
|
|
30
30
|
*/
|
|
@@ -5,7 +5,7 @@ import type { SandboxProfile, SandboxSession, SandboxSessionRegistry } from '@/r
|
|
|
5
5
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
6
6
|
import { toneStyle, statusGlyph, pad, postureLine, kv, type Tone } from './modal-surface-helpers.ts';
|
|
7
7
|
|
|
8
|
-
/** SandboxSessionRegistry has no event subscription
|
|
8
|
+
/** SandboxSessionRegistry has no event subscription, poll at the same 3s
|
|
9
9
|
* cadence the retired panel used (POLL_INTERVAL_MS in sandbox-panel.ts). */
|
|
10
10
|
const POLL_INTERVAL_MS = 3_000;
|
|
11
11
|
|
|
@@ -32,7 +32,7 @@ function sessionIdOf(row: ConfigModalRow | null): string | null {
|
|
|
32
32
|
* (buildSandboxReview, listSandboxProfiles, sessions.list) are synchronous, so
|
|
33
33
|
* buildView reads them live each tick. The panel's single profile+session
|
|
34
34
|
* selectable list becomes two real tabs. s/x/e call the SandboxSessionRegistry
|
|
35
|
-
* directly (these were never slash commands)
|
|
35
|
+
* directly (these were never slash commands), start/execute are wrapped so a
|
|
36
36
|
* rejection surfaces via ctx.setStatus instead of an unhandled rejection,
|
|
37
37
|
* matching the panel's try/catch + setError behaviour.
|
|
38
38
|
*/
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
// Security → config-modal surface (group-B port). Two tabs: 'Tokens' (the
|
|
16
16
|
// token scope/rotation audit list under the governance summary) and
|
|
17
17
|
// 'Governance' (policy preflight, MCP/plugin quarantine, latest incident, and
|
|
18
|
-
// the MCP attack-path review). Read-model-backed, so refresh() is a no-op
|
|
18
|
+
// the MCP attack-path review). Read-model-backed, so refresh() is a no-op,
|
|
19
19
|
// buildView reads getSnapshot() fresh every render. 'f' (preflight) routes to
|
|
20
20
|
// its `/policy preflight` command path; 'i' jumps to the incident surface
|
|
21
21
|
// (fleet) via the command path. Selection-blind port: the panel's
|
|
@@ -121,7 +121,7 @@ class SecurityModalSurface implements ConfigModalSurface {
|
|
|
121
121
|
...(resultColor(result) ? { style: resultColor(result)! } : {}),
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
-
rows.push(infoRow('audit:when', `Last audit ${fmtIso(audit.lastAuditAt)}
|
|
124
|
+
rows.push(infoRow('audit:when', `Last audit ${fmtIso(audit.lastAuditAt)}: press r to refresh`, { dim: true }));
|
|
125
125
|
return { id: 'tokens', label: 'Tokens', header, rows, hints: ['f preflight'] };
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -130,7 +130,7 @@ class SecurityModalSurface implements ConfigModalSurface {
|
|
|
130
130
|
const quarantinedMcp = snapshot.mcpServers.filter((s) => s.schemaFreshness === 'quarantined');
|
|
131
131
|
if (quarantinedMcp.length > 0) {
|
|
132
132
|
const server = quarantinedMcp[0]!;
|
|
133
|
-
rows.push(infoRow('mcp:q', `MCP quarantine: ${server.name} ${server.quarantineReason ?? 'unknown'}${server.quarantineDetail ?
|
|
133
|
+
rows.push(infoRow('mcp:q', `MCP quarantine: ${server.name} ${server.quarantineReason ?? 'unknown'}${server.quarantineDetail ? `; ${server.quarantineDetail}` : ''}`, BAD));
|
|
134
134
|
}
|
|
135
135
|
if (snapshot.quarantinedPlugins.length > 0) {
|
|
136
136
|
const plugin = snapshot.quarantinedPlugins[0]!;
|
|
@@ -140,7 +140,7 @@ class SecurityModalSurface implements ConfigModalSurface {
|
|
|
140
140
|
rows.push(infoRow('plugin:u', `Plugin trust warning: ${plugin.name} remains untrusted.`, WARN));
|
|
141
141
|
}
|
|
142
142
|
if (snapshot.latestIncident) {
|
|
143
|
-
rows.push(infoRow('incident', `Latest incident: ${snapshot.latestIncident.classification}
|
|
143
|
+
rows.push(infoRow('incident', `Latest incident: ${snapshot.latestIncident.classification}; ${snapshot.latestIncident.summary} (${fmtIso(snapshot.latestIncident.generatedAt)})`, WARN));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
const review = snapshot.attackPathReview;
|
|
@@ -47,7 +47,7 @@ function authSummary(config: ServiceConfig, manager: SubscriptionAccessQuery): s
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Services config-modal surface (migrated from the `services` panel). Reads the
|
|
50
|
-
* same ServiceInspectionQuery
|
|
50
|
+
* same ServiceInspectionQuery, getAll() is sync but inspect()/testConnection()
|
|
51
51
|
* are async, so an inspection cache is refreshed on open / `r` and read
|
|
52
52
|
* synchronously by buildView (mirrors the panel's own refresh contract).
|
|
53
53
|
* Actions map onto their existing behaviours; `s` jumps to the subscription
|
|
@@ -12,7 +12,7 @@ import type { WorkPlanItemStatus } from '@pellux/goodvibes-sdk/platform/workflow
|
|
|
12
12
|
// All mutations (add/edit item, cycle/set status, remove, clear-completed)
|
|
13
13
|
// route to the existing `/work-plan` command path (charter: no
|
|
14
14
|
// destructive/interactive mutation direct-called from a modal; the panel's
|
|
15
|
-
// inline add/edit draft form has no place in a read/navigate modal
|
|
15
|
+
// inline add/edit draft form has no place in a read/navigate modal, the bare
|
|
16
16
|
// `/work-plan add` subcommand is the honest stand-in). Selection-blind port:
|
|
17
17
|
// the panel's selected-item owner/source/notes/linked detail is folded into
|
|
18
18
|
// each row label.
|
|
@@ -65,7 +65,7 @@ class WorkPlanModalSurface implements ConfigModalSurface {
|
|
|
65
65
|
private readonly hasRow = (row: ConfigModalRow | null): boolean => row !== null;
|
|
66
66
|
|
|
67
67
|
/** 'i'/'w' jump to the selected item's linked agent/WRFC chain in Fleet
|
|
68
|
-
* (item 4
|
|
68
|
+
* (item 4, restores the retired WorkPlanPanel's per-item deep-link,
|
|
69
69
|
* lost when migrated this surface to a modal without it). Gated on
|
|
70
70
|
* the link actually being present, same as the retired panel gated the key
|
|
71
71
|
* on `item?.linked?.agentId`/`wrfcId` before ever reaching the jump. */
|
|
@@ -137,7 +137,7 @@ class WorkPlanModalSurface implements ConfigModalSurface {
|
|
|
137
137
|
if (id === 'jumpAgent') {
|
|
138
138
|
const agentId = item.linked?.agentId;
|
|
139
139
|
if (!agentId) return;
|
|
140
|
-
// 'agent' matches the SDK's ProcessKind for a live agent node
|
|
140
|
+
// 'agent' matches the SDK's ProcessKind for a live agent node, see
|
|
141
141
|
// FleetPanel.receiveDeepLink's id+kind match.
|
|
142
142
|
void ctx.executeCommand?.('panel', ['open', 'fleet', '--target', `${agentId}:agent`]);
|
|
143
143
|
ctx.setStatus(`Opened Fleet on agent ${agentId}.`);
|
|
@@ -165,7 +165,7 @@ export function createWorkPlanModalSurface(deps: WorkPlanModalDeps): ConfigModal
|
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
167
|
* Deterministic golden fixture: fixed work-plan items with frozen updatedAt
|
|
168
|
-
* epoch values
|
|
168
|
+
* epoch values, no live store, no wall-clock, no random ids.
|
|
169
169
|
*/
|
|
170
170
|
export function workPlanModalGoldenSurface(): ConfigModalSurface {
|
|
171
171
|
const FIXED_UPDATED_AT = 1735693200000; // 2025-01-01T01:00:00.000Z
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PanelNotificationFeed
|
|
2
|
+
* PanelNotificationFeed, the read-model behind the `panel_only` notification
|
|
3
3
|
* target.
|
|
4
4
|
*
|
|
5
5
|
* The SDK's NotificationRouter (`@/runtime/index.ts` -> platform runtime `ui`
|
|
6
6
|
* barrel) can route a notification's `RoutingDecision.target` to
|
|
7
7
|
* `'panel_only'`, meaning: don't surface this in the conversation or the
|
|
8
8
|
* status bar, but it must still reach a real, visible place. Before this
|
|
9
|
-
* file, nothing in this repo consumed that target outside of tests
|
|
9
|
+
* file, nothing in this repo consumed that target outside of tests, a
|
|
10
10
|
* panel-routed notification (including every burst- or batch-collapsed one)
|
|
11
11
|
* had nowhere to go. This feed is that place: a caller holding a routed
|
|
12
12
|
* `Notification` + `RoutingDecision` pair calls `record()`, and
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* are tracked by their `batchKey` and accumulate an honest running count
|
|
17
17
|
* instead of rendering one row per suppressed duplicate.
|
|
18
18
|
*
|
|
19
|
-
* Production wiring
|
|
20
|
-
* `record()` with live notifications
|
|
19
|
+
* Production wiring, something in the running app actually calling
|
|
20
|
+
* `record()` with live notifications, rides the SDK round that turns on
|
|
21
21
|
* adaptive notification suppression by default; this feed and its panel are
|
|
22
22
|
* the render target that work lands on, built ahead of it so the target is
|
|
23
23
|
* never missing when that switch flips.
|
|
@@ -33,7 +33,7 @@ export interface PanelFeedEntry {
|
|
|
33
33
|
/** Unix ms of the most recent notification folded into this entry. */
|
|
34
34
|
readonly timestamp: number;
|
|
35
35
|
readonly reasonCode: RoutingDecision['reasonCode'];
|
|
36
|
-
/** How many notifications this entry represents. 1 for a standalone item; >1 for a collapsed group. Always the true count
|
|
36
|
+
/** How many notifications this entry represents. 1 for a standalone item; >1 for a collapsed group. Always the true count, never estimated. */
|
|
37
37
|
readonly collapsedCount: number;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -53,7 +53,7 @@ export class PanelNotificationFeed {
|
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Record a routed notification. Only notifications actually targeted at
|
|
56
|
-
* `panel_only` are kept
|
|
56
|
+
* `panel_only` are kept, a caller passing anything else is a mistake (the
|
|
57
57
|
* router sent it elsewhere), so it's dropped rather than shown in the
|
|
58
58
|
* wrong place.
|
|
59
59
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NotificationsPanel
|
|
2
|
+
* NotificationsPanel, the visible home for notifications routed to the
|
|
3
3
|
* `panel_only` target (see notifications-feed.ts for why this exists).
|
|
4
4
|
*
|
|
5
5
|
* Renders every entry the feed has accumulated: standalone panel-routed
|
|
6
6
|
* notifications, and collapsed burst/batch groups shown honestly with their
|
|
7
7
|
* real running count and the plain-language reason they were held back from
|
|
8
8
|
* the conversation. Descriptive text (titles, bodies, reason descriptions)
|
|
9
|
-
* is always wrapped in full
|
|
9
|
+
* is always wrapped in full, never clipped to a column width.
|
|
10
10
|
*/
|
|
11
11
|
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
12
12
|
import type { RoutingDecision } from '@/runtime/index.ts';
|
|
@@ -23,7 +23,7 @@ import { wrapText } from '../utils/terminal-width.ts';
|
|
|
23
23
|
import { getSharedNotificationFeed, type PanelFeedEntry, type PanelNotificationFeed } from './notifications-feed.ts';
|
|
24
24
|
|
|
25
25
|
// Reuses the shared, mode-aware (dark/light) panel palette's existing
|
|
26
|
-
// bad/warn/info/dim tokens rather than minting new hex literals
|
|
26
|
+
// bad/warn/info/dim tokens rather than minting new hex literals, this
|
|
27
27
|
// panel has no color needs the shared palette doesn't already name.
|
|
28
28
|
const C = DEFAULT_PANEL_PALETTE;
|
|
29
29
|
|
|
@@ -51,17 +51,17 @@ function levelTag(level: PanelFeedEntry['level']): string {
|
|
|
51
51
|
return `[${level}]`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/** Build every Line an entry needs
|
|
54
|
+
/** Build every Line an entry needs, title/meta row, then the full body and reason text wrapped with no line cap (descriptive text is never clipped). */
|
|
55
55
|
function renderEntry(entry: PanelFeedEntry, width: number): Line[] {
|
|
56
56
|
const lines: Line[] = [];
|
|
57
57
|
const fg = levelColor(entry.level);
|
|
58
58
|
const countSuffix = entry.collapsedCount > 1 ? ` ×${entry.collapsedCount}` : '';
|
|
59
|
-
const headerText = `${levelTag(entry.level)} ${entry.domain}
|
|
59
|
+
const headerText = `${levelTag(entry.level)} ${entry.domain}: ${entry.title}${countSuffix}`;
|
|
60
60
|
for (const wrapped of wrapText(headerText, Math.max(1, width - 1))) {
|
|
61
61
|
lines.push(buildStyledPanelLine(width, [{ text: ` ${wrapped}`, fg, bold: true }]));
|
|
62
62
|
}
|
|
63
63
|
const reasonText = entry.collapsedCount > 1
|
|
64
|
-
? ` ${entry.collapsedCount} notifications collapsed
|
|
64
|
+
? ` ${entry.collapsedCount} notifications collapsed: ${REASON_DESCRIPTIONS[entry.reasonCode]}`
|
|
65
65
|
: ` ${REASON_DESCRIPTIONS[entry.reasonCode]}`;
|
|
66
66
|
for (const wrapped of wrapText(reasonText, Math.max(1, width - 1))) {
|
|
67
67
|
lines.push(buildStyledPanelLine(width, [{ text: ` ${wrapped}`, fg: C.dim }]));
|