@pellux/goodvibes-tui 0.18.12 → 0.18.13

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 (157) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/config/index.ts +1 -138
  6. package/src/config/subscription-providers.ts +1 -127
  7. package/src/core/conversation-rendering.ts +3 -3
  8. package/src/core/conversation.ts +176 -423
  9. package/src/core/history.ts +45 -0
  10. package/src/core/orchestrator.ts +3 -735
  11. package/src/core/system-message-router.ts +19 -58
  12. package/src/input/handler-content-actions.ts +2 -2
  13. package/src/input/handler-feed.ts +1 -1
  14. package/src/input/handler-modal-token-routes.ts +1 -1
  15. package/src/input/handler-ui-state.ts +1 -1
  16. package/src/input/handler.ts +1 -1
  17. package/src/input/search.ts +1 -1
  18. package/src/input/selection.ts +2 -2
  19. package/src/main.ts +1 -1
  20. package/src/panels/agent-inspector-panel.ts +3 -3
  21. package/src/panels/agent-logs-panel.ts +3 -3
  22. package/src/panels/approval-panel.ts +2 -2
  23. package/src/panels/automation-control-panel.ts +3 -3
  24. package/src/panels/base-panel.ts +14 -14
  25. package/src/panels/builtin/operations.ts +1 -1
  26. package/src/panels/builtin/session.ts +1 -1
  27. package/src/panels/builtin/shared.ts +3 -3
  28. package/src/panels/cockpit-panel.ts +2 -2
  29. package/src/panels/communication-panel.ts +3 -3
  30. package/src/panels/context-visualizer-panel.ts +2 -2
  31. package/src/panels/control-plane-panel.ts +3 -3
  32. package/src/panels/cost-tracker-panel.ts +3 -3
  33. package/src/panels/debug-panel.ts +2 -2
  34. package/src/panels/diff-panel.ts +2 -2
  35. package/src/panels/docs-panel.ts +1 -1
  36. package/src/panels/eval-panel.ts +2 -2
  37. package/src/panels/file-explorer-panel.ts +3 -3
  38. package/src/panels/file-preview-panel.ts +3 -3
  39. package/src/panels/forensics-panel.ts +2 -2
  40. package/src/panels/git-panel.ts +1 -1
  41. package/src/panels/hooks-panel.ts +3 -3
  42. package/src/panels/incident-review-panel.ts +1 -1
  43. package/src/panels/intelligence-panel.ts +2 -2
  44. package/src/panels/knowledge-panel.ts +1 -1
  45. package/src/panels/local-auth-panel.ts +2 -2
  46. package/src/panels/marketplace-panel.ts +1 -1
  47. package/src/panels/mcp-panel.ts +3 -3
  48. package/src/panels/memory-panel.ts +1 -1
  49. package/src/panels/ops-control-panel.ts +3 -3
  50. package/src/panels/ops-strategy-panel.ts +2 -2
  51. package/src/panels/orchestration-panel.ts +2 -2
  52. package/src/panels/panel-list-panel.ts +6 -6
  53. package/src/panels/plan-dashboard-panel.ts +1 -1
  54. package/src/panels/plugins-panel.ts +2 -2
  55. package/src/panels/policy-panel.ts +2 -2
  56. package/src/panels/polish.ts +3 -3
  57. package/src/panels/provider-accounts-panel.ts +2 -2
  58. package/src/panels/provider-health-panel.ts +2 -2
  59. package/src/panels/provider-stats-panel.ts +3 -3
  60. package/src/panels/remote-panel.ts +3 -3
  61. package/src/panels/routes-panel.ts +3 -3
  62. package/src/panels/sandbox-panel.ts +2 -2
  63. package/src/panels/schedule-panel.ts +1 -1
  64. package/src/panels/security-panel.ts +2 -2
  65. package/src/panels/services-panel.ts +2 -2
  66. package/src/panels/session-browser-panel.ts +2 -2
  67. package/src/panels/settings-sync-panel.ts +2 -2
  68. package/src/panels/skills-panel.ts +6 -6
  69. package/src/panels/subscription-panel.ts +2 -2
  70. package/src/panels/symbol-outline-panel.ts +3 -3
  71. package/src/panels/system-messages-panel.ts +4 -4
  72. package/src/panels/tasks-panel.ts +2 -2
  73. package/src/panels/thinking-panel.ts +3 -3
  74. package/src/panels/token-budget-panel.ts +1 -1
  75. package/src/panels/tool-inspector-panel.ts +3 -3
  76. package/src/panels/types.ts +5 -5
  77. package/src/panels/watchers-panel.ts +3 -3
  78. package/src/panels/welcome-panel.ts +1 -1
  79. package/src/panels/worktree-panel.ts +2 -2
  80. package/src/panels/wrfc-panel.ts +3 -3
  81. package/src/permissions/prompt.ts +3 -22
  82. package/src/plugins/loader.ts +15 -304
  83. package/src/renderer/agent-detail-modal.ts +1 -1
  84. package/src/renderer/autocomplete-overlay.ts +2 -2
  85. package/src/renderer/bookmark-modal.ts +1 -1
  86. package/src/renderer/bottom-bar.ts +2 -2
  87. package/src/renderer/buffer.ts +1 -1
  88. package/src/renderer/code-block.ts +2 -2
  89. package/src/renderer/compositor.ts +2 -2
  90. package/src/renderer/context-inspector.ts +1 -1
  91. package/src/renderer/conversation-layout.ts +2 -2
  92. package/src/renderer/conversation-overlays.ts +1 -1
  93. package/src/renderer/conversation-surface.ts +2 -2
  94. package/src/renderer/diff-view.ts +2 -2
  95. package/src/renderer/diff.ts +1 -1
  96. package/src/renderer/file-picker-overlay.ts +2 -2
  97. package/src/renderer/file-tree.ts +2 -2
  98. package/src/renderer/help-overlay.ts +1 -1
  99. package/src/renderer/history-search-overlay.ts +2 -2
  100. package/src/renderer/live-tail-modal.ts +1 -1
  101. package/src/renderer/markdown.ts +2 -2
  102. package/src/renderer/modal-factory.ts +3 -3
  103. package/src/renderer/model-picker-overlay.ts +2 -2
  104. package/src/renderer/overlay-box.ts +2 -2
  105. package/src/renderer/panel-composite.ts +1 -1
  106. package/src/renderer/panel-picker-overlay.ts +2 -2
  107. package/src/renderer/panel-tab-bar.ts +1 -1
  108. package/src/renderer/panel-workspace-bar.ts +1 -1
  109. package/src/renderer/process-indicator.ts +2 -2
  110. package/src/renderer/process-modal.ts +1 -1
  111. package/src/renderer/profile-picker-modal.ts +2 -2
  112. package/src/renderer/progress.ts +2 -2
  113. package/src/renderer/search-overlay.ts +2 -2
  114. package/src/renderer/selection-modal-overlay.ts +2 -2
  115. package/src/renderer/session-picker-modal.ts +2 -2
  116. package/src/renderer/settings-modal.ts +2 -2
  117. package/src/renderer/shell-surface.ts +1 -1
  118. package/src/renderer/system-message.ts +1 -1
  119. package/src/renderer/tab-strip.ts +2 -2
  120. package/src/renderer/text-layout.ts +1 -1
  121. package/src/renderer/thinking.ts +1 -1
  122. package/src/renderer/tool-call.ts +2 -2
  123. package/src/renderer/ui-factory.ts +2 -2
  124. package/src/runtime/bootstrap-command-context.ts +4 -5
  125. package/src/runtime/bootstrap-command-parts.ts +1 -3
  126. package/src/runtime/bootstrap-core.ts +3 -2
  127. package/src/runtime/bootstrap-hook-bridge.ts +15 -174
  128. package/src/runtime/bootstrap-shell.ts +4 -4
  129. package/src/runtime/bootstrap.ts +1 -1
  130. package/src/runtime/context.ts +4 -20
  131. package/src/runtime/diagnostics/panels/index.ts +1 -1
  132. package/src/runtime/diagnostics/panels/ops.ts +1 -1
  133. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  134. package/src/runtime/perf/panel-contracts.ts +32 -0
  135. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  136. package/src/runtime/services.ts +4 -4
  137. package/src/runtime/store/domains/conversation.ts +1 -181
  138. package/src/runtime/store/domains/permissions.ts +1 -143
  139. package/src/runtime/store/helpers/reducers/conversation.ts +1 -228
  140. package/src/runtime/store/helpers/reducers/lifecycle.ts +1 -440
  141. package/src/runtime/store/selectors/index.ts +11 -6
  142. package/src/runtime/store/state.ts +12 -4
  143. package/src/runtime/ui-events.ts +46 -0
  144. package/src/runtime/ui-services.ts +1 -1
  145. package/src/shell/ui-openers.ts +1 -1
  146. package/src/tools/index.ts +1 -186
  147. package/src/types/grid.ts +48 -0
  148. package/src/utils/clipboard.ts +21 -0
  149. package/src/utils/splash-lines.ts +1 -1
  150. package/src/utils/terminal-width.ts +185 -0
  151. package/src/version.ts +1 -1
  152. package/src/daemon/facade-composition.ts +0 -398
  153. package/src/daemon/facade.ts +0 -638
  154. package/src/daemon/surface-policy.ts +0 -60
  155. package/src/daemon/types.ts +0 -191
  156. package/src/runtime/ui-read-models-core.ts +0 -95
  157. package/src/runtime/ui-read-models-operations.ts +0 -203
