@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
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* fast-path.ts
|
|
2
|
+
* fast-path.ts, minimum-steps first run.
|
|
3
3
|
*
|
|
4
4
|
* The full onboarding wizard is comprehensive but long (5+ steps for a brand
|
|
5
5
|
* new user). The fast path gets a new user to a working session in the fewest
|
|
6
6
|
* possible steps:
|
|
7
7
|
* - If a provider API key is already configured (env / secrets), skip
|
|
8
|
-
* onboarding entirely
|
|
8
|
+
* onboarding entirely, write the check marker and drop straight into a
|
|
9
9
|
* working session. ZERO steps.
|
|
10
10
|
* - Otherwise ask exactly one question: start now on the default model, or
|
|
11
|
-
* run the full guided setup. Dismissing (Esc) starts now
|
|
11
|
+
* run the full guided setup. Dismissing (Esc) starts now, so it is
|
|
12
12
|
* always skippable. The start-now copy is GENERATED from the default
|
|
13
13
|
* model's registered auth state (buildOnboardingModelCopy), so a
|
|
14
14
|
* "no API key needed" promise can only render when the provider is
|
|
@@ -39,7 +39,7 @@ export interface OnboardingFastPathDeps {
|
|
|
39
39
|
readonly render: () => void;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/** Read configured provider ids defensively
|
|
42
|
+
/** Read configured provider ids defensively, returns [] if the surface can't answer. */
|
|
43
43
|
function readConfiguredProviderIds(commandContext: CommandContext): string[] | null {
|
|
44
44
|
const registry = commandContext.provider?.providerRegistry;
|
|
45
45
|
if (!registry || typeof registry.getConfiguredProviderIds !== 'function') return null;
|
|
@@ -89,10 +89,10 @@ export function startOnboardingFastPath(deps: OnboardingFastPathDeps): void {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
if (configured.length > 0) {
|
|
92
|
-
// A provider key already exists
|
|
92
|
+
// A provider key already exists, nothing to ask. Straight to a working session.
|
|
93
93
|
writeOnboardingCheckMarker(shellPaths, MARKER_OPTIONS);
|
|
94
94
|
commandContext.print?.(
|
|
95
|
-
`Ready
|
|
95
|
+
`Ready: provider configured (${configured.join(', ')}). Run /onboarding anytime for full setup.`,
|
|
96
96
|
);
|
|
97
97
|
render();
|
|
98
98
|
return;
|
|
@@ -104,7 +104,7 @@ export function startOnboardingFastPath(deps: OnboardingFastPathDeps): void {
|
|
|
104
104
|
{ id: 'full-setup', label: 'Full guided setup', detail: 'Choose provider, model, channels, and more', primaryAction: 'select' },
|
|
105
105
|
];
|
|
106
106
|
commandContext.openSelection(
|
|
107
|
-
'Welcome to GoodVibes
|
|
107
|
+
'Welcome to GoodVibes: get started',
|
|
108
108
|
items,
|
|
109
109
|
{ allowSearch: false, primaryVerbLabel: 'Choose' },
|
|
110
110
|
(result) => {
|
|
@@ -90,7 +90,7 @@ export interface WriteWizardProgressOptions {
|
|
|
90
90
|
* project switches and is shared with the resume-prompt check at startup.
|
|
91
91
|
*
|
|
92
92
|
* Masked (password) fields are deliberately excluded from the serialised
|
|
93
|
-
* textState by the caller
|
|
93
|
+
* textState by the caller, this function accepts whatever is passed in and
|
|
94
94
|
* does NOT filter. Callers must strip sensitive fields before calling.
|
|
95
95
|
*/
|
|
96
96
|
export function writeWizardProgress(
|
|
@@ -143,6 +143,6 @@ export function hasResumableWizardProgress(
|
|
|
143
143
|
const state = options.state ?? readWizardProgress(shellPaths);
|
|
144
144
|
if (!state.payload) return false;
|
|
145
145
|
const age = (options.now ?? Date.now()) - state.payload.savedAt;
|
|
146
|
-
// age < 0 means savedAt is in the future (clock skew / tampered file)
|
|
146
|
+
// age < 0 means savedAt is in the future (clock skew / tampered file), treat as non-resumable.
|
|
147
147
|
return age >= 0 && age < PROGRESS_MAX_AGE_MS;
|
|
148
148
|
}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Provider-agnostic key capture for onboarding.
|
|
3
3
|
*
|
|
4
4
|
* The onboarding wizard offers an API-key field for every provider the registry
|
|
5
|
-
* knows accepts a key
|
|
5
|
+
* knows accepts a key, derived from each provider's registration-time auth
|
|
6
6
|
* state (describeAuthState), never a hardcoded vendor list. This module holds
|
|
7
7
|
* the two shared pieces that keep the wizard step and the apply step in lockstep:
|
|
8
8
|
*
|
|
9
|
-
* 1. enrichProviderAccountsSnapshot
|
|
9
|
+
* 1. enrichProviderAccountsSnapshot, stamps each provider-account record with
|
|
10
10
|
* the secrets-store key it reads its API key from (its declared auth env
|
|
11
11
|
* var), read live from the provider registry.
|
|
12
|
-
* 2. deriveProviderKeyCaptureTargets / providerKeyFieldId
|
|
12
|
+
* 2. deriveProviderKeyCaptureTargets / providerKeyFieldId, the single source
|
|
13
13
|
* of truth for which providers get a key field and how those fields are
|
|
14
14
|
* named, used by both the field builder and the apply writer.
|
|
15
15
|
*/
|
|
@@ -99,7 +99,7 @@ export interface SubscriptionStatusTarget {
|
|
|
99
99
|
/**
|
|
100
100
|
* The providers onboarding shows a subscription-status row for: every provider
|
|
101
101
|
* that declares a subscription route (registration truth), plus any provider
|
|
102
|
-
* with an active or pending subscription session
|
|
102
|
+
* with an active or pending subscription session, so a live session is never
|
|
103
103
|
* hidden just because its account record was absent. Ordered by provider id.
|
|
104
104
|
*/
|
|
105
105
|
export function deriveSubscriptionStatusTargets(
|
|
@@ -402,7 +402,7 @@ export async function collectOnboardingSnapshot(
|
|
|
402
402
|
snapshot: authSnapshotResult.value,
|
|
403
403
|
},
|
|
404
404
|
bindSettings: {
|
|
405
|
-
// danger.daemon (since removed
|
|
405
|
+
// danger.daemon (since removed, see docs/decisions/2026-07-05-daemon-by-default.md)
|
|
406
406
|
// used to gate the earlier opt-in daemon posture; this site read it RAW
|
|
407
407
|
// (not through resolveDaemonEnabled) so the wizard's network-mode
|
|
408
408
|
// classification tracked "did the user explicitly request the legacy
|
|
@@ -412,7 +412,7 @@ export async function collectOnboardingSnapshot(
|
|
|
412
412
|
// already evaluated this to `false` (unset, or an explicit `danger.daemon:
|
|
413
413
|
// false` both did); the alias's removal migration preserves the one case
|
|
414
414
|
// that changes real daemon behavior (explicit `false` -> `daemon.enabled:
|
|
415
|
-
// false`) but does not resurrect a signal for "explicitly true"
|
|
415
|
+
// false`) but does not resurrect a signal for "explicitly true", that path
|
|
416
416
|
// was always a no-op for actual daemon operation. With the alias gone there
|
|
417
417
|
// is no signal left to read, so this is pinned at its steady-state value.
|
|
418
418
|
daemonEnabled: false,
|
|
@@ -20,9 +20,9 @@ const ONBOARDING_RUNTIME_STATE_FILE = 'onboarding-state.json';
|
|
|
20
20
|
* This is the simplest correct approach for two same-host processes (daemon
|
|
21
21
|
* + TUI) that both run this read-modify-write path:
|
|
22
22
|
*
|
|
23
|
-
* - Acquire: open(<statefile>.lock, O_CREAT|O_EXCL|O_WRONLY)
|
|
23
|
+
* - Acquire: open(<statefile>.lock, O_CREAT|O_EXCL|O_WRONLY), atomic on POSIX.
|
|
24
24
|
* - Stale detection: if the lockfile mtime is >= LOCK_STALE_MS old, force-remove.
|
|
25
|
-
* - Retry: up to LOCK_MAX_RETRIES rapid non-blocking attempts (no sleep
|
|
25
|
+
* - Retry: up to LOCK_MAX_RETRIES rapid non-blocking attempts (no sleep, main-thread safe).
|
|
26
26
|
* - Release: unlink the lockfile (best-effort on failure).
|
|
27
27
|
*
|
|
28
28
|
* O_EXCL was chosen over flock(2) because it works on all POSIX targets
|
|
@@ -103,7 +103,7 @@ function acquireLock(lp: string): boolean {
|
|
|
103
103
|
} catch { /* lockfile does not exist — expected happy path */ }
|
|
104
104
|
|
|
105
105
|
try {
|
|
106
|
-
// 'wx' ≡ O_CREAT | O_EXCL | O_WRONLY
|
|
106
|
+
// 'wx' ≡ O_CREAT | O_EXCL | O_WRONLY, fails atomically if file exists.
|
|
107
107
|
const fd = openSync(lp, 'wx');
|
|
108
108
|
closeSync(fd);
|
|
109
109
|
return true;
|
|
@@ -180,7 +180,7 @@ export function writeOnboardingAcknowledgementState(
|
|
|
180
180
|
const acquired = acquireLock(lp);
|
|
181
181
|
if (!acquired) {
|
|
182
182
|
// Lock exhaustion: another process has held the lock for all LOCK_MAX_RETRIES
|
|
183
|
-
// attempts. Proceeding without the lock
|
|
183
|
+
// attempts. Proceeding without the lock, the atomic write (rename) prevents
|
|
184
184
|
// torn files, but under true concurrent contention a concurrent read-modify-write
|
|
185
185
|
// may result in a lost-update (last writer wins). Surfaced here so it is
|
|
186
186
|
// detectable in logs rather than silently discarded.
|
|
@@ -128,7 +128,7 @@ export interface OnboardingSurfacesSnapshot {
|
|
|
128
128
|
* systemd unit (see `../legacy-daemon-migration.ts`'s `LegacyUnitInfo`), carried
|
|
129
129
|
* on the snapshot so the Network step can show the guided migration action only
|
|
130
130
|
* when there is actually something to migrate. Never implies anything was
|
|
131
|
-
* touched
|
|
131
|
+
* touched, detection only.
|
|
132
132
|
*/
|
|
133
133
|
export interface OnboardingLegacyDaemonSnapshot {
|
|
134
134
|
readonly present: boolean;
|
|
@@ -137,7 +137,7 @@ export interface OnboardingLegacyDaemonSnapshot {
|
|
|
137
137
|
/**
|
|
138
138
|
* Follow-up: the unit name this tool actually manages on this host,
|
|
139
139
|
* resolved from the `service.serviceName` config key at snapshot-collection
|
|
140
|
-
* time (`resolveConfiguredServiceName`, `../legacy-daemon-migration.ts`)
|
|
140
|
+
* time (`resolveConfiguredServiceName`, `../legacy-daemon-migration.ts`),
|
|
141
141
|
* so the wizard's detection banner names the real unit instead of the
|
|
142
142
|
* hardcoded default. Optional so snapshots built without config access
|
|
143
143
|
* (fallbacks, older fixtures) stay valid; readers fall back to
|
|
@@ -156,7 +156,7 @@ export interface OnboardingProviderAccountRecord {
|
|
|
156
156
|
readonly activeRoute: string;
|
|
157
157
|
readonly authFreshness: string;
|
|
158
158
|
/**
|
|
159
|
-
* The secrets-store key this provider reads its API key from
|
|
159
|
+
* The secrets-store key this provider reads its API key from, its declared
|
|
160
160
|
* auth env var, read from the live provider's registration-time auth state.
|
|
161
161
|
* Present only for providers that accept an API key; absent for keyless /
|
|
162
162
|
* subscription-only providers. Used to offer provider-agnostic key entry in
|
|
@@ -283,7 +283,7 @@ export type OnboardingApplyOperation =
|
|
|
283
283
|
readonly value: string;
|
|
284
284
|
/**
|
|
285
285
|
* Which secret tier the value is filed in. 'daemon' is the tier the
|
|
286
|
-
* daemon reads with every surface closed
|
|
286
|
+
* daemon reads with every surface closed, see
|
|
287
287
|
* config/secret-config.ts#defaultSecretBackedScope.
|
|
288
288
|
*/
|
|
289
289
|
readonly scope?: 'project' | 'user' | 'daemon';
|
|
@@ -21,11 +21,11 @@ export type OrchestratorCoreServicesSource = Pick<
|
|
|
21
21
|
* The single source of truth for the `Orchestrator.setCoreServices()` payload
|
|
22
22
|
* fields shared by BOTH call sites (runtime/bootstrap.ts and main.ts). Each
|
|
23
23
|
* site spreads this and adds its own site-specific extras (bootstrap:
|
|
24
|
-
* cacheHitTracker; main: favoritesStore)
|
|
24
|
+
* cacheHitTracker; main: favoritesStore), setCoreServices() merges, so the
|
|
25
25
|
* later main.ts call only overlays, never erases.
|
|
26
26
|
*
|
|
27
27
|
* BLOCKER regression guard: `memoryRegistry` here is what turns
|
|
28
|
-
* on per-turn passive knowledge injection for the MAIN interactive session
|
|
28
|
+
* on per-turn passive knowledge injection for the MAIN interactive session,
|
|
29
29
|
* the SDK turn loop hard-gates on `coreServices.memoryRegistry` (undefined is
|
|
30
30
|
* a silent no-op: Orchestrator.getTurnInjections() stays empty forever and
|
|
31
31
|
* `/recall injections` renders a misleading empty state). Both call sites once
|
|
@@ -35,12 +35,12 @@ export type OrchestratorCoreServicesSource = Pick<
|
|
|
35
35
|
*
|
|
36
36
|
* SDK 1.2.0 FULL DETACH: the SDK's turn loop reads `memoryRegistry.getAll()`
|
|
37
37
|
* SYNCHRONOUSLY (`TurnKnowledgeRegistrySource`), but the memory spine's wire
|
|
38
|
-
* reads are asynchronous
|
|
38
|
+
* reads are asynchronous, a sync function cannot await the wire. Per
|
|
39
39
|
* docs/decisions/2026-07-06-memory-wire-full-detach.md (SDK repo) this is
|
|
40
40
|
* satisfied by the spine's freshness-stamped recall snapshot instead of the
|
|
41
41
|
* raw local `memoryRegistry`: `getAll()` reads `memorySpine.recallSnapshot()`,
|
|
42
42
|
* which returns whatever the last `refreshRecallSnapshot()` (an async
|
|
43
|
-
* pre-turn hook
|
|
43
|
+
* pre-turn hook, see the `handleUserInput` call sites) captured, honestly
|
|
44
44
|
* empty/stale until refreshed. This is what lets per-turn knowledge injection
|
|
45
45
|
* detach from the local store file when a daemon is adopted, instead of
|
|
46
46
|
* always reading the (possibly divergent) local registry regardless of mode.
|
|
@@ -75,7 +75,7 @@ export function buildSharedOrchestratorCoreServices(input: {
|
|
|
75
75
|
* the CURRENT route (wire when adopted, local otherwise) so the synchronous
|
|
76
76
|
* per-turn knowledge injection (`memoryRegistry.getAll()` above) reads
|
|
77
77
|
* up-to-date records instead of whatever the previous refresh captured.
|
|
78
|
-
* Failures are swallowed
|
|
78
|
+
* Failures are swallowed, an honest stale/empty snapshot (with its own
|
|
79
79
|
* degradation note, surfaced via `recallSnapshot().note`) is preferable to
|
|
80
80
|
* blocking the user's turn on a memory-read failure.
|
|
81
81
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* path-shadow-startup.ts
|
|
2
|
+
* path-shadow-startup.ts, this terminal's boot-time reachability report.
|
|
3
3
|
*
|
|
4
4
|
* The check itself (the PATH scan, the `--version` probes, the cost discipline
|
|
5
5
|
* that keeps a healthy start free of spawns and network calls, and the wording)
|
|
@@ -21,7 +21,7 @@ const TERMINAL_PACKAGE_NAME = '@pellux/goodvibes-tui';
|
|
|
21
21
|
/** The command name the shell resolves this terminal by. */
|
|
22
22
|
const TERMINAL_COMMAND_NAME = 'goodvibes';
|
|
23
23
|
|
|
24
|
-
/** The same release lookup `/update` uses
|
|
24
|
+
/** The same release lookup `/update` uses, there is deliberately no second source of truth. */
|
|
25
25
|
function resolveLatestRelease(): Promise<string | undefined> {
|
|
26
26
|
return boundedLatestRelease(async () => {
|
|
27
27
|
const result = await checkForUpdate(fetch as UpdateFetchLike, VERSION);
|
|
@@ -32,7 +32,7 @@ function resolveLatestRelease(): Promise<string | undefined> {
|
|
|
32
32
|
/**
|
|
33
33
|
* Boot-time wiring. Prints one high-priority system message per line so the
|
|
34
34
|
* report reads as prose rather than a single wrapped paragraph, and swallows
|
|
35
|
-
* everything
|
|
35
|
+
* everything, a reachability check must never block or crash boot.
|
|
36
36
|
*/
|
|
37
37
|
export async function announceInstallReachability(router: SystemMessageRouter): Promise<void> {
|
|
38
38
|
await announceReachability(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// power-keepawake-remote.ts
|
|
2
|
+
// power-keepawake-remote.ts, forward the keep-awake toggle to an adopted
|
|
3
3
|
// EXTERNAL daemon over the power.keepAwake.set operator verb.
|
|
4
4
|
//
|
|
5
5
|
// In the external/adopted-daemon topology the in-process PowerManager is NOT
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
// closes. The owner ruling is that keep-awake is daemon-held: it must survive
|
|
8
8
|
// the TUI closing. The config file does NOT carry it across (power.keepAwake is
|
|
9
9
|
// not a cross-surface shared key), so the toggle is forwarded to the daemon
|
|
10
|
-
// over the verb instead
|
|
10
|
+
// over the verb instead, best-effort, gated on reachability. Mirrors the
|
|
11
11
|
// agent's own power-keep-awake-remote seam.
|
|
12
12
|
//
|
|
13
13
|
// A single config subscriber carries ALL THREE toggle paths (/power, Alt+A, the
|
|
14
|
-
// settings modal): each lands on the power.keepAwake config key
|
|
15
|
-
// PowerManager persists it on set, and the settings modal writes it directly
|
|
14
|
+
// settings modal): each lands on the power.keepAwake config key, the local
|
|
15
|
+
// PowerManager persists it on set, and the settings modal writes it directly,
|
|
16
16
|
// so subscribing to that one key catches every origin. When there is no adopted
|
|
17
17
|
// external daemon (`isExternalDaemon()` false), this is a no-op: there is nothing
|
|
18
18
|
// to forward to.
|
|
@@ -34,7 +34,7 @@ export interface KeepAwakeRemoteDeps {
|
|
|
34
34
|
/**
|
|
35
35
|
* Best-effort forward of the keep-awake toggle to the adopted external daemon so
|
|
36
36
|
* the DAEMON holds the inhibitor (surviving the TUI closing). A no-op in the
|
|
37
|
-
* embedded topology and quiet when the daemon is unreachable
|
|
37
|
+
* embedded topology and quiet when the daemon is unreachable, a transient
|
|
38
38
|
* daemon hiccup never breaks the local toggle.
|
|
39
39
|
*/
|
|
40
40
|
export async function forwardKeepAwakeToDaemon(enabled: boolean, deps: KeepAwakeRemoteDeps): Promise<void> {
|
|
@@ -61,7 +61,7 @@ export function installKeepAwakeRemoteForward(deps: KeepAwakeRemoteDeps): () =>
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Fetch the adopted DAEMON's power.status.get (null when unreachable or not in
|
|
64
|
-
* the external topology). The chip's external-mode poll source
|
|
64
|
+
* the external topology). The chip's external-mode poll source, the adopted
|
|
65
65
|
* wire is plain HTTP with no event bridge, so the chip syncs by polling.
|
|
66
66
|
*/
|
|
67
67
|
export async function fetchDaemonPowerState(deps: KeepAwakeRemoteDeps): Promise<PowerState | null> {
|
|
@@ -32,14 +32,14 @@ import type { ConversationMessageSnapshot } from '../core/conversation.ts';
|
|
|
32
32
|
|
|
33
33
|
/** Shutdown grace period before the "saving session…" line prints. Below this, exit stays quiet. */
|
|
34
34
|
const SAVE_NOTICE_AFTER_MS = 300;
|
|
35
|
-
/** Hard shutdown timeout
|
|
35
|
+
/** Hard shutdown timeout, matches the pre-existing race below (was previously an inline literal). */
|
|
36
36
|
const SHUTDOWN_HARD_TIMEOUT_MS = 3000;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Whether a recovery snapshot actually exists on disk for this session. The
|
|
40
40
|
* periodic autosave (see recovery-autosave.ts) only writes on a 60s tick and
|
|
41
41
|
* skips empty conversations entirely, so a session that dies sooner than that
|
|
42
|
-
* (or never had anything in it) has no snapshot at all
|
|
42
|
+
* (or never had anything in it) has no snapshot at all, the exit receipt
|
|
43
43
|
* must not claim otherwise. Never throws: a stat failure just means "absent".
|
|
44
44
|
*/
|
|
45
45
|
function defaultRecoverySnapshotExists(surface: SessionSurface, sessionId: string): boolean {
|
|
@@ -58,7 +58,7 @@ export interface ProcessLifecycleAnsi {
|
|
|
58
58
|
readonly KEYBOARD_EXT_DISABLE: string;
|
|
59
59
|
readonly MOUSE_DISABLE: string;
|
|
60
60
|
readonly CURSOR_SHOW: string;
|
|
61
|
-
/** Disables terminal focus-event reporting (DECSET ?1004l)
|
|
61
|
+
/** Disables terminal focus-event reporting (DECSET ?1004l), see main.ts FOCUS_ENABLE. */
|
|
62
62
|
readonly FOCUS_DISABLE: string;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -88,7 +88,7 @@ export interface ProcessLifecycleDeps {
|
|
|
88
88
|
readonly shutdownHardTimeoutMs?: number;
|
|
89
89
|
/**
|
|
90
90
|
* Overridable for tests; defaults to a real filesystem existence check
|
|
91
|
-
* (defaultRecoverySnapshotExists). Never throws
|
|
91
|
+
* (defaultRecoverySnapshotExists). Never throws, a stat failure reads as
|
|
92
92
|
* "absent" rather than taking the exit path down.
|
|
93
93
|
*/
|
|
94
94
|
readonly recoverySnapshotExists?: (surface: SessionSurface, sessionId: string) => boolean;
|
|
@@ -145,7 +145,7 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
145
145
|
_unhandledRejectionCount++;
|
|
146
146
|
const msg = summarizeError(reason);
|
|
147
147
|
if (_unhandledRejectionCount > 3) {
|
|
148
|
-
logger.error('CRITICAL: cascading unhandled rejections
|
|
148
|
+
logger.error('CRITICAL: cascading unhandled rejections; consider restarting', {
|
|
149
149
|
count: _unhandledRejectionCount,
|
|
150
150
|
windowMs: now - _unhandledRejectionWindowStart,
|
|
151
151
|
error: String(reason),
|
|
@@ -176,7 +176,7 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
176
176
|
// The injected `ansi` restore sequences are mapped onto the package's escape
|
|
177
177
|
// set so the exact bytes the TUI has always emitted are preserved. The no-alt
|
|
178
178
|
// exit deliberately uses the package's CLEAR_VIEWPORT_HOME ('\x1b[2J\x1b[H',
|
|
179
|
-
// no ESC[3J)
|
|
179
|
+
// no ESC[3J), never ansi.CLEAR_SCREEN, which carries the scrollback-wiping 3J.
|
|
180
180
|
const terminalLifecycle = createTerminalLifecycle({
|
|
181
181
|
write: (data) => { stdout.write(data); },
|
|
182
182
|
noAltScreen,
|
|
@@ -200,17 +200,17 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
200
200
|
|
|
201
201
|
const uncaughtExceptionHandler = (err: Error): void => {
|
|
202
202
|
restoreTerminal();
|
|
203
|
-
logger.error('uncaughtException
|
|
203
|
+
logger.error('uncaughtException: terminal restored, exiting', { error: summarizeError(err) });
|
|
204
204
|
// The line naming the crash is the only record of it. process.exit does not
|
|
205
205
|
// drain the activity log's buffer, so it goes to disk here rather than
|
|
206
|
-
// relying on the exit hook that also runs
|
|
206
|
+
// relying on the exit hook that also runs, the crash log is worth stating
|
|
207
207
|
// the intent for at the site that produces it.
|
|
208
208
|
flushActivityLogSync();
|
|
209
209
|
process.exit(1);
|
|
210
210
|
};
|
|
211
211
|
const terminationSignalHandler = (signal: NodeJS.Signals): void => {
|
|
212
212
|
restoreTerminal();
|
|
213
|
-
logger.error(`Received ${signal}
|
|
213
|
+
logger.error(`Received ${signal}: terminal restored, exiting`, {});
|
|
214
214
|
flushActivityLogSync();
|
|
215
215
|
process.exit(signal === 'SIGHUP' ? 129 : 143);
|
|
216
216
|
};
|
|
@@ -251,7 +251,7 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
251
251
|
try { stdout.write('saving session…\n'); } catch { /* best-effort */ }
|
|
252
252
|
}, saveNoticeAfterMs);
|
|
253
253
|
try {
|
|
254
|
-
// Race the graceful shutdown against a hard timeout
|
|
254
|
+
// Race the graceful shutdown against a hard timeout, externalServices.stop() can hang
|
|
255
255
|
// and we must still exit; deferredStartup.drain only budgets 100ms internally.
|
|
256
256
|
// The spoken-audio drain runs concurrently and is internally capped below
|
|
257
257
|
// this budget, so it never extends the exit beyond the hard timeout.
|
|
@@ -273,17 +273,17 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
273
273
|
if (shutdownOk) {
|
|
274
274
|
deleteRecoveryFile({ surface: ctx.services.surface }, ctx.runtime.sessionId);
|
|
275
275
|
} else {
|
|
276
|
-
// The hard timeout won the race
|
|
276
|
+
// The hard timeout won the race, the durable save never confirmed, so
|
|
277
277
|
// whatever the periodic autosave last wrote (see recovery-autosave.ts)
|
|
278
278
|
// is deliberately kept above. But that autosave only ticks every 60s
|
|
279
279
|
// and skips empty conversations, so a session that dies sooner than
|
|
280
|
-
// that (or never had anything in it) has no snapshot to keep
|
|
280
|
+
// that (or never had anything in it) has no snapshot to keep, check
|
|
281
281
|
// before claiming otherwise, since a silent exit here previously left
|
|
282
282
|
// no trace that anything was amiss.
|
|
283
283
|
const kept = recoverySnapshotExists(ctx.services.surface, ctx.runtime.sessionId);
|
|
284
284
|
const line = kept
|
|
285
|
-
? 'exit before save completed
|
|
286
|
-
: 'exit before save completed
|
|
285
|
+
? 'exit before save completed: a recovery snapshot was kept for next launch\n'
|
|
286
|
+
: 'exit before save completed: no recovery snapshot had been written yet\n';
|
|
287
287
|
try { stdout.write(line); } catch { /* best-effort */ }
|
|
288
288
|
}
|
|
289
289
|
// Best-effort, unconditional: this process is exiting either way, so its
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* provider-fallback.ts
|
|
2
|
+
* provider-fallback.ts, boot-time model routability, the part the SDK does
|
|
3
3
|
* not cover.
|
|
4
4
|
*
|
|
5
5
|
* The pre-catalog fallback registration for the configured model
|
|
6
6
|
* (`ensureConfiguredModelIsRoutable`) is the SDK's own
|
|
7
|
-
* (`@pellux/goodvibes-sdk/platform/providers`)
|
|
7
|
+
* (`@pellux/goodvibes-sdk/platform/providers`), this file only imports it for
|
|
8
8
|
* `ensureBootModelResolvable` below, which is NOT in the SDK: it is the
|
|
9
9
|
* boot-time custom-provider-readiness wrapper this app's boot path needs and
|
|
10
10
|
* the SDK's floor does not compose.
|
|
@@ -16,7 +16,7 @@ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
16
16
|
/**
|
|
17
17
|
* Boot-time custom-provider readiness. Custom providers register
|
|
18
18
|
* asynchronously (services.ts fires initCustomProviders() without awaiting),
|
|
19
|
-
* while the boot path resolves the current model synchronously
|
|
19
|
+
* while the boot path resolves the current model synchronously, without
|
|
20
20
|
* waiting here, a saved provider.model that points at a custom provider
|
|
21
21
|
* throws "not in registry" before the first frame renders. The routability
|
|
22
22
|
* guard must re-run after ready(): its services-composition pass bails when
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* recovery-autosave.ts
|
|
2
|
+
* recovery-autosave.ts, periodic recovery-file autosave + liveness-marker
|
|
3
3
|
* refresh, extracted out of main.ts to keep it under the architecture
|
|
4
4
|
* line-count gate.
|
|
5
5
|
*
|
|
@@ -22,8 +22,8 @@ export interface StartRecoveryAutosaveOptions {
|
|
|
22
22
|
readonly runtime: { readonly sessionId: string };
|
|
23
23
|
/**
|
|
24
24
|
* The app's declare-once session-storage handle. Both the snapshot and the
|
|
25
|
-
* liveness marker resolve off it, so the startup recovery offer
|
|
26
|
-
* reads through the same handle
|
|
25
|
+
* liveness marker resolve off it, so the startup recovery offer, which
|
|
26
|
+
* reads through the same handle, looks in the directory this writer used.
|
|
27
27
|
*/
|
|
28
28
|
readonly surface: SessionSurface;
|
|
29
29
|
readonly buildSessionContinuityHints: () => Parameters<typeof buildPersistedSessionContext>[2];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* recovery-decisions.ts
|
|
2
|
+
* recovery-decisions.ts, a durable record of the recovery snapshots the user
|
|
3
3
|
* has already said "Remove" to.
|
|
4
4
|
*
|
|
5
5
|
* The defect this exists for: answering Remove deletes the snapshot file, and
|
|
6
6
|
* that was the whole memory of the decision. Anything that puts the file back
|
|
7
|
-
|
|
8
|
-
* 60s, a restored backup, a synced directory
|
|
7
|
+
*, a session still running on an older build rewriting its snapshot every
|
|
8
|
+
* 60s, a restored backup, a synced directory, produced the same question
|
|
9
9
|
* again on the next launch, and again after the next removal, with no way for
|
|
10
10
|
* the user to make it stop. A decision the product forgets the instant it acts
|
|
11
11
|
* on it is not a decision the user gets to make.
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* run.
|
|
18
18
|
*
|
|
19
19
|
* WHERE IT LIVES, and why not under `surface.stateDir`:
|
|
20
|
-
* `<homeDirectory>/.goodvibes/<surfaceRoot>/recovery-decisions.json
|
|
20
|
+
* `<homeDirectory>/.goodvibes/<surfaceRoot>/recovery-decisions.json`, home-
|
|
21
21
|
* anchored, matching session-liveness-marker.ts, and deliberately NOT the
|
|
22
22
|
* project-anchored `surface.stateDir`. The snapshots this defends against
|
|
23
23
|
* include the ones in the SDK's legacy shared recovery directory, which is
|
|
@@ -38,7 +38,7 @@ import type { SessionSurface } from '@/runtime/index.ts';
|
|
|
38
38
|
/** One "the user removed this snapshot" decision. */
|
|
39
39
|
export interface RecoveryRemovalRecord {
|
|
40
40
|
readonly sessionId: string;
|
|
41
|
-
/** The project the decision was made in. Provenance
|
|
41
|
+
/** The project the decision was made in. Provenance, matching is on sessionId alone. */
|
|
42
42
|
readonly workspace: string;
|
|
43
43
|
readonly removedAt: number;
|
|
44
44
|
}
|
|
@@ -51,12 +51,12 @@ export interface RecoveryRemovalRecord {
|
|
|
51
51
|
* Note what is deliberately NOT the pruning rule: "drop records whose snapshot
|
|
52
52
|
* file no longer exists". Every record in here describes a file that was just
|
|
53
53
|
* deleted, so that rule would empty the ledger immediately and reinstate the
|
|
54
|
-
* exact defect it was written to fix
|
|
54
|
+
* exact defect it was written to fix, the point is precisely to still
|
|
55
55
|
* remember once the file comes back. Age and count are the honest bounds.
|
|
56
56
|
*
|
|
57
|
-
* Both bounds are enforced wherever records actually leave the disk
|
|
57
|
+
* Both bounds are enforced wherever records actually leave the disk,
|
|
58
58
|
* pruneRecoveryDecisions (once per boot) and recordRecoveryRemoval (every
|
|
59
|
-
* write)
|
|
59
|
+
* write), and both DISCLOSE what they discarded. A deletion nobody reports is
|
|
60
60
|
* indistinguishable from data loss, so the counts go in the log with the ledger
|
|
61
61
|
* path, the TTL and the cap alongside them.
|
|
62
62
|
*/
|
|
@@ -93,8 +93,8 @@ interface LedgerLoad {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
|
-
* Load and filter the ledger. Validation is by CONTENT
|
|
97
|
-
* every entry is shape-checked
|
|
96
|
+
* Load and filter the ledger. Validation is by CONTENT, the file is parsed and
|
|
97
|
+
* every entry is shape-checked, so a torn, truncated or zero-byte ledger left
|
|
98
98
|
* by a crash reads as "no decisions recorded" instead of being trusted because
|
|
99
99
|
* it exists. Never throws: a corrupt file must not be able to take a boot down,
|
|
100
100
|
* and the worst outcome of losing it is that the user is asked once more.
|
|
@@ -127,7 +127,7 @@ function loadLedger(surface: SessionSurface, nowMs: number): LedgerLoad {
|
|
|
127
127
|
* a corrupt file must not be able to take a boot down, and the worst outcome
|
|
128
128
|
* of losing it is that the user is asked once more.
|
|
129
129
|
*
|
|
130
|
-
* This is a filtered READ
|
|
130
|
+
* This is a filtered READ, it discards nothing from disk, so it stays silent.
|
|
131
131
|
* Disclosure belongs to the calls that actually delete: {@link pruneRecoveryDecisions}
|
|
132
132
|
* and {@link recordRecoveryRemoval}.
|
|
133
133
|
*/
|
|
@@ -139,8 +139,8 @@ export function readRecoveryRemovals(surface: SessionSurface, nowMs: number = Da
|
|
|
139
139
|
* Write `records` to the ledger through a temp file and an atomic rename.
|
|
140
140
|
*
|
|
141
141
|
* A plain `writeFileSync` onto the live path truncates first and fills after,
|
|
142
|
-
* so a crash
|
|
143
|
-
* anchored ledger at the same moment
|
|
142
|
+
* so a crash, or a second TUI in another terminal writing the same home-
|
|
143
|
+
* anchored ledger at the same moment, could leave a half-written array that a
|
|
144
144
|
* later boot reads as an empty or corrupt ledger, silently forgetting every
|
|
145
145
|
* decision the user made. `rename` is atomic on the same filesystem: a reader
|
|
146
146
|
* sees either the whole old ledger or the whole new one. The temp name carries
|
|
@@ -180,7 +180,7 @@ export interface RecoveryDecisionsPruneOutcome {
|
|
|
180
180
|
* Apply both bounds to the on-disk ledger and say what was discarded.
|
|
181
181
|
*
|
|
182
182
|
* `readRecoveryRemovals` filters in memory on every call, so the expired
|
|
183
|
-
* records were already inert
|
|
183
|
+
* records were already inert, but they stayed on disk until the next time the
|
|
184
184
|
* user happened to answer "Remove", which on a machine where that never happens
|
|
185
185
|
* again is never. This makes the reap explicit and, crucially, VISIBLE: a
|
|
186
186
|
* silent deletion is indistinguishable from data loss, so every pass that drops
|
|
@@ -189,7 +189,7 @@ export interface RecoveryDecisionsPruneOutcome {
|
|
|
189
189
|
* Idempotent and safe to run from two processes at once: it is a pure function
|
|
190
190
|
* of the file's content plus `nowMs`, the rewrite is a temp-file-plus-rename,
|
|
191
191
|
* and a second run over an already-pruned ledger drops nothing and writes
|
|
192
|
-
* nothing. Best-effort
|
|
192
|
+
* nothing. Best-effort, a ledger that cannot be pruned is left exactly as it
|
|
193
193
|
* was and never breaks a boot.
|
|
194
194
|
*/
|
|
195
195
|
export function pruneRecoveryDecisions(surface: SessionSurface, nowMs: number = Date.now()): RecoveryDecisionsPruneOutcome {
|
|
@@ -209,7 +209,7 @@ export function pruneRecoveryDecisions(surface: SessionSurface, nowMs: number =
|
|
|
209
209
|
// Do NOT rewrite: an unreadable ledger might be a transient read failure,
|
|
210
210
|
// and replacing it with `[]` would turn "could not read" into "forgot every
|
|
211
211
|
// decision the user made". Say so and leave it alone.
|
|
212
|
-
logger.warn('recovery decisions: ledger present but unreadable
|
|
212
|
+
logger.warn('recovery decisions: ledger present but unreadable; left untouched, decisions may be re-asked', {
|
|
213
213
|
ledger: path,
|
|
214
214
|
});
|
|
215
215
|
return outcome;
|
|
@@ -246,7 +246,7 @@ export function isRecoveryRemovalRecorded(surface: SessionSurface, sessionId: st
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
/**
|
|
249
|
-
* Record that the user chose Remove for `sessionId`. Idempotent
|
|
249
|
+
* Record that the user chose Remove for `sessionId`. Idempotent, a repeat
|
|
250
250
|
* removal of a snapshot that came back refreshes the existing record's
|
|
251
251
|
* timestamp rather than adding a second one, so the ledger cannot grow by one
|
|
252
252
|
* entry per reappearance.
|
|
@@ -265,7 +265,7 @@ export function recordRecoveryRemoval(surface: SessionSurface, sessionId: string
|
|
|
265
265
|
// Appending onto an unreadable ledger would rewrite it as a one-record
|
|
266
266
|
// array, silently discarding whatever the user decided before. Say so
|
|
267
267
|
// rather than quietly resetting their history.
|
|
268
|
-
logger.warn('recovery decisions: ledger unreadable
|
|
268
|
+
logger.warn('recovery decisions: ledger unreadable; recording this removal replaces it', {
|
|
269
269
|
ledger: path,
|
|
270
270
|
sessionId,
|
|
271
271
|
});
|
|
@@ -290,6 +290,6 @@ export function recordRecoveryRemoval(surface: SessionSurface, sessionId: string
|
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
292
|
} catch {
|
|
293
|
-
// Best-effort by construction
|
|
293
|
+
// Best-effort by construction, see the doc comment above.
|
|
294
294
|
}
|
|
295
295
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* recovery-offer-wiring.ts
|
|
2
|
+
* recovery-offer-wiring.ts, binds the startup recovery offer
|
|
3
3
|
* (runtime/recovery-prompt.ts) to this shell's live objects.
|
|
4
4
|
*
|
|
5
5
|
* Kept out of main.ts so the entrypoint stays under the architecture
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* actually happened. The transcript journal rebinds on that field
|
|
13
13
|
* (turn-event-wiring.ts), and every later turn snapshot keys off it.
|
|
14
14
|
* Without this the restored conversation would be written back under the
|
|
15
|
-
* fresh boot session id
|
|
15
|
+
* fresh boot session id, the recovery point would be gone and its content
|
|
16
16
|
* would live somewhere the user never asked for. It moves AFTER the apply
|
|
17
17
|
* rather than before, so a refusal (nothing on disk, a damaged file) leaves
|
|
18
18
|
* this boot on its own session instead of adopting one it never loaded.
|
|
@@ -67,7 +67,7 @@ export function buildRecoveryOfferWiring(input: RecoveryOfferWiringInput): Recov
|
|
|
67
67
|
saveSource: 'user',
|
|
68
68
|
});
|
|
69
69
|
};
|
|
70
|
-
// The user picked "Resume it" in the offer modal
|
|
70
|
+
// The user picked "Resume it" in the offer modal, that answer, and
|
|
71
71
|
// nothing else, is what unlocks the restore.
|
|
72
72
|
const result = applyRecoverySnapshot({
|
|
73
73
|
sessionId,
|
|
@@ -91,7 +91,7 @@ export function buildRecoveryOfferWiring(input: RecoveryOfferWiringInput): Recov
|
|
|
91
91
|
// Unconditional, and the reason is the whole point of this feature: a
|
|
92
92
|
// crash-recovery snapshot is NOT a session-store entry. The snapshot
|
|
93
93
|
// file has just been retired by the read that loaded it, so until this
|
|
94
|
-
// write lands the recovered conversation exists only in memory
|
|
94
|
+
// write lands the recovered conversation exists only in memory, the
|
|
95
95
|
// pointer below would name a session nothing could load, and `/session
|
|
96
96
|
// resume <id>` would still not reach it. The gap-closure persist above
|
|
97
97
|
// only fires when the journal actually had records to replay, so it
|