@pellux/goodvibes-agent 0.1.0
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 +35 -0
- package/.goodvibes/agents/reviewer.md +48 -0
- package/.goodvibes/skills/add-provider/SKILL.md +199 -0
- package/CHANGELOG.md +25 -0
- package/README.md +74 -0
- package/bin/goodvibes-agent.ts +2 -0
- package/docs/README.md +23 -0
- package/docs/deployment-and-services.md +57 -0
- package/docs/getting-started.md +53 -0
- package/docs/release-and-publishing.md +46 -0
- package/package.json +134 -0
- package/scripts/check-bun.sh +20 -0
- package/src/audio/player.ts +156 -0
- package/src/audio/spoken-turn-controller.ts +203 -0
- package/src/audio/spoken-turn-model-routing.ts +117 -0
- package/src/audio/spoken-turn-wiring.ts +44 -0
- package/src/audio/text-chunker.ts +110 -0
- package/src/cli/bundle-command.ts +227 -0
- package/src/cli/completion.ts +90 -0
- package/src/cli/config-overrides.ts +159 -0
- package/src/cli/endpoints.ts +63 -0
- package/src/cli/entrypoint.ts +172 -0
- package/src/cli/help.ts +299 -0
- package/src/cli/index.ts +11 -0
- package/src/cli/management-commands.ts +426 -0
- package/src/cli/management.ts +744 -0
- package/src/cli/network-posture.ts +46 -0
- package/src/cli/package-verification.ts +123 -0
- package/src/cli/parser.ts +369 -0
- package/src/cli/provider-auth-routes.ts +22 -0
- package/src/cli/provider-classification.ts +107 -0
- package/src/cli/redaction.ts +105 -0
- package/src/cli/service-command.ts +26 -0
- package/src/cli/service-posture.ts +482 -0
- package/src/cli/status.ts +383 -0
- package/src/cli/surface-command.ts +247 -0
- package/src/cli/tui-startup.ts +32 -0
- package/src/cli/types.ts +69 -0
- package/src/cli-flags.ts +21 -0
- package/src/config/goodvibes-home-audit.ts +465 -0
- package/src/config/index.ts +57 -0
- package/src/config/provider-model.ts +23 -0
- package/src/config/secret-config.ts +119 -0
- package/src/config/secrets.ts +71 -0
- package/src/config/surface.ts +1 -0
- package/src/core/composer-state.ts +61 -0
- package/src/core/conversation-rendering.ts +359 -0
- package/src/core/conversation.ts +551 -0
- package/src/core/history.ts +45 -0
- package/src/core/orchestrator.ts +7 -0
- package/src/core/system-message-router.ts +171 -0
- package/src/daemon/cli.ts +55 -0
- package/src/daemon/safe-serve.ts +61 -0
- package/src/input/agent-workspace.ts +428 -0
- package/src/input/autocomplete.ts +96 -0
- package/src/input/bookmark-modal.ts +115 -0
- package/src/input/command-args-hint.ts +36 -0
- package/src/input/command-registry.ts +329 -0
- package/src/input/commands/agent-externalized-tui.ts +73 -0
- package/src/input/commands/agent-workspace-runtime.ts +17 -0
- package/src/input/commands/branch-runtime.ts +72 -0
- package/src/input/commands/cloudflare-runtime.ts +370 -0
- package/src/input/commands/config.ts +18 -0
- package/src/input/commands/control-room-runtime.ts +255 -0
- package/src/input/commands/conversation-runtime.ts +207 -0
- package/src/input/commands/discovery-runtime.ts +52 -0
- package/src/input/commands/eval.ts +204 -0
- package/src/input/commands/experience-runtime.ts +278 -0
- package/src/input/commands/guidance-runtime.ts +106 -0
- package/src/input/commands/health-runtime.ts +434 -0
- package/src/input/commands/hooks-runtime.ts +148 -0
- package/src/input/commands/incident-runtime.ts +95 -0
- package/src/input/commands/integration-runtime.ts +394 -0
- package/src/input/commands/intelligence-runtime.ts +223 -0
- package/src/input/commands/knowledge.ts +531 -0
- package/src/input/commands/local-auth-runtime.ts +105 -0
- package/src/input/commands/local-provider-runtime.ts +170 -0
- package/src/input/commands/local-runtime.ts +392 -0
- package/src/input/commands/local-setup-review.ts +199 -0
- package/src/input/commands/local-setup-transfer.ts +135 -0
- package/src/input/commands/local-setup.ts +282 -0
- package/src/input/commands/managed-runtime.ts +209 -0
- package/src/input/commands/marketplace-runtime.ts +290 -0
- package/src/input/commands/mcp-runtime.ts +432 -0
- package/src/input/commands/memory-product-runtime.ts +111 -0
- package/src/input/commands/memory.ts +151 -0
- package/src/input/commands/notify-runtime.ts +83 -0
- package/src/input/commands/onboarding-runtime.ts +14 -0
- package/src/input/commands/operator-panel-runtime.ts +146 -0
- package/src/input/commands/operator-runtime.ts +392 -0
- package/src/input/commands/planning-runtime.ts +205 -0
- package/src/input/commands/platform-access-runtime.ts +422 -0
- package/src/input/commands/platform-services-runtime.ts +246 -0
- package/src/input/commands/policy-dispatch.ts +339 -0
- package/src/input/commands/policy.ts +17 -0
- package/src/input/commands/product-runtime.ts +351 -0
- package/src/input/commands/profile-sync-runtime.ts +99 -0
- package/src/input/commands/provider-accounts-runtime.ts +113 -0
- package/src/input/commands/provider.ts +363 -0
- package/src/input/commands/qrcode-runtime.ts +20 -0
- package/src/input/commands/quit-shared.ts +162 -0
- package/src/input/commands/recall-bundle.ts +132 -0
- package/src/input/commands/recall-capture.ts +152 -0
- package/src/input/commands/recall-query.ts +229 -0
- package/src/input/commands/recall-review.ts +98 -0
- package/src/input/commands/recall-shared.ts +22 -0
- package/src/input/commands/remote-runtime-pool.ts +106 -0
- package/src/input/commands/remote-runtime-setup.ts +199 -0
- package/src/input/commands/remote-runtime.ts +431 -0
- package/src/input/commands/replay-runtime.ts +18 -0
- package/src/input/commands/runtime-services.ts +291 -0
- package/src/input/commands/schedule-runtime.ts +91 -0
- package/src/input/commands/services-runtime.ts +209 -0
- package/src/input/commands/session-content.ts +408 -0
- package/src/input/commands/session-workflow.ts +464 -0
- package/src/input/commands/session.ts +375 -0
- package/src/input/commands/settings-sync-runtime.ts +174 -0
- package/src/input/commands/share-runtime.ts +119 -0
- package/src/input/commands/shell-core.ts +307 -0
- package/src/input/commands/skills-runtime.ts +221 -0
- package/src/input/commands/subscription-runtime.ts +434 -0
- package/src/input/commands/tasks-runtime.ts +230 -0
- package/src/input/commands/teamwork-runtime.ts +339 -0
- package/src/input/commands/teleport-runtime.ts +57 -0
- package/src/input/commands/tts-runtime.ts +29 -0
- package/src/input/commands/work-plan-runtime.ts +169 -0
- package/src/input/commands.ts +131 -0
- package/src/input/feed-context-factory.ts +254 -0
- package/src/input/file-picker.ts +192 -0
- package/src/input/handler-command-route.ts +180 -0
- package/src/input/handler-content-actions.ts +497 -0
- package/src/input/handler-feed-routes.ts +648 -0
- package/src/input/handler-feed.ts +452 -0
- package/src/input/handler-interactions.ts +281 -0
- package/src/input/handler-modal-routes.ts +418 -0
- package/src/input/handler-modal-stack.ts +263 -0
- package/src/input/handler-modal-token-routes.ts +329 -0
- package/src/input/handler-onboarding-cloudflare.ts +391 -0
- package/src/input/handler-onboarding.ts +620 -0
- package/src/input/handler-picker-routes.ts +472 -0
- package/src/input/handler-prompt-buffer.ts +320 -0
- package/src/input/handler-shortcuts.ts +213 -0
- package/src/input/handler-ui-state.ts +372 -0
- package/src/input/handler.ts +729 -0
- package/src/input/input-history.ts +297 -0
- package/src/input/keybindings.ts +292 -0
- package/src/input/mcp-workspace.ts +554 -0
- package/src/input/model-picker-provider-filter.ts +28 -0
- package/src/input/model-picker-types.ts +137 -0
- package/src/input/model-picker.ts +797 -0
- package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
- package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
- package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
- package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
- package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
- package/src/input/onboarding/onboarding-wizard.ts +711 -0
- package/src/input/panel-integration-actions.ts +78 -0
- package/src/input/profile-picker-modal.ts +222 -0
- package/src/input/search.ts +100 -0
- package/src/input/selection-modal.ts +163 -0
- package/src/input/selection.ts +135 -0
- package/src/input/session-picker-modal.ts +136 -0
- package/src/input/settings-modal-behavior.ts +37 -0
- package/src/input/settings-modal-secrets.ts +41 -0
- package/src/input/settings-modal-subscriptions.ts +95 -0
- package/src/input/settings-modal-types.ts +91 -0
- package/src/input/settings-modal.ts +793 -0
- package/src/input/submission-intent.ts +17 -0
- package/src/input/submission-router.ts +59 -0
- package/src/input/tts-settings-actions.ts +100 -0
- package/src/main.ts +792 -0
- package/src/mcp/runtime-reload.ts +81 -0
- package/src/panels/agent-inspector-panel.ts +521 -0
- package/src/panels/agent-inspector-shared.ts +94 -0
- package/src/panels/agent-logs-panel.ts +559 -0
- package/src/panels/agent-logs-shared.ts +129 -0
- package/src/panels/approval-panel.ts +150 -0
- package/src/panels/automation-control-panel.ts +212 -0
- package/src/panels/base-panel.ts +254 -0
- package/src/panels/builtin/agent.ts +117 -0
- package/src/panels/builtin/development.ts +31 -0
- package/src/panels/builtin/knowledge.ts +26 -0
- package/src/panels/builtin/operations.ts +349 -0
- package/src/panels/builtin/session.ts +129 -0
- package/src/panels/builtin/shared.ts +274 -0
- package/src/panels/builtin-panels.ts +23 -0
- package/src/panels/cockpit-panel.ts +183 -0
- package/src/panels/communication-panel.ts +153 -0
- package/src/panels/confirm-state.ts +61 -0
- package/src/panels/context-visualizer-panel.ts +204 -0
- package/src/panels/control-plane-panel.ts +211 -0
- package/src/panels/cost-tracker-panel.ts +444 -0
- package/src/panels/debug-panel.ts +432 -0
- package/src/panels/diff-panel.ts +520 -0
- package/src/panels/docs-panel.ts +283 -0
- package/src/panels/eval-panel.ts +399 -0
- package/src/panels/file-explorer-panel.ts +584 -0
- package/src/panels/file-preview-panel.ts +434 -0
- package/src/panels/forensics-panel.ts +364 -0
- package/src/panels/git-panel.ts +638 -0
- package/src/panels/hooks-panel.ts +239 -0
- package/src/panels/incident-review-panel.ts +197 -0
- package/src/panels/index.ts +46 -0
- package/src/panels/intelligence-panel.ts +176 -0
- package/src/panels/knowledge-panel.ts +345 -0
- package/src/panels/local-auth-panel.ts +130 -0
- package/src/panels/marketplace-panel.ts +212 -0
- package/src/panels/memory-panel.ts +225 -0
- package/src/panels/ops-control-panel.ts +150 -0
- package/src/panels/ops-strategy-panel.ts +235 -0
- package/src/panels/orchestration-panel.ts +273 -0
- package/src/panels/panel-list-panel.ts +509 -0
- package/src/panels/panel-manager.ts +570 -0
- package/src/panels/panel-picker.ts +106 -0
- package/src/panels/plan-dashboard-panel.ts +274 -0
- package/src/panels/plugins-panel.ts +178 -0
- package/src/panels/policy-panel.ts +308 -0
- package/src/panels/polish.ts +717 -0
- package/src/panels/project-planning-panel.ts +711 -0
- package/src/panels/provider-account-snapshot.ts +259 -0
- package/src/panels/provider-accounts-panel.ts +218 -0
- package/src/panels/provider-health-domains.ts +215 -0
- package/src/panels/provider-health-panel.ts +727 -0
- package/src/panels/provider-health-tracker.ts +115 -0
- package/src/panels/provider-stats-panel.ts +366 -0
- package/src/panels/qr-panel.ts +182 -0
- package/src/panels/remote-panel.ts +449 -0
- package/src/panels/routes-panel.ts +178 -0
- package/src/panels/sandbox-panel.ts +283 -0
- package/src/panels/schedule-panel.ts +329 -0
- package/src/panels/scrollable-list-panel.ts +491 -0
- package/src/panels/search-focus.ts +32 -0
- package/src/panels/security-panel.ts +295 -0
- package/src/panels/services-panel.ts +231 -0
- package/src/panels/session-browser-panel.ts +400 -0
- package/src/panels/session-maintenance.ts +125 -0
- package/src/panels/settings-sync-panel.ts +120 -0
- package/src/panels/skills-panel.ts +431 -0
- package/src/panels/subscription-panel.ts +263 -0
- package/src/panels/symbol-outline-panel.ts +486 -0
- package/src/panels/system-messages-panel.ts +230 -0
- package/src/panels/tasks-panel.ts +399 -0
- package/src/panels/thinking-panel.ts +304 -0
- package/src/panels/token-budget-panel.ts +475 -0
- package/src/panels/tool-inspector-panel.ts +429 -0
- package/src/panels/types.ts +54 -0
- package/src/panels/watchers-panel.ts +193 -0
- package/src/panels/work-plan-panel.ts +175 -0
- package/src/panels/worktree-panel.ts +182 -0
- package/src/panels/wrfc-panel.ts +609 -0
- package/src/permissions/prompt.ts +165 -0
- package/src/planning/project-planning-coordinator.ts +543 -0
- package/src/plugins/loader.ts +15 -0
- package/src/renderer/agent-detail-modal.ts +331 -0
- package/src/renderer/agent-workspace.ts +238 -0
- package/src/renderer/ansi-sanitize.ts +76 -0
- package/src/renderer/autocomplete-overlay.ts +154 -0
- package/src/renderer/block-actions.ts +76 -0
- package/src/renderer/bookmark-modal.ts +101 -0
- package/src/renderer/bottom-bar.ts +58 -0
- package/src/renderer/buffer.ts +113 -0
- package/src/renderer/code-block.ts +373 -0
- package/src/renderer/compositor.ts +283 -0
- package/src/renderer/context-inspector.ts +219 -0
- package/src/renderer/conversation-layout.ts +67 -0
- package/src/renderer/conversation-overlays.ts +140 -0
- package/src/renderer/conversation-surface.ts +260 -0
- package/src/renderer/diff-view.ts +132 -0
- package/src/renderer/diff.ts +130 -0
- package/src/renderer/file-picker-overlay.ts +101 -0
- package/src/renderer/file-tree.ts +153 -0
- package/src/renderer/fullscreen-primitives.ts +130 -0
- package/src/renderer/fullscreen-workspace.ts +199 -0
- package/src/renderer/git-status.ts +89 -0
- package/src/renderer/help-overlay.ts +267 -0
- package/src/renderer/history-search-overlay.ts +73 -0
- package/src/renderer/layout-engine.ts +97 -0
- package/src/renderer/layout.ts +32 -0
- package/src/renderer/live-tail-modal.ts +156 -0
- package/src/renderer/markdown.ts +635 -0
- package/src/renderer/mcp-workspace.ts +237 -0
- package/src/renderer/modal-factory.ts +467 -0
- package/src/renderer/modal-utils.ts +24 -0
- package/src/renderer/model-picker-overlay.ts +473 -0
- package/src/renderer/model-workspace.ts +488 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
- package/src/renderer/overlay-box.ts +146 -0
- package/src/renderer/overlay-viewport.ts +104 -0
- package/src/renderer/panel-composite.ts +158 -0
- package/src/renderer/panel-picker-overlay.ts +202 -0
- package/src/renderer/panel-tab-bar.ts +69 -0
- package/src/renderer/panel-workspace-bar.ts +42 -0
- package/src/renderer/process-indicator.ts +96 -0
- package/src/renderer/process-modal.ts +656 -0
- package/src/renderer/process-summary.ts +67 -0
- package/src/renderer/profile-picker-modal.ts +129 -0
- package/src/renderer/progress.ts +98 -0
- package/src/renderer/qr-renderer.ts +120 -0
- package/src/renderer/search-overlay.ts +54 -0
- package/src/renderer/selection-modal-overlay.ts +214 -0
- package/src/renderer/semantic-diff.ts +369 -0
- package/src/renderer/session-picker-modal.ts +127 -0
- package/src/renderer/settings-modal-helpers.ts +193 -0
- package/src/renderer/settings-modal.ts +537 -0
- package/src/renderer/shell-surface.ts +88 -0
- package/src/renderer/status-glyphs.ts +21 -0
- package/src/renderer/status-token.ts +67 -0
- package/src/renderer/surface-layout.ts +101 -0
- package/src/renderer/syntax-highlighter.ts +542 -0
- package/src/renderer/system-message.ts +83 -0
- package/src/renderer/tab-strip.ts +108 -0
- package/src/renderer/text-layout.ts +31 -0
- package/src/renderer/thinking.ts +17 -0
- package/src/renderer/tool-call.ts +234 -0
- package/src/renderer/ui-factory.ts +524 -0
- package/src/renderer/ui-primitives.ts +96 -0
- package/src/runtime/bootstrap-command-context.ts +278 -0
- package/src/runtime/bootstrap-command-parts.ts +386 -0
- package/src/runtime/bootstrap-core.ts +540 -0
- package/src/runtime/bootstrap-hook-bridge.ts +112 -0
- package/src/runtime/bootstrap-shell.ts +283 -0
- package/src/runtime/bootstrap.ts +575 -0
- package/src/runtime/cloudflare-control-plane.ts +349 -0
- package/src/runtime/context.ts +142 -0
- package/src/runtime/diagnostics/panels/index.ts +24 -0
- package/src/runtime/diagnostics/panels/ops.ts +156 -0
- package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
- package/src/runtime/diagnostics/panels/policy.ts +177 -0
- package/src/runtime/index.ts +662 -0
- package/src/runtime/onboarding/apply.ts +642 -0
- package/src/runtime/onboarding/derivation.ts +534 -0
- package/src/runtime/onboarding/index.ts +7 -0
- package/src/runtime/onboarding/markers.ts +148 -0
- package/src/runtime/onboarding/snapshot.ts +406 -0
- package/src/runtime/onboarding/state.ts +141 -0
- package/src/runtime/onboarding/types.ts +404 -0
- package/src/runtime/onboarding/verify.ts +171 -0
- package/src/runtime/operator-token-cleanup.ts +27 -0
- package/src/runtime/perf/panel-contracts.ts +32 -0
- package/src/runtime/perf/panel-health-monitor.ts +18 -0
- package/src/runtime/sandbox-public-gaps.ts +358 -0
- package/src/runtime/services.ts +670 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
- package/src/runtime/store/domains/index.ts +222 -0
- package/src/runtime/store/domains/panels.ts +117 -0
- package/src/runtime/store/domains/ui-perf.ts +103 -0
- package/src/runtime/store/index.ts +305 -0
- package/src/runtime/store/selectors/index.ts +359 -0
- package/src/runtime/store/state.ts +145 -0
- package/src/runtime/surface-feature-flags.ts +65 -0
- package/src/runtime/terminal-output-guard.ts +228 -0
- package/src/runtime/ui/index.ts +39 -0
- package/src/runtime/ui/model-picker/data-provider.ts +182 -0
- package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
- package/src/runtime/ui/model-picker/index.ts +59 -0
- package/src/runtime/ui/model-picker/types.ts +149 -0
- package/src/runtime/ui/provider-health/data-provider.ts +244 -0
- package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
- package/src/runtime/ui/provider-health/index.ts +46 -0
- package/src/runtime/ui/provider-health/types.ts +146 -0
- package/src/runtime/ui-events.ts +1 -0
- package/src/runtime/ui-read-model-helpers.ts +1 -0
- package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
- package/src/runtime/ui-read-models-observability-options.ts +1 -0
- package/src/runtime/ui-read-models-observability-remote.ts +1 -0
- package/src/runtime/ui-read-models-observability-security.ts +1 -0
- package/src/runtime/ui-read-models-observability-system.ts +1 -0
- package/src/runtime/ui-read-models-observability.ts +1 -0
- package/src/runtime/ui-read-models.ts +61 -0
- package/src/runtime/ui-service-queries.ts +1 -0
- package/src/runtime/ui-services.ts +190 -0
- package/src/scripts/process-messages.ts +42 -0
- package/src/shell/blocking-input.ts +98 -0
- package/src/shell/service-settings-sync.ts +273 -0
- package/src/shell/ui-openers.ts +352 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/wrfc-agent-guard.ts +49 -0
- package/src/types/grid.ts +48 -0
- package/src/types/sql-js.d.ts +15 -0
- package/src/utils/clipboard.ts +22 -0
- package/src/utils/splash-lines.ts +46 -0
- package/src/utils/terminal-width.ts +185 -0
- package/src/verification/live-verifier.ts +430 -0
- package/src/verification/verification-ledger.ts +242 -0
- package/src/version.ts +17 -0
- package/src/widget/index.ts +2 -0
- package/src/widget/types.ts +9 -0
- package/src/widget/widget.ts +8 -0
- package/src/work-plans/work-plan-store.ts +374 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { ConversationManager } from '../core/conversation';
|
|
2
|
+
import { SelectionManager } from '../input/selection.ts';
|
|
3
|
+
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
4
|
+
import { ConfigManager, getConfiguredSystemPrompt } from '../config/index.ts';
|
|
5
|
+
import { getProviderIdFromModel } from '../config/provider-model.ts';
|
|
6
|
+
import { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
7
|
+
import { registerAllTools } from '@pellux/goodvibes-sdk/platform/tools';
|
|
8
|
+
import { PermissionManager, createPermissionConfigReader } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
9
|
+
import { Notifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
10
|
+
import { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
11
|
+
import { Compositor } from '../renderer/compositor.ts';
|
|
12
|
+
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
13
|
+
import type { SystemMessageRouter } from '../core/system-message-router.ts';
|
|
14
|
+
import type { ConversationFollowUpItem } from '@pellux/goodvibes-sdk/platform/core';
|
|
15
|
+
import type { OrchestratorUserInputOptions } from '../core/orchestrator.ts';
|
|
16
|
+
import type { ControlPlaneRecentEvent } from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
17
|
+
import type { MutableRuntimeState } from '@/runtime/index.ts';
|
|
18
|
+
import type { BootstrapOptions } from './context.ts';
|
|
19
|
+
import { createFeatureFlagManager } from '@/runtime/index.ts';
|
|
20
|
+
import { RuntimeEventBus } from '@/runtime/index.ts';
|
|
21
|
+
import type { SessionEvent } from '@/runtime/index.ts';
|
|
22
|
+
import { createRuntimeStore, createDomainDispatch, type RuntimeStore } from './store/index.ts';
|
|
23
|
+
import { ForensicsCollector, ForensicsRegistry } from '@/runtime/index.ts';
|
|
24
|
+
import {
|
|
25
|
+
generateUserSessionId,
|
|
26
|
+
} from '@/runtime/index.ts';
|
|
27
|
+
import { loadBootstrapSystemPrompt, syncConfiguredServices } from '@/runtime/index.ts';
|
|
28
|
+
import { registerBootstrapHookBridge } from '@/runtime/index.ts';
|
|
29
|
+
import { registerBootstrapRuntimeEvents } from '@/runtime/index.ts';
|
|
30
|
+
import { createRuntimeServices, type RuntimeServices } from './services.ts';
|
|
31
|
+
import { createUiRuntimeServices, type UiRuntimeServices } from './ui-services.ts';
|
|
32
|
+
import { installAgentToolPolicyGuard } from '../tools/wrfc-agent-guard.ts';
|
|
33
|
+
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
34
|
+
|
|
35
|
+
export interface BootstrapCoreState {
|
|
36
|
+
readonly userSessionId: string;
|
|
37
|
+
readonly runtimeBus: RuntimeEventBus;
|
|
38
|
+
readonly store: RuntimeStore;
|
|
39
|
+
readonly services: RuntimeServices;
|
|
40
|
+
readonly uiServices: UiRuntimeServices;
|
|
41
|
+
readonly conversation: ConversationManager;
|
|
42
|
+
readonly compositor: Compositor;
|
|
43
|
+
readonly selection: SelectionManager;
|
|
44
|
+
readonly toolRegistry: ToolRegistry;
|
|
45
|
+
readonly fileCache: ReturnType<typeof registerAllTools>['fileCache'];
|
|
46
|
+
readonly projectIndex: ReturnType<typeof registerAllTools>['projectIndex'];
|
|
47
|
+
readonly permissionManager: PermissionManager;
|
|
48
|
+
readonly forensicsCollector: ForensicsCollector;
|
|
49
|
+
readonly forensicsRegistry: ForensicsRegistry;
|
|
50
|
+
readonly runtime: MutableRuntimeState;
|
|
51
|
+
readonly bootstrapUnsubs: Array<() => void>;
|
|
52
|
+
readonly runtimeUnsubs: Array<() => void>;
|
|
53
|
+
readonly agentStatusIntervalRef: { value: ReturnType<typeof setInterval> | null };
|
|
54
|
+
readonly permissionPromptRef: { requestPermission: PermissionRequestHandler };
|
|
55
|
+
readonly systemMessageRouterRef: { value: SystemMessageRouter | null };
|
|
56
|
+
readonly conversationFollowUpRef: { value: ((item: ConversationFollowUpItem) => void) | null };
|
|
57
|
+
/**
|
|
58
|
+
* Mutable ref patched by bootstrap.ts after the Orchestrator is constructed.
|
|
59
|
+
* When non-null, COMPANION_MESSAGE_RECEIVED fires a real LLM turn via
|
|
60
|
+
* orchestrator.handleUserInput() instead of only appending the user message.
|
|
61
|
+
*/
|
|
62
|
+
readonly orchestratorHandleUserInputRef: { value: ((text: string, options?: OrchestratorUserInputOptions) => void) | null };
|
|
63
|
+
readonly requestRender: () => void;
|
|
64
|
+
readonly setRenderRequest: (fn: () => void) => void;
|
|
65
|
+
readonly runtimeSessionIdRef: { value: string };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type CompanionMessagePayload = Extract<SessionEvent, { type: 'COMPANION_MESSAGE_RECEIVED' }>;
|
|
69
|
+
|
|
70
|
+
export function companionMessageToOrchestratorInputOptions(
|
|
71
|
+
payload: CompanionMessagePayload,
|
|
72
|
+
): OrchestratorUserInputOptions {
|
|
73
|
+
const metadata = payload.metadata;
|
|
74
|
+
const surface = typeof metadata?.surface === 'string' ? metadata.surface : undefined;
|
|
75
|
+
const topic = typeof metadata?.topic === 'string' ? metadata.topic : undefined;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
origin: {
|
|
79
|
+
source: payload.source,
|
|
80
|
+
messageId: payload.messageId,
|
|
81
|
+
...(surface ? { surface } : {}),
|
|
82
|
+
...(topic ? { topic } : {}),
|
|
83
|
+
...(metadata ? { metadata } : {}),
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function initializeBootstrapCore(
|
|
89
|
+
stdout: NodeJS.WriteStream,
|
|
90
|
+
options: BootstrapOptions,
|
|
91
|
+
getControlPlaneRecentEvents: (limit: number) => readonly ControlPlaneRecentEvent[],
|
|
92
|
+
): Promise<BootstrapCoreState> {
|
|
93
|
+
const workingDir = options.workingDir;
|
|
94
|
+
const homeDirectory = options.homeDirectory;
|
|
95
|
+
const configManager = options.configManager;
|
|
96
|
+
|
|
97
|
+
const featureFlags = createFeatureFlagManager();
|
|
98
|
+
featureFlags.loadFromConfig({
|
|
99
|
+
flags: (configManager.getCategory('featureFlags') as Record<string, import('@/runtime/index.ts').FlagState>) ?? {},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const userSessionId = `user-${generateUserSessionId()}`;
|
|
103
|
+
const runtimeBus = new RuntimeEventBus();
|
|
104
|
+
const store = createRuntimeStore();
|
|
105
|
+
const domainDispatch = createDomainDispatch(store);
|
|
106
|
+
let getConversationTitle = (): string | undefined => undefined;
|
|
107
|
+
const services = createRuntimeServices({
|
|
108
|
+
configManager,
|
|
109
|
+
featureFlags,
|
|
110
|
+
runtimeBus,
|
|
111
|
+
runtimeStore: store,
|
|
112
|
+
getConversationTitle: () => getConversationTitle(),
|
|
113
|
+
workingDir,
|
|
114
|
+
homeDirectory,
|
|
115
|
+
});
|
|
116
|
+
const providerRegistry = services.providerRegistry;
|
|
117
|
+
providerRegistry.initModelLimits();
|
|
118
|
+
services.benchmarkStore.initBenchmarks();
|
|
119
|
+
providerRegistry.initCatalog();
|
|
120
|
+
services.keybindingsManager.loadFromDisk();
|
|
121
|
+
domainDispatch.syncControlPlaneState({
|
|
122
|
+
enabled: Boolean(configManager.get('controlPlane.enabled')),
|
|
123
|
+
host: String(configManager.get('controlPlane.host') ?? '127.0.0.1'),
|
|
124
|
+
port: Number(configManager.get('controlPlane.port') ?? 3421),
|
|
125
|
+
connectionState: configManager.get('controlPlane.enabled') ? 'connected' : 'disabled',
|
|
126
|
+
isRunning: Boolean(configManager.get('controlPlane.enabled')),
|
|
127
|
+
}, 'bootstrap.control-plane');
|
|
128
|
+
domainDispatch.syncControlPlaneClient({
|
|
129
|
+
id: 'client:tui',
|
|
130
|
+
kind: 'tui',
|
|
131
|
+
label: 'Terminal UI',
|
|
132
|
+
transport: 'local',
|
|
133
|
+
connected: true,
|
|
134
|
+
sessionId: userSessionId,
|
|
135
|
+
authenticatedAt: Date.now(),
|
|
136
|
+
lastSeenAt: Date.now(),
|
|
137
|
+
capabilities: ['session', 'panels', 'commands', 'automation'],
|
|
138
|
+
metadata: {},
|
|
139
|
+
}, 'bootstrap.control-plane');
|
|
140
|
+
|
|
141
|
+
const {
|
|
142
|
+
approvalBroker,
|
|
143
|
+
automationManager,
|
|
144
|
+
deliveryManager,
|
|
145
|
+
hookDispatcher,
|
|
146
|
+
hookWorkbench,
|
|
147
|
+
memoryStore,
|
|
148
|
+
panelManager,
|
|
149
|
+
routeBindings,
|
|
150
|
+
sessionBroker: sharedSessionBroker,
|
|
151
|
+
surfaceRegistry,
|
|
152
|
+
watcherRegistry,
|
|
153
|
+
} = services;
|
|
154
|
+
|
|
155
|
+
routeBindings.attachRuntime({ runtimeBus, runtimeStore: store });
|
|
156
|
+
surfaceRegistry.attachRuntime(store);
|
|
157
|
+
surfaceRegistry.syncConfiguredSurfaces();
|
|
158
|
+
watcherRegistry.attachRuntime({ runtimeBus, runtimeStore: store });
|
|
159
|
+
if (configManager.get('watchers.enabled')) {
|
|
160
|
+
watcherRegistry.registerPollingWatcher({
|
|
161
|
+
id: 'runtime-heartbeat',
|
|
162
|
+
label: 'Runtime heartbeat',
|
|
163
|
+
source: {
|
|
164
|
+
id: 'source:runtime-heartbeat',
|
|
165
|
+
kind: 'watcher',
|
|
166
|
+
label: 'Runtime heartbeat',
|
|
167
|
+
enabled: true,
|
|
168
|
+
createdAt: Date.now(),
|
|
169
|
+
updatedAt: Date.now(),
|
|
170
|
+
metadata: {},
|
|
171
|
+
},
|
|
172
|
+
intervalMs: Number(configManager.get('watchers.heartbeatIntervalMs') ?? 30_000),
|
|
173
|
+
run: () => new Date().toISOString(),
|
|
174
|
+
});
|
|
175
|
+
watcherRegistry.startWatcher('runtime-heartbeat');
|
|
176
|
+
}
|
|
177
|
+
automationManager.attachRuntime({ runtimeBus, runtimeStore: store, deliveryManager });
|
|
178
|
+
|
|
179
|
+
const forensicsRegistry = new ForensicsRegistry();
|
|
180
|
+
const forensicsCollector = new ForensicsCollector(runtimeBus, forensicsRegistry);
|
|
181
|
+
const policyRuntimeState = services.policyRuntimeState;
|
|
182
|
+
const uiServices = createUiRuntimeServices(services, {
|
|
183
|
+
forensicsRegistry,
|
|
184
|
+
getControlPlaneRecentEvents,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const conversation = new ConversationManager(() => {
|
|
188
|
+
const width = stdout.columns || 80;
|
|
189
|
+
if (panelManager.isVisible() && panelManager.getAllOpen().length > 0) {
|
|
190
|
+
return Math.max(1, panelManager.getLeftWidth(width) - 1);
|
|
191
|
+
}
|
|
192
|
+
return width;
|
|
193
|
+
});
|
|
194
|
+
conversation.setConfigManager(configManager);
|
|
195
|
+
getConversationTitle = () => conversation.title;
|
|
196
|
+
|
|
197
|
+
const compositor = new Compositor(stdout);
|
|
198
|
+
const selection = new SelectionManager();
|
|
199
|
+
|
|
200
|
+
const toolRegistry = new ToolRegistry();
|
|
201
|
+
const { fileCache, projectIndex } = registerAllTools(toolRegistry, {
|
|
202
|
+
surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
|
|
203
|
+
fileUndoManager: services.fileUndoManager,
|
|
204
|
+
modeManager: services.modeManager,
|
|
205
|
+
processManager: services.processManager,
|
|
206
|
+
agentManager: services.agentManager,
|
|
207
|
+
agentMessageBus: services.agentMessageBus,
|
|
208
|
+
archetypeLoader: services.archetypeLoader,
|
|
209
|
+
wrfcController: services.wrfcController,
|
|
210
|
+
webSearchService: services.webSearchService,
|
|
211
|
+
channelRegistry: services.channelPlugins,
|
|
212
|
+
remoteRunnerRegistry: services.remoteRunnerRegistry,
|
|
213
|
+
workflowServices: services.workflow,
|
|
214
|
+
mcpRegistry: services.mcpRegistry,
|
|
215
|
+
sessionOrchestration: services.sessionOrchestration,
|
|
216
|
+
sandboxSessionRegistry: services.sandboxSessionRegistry,
|
|
217
|
+
workingDirectory: services.workingDirectory,
|
|
218
|
+
configManager,
|
|
219
|
+
providerRegistry: services.providerRegistry,
|
|
220
|
+
toolLLM: services.toolLLM,
|
|
221
|
+
featureFlags: services.featureFlags,
|
|
222
|
+
serviceRegistry: services.serviceRegistry,
|
|
223
|
+
overflowHandler: services.overflowHandler,
|
|
224
|
+
changeTracker: services.sessionChangeTracker,
|
|
225
|
+
});
|
|
226
|
+
installAgentToolPolicyGuard(toolRegistry, {
|
|
227
|
+
getLastUserMessage: () => conversation.getLastUserMessage(),
|
|
228
|
+
});
|
|
229
|
+
services.agentOrchestrator.setDependencies({
|
|
230
|
+
surfaceRoot: GOODVIBES_AGENT_SURFACE_ROOT,
|
|
231
|
+
fileCache,
|
|
232
|
+
projectIndex,
|
|
233
|
+
workingDirectory: services.workingDirectory,
|
|
234
|
+
fileUndoManager: services.fileUndoManager,
|
|
235
|
+
modeManager: services.modeManager,
|
|
236
|
+
processManager: services.processManager,
|
|
237
|
+
agentMessageBus: services.agentMessageBus,
|
|
238
|
+
webSearchService: services.webSearchService,
|
|
239
|
+
channelRegistry: services.channelPlugins,
|
|
240
|
+
remoteRunnerRegistry: services.remoteRunnerRegistry,
|
|
241
|
+
knowledgeService: services.knowledgeService,
|
|
242
|
+
archetypeLoader: services.archetypeLoader,
|
|
243
|
+
configManager,
|
|
244
|
+
providerRegistry: services.providerRegistry,
|
|
245
|
+
providerOptimizer: services.providerOptimizer,
|
|
246
|
+
toolLLM: services.toolLLM,
|
|
247
|
+
serviceRegistry: services.serviceRegistry,
|
|
248
|
+
sessionOrchestration: services.sessionOrchestration,
|
|
249
|
+
featureFlags: services.featureFlags,
|
|
250
|
+
overflowHandler: services.overflowHandler,
|
|
251
|
+
memoryRegistry: services.memoryRegistry,
|
|
252
|
+
sandboxSessionRegistry: services.sandboxSessionRegistry,
|
|
253
|
+
workflowServices: services.workflow,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const bootstrapUnsubs: Array<() => void> = [];
|
|
257
|
+
await memoryStore.init();
|
|
258
|
+
bootstrapUnsubs.push(() => {
|
|
259
|
+
void memoryStore.save();
|
|
260
|
+
memoryStore.close();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const renderRequestRef = { value: (): void => {} };
|
|
264
|
+
// R1: Coalescing render scheduler — collapses N same-microtask requestRender() calls into 1.
|
|
265
|
+
// Also enforces a 16ms minimum interval to cap at ~60fps during streaming.
|
|
266
|
+
let renderScheduled = false;
|
|
267
|
+
let lastRenderTime = 0;
|
|
268
|
+
const RENDER_INTERVAL_MS = 16;
|
|
269
|
+
const requestRender = (): void => {
|
|
270
|
+
if (renderScheduled) return;
|
|
271
|
+
renderScheduled = true;
|
|
272
|
+
setImmediate(() => {
|
|
273
|
+
// Error Handling: the scheduler flag MUST be cleared even if the render
|
|
274
|
+
// callback throws; otherwise a single render exception would wedge the
|
|
275
|
+
// entire TUI (no future requestRender() call would schedule anything).
|
|
276
|
+
renderScheduled = false;
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
const elapsed = now - lastRenderTime;
|
|
279
|
+
try {
|
|
280
|
+
if (elapsed < RENDER_INTERVAL_MS) {
|
|
281
|
+
// Too soon — debounce to the tail of the current 16ms window
|
|
282
|
+
const delay = RENDER_INTERVAL_MS - elapsed;
|
|
283
|
+
setTimeout(() => {
|
|
284
|
+
try {
|
|
285
|
+
lastRenderTime = Date.now();
|
|
286
|
+
renderRequestRef.value();
|
|
287
|
+
} catch (err) {
|
|
288
|
+
// Throttled-render error: swallow but log at error so the next
|
|
289
|
+
// requestRender() call can still schedule. The renderer itself
|
|
290
|
+
// is expected to surface failures via its own error path.
|
|
291
|
+
logger.error('Throttled render threw; next requestRender will reschedule', { error: String(err) });
|
|
292
|
+
}
|
|
293
|
+
}, delay);
|
|
294
|
+
} else {
|
|
295
|
+
lastRenderTime = now;
|
|
296
|
+
renderRequestRef.value();
|
|
297
|
+
}
|
|
298
|
+
} catch (err) {
|
|
299
|
+
logger.error('Immediate render threw; next requestRender will reschedule', { error: String(err) });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
};
|
|
303
|
+
const permissionPromptRef = {
|
|
304
|
+
requestPermission: (async () => ({ approved: false, remember: false })) as PermissionRequestHandler,
|
|
305
|
+
};
|
|
306
|
+
void approvalBroker.start();
|
|
307
|
+
void sharedSessionBroker.start();
|
|
308
|
+
const runtimeSessionIdRef = { value: userSessionId };
|
|
309
|
+
const systemMessageRouterRef: { value: SystemMessageRouter | null } = { value: null };
|
|
310
|
+
const conversationFollowUpRef: { value: ((item: ConversationFollowUpItem) => void) | null } = { value: null };
|
|
311
|
+
const { unsubs: runtimeUnsubs, agentStatusIntervalRef } = registerBootstrapRuntimeEvents({
|
|
312
|
+
runtimeBus,
|
|
313
|
+
domainDispatch,
|
|
314
|
+
getSystemMessageRouter: () => systemMessageRouterRef.value,
|
|
315
|
+
queueConversationFollowUp: (item) => conversationFollowUpRef.value?.(item),
|
|
316
|
+
requestRender,
|
|
317
|
+
configManager,
|
|
318
|
+
agentManager: services.agentManager,
|
|
319
|
+
wrfcController: services.wrfcController,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// ── TUI-specific WRFC constraint-propagation event subscriptions (SDK 0.23.0) ──
|
|
323
|
+
// These supplement the SDK's registerBootstrapRuntimeEvents which handles the
|
|
324
|
+
// core WORKFLOW_REVIEW_COMPLETED / WORKFLOW_CHAIN_CREATED messages.
|
|
325
|
+
// The SDK does not surface constraint-specific system messages; the TUI layer
|
|
326
|
+
// adds them here so operators can observe constraint enumeration and violations
|
|
327
|
+
// in the SystemMessagesPanel and main conversation.
|
|
328
|
+
runtimeUnsubs.push(
|
|
329
|
+
runtimeBus.on<Extract<import('@/runtime/index.ts').WorkflowEvent, { type: 'WORKFLOW_CONSTRAINTS_ENUMERATED' }>>(
|
|
330
|
+
'WORKFLOW_CONSTRAINTS_ENUMERATED',
|
|
331
|
+
({ payload }) => {
|
|
332
|
+
const router = systemMessageRouterRef.value;
|
|
333
|
+
if (!router) return;
|
|
334
|
+
const count = payload.constraints.length;
|
|
335
|
+
if (count > 0) {
|
|
336
|
+
router.wrfc(
|
|
337
|
+
`[WRFC] Engineer enumerated ${count} constraint${count !== 1 ? 's' : ''} for chain ${payload.chainId.slice(0, 12)}`,
|
|
338
|
+
'low',
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
requestRender();
|
|
342
|
+
},
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
runtimeUnsubs.push(
|
|
346
|
+
runtimeBus.on<Extract<import('@/runtime/index.ts').WorkflowEvent, { type: 'WORKFLOW_FIX_ATTEMPTED' }>>(
|
|
347
|
+
'WORKFLOW_FIX_ATTEMPTED',
|
|
348
|
+
({ payload }) => {
|
|
349
|
+
const router = systemMessageRouterRef.value;
|
|
350
|
+
if (!router) return;
|
|
351
|
+
const targetIds = payload.targetConstraintIds;
|
|
352
|
+
if (targetIds && targetIds.length > 0) {
|
|
353
|
+
router.wrfc(
|
|
354
|
+
`[WRFC] Fix #${payload.attempt} targeting ${targetIds.length} constraint${targetIds.length !== 1 ? 's' : ''} on chain ${payload.chainId.slice(0, 12)}`,
|
|
355
|
+
'low',
|
|
356
|
+
);
|
|
357
|
+
requestRender();
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
),
|
|
361
|
+
);
|
|
362
|
+
runtimeUnsubs.push(
|
|
363
|
+
runtimeBus.on<Extract<import('@/runtime/index.ts').WorkflowEvent, { type: 'WORKFLOW_REVIEW_COMPLETED' }>>(
|
|
364
|
+
'WORKFLOW_REVIEW_COMPLETED',
|
|
365
|
+
({ payload }) => {
|
|
366
|
+
const router = systemMessageRouterRef.value;
|
|
367
|
+
if (!router) return;
|
|
368
|
+
const unsatisfied = payload.unsatisfiedConstraintIds;
|
|
369
|
+
if (!payload.passed && unsatisfied && unsatisfied.length > 0) {
|
|
370
|
+
router.wrfc(
|
|
371
|
+
`[WRFC] ✗ Chain ${payload.chainId.slice(0, 12)}: ${unsatisfied.length} constraint violation${unsatisfied.length !== 1 ? 's' : ''} forced failure`,
|
|
372
|
+
'high',
|
|
373
|
+
);
|
|
374
|
+
requestRender();
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// Subscribe to companion main-chat messages received from the daemon's HTTP layer.
|
|
381
|
+
// The daemon emits COMPANION_MESSAGE_RECEIVED on the runtime bus when a companion
|
|
382
|
+
// POST /api/sessions/:id/messages with kind='message' arrives.
|
|
383
|
+
//
|
|
384
|
+
// bootstrap.ts patches orchestratorHandleUserInputRef.value after the Orchestrator
|
|
385
|
+
// is constructed. When that ref is set, we delegate to orchestrator.handleUserInput()
|
|
386
|
+
// which (a) adds the user message to the conversation view and (b) fires a real LLM
|
|
387
|
+
// turn whose STREAM_DELTA / TURN_COMPLETED events flow to both TUI and companion SSE.
|
|
388
|
+
//
|
|
389
|
+
// The fallback (ref not yet set) adds the message to the conversation view only —
|
|
390
|
+
// this path is unreachable in practice because the event bus is not connected to
|
|
391
|
+
// any live HTTP traffic until after the orchestrator is wired in bootstrap.ts.
|
|
392
|
+
const orchestratorHandleUserInputRef: {
|
|
393
|
+
value: ((text: string, options?: OrchestratorUserInputOptions) => void) | null;
|
|
394
|
+
} = { value: null };
|
|
395
|
+
runtimeUnsubs.push(runtimeBus.on<Extract<SessionEvent, { type: 'COMPANION_MESSAGE_RECEIVED' }>>(
|
|
396
|
+
'COMPANION_MESSAGE_RECEIVED',
|
|
397
|
+
({ payload }) => {
|
|
398
|
+
if (orchestratorHandleUserInputRef.value) {
|
|
399
|
+
// Delegate to the orchestrator: adds user message + fires a real LLM turn.
|
|
400
|
+
// Preserve surface origin metadata so the SDK can correlate replies back
|
|
401
|
+
// to the originating external channel, including ntfy chat topics.
|
|
402
|
+
orchestratorHandleUserInputRef.value(payload.body, companionMessageToOrchestratorInputOptions(payload));
|
|
403
|
+
} else {
|
|
404
|
+
// Fallback: render the user message immediately (orchestrator not yet ready).
|
|
405
|
+
conversation.addUserMessage(payload.body);
|
|
406
|
+
requestRender();
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
));
|
|
410
|
+
|
|
411
|
+
providerRegistry.startWatching(runtimeBus);
|
|
412
|
+
|
|
413
|
+
const webhookUrls = (configManager.getCategory('notifications') as { webhookUrls?: string[] }).webhookUrls ?? [];
|
|
414
|
+
if (webhookUrls.length > 0) {
|
|
415
|
+
const webhookNotifier = WebhookNotifier.fromConfig(webhookUrls);
|
|
416
|
+
webhookNotifier.attachToRuntimeBus(runtimeBus);
|
|
417
|
+
domainDispatch.syncIntegration({
|
|
418
|
+
id: 'webhooks',
|
|
419
|
+
displayName: 'Webhooks',
|
|
420
|
+
category: 'communication',
|
|
421
|
+
status: 'healthy',
|
|
422
|
+
enabled: true,
|
|
423
|
+
successCount: 0,
|
|
424
|
+
errorCount: 0,
|
|
425
|
+
meta: { urlCount: webhookUrls.length },
|
|
426
|
+
}, 'bootstrap.webhooks');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const notifier = await Notifier.fromConfig(services.serviceRegistry);
|
|
430
|
+
const queueStatuses = notifier.getQueueStatus();
|
|
431
|
+
if (queueStatuses.length > 0) {
|
|
432
|
+
notifier.attachToRuntimeBus(runtimeBus);
|
|
433
|
+
for (const queueStatus of queueStatuses) {
|
|
434
|
+
domainDispatch.syncIntegration({
|
|
435
|
+
id: queueStatus.channel,
|
|
436
|
+
displayName: queueStatus.channel[0]!.toUpperCase() + queueStatus.channel.slice(1),
|
|
437
|
+
category: 'communication',
|
|
438
|
+
status: queueStatus.metrics.deadLettered > 0 ? 'degraded' : 'healthy',
|
|
439
|
+
enabled: true,
|
|
440
|
+
successCount: queueStatus.metrics.delivered,
|
|
441
|
+
errorCount: queueStatus.metrics.deadLettered,
|
|
442
|
+
...(queueStatus.dlqEntries[0]?.deadAt ? { lastErrorAt: queueStatus.dlqEntries[0].deadAt } : {}),
|
|
443
|
+
...(queueStatus.dlqEntries[0]?.finalError ? { lastError: queueStatus.dlqEntries[0].finalError } : {}),
|
|
444
|
+
meta: {
|
|
445
|
+
attempts: queueStatus.metrics.totalAttempts,
|
|
446
|
+
retrying: queueStatus.metrics.retrying,
|
|
447
|
+
deadLetters: queueStatus.metrics.deadLettered,
|
|
448
|
+
dlqSize: queueStatus.metrics.dlqSize,
|
|
449
|
+
sloEnforced: queueStatus.sloEnforced,
|
|
450
|
+
},
|
|
451
|
+
}, 'bootstrap.notifier');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
await syncConfiguredServices(domainDispatch.syncIntegration, services.serviceRegistry);
|
|
456
|
+
|
|
457
|
+
const permissionManager = new PermissionManager(
|
|
458
|
+
(request) => approvalBroker.requestApproval({
|
|
459
|
+
request,
|
|
460
|
+
sessionId: runtimeSessionIdRef.value,
|
|
461
|
+
localPrompt: permissionPromptRef.requestPermission,
|
|
462
|
+
}),
|
|
463
|
+
createPermissionConfigReader(configManager),
|
|
464
|
+
policyRuntimeState,
|
|
465
|
+
services.hookDispatcher,
|
|
466
|
+
featureFlags,
|
|
467
|
+
);
|
|
468
|
+
await hookWorkbench.loadAndApplyManagedHooks();
|
|
469
|
+
|
|
470
|
+
const runtime: MutableRuntimeState = {
|
|
471
|
+
model: configManager.get('provider.model') as string,
|
|
472
|
+
provider: getProviderIdFromModel(configManager.get('provider.model')),
|
|
473
|
+
debugMode: false,
|
|
474
|
+
systemPrompt: loadBootstrapSystemPrompt(configManager) || getConfiguredSystemPrompt(configManager) || '',
|
|
475
|
+
reasoningEffort: (configManager.get('provider.reasoningEffort') as string | undefined) ?? '',
|
|
476
|
+
sessionId: userSessionId,
|
|
477
|
+
};
|
|
478
|
+
runtimeSessionIdRef.value = runtime.sessionId;
|
|
479
|
+
void sharedSessionBroker.createSession({
|
|
480
|
+
id: runtime.sessionId,
|
|
481
|
+
title: 'GoodVibes Agent session',
|
|
482
|
+
metadata: { source: 'goodvibes-agent' },
|
|
483
|
+
participant: {
|
|
484
|
+
surfaceKind: 'service',
|
|
485
|
+
surfaceId: 'surface:goodvibes-agent',
|
|
486
|
+
displayName: 'GoodVibes Agent',
|
|
487
|
+
lastSeenAt: Date.now(),
|
|
488
|
+
},
|
|
489
|
+
}).catch((err) => { logger.debug('session broker create session failed at bootstrap', { err }); });
|
|
490
|
+
|
|
491
|
+
domainDispatch.syncSessionState({
|
|
492
|
+
id: userSessionId,
|
|
493
|
+
projectRoot: workingDir,
|
|
494
|
+
status: 'active',
|
|
495
|
+
startedAt: Date.now(),
|
|
496
|
+
recoveryState: 'ready',
|
|
497
|
+
isResumed: false,
|
|
498
|
+
wasRepaired: false,
|
|
499
|
+
lineageId: userSessionId,
|
|
500
|
+
lineage: [{ sessionId: userSessionId, createdAt: Date.now() }],
|
|
501
|
+
}, 'bootstrap.session');
|
|
502
|
+
|
|
503
|
+
runtimeUnsubs.push(
|
|
504
|
+
...registerBootstrapHookBridge({
|
|
505
|
+
runtimeBus,
|
|
506
|
+
hookDispatcher,
|
|
507
|
+
runtime,
|
|
508
|
+
}),
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
userSessionId,
|
|
513
|
+
runtimeBus,
|
|
514
|
+
store,
|
|
515
|
+
services,
|
|
516
|
+
uiServices,
|
|
517
|
+
conversation,
|
|
518
|
+
compositor,
|
|
519
|
+
selection,
|
|
520
|
+
toolRegistry,
|
|
521
|
+
fileCache,
|
|
522
|
+
projectIndex,
|
|
523
|
+
permissionManager,
|
|
524
|
+
forensicsCollector,
|
|
525
|
+
forensicsRegistry,
|
|
526
|
+
runtime,
|
|
527
|
+
bootstrapUnsubs,
|
|
528
|
+
runtimeUnsubs,
|
|
529
|
+
agentStatusIntervalRef,
|
|
530
|
+
permissionPromptRef,
|
|
531
|
+
systemMessageRouterRef,
|
|
532
|
+
conversationFollowUpRef,
|
|
533
|
+
orchestratorHandleUserInputRef,
|
|
534
|
+
requestRender,
|
|
535
|
+
setRenderRequest: (fn) => {
|
|
536
|
+
renderRequestRef.value = fn;
|
|
537
|
+
},
|
|
538
|
+
runtimeSessionIdRef,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ConversationManager } from '../core/conversation';
|
|
2
|
+
import type { HookDispatcher } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
3
|
+
import type { MutableRuntimeState } from '@/runtime/index.ts';
|
|
4
|
+
import { registerBootstrapHookBridge } from '@/runtime/index.ts';
|
|
5
|
+
import type { RuntimeEventBus } from '@/runtime/index.ts';
|
|
6
|
+
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
7
|
+
import { emitSessionResumed } from '@/runtime/index.ts';
|
|
8
|
+
import { HelperModel } from '@pellux/goodvibes-sdk/platform/config';
|
|
9
|
+
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
10
|
+
import { formatReturnContextForDisplay, getReturnContextMode, maybeAssistReturnContextSummary } from '@/runtime/index.ts';
|
|
11
|
+
import type { SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
12
|
+
import type { SessionManager } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
13
|
+
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
14
|
+
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
15
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
16
|
+
|
|
17
|
+
export interface ResumeSessionOptions {
|
|
18
|
+
readonly runtimeBus: RuntimeEventBus;
|
|
19
|
+
readonly runtime: MutableRuntimeState;
|
|
20
|
+
readonly conversation: ConversationManager;
|
|
21
|
+
readonly requestRender: () => void;
|
|
22
|
+
readonly onSessionIdChanged?: (sessionId: string) => void;
|
|
23
|
+
readonly sharedSessionBroker: Pick<SharedSessionBroker, 'reopenSession'>;
|
|
24
|
+
readonly writeLastSessionPointer: (sessionId: string) => void;
|
|
25
|
+
readonly hookDispatcher: HookDispatcher;
|
|
26
|
+
readonly sessionManager: SessionManager;
|
|
27
|
+
readonly panelManager: PanelManager;
|
|
28
|
+
readonly configManager: Pick<ConfigManager, 'get' | 'getCategory'>;
|
|
29
|
+
readonly providerRegistry: Pick<ProviderRegistry, 'get' | 'getCurrentModel' | 'getForModel' | 'require'>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createResumeSessionHandler(options: ResumeSessionOptions): (sessionId: string) => void {
|
|
33
|
+
return (sessionId: string): void => {
|
|
34
|
+
try {
|
|
35
|
+
const { messages, meta } = options.sessionManager.load(sessionId);
|
|
36
|
+
emitSessionResumed(options.runtimeBus, {
|
|
37
|
+
sessionId: options.runtime.sessionId,
|
|
38
|
+
traceId: `${options.runtime.sessionId}:session-resume:${sessionId}`,
|
|
39
|
+
source: 'bootstrap',
|
|
40
|
+
}, {
|
|
41
|
+
sessionId,
|
|
42
|
+
turnCount: messages.length,
|
|
43
|
+
});
|
|
44
|
+
options.conversation.fromJSON({
|
|
45
|
+
messages: messages as Parameters<typeof options.conversation.fromJSON>[0]['messages'],
|
|
46
|
+
title: meta.title,
|
|
47
|
+
titleSource: meta.titleSource,
|
|
48
|
+
});
|
|
49
|
+
options.runtime.sessionId = sessionId;
|
|
50
|
+
options.onSessionIdChanged?.(sessionId);
|
|
51
|
+
if (meta?.model) options.runtime.model = meta.model;
|
|
52
|
+
if (meta?.provider) options.runtime.provider = meta.provider;
|
|
53
|
+
options.writeLastSessionPointer(sessionId);
|
|
54
|
+
void options.sharedSessionBroker.reopenSession(sessionId).catch((err) => { logger.debug('session broker reopen session failed', { err }); });
|
|
55
|
+
options.conversation.log(`Resumed session: ${sessionId}`, { fg: '135' });
|
|
56
|
+
const reopenedPanels: string[] = [];
|
|
57
|
+
if (meta.returnContext?.openPanels?.length) {
|
|
58
|
+
for (const panelId of meta.returnContext.openPanels.slice(0, 4)) {
|
|
59
|
+
try {
|
|
60
|
+
options.panelManager.open(panelId);
|
|
61
|
+
reopenedPanels.push(panelId);
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore unavailable panels during restore.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (reopenedPanels.length > 0) options.panelManager.show();
|
|
67
|
+
}
|
|
68
|
+
const returnContextMode = getReturnContextMode(options.configManager);
|
|
69
|
+
if (returnContextMode !== 'off' && meta.returnContext) {
|
|
70
|
+
for (const line of formatReturnContextForDisplay(meta.returnContext)) {
|
|
71
|
+
options.conversation.log(`Resume: ${line}`, { fg: '244' });
|
|
72
|
+
}
|
|
73
|
+
if (reopenedPanels.length > 0) {
|
|
74
|
+
options.conversation.log(`Resume: Reopened panels: ${reopenedPanels.join(', ')}`, { fg: '244' });
|
|
75
|
+
}
|
|
76
|
+
if ((meta.returnContext.remoteRunners?.length ?? 0) > 0) {
|
|
77
|
+
options.conversation.log(`Resume: Remote re-entry -> /remote recover ${meta.returnContext.remoteRunners![0]}`, { fg: '244' });
|
|
78
|
+
}
|
|
79
|
+
if ((meta.returnContext.worktreePaths?.length ?? 0) > 0) {
|
|
80
|
+
options.conversation.log('Resume: Worktree re-entry -> /worktree review', { fg: '244' });
|
|
81
|
+
}
|
|
82
|
+
if (returnContextMode === 'assisted') {
|
|
83
|
+
const helperModel = new HelperModel({
|
|
84
|
+
configManager: options.configManager,
|
|
85
|
+
providerRegistry: options.providerRegistry,
|
|
86
|
+
});
|
|
87
|
+
void maybeAssistReturnContextSummary(options.configManager, helperModel, meta.returnContext).then((assisted) => {
|
|
88
|
+
if (!assisted.assistedNarrative) return;
|
|
89
|
+
options.conversation.log(`Resume: ${assisted.assistedNarrative}`, { fg: '244' });
|
|
90
|
+
options.requestRender();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
options.hookDispatcher.fire({
|
|
95
|
+
path: 'Lifecycle:session:load',
|
|
96
|
+
phase: 'Lifecycle',
|
|
97
|
+
category: 'session',
|
|
98
|
+
specific: 'load',
|
|
99
|
+
sessionId: options.runtime.sessionId,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
payload: { sessionId },
|
|
102
|
+
}).catch((err: unknown) => logger.debug('Hook bridge fire error', {
|
|
103
|
+
path: 'Lifecycle:session:load',
|
|
104
|
+
error: summarizeError(err),
|
|
105
|
+
}));
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.debug('resumeSession failed', { error: summarizeError(error) });
|
|
108
|
+
options.conversation.log('Failed to resume session.', { fg: '#ef4444' });
|
|
109
|
+
}
|
|
110
|
+
options.requestRender();
|
|
111
|
+
};
|
|
112
|
+
}
|