@@ -1,440 +1 @@
1
- import type { CompactionEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/compaction';
2
- import type { PermissionEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/permissions';
3
- import type { TaskEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/tasks';
4
- import type { AgentEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/agents';
5
- import type { OrchestrationEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/orchestration';
6
- import type { SessionDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/session';
7
- import type {
8
- PermissionDomainState,
9
- PermissionDecisionMachineState,
10
- PermissionDecision,
11
- } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/permissions';
12
- import type { TaskDomainState, RuntimeTask, TaskLifecycleState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/tasks';
13
- import type { AgentDomainState, RuntimeAgent, AgentLifecycleState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/agents';
14
- import type {
15
- OrchestrationDomainState,
16
- OrchestrationGraphRecord,
17
- OrchestrationNodeRecord,
18
- } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/orchestration';
19
- import type { PermissionCategory } from '@pellux/goodvibes-sdk/platform/permissions/manager';
20
- import { now, uniq, updateDomainMetadata } from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/shared';
21
-
22
- function permissionMachineStateForEvent(event: PermissionEvent): PermissionDecisionMachineState {
23
- switch (event.type) {
24
- case 'PERMISSION_REQUESTED':
25
- return 'collect_rules';
26
- case 'RULES_COLLECTED':
27
- return 'normalize_input';
28
- case 'INPUT_NORMALIZED':
29
- return 'evaluate_policy';
30
- case 'POLICY_EVALUATED':
31
- return 'evaluate_runtime_mode';
32
- case 'MODE_EVALUATED':
33
- return 'evaluate_session_override';
34
- case 'SESSION_OVERRIDE_EVALUATED':
35
- return 'final_safety_checks';
36
- case 'SAFETY_CHECKED':
37
- case 'DECISION_EMITTED':
38
- return 'decision_emitted';
39
- }
40
- }
41
-
42
- function inferPermissionCategory(toolName: string): PermissionCategory {
43
- if (toolName === 'agent' || toolName === 'delegate') return 'delegate';
44
- if (toolName === 'write' || toolName === 'edit' || toolName === 'apply_patch') return 'write';
45
- if (toolName === 'exec' || toolName === 'precision_exec' || toolName === 'bash') return 'execute';
46
- return 'read';
47
- }
48
-
49
- export function updateSessionState(
50
- domain: SessionDomainState,
51
- event: CompactionEvent,
52
- ): SessionDomainState {
53
- const base = updateDomainMetadata(domain, event.type);
54
- switch (event.type) {
55
- case 'COMPACTION_CHECK':
56
- return { ...base, compactionState: 'checking_threshold' };
57
- case 'COMPACTION_MICROCOMPACT':
58
- return { ...base, compactionState: 'microcompact' };
59
- case 'COMPACTION_COLLAPSE':
60
- return { ...base, compactionState: 'collapse', compactionMessageCount: event.messageCount };
61
- case 'COMPACTION_AUTOCOMPACT':
62
- return { ...base, compactionState: 'autocompact' };
63
- case 'COMPACTION_REACTIVE':
64
- return { ...base, compactionState: 'reactive_compact' };
65
- case 'COMPACTION_BOUNDARY_COMMIT':
66
- return { ...base, compactionState: 'boundary_commit' };
67
- case 'COMPACTION_DONE':
68
- return { ...base, compactionState: 'done', lastCompactedAt: now() };
69
- case 'COMPACTION_FAILED':
70
- return { ...base, compactionState: 'failed', recoveryError: event.error };
71
- case 'COMPACTION_RESUME_REPAIR':
72
- return { ...base, wasRepaired: event.repaired, recoveryState: event.safeToResume ? 'ready' : domain.recoveryState };
73
- case 'COMPACTION_QUALITY_SCORE':
74
- case 'COMPACTION_STRATEGY_SWITCH':
75
- return base;
76
- }
77
- }
78
-
79
- export function updatePermissionState(
80
- domain: PermissionDomainState,
81
- event: PermissionEvent,
82
- ): PermissionDomainState {
83
- const base = updateDomainMetadata(domain, event.type);
84
- switch (event.type) {
85
- case 'PERMISSION_REQUESTED':
86
- return { ...base, awaitingDecision: true, decisionMachineState: permissionMachineStateForEvent(event), totalChecks: domain.totalChecks + 1 };
87
- case 'RULES_COLLECTED':
88
- case 'INPUT_NORMALIZED':
89
- case 'POLICY_EVALUATED':
90
- case 'MODE_EVALUATED':
91
- case 'SESSION_OVERRIDE_EVALUATED':
92
- case 'SAFETY_CHECKED':
93
- return { ...base, awaitingDecision: true, decisionMachineState: permissionMachineStateForEvent(event) };
94
- case 'DECISION_EMITTED':
95
- return {
96
- ...base,
97
- awaitingDecision: false,
98
- decisionMachineState: permissionMachineStateForEvent(event),
99
- approvalCount: domain.approvalCount + (event.approved ? 1 : 0),
100
- denialCount: domain.denialCount + (event.approved ? 0 : 1),
101
- lastDecision: {
102
- callId: event.callId,
103
- toolName: event.tool,
104
- category: inferPermissionCategory(event.tool),
105
- machineState: 'decision_emitted',
106
- outcome: event.approved ? 'approved' : 'denied',
107
- reason: (event.reasonCode as PermissionDecision['reason']) ?? (event.approved ? 'user_approved' : 'user_denied'),
108
- sourceLayer: (event.sourceLayer as PermissionDecision['sourceLayer']) ?? (event.source as PermissionDecision['sourceLayer']) ?? 'config_policy',
109
- persisted: event.persisted ?? false,
110
- classification: event.classification,
111
- riskLevel: event.riskLevel as PermissionDecision['riskLevel'],
112
- summary: event.summary,
113
- decidedAt: now(),
114
- },
115
- };
116
- }
117
- }
118
-
119
- function updateTaskIndexes(tasks: Map<string, RuntimeTask>) {
120
- const queuedIds: string[] = [];
121
- const runningIds: string[] = [];
122
- const blockedIds: string[] = [];
123
- for (const [taskId, task] of tasks.entries()) {
124
- if (task.status === 'queued') queuedIds.push(taskId);
125
- if (task.status === 'running') runningIds.push(taskId);
126
- if (task.status === 'blocked') blockedIds.push(taskId);
127
- }
128
- return { queuedIds, runningIds, blockedIds };
129
- }
130
-
131
- export function updateTaskState(domain: TaskDomainState, event: TaskEvent): TaskDomainState {
132
- const tasks = new Map(domain.tasks);
133
- const existing = tasks.get(event.taskId);
134
- const timestamp = now();
135
- const task: RuntimeTask =
136
- existing ??
137
- {
138
- id: event.taskId,
139
- kind: 'agentId' in event && event.agentId ? 'agent' : 'exec',
140
- title: 'description' in event ? event.description : `task:${event.taskId}`,
141
- status: 'queued',
142
- owner: event.agentId ?? 'runtime',
143
- cancellable: true,
144
- childTaskIds: [],
145
- queuedAt: timestamp,
146
- };
147
- switch (event.type) {
148
- case 'TASK_CREATED':
149
- tasks.set(event.taskId, task);
150
- break;
151
- case 'TASK_STARTED':
152
- tasks.set(event.taskId, { ...task, status: 'running', startedAt: task.startedAt ?? timestamp });
153
- break;
154
- case 'TASK_BLOCKED':
155
- tasks.set(event.taskId, { ...task, status: 'blocked', error: event.reason });
156
- break;
157
- case 'TASK_PROGRESS':
158
- tasks.set(event.taskId, { ...task, description: event.message ?? task.description });
159
- break;
160
- case 'TASK_COMPLETED':
161
- tasks.set(event.taskId, { ...task, status: 'completed', endedAt: timestamp, result: { durationMs: event.durationMs } });
162
- break;
163
- case 'TASK_FAILED':
164
- tasks.set(event.taskId, { ...task, status: 'failed', endedAt: timestamp, error: event.error });
165
- break;
166
- case 'TASK_CANCELLED':
167
- tasks.set(event.taskId, { ...task, status: 'cancelled', endedAt: timestamp, error: event.reason });
168
- break;
169
- }
170
- const indexes = updateTaskIndexes(tasks);
171
- return {
172
- ...updateDomainMetadata(domain, event.type),
173
- tasks,
174
- ...indexes,
175
- totalCreated: domain.totalCreated + (event.type === 'TASK_CREATED' ? 1 : 0),
176
- totalCompleted: domain.totalCompleted + (event.type === 'TASK_COMPLETED' ? 1 : 0),
177
- totalFailed: domain.totalFailed + (event.type === 'TASK_FAILED' ? 1 : 0),
178
- totalCancelled: domain.totalCancelled + (event.type === 'TASK_CANCELLED' ? 1 : 0),
179
- };
180
- }
181
-
182
- function updateTaskDomainFromRecord(domain: TaskDomainState, task: RuntimeTask, source: string): TaskDomainState {
183
- const tasks = new Map(domain.tasks);
184
- const previous = tasks.get(task.id);
185
- tasks.set(task.id, task);
186
- const indexes = updateTaskIndexes(tasks);
187
-
188
- let { totalCreated, totalCompleted, totalFailed, totalCancelled } = domain;
189
- if (!previous) totalCreated += 1;
190
- if (previous?.status !== task.status) {
191
- if (task.status === 'completed') totalCompleted += 1;
192
- else if (task.status === 'failed') totalFailed += 1;
193
- else if (task.status === 'cancelled') totalCancelled += 1;
194
- }
195
-
196
- return { ...updateDomainMetadata(domain, source), tasks, ...indexes, totalCreated, totalCompleted, totalFailed, totalCancelled };
197
- }
198
-
199
- function transitionTaskDomainRecord(
200
- domain: TaskDomainState,
201
- taskId: string,
202
- status: TaskLifecycleState,
203
- patch: Partial<RuntimeTask> | undefined,
204
- source: string,
205
- ): TaskDomainState {
206
- const existing = domain.tasks.get(taskId);
207
- if (!existing) return domain;
208
- return updateTaskDomainFromRecord(domain, { ...existing, ...patch, status }, source);
209
- }
210
-
211
- export function updateAgentState(domain: AgentDomainState, event: AgentEvent): AgentDomainState {
212
- const agents = new Map(domain.agents);
213
- const timestamp = now();
214
- const existing = agents.get(event.agentId);
215
- const statusMap: Partial<Record<AgentEvent['type'], AgentLifecycleState>> = {
216
- AGENT_SPAWNING: 'spawning',
217
- AGENT_RUNNING: 'running',
218
- AGENT_PROGRESS: 'running',
219
- AGENT_STREAM_DELTA: 'running',
220
- AGENT_AWAITING_MESSAGE: 'awaiting_message',
221
- AGENT_AWAITING_TOOL: 'awaiting_tool',
222
- AGENT_FINALIZING: 'finalizing',
223
- AGENT_COMPLETED: 'completed',
224
- AGENT_FAILED: 'failed',
225
- AGENT_CANCELLED: 'cancelled',
226
- };
227
- const agent: RuntimeAgent =
228
- existing ??
229
- {
230
- id: event.agentId,
231
- label: 'task' in event ? event.task : event.agentId,
232
- role: 'subagent',
233
- status: statusMap[event.type] ?? 'running',
234
- providerId: 'unknown',
235
- modelId: 'unknown',
236
- childAgentIds: [],
237
- taskId: event.taskId,
238
- turnCount: 0,
239
- toolCallCount: 0,
240
- latestOutput: '',
241
- spawnedAt: timestamp,
242
- };
243
- agents.set(event.agentId, {
244
- ...agent,
245
- status: statusMap[event.type] ?? agent.status,
246
- taskId: event.taskId ?? agent.taskId,
247
- latestProgress:
248
- event.type === 'AGENT_PROGRESS'
249
- ? event.progress
250
- : event.type === 'AGENT_AWAITING_TOOL'
251
- ? `${event.tool}:${event.callId}`
252
- : agent.latestProgress,
253
- latestOutput:
254
- event.type === 'AGENT_STREAM_DELTA'
255
- ? event.accumulated
256
- : event.type === 'AGENT_COMPLETED' && event.output !== undefined
257
- ? event.output
258
- : agent.latestOutput,
259
- endedAt:
260
- event.type === 'AGENT_COMPLETED' || event.type === 'AGENT_FAILED' || event.type === 'AGENT_CANCELLED'
261
- ? timestamp
262
- : agent.endedAt,
263
- error: event.type === 'AGENT_FAILED' ? event.error : agent.error,
264
- toolCallCount:
265
- event.type === 'AGENT_COMPLETED' && event.toolCallsMade !== undefined
266
- ? event.toolCallsMade
267
- : agent.toolCallCount,
268
- result:
269
- event.type === 'AGENT_COMPLETED'
270
- ? {
271
- durationMs: event.durationMs,
272
- ...(event.output !== undefined ? { output: event.output } : {}),
273
- ...(event.toolCallsMade !== undefined ? { toolCallsMade: event.toolCallsMade } : {}),
274
- }
275
- : agent.result,
276
- });
277
- const activeAgentIds = [...agents.values()].filter((value) => !['completed', 'failed', 'cancelled'].includes(value.status)).map((value) => value.id);
278
- return {
279
- ...updateDomainMetadata(domain, event.type),
280
- agents,
281
- activeAgentIds,
282
- totalSpawned: domain.totalSpawned + (event.type === 'AGENT_SPAWNING' ? 1 : 0),
283
- totalCompleted: domain.totalCompleted + (event.type === 'AGENT_COMPLETED' ? 1 : 0),
284
- totalFailed: domain.totalFailed + (event.type === 'AGENT_FAILED' ? 1 : 0),
285
- peakConcurrency: Math.max(domain.peakConcurrency, activeAgentIds.length),
286
- };
287
- }
288
-
289
- function transitionAgentDomainRecord(
290
- domain: AgentDomainState,
291
- agentId: string,
292
- status: AgentLifecycleState,
293
- patch: Partial<RuntimeAgent> | undefined,
294
- source: string,
295
- ): AgentDomainState {
296
- const existing = domain.agents.get(agentId);
297
- if (!existing) return domain;
298
-
299
- const agents = new Map(domain.agents);
300
- agents.set(agentId, { ...existing, ...patch, status });
301
- const activeAgentIds = [...agents.values()].filter((value) => !['completed', 'failed', 'cancelled'].includes(value.status)).map((value) => value.id);
302
-
303
- return {
304
- ...updateDomainMetadata(domain, source),
305
- agents,
306
- activeAgentIds,
307
- totalCompleted: domain.totalCompleted + (existing.status !== 'completed' && status === 'completed' ? 1 : 0),
308
- totalFailed: domain.totalFailed + (existing.status !== 'failed' && status === 'failed' ? 1 : 0),
309
- peakConcurrency: Math.max(domain.peakConcurrency, activeAgentIds.length),
310
- };
311
- }
312
-
313
- function orchestrationGraphStatus(graph: OrchestrationGraphRecord): OrchestrationGraphRecord['status'] {
314
- const nodes = [...graph.nodes.values()];
315
- if (nodes.length === 0) return 'planning';
316
- if (nodes.some((node) => node.status === 'failed')) return 'failed';
317
- if (nodes.some((node) => node.status === 'blocked')) return 'blocked';
318
- if (nodes.some((node) => node.status === 'running')) return 'running';
319
- if (nodes.every((node) => node.status === 'cancelled')) return 'cancelled';
320
- if (nodes.every((node) => node.status === 'completed')) return 'completed';
321
- if (nodes.every((node) => node.status === 'pending' || node.status === 'ready')) {
322
- return nodes.some((node) => node.status === 'ready') ? 'ready' : 'planning';
323
- }
324
- return 'running';
325
- }
326
-
327
- export function updateOrchestrationState(
328
- domain: OrchestrationDomainState,
329
- event: OrchestrationEvent,
330
- ): OrchestrationDomainState {
331
- const graphs = new Map(domain.graphs);
332
- const timestamp = now();
333
- const existing = 'graphId' in event ? graphs.get(event.graphId) : undefined;
334
-
335
- switch (event.type) {
336
- case 'ORCHESTRATION_GRAPH_CREATED':
337
- graphs.set(event.graphId, {
338
- id: event.graphId,
339
- title: event.title,
340
- mode: event.mode,
341
- status: 'planning',
342
- nodeOrder: [],
343
- nodes: new Map(),
344
- createdAt: timestamp,
345
- });
346
- break;
347
- case 'ORCHESTRATION_NODE_ADDED': {
348
- if (!existing) return domain;
349
- const nodes = new Map(existing.nodes);
350
- const previousParent = event.parentNodeId ? nodes.get(event.parentNodeId) : undefined;
351
- const nextNode: OrchestrationNodeRecord = {
352
- id: event.nodeId,
353
- title: event.title,
354
- role: event.role,
355
- status: 'pending',
356
- parentNodeId: event.parentNodeId,
357
- childNodeIds: [],
358
- dependencyNodeIds: event.dependsOn ?? [],
359
- ...(event.taskId !== undefined ? { taskId: event.taskId } : {}),
360
- ...(event.agentId !== undefined ? { agentId: event.agentId } : {}),
361
- ...(event.contract !== undefined ? { contract: event.contract } : {}),
362
- };
363
- nodes.set(event.nodeId, nextNode);
364
- if (previousParent) {
365
- nodes.set(event.parentNodeId!, { ...previousParent, childNodeIds: uniq([...previousParent.childNodeIds, event.nodeId]) });
366
- }
367
- const graph: OrchestrationGraphRecord = { ...existing, nodeOrder: uniq([...existing.nodeOrder, event.nodeId]), nodes };
368
- graph.status = orchestrationGraphStatus(graph);
369
- graphs.set(event.graphId, graph);
370
- break;
371
- }
372
- case 'ORCHESTRATION_NODE_READY':
373
- case 'ORCHESTRATION_NODE_STARTED':
374
- case 'ORCHESTRATION_NODE_PROGRESS':
375
- case 'ORCHESTRATION_NODE_BLOCKED':
376
- case 'ORCHESTRATION_NODE_COMPLETED':
377
- case 'ORCHESTRATION_NODE_FAILED':
378
- case 'ORCHESTRATION_NODE_CANCELLED':
379
- case 'ORCHESTRATION_RECURSION_GUARD_TRIGGERED': {
380
- if (!existing) return domain;
381
- const nodes = new Map(existing.nodes);
382
- const nodeId = 'nodeId' in event ? event.nodeId : undefined;
383
- if (nodeId) {
384
- const node = nodes.get(nodeId);
385
- if (!node) return domain;
386
- const updatedNode: OrchestrationNodeRecord =
387
- event.type === 'ORCHESTRATION_NODE_READY'
388
- ? { ...node, status: 'ready' }
389
- : event.type === 'ORCHESTRATION_NODE_STARTED'
390
- ? { ...node, status: 'running', startedAt: node.startedAt ?? timestamp, ...(event.taskId !== undefined ? { taskId: event.taskId } : {}), ...(event.agentId !== undefined ? { agentId: event.agentId } : {}) }
391
- : event.type === 'ORCHESTRATION_NODE_PROGRESS'
392
- ? { ...node, latestMessage: event.message }
393
- : event.type === 'ORCHESTRATION_NODE_BLOCKED'
394
- ? { ...node, status: 'blocked', error: event.reason }
395
- : event.type === 'ORCHESTRATION_NODE_COMPLETED'
396
- ? { ...node, status: 'completed', endedAt: timestamp, latestMessage: event.summary ?? node.latestMessage }
397
- : event.type === 'ORCHESTRATION_NODE_FAILED'
398
- ? { ...node, status: 'failed', endedAt: timestamp, error: event.error }
399
- : { ...node, status: 'cancelled', endedAt: timestamp, error: event.reason };
400
- nodes.set(nodeId, updatedNode);
401
- }
402
- const graph: OrchestrationGraphRecord = {
403
- ...existing,
404
- nodes,
405
- ...(event.type === 'ORCHESTRATION_NODE_STARTED' ? { startedAt: existing.startedAt ?? timestamp } : {}),
406
- ...(event.type === 'ORCHESTRATION_RECURSION_GUARD_TRIGGERED'
407
- ? {
408
- lastRecursionGuard: {
409
- depth: event.depth,
410
- activeAgents: event.activeAgents,
411
- reason: event.reason,
412
- ...(event.nodeId !== undefined ? { nodeId: event.nodeId } : {}),
413
- triggeredAt: timestamp,
414
- },
415
- }
416
- : {}),
417
- };
418
- graph.status = orchestrationGraphStatus(graph);
419
- if (graph.status === 'completed' || graph.status === 'failed' || graph.status === 'cancelled') {
420
- graph.endedAt = graph.endedAt ?? timestamp;
421
- }
422
- graphs.set(graph.id, graph);
423
- break;
424
- }
425
- }
426
-
427
- const activeGraphIds = [...graphs.values()].filter((graph) => !['completed', 'failed', 'cancelled'].includes(graph.status)).map((graph) => graph.id);
428
-
429
- return {
430
- ...updateDomainMetadata(domain, event.type),
431
- graphs,
432
- activeGraphIds,
433
- totalGraphs: graphs.size,
434
- totalCompletedGraphs: [...graphs.values()].filter((graph) => graph.status === 'completed').length,
435
- totalFailedGraphs: [...graphs.values()].filter((graph) => graph.status === 'failed').length,
436
- recursionGuardTrips: domain.recursionGuardTrips + (event.type === 'ORCHESTRATION_RECURSION_GUARD_TRIGGERED' ? 1 : 0),
437
- };
438
- }
439
-
440
- export { updateTaskDomainFromRecord, transitionTaskDomainRecord, transitionAgentDomainRecord };
1
+ export * from '@pellux/goodvibes-sdk/platform/runtime/store/helpers/reducers/lifecycle';
@@ -53,9 +53,9 @@ export function selectOverlays(state: RuntimeState): OverlayDomainState {
53
53
  return state.overlays;
54
54
  }
55
55
 
56
- /** Select the full panels domain slice. */
56
+ /** Select the full panels domain slice. Casts from Record<string,unknown> to PanelDomainState. */
57
57
  export function selectPanels(state: RuntimeState): PanelDomainState {
58
- return state.panels;
58
+ return state.panels as unknown as PanelDomainState;
59
59
  }
60
60
 
61
61
  /** Select the full permissions domain slice. */
@@ -123,11 +123,14 @@ export function selectIntelligence(state: RuntimeState): IntelligenceDomainState
123
123
  return state.intelligence;
124
124
  }
125
125
 
126
- /** Select the full UI performance domain slice. */
126
+ /** Select the full surface/UI performance domain slice. */
127
127
  export function selectUiPerf(state: RuntimeState): UiPerfDomainState {
128
- return state.uiPerf;
128
+ return state.surfacePerf;
129
129
  }
130
130
 
131
+ /** Select the full surface performance domain slice (SDK-compatible alias). */
132
+ export const selectSurfacePerf = selectUiPerf;
133
+
131
134
  // ---------------------------------------------------------------------------
132
135
  // Derived selectors
133
136
  // ---------------------------------------------------------------------------
@@ -271,7 +274,8 @@ export function selectPermissionMode(state: RuntimeState): PermissionMode {
271
274
  */
272
275
  export function selectActivePanels(state: RuntimeState): PanelState[] {
273
276
  const result: PanelState[] = [];
274
- for (const panel of state.panels.panels.values()) {
277
+ const panelDomain = state.panels as unknown as PanelDomainState;
278
+ for (const panel of panelDomain.panels.values()) {
275
279
  if (panel.open) {
276
280
  result.push(panel);
277
281
  }
@@ -283,7 +287,8 @@ export function selectActivePanels(state: RuntimeState): PanelState[] {
283
287
  * Returns the panel currently holding focus.
284
288
  */
285
289
  export function selectFocusedPanel(state: RuntimeState): PanelState | undefined {
286
- return state.panels.panels.get(state.panels.focusedPanelId);
290
+ const panelDomain = state.panels as unknown as PanelDomainState;
291
+ return panelDomain.panels.get(panelDomain.focusedPanelId);
287
292
  }
288
293
 
289
294
  /**
@@ -33,6 +33,9 @@ import type { GitDomainState } from '@pellux/goodvibes-sdk/platform/runtime/stor
33
33
  import type { DiscoveryDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/discovery';
34
34
  import type { IntelligenceDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/intelligence';
35
35
  import type { UiPerfDomainState } from './domains/ui-perf.ts';
36
+ // UiPerfDomainState is structurally identical to SDK's SurfacePerfDomainState.
37
+ // Export as SurfacePerfDomainState alias for SDK compatibility.
38
+ export type { UiPerfDomainState };
36
39
 
37
40
  import { createInitialSessionState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/session';
38
41
  import { createInitialModelState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/model';
@@ -73,7 +76,11 @@ export interface RuntimeState {
73
76
  model: ModelDomainState;
74
77
  conversation: ConversationDomainState;
75
78
  overlays: OverlayDomainState;
76
- panels: PanelDomainState;
79
+ /**
80
+ * TUI panel state. Typed as Record<string,unknown> for SDK RuntimeState
81
+ * compatibility. Use selectPanels() which casts to PanelDomainState.
82
+ */
83
+ panels: Record<string, unknown>;
77
84
  permissions: PermissionDomainState;
78
85
  tasks: TaskDomainState;
79
86
  agents: AgentDomainState;
@@ -95,7 +102,8 @@ export interface RuntimeState {
95
102
  git: GitDomainState;
96
103
  discovery: DiscoveryDomainState;
97
104
  intelligence: IntelligenceDomainState;
98
- uiPerf: UiPerfDomainState;
105
+ /** Surface/UI performance metrics. SDK-compatible field name. */
106
+ surfacePerf: UiPerfDomainState;
99
107
  }
100
108
 
101
109
  /**
@@ -110,7 +118,7 @@ export function createInitialRuntimeState(): RuntimeState {
110
118
  model: createInitialModelState(),
111
119
  conversation: createInitialConversationState(),
112
120
  overlays: createInitialOverlaysState(),
113
- panels: createInitialPanelsState(),
121
+ panels: createInitialPanelsState() as unknown as Record<string, unknown>,
114
122
  permissions: createInitialPermissionsState(),
115
123
  tasks: createInitialTasksState(),
116
124
  agents: createInitialAgentsState(),
@@ -132,6 +140,6 @@ export function createInitialRuntimeState(): RuntimeState {
132
140
  git: createInitialGitState(),
133
141
  discovery: createInitialDiscoveryState(),
134
142
  intelligence: createInitialIntelligenceState(),
135
- uiPerf: createInitialUiPerfState(),
143
+ surfacePerf: createInitialUiPerfState(),
136
144
  };
137
145
  }
@@ -0,0 +1,46 @@
1
+ import type {
2
+ AgentEvent,
3
+ AnyRuntimeEvent,
4
+ OpsEvent,
5
+ PlannerEvent,
6
+ ProviderEvent,
7
+ RuntimeEventBus,
8
+ RuntimeEventEnvelope,
9
+ SessionEvent,
10
+ ToolEvent,
11
+ TurnEvent,
12
+ WorkflowEvent,
13
+ } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
14
+ import { createRuntimeEventFeed, type RuntimeEventFeed } from '@pellux/goodvibes-sdk/platform/runtime/event-feeds';
15
+
16
+ export type UiEventFeed<TEvent extends AnyRuntimeEvent> = RuntimeEventFeed<TEvent>;
17
+
18
+ export interface UiRuntimeEvents {
19
+ readonly sessions: UiEventFeed<SessionEvent>;
20
+ readonly turns: UiEventFeed<TurnEvent>;
21
+ readonly tools: UiEventFeed<ToolEvent>;
22
+ readonly providers: UiEventFeed<ProviderEvent>;
23
+ readonly agents: UiEventFeed<AgentEvent>;
24
+ readonly workflows: UiEventFeed<WorkflowEvent>;
25
+ readonly planner: UiEventFeed<PlannerEvent>;
26
+ readonly ops: UiEventFeed<OpsEvent>;
27
+ }
28
+
29
+ function createUiEventFeed<TEvent extends AnyRuntimeEvent>(runtimeBus: RuntimeEventBus): UiEventFeed<TEvent> {
30
+ return createRuntimeEventFeed<TEvent>((type, listener) => (
31
+ runtimeBus.on(type as TEvent['type'], listener as (envelope: RuntimeEventEnvelope<TEvent['type'], TEvent>) => void)
32
+ ));
33
+ }
34
+
35
+ export function createUiRuntimeEvents(runtimeBus: RuntimeEventBus): UiRuntimeEvents {
36
+ return {
37
+ sessions: createUiEventFeed<SessionEvent>(runtimeBus),
38
+ turns: createUiEventFeed<TurnEvent>(runtimeBus),
39
+ tools: createUiEventFeed<ToolEvent>(runtimeBus),
40
+ providers: createUiEventFeed<ProviderEvent>(runtimeBus),
41
+ agents: createUiEventFeed<AgentEvent>(runtimeBus),
42
+ workflows: createUiEventFeed<WorkflowEvent>(runtimeBus),
43
+ planner: createUiEventFeed<PlannerEvent>(runtimeBus),
44
+ ops: createUiEventFeed<OpsEvent>(runtimeBus),
45
+ };
46
+ }
@@ -1,7 +1,7 @@
1
1
  import type { RuntimeServices } from './services.ts';
2
2
  import type { RemoteRunnerRegistry } from '@pellux/goodvibes-sdk/platform/runtime/remote/runner-registry';
3
3
  import type { RemoteSupervisor } from '@pellux/goodvibes-sdk/platform/runtime/remote/supervisor';
4
- import { createUiRuntimeEvents, type UiRuntimeEvents } from '@pellux/goodvibes-sdk/platform/runtime/ui-events';
4
+ import { createUiRuntimeEvents, type UiRuntimeEvents } from './ui-events.ts';
5
5
  import { createUiReadModels, type UiReadModels, type UiReadModelOptions } from './ui-read-models.ts';
6
6
  import type { ForensicsRegistry } from '@pellux/goodvibes-sdk/platform/runtime/forensics/index';
7
7
  import type { ControlPlaneRecentEvent } from '@pellux/goodvibes-sdk/platform/control-plane/index';
@@ -4,7 +4,7 @@ import type { CommandContext } from '../input/command-registry.ts';
4
4
  import type { InputHandler } from '../input/handler.ts';
5
5
  import type { PanelManager } from '../panels/panel-manager.ts';
6
6
  import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers/registry';
7
- import type { MutableRuntimeState } from '../runtime/context.ts';
7
+ import type { MutableRuntimeState } from '@pellux/goodvibes-sdk/platform/runtime/mutable-runtime-state';
8
8
  import type { FeatureFlagManager } from '@pellux/goodvibes-sdk/platform/runtime/feature-flags/index';
9
9
  import type { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp/registry';
10
10
  import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config/subscriptions';