@pellux/goodvibes-tui 0.18.12 → 0.18.17

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.
Files changed (196) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +3 -2
  5. package/src/config/index.ts +1 -138
  6. package/src/core/conversation-rendering.ts +3 -3
  7. package/src/core/conversation.ts +176 -423
  8. package/src/core/history.ts +45 -0
  9. package/src/core/orchestrator.ts +3 -735
  10. package/src/core/system-message-router.ts +19 -58
  11. package/src/daemon/cli.ts +82 -6
  12. package/src/input/command-registry.ts +2 -0
  13. package/src/input/commands/control-room-runtime.ts +1 -1
  14. package/src/input/commands/health-runtime.ts +1 -1
  15. package/src/input/commands/local-setup-review.ts +1 -1
  16. package/src/input/commands/platform-access-runtime.ts +1 -1
  17. package/src/input/commands/qrcode-runtime.ts +20 -0
  18. package/src/input/commands/subscription-runtime.ts +1 -1
  19. package/src/input/commands.ts +2 -0
  20. package/src/input/handler-content-actions.ts +2 -2
  21. package/src/input/handler-feed.ts +7 -1
  22. package/src/input/handler-modal-routes.ts +19 -2
  23. package/src/input/handler-modal-token-routes.ts +4 -1
  24. package/src/input/handler-picker-routes.ts +4 -2
  25. package/src/input/handler-ui-state.ts +1 -1
  26. package/src/input/handler.ts +1 -1
  27. package/src/input/model-picker.ts +11 -0
  28. package/src/input/search.ts +1 -1
  29. package/src/input/selection.ts +2 -2
  30. package/src/input/settings-modal.ts +31 -3
  31. package/src/main.ts +1 -1
  32. package/src/panels/agent-inspector-panel.ts +3 -3
  33. package/src/panels/agent-logs-panel.ts +26 -27
  34. package/src/panels/approval-panel.ts +2 -2
  35. package/src/panels/automation-control-panel.ts +3 -3
  36. package/src/panels/base-panel.ts +14 -14
  37. package/src/panels/builtin/operations.ts +1 -1
  38. package/src/panels/builtin/session.ts +67 -1
  39. package/src/panels/builtin/shared.ts +4 -4
  40. package/src/panels/cockpit-panel.ts +2 -2
  41. package/src/panels/communication-panel.ts +3 -3
  42. package/src/panels/context-visualizer-panel.ts +2 -2
  43. package/src/panels/control-plane-panel.ts +3 -3
  44. package/src/panels/cost-tracker-panel.ts +3 -3
  45. package/src/panels/debug-panel.ts +2 -2
  46. package/src/panels/diff-panel.ts +2 -2
  47. package/src/panels/docs-panel.ts +1 -1
  48. package/src/panels/eval-panel.ts +2 -2
  49. package/src/panels/file-explorer-panel.ts +3 -3
  50. package/src/panels/file-preview-panel.ts +3 -3
  51. package/src/panels/forensics-panel.ts +2 -2
  52. package/src/panels/git-panel.ts +1 -1
  53. package/src/panels/hooks-panel.ts +3 -3
  54. package/src/panels/incident-review-panel.ts +1 -1
  55. package/src/panels/intelligence-panel.ts +2 -2
  56. package/src/panels/knowledge-panel.ts +1 -1
  57. package/src/panels/local-auth-panel.ts +2 -2
  58. package/src/panels/marketplace-panel.ts +1 -1
  59. package/src/panels/mcp-panel.ts +3 -3
  60. package/src/panels/memory-panel.ts +1 -1
  61. package/src/panels/ops-control-panel.ts +3 -3
  62. package/src/panels/ops-strategy-panel.ts +2 -2
  63. package/src/panels/orchestration-panel.ts +2 -2
  64. package/src/panels/panel-list-panel.ts +6 -6
  65. package/src/panels/plan-dashboard-panel.ts +1 -1
  66. package/src/panels/plugins-panel.ts +2 -2
  67. package/src/panels/policy-panel.ts +2 -2
  68. package/src/panels/polish.ts +3 -3
  69. package/src/panels/provider-account-snapshot.ts +1 -1
  70. package/src/panels/provider-accounts-panel.ts +25 -29
  71. package/src/panels/provider-health-panel.ts +2 -2
  72. package/src/panels/provider-stats-panel.ts +3 -3
  73. package/src/panels/qr-panel.ts +182 -0
  74. package/src/panels/remote-panel.ts +3 -3
  75. package/src/panels/routes-panel.ts +3 -3
  76. package/src/panels/sandbox-panel.ts +2 -2
  77. package/src/panels/schedule-panel.ts +1 -1
  78. package/src/panels/scrollable-list-panel.ts +407 -0
  79. package/src/panels/security-panel.ts +2 -2
  80. package/src/panels/services-panel.ts +3 -3
  81. package/src/panels/session-browser-panel.ts +2 -2
  82. package/src/panels/settings-sync-panel.ts +2 -2
  83. package/src/panels/skills-panel.ts +6 -6
  84. package/src/panels/subscription-panel.ts +3 -3
  85. package/src/panels/symbol-outline-panel.ts +3 -3
  86. package/src/panels/system-messages-panel.ts +4 -4
  87. package/src/panels/tasks-panel.ts +2 -2
  88. package/src/panels/thinking-panel.ts +3 -3
  89. package/src/panels/token-budget-panel.ts +1 -1
  90. package/src/panels/tool-inspector-panel.ts +3 -3
  91. package/src/panels/types.ts +5 -5
  92. package/src/panels/watchers-panel.ts +3 -3
  93. package/src/panels/welcome-panel.ts +1 -1
  94. package/src/panels/worktree-panel.ts +22 -21
  95. package/src/panels/wrfc-panel.ts +3 -3
  96. package/src/permissions/prompt.ts +3 -22
  97. package/src/plugins/loader.ts +15 -304
  98. package/src/renderer/agent-detail-modal.ts +1 -1
  99. package/src/renderer/autocomplete-overlay.ts +2 -2
  100. package/src/renderer/bookmark-modal.ts +1 -1
  101. package/src/renderer/bottom-bar.ts +2 -2
  102. package/src/renderer/buffer.ts +1 -1
  103. package/src/renderer/code-block.ts +2 -2
  104. package/src/renderer/compositor.ts +2 -2
  105. package/src/renderer/context-inspector.ts +1 -1
  106. package/src/renderer/conversation-layout.ts +2 -2
  107. package/src/renderer/conversation-overlays.ts +1 -1
  108. package/src/renderer/conversation-surface.ts +2 -2
  109. package/src/renderer/diff-view.ts +2 -2
  110. package/src/renderer/diff.ts +1 -1
  111. package/src/renderer/file-picker-overlay.ts +2 -2
  112. package/src/renderer/file-tree.ts +2 -2
  113. package/src/renderer/help-overlay.ts +1 -1
  114. package/src/renderer/history-search-overlay.ts +2 -2
  115. package/src/renderer/live-tail-modal.ts +1 -1
  116. package/src/renderer/markdown.ts +2 -2
  117. package/src/renderer/modal-factory.ts +3 -3
  118. package/src/renderer/model-picker-overlay.ts +2 -2
  119. package/src/renderer/overlay-box.ts +2 -2
  120. package/src/renderer/panel-composite.ts +1 -1
  121. package/src/renderer/panel-picker-overlay.ts +2 -2
  122. package/src/renderer/panel-tab-bar.ts +1 -1
  123. package/src/renderer/panel-workspace-bar.ts +1 -1
  124. package/src/renderer/process-indicator.ts +2 -2
  125. package/src/renderer/process-modal.ts +1 -1
  126. package/src/renderer/profile-picker-modal.ts +2 -2
  127. package/src/renderer/progress.ts +2 -2
  128. package/src/renderer/qr-renderer.ts +117 -0
  129. package/src/renderer/search-overlay.ts +2 -2
  130. package/src/renderer/selection-modal-overlay.ts +2 -2
  131. package/src/renderer/session-picker-modal.ts +2 -2
  132. package/src/renderer/settings-modal-helpers.ts +122 -0
  133. package/src/renderer/settings-modal.ts +149 -113
  134. package/src/renderer/shell-surface.ts +1 -1
  135. package/src/renderer/system-message.ts +1 -1
  136. package/src/renderer/tab-strip.ts +2 -2
  137. package/src/renderer/text-layout.ts +1 -1
  138. package/src/renderer/thinking.ts +1 -1
  139. package/src/renderer/tool-call.ts +2 -2
  140. package/src/renderer/ui-factory.ts +2 -2
  141. package/src/runtime/bootstrap-command-context.ts +5 -6
  142. package/src/runtime/bootstrap-command-parts.ts +32 -18
  143. package/src/runtime/bootstrap-core.ts +3 -2
  144. package/src/runtime/bootstrap-hook-bridge.ts +15 -174
  145. package/src/runtime/bootstrap-shell.ts +4 -4
  146. package/src/runtime/bootstrap.ts +7 -2
  147. package/src/runtime/context.ts +4 -20
  148. package/src/runtime/diagnostics/panels/index.ts +6 -6
  149. package/src/runtime/diagnostics/panels/ops.ts +1 -1
  150. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  151. package/src/runtime/perf/panel-contracts.ts +32 -0
  152. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  153. package/src/runtime/services.ts +5 -5
  154. package/src/runtime/store/domains/domain-read-matrix.ts +0 -2
  155. package/src/runtime/store/selectors/index.ts +11 -6
  156. package/src/runtime/store/state.ts +12 -4
  157. package/src/runtime/ui-events.ts +1 -0
  158. package/src/runtime/ui-read-model-helpers.ts +1 -32
  159. package/src/runtime/ui-read-models-observability-maintenance.ts +1 -81
  160. package/src/runtime/ui-read-models-observability-options.ts +1 -5
  161. package/src/runtime/ui-read-models-observability-remote.ts +1 -73
  162. package/src/runtime/ui-read-models-observability-security.ts +1 -172
  163. package/src/runtime/ui-read-models-observability-system.ts +1 -217
  164. package/src/runtime/ui-read-models-observability.ts +1 -59
  165. package/src/runtime/ui-service-queries.ts +1 -114
  166. package/src/runtime/ui-services.ts +1 -1
  167. package/src/shell/ui-openers.ts +1 -1
  168. package/src/tools/index.ts +1 -186
  169. package/src/types/grid.ts +48 -0
  170. package/src/utils/clipboard.ts +21 -0
  171. package/src/utils/splash-lines.ts +1 -1
  172. package/src/utils/terminal-width.ts +185 -0
  173. package/src/version.ts +1 -1
  174. package/src/config/service-registry.ts +0 -1
  175. package/src/config/subscription-providers.ts +0 -127
  176. package/src/daemon/facade-composition.ts +0 -398
  177. package/src/daemon/facade.ts +0 -638
  178. package/src/daemon/surface-policy.ts +0 -60
  179. package/src/daemon/types.ts +0 -191
  180. package/src/runtime/diagnostics/actions.ts +0 -776
  181. package/src/runtime/diagnostics/index.ts +0 -99
  182. package/src/runtime/diagnostics/panels/agents.ts +0 -252
  183. package/src/runtime/diagnostics/panels/events.ts +0 -188
  184. package/src/runtime/diagnostics/panels/health.ts +0 -242
  185. package/src/runtime/diagnostics/panels/tasks.ts +0 -251
  186. package/src/runtime/diagnostics/panels/tool-calls.ts +0 -267
  187. package/src/runtime/diagnostics/provider.ts +0 -262
  188. package/src/runtime/store/domains/conversation.ts +0 -181
  189. package/src/runtime/store/domains/permissions.ts +0 -143
  190. package/src/runtime/store/helpers/reducers/conversation.ts +0 -228
  191. package/src/runtime/store/helpers/reducers/lifecycle.ts +0 -440
  192. package/src/runtime/store/helpers/reducers/shared.ts +0 -60
  193. package/src/runtime/store/helpers/reducers/sync.ts +0 -555
  194. package/src/runtime/store/helpers/reducers.ts +0 -30
  195. package/src/runtime/ui-read-models-core.ts +0 -95
  196. package/src/runtime/ui-read-models-operations.ts +0 -203
