@pellux/goodvibes-tui 1.27.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -2
- package/README.md +7 -9
- package/bin/goodvibes +2 -2
- package/bin/launcher-support.js +8 -4
- package/docs/README.md +11 -2
- package/docs/foundation-artifacts/operator-contract.json +6403 -2347
- package/package.json +14 -25
- package/scripts/postinstall.js +35 -35
- package/src/audio/spoken-turn-model-routing.ts +1 -1
- package/src/cli/bundle-command.ts +5 -5
- package/src/cli/completions/generate.ts +1 -1
- package/src/cli/doctor.ts +3 -3
- package/src/cli/entrypoint.ts +12 -7
- package/src/cli/help.ts +4 -5
- package/src/cli/hooks-command.ts +1 -1
- package/src/cli/index.ts +1 -4
- package/src/cli/launch-auto-update.ts +59 -7
- package/src/cli/management-commands.ts +6 -6
- package/src/cli/management-utils.ts +5 -5
- package/src/cli/management.ts +7 -7
- package/src/cli/network-posture.ts +1 -1
- package/src/cli/package-verification.ts +7 -6
- package/src/cli/plugin-bundles-command.ts +1 -1
- package/src/cli/plugin-command.ts +2 -2
- package/src/cli/service-command.ts +2 -2
- package/src/cli/service-posture.ts +4 -4
- package/src/cli/status.ts +9 -8
- package/src/cli/surface-command.ts +4 -4
- package/src/cli/tui-startup.ts +3 -3
- package/src/cli-flags.ts +3 -3
- package/src/config/goodvibes-home-audit.ts +0 -2
- package/src/config/secret-config.ts +37 -5
- package/src/config/tui-extension-settings.ts +1 -1
- package/src/core/approval-alert.ts +2 -2
- package/src/core/budget-breach-notifier.ts +3 -3
- package/src/core/conversation-compaction-render.ts +1 -1
- package/src/core/conversation-line-cache.ts +1 -1
- package/src/core/conversation-render-context.ts +1 -1
- package/src/core/conversation-rendering.ts +2 -2
- package/src/core/conversation.ts +10 -10
- package/src/core/long-task-notifier.ts +2 -2
- package/src/core/memory-diagnostics-gateway.ts +1 -1
- package/src/core/scriptable-statusline.ts +1 -1
- package/src/core/session-auto-titler.ts +1 -1
- package/src/core/session-resume-core.ts +2 -2
- package/src/core/stream-event-wiring.ts +24 -44
- package/src/core/terminal-notifier.ts +3 -3
- package/src/core/turn-cancellation.ts +1 -1
- package/src/core/turn-event-wiring.ts +5 -5
- package/src/input/command-registry.ts +6 -6
- package/src/input/commands/channel-pairing.ts +1 -1
- package/src/input/commands/cluster-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/devices-runtime.ts +1 -1
- package/src/input/commands/hosted-runtime.ts +330 -0
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup-transfer.ts +2 -2
- package/src/input/commands/local-setup.ts +3 -3
- package/src/input/commands/managed-runtime.ts +2 -2
- package/src/input/commands/operator-rpc.ts +24 -110
- package/src/input/commands/payment-card-intake.ts +1 -1
- package/src/input/commands/permissions-provenance.ts +2 -2
- package/src/input/commands/plugin-runtime.ts +1 -1
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/relay-runtime.ts +18 -9
- package/src/input/commands/rewind-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +1 -1
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +145 -55
- package/src/input/commands/update-runtime.ts +31 -28
- package/src/input/commands/work-plan-runtime.ts +2 -2
- package/src/input/commands/workstream-attempts.ts +1 -1
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands.ts +2 -0
- package/src/input/feature-unit-layout.ts +1 -1
- package/src/input/feed-context-factory.ts +3 -3
- package/src/input/handler-content-actions.ts +2 -2
- package/src/input/handler-feed-routes.ts +1 -1
- package/src/input/handler-feed.ts +3 -3
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +3 -2
- package/src/input/handler-onboarding.ts +1 -1
- package/src/input/handler-types.ts +2 -2
- package/src/input/handler-ui-state.ts +1 -1
- package/src/input/handler.ts +4 -4
- package/src/input/input-history.ts +1 -1
- package/src/input/onboarding/onboarding-feature-units.ts +2 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +15 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +17 -8
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +5 -3
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-types.ts +1 -1
- package/src/input/payments-config.ts +6 -6
- package/src/input/search.ts +1 -1
- package/src/input/session-picker-modal.ts +46 -0
- package/src/input/settings-modal-activation.ts +1 -1
- package/src/input/settings-modal-behavior.ts +4 -4
- package/src/input/settings-modal-commit.ts +11 -0
- package/src/input/settings-modal-data.ts +13 -80
- package/src/input/settings-modal-mutations.ts +33 -0
- package/src/input/settings-modal-secrets.ts +34 -0
- package/src/input/settings-modal-subscriptions.ts +1 -1
- package/src/input/settings-modal-types.ts +15 -1
- package/src/input/settings-modal.ts +28 -3
- package/src/input/worktree-setup-config.ts +1 -1
- package/src/main.ts +11 -9
- package/src/panels/agent-inspector-shared.ts +2 -2
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/operations.ts +6 -8
- package/src/panels/builtin/session.ts +17 -0
- package/src/panels/builtin/shared.ts +5 -5
- package/src/panels/builtin-modals.ts +3 -3
- package/src/panels/confirm-state.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +4 -4
- package/src/panels/diff-panel.ts +2 -2
- package/src/panels/diff-review-panel.ts +2 -2
- package/src/panels/expandable-list-panel.ts +2 -2
- package/src/panels/fleet-acts.ts +1 -1
- package/src/panels/fleet-observed-render.ts +1 -1
- package/src/panels/fleet-panel-format.ts +1 -1
- package/src/panels/fleet-panel.ts +1 -1
- package/src/panels/fleet-spawn.ts +1 -1
- package/src/panels/fleet-steer.ts +1 -1
- package/src/panels/fleet-transcript.ts +1 -1
- package/src/panels/git-panel.ts +1 -1
- package/src/panels/hosted-session-feed.ts +352 -0
- package/src/panels/hosted-session-panel.ts +221 -0
- package/src/panels/local-auth-panel.ts +3 -3
- package/src/panels/modals/devices-modal.ts +1 -1
- package/src/panels/modals/local-auth-modal.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +1 -1
- package/src/panels/modals/services-modal.ts +1 -1
- package/src/panels/modals/settings-sync-modal.ts +1 -1
- package/src/panels/modals/subscription-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +1 -1
- package/src/panels/notifications-panel.ts +1 -1
- package/src/panels/panel-confirm-overlay.ts +1 -1
- package/src/panels/plugins-panel.ts +2 -2
- package/src/panels/polish-core.ts +2 -2
- package/src/panels/polish-tables.ts +1 -1
- package/src/panels/polish.ts +2 -2
- package/src/panels/scrollable-list-panel.ts +3 -3
- package/src/panels/skills-panel.ts +3 -3
- package/src/panels/token-budget-panel.ts +5 -5
- package/src/panels/types.ts +1 -2
- package/src/permissions/prompt.ts +1 -1
- package/src/renderer/autocomplete-overlay.ts +1 -1
- package/src/renderer/block-actions-overlay.ts +1 -1
- package/src/renderer/bookmark-modal.ts +1 -1
- package/src/renderer/buffer.ts +1 -1
- package/src/renderer/code-block.ts +1 -1
- package/src/renderer/composer-fragments.ts +1 -1
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/context-inspector.ts +1 -1
- package/src/renderer/conversation-layout.ts +2 -2
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/conversation-surface.ts +2 -2
- package/src/renderer/diff-view.ts +1 -1
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +1 -1
- package/src/renderer/fleet-tab-strip.ts +1 -1
- package/src/renderer/fullscreen-primitives.ts +2 -2
- package/src/renderer/fullscreen-workspace.ts +1 -1
- package/src/renderer/header-line.ts +1 -1
- package/src/renderer/help-overlay.ts +1 -1
- package/src/renderer/history-search-overlay.ts +2 -2
- package/src/renderer/layout.ts +5 -8
- package/src/renderer/markdown-inline.ts +1 -1
- package/src/renderer/markdown-table.ts +1 -1
- package/src/renderer/markdown.ts +1 -1
- package/src/renderer/mcp-workspace.ts +1 -1
- package/src/renderer/modal-factory.ts +2 -2
- package/src/renderer/model-workspace.ts +2 -2
- package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
- package/src/renderer/overlay-box.ts +1 -1
- package/src/renderer/panel-composite.ts +1 -1
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/profile-picker-modal.ts +1 -1
- package/src/renderer/search-overlay.ts +2 -2
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +124 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +2 -2
- package/src/renderer/shell-surface.ts +2 -2
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/theme-mode-config.ts +1 -1
- package/src/renderer/theme.ts +11 -12
- package/src/renderer/thinking.ts +1 -1
- package/src/renderer/tool-call.ts +2 -2
- package/src/renderer/tool-result-expanded-lines.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -8
- package/src/renderer/ui-factory.ts +3 -3
- package/src/runtime/bootstrap-command-context.ts +4 -4
- package/src/runtime/bootstrap-command-parts.ts +5 -5
- package/src/runtime/bootstrap-core.ts +30 -30
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +128 -157
- package/src/runtime/client/approval-updates.ts +82 -0
- package/src/runtime/client/build-floors.ts +167 -0
- package/src/runtime/client/daemon-attach-handshake.ts +115 -0
- package/src/runtime/client/fleet-union.ts +147 -0
- package/src/runtime/client/host-service-status.ts +71 -0
- package/src/runtime/client/hosted-exit.ts +86 -0
- package/src/runtime/client/hosted-roster.ts +110 -0
- package/src/runtime/client/hosted-session-stream.ts +178 -0
- package/src/runtime/client/hosted-sessions.ts +187 -0
- package/src/runtime/client/operator-endpoint.ts +213 -0
- package/src/runtime/client/spine-adoption.ts +99 -0
- package/src/runtime/cloudflare-control-plane.ts +1 -1
- package/src/runtime/context-accounting-source.ts +1 -1
- package/src/runtime/context.ts +2 -2
- package/src/runtime/conversation-rewind-port.ts +42 -14
- package/src/runtime/daemon-attach-notices.ts +55 -19
- package/src/runtime/diagnostics/panels/ops.ts +1 -1
- package/src/runtime/disposal-wiring.ts +4 -24
- package/src/runtime/index.ts +119 -105
- package/src/runtime/install-self-check-startup.ts +2 -2
- package/src/runtime/interaction-seams.ts +1 -1
- package/src/runtime/knowledge-services.ts +8 -2
- package/src/runtime/legacy-daemon-migration.ts +4 -3
- package/src/runtime/legacy-daemon-reconcile.ts +3 -2
- package/src/runtime/memory-spine-transport.ts +37 -267
- package/src/runtime/notification-dispatch.ts +2 -2
- package/src/runtime/onboarding/apply.ts +2 -2
- package/src/runtime/onboarding/derivation.ts +2 -2
- package/src/runtime/onboarding/markers.ts +2 -2
- package/src/runtime/onboarding/progress.ts +2 -2
- package/src/runtime/onboarding/snapshot.ts +1 -1
- package/src/runtime/onboarding/state.ts +2 -2
- package/src/runtime/onboarding/types.ts +2 -2
- package/src/runtime/orchestrator-core-services.ts +1 -1
- package/src/runtime/path-shadow-startup.ts +28 -221
- package/src/runtime/power-keepawake-remote.ts +3 -2
- package/src/runtime/process-lifecycle.ts +2 -2
- package/src/runtime/provider-fallback.ts +10 -66
- package/src/runtime/recovery-autosave.ts +1 -1
- package/src/runtime/recovery-offer-wiring.ts +39 -23
- package/src/runtime/recovery-prompt.ts +31 -17
- package/src/runtime/relay-reachability-bridge.ts +53 -42
- package/src/runtime/release-artifacts.ts +2 -1
- package/src/runtime/runtime-services-types.ts +82 -35
- package/src/runtime/services.ts +305 -300
- package/src/runtime/session-resume-liveness-confirm.ts +1 -1
- package/src/runtime/ui-services.ts +7 -6
- package/src/runtime/update-check.ts +23 -43
- package/src/runtime/workspace-checkpointing.ts +1 -1
- package/src/runtime/wrfc-persistence.ts +2 -2
- package/src/shell/ui-openers.ts +30 -7
- package/src/utils/clipboard.ts +1 -1
- package/src/verification/verification-ledger.ts +10 -8
- package/src/version.ts +1 -1
- package/bin/goodvibes-daemon +0 -90
- package/src/cli/config-overrides.ts +0 -267
- package/src/cli/endpoints.ts +0 -109
- package/src/cli/parser.ts +0 -395
- package/src/cli/redaction.ts +0 -170
- package/src/cli/types.ts +0 -84
- package/src/cluster/commands.ts +0 -382
- package/src/cluster/remote-daemon-target.ts +0 -225
- package/src/cluster/render.ts +0 -268
- package/src/config/atomic-write.ts +0 -70
- package/src/config/credential-availability.ts +0 -122
- package/src/config/goodvibes-home.ts +0 -149
- package/src/config/index.ts +0 -88
- package/src/config/provider-model.ts +0 -23
- package/src/config/read-versioned.ts +0 -228
- package/src/core/alert-gating.ts +0 -67
- package/src/core/consolidation-receipt.ts +0 -41
- package/src/core/focus-tracker.ts +0 -41
- package/src/core/history.ts +0 -45
- package/src/core/memory-status.ts +0 -118
- package/src/core/orchestrator.ts +0 -7
- package/src/core/pairing-devices.ts +0 -49
- package/src/core/pairing-handoff.ts +0 -101
- package/src/core/pairing-offers.ts +0 -83
- package/src/core/pairing-origin.ts +0 -78
- package/src/core/rewind-turn-anchors.ts +0 -311
- package/src/core/session-recovery.ts +0 -210
- package/src/core/stable-host.ts +0 -171
- package/src/core/transcript-journal.ts +0 -503
- package/src/daemon/cli.ts +0 -693
- package/src/daemon/fatal-boot-report.ts +0 -111
- package/src/daemon/handlers/context.ts +0 -29
- package/src/daemon/handlers/contracts.ts +0 -77
- package/src/daemon/handlers/credentials.ts +0 -139
- package/src/daemon/handlers/drafts/draft-store.ts +0 -427
- package/src/daemon/handlers/drafts/index.ts +0 -17
- package/src/daemon/handlers/drafts/register.ts +0 -331
- package/src/daemon/handlers/errors.ts +0 -18
- package/src/daemon/handlers/inbox/cursor-store.ts +0 -457
- package/src/daemon/handlers/inbox/index.ts +0 -284
- package/src/daemon/handlers/inbox/mapping.ts +0 -192
- package/src/daemon/handlers/inbox/poller.ts +0 -212
- package/src/daemon/handlers/inbox/provider-adapter.ts +0 -156
- package/src/daemon/handlers/inbox/providers/discord.ts +0 -252
- package/src/daemon/handlers/inbox/providers/email.ts +0 -152
- package/src/daemon/handlers/inbox/providers/imap-client.ts +0 -300
- package/src/daemon/handlers/inbox/providers/route-util.ts +0 -24
- package/src/daemon/handlers/inbox/providers/slack.ts +0 -263
- package/src/daemon/handlers/index.ts +0 -117
- package/src/daemon/handlers/register.ts +0 -168
- package/src/daemon/handlers/remote/backends/cloud-terminal.ts +0 -142
- package/src/daemon/handlers/remote/backends/docker.ts +0 -79
- package/src/daemon/handlers/remote/backends/index.ts +0 -40
- package/src/daemon/handlers/remote/backends/local-process.ts +0 -113
- package/src/daemon/handlers/remote/backends/process-runner.ts +0 -127
- package/src/daemon/handlers/remote/backends/ssh.ts +0 -125
- package/src/daemon/handlers/remote/backends/types.ts +0 -97
- package/src/daemon/handlers/remote/dispatcher.ts +0 -181
- package/src/daemon/handlers/remote/index.ts +0 -119
- package/src/daemon/handlers/remote/peer-registry.ts +0 -357
- package/src/daemon/handlers/remote/service.ts +0 -191
- package/src/daemon/handlers/routing/inbox-bridge.ts +0 -71
- package/src/daemon/handlers/routing/index.ts +0 -261
- package/src/daemon/handlers/routing/route-store.ts +0 -319
- package/src/daemon/handlers/routing/routing-resolver.ts +0 -75
- package/src/daemon/handlers/sqlite-store.ts +0 -278
- package/src/daemon/handlers/triage/index.ts +0 -57
- package/src/daemon/handlers/triage/integration.ts +0 -213
- package/src/daemon/handlers/triage/pipeline.ts +0 -274
- package/src/daemon/handlers/triage/scorer.ts +0 -287
- package/src/daemon/handlers/triage/tagger/discord.ts +0 -187
- package/src/daemon/handlers/triage/tagger/imap.ts +0 -384
- package/src/daemon/handlers/triage/tagger/index.ts +0 -184
- package/src/daemon/handlers/triage/tagger/shared.ts +0 -70
- package/src/daemon/handlers/triage/tagger/slack.ts +0 -69
- package/src/daemon/handlers/triage/types.ts +0 -50
- package/src/daemon/lifecycle.ts +0 -41
- package/src/daemon/provision-wake-model.ts +0 -81
- package/src/daemon/safe-serve.ts +0 -61
- package/src/daemon/send/channels.ts +0 -200
- package/src/daemon/send/command.ts +0 -332
- package/src/daemon/send/composition.ts +0 -99
- package/src/daemon/send/failure-text.ts +0 -93
- package/src/daemon/send/inert-text.ts +0 -225
- package/src/daemon/send/stdin.ts +0 -24
- package/src/daemon/service-commands.ts +0 -329
- package/src/export/cost-utils.ts +0 -319
- package/src/input/selection.ts +0 -135
- package/src/plugins/loader.ts +0 -15
- package/src/renderer/bottom-bar.ts +0 -58
- package/src/renderer/conversation-tree.ts +0 -228
- package/src/runtime/agent-graph-composition.ts +0 -78
- package/src/runtime/channel-composition.ts +0 -46
- package/src/runtime/cluster-composition.ts +0 -124
- package/src/runtime/cluster-group-composition.ts +0 -284
- package/src/runtime/code-index-services.ts +0 -167
- package/src/runtime/daemon-handler-composition.ts +0 -76
- package/src/runtime/device-posture-composition.ts +0 -110
- package/src/runtime/durability-housekeeping.ts +0 -183
- package/src/runtime/durability-services.ts +0 -155
- package/src/runtime/embedded-service-factories.ts +0 -74
- package/src/runtime/feature-settings.ts +0 -154
- package/src/runtime/fleet-needs-input-push.ts +0 -70
- package/src/runtime/fleet-services.ts +0 -41
- package/src/runtime/idle-power-services.ts +0 -85
- package/src/runtime/mail-composition.ts +0 -65
- package/src/runtime/memory-fold.ts +0 -55
- package/src/runtime/operator-token-cleanup.ts +0 -109
- package/src/runtime/path-shadow.ts +0 -418
- package/src/runtime/perf/panel-health-monitor.ts +0 -18
- package/src/runtime/provider-optimizer-wiring.ts +0 -55
- package/src/runtime/reachability-notice.ts +0 -100
- package/src/runtime/remote-execution-composition.ts +0 -62
- package/src/runtime/session-liveness-marker.ts +0 -257
- package/src/runtime/session-pointer-surface.ts +0 -22
- package/src/runtime/session-storage-services.ts +0 -41
- package/src/runtime/surface-feature-flags.ts +0 -96
- package/src/runtime/terminal-output-guard.ts +0 -235
- package/src/runtime/trust/workspace-registration.ts +0 -192
- package/src/runtime/trust/workspace-trust.ts +0 -229
- package/src/runtime/ui/index.ts +0 -39
- package/src/runtime/ui/model-picker/data-provider.ts +0 -182
- package/src/runtime/ui/model-picker/health-enrichment.ts +0 -231
- package/src/runtime/ui/model-picker/index.ts +0 -59
- package/src/runtime/ui/model-picker/types.ts +0 -153
- package/src/runtime/ui/provider-health/data-provider.ts +0 -244
- package/src/runtime/ui/provider-health/fallback-visualizer.ts +0 -71
- package/src/runtime/ui/provider-health/index.ts +0 -46
- package/src/runtime/ui/provider-health/types.ts +0 -146
- package/src/runtime/ui-events.ts +0 -1
- package/src/runtime/ui-read-model-helpers.ts +0 -1
- package/src/runtime/ui-read-models-observability-maintenance.ts +0 -1
- package/src/runtime/ui-read-models-observability-options.ts +0 -1
- package/src/runtime/ui-read-models-observability-remote.ts +0 -1
- package/src/runtime/ui-read-models-observability-security.ts +0 -1
- package/src/runtime/ui-read-models-observability-system.ts +0 -1
- package/src/runtime/ui-read-models-observability.ts +0 -1
- package/src/runtime/ui-service-queries.ts +0 -1
- package/src/runtime/voice-setup-services.ts +0 -98
- package/src/runtime/workstream-attempts-validation.ts +0 -77
- package/src/runtime/workstream-draft-edits.ts +0 -95
- package/src/runtime/workstream-draft-store.ts +0 -274
- package/src/runtime/workstream-draft-types.ts +0 -42
- package/src/runtime/workstream-services.ts +0 -373
- package/src/tools/index.ts +0 -1
- package/src/types/grid.ts +0 -48
- package/src/types/sql-js.d.ts +0 -15
- package/src/work-plans/work-plan-store.ts +0 -702
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -10,32 +10,31 @@
|
|
|
10
10
|
* - lifecycle.ts: save/shutdown helpers
|
|
11
11
|
*/
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
-
import { Orchestrator, type OrchestratorUserInputOptions } from '
|
|
13
|
+
import { Orchestrator, type OrchestratorUserInputOptions } from '@pellux/goodvibes-sdk/platform/core';
|
|
14
14
|
import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp';
|
|
15
15
|
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
16
16
|
import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
17
17
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
18
|
-
import type { WorkspaceTrustLevel } from '
|
|
18
|
+
import type { WorkspaceTrustLevel } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
19
19
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
20
20
|
import type { InputHistory } from '../input/input-history.ts';
|
|
21
21
|
import type { GitStatusProvider, GitHeaderInfo } from '../renderer/git-status.ts';
|
|
22
|
-
import type { SelectionManager } from '
|
|
22
|
+
import type { SelectionManager } from '@pellux/goodvibes-terminal-shell';
|
|
23
23
|
import type { Compositor } from '../renderer/compositor.ts';
|
|
24
24
|
|
|
25
25
|
import type { RuntimeContext, BootstrapOptions } from './context.ts';
|
|
26
26
|
import type { SystemMessageRouter } from '../core/system-message-router.ts';
|
|
27
27
|
import {
|
|
28
28
|
shutdownRuntime, fireSessionStart, createTaskManager, OpsControlPlane, AcpTaskAdapter,
|
|
29
|
-
emitSessionReady, emitSessionStarted, loadLastConversation,
|
|
29
|
+
emitSessionReady, emitSessionStarted, loadLastConversation, leaveHostedSessionOnExit,
|
|
30
30
|
scheduleBackgroundMcpDiscovery, startBackgroundProviderRegistration, restoreSavedModel, startExternalServices,
|
|
31
31
|
type ExternalServicesHandle, type HostServiceStatus, createHttpTransport, createDeferredStartupCoordinator,
|
|
32
32
|
} from '@/runtime/index.ts';
|
|
33
|
-
import { bindWriteLastSessionPointerToSurface } from '
|
|
33
|
+
import { bindWriteLastSessionPointerToSurface } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
34
34
|
import { foldLegacySpineStore, deriveSpineFooterStatus } from '@pellux/goodvibes-sdk/platform/runtime/session-spine';
|
|
35
|
-
import {
|
|
36
|
-
import { syncMemorySpineToHostStatus, type MemorySpineActiveRef } from './memory-spine-transport.ts';
|
|
35
|
+
import { createSpineAdoptionSync } from './client/spine-adoption.ts';
|
|
37
36
|
import { pruneStaleOperatorTokens } from '@pellux/goodvibes-sdk/platform/pairing';
|
|
38
|
-
import { resolveDaemonCompanionToken, workspaceOperatorTokenCandidates } from '
|
|
37
|
+
import { resolveDaemonCompanionToken, workspaceOperatorTokenCandidates } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
39
38
|
import type { UiRuntimeServices } from './ui-services.ts';
|
|
40
39
|
import { initializeBootstrapCore } from './bootstrap-core.ts';
|
|
41
40
|
import { ensureBootModelResolvable } from './provider-fallback.ts';
|
|
@@ -43,11 +42,16 @@ import { createBootstrapShell } from './bootstrap-shell.ts';
|
|
|
43
42
|
import { announceResumeState } from './resume-notice.ts';
|
|
44
43
|
import { announceInstallHealth } from './install-self-check-startup.ts';
|
|
45
44
|
import { buildSharedOrchestratorCoreServices, refreshMemoryRecallSnapshot } from './orchestrator-core-services.ts';
|
|
46
|
-
import { consumeDaemonAttachNotices
|
|
45
|
+
import { consumeDaemonAttachNotices } from './daemon-attach-notices.ts';
|
|
47
46
|
import { wireContextAccountingSource } from './context-accounting-source.ts';
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
47
|
+
import { autostartInstalledDaemon, createDaemonServiceControl, describeDaemonAutostart } from '@pellux/goodvibes-sdk/platform/runtime/client';
|
|
48
|
+
import { createDaemonAttachHandshake } from './client/daemon-attach-handshake.ts';
|
|
49
|
+
import {
|
|
50
|
+
createPendingServiceStatus, hostServiceIsActive, hostServiceIsBlocked,
|
|
51
|
+
} from './client/host-service-status.ts';
|
|
52
|
+
import { relayReadAccessors } from './relay-reachability-bridge.ts';
|
|
50
53
|
import { startMcpConfigAutoReload } from '../mcp/runtime-reload.ts';
|
|
54
|
+
import { GOODVIBES_TUI_SURFACE_ROOT } from '../config/surface.ts';
|
|
51
55
|
|
|
52
56
|
type ExternalServiceFactories = NonNullable<Parameters<typeof startExternalServices>[4]>;
|
|
53
57
|
|
|
@@ -196,9 +200,6 @@ export async function bootstrapRuntime(
|
|
|
196
200
|
} = services;
|
|
197
201
|
// A saved custom-provider model must never crash boot — see provider-fallback.ts.
|
|
198
202
|
await ensureBootModelResolvable(providerRegistry, configManager);
|
|
199
|
-
// A liveness flip is only PAINTED once something calls requestRender(); without
|
|
200
|
-
// this, the footer's spine segment sat correct-but-undrawn until incidental activity redrew it (minutes, during an idle stretch).
|
|
201
|
-
sessionUnionCache.setOnTransition(() => requestRender());
|
|
202
203
|
|
|
203
204
|
// ── Phase 6: Orchestrator + AcpManager ───────────────────────────────────
|
|
204
205
|
|
|
@@ -350,103 +351,28 @@ export async function bootstrapRuntime(
|
|
|
350
351
|
|
|
351
352
|
const deferredStartup = createDeferredStartupCoordinator();
|
|
352
353
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
};
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const hostServiceIsActive = (status: HostServiceStatus): boolean => status.mode === 'embedded' || status.mode === 'external';
|
|
379
|
-
|
|
380
|
-
// 'blocked' (occupied by an unverified process) and 'incompatible' (occupied
|
|
381
|
-
// by a GoodVibes daemon we refused to adopt on a wire-version mismatch) both
|
|
382
|
-
// mean the configured port is held and unusable by this TUI instance.
|
|
383
|
-
const hostServiceIsBlocked = (status: HostServiceStatus): boolean => status.mode === 'blocked' || status.mode === 'incompatible';
|
|
384
|
-
|
|
385
|
-
// PERMANENT DESIGN (docs/decisions/2026-07-06-session-spine-mode-branch-is-permanent.md):
|
|
386
|
-
// 'embedded'/'external' are two distinct, both-supported daemon topologies, not
|
|
387
|
-
// migration stages — the ONE client-selection point for the session spine, driven by
|
|
388
|
-
// the SAME authoritative HostServiceMode adopt-or-start already computed above.
|
|
389
|
-
// 'embedded' (daemon.embedInProcess=true) means THIS process's own SharedSessionBroker
|
|
390
|
-
// already IS the daemon's broker — nothing to mirror TO, so the spine stays
|
|
391
|
-
// permanently dormant. Every other mode ('disabled'/'blocked'/'incompatible'/
|
|
392
|
-
// 'unavailable') also stays local-only and honest. Only 'external' (a separately-
|
|
393
|
-
// running daemon this TUI adopted) activates the wire mirror.
|
|
394
|
-
let spineActiveForBaseUrl: string | null = null;
|
|
395
|
-
// Same adoption signal drives the memory spine (WO memory-adopt) — see
|
|
396
|
-
// memory-spine-transport.ts. 'embedded' (this process hosts its own daemon)
|
|
397
|
-
// stays local: the daemon's canonical store IS this process's own
|
|
398
|
-
// memoryRegistry, so there is no wire hop to make.
|
|
399
|
-
const memorySpineActiveRef: MemorySpineActiveRef = { value: null };
|
|
400
|
-
const syncSessionSpineToHostStatus = (daemonStatus: HostServiceStatus, sharedDaemonToken: string): void => {
|
|
401
|
-
syncMemorySpineToHostStatus(services.memorySpine, daemonStatus.mode, daemonStatus.baseUrl, sharedDaemonToken, memorySpineActiveRef, logger);
|
|
402
|
-
if (daemonStatus.mode !== 'external') {
|
|
403
|
-
if (spineActiveForBaseUrl !== null) {
|
|
404
|
-
sessionSpine.deactivate(`daemon mode changed to '${daemonStatus.mode}'`);
|
|
405
|
-
sessionInboundInputs.deactivate(`daemon mode changed to '${daemonStatus.mode}'`);
|
|
406
|
-
spineActiveForBaseUrl = null;
|
|
407
|
-
}
|
|
408
|
-
// Keep the read facade honest per topology (permanent, not staged): 'embedded'
|
|
409
|
-
// means this process's broker IS the daemon's broker (local reads are the whole
|
|
410
|
-
// truth); every other non-external mode is local-only/dormant.
|
|
411
|
-
if (daemonStatus.mode === 'embedded') sessionUnionCache.markEmbedded();
|
|
412
|
-
else sessionUnionCache.deactivate(`daemon mode '${daemonStatus.mode}'`);
|
|
413
|
-
logger.info(`[bootstrap] session spine: daemon mode '${daemonStatus.mode}' — local-only (no spine mirror)`);
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
const baseUrl = daemonStatus.baseUrl;
|
|
417
|
-
if (spineActiveForBaseUrl === baseUrl) return; // already wired to this exact adopted daemon
|
|
418
|
-
const httpTransport = createHttpTransport({ baseUrl, authToken: sharedDaemonToken });
|
|
419
|
-
const sessionsClient: SpineSessionsClient = {
|
|
420
|
-
register: (input) => httpTransport.operator.sessions.register(input),
|
|
421
|
-
close: (sessionId) => httpTransport.operator.sessions.close(sessionId),
|
|
422
|
-
};
|
|
423
|
-
sessionSpine.activate(createTuiSpineTransport(sessionsClient));
|
|
424
|
-
// Adopt the same daemon's wire for the INBOUND steer path — collect
|
|
425
|
-
// steer/follow-up inputs another live surface queued for this session and
|
|
426
|
-
// inject them into the turn machinery (acking delivery on the wire).
|
|
427
|
-
sessionInboundInputs.activate({
|
|
428
|
-
listInputs: async (sessionId, opts) => ({
|
|
429
|
-
inputs: await httpTransport.operator.sessions.inputs(sessionId, opts.limit, { state: opts.state, since: opts.since }),
|
|
430
|
-
}),
|
|
431
|
-
deliverInput: (sessionId, inputId, opts) => httpTransport.operator.sessions.deliverInput(sessionId, inputId, opts),
|
|
432
|
-
});
|
|
433
|
-
// 'external' only: adopt the same daemon's wire as the read facade's cross-surface
|
|
434
|
-
// union source (interval-refreshed; served synchronously to panels).
|
|
435
|
-
sessionUnionCache.activate({ list: (limit) => httpTransport.operator.sessions.list(limit) });
|
|
436
|
-
spineActiveForBaseUrl = baseUrl;
|
|
437
|
-
logger.info(`[bootstrap] session spine: adopted external daemon at ${baseUrl} — mirroring session identity`);
|
|
438
|
-
// Legacy fold: one-time (marker-guarded) import of this project's own
|
|
439
|
-
// pre-spine control-plane sessions.json into the now-adopted daemon.
|
|
440
|
-
const legacyStorePath = services.shellPaths.resolveProjectPath('tui', 'control-plane', 'sessions.json');
|
|
441
|
-
const fold = foldLegacySpineStore(sessionSpine, {
|
|
442
|
-
storePath: legacyStorePath,
|
|
443
|
-
markerPath: `${legacyStorePath}.spine-migrated`,
|
|
444
|
-
project: services.workingDirectory,
|
|
445
|
-
});
|
|
446
|
-
if (fold.folded > 0) {
|
|
447
|
-
logger.info(`[bootstrap] session spine: folded ${fold.folded} legacy local session(s) into the adopted daemon`);
|
|
448
|
-
}
|
|
449
|
-
};
|
|
354
|
+
// Adopting a daemon: session identity, the inbound steer path, the
|
|
355
|
+
// cross-surface union read, and the memory spine — see client/spine-adoption.ts.
|
|
356
|
+
const syncSessionSpineToHostStatus = createSpineAdoptionSync({
|
|
357
|
+
sessionSpine,
|
|
358
|
+
memorySpine: services.memorySpine,
|
|
359
|
+
sessionInboundInputs: sessionInboundInputs as unknown as Parameters<typeof createSpineAdoptionSync>[0]['sessionInboundInputs'],
|
|
360
|
+
sessionUnionCache: sessionUnionCache as unknown as Parameters<typeof createSpineAdoptionSync>[0]['sessionUnionCache'],
|
|
361
|
+
legacyStorePath: services.shellPaths.resolveProjectPath('tui', 'control-plane', 'sessions.json'),
|
|
362
|
+
workingDirectory: services.workingDirectory,
|
|
363
|
+
// Both ride adoption. A message submitted into a session this surface hosts
|
|
364
|
+
// must reach the loop here, because the loop is here; and a rewind driven
|
|
365
|
+
// from the web app or another terminal is answerable only by the process
|
|
366
|
+
// holding the messages, which is also here.
|
|
367
|
+
onAdopted: (client) => {
|
|
368
|
+
services.wireSessionDispatch.activate(client as never);
|
|
369
|
+
if (runtime.sessionId) services.conversationRewindHost.start(runtime.sessionId);
|
|
370
|
+
},
|
|
371
|
+
onDetached: (reason) => {
|
|
372
|
+
services.wireSessionDispatch.deactivate(reason);
|
|
373
|
+
void services.conversationRewindHost.stop();
|
|
374
|
+
},
|
|
375
|
+
});
|
|
450
376
|
|
|
451
377
|
const inspectExternalServices = () => {
|
|
452
378
|
const daemonStatus = externalServices.daemonStatus;
|
|
@@ -467,29 +393,61 @@ export async function bootstrapRuntime(
|
|
|
467
393
|
};
|
|
468
394
|
};
|
|
469
395
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
396
|
+
// ADOPT ONLY. This app never constructs a DaemonServer or an HttpListener: the
|
|
397
|
+
// daemon is a separate product with its own binary and its own service unit.
|
|
398
|
+
// The factories carry the shared bearer and the daemon's state directory so an
|
|
399
|
+
// adopted daemon is authenticated the same way it always was — no factory that
|
|
400
|
+
// BUILDS anything is passed, and `adoptOnly` makes the SDK refuse to start one
|
|
401
|
+
// even if a future caller did.
|
|
402
|
+
//
|
|
403
|
+
// `daemonRuntimeDir` names the daemon's own STATE directory
|
|
404
|
+
// (`<home>/.goodvibes/daemon`), which is where operator-tokens.json,
|
|
405
|
+
// auth-users.json and daemon-settings.json live — not the home above it. Every
|
|
406
|
+
// reader in this repository resolves it that way.
|
|
407
|
+
const createExternalServiceFactories = (token: string): ExternalServiceFactories => ({
|
|
408
|
+
sharedDaemonToken: token,
|
|
409
|
+
daemonRuntimeDir: join(services.homeDirectory, '.goodvibes', 'daemon'),
|
|
410
|
+
adoptOnly: true,
|
|
411
|
+
});
|
|
477
412
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
413
|
+
// The one bounded recovery step: a daemon that is INSTALLED on this machine
|
|
414
|
+
// and simply stopped gets started once and waited for, rather than becoming
|
|
415
|
+
// the user's homework. Every boundary is in the SDK's client/daemon-autostart.ts.
|
|
416
|
+
const maybeStartInstalledDaemon = async (): Promise<void> => {
|
|
417
|
+
try {
|
|
418
|
+
const outcome = await autostartInstalledDaemon({
|
|
419
|
+
daemonMode: externalServices.daemonStatus.mode,
|
|
420
|
+
control: createDaemonServiceControl({
|
|
421
|
+
configManager,
|
|
422
|
+
workingDirectory: services.workingDirectory,
|
|
423
|
+
homeDirectory: services.homeDirectory,
|
|
424
|
+
}),
|
|
425
|
+
isReachable: async () => await sessionSpine.probeReachability() === 'online',
|
|
426
|
+
});
|
|
427
|
+
if (outcome.action === 'started' || outcome.action === 'came-online') {
|
|
428
|
+
const companionTokenRecord = resolveDaemonCompanionToken(join(services.homeDirectory, '.goodvibes', 'daemon'), GOODVIBES_TUI_SURFACE_ROOT);
|
|
429
|
+
externalServicesPromise = startExternalServices(
|
|
430
|
+
configManager, runtimeBus, hookDispatcher, services,
|
|
431
|
+
createExternalServiceFactories(companionTokenRecord.token),
|
|
432
|
+
);
|
|
433
|
+
externalServices = await externalServicesPromise;
|
|
434
|
+
await daemonHandshake.attach(companionTokenRecord.token);
|
|
435
|
+
}
|
|
436
|
+
const notice = describeDaemonAutostart(
|
|
437
|
+
outcome,
|
|
438
|
+
externalServices.daemonStatus.mode === 'external',
|
|
439
|
+
externalServices.daemonStatus.reason ?? externalServices.daemonStatus.mode,
|
|
440
|
+
);
|
|
441
|
+
if (notice) systemMessageRouter[notice.level](notice.text);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
logger.debug('Boot-time daemon start check failed', { error: summarizeError(error) });
|
|
444
|
+
}
|
|
482
445
|
};
|
|
483
446
|
|
|
484
|
-
// Embedded DaemonServer/HttpListener factories (incl. the facade auto-update
|
|
485
|
-
// suppression), plus the daemon STATE directory — see embedded-service-factories.ts.
|
|
486
|
-
const createExternalServiceFactories = (token: string): ExternalServiceFactories =>
|
|
487
|
-
createEmbeddedServiceFactories(token, services.clusterCoordinator, services.clusterGroup.verbs, join(services.homeDirectory, '.goodvibes', 'daemon'));
|
|
488
|
-
|
|
489
447
|
let externalServices: ExternalServicesHandle = {
|
|
490
448
|
daemonServer: null, httpListener: null,
|
|
491
|
-
daemonStatus: createPendingServiceStatus('daemon'),
|
|
492
|
-
httpListenerStatus: createPendingServiceStatus('httpListener'),
|
|
449
|
+
daemonStatus: createPendingServiceStatus(configManager, 'daemon'),
|
|
450
|
+
httpListenerStatus: createPendingServiceStatus(configManager, 'httpListener'),
|
|
493
451
|
listRecentControlPlaneEvents: () => [],
|
|
494
452
|
async stop(): Promise<void> {},
|
|
495
453
|
};
|
|
@@ -498,11 +456,12 @@ export async function bootstrapRuntime(
|
|
|
498
456
|
externalServices: NonNullable<typeof uiServices.platform.externalServices>;
|
|
499
457
|
};
|
|
500
458
|
platformExternalServices.externalServices = {
|
|
501
|
-
|
|
459
|
+
// The relay is a DAEMON feature and reading an adopted daemon's relay state
|
|
460
|
+
// is a verb this contract does not carry, so these two report 'unavailable'
|
|
461
|
+
// rather than a state this process is in no position to know — see
|
|
462
|
+
// relay-reachability-bridge.ts.
|
|
463
|
+
...relayReadAccessors,
|
|
502
464
|
inspect: inspectExternalServices,
|
|
503
|
-
// Undelivered daemon receipts (update/crash/migration) for the attach-time
|
|
504
|
-
// consuming read; empty when no daemon runs. Marked delivered once served.
|
|
505
|
-
collectDaemonReceipts: () => (externalServices.daemonServer as { collectDaemonReceipts?: () => readonly { id: string; text: string; at: number }[] } | null)?.collectDaemonReceipts?.() ?? [],
|
|
506
465
|
restart: async () => {
|
|
507
466
|
if (externalServicesPromise) {
|
|
508
467
|
try {
|
|
@@ -511,10 +470,9 @@ export async function bootstrapRuntime(
|
|
|
511
470
|
// A failed previous startup should not prevent a restart attempt.
|
|
512
471
|
}
|
|
513
472
|
}
|
|
514
|
-
await waitForConfigDrivenRestarts(externalServices);
|
|
515
473
|
await externalServices.stop();
|
|
516
474
|
const daemonHomeDir = join(services.homeDirectory, '.goodvibes', 'daemon');
|
|
517
|
-
const companionTokenRecord = resolveDaemonCompanionToken(daemonHomeDir);
|
|
475
|
+
const companionTokenRecord = resolveDaemonCompanionToken(daemonHomeDir, GOODVIBES_TUI_SURFACE_ROOT);
|
|
518
476
|
externalServicesPromise = startExternalServices(
|
|
519
477
|
configManager,
|
|
520
478
|
runtimeBus,
|
|
@@ -524,23 +482,39 @@ export async function bootstrapRuntime(
|
|
|
524
482
|
);
|
|
525
483
|
externalServices = await externalServicesPromise;
|
|
526
484
|
controlPlaneRecentEventsRef.value = (limit) => externalServices.listRecentControlPlaneEvents(limit);
|
|
527
|
-
|
|
528
|
-
await renderDaemonAttachNotices(companionTokenRecord.token);
|
|
485
|
+
await daemonHandshake.attach(companionTokenRecord.token);
|
|
529
486
|
requestRender();
|
|
530
487
|
return inspectExternalServices();
|
|
531
488
|
},
|
|
532
489
|
};
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
490
|
+
// Attaching to a daemon settles both build floors and its undelivered
|
|
491
|
+
// receipts off one /status read — see client/daemon-attach-handshake.ts.
|
|
492
|
+
const daemonHandshake = createDaemonAttachHandshake({
|
|
493
|
+
clientBuildGuard: services.clientBuildGuard,
|
|
494
|
+
readDaemonStatus: () => externalServices.daemonStatus,
|
|
495
|
+
recordRefusal: (status) => { externalServices = { ...externalServices, daemonStatus: status }; },
|
|
496
|
+
adopt: (status, daemonToken) => syncSessionSpineToHostStatus(status, daemonToken),
|
|
497
|
+
notify: (text) => { systemMessageRouter.high(`[Daemon] ${text}`); requestRender(); },
|
|
498
|
+
});
|
|
499
|
+
// A liveness flip is only PAINTED once something calls requestRender(); without
|
|
500
|
+
// this, the footer's spine segment sat correct-but-undrawn until incidental activity redrew it (minutes, during an idle stretch).
|
|
501
|
+
// The same flip is what tells the handshake a daemon came back.
|
|
502
|
+
sessionUnionCache.setOnTransition((online) => {
|
|
503
|
+
requestRender();
|
|
504
|
+
daemonHandshake.onLivenessTransition(online);
|
|
505
|
+
});
|
|
541
506
|
deferredStartup.schedule({
|
|
542
507
|
label: 'plugins',
|
|
543
508
|
run: async () => {
|
|
509
|
+
// Plugin loading stays here: a plugin's commands, tools, providers, voice
|
|
510
|
+
// and media providers and embedding providers all reach things a turn
|
|
511
|
+
// needs in THIS process. Three of the eleven registries a plugin can
|
|
512
|
+
// reach are the daemon's now — gateway methods, channel plugins and
|
|
513
|
+
// delivery strategies — and a registration into those reaches nothing
|
|
514
|
+
// from here. No plugin is affected today (none is installed, and this
|
|
515
|
+
// package bundles none); the classification and the open question about
|
|
516
|
+
// where a daemon-side plugin is loaded are recorded in
|
|
517
|
+
// docs/decisions/2026-07-30-plugin-registrations-split-verb-side-and-surface-side.md.
|
|
544
518
|
await pluginManager.init({
|
|
545
519
|
runtimeBus,
|
|
546
520
|
commandRegistry: pluginCommandRegistry,
|
|
@@ -570,9 +544,9 @@ export async function bootstrapRuntime(
|
|
|
570
544
|
run: async () => {
|
|
571
545
|
// Register the persistent companion-pairing token as the daemon's shared
|
|
572
546
|
// bearer, so tokens scanned from the /qrcode panel's QR actually
|
|
573
|
-
// authenticate against the
|
|
547
|
+
// authenticate against the daemon this surface adopts.
|
|
574
548
|
const daemonHomeDir = join(services.homeDirectory, '.goodvibes', 'daemon');
|
|
575
|
-
const companionTokenRecord = resolveDaemonCompanionToken(daemonHomeDir);
|
|
549
|
+
const companionTokenRecord = resolveDaemonCompanionToken(daemonHomeDir, GOODVIBES_TUI_SURFACE_ROOT);
|
|
576
550
|
// Fix (TUI 0.19.20): remove stale pre-0.21.28 workspace-scoped operator
|
|
577
551
|
// token files so only the canonical <daemonHomeDir>/operator-tokens.json survives.
|
|
578
552
|
// The prune is best-effort — it silently skips missing files, no-ops when tokens
|
|
@@ -580,7 +554,7 @@ export async function bootstrapRuntime(
|
|
|
580
554
|
// See `pruneStaleOperatorTokens` in the SDK for semantics.
|
|
581
555
|
const prune = pruneStaleOperatorTokens({
|
|
582
556
|
daemonHomeDir,
|
|
583
|
-
candidatePaths: workspaceOperatorTokenCandidates(services.workingDirectory),
|
|
557
|
+
candidatePaths: workspaceOperatorTokenCandidates(services.workingDirectory, GOODVIBES_TUI_SURFACE_ROOT),
|
|
584
558
|
});
|
|
585
559
|
if (prune.prunedPaths.length > 0) {
|
|
586
560
|
logger.info(`[bootstrap] Pruned ${prune.prunedPaths.length} stale operator-token file(s): ${prune.prunedPaths.join(', ')}`);
|
|
@@ -596,13 +570,10 @@ export async function bootstrapRuntime(
|
|
|
596
570
|
createExternalServiceFactories(companionTokenRecord.token),
|
|
597
571
|
);
|
|
598
572
|
externalServices = await externalServicesPromise;
|
|
599
|
-
//
|
|
600
|
-
|
|
601
|
-
// daemon.enabled is false. Idempotent with the DaemonServer's own call.
|
|
602
|
-
await services.startCluster();
|
|
573
|
+
// Installed-but-stopped recovery, before anything reads the status.
|
|
574
|
+
await maybeStartInstalledDaemon();
|
|
603
575
|
controlPlaneRecentEventsRef.value = (limit) => externalServices.listRecentControlPlaneEvents(limit);
|
|
604
|
-
|
|
605
|
-
await renderDaemonAttachNotices(companionTokenRecord.token);
|
|
576
|
+
await daemonHandshake.attach(companionTokenRecord.token);
|
|
606
577
|
requestRender();
|
|
607
578
|
},
|
|
608
579
|
onError: (error) => {
|
|
@@ -743,9 +714,9 @@ export async function bootstrapRuntime(
|
|
|
743
714
|
// a no-op when the spine was never activated (embedded/local-only topology).
|
|
744
715
|
sessionSpine.close(runtime.sessionId);
|
|
745
716
|
sessionSpine.dispose();
|
|
746
|
-
sessionInboundInputs.dispose(); // stop the inbound
|
|
747
|
-
|
|
748
|
-
await deferredStartup.drain(100);
|
|
717
|
+
sessionInboundInputs.dispose(); sessionUnionCache.dispose(); // stop the inbound-steer poll and the wire-refresh interval on exit
|
|
718
|
+
// Quitting has to SAY it is leaving: a hosted session's detach policy applies when the LAST client detaches, so an exit that never detaches leaves a kill-policy session (the shipped default) alive, attached to a process that is gone. Bounded and non-throwing — see client/hosted-exit.ts.
|
|
719
|
+
await Promise.all([leaveHostedSessionOnExit({ configManager, homeDirectory: services.homeDirectory }), deferredStartup.drain(100)]);
|
|
749
720
|
if (externalServicesPromise) {
|
|
750
721
|
try {
|
|
751
722
|
externalServices = await externalServicesPromise;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approval-updates.ts — this terminal's subscription to approval decisions made
|
|
3
|
+
* somewhere else.
|
|
4
|
+
*
|
|
5
|
+
* ── What this replaces ────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* The SDK's client raiser (approval-raiser.ts) raises an ask on the daemon and
|
|
8
|
+
* prompts locally at the same time, and then has to learn about a decision made
|
|
9
|
+
* on another surface — a phone, the web UI, a second terminal. Without a push
|
|
10
|
+
* channel wired it learns by RE-READING the record on an interval, which is
|
|
11
|
+
* both slower than it needs to be and the thing the `control.approval_update`
|
|
12
|
+
* event exists to end. Wiring this seam makes a remote decision arrive in the
|
|
13
|
+
* time one frame takes.
|
|
14
|
+
*
|
|
15
|
+
* The interval is deliberately KEPT as the fallback, not removed: a terminal
|
|
16
|
+
* whose daemon refuses the stream (no daemon, a 401, a proxy that will not hold
|
|
17
|
+
* a connection) still has to be able to answer a tool call. `watchApprovalUpdates`
|
|
18
|
+
* reports failure by returning null rather than throwing, and the raiser then
|
|
19
|
+
* behaves exactly as it did before this seam existed. Push is the fast path, not
|
|
20
|
+
* a new dependency — and the raiser's own discipline of doing one immediate read
|
|
21
|
+
* after subscribing stays intact, because a decision can land between the raise
|
|
22
|
+
* and the subscription and no push channel can deliver what happened before it
|
|
23
|
+
* opened.
|
|
24
|
+
*
|
|
25
|
+
* ── Why the product owns this and the SDK does not ────────────────────────
|
|
26
|
+
*
|
|
27
|
+
* Resolving "which daemon" and proving this surface may subscribe to it are
|
|
28
|
+
* trust-boundary concerns the SDK core deliberately never reaches into (the
|
|
29
|
+
* same carve-out `DaemonVerbCaller` records). The resolution here is the one
|
|
30
|
+
* every other client seam in this repo uses: operator-endpoint.ts.
|
|
31
|
+
*/
|
|
32
|
+
import { watchApprovalUpdates } from '@pellux/goodvibes-sdk/platform/runtime/client';
|
|
33
|
+
import type { ApprovalUpdateSubscriber } from '@pellux/goodvibes-sdk/platform/runtime/client';
|
|
34
|
+
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
35
|
+
import { resolveDaemonEnabled } from '@pellux/goodvibes-sdk/platform/config';
|
|
36
|
+
import { getOrCreateCompanionToken } from '@pellux/goodvibes-sdk/platform/pairing';
|
|
37
|
+
import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
38
|
+
import { resolveControlPlaneBaseUrl, resolveDaemonStateDirectory } from './operator-endpoint.ts';
|
|
39
|
+
|
|
40
|
+
export interface TerminalApprovalUpdateSubscriberOptions {
|
|
41
|
+
readonly configManager: ConfigManager;
|
|
42
|
+
readonly homeDirectory: string | (() => string);
|
|
43
|
+
/** Injectable fetch (tests, or a relay-tunnelled fetch). */
|
|
44
|
+
readonly fetchImpl?: typeof fetch | undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Build the subscriber the SDK raiser takes.
|
|
49
|
+
*
|
|
50
|
+
* Every refusal is a null return, never a throw: this runs on the path that
|
|
51
|
+
* decides a tool call, and a subscription that cannot be opened must degrade to
|
|
52
|
+
* the interval rather than take the ask down with it.
|
|
53
|
+
*/
|
|
54
|
+
export function createTerminalApprovalUpdateSubscriber(
|
|
55
|
+
options: TerminalApprovalUpdateSubscriberOptions,
|
|
56
|
+
): ApprovalUpdateSubscriber {
|
|
57
|
+
return async (onUpdate) => {
|
|
58
|
+
if (!resolveDaemonEnabled(options.configManager)) return null;
|
|
59
|
+
const baseUrl = resolveControlPlaneBaseUrl(options.configManager);
|
|
60
|
+
if (!baseUrl) return null;
|
|
61
|
+
let token: string;
|
|
62
|
+
try {
|
|
63
|
+
const homeDirectory = typeof options.homeDirectory === 'function'
|
|
64
|
+
? options.homeDirectory()
|
|
65
|
+
: options.homeDirectory;
|
|
66
|
+
token = getOrCreateCompanionToken('tui', {
|
|
67
|
+
daemonHomeDir: resolveDaemonStateDirectory(homeDirectory),
|
|
68
|
+
}).token;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.debug('[approvals] no bearer token could be read for the approval-update stream', {
|
|
71
|
+
error: summarizeError(error),
|
|
72
|
+
});
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return await watchApprovalUpdates({
|
|
76
|
+
baseUrl,
|
|
77
|
+
getAuthToken: () => token,
|
|
78
|
+
onUpdate,
|
|
79
|
+
...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
}
|