@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,440 +0,0 @@
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,60 +0,0 @@
1
- import type { PartialToolCall } from '@pellux/goodvibes-sdk/platform/providers/interface';
2
- import type { ConversationDomainState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/conversation';
3
-
4
- export function uniq<T>(items: T[]): T[] {
5
- return [...new Set(items)];
6
- }
7
-
8
- export function now(): number {
9
- return Date.now();
10
- }
11
-
12
- export function updateDomainMetadata<T extends { revision: number; lastUpdatedAt: number; source: string }>(
13
- domain: T,
14
- source: string,
15
- ): T {
16
- return {
17
- ...domain,
18
- revision: domain.revision + 1,
19
- lastUpdatedAt: now(),
20
- source,
21
- };
22
- }
23
-
24
- export function isTerminalTurnState(state: ConversationDomainState['turnState']): boolean {
25
- return state === 'completed' || state === 'failed' || state === 'cancelled';
26
- }
27
-
28
- export function canStartNewTurn(domain: ConversationDomainState): boolean {
29
- return domain.turnState === 'idle' || isTerminalTurnState(domain.turnState);
30
- }
31
-
32
- export function isCurrentTurnEvent(domain: ConversationDomainState, turnId: string): boolean {
33
- return domain.currentTurnId !== undefined && domain.currentTurnId === turnId;
34
- }
35
-
36
- export function formatPartialToolPreview(toolCalls?: PartialToolCall[]): string | undefined {
37
- if (!toolCalls || toolCalls.length === 0) return undefined;
38
- const last = toolCalls[toolCalls.length - 1] as { name?: unknown; arguments?: unknown };
39
- const name = typeof last.name === 'string' ? last.name : '';
40
- const args =
41
- typeof last.arguments === 'string'
42
- ? last.arguments
43
- : last.arguments !== undefined
44
- ? JSON.stringify(last.arguments)
45
- : '';
46
- if (!name) return undefined;
47
- const preview = args.length > 60 ? `${args.slice(0, 57)}...` : args;
48
- return `${name}(${preview})`;
49
- }
50
-
51
- export function resetStreamState(): ConversationDomainState['stream'] {
52
- return {
53
- accumulated: '',
54
- reasoningAccumulated: '',
55
- partialToolPreview: undefined,
56
- deltaCount: 0,
57
- firstDeltaAt: undefined,
58
- lastDeltaAt: undefined,
59
- };
60
- }