@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,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* renderSessionPickerModal
|
|
2
|
+
* renderSessionPickerModal, renders the /sessions picker modal as Line[]
|
|
3
3
|
* using ModalFactory.
|
|
4
4
|
*
|
|
5
5
|
* Shows a list of saved sessions with:
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* When the modal was wired with a cross-surface session union
|
|
10
10
|
* (`modal.crossSurfaceView.mode !== 'local'`), an additional read-only
|
|
11
11
|
* "Cross-surface sessions" section is appended, badged kind/status/project
|
|
12
|
-
* (parity with the webui SessionsView) with one of three honest states
|
|
13
|
-
* true-empty, offline, or stale
|
|
12
|
+
* (parity with the webui SessionsView) with one of three honest states,
|
|
13
|
+
* true-empty, offline, or stale, never a silently-collapsed list. In
|
|
14
14
|
* 'local' mode (no sessionBroker wired, e.g. every pre-existing caller/test)
|
|
15
15
|
* this section is entirely absent and the box sizing is unchanged.
|
|
16
16
|
*/
|
|
@@ -28,7 +28,7 @@ import { getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay
|
|
|
28
28
|
// Cross-surface badge helpers (parity with webui's src/lib/sessions-union.ts)
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
|
|
31
|
-
/** Verbatim for unknown/future kinds
|
|
31
|
+
/** Verbatim for unknown/future kinds, never dropped, never guessed. */
|
|
32
32
|
function kindLabel(kind: string): string {
|
|
33
33
|
return kind.trim() || 'unknown';
|
|
34
34
|
}
|
|
@@ -44,10 +44,10 @@ function isClosedStatus(status: string): boolean {
|
|
|
44
44
|
/**
|
|
45
45
|
* Closed sessions carry an optional, honest reason for WHY they
|
|
46
46
|
* closed under `metadata.closeReason` (SDK's `SharedSessionCloseReason`,
|
|
47
|
-
* 'closeReason' key
|
|
47
|
+
* 'closeReason' key, see `@pellux/goodvibes-sdk` platform/control-plane
|
|
48
48
|
* session-broker-sessions.ts's `readSessionCloseReason`). `metadata` is an
|
|
49
49
|
* open record so old readers and records from a build that predates this
|
|
50
|
-
* field ignore it safely
|
|
50
|
+
* field ignore it safely, read it duck-typed here rather than importing the
|
|
51
51
|
* SDK's helper, and tolerate `metadata` itself being absent or malformed.
|
|
52
52
|
*/
|
|
53
53
|
function readCloseReason(record: SharedSessionRecord): string | undefined {
|
|
@@ -57,7 +57,7 @@ function readCloseReason(record: SharedSessionRecord): string | undefined {
|
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* A GC sweep closing an idle session ('idle-reaped') auto-reopens on the next
|
|
60
|
-
* heartbeat
|
|
60
|
+
* heartbeat, it is NOT the same event as a deliberate user/surface close, so
|
|
61
61
|
* it must never render under the same "closed" badge. Tolerant of
|
|
62
62
|
* records without the field (pre-feature builds, or a deliberate close).
|
|
63
63
|
*/
|
|
@@ -67,16 +67,16 @@ function isReapedRecord(record: SharedSessionRecord): boolean {
|
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* UX-lens note: 'reaped' names a mechanism (the idle-session sweep),
|
|
70
|
-
* not a state a first-time reader can guess
|
|
70
|
+
* not a state a first-time reader can guess, the webui pairs its own
|
|
71
71
|
* 'reaped' badge with a tooltip explaining it
|
|
72
|
-
* (SessionsView.tsx: "Closed by the idle-session sweep
|
|
72
|
+
* (SessionsView.tsx: "Closed by the idle-session sweep, reopens
|
|
73
73
|
* automatically on the next activity"). The TUI has no hover/tooltip
|
|
74
74
|
* surface, so the plain-language explanation is rendered as its own short
|
|
75
|
-
* line under the cross-surface list instead
|
|
75
|
+
* line under the cross-surface list instead, shown ONLY when at least one
|
|
76
76
|
* visible row actually carries the badge, so it never adds noise to a list
|
|
77
77
|
* with no reaped rows.
|
|
78
78
|
*/
|
|
79
|
-
const REAPED_BADGE_HINT = 'reaped = closed by the idle sweep
|
|
79
|
+
const REAPED_BADGE_HINT = 'reaped = closed by the idle sweep; reopens on next activity';
|
|
80
80
|
|
|
81
81
|
/** True when at least one of the currently-rendered (post-truncation) cross-surface rows carries the 'reaped' badge. */
|
|
82
82
|
function hasVisibleReapedRow(modal: SessionPickerModal): boolean {
|
|
@@ -114,8 +114,8 @@ function hostedRosterNote(roster: SessionPickerModal['hostedRoster']): string |
|
|
|
114
114
|
* One hosted row: the id first, then what it is doing, what leaving it would
|
|
115
115
|
* do, and finally its title.
|
|
116
116
|
*
|
|
117
|
-
* The id leads because it is the ACTIONABLE part
|
|
118
|
-
* attach <id>` can be typed off it
|
|
117
|
+
* The id leads because it is the ACTIONABLE part, the row exists so `/hosted
|
|
118
|
+
* attach <id>` can be typed off it, and a narrow terminal clips the tail. What
|
|
119
119
|
* gets clipped must therefore be the descriptive end of the line, never the
|
|
120
120
|
* thing the user has to retype.
|
|
121
121
|
*/
|
|
@@ -140,8 +140,8 @@ function hostedRowLabel(
|
|
|
140
140
|
*
|
|
141
141
|
* Absent when NOTHING is known: no roster was wired (every pre-existing caller
|
|
142
142
|
* and test), so the box size and content are unchanged for them. The moment the
|
|
143
|
-
* roster has an answer
|
|
144
|
-
* read
|
|
143
|
+
* roster has an answer, rows, an empty-but-read list, or a reason it could not
|
|
144
|
+
* read, the section appears, because each of those is a fact worth showing.
|
|
145
145
|
*/
|
|
146
146
|
function hostedSectionVisible(modal: SessionPickerModal): boolean {
|
|
147
147
|
const roster = modal.hostedRoster;
|
|
@@ -155,7 +155,7 @@ function hostedRowCount(modal: SessionPickerModal): number {
|
|
|
155
155
|
/**
|
|
156
156
|
* Extra content rows the hosted section needs: separator + header (+note)
|
|
157
157
|
* + rows (+overflow line + the attach hint). Counted for the same reason the
|
|
158
|
-
* cross-surface section counts its own
|
|
158
|
+
* cross-surface section counts its own, an uncounted trailing row is silently
|
|
159
159
|
* eaten by modal-factory's tail clip.
|
|
160
160
|
*/
|
|
161
161
|
function hostedExtraRows(modal: SessionPickerModal): number {
|
|
@@ -170,12 +170,12 @@ function hostedExtraRows(modal: SessionPickerModal): number {
|
|
|
170
170
|
/**
|
|
171
171
|
* The exact honest note for the current state, or null when the union view
|
|
172
172
|
* needs no caveat (fresh, with rows). Precedence: offline > stale >
|
|
173
|
-
* true-empty
|
|
173
|
+
* true-empty, the three designed states, never collapsed into
|
|
174
174
|
* each other (an offline view never silently renders as "no sessions yet").
|
|
175
175
|
*/
|
|
176
176
|
function crossSurfaceNote(view: SessionPickerModal['crossSurfaceView'], rowCount: number): string | null {
|
|
177
177
|
if (view.mode === 'local') return null;
|
|
178
|
-
if (view.offlineNote) return `${view.offlineNote}
|
|
178
|
+
if (view.offlineNote) return `${view.offlineNote}: showing local sessions only`;
|
|
179
179
|
if (view.stale) {
|
|
180
180
|
if (view.lastSyncAt === null) return 'Union view may be stale.';
|
|
181
181
|
const ageSeconds = Math.max(0, Math.round((Date.now() - view.lastSyncAt) / 1000));
|
|
@@ -192,7 +192,7 @@ function crossSurfaceRowCount(modal: SessionPickerModal): number {
|
|
|
192
192
|
/**
|
|
193
193
|
* Extra content rows the cross-surface section needs: separator + header
|
|
194
194
|
* (+note) + rows (+the "[showing N of M]" overflow line when the union has
|
|
195
|
-
* more records than MAX_CROSS_SURFACE_ROWS
|
|
195
|
+
* more records than MAX_CROSS_SURFACE_ROWS, previously uncounted here, which
|
|
196
196
|
* let modal-factory's tail-clip silently eat that trailing line even though
|
|
197
197
|
* the rest of the budget accounting held; see W3 Finding 1).
|
|
198
198
|
*/
|
|
@@ -201,7 +201,7 @@ function crossSurfaceExtraRows(modal: SessionPickerModal): number {
|
|
|
201
201
|
const rows = crossSurfaceRowCount(modal);
|
|
202
202
|
const noteRow = crossSurfaceNote(modal.crossSurfaceView, rows) ? 1 : 0;
|
|
203
203
|
const overflowRow = modal.crossSurfaceSessions.length > MAX_CROSS_SURFACE_ROWS ? 1 : 0;
|
|
204
|
-
// W4 UX-lens: the plain-language 'reaped' explainer is its own row
|
|
204
|
+
// W4 UX-lens: the plain-language 'reaped' explainer is its own row,
|
|
205
205
|
// uncounted here it would be silently eaten by modal-factory's tail-clip
|
|
206
206
|
// exactly the way W3 Finding 1 describes above.
|
|
207
207
|
const reapedHintRow = hasVisibleReapedRow(modal) ? 1 : 0;
|
|
@@ -236,7 +236,7 @@ export function renderSessionPickerModal(
|
|
|
236
236
|
|
|
237
237
|
// W3 Finding 1: when a cross-surface union section is present, cap the
|
|
238
238
|
// LOCAL list's window to what's left after reserving extraRows for the
|
|
239
|
-
// union
|
|
239
|
+
// union, otherwise the local list expands to fill metrics.contentRows
|
|
240
240
|
// (which already includes the union's reservation) and modal-factory's
|
|
241
241
|
// single tail-clip (createModal, targetContentRows) silently drops the
|
|
242
242
|
// union section, or its trailing rows, once the box fills up. In local
|
|
@@ -312,7 +312,7 @@ export function renderSessionPickerModal(
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
// Cross-surface session union
|
|
315
|
+
// Cross-surface session union, visible only when a sessionBroker
|
|
316
316
|
// was wired (mode !== 'local'); absent entirely otherwise, so the box size
|
|
317
317
|
// and content of every pre-existing (local-only) caller is unaffected.
|
|
318
318
|
if (modal.crossSurfaceView.mode !== 'local') {
|
|
@@ -353,7 +353,7 @@ export function renderSessionPickerModal(
|
|
|
353
353
|
});
|
|
354
354
|
}
|
|
355
355
|
// W4 UX-lens: only ever rendered when a visible row actually carries
|
|
356
|
-
// the 'reaped' badge
|
|
356
|
+
// the 'reaped' badge, never speculative noise.
|
|
357
357
|
if (hasVisibleReapedRow(modal)) {
|
|
358
358
|
sections.push({
|
|
359
359
|
type: 'text',
|
|
@@ -364,7 +364,7 @@ export function renderSessionPickerModal(
|
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
// Daemon-hosted sessions
|
|
367
|
+
// Daemon-hosted sessions, conversations whose loop runs in the daemon rather
|
|
368
368
|
// than in this terminal. Read-only here: joining one is `/hosted attach <id>`,
|
|
369
369
|
// which opens a live stream this modal has no business owning, so the full id
|
|
370
370
|
// is rendered for the command to be typed straight off the row.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* settings-modal-connections.ts
|
|
2
|
+
* settings-modal-connections.ts, rendering for the Connections category of the
|
|
3
3
|
* settings workspace.
|
|
4
4
|
*
|
|
5
5
|
* Split out of settings-modal.ts for the 800-line file cap, the same reason
|
|
@@ -18,7 +18,7 @@ import { clamp, padDisplay, stableWindow } from './fullscreen-workspace.ts';
|
|
|
18
18
|
*
|
|
19
19
|
* `checking` is rendered as its own state rather than blanked, because a row
|
|
20
20
|
* that showed nothing while the probe was in flight would read as "nothing
|
|
21
|
-
* configured"
|
|
21
|
+
* configured", the one thing this category exists to stop claiming falsely.
|
|
22
22
|
*/
|
|
23
23
|
export function renderConnectionRows(modal: SettingsModal, width: number, height: number): string[] {
|
|
24
24
|
const rows: string[] = [];
|
|
@@ -44,7 +44,7 @@ export function renderConnectionRows(modal: SettingsModal, width: number, height
|
|
|
44
44
|
/**
|
|
45
45
|
* The detail pane for one connection: the state, what it means, and every next
|
|
46
46
|
* step verbatim. The next steps are the daemon's requirements, so they are
|
|
47
|
-
* listed in full rather than summarized
|
|
47
|
+
* listed in full rather than summarized, a truncated instruction is a wrong
|
|
48
48
|
* instruction.
|
|
49
49
|
*/
|
|
50
50
|
export function buildConnectionContext(modal: SettingsModal): string[] {
|
|
@@ -10,7 +10,7 @@ import { isSecretConfigKey, isSecretReferenceValue } from '../config/secret-conf
|
|
|
10
10
|
import { UI_TONES } from './ui-primitives.ts';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* "Modified / active" accent for settings rows
|
|
13
|
+
* "Modified / active" accent for settings rows, cyan-green, no UI_TONES
|
|
14
14
|
* role matches it (preserved byte-exact as a named constant).
|
|
15
15
|
*/
|
|
16
16
|
const SETTINGS_ACCENT = '#00ffcc';
|
|
@@ -32,7 +32,7 @@ export function formatValue(entry: SettingEntry): string {
|
|
|
32
32
|
// comma-separated list, matching how they're edited (see worktree-setup-config.ts).
|
|
33
33
|
if (Array.isArray(val)) return val.length > 0 ? val.join(', ') : '(empty)';
|
|
34
34
|
// Object-backed settings (e.g. pricing.modelPrices) display their JSON when
|
|
35
|
-
// short, or an honest entry count
|
|
35
|
+
// short, or an honest entry count, never the useless "[object Object]".
|
|
36
36
|
if (typeof val === 'object') {
|
|
37
37
|
const keys = Object.keys(val as Record<string, unknown>);
|
|
38
38
|
if (keys.length === 0) return '(none set)';
|
|
@@ -77,7 +77,7 @@ export const CATEGORY_INFO: Record<SettingsCategory, string> = {
|
|
|
77
77
|
wrfc: 'Work-review-fix-cycle thresholds, retry limits, and automatic commit behavior.',
|
|
78
78
|
helper: 'Helper model defaults used by helper subsystems when they do not use the main chat route.',
|
|
79
79
|
tts: 'Text-to-speech provider, voice, and optional spoken-turn LLM overrides.',
|
|
80
|
-
voice: 'Two independent voice capabilities. Local engines (voice.local.*): the STT and TTS engine, binary, and model-path for whisper.cpp/faster-whisper and Piper/Kokoro
|
|
80
|
+
voice: 'Two independent voice capabilities. Local engines (voice.local.*): the STT and TTS engine, binary, and model-path for whisper.cpp/faster-whisper and Piper/Kokoro; all empty by default, configurable-not-configured, nothing auto-downloads, and an unconfigured machine reports honestly (never an error); once set, "Local engines" appears in the TTS provider picker beside elevenlabs, with no billing dimension. Wake-word detection (voice.wake.*, rendered as its own unit below): listening continuously for a spoken wake phrase and handing the utterance that follows to speech-to-text; off by default, because holding a microphone open is meant to be an explicit act. It is live on this terminal: enabling it starts a recorder subprocess (voice.wake.captureCommand), scores every frame with the pinned classifier through a WASM runtime, plays voice.wake.activationSound at a confirmed wake, shows a persistent footer row per voice.wake.indicator, and sends what follows to the same speech-to-text call: into the composer, or submitted outright when voice.wake.autoSubmit is on. The same capture path backs push-to-talk voice input (Alt+V by default), which always lands in the composer. voice.wake.noiseSuppression: speex runs here, the filter travels with the platform, so there is nothing to install for it, and voice.wake.vadThreshold above 0 screens frames with the pinned speech gate once provisioning has fetched it, refusing to start before that rather than scoring frames it claims to be screening. The model is already on this machine: installing goodvibes downloads and checksum-verifies it, and a running daemon retries at boot if the install could not reach the network, so switching this on normally needs no setup step. Switching it on never downloads anything itself; a host whose artifacts are missing or fail verification says which and names /voice wake setup rather than pretending to listen. Its published recall figures are measured on synthesised speech only, so no human recording of the phrase is behind them.',
|
|
81
81
|
service: 'Background service posture: enabled state, autostart, restart behavior, service name, platform, and logs.',
|
|
82
82
|
daemon: 'Local session daemon. It hosts the shared session broker and companion chat so a session started here is visible and steerable from other surfaces. On by default, bound to loopback (127.0.0.1) only.',
|
|
83
83
|
controlPlane: 'Daemon control-plane settings for local admin/API access.',
|
|
@@ -85,10 +85,10 @@ export const CATEGORY_INFO: Record<SettingsCategory, string> = {
|
|
|
85
85
|
web: 'Browser surface settings for the local or network web UI.',
|
|
86
86
|
batch: 'Batch execution settings, including local vs Cloudflare queue behavior.',
|
|
87
87
|
automation: 'Scheduled and automated run settings, concurrency, timeout, catch-up, cooldown, and retention behavior.',
|
|
88
|
-
checkin: 'Proactive check-in: on a cadence, a briefing is judged and you are contacted only when something warrants it. Off by default; every run
|
|
89
|
-
occasions: 'Proactive occasions and plans: dated, usually-recurring things that need an action (birthdays, anniversaries) and dated ranges that need only to be known about (a vacation). On by default. Lead time before a date, active hours nudges are allowed to arrive in, the nudge channel (empty makes it pull-only through /occasions), cadence while a window is open and the daily final stretch before it closes, whether an away plan moves a nudge earlier, whether a confirmed occasion mirrors to the calendar and whether a mirrored one suppresses its own nudge, how many questions the gift interview asks, how many years of gift history it keeps, and how often the sweep pass runs. The nudge itself never names the date
|
|
90
|
-
hostedSessions: 'Sessions whose conversation loop runs INSIDE the daemon instead of this terminal, so the work does not end when the window that started it closes. What happens when the last client leaves is the detach policy: the shipped default is `kill
|
|
91
|
-
watchers: 'File/process watcher heartbeat, polling, and recovery-window behavior, plus the trigger family (watchers.triggers.*, rendered as its own unit): stream watchers that regex-filter and batch a long-lived command\'s output, model-free condition checks running a probe/extract/rule pipeline with no LLM in the loop, and one-shot on-exit triggers that fire exactly one payload when a launched command terminates. The trigger family is off by default
|
|
88
|
+
checkin: 'Proactive check-in: on a cadence, a briefing is judged and you are contacted only when something warrants it. Off by default; every run, scheduled or manual, leaves a receipt (see /checkin) recording whether it stayed quiet, delivered a message, or was skipped.',
|
|
89
|
+
occasions: 'Proactive occasions and plans: dated, usually-recurring things that need an action (birthdays, anniversaries) and dated ranges that need only to be known about (a vacation). On by default. Lead time before a date, active hours nudges are allowed to arrive in, the nudge channel (empty makes it pull-only through /occasions), cadence while a window is open and the daily final stretch before it closes, whether an away plan moves a nudge earlier, whether a confirmed occasion mirrors to the calendar and whether a mirrored one suppresses its own nudge, how many questions the gift interview asks, how many years of gift history it keeps, and how often the sweep pass runs. The nudge itself never names the date; only how soon.',
|
|
90
|
+
hostedSessions: 'Sessions whose conversation loop runs INSIDE the daemon instead of this terminal, so the work does not end when the window that started it closes. What happens when the last client leaves is the detach policy: the shipped default is `kill`, exactly what closing a client has always done, and `survive` is the opt-in that leaves the session idle and reattachable. A session created with /hosted new --survive or --kill overrides the setting for itself. The remaining rows bound the capability: how many hosted sessions may run at once, how many messages one keeps, and how long a terminated session\'s record (with the reason it ended) is retained before it is retired.',
|
|
91
|
+
watchers: 'File/process watcher heartbeat, polling, and recovery-window behavior, plus the trigger family (watchers.triggers.*, rendered as its own unit): stream watchers that regex-filter and batch a long-lived command\'s output, model-free condition checks running a probe/extract/rule pipeline with no LLM in the loop, and one-shot on-exit triggers that fire exactly one payload when a launched command terminates. The trigger family is off by default, a trigger supervises real processes with nobody watching, and its rows tune the backoff ladder, the strike breaker, retention bounds, batching, and process caps.',
|
|
92
92
|
runtime: 'Runtime guardrails such as companion chat limiter and event bus listener caps.',
|
|
93
93
|
telemetry: 'Telemetry payload policy.',
|
|
94
94
|
cache: 'Provider and model cache behavior, TTL, and hit-rate monitoring.',
|
|
@@ -96,12 +96,12 @@ export const CATEGORY_INFO: Record<SettingsCategory, string> = {
|
|
|
96
96
|
mcp: 'MCP server trust and scope review. Trust changes can expose local files, tools, databases, browsers, or remote automation depending on the server.',
|
|
97
97
|
sandbox: 'Isolation strategy for REPL, MCP, Windows, and QEMU-backed execution. Use these settings to separate risky tools from the host shell.',
|
|
98
98
|
surfaces: 'External app surfaces such as Slack, Discord, ntfy, Home Assistant, Telegram, webhooks, chat bridges, and messaging providers.',
|
|
99
|
-
device: 'Posture for using a PAIRED phone as a tool
|
|
100
|
-
conversationGate: 'What a message arriving from one of those channels does. By default it gets a conversational answer, and work is proposed and waits for your agreement rather than starting on its own; you can instead confirm every run, or restore the old behavior where a message starts work immediately. Also how long a pending proposal stays answerable and how many can wait at once. This governs messages from channels only
|
|
99
|
+
device: 'Posture for using a PAIRED phone as a tool: its cameras, its screen, its location, its clipboard, and a small set of device effects. Every row here changes behaviour, and every change applies to the next request without a restart. device.capabilities.mode is the master switch: off stops every capability request reaching any paired device (nothing is dispatched and nobody is asked), ask-every-time asks on every single request and never consults or offers a durable grant, and honor-grants (stock) asks the first time and every time after unless you chose "always allow" for that one capability on that one phone. allowAlwaysOffer decides which capabilities may offer that durable grant at all: every-capability (stock), standard-only (the elevated ones: front camera, screen capture, precise location, clipboard read; keep asking and an "always" answer is honoured for that one request only), or never. requestTimeoutSeconds is the deadline on three things at once: how long the phone has to answer, the deadline written into the request the phone receives, and how long the confirmation prompt itself stays open. device.location.precision gates the street-level fix (coarse-only refuses it and still serves approximate; ask-precise serves it but never remembers it), and device.clipboard.readMode gates reading the clipboard the same way; putting text ON the clipboard is a different capability and is unaffected. Captures from the camera and the screen are kept for capture.retentionHours and capped at capture.maxArtifacts, whichever bites first; housekeeping runs at every start and then every capture.sweepIntervalMinutes, deleting expired grants and captures and recording what it removed and why. A grant lasts grants.expiryDays and is never honoured past it, one phone holds at most grants.maxPerNode grants (the oldest are reaped past the cap), and the ledger of grants given, used, revoked and expired is kept for grants.auditRetentionDays. device.nodes.maxPaired is enforced at the moment a device pairs: at the cap a NEW device is refused with a message naming the setting, the cap and how many are already paired, while a phone that is already in the list simply supersedes its own record and keeps working. Lowering the cap below the number already paired unpairs no one; existing devices keep working and only the next new pairing is refused; a device moving off the legacy shared token to its own is exempt so it cannot be stranded. A nonsensical value anywhere here (zero, negative, not a number, an enum value that is not on the list) is read as the stock value rather than passed through, so a broken setting can neither lock you out of your own daemon nor widen the posture.',
|
|
100
|
+
conversationGate: 'What a message arriving from one of those channels does. By default it gets a conversational answer, and work is proposed and waits for your agreement rather than starting on its own; you can instead confirm every run, or restore the old behavior where a message starts work immediately. Also how long a pending proposal stays answerable and how many can wait at once. This governs messages from channels only; what you type here always runs, and schedules and triggers were authorized when created.',
|
|
101
101
|
cloudflare: 'Optional Cloudflare control plane, batch queue, Worker, Tunnel, Access, DNS, KV, Durable Objects, Secrets Store, and R2 settings.',
|
|
102
102
|
release: 'Release-channel preference.',
|
|
103
|
-
power: 'Host sleep ownership. Keep-awake holds the host awake (the always-visible "sleep disabled" chip is the safety mechanism
|
|
104
|
-
memory: 'Memory provenance surfacing. Whether turns that drew on your standing memories show a small "used N memories" chip with a drill-in listing them. Off by default
|
|
103
|
+
power: 'Host sleep ownership. Keep-awake holds the host awake (the always-visible "sleep disabled" chip is the safety mechanism; no timer, no AC-only option); automatic inhibition keeps the host awake while real work runs, with an honest hard cap on that work hold.',
|
|
104
|
+
memory: 'Memory provenance surfacing. Whether turns that drew on your standing memories show a small "used N memories" chip with a drill-in listing them. Off by default: when off, nothing is rendered and no context is added.',
|
|
105
105
|
danger: 'High-impact switches for daemon and HTTP listener behavior. These are operational overrides, not normal preferences.',
|
|
106
106
|
tools: 'Tool LLM and helper model routing. Empty provider/model values inherit the active chat route unless a specific helper/tool route is set.',
|
|
107
107
|
network: 'Combined network view for daemon control-plane, HTTP listener, browser web surface, and general outbound network settings.',
|
|
@@ -112,13 +112,13 @@ export const CATEGORY_INFO: Record<SettingsCategory, string> = {
|
|
|
112
112
|
connections: 'Whether mail and calendar are actually usable right now. Each row is answered by asking the daemon that owns the connection, not by reading config here, so a row says ready only when a real request succeeded.',
|
|
113
113
|
policy: 'Policy-as-code bundle loading: where the startup policy bundle is loaded from and its file path. Gated by the Policy-as-Code feature.',
|
|
114
114
|
notifications: 'Notification router burst-suppression tuning: burst window, threshold, and cooldown. Gated by the Adaptive Notification Suppression feature.',
|
|
115
|
-
relay: 'Outbound zero-knowledge relay reachability, for reaching this daemon from outside the LAN without opening an inbound port. Off by default
|
|
116
|
-
learning: 'Idle-time memory consolidation: merging duplicate standing memory records and decaying/archiving stale ones. Off by default
|
|
117
|
-
payments: 'Payment card on file for daemon-initiated purchases: card number, expiry, CVV, cardholder name, billing and shipping address, and whether the CVV is stored or requested at purchase time. Card fields are entered through /payments card (concealed input) and are secret-tier
|
|
118
|
-
email: 'The daemon\'s own mailbox connection
|
|
115
|
+
relay: 'Outbound zero-knowledge relay reachability, for reaching this daemon from outside the LAN without opening an inbound port. Off by default: relay.enabled is the switch. The relay operator sees only ciphertext and connection metadata; self-host your own relay for full control.',
|
|
116
|
+
learning: 'Idle-time memory consolidation: merging duplicate standing memory records and decaying/archiving stale ones. Off by default; nothing runs until enabled.',
|
|
117
|
+
payments: 'Payment card on file for daemon-initiated purchases: card number, expiry, CVV, cardholder name, billing and shipping address, and whether the CVV is stored or requested at purchase time. Card fields are entered through /payments card (concealed input) and are secret-tier; never rendered in plaintext.',
|
|
118
|
+
email: 'The daemon\'s own mailbox connection; IMAP and SMTP hosts, ports and security, the mailbox and drafts folders, and the username. Password fields hold secret-store references, never values; /google connects a Gmail-backed setup in one action.',
|
|
119
119
|
calendar: 'Calendar connections the daemon reads and writes: the Google or Microsoft OAuth client for CalDAV-free access, or a read-only ICS URL. Secret fields hold secret-store references, never values.',
|
|
120
120
|
google: 'The Google OAuth connection record: project id, publishing status, and the refresh-token reference the mail and calendar services share. Established by /google in one action; a dead grant is diagnosed in plain words there.',
|
|
121
|
-
profile: 'What the platform knows about you
|
|
121
|
+
profile: 'What the platform knows about you: your name, how to reach you, where you are, how you like answers written, the people and places you mention, kept as one Markdown file at daemon scope that you can open and edit by hand at any time; your edits win and are never rewritten. These rows decide whether it is loaded at all, whether the assistant records facts it learns from things you say directly to it, whether it tells you in one line each time it does, whether the harmless part (how you are addressed, your city, your timezone, your formatting preferences, your style) is put in the model\'s context so it stops guessing, whether using a private detail such as your address is announced in the reply, whether settings left unset read their value from the profile, and where the file lives. Everything private: your name, contact details, home address, billing and shipping details, quiet hours, and the People, Places, Work and Notes sections, is never put in context in bulk and is reachable only by a named lookup that is disclosed when it happens. Run /profile to read the whole thing, /profile where <field> to see the surface, date and your exact words behind any recorded line, and /profile forget <field> to delete one.',
|
|
122
122
|
};
|
|
123
123
|
|
|
124
124
|
export const CATEGORY_LABELS: Record<(typeof SETTINGS_CATEGORIES)[number], string> = {
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
|
|
31
31
|
const ENUM_VALUE_DESCRIPTIONS: Record<string, Record<string, string>> = {
|
|
32
32
|
'display.themeMode': {
|
|
33
|
-
auto: 'Probe the terminal background colour (OSC 11) once at startup and pick light or dark. Falls back to dark on unreadable/unsupported terminals. Only evaluated at startup
|
|
33
|
+
auto: 'Probe the terminal background colour (OSC 11) once at startup and pick light or dark. Falls back to dark on unreadable/unsupported terminals. Only evaluated at startup: selecting auto takes effect next launch.',
|
|
34
34
|
dark: 'Force the dark theme regardless of terminal background. Applies immediately.',
|
|
35
35
|
light: 'Force the light theme regardless of terminal background. Applies immediately.',
|
|
36
36
|
},
|
|
@@ -46,7 +46,7 @@ const ENUM_VALUE_DESCRIPTIONS: Record<string, Record<string, string>> = {
|
|
|
46
46
|
},
|
|
47
47
|
'permissions.mode': {
|
|
48
48
|
prompt: 'Normal: ask before powerful or risky actions according to tool policy.',
|
|
49
|
-
plan: 'Plan mode: read-only planning posture
|
|
49
|
+
plan: 'Plan mode: read-only planning posture; writes, commands, and network calls are blocked so the model can plan without changing anything. Toggle with /plan or Shift+Tab.',
|
|
50
50
|
'accept-edits': 'Accept edits: file writes and edits are auto-approved, but exec, network, and escalations are still gated.',
|
|
51
51
|
'allow-all': 'Auto: allow all actions without prompting. Fast, but removes an important safety gate.',
|
|
52
52
|
custom: 'Use per-tool-class permission settings from the rows below.',
|
|
@@ -128,11 +128,11 @@ function formatDefaultValue(value: unknown): string {
|
|
|
128
128
|
|
|
129
129
|
function currentSettingValue(modal: SettingsModal, entry: SettingEntry, selected: boolean): string {
|
|
130
130
|
if (selected && modal.editingMode) {
|
|
131
|
-
// Secret-backed keys (surfaces.*.botToken, .signingSecret, etc
|
|
131
|
+
// Secret-backed keys (surfaces.*.botToken, .signingSecret, etc., see
|
|
132
132
|
// secret-config.ts) must never echo the in-progress plaintext buffer:
|
|
133
133
|
// not in the row, not in the "Current: ..." context line, not in search
|
|
134
134
|
// results. Reuse the composer's concealed-input mask (concealed-input.ts)
|
|
135
|
-
// rather than a second masking implementation
|
|
135
|
+
// rather than a second masking implementation, same bullet-per-character
|
|
136
136
|
// shape, so keystrokes still visibly register without revealing content.
|
|
137
137
|
const buffer = isSecretConfigKey(entry.setting.key) ? maskConcealedText(modal.editBuffer) : modal.editBuffer;
|
|
138
138
|
return `${buffer}${GLYPHS.surface.cursor}`;
|
|
@@ -254,7 +254,7 @@ function buildFlagContext(entry: SettingEntry | null): string[] {
|
|
|
254
254
|
`State: ${displayState}`,
|
|
255
255
|
// A capability the registry declares not operable reads as disabled no
|
|
256
256
|
// matter what its settings key says. Without this line the row shows a
|
|
257
|
-
// value of true beside a state of disabled and explains nothing
|
|
257
|
+
// value of true beside a state of disabled and explains nothing, the
|
|
258
258
|
// written reason exists in the registry, so render it where the user meets
|
|
259
259
|
// the contradiction rather than leaving it to be discovered.
|
|
260
260
|
...(feature.operable === false && feature.inoperableDetail
|
|
@@ -680,10 +680,10 @@ function footerText(modal: SettingsModal, width: number): string {
|
|
|
680
680
|
{ key: 'Esc', verb: 'Close' },
|
|
681
681
|
]));
|
|
682
682
|
// Every topical category that hosts feature units flows through the default
|
|
683
|
-
// settings footer below
|
|
683
|
+
// settings footer below, Enter/Space toggles a feature-unit header exactly
|
|
684
684
|
// as it edits/toggles any setting row.
|
|
685
685
|
// Default settings pane: tier the reset affordances by available width.
|
|
686
|
-
// W<80: minimal
|
|
686
|
+
// W<80: minimal, only the most critical action survives.
|
|
687
687
|
// W<160: compact but still shows both reset affordances.
|
|
688
688
|
// W≥160: standard with all navigation tokens.
|
|
689
689
|
if (width < 80)
|
|
@@ -720,18 +720,18 @@ export function renderSettingsModal(
|
|
|
720
720
|
const metrics = getFullscreenWorkspaceMetrics({ width, height: viewportHeight });
|
|
721
721
|
const categoryRows = renderCategories(modal, metrics.leftWidth - 2, metrics.bodyRows);
|
|
722
722
|
// Documentation pane: long content SCROLLS (PgUp/PgDn) with honest
|
|
723
|
-
// more-above/below markers
|
|
723
|
+
// more-above/below markers, it is never silently clipped.
|
|
724
724
|
const allContextLines = buildContextLines(modal, metrics.contextWidth);
|
|
725
725
|
const visibleContext = metrics.contextRows;
|
|
726
726
|
const maxContextScroll = Math.max(0, allContextLines.length - visibleContext);
|
|
727
727
|
const contextOffset = Math.min(Math.max(0, modal.contextScroll ?? 0), maxContextScroll);
|
|
728
728
|
const windowedContext = allContextLines.slice(contextOffset, contextOffset + visibleContext);
|
|
729
729
|
if (contextOffset > 0) {
|
|
730
|
-
windowedContext[0] = `${GLYPHS.navigation.moreAbove} ${contextOffset} more line(s) above
|
|
730
|
+
windowedContext[0] = `${GLYPHS.navigation.moreAbove} ${contextOffset} more line(s) above; PgUp`;
|
|
731
731
|
}
|
|
732
732
|
if (contextOffset + visibleContext < allContextLines.length) {
|
|
733
733
|
const below = allContextLines.length - contextOffset - visibleContext;
|
|
734
|
-
windowedContext[windowedContext.length - 1] = `${GLYPHS.navigation.moreBelow} ${below} more line(s) below
|
|
734
|
+
windowedContext[windowedContext.length - 1] = `${GLYPHS.navigation.moreBelow} ${below} more line(s) below; PgDn`;
|
|
735
735
|
}
|
|
736
736
|
const contextRows = windowedContext.map((text, row): WorkspaceRow => {
|
|
737
737
|
const selectedSetting = modal.getSelected();
|
|
@@ -11,7 +11,7 @@ export interface ShellFooterBuildOptions {
|
|
|
11
11
|
readonly promptFocused?: boolean;
|
|
12
12
|
/**
|
|
13
13
|
* True when the panel workspace owns keyboard focus. Only consulted as a
|
|
14
|
-
* fallback when `promptFocused` is not supplied
|
|
14
|
+
* fallback when `promptFocused` is not supplied, see buildShellFooter's
|
|
15
15
|
* default expression. Callers that already compute `promptFocused`
|
|
16
16
|
* themselves (main.ts does) do not need to also pass this.
|
|
17
17
|
*/
|
|
@@ -64,7 +64,7 @@ export interface ShellFooterBuildOptions {
|
|
|
64
64
|
/**
|
|
65
65
|
* The one-key retry/switch-model affordance's transient hint text (see
|
|
66
66
|
* retry-affordance.ts). Rendered as its own line ABOVE the context
|
|
67
|
-
* pressure hint while armed; present here only means "show it now"
|
|
67
|
+
* pressure hint while armed; present here only means "show it now", the
|
|
68
68
|
* caller is responsible for passing null the instant it disarms, so this
|
|
69
69
|
* is a time-bounded status line, never a lingering claim.
|
|
70
70
|
*/
|
|
@@ -83,10 +83,10 @@ export interface ShellFooterBuildOptions {
|
|
|
83
83
|
readonly sessionSpineStatus?: 'online' | 'offline';
|
|
84
84
|
/** The web surface's reachable URL, when that surface is enabled; a persistent footer segment. */
|
|
85
85
|
readonly webSurfaceUrl?: string;
|
|
86
|
-
/** True while power.keepAwake holds
|
|
86
|
+
/** True while power.keepAwake holds, renders the always-visible "sleep disabled" chip. */
|
|
87
87
|
readonly powerKeepAwake?: boolean;
|
|
88
88
|
/**
|
|
89
|
-
* Live microphone state
|
|
89
|
+
* Live microphone state, a push-to-talk recording, or the wake detector for
|
|
90
90
|
* as long as it runs. Rendered as a persistent row beside the process
|
|
91
91
|
* indicator, because a capture device held open with nothing on screen saying
|
|
92
92
|
* so is the one state a voice feature must never be in. Null (or a wake state
|
|
@@ -131,7 +131,7 @@ const DIM_STATUS_FG = '#64748b';
|
|
|
131
131
|
let lastRenderedFooterHeight: { compact: boolean; height: number } | null = null;
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* The wrapped-prompt shape the composer reports each frame
|
|
134
|
+
* The wrapped-prompt shape the composer reports each frame, the subset
|
|
135
135
|
* promptCursorOffset needs (see InputHandler.getWrappedPromptInfo).
|
|
136
136
|
*/
|
|
137
137
|
export interface WrappedPromptCursorInfo {
|
|
@@ -226,17 +226,17 @@ export function buildShellFooter(
|
|
|
226
226
|
// indicator: an open microphone is a live condition the user is acting inside,
|
|
227
227
|
// while the process indicator is a background summary.
|
|
228
228
|
lines.splice(inputBoxRows, 0, ...renderVoiceCaptureIndicator(options.width, options.voiceCapture ?? null), ...processIndicator);
|
|
229
|
-
// Scriptable status line
|
|
229
|
+
// Scriptable status line, dim informational line above the prompt. Unshifted
|
|
230
230
|
// before the context hint so the context hint (if any) sits above it.
|
|
231
231
|
if (options.scriptableStatusLine) {
|
|
232
232
|
lines.unshift(UIFactory.stringToLine(options.scriptableStatusLine, options.width, { fg: DIM_STATUS_FG }));
|
|
233
233
|
}
|
|
234
|
-
// Passive context status hint
|
|
234
|
+
// Passive context status hint, rendered as a dim informational line before the prompt.
|
|
235
235
|
if (options.contextStatusHint) {
|
|
236
236
|
const hintLine = UIFactory.stringToLine(options.contextStatusHint, options.width, { fg: DIM_STATUS_FG });
|
|
237
237
|
lines.unshift(hintLine);
|
|
238
238
|
}
|
|
239
|
-
// Retry affordance
|
|
239
|
+
// Retry affordance, topmost of the passive hint lines while armed (an
|
|
240
240
|
// actionable, time-bounded prompt outranks the passive status hints
|
|
241
241
|
// below it); simply absent the instant it disarms.
|
|
242
242
|
if (options.retryHint) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// status-glyphs.ts
|
|
2
|
+
// status-glyphs.ts, canonical glyph map for status states.
|
|
3
3
|
//
|
|
4
4
|
// Extracted as a neutral module so both status-token.ts and polish.ts can
|
|
5
5
|
// import from here without creating a circular ESM dependency.
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
// drift from the registry again. Re-exported under the historical name so
|
|
12
12
|
// status-token.ts and polish.ts import unchanged.
|
|
13
13
|
//
|
|
14
|
-
// Glyphs (unchanged
|
|
14
|
+
// Glyphs (unchanged, this module's historical values, the TUI reference the
|
|
15
15
|
// contract was extracted from):
|
|
16
|
-
// good ✓ (CHECK MARK U+2713)
|
|
17
|
-
// warn ⚠ (WARNING SIGN U+26A0)
|
|
18
|
-
// bad ✕ (MULTIPLICATION X U+2715)
|
|
19
|
-
// info ○ (WHITE CIRCLE U+25CB)
|
|
16
|
+
// good ✓ (CHECK MARK U+2713) , GLYPHS.status.success
|
|
17
|
+
// warn ⚠ (WARNING SIGN U+26A0) , GLYPHS.status.warn
|
|
18
|
+
// bad ✕ (MULTIPLICATION X U+2715), GLYPHS.status.failure
|
|
19
|
+
// info ○ (WHITE CIRCLE U+25CB) , GLYPHS.status.info
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
export { STATE_GLYPHS, type StatusState } from '@pellux/goodvibes-sdk/platform/presentation';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SyntaxHighlighter
|
|
2
|
+
* SyntaxHighlighter, Tree-sitter-powered syntax highlighting for code blocks.
|
|
3
3
|
*
|
|
4
4
|
* Designed for the synchronous TUI render loop:
|
|
5
5
|
* - Initializes tree-sitter WASM and grammar parsers asynchronously in background
|
|
@@ -372,7 +372,6 @@ function isLeafLike(node: Node): boolean {
|
|
|
372
372
|
* Handles multi-line spans (e.g., block comments, template literals).
|
|
373
373
|
*/
|
|
374
374
|
function spansToLines(spans: Span[], codeLines: string[]): HighlightedLine[] {
|
|
375
|
-
// Initialize result: one entry per code line, each starting with no tokens
|
|
376
375
|
const result: HighlightedLine[] = codeLines.map(() => []);
|
|
377
376
|
|
|
378
377
|
// Track the current position to emit default-colored text for gaps
|
|
@@ -491,7 +490,7 @@ export class SyntaxHighlighter {
|
|
|
491
490
|
}
|
|
492
491
|
|
|
493
492
|
/**
|
|
494
|
-
* Schedule an async parse. Fires and forgets
|
|
493
|
+
* Schedule an async parse. Fires and forgets, result lands in cache.
|
|
495
494
|
* Callers will pick it up on the next render cycle.
|
|
496
495
|
*/
|
|
497
496
|
private scheduleParse(code: string, langId: string, key: string): void {
|
|
@@ -15,7 +15,7 @@ export function classifySystemMessage(content: string): SystemMessageType {
|
|
|
15
15
|
|
|
16
16
|
// [WRFC] messages
|
|
17
17
|
if (/^\[WRFC\]/.test(content)) {
|
|
18
|
-
// Failed gate result → error (red)
|
|
18
|
+
// Failed gate result → error (red), must be checked before generic FAILED
|
|
19
19
|
if (/Gate:.*FAILED/i.test(content)) return 'error';
|
|
20
20
|
// Hard failures and cascade aborts → error (red)
|
|
21
21
|
if (/FAILED|cascade abort/i.test(content)) return 'error';
|
|
@@ -40,13 +40,13 @@ export function classifySystemMessage(content: string): SystemMessageType {
|
|
|
40
40
|
// [Plan] messages are always informational
|
|
41
41
|
if (/^\[Plan\]/.test(content)) return 'info';
|
|
42
42
|
|
|
43
|
-
// [Model] messages
|
|
43
|
+
// [Model] messages, "Unknown model" is a warning, switch is info
|
|
44
44
|
if (/^\[Model\]/.test(content)) {
|
|
45
45
|
if (/Unknown model/i.test(content)) return 'warning';
|
|
46
46
|
return 'info';
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// [Routing] chip
|
|
49
|
+
// [Routing] chip, a mid-session model change is informational (the [Failover]
|
|
50
50
|
// line, not this, carries the alarming transport-error cases).
|
|
51
51
|
if (/^\[Routing\]/.test(content)) return 'info';
|
|
52
52
|
|
|
@@ -43,7 +43,7 @@ export interface TabStripConfig {
|
|
|
43
43
|
readonly overflowIndicatorLeft?: string;
|
|
44
44
|
/**
|
|
45
45
|
* Optional callback invoked with the absolute column ranges of each rendered
|
|
46
|
-
* tab. Used by the compositor to hit-test mouse clicks onto tabs. Additive
|
|
46
|
+
* tab. Used by the compositor to hit-test mouse clicks onto tabs. Additive,
|
|
47
47
|
* the function still returns a `Line` so existing callers are unaffected.
|
|
48
48
|
*/
|
|
49
49
|
readonly onLayout?: (regions: readonly TabHitRegion[]) => void;
|
|
@@ -91,7 +91,7 @@ function makeSegments(
|
|
|
91
91
|
let used = entryWidths[activeIndex] + closing;
|
|
92
92
|
|
|
93
93
|
if (totalAll <= maxWidth) {
|
|
94
|
-
// Everything fits
|
|
94
|
+
// Everything fits, show all tabs, no windowing.
|
|
95
95
|
start = 0;
|
|
96
96
|
end = n - 1;
|
|
97
97
|
} else {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* term-caps.ts
|
|
2
|
+
* term-caps.ts, Terminal capability detection and color downsampling.
|
|
3
3
|
*
|
|
4
4
|
* Probes the terminal's color support level once at renderer init and exposes
|
|
5
5
|
* a `downsampleColor` function that maps hex/RGB color strings to the
|
|
6
6
|
* appropriate SGR parameter string for the detected capability level.
|
|
7
7
|
*
|
|
8
8
|
* Capability levels (in ascending order):
|
|
9
|
-
* none
|
|
10
|
-
* basic16
|
|
11
|
-
* ansi256
|
|
12
|
-
* truecolor
|
|
9
|
+
* none , NO_COLOR set or TERM=dumb; emit no SGR color sequences.
|
|
10
|
+
* basic16 , 16 ANSI colors (\x1b[30-37m / \x1b[90-97m / \x1b[40-47m).
|
|
11
|
+
* ansi256 , 256-color palette (\x1b[38;5;Nm).
|
|
12
|
+
* truecolor, 24-bit RGB (\x1b[38;2;R;G;Bm).
|
|
13
13
|
*
|
|
14
14
|
* References:
|
|
15
15
|
* - NO_COLOR spec: https://no-color.org/ (any non-empty value disables color)
|
|
@@ -89,7 +89,7 @@ function parseHex(hex: string): [number, number, number] | null {
|
|
|
89
89
|
* Parse a sanitized color string in one of two forms:
|
|
90
90
|
* - "#rrggbb" → RGB tuple
|
|
91
91
|
* - "r;g;b" → RGB tuple (already decomposed by sanitizeColor)
|
|
92
|
-
* - "N" → null (already a palette index
|
|
92
|
+
* - "N" → null (already a palette index, pass through)
|
|
93
93
|
* Returns [r, g, b] or null (non-RGB / palette index).
|
|
94
94
|
*/
|
|
95
95
|
function parseRgbString(color: string): [number, number, number] | null {
|
|
@@ -114,7 +114,7 @@ function parseRgbString(color: string): [number, number, number] | null {
|
|
|
114
114
|
* Map [r, g, b] (0-255 each) to the nearest xterm-256 palette index.
|
|
115
115
|
*
|
|
116
116
|
* The 256-color palette is structured as:
|
|
117
|
-
* 0-15: System colors (16 named colors)
|
|
117
|
+
* 0-15: System colors (16 named colors), we avoid these for predictability
|
|
118
118
|
* and instead target the 6×6×6 cube + grayscale ramp.
|
|
119
119
|
* 16-231: 6×6×6 color cube, index = 16 + 36*r6 + 6*g6 + b6
|
|
120
120
|
* where r6/g6/b6 ∈ 0-5 map via [0,95,135,175,215,255]
|
|
@@ -240,11 +240,11 @@ function ansi16FgToBg(fgCode: number): number {
|
|
|
240
240
|
* sanitizeColor() decomposition. Supported forms:
|
|
241
241
|
* - "#rrggbb" hex
|
|
242
242
|
* - "r;g;b" pre-decomposed RGB (from sanitizeColor)
|
|
243
|
-
* - "N" already a palette index
|
|
243
|
+
* - "N" already a palette index, returned as-is for ansi256/truecolor,
|
|
244
244
|
* or omitted for none
|
|
245
245
|
*
|
|
246
246
|
* @param caps - The probed terminal capabilities.
|
|
247
|
-
* @param role - 'fg' or 'bg'
|
|
247
|
+
* @param role - 'fg' or 'bg', determines which SGR range to use for basic16.
|
|
248
248
|
*
|
|
249
249
|
* @returns The SGR parameter string suitable for embedding in \x1b[38;2;...m
|
|
250
250
|
* (truecolor), \x1b[38;5;Nm (ansi256), \x1b[Nm (basic16 fg), etc.
|
|
@@ -257,7 +257,7 @@ function ansi16FgToBg(fgCode: number): number {
|
|
|
257
257
|
* style += isRgb ? `\x1b[38;2;${fg}m` : `\x1b[38;5;${fg}m`;
|
|
258
258
|
* }
|
|
259
259
|
*
|
|
260
|
-
* For basic16 the caller must use a different SGR prefix
|
|
260
|
+
* For basic16 the caller must use a different SGR prefix, see applyStyles.
|
|
261
261
|
*/
|
|
262
262
|
export function downsampleColor(
|
|
263
263
|
rawColor: string,
|
|
@@ -272,13 +272,13 @@ export function downsampleColor(
|
|
|
272
272
|
if (caps.capability === 'truecolor') {
|
|
273
273
|
// Pass hex through as r;g;b decomposed, pass r;g;b through as-is
|
|
274
274
|
if (rgb) return `${rgb[0]};${rgb[1]};${rgb[2]}`;
|
|
275
|
-
// Already a palette index
|
|
275
|
+
// Already a palette index, emit as 256-color
|
|
276
276
|
return rawColor; // caller will use 38;5;N or 48;5;N
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
if (caps.capability === 'ansi256') {
|
|
280
280
|
if (rgb) return String(nearestAnsi256(rgb[0], rgb[1], rgb[2]));
|
|
281
|
-
// Already a palette index
|
|
281
|
+
// Already a palette index, pass through
|
|
282
282
|
return rawColor;
|
|
283
283
|
}
|
|
284
284
|
|