@@ -1,555 +0,0 @@
1
- import type { CommunicationEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/communication';
2
- import type { PluginEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/plugins';
3
- import type { McpEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/mcp';
4
- import type { TransportEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/transport';
5
- import type { IntegrationDomainState, IntegrationRecord, IntegrationStatus } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/integrations';
6
- import type { AutomationDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/automation';
7
- import type { RoutesDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/routes';
8
- import type { ControlPlaneDomainState, ControlPlaneClientRecord } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/control-plane';
9
- import type { DeliveryDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/deliveries';
10
- import type { WatcherDomainState, WatcherRecord } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/watchers';
11
- import type { SurfaceDomainState, SurfaceRecord } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/surfaces';
12
- import type { CommunicationDomainState, RuntimeCommunicationRecord } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/communication';
13
- import type { PluginDomainState, RuntimePlugin, PluginLifecycleState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/plugins';
14
- import type { McpDomainState, McpServerRecord, McpServerLifecycleState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/mcp';
15
- import type { AcpDomainState, AcpTransportState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/acp';
16
- import type { DaemonDomainState, DaemonTransportState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/daemon';
17
- import type { AutomationJob } from '@pellux/goodvibes-sdk/platform/automation/jobs';
18
- import type { AutomationRun } from '@pellux/goodvibes-sdk/platform/automation/runs';
19
- import type { AutomationSourceRecord } from '@pellux/goodvibes-sdk/platform/automation/sources';
20
- import type { AutomationRouteBinding } from '@pellux/goodvibes-sdk/platform/automation/routes';
21
- import type { AutomationSurfaceKind } from '@pellux/goodvibes-sdk/platform/automation/types';
22
- import type { AutomationDeliveryAttempt } from '@pellux/goodvibes-sdk/platform/automation/delivery';
23
- import type { SessionDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/session';
24
- import { now, uniq, updateDomainMetadata } from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/shared';
25
-
26
- export function updateCommunicationState(
27
- domain: CommunicationDomainState,
28
- event: CommunicationEvent,
29
- ): CommunicationDomainState {
30
- const timestamp = now();
31
- const records = new Map(domain.records);
32
- const base: RuntimeCommunicationRecord | undefined =
33
- event.type === 'COMMUNICATION_SENT' || event.type === 'COMMUNICATION_BLOCKED'
34
- ? {
35
- id: event.messageId,
36
- fromId: event.fromId,
37
- toId: event.toId,
38
- scope: event.scope,
39
- kind: event.kind,
40
- content: 'content' in event ? event.content : '',
41
- timestamp,
42
- status: event.type === 'COMMUNICATION_BLOCKED' ? 'blocked' : 'sent',
43
- ...(event.fromRole !== undefined ? { fromRole: event.fromRole } : {}),
44
- ...(event.toRole !== undefined ? { toRole: event.toRole } : {}),
45
- ...(event.cohort !== undefined ? { cohort: event.cohort } : {}),
46
- ...(event.wrfcId !== undefined ? { wrfcId: event.wrfcId } : {}),
47
- ...(event.parentAgentId !== undefined ? { parentAgentId: event.parentAgentId } : {}),
48
- ...('reason' in event && event.reason !== undefined ? { reason: event.reason } : {}),
49
- }
50
- : undefined;
51
-
52
- if (base) {
53
- records.set(event.messageId, base);
54
- } else {
55
- const existing = records.get(event.messageId);
56
- if (!existing) return domain;
57
- records.set(event.messageId, {
58
- ...existing,
59
- status: event.type === 'COMMUNICATION_DELIVERED' ? 'delivered' : existing.status,
60
- });
61
- }
62
-
63
- const recentRecordIds = uniq([event.messageId, ...domain.recentRecordIds]).slice(0, 200);
64
- return {
65
- ...updateDomainMetadata(domain, event.type),
66
- records,
67
- recentRecordIds,
68
- totalSent: domain.totalSent + (event.type === 'COMMUNICATION_SENT' ? 1 : 0),
69
- totalDelivered: domain.totalDelivered + (event.type === 'COMMUNICATION_DELIVERED' ? 1 : 0),
70
- totalBlocked: domain.totalBlocked + (event.type === 'COMMUNICATION_BLOCKED' ? 1 : 0),
71
- };
72
- }
73
-
74
- function pluginStatusForEvent(event: PluginEvent): PluginLifecycleState {
75
- switch (event.type) {
76
- case 'PLUGIN_DISCOVERED':
77
- return 'discovered';
78
- case 'PLUGIN_LOADING':
79
- return 'loading';
80
- case 'PLUGIN_LOADED':
81
- return 'loaded';
82
- case 'PLUGIN_ACTIVE':
83
- return 'active';
84
- case 'PLUGIN_DEGRADED':
85
- return 'degraded';
86
- case 'PLUGIN_ERROR':
87
- return 'error';
88
- case 'PLUGIN_UNLOADING':
89
- return 'unloading';
90
- case 'PLUGIN_DISABLED':
91
- return 'disabled';
92
- }
93
- }
94
-
95
- export function updatePluginState(domain: PluginDomainState, event: PluginEvent): PluginDomainState {
96
- const plugins = new Map(domain.plugins);
97
- const existing = plugins.get(event.pluginId);
98
- const timestamp = now();
99
- const plugin: RuntimePlugin =
100
- existing ??
101
- {
102
- name: event.pluginId,
103
- displayName: event.pluginId,
104
- version: 'version' in event ? event.version : 'unknown',
105
- description: 'path' in event ? event.path : '',
106
- status: pluginStatusForEvent(event),
107
- enabled: true,
108
- active: false,
109
- toolCount: 'capabilities' in event ? event.capabilities.length : 0,
110
- config: {},
111
- hookInvocations: 0,
112
- };
113
- const next: RuntimePlugin = {
114
- ...plugin,
115
- status: pluginStatusForEvent(event),
116
- version: 'version' in event ? event.version : plugin.version,
117
- description: 'path' in event ? event.path : plugin.description,
118
- active: event.type === 'PLUGIN_ACTIVE' ? true : event.type === 'PLUGIN_DISABLED' ? false : plugin.active,
119
- enabled: event.type === 'PLUGIN_DISABLED' ? false : plugin.enabled,
120
- toolCount: 'capabilities' in event ? event.capabilities.length : plugin.toolCount,
121
- error:
122
- event.type === 'PLUGIN_ERROR'
123
- ? event.error
124
- : event.type === 'PLUGIN_DEGRADED'
125
- ? event.reason
126
- : plugin.error,
127
- loadedAt: event.type === 'PLUGIN_LOADED' || event.type === 'PLUGIN_ACTIVE' ? timestamp : plugin.loadedAt,
128
- errorAt: event.type === 'PLUGIN_ERROR' || event.type === 'PLUGIN_DEGRADED' ? timestamp : plugin.errorAt,
129
- };
130
- plugins.set(event.pluginId, next);
131
- const activePluginNames = [...plugins.values()].filter((value) => value.active).map((value) => value.name);
132
- const erroredPluginNames = [...plugins.values()]
133
- .filter((value) => value.status === 'error' || value.status === 'degraded')
134
- .map((value) => value.name);
135
- return {
136
- ...updateDomainMetadata(domain, event.type),
137
- plugins,
138
- activePluginNames,
139
- erroredPluginNames,
140
- totalDiscovered: domain.totalDiscovered + (event.type === 'PLUGIN_DISCOVERED' ? 1 : 0),
141
- totalActive: activePluginNames.length,
142
- totalToolsContributed: [...plugins.values()].reduce((sum, value) => sum + (value.active ? value.toolCount : 0), 0),
143
- initialLoadComplete: domain.initialLoadComplete || event.type === 'PLUGIN_LOADED' || event.type === 'PLUGIN_ACTIVE',
144
- reloadInProgress: event.type === 'PLUGIN_LOADING' || event.type === 'PLUGIN_UNLOADING',
145
- };
146
- }
147
-
148
- function mcpStatusForEvent(event: McpEvent): McpServerLifecycleState {
149
- switch (event.type) {
150
- case 'MCP_CONFIGURED':
151
- return 'configured';
152
- case 'MCP_CONNECTING':
153
- return 'connecting';
154
- case 'MCP_CONNECTED':
155
- return 'connected';
156
- case 'MCP_DEGRADED':
157
- case 'MCP_SCHEMA_QUARANTINED':
158
- case 'MCP_SCHEMA_QUARANTINE_APPROVED':
159
- return 'degraded';
160
- case 'MCP_AUTH_REQUIRED':
161
- return 'auth_required';
162
- case 'MCP_RECONNECTING':
163
- return 'reconnecting';
164
- case 'MCP_DISCONNECTED':
165
- return 'disconnected';
166
- case 'MCP_POLICY_UPDATED':
167
- return 'configured';
168
- }
169
- }
170
-
171
- export function updateMcpState(domain: McpDomainState, event: McpEvent): McpDomainState {
172
- const servers = new Map(domain.servers);
173
- const existing = servers.get(event.serverId);
174
- const timestamp = now();
175
- const server: McpServerRecord =
176
- existing ??
177
- {
178
- name: event.serverId,
179
- displayName: event.serverId,
180
- status: mcpStatusForEvent(event),
181
- transport: event.type === 'MCP_CONFIGURED' && event.transport === 'http' ? 'http' : 'stdio',
182
- toolCount: 0,
183
- toolNames: [],
184
- callCount: 0,
185
- errorCount: 0,
186
- reconnectAttempts: 0,
187
- trustMode:
188
- event.type === 'MCP_POLICY_UPDATED'
189
- ? event.trustMode
190
- : event.type === 'MCP_CONFIGURED'
191
- ? event.trustMode ?? 'ask-on-risk'
192
- : 'ask-on-risk',
193
- role:
194
- event.type === 'MCP_POLICY_UPDATED'
195
- ? event.role
196
- : event.type === 'MCP_CONFIGURED'
197
- ? event.role ?? 'general'
198
- : 'general',
199
- allowedPaths:
200
- event.type === 'MCP_POLICY_UPDATED'
201
- ? [...event.allowedPaths]
202
- : event.type === 'MCP_CONFIGURED'
203
- ? [...(event.allowedPaths ?? [])]
204
- : [],
205
- allowedHosts:
206
- event.type === 'MCP_POLICY_UPDATED'
207
- ? [...event.allowedHosts]
208
- : event.type === 'MCP_CONFIGURED'
209
- ? [...(event.allowedHosts ?? [])]
210
- : [],
211
- schemaFreshness:
212
- event.type === 'MCP_SCHEMA_QUARANTINED'
213
- ? 'quarantined'
214
- : event.type === 'MCP_SCHEMA_QUARANTINE_APPROVED'
215
- ? 'stale'
216
- : 'unknown',
217
- quarantineReason: event.type === 'MCP_SCHEMA_QUARANTINED' ? event.reason : undefined,
218
- quarantineDetail: event.type === 'MCP_SCHEMA_QUARANTINED' ? event.detail : undefined,
219
- quarantineApprovedBy: event.type === 'MCP_SCHEMA_QUARANTINE_APPROVED' ? event.operatorId : undefined,
220
- };
221
- servers.set(event.serverId, {
222
- ...server,
223
- status:
224
- event.type === 'MCP_POLICY_UPDATED' ||
225
- event.type === 'MCP_SCHEMA_QUARANTINED' ||
226
- event.type === 'MCP_SCHEMA_QUARANTINE_APPROVED'
227
- ? server.status
228
- : mcpStatusForEvent(event),
229
- transport:
230
- event.type === 'MCP_CONFIGURED'
231
- ? event.transport === 'sse' || event.transport === 'http'
232
- ? event.transport
233
- : 'stdio'
234
- : server.transport,
235
- toolCount: event.type === 'MCP_CONNECTED' ? event.toolCount : server.toolCount,
236
- connectedAt: event.type === 'MCP_CONNECTED' ? timestamp : server.connectedAt,
237
- reconnectAttempts: event.type === 'MCP_RECONNECTING' ? event.attempt : server.reconnectAttempts,
238
- trustMode: event.type === 'MCP_POLICY_UPDATED'
239
- ? event.trustMode
240
- : event.type === 'MCP_CONFIGURED'
241
- ? event.trustMode ?? server.trustMode
242
- : server.trustMode,
243
- role: event.type === 'MCP_POLICY_UPDATED'
244
- ? event.role
245
- : event.type === 'MCP_CONFIGURED'
246
- ? event.role ?? server.role
247
- : server.role,
248
- allowedPaths: event.type === 'MCP_POLICY_UPDATED'
249
- ? [...event.allowedPaths]
250
- : event.type === 'MCP_CONFIGURED'
251
- ? [...(event.allowedPaths ?? server.allowedPaths)]
252
- : server.allowedPaths,
253
- allowedHosts: event.type === 'MCP_POLICY_UPDATED'
254
- ? [...event.allowedHosts]
255
- : event.type === 'MCP_CONFIGURED'
256
- ? [...(event.allowedHosts ?? server.allowedHosts)]
257
- : server.allowedHosts,
258
- schemaFreshness:
259
- event.type === 'MCP_SCHEMA_QUARANTINED'
260
- ? 'quarantined'
261
- : event.type === 'MCP_SCHEMA_QUARANTINE_APPROVED'
262
- ? 'stale'
263
- : event.type === 'MCP_CONNECTED'
264
- ? 'fresh'
265
- : server.schemaFreshness,
266
- quarantineReason:
267
- event.type === 'MCP_SCHEMA_QUARANTINED'
268
- ? event.reason
269
- : server.quarantineReason,
270
- quarantineDetail:
271
- event.type === 'MCP_SCHEMA_QUARANTINED'
272
- ? event.detail
273
- : server.quarantineDetail,
274
- quarantineApprovedBy:
275
- event.type === 'MCP_SCHEMA_QUARANTINE_APPROVED'
276
- ? event.operatorId
277
- : server.quarantineApprovedBy,
278
- lastError:
279
- event.type === 'MCP_DEGRADED'
280
- ? event.reason
281
- : event.type === 'MCP_DISCONNECTED'
282
- ? event.reason
283
- : event.type === 'MCP_SCHEMA_QUARANTINED'
284
- ? event.detail ?? String(event.reason)
285
- : server.lastError,
286
- });
287
- const connectedServerNames = [...servers.values()].filter((value) => value.status === 'connected').map((value) => value.name);
288
- return {
289
- ...updateDomainMetadata(domain, event.type),
290
- servers,
291
- connectedServerNames,
292
- availableToolCount: [...servers.values()].reduce((sum, value) => sum + (value.status === 'connected' ? value.toolCount : 0), 0),
293
- totalErrors:
294
- domain.totalErrors +
295
- (event.type === 'MCP_DEGRADED' || event.type === 'MCP_DISCONNECTED' || event.type === 'MCP_SCHEMA_QUARANTINED' ? 1 : 0),
296
- };
297
- }
298
-
299
- function transportStateForEvent(event: TransportEvent): AcpTransportState | DaemonTransportState {
300
- switch (event.type) {
301
- case 'TRANSPORT_INITIALIZING':
302
- return 'initializing';
303
- case 'TRANSPORT_AUTHENTICATING':
304
- return 'authenticating';
305
- case 'TRANSPORT_CONNECTED':
306
- return 'connected';
307
- case 'TRANSPORT_SYNCING':
308
- return 'syncing';
309
- case 'TRANSPORT_DEGRADED':
310
- return 'degraded';
311
- case 'TRANSPORT_RECONNECTING':
312
- return 'reconnecting';
313
- case 'TRANSPORT_DISCONNECTED':
314
- return 'disconnected';
315
- case 'TRANSPORT_TERMINAL_FAILURE':
316
- return 'terminal_failure';
317
- }
318
- }
319
-
320
- export function updateTransportState(
321
- acp: AcpDomainState,
322
- daemon: DaemonDomainState,
323
- event: TransportEvent,
324
- ): Pick<import('../../state.ts').RuntimeState, 'acp' | 'daemon'> {
325
- const nextTransportState = transportStateForEvent(event);
326
- const isAcp = event.transportId.startsWith('acp');
327
- const nextAcp = isAcp
328
- ? {
329
- ...updateDomainMetadata(acp, event.type),
330
- managerTransportState: nextTransportState as AcpTransportState,
331
- initialized: acp.initialized || event.type === 'TRANSPORT_INITIALIZING',
332
- }
333
- : acp;
334
- const nextDaemon = !isAcp
335
- ? {
336
- ...updateDomainMetadata(daemon, event.type),
337
- transportState: nextTransportState as DaemonTransportState,
338
- isRunning:
339
- event.type === 'TRANSPORT_CONNECTED'
340
- ? true
341
- : event.type === 'TRANSPORT_DISCONNECTED'
342
- ? false
343
- : daemon.isRunning,
344
- reconnectAttempts: event.type === 'TRANSPORT_RECONNECTING' ? event.attempt : daemon.reconnectAttempts,
345
- lastConnectedAt: event.type === 'TRANSPORT_CONNECTED' ? now() : daemon.lastConnectedAt,
346
- lastError:
347
- event.type === 'TRANSPORT_DEGRADED'
348
- ? event.reason
349
- : event.type === 'TRANSPORT_DISCONNECTED'
350
- ? event.reason
351
- : event.type === 'TRANSPORT_TERMINAL_FAILURE'
352
- ? event.error
353
- : daemon.lastError,
354
- }
355
- : daemon;
356
- return { acp: nextAcp, daemon: nextDaemon };
357
- }
358
-
359
- export function updateIntegrationDomainFromRecord(domain: IntegrationDomainState, record: IntegrationRecord, source: string): IntegrationDomainState {
360
- const integrations = new Map(domain.integrations);
361
- const previous = integrations.get(record.id);
362
- integrations.set(record.id, record);
363
- const problemStatuses: IntegrationStatus[] = ['degraded', 'error'];
364
- const healthyIds = [...integrations.values()].filter((value) => value.status === 'healthy').map((value) => value.id);
365
- const problemIds = [...integrations.values()].filter((value) => problemStatuses.includes(value.status)).map((value) => value.id);
366
- return {
367
- ...updateDomainMetadata(domain, source),
368
- integrations,
369
- healthyIds,
370
- problemIds,
371
- totalOperations: domain.totalOperations + ((record.successCount ?? 0) - (previous?.successCount ?? 0)),
372
- totalErrors: domain.totalErrors + ((record.errorCount ?? 0) - (previous?.errorCount ?? 0)),
373
- };
374
- }
375
-
376
- export function updateAutomationDomainFromSource(domain: AutomationDomainState, sourceRecord: AutomationSourceRecord, source: string): AutomationDomainState {
377
- const sources = new Map(domain.sources);
378
- sources.set(sourceRecord.id, sourceRecord);
379
- const sourceIds = [...sources.values()].sort((a, b) => a.label.localeCompare(b.label) || a.createdAt - b.createdAt).map((record) => record.id);
380
- return { ...updateDomainMetadata(domain, source), sources, sourceIds };
381
- }
382
-
383
- export function updateAutomationDomainFromJob(domain: AutomationDomainState, job: AutomationJob, source: string): AutomationDomainState {
384
- const jobs = new Map(domain.jobs);
385
- const sources = new Map(domain.sources);
386
- jobs.set(job.id, job);
387
- sources.set(job.source.id, job.source);
388
- const allRuns = [...domain.runs.values()];
389
- const totalDeadLettered = allRuns.reduce(
390
- (count, run) => count + (run.deliveryAttempts?.filter((attempt) => attempt.status === 'dead_lettered').length ?? 0),
391
- 0,
392
- );
393
- return {
394
- ...updateDomainMetadata(domain, source),
395
- jobs,
396
- jobIds: [...jobs.values()].sort((a, b) => a.name.localeCompare(b.name) || a.createdAt - b.createdAt).map((record) => record.id),
397
- sources,
398
- sourceIds: [...sources.values()].sort((a, b) => a.label.localeCompare(b.label) || a.createdAt - b.createdAt).map((record) => record.id),
399
- totalJobs: jobs.size,
400
- totalRuns: allRuns.length,
401
- totalSucceeded: allRuns.filter((run) => run.status === 'completed').length,
402
- totalFailed: allRuns.filter((run) => run.status === 'failed').length,
403
- totalCancelled: allRuns.filter((run) => run.status === 'cancelled').length,
404
- totalDeadLettered,
405
- };
406
- }
407
-
408
- export function updateAutomationDomainFromRun(domain: AutomationDomainState, run: AutomationRun, source: string): AutomationDomainState {
409
- const runs = new Map(domain.runs);
410
- const sources = new Map(domain.sources);
411
- runs.set(run.id, run);
412
- sources.set(run.triggeredBy.id, run.triggeredBy);
413
- const allRuns = [...runs.values()];
414
- const totalDeadLettered = allRuns.reduce(
415
- (count, record) => count + (record.deliveryAttempts?.filter((attempt) => attempt.status === 'dead_lettered').length ?? 0),
416
- 0,
417
- );
418
- return {
419
- ...updateDomainMetadata(domain, source),
420
- runs,
421
- runIds: allRuns.sort((a, b) => b.queuedAt - a.queuedAt || a.id.localeCompare(b.id)).map((record) => record.id),
422
- activeRunIds: allRuns.filter((record) => record.status === 'queued' || record.status === 'running').map((record) => record.id),
423
- failedRunIds: allRuns.filter((record) => record.status === 'failed').map((record) => record.id),
424
- sources,
425
- sourceIds: [...sources.values()].sort((a, b) => a.label.localeCompare(b.label) || a.createdAt - b.createdAt).map((record) => record.id),
426
- totalJobs: domain.jobs.size,
427
- totalRuns: allRuns.length,
428
- totalSucceeded: allRuns.filter((record) => record.status === 'completed').length,
429
- totalFailed: allRuns.filter((record) => record.status === 'failed').length,
430
- totalCancelled: allRuns.filter((record) => record.status === 'cancelled').length,
431
- totalDeadLettered,
432
- };
433
- }
434
-
435
- function buildBindingIdsBySurface(bindings: readonly AutomationRouteBinding[]): Readonly<Record<string, readonly string[]>> {
436
- const grouped: Record<string, string[]> = {
437
- slack: [],
438
- discord: [],
439
- web: [],
440
- ntfy: [],
441
- webhook: [],
442
- tui: [],
443
- service: [],
444
- };
445
- for (const binding of bindings) {
446
- grouped[binding.surfaceKind] ??= [];
447
- grouped[binding.surfaceKind]!.push(binding.id);
448
- }
449
- return grouped;
450
- }
451
-
452
- export function updateRoutesDomainFromBinding(domain: RoutesDomainState, binding: AutomationRouteBinding, source: string): RoutesDomainState {
453
- const bindings = new Map(domain.bindings);
454
- bindings.set(binding.id, binding);
455
- const records = [...bindings.values()].sort((a, b) => b.lastSeenAt - a.lastSeenAt || a.id.localeCompare(b.id));
456
- return {
457
- ...updateDomainMetadata(domain, source),
458
- bindings,
459
- bindingIds: records.map((record) => record.id),
460
- bindingIdsBySurface: buildBindingIdsBySurface(records),
461
- activeBindingIds: records.map((record) => record.id),
462
- recentBindingIds: records.slice(0, 20).map((record) => record.id),
463
- totalBindings: records.length,
464
- totalResolved: records.filter((record) => record.sessionId || record.jobId || record.runId).length,
465
- };
466
- }
467
-
468
- export function updateRouteFailureState(domain: RoutesDomainState, _surfaceKind: AutomationSurfaceKind, _externalId: string, source: string): RoutesDomainState {
469
- return { ...updateDomainMetadata(domain, source), totalFailures: domain.totalFailures + 1 };
470
- }
471
-
472
- export function updateControlPlaneDomainFromClient(domain: ControlPlaneDomainState, client: ControlPlaneClientRecord, source: string): ControlPlaneDomainState {
473
- const clients = new Map(domain.clients);
474
- const previous = clients.get(client.id);
475
- clients.set(client.id, client);
476
- const records = [...clients.values()].sort((a, b) => (b.lastSeenAt ?? 0) - (a.lastSeenAt ?? 0) || a.id.localeCompare(b.id));
477
- const active = records.filter((record) => record.connected);
478
- return {
479
- ...updateDomainMetadata(domain, source),
480
- clients,
481
- activeClients: new Map(active.map((record) => [record.id, record])),
482
- clientIds: records.map((record) => record.id),
483
- activeClientIds: active.map((record) => record.id),
484
- isRunning: domain.isRunning || active.length > 0,
485
- connectionState: active.length > 0 ? 'connected' : domain.isRunning ? 'disconnected' : domain.connectionState,
486
- totalConnections: domain.totalConnections + (client.connected && !previous?.connected ? 1 : 0),
487
- totalDisconnects: domain.totalDisconnects + (!client.connected && previous?.connected ? 1 : 0),
488
- };
489
- }
490
-
491
- export function patchControlPlaneDomain(domain: ControlPlaneDomainState, patch: Partial<ControlPlaneDomainState>, source: string): ControlPlaneDomainState {
492
- return {
493
- ...updateDomainMetadata(domain, source),
494
- ...patch,
495
- totalFailures: patch.connectionState === 'terminal_failure' ? domain.totalFailures + 1 : patch.totalFailures ?? domain.totalFailures,
496
- };
497
- }
498
-
499
- export function updateDeliveryDomainFromAttempt(domain: DeliveryDomainState, attempt: AutomationDeliveryAttempt, source: string): DeliveryDomainState {
500
- const deliveryAttempts = new Map(domain.deliveryAttempts);
501
- deliveryAttempts.set(attempt.id, attempt);
502
- const attempts = [...deliveryAttempts.values()].sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0) || a.id.localeCompare(b.id));
503
- return {
504
- ...updateDomainMetadata(domain, source),
505
- deliveryAttempts,
506
- attemptIds: attempts.map((record) => record.id),
507
- pendingAttemptIds: attempts.filter((record) => record.status === 'pending' || record.status === 'sending').map((record) => record.id),
508
- failedAttemptIds: attempts.filter((record) => record.status === 'failed').map((record) => record.id),
509
- deadLetterIds: attempts.filter((record) => record.status === 'dead_lettered').map((record) => record.id),
510
- totalQueued: attempts.length,
511
- totalStarted: attempts.filter((record) => record.startedAt !== undefined || record.status !== 'pending').length,
512
- totalSucceeded: attempts.filter((record) => record.status === 'sent').length,
513
- totalFailed: attempts.filter((record) => record.status === 'failed').length,
514
- totalDeadLettered: attempts.filter((record) => record.status === 'dead_lettered').length,
515
- };
516
- }
517
-
518
- export function updateSurfaceDomainFromRecord(domain: SurfaceDomainState, record: SurfaceRecord, source: string): SurfaceDomainState {
519
- const surfaces = new Map(domain.surfaces);
520
- surfaces.set(record.id, record);
521
- const records = [...surfaces.values()].sort((a, b) => a.label.localeCompare(b.label) || a.configuredAt - b.configuredAt);
522
- return {
523
- ...updateDomainMetadata(domain, source),
524
- surfaces,
525
- surfaceIds: records.map((entry) => entry.id),
526
- enabledSurfaceIds: records.filter((entry) => entry.enabled).map((entry) => entry.id),
527
- problemSurfaceIds: records.filter((entry) => entry.state === 'degraded' || entry.state === 'error').map((entry) => entry.id),
528
- totalHealthy: records.filter((entry) => entry.state === 'healthy').length,
529
- totalDegraded: records.filter((entry) => entry.state === 'degraded' || entry.state === 'error').length,
530
- totalDisabled: records.filter((entry) => !entry.enabled || entry.state === 'disabled').length,
531
- };
532
- }
533
-
534
- export function updateWatcherDomainFromRecord(domain: WatcherDomainState, record: WatcherRecord, source: string): WatcherDomainState {
535
- const watchers = new Map(domain.watchers);
536
- watchers.set(record.id, record);
537
- const records = [...watchers.values()].sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id));
538
- return {
539
- ...updateDomainMetadata(domain, source),
540
- watchers,
541
- watcherIds: records.map((entry) => entry.id),
542
- activeWatcherIds: records.filter((entry) => entry.state === 'running' || entry.state === 'starting' || entry.state === 'degraded').map((entry) => entry.id),
543
- failedWatcherIds: records.filter((entry) => entry.state === 'failed').map((entry) => entry.id),
544
- totalStarted: records.filter((entry) => entry.state === 'running' || entry.state === 'starting').length,
545
- totalStopped: records.filter((entry) => entry.state === 'stopped').length,
546
- totalFailed: records.filter((entry) => entry.state === 'failed').length,
547
- totalHeartbeats: records.filter((entry) => entry.lastHeartbeatAt !== undefined).length,
548
- totalDegraded: records.filter((entry) => entry.state === 'degraded' || entry.sourceStatus === 'degraded').length,
549
- totalLagged: records.filter((entry) => entry.sourceStatus === 'lagging' || entry.sourceStatus === 'stale').length,
550
- };
551
- }
552
-
553
- export function syncSessionStatePatch(domain: SessionDomainState, patch: Partial<SessionDomainState>, source: string): SessionDomainState {
554
- return { ...updateDomainMetadata(domain, source), ...patch };
555
- }
@@ -1,30 +0,0 @@
1
- export { updateDomainMetadata } from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/shared';
2
- export { updateConversationState } from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/conversation';
3
- export {
4
- updateSessionState,
5
- updatePermissionState,
6
- updateTaskState,
7
- updateAgentState,
8
- updateOrchestrationState,
9
- transitionTaskDomainRecord,
10
- updateTaskDomainFromRecord,
11
- transitionAgentDomainRecord,
12
- } from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/lifecycle';
13
- export {
14
- updateCommunicationState,
15
- updatePluginState,
16
- updateMcpState,
17
- updateTransportState,
18
- updateIntegrationDomainFromRecord,
19
- updateAutomationDomainFromSource,
20
- updateAutomationDomainFromJob,
21
- updateAutomationDomainFromRun,
22
- updateRoutesDomainFromBinding,
23
- updateRouteFailureState,
24
- updateControlPlaneDomainFromClient,
25
- patchControlPlaneDomain,
26
- updateDeliveryDomainFromAttempt,
27
- updateSurfaceDomainFromRecord,
28
- updateWatcherDomainFromRecord,
29
- syncSessionStatePatch,
30
- } from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/sync';
@@ -1,95 +0,0 @@
1
- import type { RuntimeServices } from './services.ts';
2
- import type { RuntimeTask } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/tasks';
3
- import type { RuntimeAgent } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/agents';
4
- import type { SessionDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/session';
5
- import type { TurnState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/conversation';
6
- import { createStoreBackedReadModel, listProviderIds } from '@pellux/goodvibes-sdk/platform/runtime/ui-read-model-helpers';
7
- import type { UiReadModel } from '@pellux/goodvibes-sdk/platform/runtime/ui-read-models-base';
8
-
9
- export interface UiProvidersSnapshot {
10
- readonly providerIds: readonly string[];
11
- }
12
-
13
- export interface UiSessionSnapshot {
14
- readonly session: SessionDomainState;
15
- readonly totalTurns: number;
16
- readonly messageCount: number;
17
- readonly estimatedContextTokens: number;
18
- readonly contextWindow: number;
19
- readonly turnState: TurnState;
20
- readonly streamToolPreview?: string;
21
- readonly contextWarningActive: boolean;
22
- readonly pendingApproval: boolean;
23
- readonly denialCount: number;
24
- }
25
-
26
- export interface UiAgentsSnapshot {
27
- readonly active: readonly RuntimeAgent[];
28
- readonly totalSpawned: number;
29
- readonly totalCompleted: number;
30
- readonly totalFailed: number;
31
- }
32
-
33
- export interface UiTasksSnapshot {
34
- readonly tasks: readonly RuntimeTask[];
35
- }
36
-
37
- export interface UiCoreReadModels {
38
- readonly providers: UiReadModel<UiProvidersSnapshot>;
39
- readonly session: UiReadModel<UiSessionSnapshot>;
40
- readonly agents: UiReadModel<UiAgentsSnapshot>;
41
- readonly tasks: UiReadModel<UiTasksSnapshot>;
42
- }
43
-
44
- export function createCoreReadModels(runtimeServices: RuntimeServices): UiCoreReadModels {
45
- const { runtimeStore } = runtimeServices;
46
-
47
- return {
48
- providers: {
49
- getSnapshot() {
50
- return {
51
- providerIds: listProviderIds(runtimeServices),
52
- };
53
- },
54
- subscribe(listener) {
55
- return runtimeServices.runtimeBus.on('PROVIDERS_CHANGED', listener);
56
- },
57
- },
58
- session: createStoreBackedReadModel(runtimeServices, () => {
59
- const state = runtimeStore.getState();
60
- return {
61
- session: state.session,
62
- totalTurns: state.conversation.totalTurns,
63
- messageCount: state.conversation.messageCount,
64
- estimatedContextTokens: state.conversation.estimatedContextTokens,
65
- contextWindow: state.model.tokenLimits.contextWindow,
66
- turnState: state.conversation.turnState,
67
- streamToolPreview: state.conversation.stream.partialToolPreview,
68
- contextWarningActive: state.conversation.contextWarningActive,
69
- pendingApproval: state.permissions.awaitingDecision,
70
- denialCount: state.permissions.denialCount,
71
- };
72
- }),
73
- agents: createStoreBackedReadModel(runtimeServices, () => {
74
- const state = runtimeStore.getState().agents;
75
- const active = state.activeAgentIds
76
- .map((id) => state.agents.get(id))
77
- .filter((agent): agent is RuntimeAgent => agent !== undefined);
78
- return {
79
- active,
80
- totalSpawned: state.totalSpawned,
81
- totalCompleted: state.totalCompleted,
82
- totalFailed: state.totalFailed,
83
- };
84
- }),
85
- tasks: createStoreBackedReadModel(runtimeServices, () => {
86
- const tasksState = runtimeStore.getState().tasks;
87
- const tasks = [...tasksState.tasks.values()].sort((a, b) => {
88
- const aTime = a.startedAt ?? a.queuedAt;
89
- const bTime = b.startedAt ?? b.queuedAt;
90
- return bTime - aTime || a.title.localeCompare(b.title);
91
- });
92
- return { tasks };
93
- }),
94
- };
95
- }