@pellux/goodvibes-tui 1.1.0 → 1.9.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/CHANGELOG.md +85 -21
- package/README.md +28 -18
- package/docs/foundation-artifacts/operator-contract.json +4045 -1763
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +12 -2
- package/src/audio/spoken-turn-wiring.ts +2 -1
- package/src/cli/help.ts +8 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/cli/status.ts +2 -1
- package/src/cli/surface-command.ts +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/composer-state.ts +11 -3
- package/src/core/conversation-line-cache.ts +27 -3
- package/src/core/conversation-rendering.ts +90 -14
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/stream-event-wiring.ts +20 -1
- package/src/core/system-message-noise.ts +87 -0
- package/src/core/system-message-router.ts +104 -51
- package/src/core/turn-cancellation.ts +25 -0
- package/src/core/turn-event-wiring.ts +72 -3
- package/src/daemon/cli.ts +29 -2
- package/src/daemon/handlers/register.ts +8 -1
- package/src/daemon/service-commands.ts +329 -0
- package/src/export/cost-utils.ts +36 -0
- package/src/input/autocomplete.ts +27 -1
- package/src/input/command-registry.ts +84 -5
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +232 -0
- package/src/input/commands/config.ts +43 -3
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +10 -2
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +73 -35
- package/src/input/commands/operator-panel-runtime.ts +71 -31
- package/src/input/commands/planning-runtime.ts +116 -4
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +26 -6
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime-setup.ts +5 -3
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +20 -9
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +16 -4
- package/src/input/commands/shell-core.ts +25 -8
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +488 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +175 -0
- package/src/input/config-modal.ts +592 -0
- package/src/input/feed-context-factory.ts +10 -8
- package/src/input/handler-command-route.ts +37 -20
- package/src/input/handler-content-actions.ts +17 -2
- package/src/input/handler-feed-routes.ts +74 -114
- package/src/input/handler-feed.ts +79 -27
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +136 -6
- package/src/input/handler-modal-stack.ts +14 -7
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
- package/src/input/handler-onboarding.ts +71 -59
- package/src/input/handler-picker-routes.ts +26 -102
- package/src/input/handler-shortcuts.ts +74 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +12 -29
- package/src/input/keybindings.ts +23 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +77 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/panel-mouse-geometry.ts +97 -0
- package/src/input/panel-paste-flood-guard.ts +86 -0
- package/src/input/selection-modal.ts +11 -0
- package/src/input/session-picker-modal.ts +44 -4
- package/src/input/settings-modal-data.ts +146 -2
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/input/settings-modal-types.ts +4 -2
- package/src/main.ts +49 -51
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +18 -46
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-deep-link.ts +31 -0
- package/src/panels/fleet-panel-format.ts +62 -0
- package/src/panels/fleet-panel-worktree-detail.ts +48 -0
- package/src/panels/fleet-panel.ts +758 -0
- package/src/panels/fleet-read-model.ts +520 -0
- package/src/panels/fleet-steer.ts +125 -0
- package/src/panels/fleet-stop.ts +153 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +166 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +42 -0
- package/src/panels/modals/pairing-modal.ts +163 -0
- package/src/panels/modals/planning-modal.ts +304 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +177 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +123 -9
- package/src/panels/polish-core.ts +38 -25
- package/src/panels/project-planning-answer-actions.ts +8 -11
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +37 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +216 -13
- package/src/renderer/autocomplete-overlay.ts +28 -2
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/compositor.ts +2 -3
- package/src/renderer/config-modal.ts +95 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +15 -3
- package/src/renderer/fullscreen-primitives.ts +32 -22
- package/src/renderer/git-status.ts +43 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/layout.ts +0 -4
- package/src/renderer/markdown.ts +7 -3
- package/src/renderer/modal-factory.ts +25 -20
- package/src/renderer/model-workspace.ts +62 -4
- package/src/renderer/overlay-box.ts +21 -17
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/process-indicator.ts +14 -3
- package/src/renderer/selection-modal-overlay.ts +6 -1
- package/src/renderer/session-picker-modal.ts +196 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +7 -0
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/status-glyphs.ts +14 -15
- package/src/renderer/system-message.ts +15 -3
- package/src/renderer/terminal-bg-probe.ts +339 -0
- package/src/renderer/terminal-escapes.ts +20 -0
- package/src/renderer/theme-mode-config.ts +67 -0
- package/src/renderer/theme.ts +91 -1
- package/src/renderer/thinking.ts +11 -3
- package/src/renderer/tool-call.ts +15 -9
- package/src/renderer/tool-result-summary.ts +148 -0
- package/src/renderer/turn-injection.ts +133 -0
- package/src/renderer/ui-factory.ts +154 -85
- package/src/renderer/ui-primitives.ts +30 -129
- package/src/runtime/bootstrap-command-context.ts +27 -1
- package/src/runtime/bootstrap-command-parts.ts +41 -10
- package/src/runtime/bootstrap-core.ts +47 -15
- package/src/runtime/bootstrap-hook-bridge.ts +7 -0
- package/src/runtime/bootstrap-shell.ts +48 -17
- package/src/runtime/bootstrap.ts +129 -22
- package/src/runtime/code-index-services.ts +135 -0
- package/src/runtime/legacy-daemon-migration.ts +516 -0
- package/src/runtime/memory-fold.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +7 -2
- package/src/runtime/onboarding/snapshot.ts +27 -1
- package/src/runtime/onboarding/types.ts +31 -1
- package/src/runtime/operator-token-cleanup.ts +82 -1
- package/src/runtime/orchestrator-core-services.ts +59 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/resume-notice.ts +209 -0
- package/src/runtime/services.ts +98 -46
- package/src/runtime/session-inbound-inputs.ts +252 -0
- package/src/runtime/session-spine-transport.ts +64 -0
- package/src/runtime/terminal-output-guard.ts +15 -8
- package/src/runtime/ui-services.ts +27 -3
- package/src/runtime/workstream-services.ts +327 -0
- package/src/runtime/wrfc-persistence.ts +124 -17
- package/src/shell/blocking-input.ts +68 -4
- package/src/shell/recovery-input-helpers.ts +170 -1
- package/src/shell/ui-openers.ts +171 -26
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +52 -0
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
package/src/runtime/services.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { FocusTracker } from '../core/focus-tracker.ts';
|
|
3
|
+
import { ConfigManager, ServiceRegistry, SubscriptionManager, ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
|
|
3
4
|
import { SecretsManager } from '../config/secrets.ts';
|
|
4
|
-
import { ServiceRegistry } from '@pellux/goodvibes-sdk/platform/config';
|
|
5
|
-
import { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
6
5
|
import { AutomationDeliveryManager, AutomationManager, AutomationRouteStore } from '@pellux/goodvibes-sdk/platform/automation';
|
|
7
|
-
import { ChannelPluginRegistry, ChannelPolicyManager, RouteBindingManager, SurfaceRegistry } from '@pellux/goodvibes-sdk/platform/channels';
|
|
8
|
-
import { ChannelDeliveryRouter } from '@pellux/goodvibes-sdk/platform/channels';
|
|
6
|
+
import { ChannelDeliveryRouter, ChannelPluginRegistry, ChannelPolicyManager, RouteBindingManager, SurfaceRegistry } from '@pellux/goodvibes-sdk/platform/channels';
|
|
9
7
|
import { ApprovalBroker, GatewayMethodCatalog, SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
10
8
|
import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
|
|
11
9
|
import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
@@ -23,16 +21,10 @@ import {
|
|
|
23
21
|
} from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
24
22
|
import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
|
|
25
23
|
import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import { ArchetypeLoader } from '@pellux/goodvibes-sdk/platform/agents';
|
|
31
|
-
import { ProcessManager } from '@pellux/goodvibes-sdk/platform/tools';
|
|
32
|
-
import { ModeManager } from '@pellux/goodvibes-sdk/platform/state';
|
|
33
|
-
import { FileUndoManager } from '@pellux/goodvibes-sdk/platform/state';
|
|
34
|
-
import { MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
35
|
-
import { MemoryStore } from '@pellux/goodvibes-sdk/platform/state';
|
|
24
|
+
import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
|
|
25
|
+
import { AgentManager, OverflowHandler, ProcessManager, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
|
|
26
|
+
import { FileStateCache, FileUndoManager, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, resolveCanonicalMemoryDbPath, type CodeIndexStore, type CodeIndexReindexScheduler } from '@pellux/goodvibes-sdk/platform/state';
|
|
27
|
+
import { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
|
|
36
28
|
import type { RuntimeEventBus } from '@/runtime/index.ts';
|
|
37
29
|
import { createDomainDispatch } from './store/index.ts';
|
|
38
30
|
import type { DomainDispatch, RuntimeStore } from './store/index.ts';
|
|
@@ -41,40 +33,20 @@ import { RemoteRunnerRegistry, RemoteSupervisor } from '@/runtime/index.ts';
|
|
|
41
33
|
import { IntegrationHelperService } from '@/runtime/index.ts';
|
|
42
34
|
import { VoiceProviderRegistry, VoiceService, ensureBuiltinVoiceProviders } from '@pellux/goodvibes-sdk/platform/voice';
|
|
43
35
|
import { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
|
|
44
|
-
import { MemoryEmbeddingProviderRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
45
36
|
import { PanelManager } from '../panels/panel-manager.ts';
|
|
46
37
|
import { HookActivityTracker } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
47
38
|
import { HookDispatcher, createHookWorkbench, type HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
48
39
|
import { PluginManager } from '@pellux/goodvibes-sdk/platform/plugins';
|
|
49
40
|
import { BookmarkManager } from '@pellux/goodvibes-sdk/platform/bookmarks';
|
|
50
41
|
import { ProfileManager } from '@pellux/goodvibes-sdk/platform/profiles';
|
|
51
|
-
import { SessionManager } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
52
|
-
import {
|
|
53
|
-
import { ApiTokenAuditor } from '@pellux/goodvibes-sdk/platform/security';
|
|
54
|
-
import { UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
|
|
42
|
+
import { SessionManager, CrossSessionTaskRegistry, SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
43
|
+
import { ApiTokenAuditor, UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
|
|
55
44
|
import { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
56
45
|
import { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
57
|
-
import {
|
|
58
|
-
import { ProviderOptimizer } from '@pellux/goodvibes-sdk/platform/providers';
|
|
59
|
-
import { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
60
|
-
import type { ModelDefinition } from '@pellux/goodvibes-sdk/platform/providers';
|
|
61
|
-
import { ProviderCapabilityRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
62
|
-
import { CacheHitTracker } from '@pellux/goodvibes-sdk/platform/providers';
|
|
63
|
-
import { FavoritesStore } from '@pellux/goodvibes-sdk/platform/providers';
|
|
64
|
-
import { BenchmarkStore } from '@pellux/goodvibes-sdk/platform/providers';
|
|
65
|
-
import { ModelLimitsService } from '@pellux/goodvibes-sdk/platform/providers';
|
|
66
|
-
import { inferFallbackContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
46
|
+
import { BenchmarkStore, CacheHitTracker, FavoritesStore, inferFallbackContextWindow, type ModelDefinition, ModelLimitsService, ProviderCapabilityRegistry, ProviderOptimizer, ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
67
47
|
import { KeybindingsManager } from '../input/keybindings.ts';
|
|
68
|
-
import { SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
|
|
69
|
-
import { SessionLineageTracker } from '@pellux/goodvibes-sdk/platform/core';
|
|
70
|
-
import { SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
71
|
-
import { ExecutionPlanManager } from '@pellux/goodvibes-sdk/platform/core';
|
|
72
|
-
import { AdaptivePlanner } from '@pellux/goodvibes-sdk/platform/core';
|
|
73
|
-
import { FileStateCache } from '@pellux/goodvibes-sdk/platform/state';
|
|
74
|
-
import { ProjectIndex } from '@pellux/goodvibes-sdk/platform/state';
|
|
48
|
+
import { AdaptivePlanner, DeterministicReplayEngine, ExecutionPlanManager, SessionLineageTracker, SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
|
|
75
49
|
import { IdempotencyStore } from '@/runtime/index.ts';
|
|
76
|
-
import { OverflowHandler } from '@pellux/goodvibes-sdk/platform/tools';
|
|
77
|
-
import { ToolLLM } from '@pellux/goodvibes-sdk/platform/config';
|
|
78
50
|
import { ComponentHealthMonitor } from '@/runtime/index.ts';
|
|
79
51
|
import { WorktreeRegistry } from '@/runtime/index.ts';
|
|
80
52
|
import { SandboxSessionRegistry } from '@/runtime/index.ts';
|
|
@@ -83,10 +55,10 @@ import { isFeatureFlagEnabled } from './surface-feature-flags.ts';
|
|
|
83
55
|
import type { FeatureFlagManager } from '@/runtime/index.ts';
|
|
84
56
|
import { createFeatureFlagManager } from '@/runtime/index.ts';
|
|
85
57
|
import { PolicyRuntimeState } from '@/runtime/index.ts';
|
|
86
|
-
import {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} from '
|
|
58
|
+
import { createProcessRegistry, type ProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
59
|
+
import { calcSessionCost, isModelPriced } from '../export/cost-utils.ts';
|
|
60
|
+
import { createWorkstreamServices, type OrchestrationEngine, type WorkstreamCommandService } from './workstream-services.ts';
|
|
61
|
+
import { codeIndexDbPath, createCodeIndexServices, isCodeInjectionSettingEnabled } from './code-index-services.ts';
|
|
90
62
|
import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
|
|
91
63
|
import {
|
|
92
64
|
registerDaemonHandlers,
|
|
@@ -221,6 +193,8 @@ export interface RuntimeServices {
|
|
|
221
193
|
readonly worktreeRegistry: WorktreeRegistry;
|
|
222
194
|
readonly sandboxSessionRegistry: SandboxSessionRegistry;
|
|
223
195
|
readonly webhookNotifier: WebhookNotifier;
|
|
196
|
+
/** Terminal focus tracker (W2.3) — fed by input/handler-feed.ts, read by the alert notifiers in core/. */
|
|
197
|
+
readonly focusTracker: FocusTracker;
|
|
224
198
|
readonly replayEngine: DeterministicReplayEngine;
|
|
225
199
|
readonly providerOptimizer: ProviderOptimizer;
|
|
226
200
|
readonly providerCapabilityRegistry: ProviderCapabilityRegistry;
|
|
@@ -248,8 +222,17 @@ export interface RuntimeServices {
|
|
|
248
222
|
readonly agentOrchestrator: AgentOrchestrator;
|
|
249
223
|
readonly wrfcController: WrfcController;
|
|
250
224
|
readonly processManager: ProcessManager;
|
|
225
|
+
/** Wave 4 (wo703): the phase/work-item orchestration engine — see runtime/workstream-services.ts. */
|
|
226
|
+
readonly orchestrationEngine: OrchestrationEngine;
|
|
227
|
+
readonly workstreamCommands: WorkstreamCommandService;
|
|
228
|
+
/** Wave 5 (wo804): the repo source-tree code index — see runtime/code-index-services.ts. */
|
|
229
|
+
readonly codeIndexStore: CodeIndexStore;
|
|
230
|
+
readonly codeIndexReindexScheduler: CodeIndexReindexScheduler; // Wave-5 Stage B tool-site reindex
|
|
231
|
+
/** W2.1/W2.2: unified live process registry (agents, WRFC chains, workflows, watchers, background processes) backing the Fleet panel. */
|
|
232
|
+
readonly processRegistry: ProcessRegistry;
|
|
251
233
|
readonly modeManager: ModeManager;
|
|
252
234
|
readonly fileUndoManager: FileUndoManager;
|
|
235
|
+
readonly workspaceCheckpointManager: WorkspaceCheckpointManager;
|
|
253
236
|
readonly integrationHelpers: IntegrationHelperService;
|
|
254
237
|
/**
|
|
255
238
|
* Re-root path-bound stores (MemoryStore, ProjectIndex) to a new working directory.
|
|
@@ -272,6 +255,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
272
255
|
const runtimeDispatch = createDomainDispatch(options.runtimeStore);
|
|
273
256
|
const gatewayMethods = new GatewayMethodCatalog();
|
|
274
257
|
const panelManager = new PanelManager();
|
|
258
|
+
// W6.1 (the purge): MIGRATE-TO-MODAL surface + redirect registration moved to
|
|
259
|
+
// registerBuiltinPanels (builtin-panels.ts), where the panels' resolved deps
|
|
260
|
+
// are available for the surfaces to close over.
|
|
275
261
|
const keybindingsManager = new KeybindingsManager({
|
|
276
262
|
configPath: shellPaths.resolveUserPath('tui', 'keybindings.json'),
|
|
277
263
|
});
|
|
@@ -352,6 +338,10 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
352
338
|
executor: agentOrchestrator,
|
|
353
339
|
configManager,
|
|
354
340
|
});
|
|
341
|
+
agentOrchestrator.setConversationSink({ // Wave-3 Part C6 bridge (mirrors the SDK's own createRuntimeServices)
|
|
342
|
+
register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
|
|
343
|
+
release: (agentId) => agentManager.releaseConversationSource(agentId),
|
|
344
|
+
});
|
|
355
345
|
agentManager.setRuntimeBus(options.runtimeBus);
|
|
356
346
|
const wrfcController = new WrfcController(options.runtimeBus, agentMessageBus, {
|
|
357
347
|
agentManager,
|
|
@@ -397,7 +387,8 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
397
387
|
});
|
|
398
388
|
const artifactStore = new ArtifactStore({ configManager });
|
|
399
389
|
const memoryEmbeddingRegistry = new MemoryEmbeddingProviderRegistry({ configManager });
|
|
400
|
-
|
|
390
|
+
// W6-C2 (E6): open the ONE home-scoped canonical store; legacy per-project TUI memory folds in at boot (foldTuiLegacyMemory).
|
|
391
|
+
const memoryDbPath = resolveCanonicalMemoryDbPath(homeDirectory);
|
|
401
392
|
const memoryStore = new MemoryStore(memoryDbPath, {
|
|
402
393
|
embeddingRegistry: memoryEmbeddingRegistry,
|
|
403
394
|
});
|
|
@@ -580,6 +571,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
580
571
|
const componentHealthMonitor = new ComponentHealthMonitor();
|
|
581
572
|
const worktreeRegistry = new WorktreeRegistry(workingDirectory);
|
|
582
573
|
const webhookNotifier = new WebhookNotifier();
|
|
574
|
+
const focusTracker = new FocusTracker();
|
|
583
575
|
const replayEngine = new DeterministicReplayEngine(workingDirectory);
|
|
584
576
|
const providerOptimizer = new ProviderOptimizer(
|
|
585
577
|
providerRegistry,
|
|
@@ -603,8 +595,58 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
603
595
|
artifactStore,
|
|
604
596
|
});
|
|
605
597
|
const processManager = new ProcessManager();
|
|
598
|
+
// Wave 4 (wo703): the phase/work-item orchestration engine, constructed
|
|
599
|
+
// before the process registry so its fleet nodes (workstream/phase/
|
|
600
|
+
// work-item) can be folded in below via the registry's optional
|
|
601
|
+
// orchestrationEngine dep.
|
|
602
|
+
const { orchestrationEngine, workstreamCommands } = createWorkstreamServices({
|
|
603
|
+
agentManager, configManager, adaptivePlanner, runtimeBus: options.runtimeBus, projectRoot: workingDirectory,
|
|
604
|
+
});
|
|
605
|
+
// Wave 5 (wo804): repo source-tree code index, sharing memoryEmbeddingRegistry
|
|
606
|
+
// with MemoryStore above. Auto-build is config-gated (default off) — see
|
|
607
|
+
// code-index-services.ts's header doc.
|
|
608
|
+
const { codeIndexStore, codeIndexReindexScheduler } = createCodeIndexServices({ workingDirectory, configManager, memoryEmbeddingRegistry });
|
|
609
|
+
const codeInjectionOrchestratorDeps = { codeIndex: codeIndexStore, isCodeInjectionSettingEnabled: () => isCodeInjectionSettingEnabled(configManager), codeIndexReindexScheduler }; // Wave-5 Stage B seam (agent here; main via orchestrator-core-services.ts)
|
|
610
|
+
// W2.1/W2.2: one shared process registry aggregating the managers above —
|
|
611
|
+
// the Fleet panel (panels/fleet-read-model.ts) is its first consumer.
|
|
612
|
+
// Constructed once here (not per-consumer) so the coalesced tick and the
|
|
613
|
+
// agent-activity side-table are shared, not duplicated.
|
|
614
|
+
const processRegistry = createProcessRegistry({
|
|
615
|
+
agentManager,
|
|
616
|
+
wrfcController,
|
|
617
|
+
orchestrationEngine, // Wave 4 (wo703): folds workstream/phase/work-item nodes into the fleet
|
|
618
|
+
codeIndexService: codeIndexStore, // Wave 5 (wo804): folds a single 'code-index' node into the fleet
|
|
619
|
+
processManager,
|
|
620
|
+
watcherRegistry,
|
|
621
|
+
workflow,
|
|
622
|
+
approvalBroker,
|
|
623
|
+
sessionBroker,
|
|
624
|
+
messageBus: agentMessageBus, // Wave-3: backs steer()/`steerable` (wo612 builds the composer UI on top)
|
|
625
|
+
automationManager, // Wave 6 (wo-F item d4): folds /schedule AutomationJobs into the fleet as 'schedule' nodes
|
|
626
|
+
runtimeBus: options.runtimeBus,
|
|
627
|
+
// Honest pricing: never fabricate a cost for an unrecognized model.
|
|
628
|
+
priceUsage: (model, usage) => {
|
|
629
|
+
const modelId = model ?? 'unknown';
|
|
630
|
+
if (!isModelPriced(modelId)) return null;
|
|
631
|
+
return calcSessionCost(usage.inputTokens, usage.outputTokens, usage.cacheReadTokens, usage.cacheWriteTokens, modelId);
|
|
632
|
+
},
|
|
633
|
+
});
|
|
606
634
|
const modeManager = new ModeManager();
|
|
607
635
|
const fileUndoManager = new FileUndoManager();
|
|
636
|
+
const workspaceCheckpointManager = new WorkspaceCheckpointManager({
|
|
637
|
+
workspaceRoot: workingDirectory,
|
|
638
|
+
runtimeBus: options.runtimeBus,
|
|
639
|
+
});
|
|
640
|
+
// Fire-and-forget: subscriptions go live immediately if init() succeeds.
|
|
641
|
+
// If it rejects, WorkspaceCheckpointManager caches that rejection on
|
|
642
|
+
// `initPromise` and never clears it, so every later call (create/list/
|
|
643
|
+
// diff/restore — each awaits init() first) re-throws the same error
|
|
644
|
+
// forever: checkpointing becomes entirely unavailable for the rest of the
|
|
645
|
+
// session, not "degraded to manual-only". The `.catch(() => {})` here only
|
|
646
|
+
// exists to prevent an unhandled rejection at startup; the checkpoint
|
|
647
|
+
// commands (checkpoint-runtime.ts) are what actually catch and report the
|
|
648
|
+
// failure to the user, on first use.
|
|
649
|
+
void workspaceCheckpointManager.init().catch(() => {});
|
|
608
650
|
const integrationHelpers = new IntegrationHelperService({
|
|
609
651
|
workingDirectory,
|
|
610
652
|
homeDirectory,
|
|
@@ -639,6 +681,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
639
681
|
remoteRunnerRegistry,
|
|
640
682
|
knowledgeService,
|
|
641
683
|
memoryRegistry,
|
|
684
|
+
...codeInjectionOrchestratorDeps, // Wave-5 Stage B: agent-run code injection + tool-site reindex
|
|
642
685
|
archetypeLoader,
|
|
643
686
|
configManager,
|
|
644
687
|
providerRegistry,
|
|
@@ -709,6 +752,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
709
752
|
worktreeRegistry,
|
|
710
753
|
sandboxSessionRegistry,
|
|
711
754
|
webhookNotifier,
|
|
755
|
+
focusTracker,
|
|
712
756
|
replayEngine,
|
|
713
757
|
providerOptimizer,
|
|
714
758
|
providerCapabilityRegistry,
|
|
@@ -736,12 +780,20 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
736
780
|
agentOrchestrator,
|
|
737
781
|
wrfcController,
|
|
738
782
|
processManager,
|
|
783
|
+
orchestrationEngine,
|
|
784
|
+
workstreamCommands,
|
|
785
|
+
codeIndexStore,
|
|
786
|
+
codeIndexReindexScheduler,
|
|
787
|
+
processRegistry,
|
|
739
788
|
modeManager,
|
|
740
789
|
fileUndoManager,
|
|
790
|
+
workspaceCheckpointManager,
|
|
741
791
|
integrationHelpers,
|
|
742
792
|
async rerootStores(newWorkingDir: string): Promise<void> {
|
|
743
|
-
|
|
744
|
-
|
|
793
|
+
// W6-C2 (E6): the memory store is the home-scoped canonical cross-surface store and
|
|
794
|
+
// deliberately does NOT reroot per-project (that would re-silo memory, the E6
|
|
795
|
+
// regression). Only working-tree-bound stores (code index, project index) reroot.
|
|
796
|
+
await codeIndexStore.reroot(newWorkingDir, codeIndexDbPath(newWorkingDir));
|
|
745
797
|
await projectIndex.reroot(newWorkingDir);
|
|
746
798
|
},
|
|
747
799
|
};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-inbound-inputs.ts
|
|
3
|
+
*
|
|
4
|
+
* D3 (One-Platform Wave 2, live-surface steer delivery — the inbound half of the
|
|
5
|
+
* session spine): the TUI's INBOUND path for steer/follow-up inputs that another
|
|
6
|
+
* live surface (e.g. the webui) queued against THIS TUI's session.
|
|
7
|
+
*
|
|
8
|
+
* Charter: when the owning surface is LIVE (heartbeating within the daemon's
|
|
9
|
+
* SURFACE_ROUTE_FRESHNESS window), a steer is DELIVERED to that surface rather
|
|
10
|
+
* than spawning a daemon-side executor (which would fail on the daemon's empty
|
|
11
|
+
* model registry). The daemon already does its half — `sessions.steer` /
|
|
12
|
+
* `sessions.followUp` to a surface-managed session with a fresh participant
|
|
13
|
+
* QUEUES the input for the surface (mode 'queued-for-surface') instead of
|
|
14
|
+
* spawning. This poller is the surface completing that contract:
|
|
15
|
+
*
|
|
16
|
+
* 1. poll `sessions.inputs.list` for QUEUED inputs on ITS OWN sessionId
|
|
17
|
+
* (the query is scoped by sessionId, so a steer for another session can
|
|
18
|
+
* never land here — the per-session isolation the concurrent-sessions
|
|
19
|
+
* charter requires);
|
|
20
|
+
* 2. hand each steer/follow-up to `onSteer` (the surface-side injection into
|
|
21
|
+
* the TUI's turn machinery — narrate "steer received from <surface>" and
|
|
22
|
+
* fire the next-turn boundary), skipping the TUI's own submissions;
|
|
23
|
+
* 3. acknowledge on the wire via `sessions.inputs.deliver` (queued → delivered)
|
|
24
|
+
* so every surface sees delivery truth live and the input is not re-picked.
|
|
25
|
+
*
|
|
26
|
+
* Discipline (mirrors SessionSpineClient): every wire call is best-effort and
|
|
27
|
+
* never throws into the render/keystroke path; a failed poll leaves the cursor
|
|
28
|
+
* where it was so the input is retried on the next tick (deliver is the only
|
|
29
|
+
* de-dup — an input already advanced past 'queued' is not returned again).
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
33
|
+
import type {
|
|
34
|
+
SharedSessionInputIntent,
|
|
35
|
+
SharedSessionInputRecord,
|
|
36
|
+
} from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
37
|
+
|
|
38
|
+
/** The TUI's own participant surfaceId — inputs it authored are never re-injected. */
|
|
39
|
+
export const TUI_SURFACE_ID = 'surface:tui';
|
|
40
|
+
|
|
41
|
+
/** The narrow inbound wire surface this poller needs. Structurally satisfied by
|
|
42
|
+
* the http operator client's `sessions.inputs` (list + deliver). Typed against
|
|
43
|
+
* this shape rather than the concrete client so tests inject a stub. */
|
|
44
|
+
export interface SpineInboundInputsClient {
|
|
45
|
+
listInputs(
|
|
46
|
+
sessionId: string,
|
|
47
|
+
options: { readonly state?: string; readonly since?: number; readonly limit?: number },
|
|
48
|
+
): Promise<{ readonly inputs: readonly SharedSessionInputRecord[] }>;
|
|
49
|
+
deliverInput(
|
|
50
|
+
sessionId: string,
|
|
51
|
+
inputId: string,
|
|
52
|
+
options?: { readonly consumed?: boolean },
|
|
53
|
+
): Promise<unknown>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** A steer/follow-up collected from the wire for surface-side injection. */
|
|
57
|
+
export interface InboundSteer {
|
|
58
|
+
readonly inputId: string;
|
|
59
|
+
readonly sessionId: string;
|
|
60
|
+
readonly intent: SharedSessionInputIntent;
|
|
61
|
+
readonly body: string;
|
|
62
|
+
readonly surfaceKind: string | undefined;
|
|
63
|
+
readonly surfaceId: string | undefined;
|
|
64
|
+
readonly displayName: string | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type InboundLogger = Pick<typeof logger, 'debug' | 'info'>;
|
|
68
|
+
|
|
69
|
+
export interface SessionInboundInputPollerOptions {
|
|
70
|
+
/** Returns the TUI's current live sessionId, or null when there is none yet.
|
|
71
|
+
* Read on every tick so a session swap re-targets the poll automatically. */
|
|
72
|
+
readonly sessionId: () => string | null;
|
|
73
|
+
/** Surface-side injection: narrate + deliver the steer into the turn machinery. */
|
|
74
|
+
readonly onSteer: (steer: InboundSteer) => void;
|
|
75
|
+
readonly intervalMs?: number;
|
|
76
|
+
readonly now?: () => number;
|
|
77
|
+
readonly log?: InboundLogger;
|
|
78
|
+
/** The poller's own surfaceId — inputs it authored are skipped (default TUI). */
|
|
79
|
+
readonly ownSurfaceId?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const DEFAULT_POLL_INTERVAL_MS = 4_000;
|
|
83
|
+
|
|
84
|
+
export class SessionInboundInputPoller {
|
|
85
|
+
private readonly sessionId: () => string | null;
|
|
86
|
+
private readonly onSteer: (steer: InboundSteer) => void;
|
|
87
|
+
private readonly intervalMs: number;
|
|
88
|
+
private readonly now: () => number;
|
|
89
|
+
private readonly log: InboundLogger;
|
|
90
|
+
private readonly ownSurfaceId: string;
|
|
91
|
+
|
|
92
|
+
private client: SpineInboundInputsClient | null = null;
|
|
93
|
+
private timer: ReturnType<typeof setInterval> | null = null;
|
|
94
|
+
private polling = false;
|
|
95
|
+
/** Exclusive createdAt cursor, per session — a session swap resets it. */
|
|
96
|
+
private cursor = 0;
|
|
97
|
+
private cursorSessionId: string | null = null;
|
|
98
|
+
|
|
99
|
+
constructor(options: SessionInboundInputPollerOptions) {
|
|
100
|
+
this.sessionId = options.sessionId;
|
|
101
|
+
this.onSteer = options.onSteer;
|
|
102
|
+
this.intervalMs = Math.max(250, options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
103
|
+
this.now = options.now ?? (() => Date.now());
|
|
104
|
+
this.log = options.log ?? logger;
|
|
105
|
+
this.ownSurfaceId = options.ownSurfaceId ?? TUI_SURFACE_ID;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Whether a backend is attached (external daemon adopted). */
|
|
109
|
+
get active(): boolean {
|
|
110
|
+
return this.client !== null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Attach the wire client and begin polling (external daemon adopted). Idempotent:
|
|
115
|
+
* re-activating swaps the client without stacking timers.
|
|
116
|
+
*/
|
|
117
|
+
activate(client: SpineInboundInputsClient): void {
|
|
118
|
+
this.client = client;
|
|
119
|
+
if (this.timer === null) {
|
|
120
|
+
this.timer = setInterval(() => { void this.pollOnce(); }, this.intervalMs);
|
|
121
|
+
this.timer.unref?.();
|
|
122
|
+
}
|
|
123
|
+
this.log.info('session inbound-input poller activated — collecting queued steers for this surface', {});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Detach the backend and stop polling (daemon mode resolved to non-external, or lost). */
|
|
127
|
+
deactivate(reason: string): void {
|
|
128
|
+
this.client = null;
|
|
129
|
+
if (this.timer !== null) {
|
|
130
|
+
clearInterval(this.timer);
|
|
131
|
+
this.timer = null;
|
|
132
|
+
}
|
|
133
|
+
this.log.info('session inbound-input poller deactivated', { reason });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Stop the timer (shutdown). */
|
|
137
|
+
dispose(): void {
|
|
138
|
+
if (this.timer !== null) {
|
|
139
|
+
clearInterval(this.timer);
|
|
140
|
+
this.timer = null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* One poll pass: collect queued steer/follow-up inputs for the current session,
|
|
146
|
+
* inject each, and acknowledge delivery. Returns the number of steers delivered.
|
|
147
|
+
* Best-effort: any wire error is absorbed and the pass is retried next tick.
|
|
148
|
+
*/
|
|
149
|
+
async pollOnce(): Promise<number> {
|
|
150
|
+
const client = this.client;
|
|
151
|
+
if (!client || this.polling) return 0;
|
|
152
|
+
const sessionId = this.sessionId();
|
|
153
|
+
if (!sessionId) return 0;
|
|
154
|
+
|
|
155
|
+
// A session swap resets the cursor so we never skip the new session's backlog.
|
|
156
|
+
if (sessionId !== this.cursorSessionId) {
|
|
157
|
+
this.cursor = 0;
|
|
158
|
+
this.cursorSessionId = sessionId;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.polling = true;
|
|
162
|
+
let delivered = 0;
|
|
163
|
+
try {
|
|
164
|
+
const result = await client.listInputs(sessionId, { state: 'queued', since: this.cursor });
|
|
165
|
+
const inputs = result?.inputs ?? [];
|
|
166
|
+
for (const input of inputs) {
|
|
167
|
+
// Advance the cursor across every queued input we observe (even skipped
|
|
168
|
+
// ones) so it is monotonic; deliver() is the real de-dup for injected ones.
|
|
169
|
+
if (input.createdAt > this.cursor) this.cursor = input.createdAt;
|
|
170
|
+
|
|
171
|
+
if (input.state !== 'queued') continue;
|
|
172
|
+
if (input.intent !== 'steer' && input.intent !== 'follow-up') continue;
|
|
173
|
+
// Never re-inject this surface's OWN submissions.
|
|
174
|
+
if (input.surfaceId === this.ownSurfaceId) continue;
|
|
175
|
+
|
|
176
|
+
const steer: InboundSteer = {
|
|
177
|
+
inputId: input.id,
|
|
178
|
+
sessionId,
|
|
179
|
+
intent: input.intent,
|
|
180
|
+
body: input.body,
|
|
181
|
+
surfaceKind: input.surfaceKind,
|
|
182
|
+
surfaceId: input.surfaceId,
|
|
183
|
+
displayName: input.displayName,
|
|
184
|
+
};
|
|
185
|
+
try {
|
|
186
|
+
this.onSteer(steer);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
// A failing injection must not wedge the poll loop or drop the ack —
|
|
189
|
+
// narrate-and-continue; the input is still acked so it is not re-picked.
|
|
190
|
+
this.log.debug('session inbound steer injection threw — continuing', {
|
|
191
|
+
error: error instanceof Error ? error.message : String(error),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
// consumed:false → queued → delivered (surface has collected it).
|
|
196
|
+
await client.deliverInput(sessionId, input.id, { consumed: false });
|
|
197
|
+
delivered += 1;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
this.log.debug('session inbound deliver ack failed — will retry', {
|
|
200
|
+
error: error instanceof Error ? error.message : String(error),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
// Poll failed entirely (daemon gone, transient): keep the cursor, retry next tick.
|
|
206
|
+
this.log.debug('session inbound-input poll failed — retry next tick', {
|
|
207
|
+
error: error instanceof Error ? error.message : String(error),
|
|
208
|
+
});
|
|
209
|
+
} finally {
|
|
210
|
+
this.polling = false;
|
|
211
|
+
}
|
|
212
|
+
return delivered;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Format the operator-facing narration for an inbound steer, e.g.
|
|
217
|
+
* "steer received from webui (Alice): resize the panel". */
|
|
218
|
+
export function narrateInboundSteer(steer: InboundSteer): string {
|
|
219
|
+
const surface = steer.surfaceKind ?? steer.surfaceId ?? 'another surface';
|
|
220
|
+
const who = steer.displayName ? ` (${steer.displayName})` : '';
|
|
221
|
+
const verb = steer.intent === 'follow-up' ? 'follow-up received from' : 'steer received from';
|
|
222
|
+
return `${verb} ${surface}${who}: ${steer.body}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Build the standard bootstrap-time poller: sessionId tracks the live runtime
|
|
227
|
+
* session ref, and onSteer narrates the steer to the operator then injects it
|
|
228
|
+
* via the SAME path COMPANION_MESSAGE_RECEIVED uses (orchestrator.handleUserInput
|
|
229
|
+
* when wired, else a bare conversation append + render request as a fallback
|
|
230
|
+
* before the orchestrator ref is patched). Extracted from bootstrap-core.ts to
|
|
231
|
+
* keep this module (not the bootstrap god-function) the owner of the wiring.
|
|
232
|
+
*/
|
|
233
|
+
export function createBootstrapInboundInputPoller(deps: {
|
|
234
|
+
readonly runtimeSessionIdRef: { readonly value: string };
|
|
235
|
+
readonly routeOrBuffer: (text: string, priority: 'low' | 'high') => void;
|
|
236
|
+
readonly orchestratorHandleUserInputRef: { readonly value: ((text: string) => void) | null };
|
|
237
|
+
readonly conversation: { readonly addUserMessage: (text: string) => void };
|
|
238
|
+
readonly requestRender: () => void;
|
|
239
|
+
}): SessionInboundInputPoller {
|
|
240
|
+
return new SessionInboundInputPoller({
|
|
241
|
+
sessionId: () => deps.runtimeSessionIdRef.value || null,
|
|
242
|
+
onSteer: (steer) => {
|
|
243
|
+
deps.routeOrBuffer(narrateInboundSteer(steer), 'low');
|
|
244
|
+
if (deps.orchestratorHandleUserInputRef.value) {
|
|
245
|
+
deps.orchestratorHandleUserInputRef.value(steer.body);
|
|
246
|
+
} else {
|
|
247
|
+
deps.conversation.addUserMessage(steer.body);
|
|
248
|
+
deps.requestRender();
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-spine-transport.ts
|
|
3
|
+
*
|
|
4
|
+
* W3-T1 (One-Platform Wave 3, TUI session-spine cutover): the thin typed-client
|
|
5
|
+
* adapter that lets the TUI's bootstrap drive the SDK's
|
|
6
|
+
* `@pellux/goodvibes-sdk/platform/runtime/session-spine` `SessionSpineClient`
|
|
7
|
+
* (extracted in W3-S4) via its injected `SpineTransport` seam.
|
|
8
|
+
*
|
|
9
|
+
* The TUI is SDK-clean here: `SpineSessionsClient` is exactly the narrow wire
|
|
10
|
+
* surface the old TUI-local `session-spine-client.ts` declared (structurally
|
|
11
|
+
* satisfied by `HttpTransport.operator.sessions`), and `createTuiSpineTransport`
|
|
12
|
+
* folds it into the SDK's `SpineTransport` the SAME way the SDK's own
|
|
13
|
+
* consumability proof (`session-spine-daemon-integration.test.ts`) does:
|
|
14
|
+
* resolve -> `'ok'`, throw -> `'offline'`. The TUI never emits `'rejected'` — it
|
|
15
|
+
* has no durable-refusal vocabulary to fold (that distinction is the agent's
|
|
16
|
+
* REST mirror's job, per the SDK's divergence ruling #6/#7).
|
|
17
|
+
*/
|
|
18
|
+
import type {
|
|
19
|
+
RegisterSharedSessionInput,
|
|
20
|
+
SharedSessionRecord,
|
|
21
|
+
SharedSessionRegisterResult,
|
|
22
|
+
} from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
23
|
+
import type { SpineResult, SpineTransport } from '@pellux/goodvibes-sdk/platform/runtime/session-spine';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The minimal wire surface this adapter needs. Structurally satisfied by BOTH
|
|
27
|
+
* the SDK's in-process `OperatorClient['sessions']` (DirectTransport) and the
|
|
28
|
+
* wire `HttpTransportOperatorClient['sessions']` (HttpTransport) — bootstrap.ts
|
|
29
|
+
* only ever constructs an Http-backed instance in practice, but this is
|
|
30
|
+
* deliberately typed against the narrow shape rather than either concrete
|
|
31
|
+
* transport type so it has no compile-time dependency on which transport
|
|
32
|
+
* backs it.
|
|
33
|
+
*/
|
|
34
|
+
export interface SpineSessionsClient {
|
|
35
|
+
register(input: RegisterSharedSessionInput): Promise<SharedSessionRegisterResult>;
|
|
36
|
+
close(sessionId: string): Promise<SharedSessionRecord | null>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Adapt the TUI's typed `SpineSessionsClient` into the SDK `SessionSpineClient`'s
|
|
41
|
+
* injected `SpineTransport`. A rejecting wire call is treated as a transient
|
|
42
|
+
* connectivity fault (`'offline'`) rather than a durable refusal — the same
|
|
43
|
+
* binary reachability model the pre-extraction TUI-local client used.
|
|
44
|
+
*/
|
|
45
|
+
export function createTuiSpineTransport(sessionsClient: SpineSessionsClient): SpineTransport {
|
|
46
|
+
return {
|
|
47
|
+
register: async (input: RegisterSharedSessionInput): Promise<SpineResult> => {
|
|
48
|
+
try {
|
|
49
|
+
await sessionsClient.register(input);
|
|
50
|
+
return { outcome: 'ok' };
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return { outcome: 'offline', error: error instanceof Error ? error.message : String(error) };
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
close: async (sessionId: string): Promise<SpineResult> => {
|
|
56
|
+
try {
|
|
57
|
+
await sessionsClient.close(sessionId);
|
|
58
|
+
return { outcome: 'ok' };
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return { outcome: 'offline', error: error instanceof Error ? error.message : String(error) };
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -40,7 +40,14 @@ export type TuiTerminalOutputGuardOptions = {
|
|
|
40
40
|
readonly stdout: WritableStreamLike;
|
|
41
41
|
readonly stderr?: WritableStreamLike;
|
|
42
42
|
readonly active?: boolean;
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Called (rate-limited) after intercepts with the cumulative count of direct
|
|
45
|
+
* writes captured this session, so an honest quiet counter (e.g. /debug) can
|
|
46
|
+
* refresh. The per-write detail is already recorded to the activity log via
|
|
47
|
+
* logger.warn — this callback intentionally does NOT push transcript lines.
|
|
48
|
+
* (UX-B item 1a.)
|
|
49
|
+
*/
|
|
50
|
+
readonly onCapture?: (total: number) => void;
|
|
44
51
|
};
|
|
45
52
|
|
|
46
53
|
const MAX_LOG_TEXT = 4_000;
|
|
@@ -208,21 +215,21 @@ export function installTerminalOutputGuard(options: TerminalOutputGuardOptions):
|
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
export function installTuiTerminalOutputGuard(options: TuiTerminalOutputGuardOptions): TerminalOutputGuard {
|
|
211
|
-
let
|
|
218
|
+
let totalInterceptedWrites = 0;
|
|
212
219
|
let lastNoticeAt = 0;
|
|
213
220
|
return installTerminalOutputGuard({
|
|
214
221
|
stdout: options.stdout,
|
|
215
222
|
stderr: options.stderr,
|
|
216
223
|
active: options.active,
|
|
217
|
-
onIntercept: (
|
|
218
|
-
|
|
224
|
+
onIntercept: () => {
|
|
225
|
+
// Each intercept is already logged (logger.warn in record()). Here we only
|
|
226
|
+
// maintain a cumulative counter and, rate-limited, refresh a quiet counter
|
|
227
|
+
// surface — no repeated transcript lines. (UX-B item 1a.)
|
|
228
|
+
totalInterceptedWrites++;
|
|
219
229
|
const now = Date.now();
|
|
220
230
|
if (now - lastNoticeAt < 5_000) return;
|
|
221
|
-
const count = capturedWriteCount;
|
|
222
|
-
capturedWriteCount = 0;
|
|
223
231
|
lastNoticeAt = now;
|
|
224
|
-
|
|
225
|
-
options.notify(`[Terminal] Captured ${count} direct ${event.source} write${plural} that would have corrupted the TUI: ${event.preview}`);
|
|
232
|
+
options.onCapture?.(totalInterceptedWrites);
|
|
226
233
|
},
|
|
227
234
|
});
|
|
228
235
|
}
|