@h-rig/core 0.0.6-alpha.155 → 0.0.6-alpha.157

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.
@@ -1,1784 +0,0 @@
1
- // @bun
2
- // packages/core/src/engineReadModelReducer.ts
3
- import {
4
- ActionId,
5
- ConversationId,
6
- EngineRuntimeId,
7
- MessageId,
8
- RunId,
9
- TaskId,
10
- WorkspaceId,
11
- WorktreeId
12
- } from "@rig/contracts";
13
- var CANONICAL_RUNTIME_ADAPTER = "pi";
14
- function isRecord(value) {
15
- return typeof value === "object" && value !== null;
16
- }
17
- function readString(payload, key) {
18
- const value = payload[key];
19
- return typeof value === "string" ? value : undefined;
20
- }
21
- function readNullableString(payload, key) {
22
- const value = payload[key];
23
- return typeof value === "string" ? value : value === null ? null : undefined;
24
- }
25
- function readRecord(payload, key) {
26
- const value = payload[key];
27
- return isRecord(value) ? value : undefined;
28
- }
29
- function asWorkspaceId(value) {
30
- return WorkspaceId.makeUnsafe(value);
31
- }
32
- function asTaskId(value) {
33
- return TaskId.makeUnsafe(value);
34
- }
35
- function asRunId(value) {
36
- return RunId.makeUnsafe(value);
37
- }
38
- function conversationIdFromRunId(value) {
39
- return ConversationId.makeUnsafe(value);
40
- }
41
- function asActionId(value) {
42
- return ActionId.makeUnsafe(value);
43
- }
44
- function runtimeIdFromRunId(value) {
45
- return EngineRuntimeId.makeUnsafe(value);
46
- }
47
- function worktreeIdFromRunId(value) {
48
- return WorktreeId.makeUnsafe(value);
49
- }
50
- function asMessageId(value) {
51
- return MessageId.makeUnsafe(value);
52
- }
53
- function upsertById(items, entry) {
54
- const index = items.findIndex((candidate) => candidate.id === entry.id);
55
- if (index < 0) {
56
- return [...items, entry];
57
- }
58
- const next = items.slice();
59
- next[index] = entry;
60
- return next;
61
- }
62
- function removeById(items, id) {
63
- return items.filter((candidate) => candidate.id !== id);
64
- }
65
- function patchById(items, id, updater) {
66
- const index = items.findIndex((candidate) => candidate.id === id);
67
- if (index < 0) {
68
- return items.slice();
69
- }
70
- const next = items.slice();
71
- next[index] = updater(next[index]);
72
- return next;
73
- }
74
- function upsertByKey(items, entry, key) {
75
- const index = items.findIndex((candidate) => candidate[key] === entry[key]);
76
- if (index < 0) {
77
- return [...items, entry];
78
- }
79
- const next = items.slice();
80
- next[index] = entry;
81
- return next;
82
- }
83
- function patchByKey(items, keyValue, key, updater) {
84
- const index = items.findIndex((candidate) => candidate[key] === keyValue);
85
- if (index < 0) {
86
- return items.slice();
87
- }
88
- const next = items.slice();
89
- next[index] = updater(next[index]);
90
- return next;
91
- }
92
- function mergeById(items, incoming) {
93
- return incoming.reduce((acc, item) => upsertById(acc, item), [...items]);
94
- }
95
- function replaceWorkspaceSlice(items, workspaceId, incoming) {
96
- return [...items.filter((item) => item.workspaceId !== workspaceId), ...incoming];
97
- }
98
- function withQueuePositions(items) {
99
- return items.map((item, index) => Object.assign({}, item, {
100
- position: index
101
- }));
102
- }
103
- function maxIsoDate(left, right) {
104
- return left.localeCompare(right) >= 0 ? left : right;
105
- }
106
- var REMOTE_HOST_STATUS_ONLINE = new Set([
107
- "ready",
108
- "busy",
109
- "degraded",
110
- "draining"
111
- ]);
112
- function deriveRemoteFleetStatus(hosts, warnings) {
113
- if (hosts.length === 0)
114
- return "empty";
115
- if (warnings.length > 0)
116
- return "degraded";
117
- if (hosts.some((host) => host.status === "degraded" || host.status === "quarantined")) {
118
- return "degraded";
119
- }
120
- const onlineHostCount = hosts.filter((host) => REMOTE_HOST_STATUS_ONLINE.has(host.status)).length;
121
- return onlineHostCount > 0 ? "ready" : "degraded";
122
- }
123
- function buildRemoteFleetSummary(input) {
124
- const warnings = input.fleet?.warnings ?? [];
125
- const manifestCount = input.fleet?.manifestCount ?? 0;
126
- const onlineHostCount = input.hosts.filter((host) => REMOTE_HOST_STATUS_ONLINE.has(host.status)).length;
127
- return {
128
- updatedAt: input.updatedAt,
129
- status: deriveRemoteFleetStatus(input.hosts, warnings),
130
- manifestCount,
131
- hostCount: input.hosts.length,
132
- onlineHostCount,
133
- hosts: [...input.hosts].sort((left, right) => left.name.localeCompare(right.name)),
134
- warnings
135
- };
136
- }
137
- function toRunMode(interactionMode) {
138
- return interactionMode === "plan" ? "supervised" : "interactive";
139
- }
140
- function mapLegacySessionStatusToRunStatus(status) {
141
- switch (status) {
142
- case "starting":
143
- return "preparing";
144
- case "running":
145
- return "running";
146
- case "ready":
147
- return "completed";
148
- case "interrupted":
149
- return "paused";
150
- case "error":
151
- return "failed";
152
- case "stopped":
153
- return "stopped";
154
- default:
155
- return "created";
156
- }
157
- }
158
- function mapLegacySessionStatusToRuntimeStatus(status) {
159
- switch (status) {
160
- case "starting":
161
- return "starting";
162
- case "running":
163
- return "running";
164
- case "interrupted":
165
- return "interrupted";
166
- case "error":
167
- return "failed";
168
- default:
169
- return "exited";
170
- }
171
- }
172
- function mapTaskStatusFromRunStatus(status, fallback) {
173
- if (fallback === "blocked" || fallback === "cancelled" || fallback === "completed" || fallback === "closed" || fallback === "unknown") {
174
- return fallback === "closed" ? "completed" : fallback;
175
- }
176
- switch (status) {
177
- case "created":
178
- return fallback;
179
- case "queued":
180
- return "queued";
181
- case "preparing":
182
- case "running":
183
- case "waiting-approval":
184
- case "waiting-user-input":
185
- case "validating":
186
- case "paused":
187
- return "in_progress";
188
- case "reviewing":
189
- case "closing-out":
190
- return "under_review";
191
- case "needs-attention":
192
- return "blocked";
193
- case "completed":
194
- return "completed";
195
- case "failed":
196
- return "ready";
197
- case "stopped":
198
- return "cancelled";
199
- }
200
- }
201
- function withSnapshotMetadata(snapshot, event, next) {
202
- return {
203
- ...snapshot,
204
- ...next,
205
- snapshotSequence: Math.max(snapshot.snapshotSequence, event.sequence),
206
- updatedAt: maxIsoDate(snapshot.updatedAt, event.createdAt)
207
- };
208
- }
209
- function ensureConversation(snapshot, run) {
210
- const conversationId = conversationIdFromRunId(run.id);
211
- if (snapshot.conversations.some((conversation) => conversation.id === conversationId)) {
212
- return snapshot;
213
- }
214
- return {
215
- ...snapshot,
216
- conversations: [
217
- ...snapshot.conversations,
218
- {
219
- id: conversationId,
220
- runId: run.id,
221
- title: run.title,
222
- createdAt: run.createdAt,
223
- updatedAt: run.updatedAt
224
- }
225
- ]
226
- };
227
- }
228
- function updateTaskFromRun(snapshot, run) {
229
- if (!run.taskId) {
230
- return snapshot;
231
- }
232
- return {
233
- ...snapshot,
234
- tasks: patchById(snapshot.tasks, run.taskId, (task) => ({
235
- ...task,
236
- status: mapTaskStatusFromRunStatus(run.status, task.status),
237
- updatedAt: maxIsoDate(task.updatedAt, run.updatedAt)
238
- }))
239
- };
240
- }
241
- function applyRun(snapshot, run) {
242
- const withConversation = ensureConversation(snapshot, run);
243
- const nextRuns = upsertById(withConversation.runs, run);
244
- return updateTaskFromRun({ ...withConversation, runs: nextRuns }, run);
245
- }
246
- function applyRuntime(snapshot, runtime) {
247
- return {
248
- ...snapshot,
249
- runtimes: upsertById(snapshot.runtimes, runtime)
250
- };
251
- }
252
- function applyWorktree(snapshot, worktree) {
253
- return {
254
- ...snapshot,
255
- worktrees: upsertById(snapshot.worktrees, worktree)
256
- };
257
- }
258
- function countPendingApprovals(approvals, runId) {
259
- return approvals.filter((approval) => approval.runId === runId && approval.status === "pending").length;
260
- }
261
- function countPendingUserInputs(userInputs, actions, runId) {
262
- const persistedCount = (userInputs ?? []).filter((request) => request.runId === runId && request.status === "pending").length;
263
- if (persistedCount > 0) {
264
- return persistedCount;
265
- }
266
- const openRequestIds = new Set;
267
- for (const action of actions) {
268
- if (action.runId !== runId || !isRecord(action.payload)) {
269
- continue;
270
- }
271
- const requestId = readString(action.payload, "requestId");
272
- if (!requestId) {
273
- continue;
274
- }
275
- if (action.actionType === "user-input.requested") {
276
- openRequestIds.add(requestId);
277
- }
278
- if (action.actionType === "user-input.resolved") {
279
- openRequestIds.delete(requestId);
280
- }
281
- }
282
- return openRequestIds.size;
283
- }
284
- function reconcileRunCounts(snapshot, runId, statusOverride) {
285
- return {
286
- ...snapshot,
287
- runs: patchById(snapshot.runs, runId, (run) => {
288
- const pendingApprovalCount = countPendingApprovals(snapshot.approvals, runId);
289
- const pendingUserInputCount = countPendingUserInputs(snapshot.userInputs, snapshot.actions, runId);
290
- const nextStatus = statusOverride ?? (pendingApprovalCount > 0 ? "waiting-approval" : pendingUserInputCount > 0 ? "waiting-user-input" : run.status === "waiting-approval" || run.status === "waiting-user-input" ? "running" : run.status);
291
- return {
292
- ...run,
293
- pendingApprovalCount,
294
- pendingUserInputCount,
295
- status: nextStatus
296
- };
297
- })
298
- };
299
- }
300
- function applyApprovalActivity(approvals, runId, action) {
301
- if (!isRecord(action.payload)) {
302
- return approvals.slice();
303
- }
304
- const requestId = readString(action.payload, "requestId");
305
- if (!requestId) {
306
- return approvals.slice();
307
- }
308
- if (action.actionType === "approval.requested") {
309
- const requestKind = readString(action.payload, "requestKind") ?? "command";
310
- return upsertById(approvals, {
311
- id: requestId,
312
- runId,
313
- actionId: action.id,
314
- requestKind,
315
- status: "pending",
316
- payload: action.payload,
317
- createdAt: action.startedAt,
318
- resolvedAt: null
319
- });
320
- }
321
- const shouldResolve = action.actionType === "approval.resolved" || action.actionType === "provider.approval.respond.failed" && ((readString(action.payload, "detail") ?? "").includes("Unknown pending permission request") || (readString(action.payload, "message") ?? "").includes("Unknown pending permission request"));
322
- if (!shouldResolve) {
323
- return approvals.slice();
324
- }
325
- return approvals.map((approval) => approval.id === requestId ? {
326
- ...approval,
327
- status: "resolved",
328
- resolvedAt: action.completedAt ?? action.startedAt
329
- } : approval);
330
- }
331
- function makeRuntimeFromRun(run, occurredAt) {
332
- return {
333
- id: run.activeRuntimeId ?? runtimeIdFromRunId(run.id),
334
- workspaceId: run.workspaceId,
335
- runId: run.id,
336
- adapterKind: run.runtimeAdapter,
337
- executionTarget: run.executionTarget ?? "local",
338
- remoteHostId: run.remoteHostId ?? null,
339
- status: run.status === "failed" ? "failed" : run.status === "paused" ? "interrupted" : "starting",
340
- sandboxMode: "danger-full-access",
341
- isolationMode: run.worktreePath ? "worktree" : "env",
342
- workspaceDir: run.worktreePath,
343
- homeDir: null,
344
- tmpDir: null,
345
- cacheDir: null,
346
- logsDir: null,
347
- stateDir: null,
348
- sessionDir: null,
349
- sessionLogPath: null,
350
- pid: null,
351
- startedAt: run.startedAt ?? occurredAt,
352
- updatedAt: occurredAt,
353
- exitedAt: run.completedAt
354
- };
355
- }
356
- function applySyntheticRuntimePreparation(snapshot, event) {
357
- if (!isRecord(event.payload)) {
358
- return { status: "ignored", snapshot };
359
- }
360
- const runId = readString(event.payload, "runId") ?? event.aggregateId;
361
- const workspaceId = readString(event.payload, "workspaceId");
362
- const taskId = readString(event.payload, "taskId") ?? null;
363
- const workspaceDir = readNullableString(event.payload, "workspaceDir");
364
- const homeDir = readNullableString(event.payload, "homeDir");
365
- const tmpDir = readNullableString(event.payload, "tmpDir");
366
- const cacheDir = readNullableString(event.payload, "cacheDir");
367
- const branchName = readString(event.payload, "taskExternalId") ?? snapshot.runs.find((run) => run.id === runId)?.branch ?? "task";
368
- const failed = event.type.endsWith(".failed");
369
- const finished = event.type.endsWith(".finished");
370
- const existingRun = snapshot.runs.find((run) => run.id === runId);
371
- if (!existingRun || !workspaceId) {
372
- return { status: "ignored", snapshot };
373
- }
374
- const runtimeRunId = asRunId(runId);
375
- const nextTaskId = taskId ? asTaskId(taskId) : null;
376
- const nextWorkspaceId = asWorkspaceId(workspaceId);
377
- const nextRun = {
378
- ...existingRun,
379
- taskId: existingRun.taskId ?? nextTaskId,
380
- runKind: existingRun.runKind === "adhoc" && nextTaskId ? "task" : existingRun.runKind,
381
- runtimeAdapter: CANONICAL_RUNTIME_ADAPTER,
382
- initialPrompt: existingRun.initialPrompt ?? null,
383
- executionTarget: existingRun.executionTarget ?? "local",
384
- remoteHostId: existingRun.remoteHostId ?? null,
385
- activeRuntimeId: existingRun.activeRuntimeId ?? runtimeIdFromRunId(runtimeRunId),
386
- worktreePath: workspaceDir ?? existingRun.worktreePath,
387
- status: failed ? "failed" : finished ? "preparing" : "preparing",
388
- errorText: failed ? readString(event.payload, "message") ?? existingRun.errorText : existingRun.errorText,
389
- updatedAt: event.createdAt,
390
- completedAt: failed ? event.createdAt : existingRun.completedAt
391
- };
392
- const runtimeBase = snapshot.runtimes.find((runtime) => runtime.runId === runId) ?? makeRuntimeFromRun(nextRun, event.createdAt);
393
- const nextRuntime = {
394
- ...runtimeBase,
395
- workspaceId: nextWorkspaceId,
396
- runId: runtimeRunId,
397
- adapterKind: CANONICAL_RUNTIME_ADAPTER,
398
- executionTarget: existingRun.executionTarget ?? "local",
399
- remoteHostId: existingRun.remoteHostId ?? null,
400
- status: failed ? "failed" : finished ? "prepared" : "starting",
401
- isolationMode: "worktree",
402
- workspaceDir: workspaceDir ?? runtimeBase.workspaceDir,
403
- homeDir: homeDir ?? runtimeBase.homeDir,
404
- tmpDir: tmpDir ?? runtimeBase.tmpDir,
405
- cacheDir: cacheDir ?? runtimeBase.cacheDir,
406
- updatedAt: event.createdAt,
407
- exitedAt: failed ? event.createdAt : runtimeBase.exitedAt
408
- };
409
- let nextSnapshot = applyRun(snapshot, nextRun);
410
- nextSnapshot = applyRuntime(nextSnapshot, nextRuntime);
411
- if (workspaceDir) {
412
- nextSnapshot = applyWorktree(nextSnapshot, {
413
- id: worktreeIdFromRunId(runtimeRunId),
414
- workspaceId: nextWorkspaceId,
415
- runId: runtimeRunId,
416
- taskId: nextRun.taskId,
417
- branchName,
418
- path: workspaceDir,
419
- status: failed ? "failed" : finished ? "prepared" : "preparing",
420
- createdAt: existingRun.createdAt,
421
- cleanedAt: null
422
- });
423
- }
424
- return {
425
- status: "applied",
426
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
427
- };
428
- }
429
- function applySyntheticRuntimePrepared(snapshot, event) {
430
- if (!isRecord(event.payload)) {
431
- return { status: "ignored", snapshot };
432
- }
433
- const runId = readString(event.payload, "runId") ?? event.aggregateId;
434
- const workspaceId = readString(event.payload, "workspaceId");
435
- const worktreePath = readString(event.payload, "worktreePath");
436
- const existingRun = snapshot.runs.find((run) => run.id === runId);
437
- if (!existingRun || !workspaceId || !worktreePath) {
438
- return { status: "ignored", snapshot };
439
- }
440
- const runtimeRunId = asRunId(runId);
441
- const nextWorkspaceId = asWorkspaceId(workspaceId);
442
- let nextSnapshot = applyRuntime(snapshot, {
443
- ...snapshot.runtimes.find((runtime) => runtime.runId === runId) ?? makeRuntimeFromRun(existingRun, event.createdAt),
444
- workspaceId: nextWorkspaceId,
445
- runId: runtimeRunId,
446
- adapterKind: CANONICAL_RUNTIME_ADAPTER,
447
- executionTarget: existingRun.executionTarget ?? "local",
448
- remoteHostId: existingRun.remoteHostId ?? null,
449
- status: "prepared",
450
- isolationMode: "worktree",
451
- workspaceDir: worktreePath,
452
- updatedAt: event.createdAt
453
- });
454
- nextSnapshot = applyWorktree(nextSnapshot, {
455
- id: worktreeIdFromRunId(runtimeRunId),
456
- workspaceId: nextWorkspaceId,
457
- runId: runtimeRunId,
458
- taskId: existingRun.taskId,
459
- branchName: existingRun.branch ?? "task",
460
- path: worktreePath,
461
- status: "prepared",
462
- createdAt: existingRun.createdAt,
463
- cleanedAt: null
464
- });
465
- return {
466
- status: "applied",
467
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
468
- };
469
- }
470
- function applyLegacyProjectEvent(snapshot, event) {
471
- if (!isRecord(event.payload)) {
472
- return { status: "ignored", snapshot };
473
- }
474
- const payload = event.payload;
475
- if (event.type === "legacy.project.created") {
476
- const workspaceId = readString(payload, "projectId");
477
- const title = readString(payload, "title");
478
- const rootPath = readString(payload, "workspaceRoot");
479
- const createdAt = readString(payload, "createdAt");
480
- const updatedAt = readString(payload, "updatedAt");
481
- if (!workspaceId || !title || !rootPath || !createdAt || !updatedAt) {
482
- return { status: "ignored", snapshot };
483
- }
484
- const workspace = {
485
- id: asWorkspaceId(workspaceId),
486
- title,
487
- rootPath,
488
- sourceKind: "native",
489
- defaultRuntimeAdapter: CANONICAL_RUNTIME_ADAPTER,
490
- defaultModel: readNullableString(payload, "defaultModel") ?? null,
491
- createdAt,
492
- updatedAt
493
- };
494
- return {
495
- status: "applied",
496
- snapshot: withSnapshotMetadata(snapshot, event, {
497
- workspaces: upsertById(snapshot.workspaces, workspace)
498
- })
499
- };
500
- }
501
- if (event.type === "legacy.project.meta-updated") {
502
- const workspaceId = readString(payload, "projectId");
503
- if (!workspaceId) {
504
- return { status: "ignored", snapshot };
505
- }
506
- return {
507
- status: "applied",
508
- snapshot: withSnapshotMetadata(snapshot, event, {
509
- workspaces: patchById(snapshot.workspaces, workspaceId, (workspace) => ({
510
- ...workspace,
511
- title: readString(payload, "title") ?? workspace.title,
512
- rootPath: readString(payload, "workspaceRoot") ?? workspace.rootPath,
513
- defaultModel: readNullableString(payload, "defaultModel") ?? workspace.defaultModel,
514
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
515
- }))
516
- })
517
- };
518
- }
519
- if (event.type === "legacy.project.deleted") {
520
- const workspaceId = readString(payload, "projectId");
521
- if (!workspaceId) {
522
- return { status: "ignored", snapshot };
523
- }
524
- return {
525
- status: "applied",
526
- snapshot: withSnapshotMetadata(snapshot, event, {
527
- workspaces: removeById(snapshot.workspaces, workspaceId),
528
- graphs: snapshot.graphs.filter((graph) => graph.workspaceId !== workspaceId),
529
- tasks: snapshot.tasks.filter((task) => task.workspaceId !== workspaceId),
530
- runs: snapshot.runs.filter((run) => run.workspaceId !== workspaceId),
531
- runtimes: snapshot.runtimes.filter((runtime) => runtime.workspaceId !== workspaceId),
532
- conversations: snapshot.conversations.filter((conversation) => {
533
- const run = snapshot.runs.find((candidate) => candidate.id === conversation.runId);
534
- return run?.workspaceId !== workspaceId;
535
- }),
536
- messages: snapshot.messages.filter((message) => {
537
- const conversation = snapshot.conversations.find((candidate) => candidate.id === message.conversationId);
538
- const run = snapshot.runs.find((candidate) => candidate.id === conversation?.runId);
539
- return run?.workspaceId !== workspaceId;
540
- }),
541
- actions: snapshot.actions.filter((action) => {
542
- const run = snapshot.runs.find((candidate) => candidate.id === action.runId);
543
- return run?.workspaceId !== workspaceId;
544
- }),
545
- approvals: snapshot.approvals.filter((approval) => {
546
- const run = snapshot.runs.find((candidate) => candidate.id === approval.runId);
547
- return run?.workspaceId !== workspaceId;
548
- }),
549
- queue: snapshot.queue.filter((entry) => {
550
- const task = snapshot.tasks.find((candidate) => candidate.id === entry.taskId);
551
- return task?.workspaceId !== workspaceId;
552
- }),
553
- worktrees: snapshot.worktrees.filter((worktree) => worktree.workspaceId !== workspaceId)
554
- })
555
- };
556
- }
557
- return { status: "ignored", snapshot };
558
- }
559
- function applyLegacyThreadEvent(snapshot, event) {
560
- if (!isRecord(event.payload)) {
561
- return { status: "ignored", snapshot };
562
- }
563
- const payload = event.payload;
564
- if (event.type === "legacy.thread.created") {
565
- const runId = readString(payload, "threadId");
566
- const workspaceId = readString(payload, "projectId");
567
- const title = readString(payload, "title");
568
- const model = readString(payload, "model");
569
- const createdAt = readString(payload, "createdAt");
570
- const updatedAt = readString(payload, "updatedAt");
571
- if (!runId || !workspaceId || !title || !model || !createdAt || !updatedAt) {
572
- return { status: "ignored", snapshot };
573
- }
574
- const run = {
575
- id: asRunId(runId),
576
- workspaceId: asWorkspaceId(workspaceId),
577
- taskId: null,
578
- title,
579
- runKind: "adhoc",
580
- mode: toRunMode(readString(payload, "interactionMode")),
581
- runtimeMode: "full-access",
582
- interactionMode: "default",
583
- status: "created",
584
- runtimeAdapter: CANONICAL_RUNTIME_ADAPTER,
585
- model,
586
- initialPrompt: null,
587
- activeRuntimeId: null,
588
- latestMessageId: null,
589
- pendingApprovalCount: 0,
590
- pendingUserInputCount: 0,
591
- branch: readNullableString(payload, "branch") ?? null,
592
- worktreePath: readNullableString(payload, "worktreePath") ?? null,
593
- errorText: null,
594
- createdAt,
595
- updatedAt,
596
- startedAt: null,
597
- completedAt: null
598
- };
599
- let nextSnapshot = applyRun(snapshot, run);
600
- if (run.branch && run.worktreePath) {
601
- nextSnapshot = applyWorktree(nextSnapshot, {
602
- id: worktreeIdFromRunId(asRunId(runId)),
603
- workspaceId: asWorkspaceId(workspaceId),
604
- runId: asRunId(runId),
605
- taskId: null,
606
- branchName: run.branch,
607
- path: run.worktreePath,
608
- status: "active",
609
- createdAt,
610
- cleanedAt: null
611
- });
612
- }
613
- return {
614
- status: "applied",
615
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
616
- };
617
- }
618
- if (event.type === "legacy.thread.deleted") {
619
- const runId = readString(payload, "threadId");
620
- if (!runId) {
621
- return { status: "ignored", snapshot };
622
- }
623
- return {
624
- status: "applied",
625
- snapshot: withSnapshotMetadata(snapshot, event, {
626
- runs: removeById(snapshot.runs, runId),
627
- runtimes: snapshot.runtimes.filter((runtime) => runtime.runId !== runId),
628
- conversations: snapshot.conversations.filter((conversation) => conversation.runId !== runId),
629
- messages: snapshot.messages.filter((message) => message.conversationId !== runId),
630
- actions: snapshot.actions.filter((action) => action.runId !== runId),
631
- approvals: snapshot.approvals.filter((approval) => approval.runId !== runId),
632
- worktrees: snapshot.worktrees.filter((worktree) => worktree.runId !== runId)
633
- })
634
- };
635
- }
636
- if (event.type === "legacy.thread.meta-updated") {
637
- const runId = readString(payload, "threadId");
638
- if (!runId) {
639
- return { status: "ignored", snapshot };
640
- }
641
- let nextSnapshot = {
642
- ...snapshot,
643
- runs: patchById(snapshot.runs, runId, (run2) => ({
644
- ...run2,
645
- title: readString(payload, "title") ?? run2.title,
646
- model: readString(payload, "model") ?? run2.model,
647
- branch: readNullableString(payload, "branch") === undefined ? run2.branch : readNullableString(payload, "branch") ?? null,
648
- worktreePath: readNullableString(payload, "worktreePath") === undefined ? run2.worktreePath : readNullableString(payload, "worktreePath") ?? null,
649
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
650
- })),
651
- conversations: patchById(snapshot.conversations, runId, (conversation) => ({
652
- ...conversation,
653
- title: readString(payload, "title") ?? conversation.title,
654
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
655
- }))
656
- };
657
- const run = nextSnapshot.runs.find((candidate) => candidate.id === runId);
658
- if (run?.worktreePath && run.branch) {
659
- nextSnapshot = applyWorktree(nextSnapshot, {
660
- id: worktreeIdFromRunId(run.id),
661
- workspaceId: run.workspaceId,
662
- runId: run.id,
663
- taskId: run.taskId,
664
- branchName: run.branch,
665
- path: run.worktreePath,
666
- status: "active",
667
- createdAt: run.createdAt,
668
- cleanedAt: null
669
- });
670
- }
671
- if (run && run.worktreePath === null) {
672
- nextSnapshot = {
673
- ...nextSnapshot,
674
- worktrees: nextSnapshot.worktrees.filter((worktree) => worktree.runId !== runId)
675
- };
676
- }
677
- return {
678
- status: "applied",
679
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
680
- };
681
- }
682
- if (event.type === "legacy.thread.interaction-mode-set") {
683
- const runId = readString(payload, "threadId");
684
- if (!runId) {
685
- return { status: "ignored", snapshot };
686
- }
687
- return {
688
- status: "applied",
689
- snapshot: withSnapshotMetadata(snapshot, event, {
690
- runs: patchById(snapshot.runs, runId, (run) => ({
691
- ...run,
692
- mode: toRunMode(readString(payload, "interactionMode")),
693
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
694
- }))
695
- })
696
- };
697
- }
698
- if (event.type === "legacy.thread.runtime-mode-set") {
699
- const runId = readString(payload, "threadId");
700
- if (!runId) {
701
- return { status: "ignored", snapshot };
702
- }
703
- const runtimeMode = readString(payload, "runtimeMode");
704
- return {
705
- status: "applied",
706
- snapshot: withSnapshotMetadata(snapshot, event, {
707
- runtimes: patchById(snapshot.runtimes, runId, (runtime) => ({
708
- ...runtime,
709
- sandboxMode: runtimeMode === "approval-required" ? "workspace-write" : "danger-full-access",
710
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
711
- }))
712
- })
713
- };
714
- }
715
- if (event.type === "legacy.thread.message-sent") {
716
- const runId = readString(payload, "threadId");
717
- const messageId = readString(payload, "messageId");
718
- const role = readString(payload, "role");
719
- const text = readString(payload, "text");
720
- const createdAt = readString(payload, "createdAt");
721
- const updatedAt = readString(payload, "updatedAt");
722
- if (!runId || !messageId || !role || text === undefined || !createdAt || !updatedAt) {
723
- return { status: "ignored", snapshot };
724
- }
725
- const attachments = Array.isArray(payload.attachments) ? payload.attachments : [];
726
- const streaming = payload.streaming === true;
727
- const message = {
728
- id: asMessageId(messageId),
729
- conversationId: conversationIdFromRunId(asRunId(runId)),
730
- role: role === "assistant" || role === "system" ? role : "user",
731
- text,
732
- attachments,
733
- state: streaming ? "streaming" : "completed",
734
- createdAt,
735
- completedAt: streaming ? null : updatedAt
736
- };
737
- const existingRun = snapshot.runs.find((run) => run.id === runId);
738
- if (!existingRun) {
739
- return { status: "ignored", snapshot };
740
- }
741
- return {
742
- status: "applied",
743
- snapshot: withSnapshotMetadata(ensureConversation({
744
- ...snapshot,
745
- messages: upsertById(snapshot.messages, message),
746
- runs: patchById(snapshot.runs, runId, (run) => ({
747
- ...run,
748
- latestMessageId: asMessageId(messageId),
749
- updatedAt
750
- }))
751
- }, existingRun), event, {})
752
- };
753
- }
754
- if (event.type === "legacy.thread.session-set") {
755
- const runId = readString(event.payload, "threadId");
756
- const session = readRecord(event.payload, "session");
757
- if (!runId || !session) {
758
- return { status: "ignored", snapshot };
759
- }
760
- const existingRun = snapshot.runs.find((run) => run.id === runId);
761
- if (!existingRun) {
762
- return { status: "ignored", snapshot };
763
- }
764
- const sessionUpdatedAt = readString(session, "updatedAt") ?? event.createdAt;
765
- const providerName = readNullableString(session, "providerName");
766
- const nextRun = {
767
- ...existingRun,
768
- runtimeAdapter: CANONICAL_RUNTIME_ADAPTER,
769
- initialPrompt: existingRun.initialPrompt ?? null,
770
- activeRuntimeId: runtimeIdFromRunId(asRunId(runId)),
771
- status: mapLegacySessionStatusToRunStatus(readString(session, "status")),
772
- errorText: readNullableString(session, "lastError") ?? existingRun.errorText,
773
- updatedAt: sessionUpdatedAt,
774
- completedAt: readString(session, "status") === "ready" || readString(session, "status") === "stopped" || readString(session, "status") === "error" ? sessionUpdatedAt : existingRun.completedAt
775
- };
776
- let nextSnapshot = applyRun(snapshot, nextRun);
777
- nextSnapshot = applyRuntime(nextSnapshot, {
778
- ...snapshot.runtimes.find((runtime) => runtime.runId === runId) ?? makeRuntimeFromRun(nextRun, sessionUpdatedAt),
779
- workspaceId: existingRun.workspaceId,
780
- runId: asRunId(runId),
781
- adapterKind: CANONICAL_RUNTIME_ADAPTER,
782
- status: mapLegacySessionStatusToRuntimeStatus(readString(session, "status")),
783
- workspaceDir: existingRun.worktreePath,
784
- isolationMode: existingRun.worktreePath ? "worktree" : "env",
785
- updatedAt: sessionUpdatedAt,
786
- exitedAt: readString(session, "status") === "ready" || readString(session, "status") === "stopped" || readString(session, "status") === "error" ? sessionUpdatedAt : null
787
- });
788
- return {
789
- status: "applied",
790
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
791
- };
792
- }
793
- if (event.type === "legacy.thread.activity-appended") {
794
- const runId = readString(event.payload, "threadId");
795
- const activity = readRecord(event.payload, "activity");
796
- if (!runId || !activity) {
797
- return { status: "ignored", snapshot };
798
- }
799
- const actionId = readString(activity, "id");
800
- const actionType = readString(activity, "kind");
801
- const title = readString(activity, "summary");
802
- const startedAt = readString(activity, "createdAt");
803
- if (!actionId || !actionType || !title || !startedAt) {
804
- return { status: "ignored", snapshot };
805
- }
806
- const payload2 = readRecord(activity, "payload") ?? {};
807
- const detail = readString(payload2, "detail") ?? null;
808
- const action = {
809
- id: asActionId(actionId),
810
- runId: asRunId(runId),
811
- messageId: null,
812
- actionType,
813
- title,
814
- detail,
815
- state: "completed",
816
- payload: payload2,
817
- startedAt,
818
- completedAt: startedAt
819
- };
820
- let nextSnapshot = {
821
- ...snapshot,
822
- actions: upsertById(snapshot.actions, action),
823
- approvals: applyApprovalActivity(snapshot.approvals, asRunId(runId), action),
824
- runs: patchById(snapshot.runs, runId, (run) => ({
825
- ...run,
826
- updatedAt: maxIsoDate(run.updatedAt, startedAt),
827
- status: actionType === "engine.runtime.ready" ? "running" : actionType === "engine.runtime.failed" ? "failed" : run.status,
828
- errorText: actionType === "engine.runtime.failed" && detail ? detail : run.errorText,
829
- completedAt: actionType === "engine.runtime.failed" ? startedAt : run.completedAt
830
- }))
831
- };
832
- nextSnapshot = reconcileRunCounts(nextSnapshot, asRunId(runId));
833
- return {
834
- status: "applied",
835
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
836
- };
837
- }
838
- if (event.type === "legacy.thread.reverted") {
839
- return { status: "requires-resync", snapshot };
840
- }
841
- return { status: "ignored", snapshot };
842
- }
843
- function applyEngineEvent(snapshot, event) {
844
- if (event.sequence <= snapshot.snapshotSequence) {
845
- return { status: "ignored", snapshot };
846
- }
847
- if (event.sequence > snapshot.snapshotSequence + 1) {
848
- return { status: "requires-resync", snapshot };
849
- }
850
- const base = {
851
- ...snapshot,
852
- snapshotSequence: event.sequence,
853
- updatedAt: event.createdAt
854
- };
855
- const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
856
- switch (event.type) {
857
- case "WorkspaceRegistered":
858
- case "RigWorkspaceImported": {
859
- const workspace = {
860
- id: payload.workspaceId,
861
- title: payload.title,
862
- rootPath: payload.rootPath,
863
- sourceKind: payload.sourceKind,
864
- defaultModel: payload.defaultModel ?? null,
865
- topology: undefined,
866
- remoteFleet: undefined,
867
- serviceFabric: undefined,
868
- createdAt: payload.createdAt,
869
- updatedAt: payload.createdAt
870
- };
871
- return {
872
- status: "applied",
873
- snapshot: {
874
- ...base,
875
- workspaces: upsertById(base.workspaces, workspace)
876
- }
877
- };
878
- }
879
- case "RigStateHydrated": {
880
- const workspaceRunIds = new Set([
881
- ...base.runs.filter((run) => run.workspaceId === payload.workspaceId).map((run) => run.id),
882
- ...(payload.runs ?? []).map((run) => run.id)
883
- ]);
884
- const withoutWorkspaceRunData = (items) => items.filter((item) => !workspaceRunIds.has(item.runId));
885
- const workspaces = payload.rootPath ? patchById(base.workspaces, payload.workspaceId, (workspace) => ({
886
- ...workspace,
887
- rootPath: payload.rootPath,
888
- updatedAt: payload.createdAt
889
- })) : base.workspaces;
890
- return {
891
- status: "applied",
892
- snapshot: {
893
- ...base,
894
- workspaces,
895
- graphs: upsertById(base.graphs, payload.graph),
896
- tasks: replaceWorkspaceSlice(base.tasks, payload.workspaceId, payload.tasks ?? []),
897
- runs: replaceWorkspaceSlice(base.runs, payload.workspaceId, payload.runs ?? []),
898
- runtimes: replaceWorkspaceSlice(base.runtimes, payload.workspaceId, payload.runtimes ?? []),
899
- actions: mergeById(withoutWorkspaceRunData(base.actions), payload.actions ?? []),
900
- logs: mergeById(withoutWorkspaceRunData(base.logs), payload.logs ?? []),
901
- userInputs: (base.userInputs ?? []).filter((request) => !workspaceRunIds.has(request.runId)),
902
- validations: mergeById(withoutWorkspaceRunData(base.validations), payload.validations ?? []),
903
- reviews: mergeById(withoutWorkspaceRunData(base.reviews), payload.reviews ?? []),
904
- artifacts: mergeById(withoutWorkspaceRunData(base.artifacts), payload.artifacts ?? []),
905
- policyDecisions: mergeById(withoutWorkspaceRunData(base.policyDecisions), payload.policyDecisions ?? []),
906
- queue: [...payload.queue ?? []]
907
- }
908
- };
909
- }
910
- case "WorkspaceTopologyCompiled": {
911
- return {
912
- status: "applied",
913
- snapshot: {
914
- ...base,
915
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => ({
916
- ...workspace,
917
- rootPath: payload.rootPath ?? workspace.rootPath,
918
- topology: payload.topology,
919
- updatedAt: payload.createdAt
920
- }))
921
- }
922
- };
923
- }
924
- case "WorkspaceRemoteFleetSynced": {
925
- return {
926
- status: "applied",
927
- snapshot: {
928
- ...base,
929
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => ({
930
- ...workspace,
931
- rootPath: payload.rootPath ?? workspace.rootPath,
932
- remoteFleet: payload.remoteFleet,
933
- updatedAt: payload.createdAt
934
- }))
935
- }
936
- };
937
- }
938
- case "WorkspaceServiceFabricSynced": {
939
- return {
940
- status: "applied",
941
- snapshot: {
942
- ...base,
943
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => ({
944
- ...workspace,
945
- rootPath: payload.rootPath ?? workspace.rootPath,
946
- serviceFabric: payload.serviceFabric,
947
- updatedAt: payload.createdAt
948
- }))
949
- }
950
- };
951
- }
952
- case "WorkspaceRemoteHostRegistered": {
953
- return {
954
- status: "applied",
955
- snapshot: {
956
- ...base,
957
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => {
958
- const withoutExisting = (workspace.remoteFleet?.hosts ?? []).filter((host) => host.id !== payload.host.id);
959
- return {
960
- ...workspace,
961
- remoteFleet: buildRemoteFleetSummary({
962
- fleet: workspace.remoteFleet,
963
- hosts: [...withoutExisting, payload.host],
964
- updatedAt: payload.createdAt
965
- }),
966
- updatedAt: payload.createdAt
967
- };
968
- })
969
- }
970
- };
971
- }
972
- case "WorkspaceRemoteHostStatusUpdated": {
973
- return {
974
- status: "applied",
975
- snapshot: {
976
- ...base,
977
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => {
978
- const fleet = workspace.remoteFleet;
979
- if (!fleet) {
980
- return {
981
- ...workspace,
982
- updatedAt: payload.createdAt
983
- };
984
- }
985
- const hosts = fleet.hosts.map((host) => host.id !== payload.hostId ? host : {
986
- ...host,
987
- status: payload.status,
988
- currentLeaseCount: typeof payload.currentLeaseCount === "number" ? payload.currentLeaseCount : host.currentLeaseCount,
989
- lastHeartbeatAt: payload.lastHeartbeatAt ?? host.lastHeartbeatAt
990
- });
991
- return {
992
- ...workspace,
993
- remoteFleet: buildRemoteFleetSummary({
994
- fleet,
995
- hosts,
996
- updatedAt: payload.createdAt
997
- }),
998
- updatedAt: payload.createdAt
999
- };
1000
- })
1001
- }
1002
- };
1003
- }
1004
- case "RunCreated": {
1005
- const run = {
1006
- id: payload.runId,
1007
- workspaceId: payload.workspaceId,
1008
- taskId: payload.taskId ?? null,
1009
- title: payload.title,
1010
- runKind: payload.runKind,
1011
- mode: payload.mode,
1012
- runtimeMode: payload.runtimeMode,
1013
- interactionMode: payload.interactionMode,
1014
- status: "created",
1015
- runtimeAdapter: CANONICAL_RUNTIME_ADAPTER,
1016
- model: payload.model ?? null,
1017
- initialPrompt: payload.initialPrompt ?? null,
1018
- executionTarget: payload.executionTarget ?? (payload.remoteHostId ? "remote" : "local"),
1019
- remoteHostId: payload.remoteHostId ?? null,
1020
- remoteLeaseId: null,
1021
- remoteLeaseClaimedAt: null,
1022
- activeRuntimeId: null,
1023
- latestMessageId: null,
1024
- pendingApprovalCount: 0,
1025
- pendingUserInputCount: 0,
1026
- branch: null,
1027
- worktreePath: null,
1028
- errorText: null,
1029
- createdAt: payload.createdAt,
1030
- updatedAt: payload.createdAt,
1031
- startedAt: null,
1032
- completedAt: null
1033
- };
1034
- return {
1035
- status: "applied",
1036
- snapshot: { ...base, runs: upsertById(base.runs, run) }
1037
- };
1038
- }
1039
- case "RunInterrupted":
1040
- case "RunCancelled":
1041
- return {
1042
- status: "applied",
1043
- snapshot: {
1044
- ...base,
1045
- runs: patchById(base.runs, payload.runId, (run) => ({
1046
- ...run,
1047
- status: "stopped",
1048
- updatedAt: payload.createdAt,
1049
- completedAt: payload.createdAt
1050
- }))
1051
- }
1052
- };
1053
- case "RunCompleted":
1054
- return {
1055
- status: "applied",
1056
- snapshot: {
1057
- ...base,
1058
- runs: patchById(base.runs, payload.runId, (run) => ({
1059
- ...run,
1060
- status: "completed",
1061
- updatedAt: payload.createdAt,
1062
- completedAt: payload.createdAt
1063
- }))
1064
- }
1065
- };
1066
- case "RunStatusSet":
1067
- return {
1068
- status: "applied",
1069
- snapshot: {
1070
- ...base,
1071
- runs: patchById(base.runs, payload.runId, (run) => ({
1072
- ...run,
1073
- status: payload.status,
1074
- updatedAt: payload.createdAt,
1075
- completedAt: null
1076
- }))
1077
- }
1078
- };
1079
- case "RunFailed":
1080
- return {
1081
- status: "applied",
1082
- snapshot: {
1083
- ...base,
1084
- runs: patchById(base.runs, payload.runId, (run) => ({
1085
- ...run,
1086
- status: "failed",
1087
- errorText: payload.errorText ?? null,
1088
- updatedAt: payload.createdAt,
1089
- completedAt: payload.createdAt
1090
- }))
1091
- }
1092
- };
1093
- case "RuntimeModeSet":
1094
- return {
1095
- status: "applied",
1096
- snapshot: {
1097
- ...base,
1098
- runs: patchById(base.runs, payload.runId, (run) => ({
1099
- ...run,
1100
- runtimeMode: payload.runtimeMode,
1101
- updatedAt: payload.createdAt
1102
- }))
1103
- }
1104
- };
1105
- case "InteractionModeSet":
1106
- return {
1107
- status: "applied",
1108
- snapshot: {
1109
- ...base,
1110
- runs: patchById(base.runs, payload.runId, (run) => ({
1111
- ...run,
1112
- interactionMode: payload.interactionMode,
1113
- updatedAt: payload.createdAt
1114
- }))
1115
- }
1116
- };
1117
- case "ConversationAttached": {
1118
- const conversation = {
1119
- id: payload.conversationId,
1120
- runId: payload.runId,
1121
- title: payload.title,
1122
- createdAt: payload.createdAt,
1123
- updatedAt: payload.createdAt
1124
- };
1125
- return {
1126
- status: "applied",
1127
- snapshot: {
1128
- ...base,
1129
- conversations: upsertById(base.conversations, conversation)
1130
- }
1131
- };
1132
- }
1133
- case "MessageAppended": {
1134
- const conversation = base.conversations.find((item) => item.runId === payload.runId);
1135
- if (!conversation) {
1136
- return { status: "ignored", snapshot: base };
1137
- }
1138
- const conversationId = conversation.id;
1139
- const message = {
1140
- id: payload.messageId,
1141
- conversationId,
1142
- role: payload.role,
1143
- text: payload.text,
1144
- attachments: payload.attachments ?? [],
1145
- state: payload.state ?? "completed",
1146
- createdAt: payload.createdAt,
1147
- completedAt: payload.completedAt ?? payload.createdAt
1148
- };
1149
- return {
1150
- status: "applied",
1151
- snapshot: {
1152
- ...base,
1153
- messages: upsertById(base.messages, message),
1154
- runs: patchById(base.runs, payload.runId, (run) => ({
1155
- ...run,
1156
- latestMessageId: payload.messageId,
1157
- updatedAt: payload.createdAt
1158
- }))
1159
- }
1160
- };
1161
- }
1162
- case "ActionStarted": {
1163
- const action = {
1164
- id: payload.actionId,
1165
- runId: payload.runId,
1166
- messageId: payload.messageId ?? null,
1167
- actionType: payload.actionType,
1168
- title: payload.title,
1169
- detail: payload.detail ?? null,
1170
- state: payload.state ?? "running",
1171
- payload: payload.payload ?? null,
1172
- startedAt: payload.startedAt,
1173
- completedAt: null
1174
- };
1175
- return {
1176
- status: "applied",
1177
- snapshot: {
1178
- ...base,
1179
- actions: upsertById(base.actions, action)
1180
- }
1181
- };
1182
- }
1183
- case "ActionCompleted":
1184
- return {
1185
- status: "applied",
1186
- snapshot: {
1187
- ...base,
1188
- actions: patchById(base.actions, payload.actionId, (action) => ({
1189
- ...action,
1190
- state: payload.state,
1191
- detail: payload.detail ?? null,
1192
- payload: payload.payload,
1193
- completedAt: payload.completedAt
1194
- }))
1195
- }
1196
- };
1197
- case "RunLogAppended": {
1198
- const log = {
1199
- id: payload.logId,
1200
- runId: payload.runId,
1201
- title: payload.title,
1202
- detail: payload.detail ?? null,
1203
- tone: payload.tone,
1204
- status: payload.status ?? null,
1205
- payload: payload.payload ?? null,
1206
- createdAt: payload.createdAt
1207
- };
1208
- return {
1209
- status: "applied",
1210
- snapshot: {
1211
- ...base,
1212
- logs: upsertById(base.logs, log),
1213
- runs: patchById(base.runs, payload.runId, (run) => ({
1214
- ...run,
1215
- updatedAt: payload.createdAt
1216
- }))
1217
- }
1218
- };
1219
- }
1220
- case "ValidationRecorded":
1221
- return {
1222
- status: "applied",
1223
- snapshot: {
1224
- ...base,
1225
- validations: upsertById(base.validations, {
1226
- id: payload.id,
1227
- runId: payload.runId,
1228
- taskId: payload.taskId ?? null,
1229
- validatorKey: payload.validatorKey,
1230
- status: payload.status,
1231
- output: payload.output,
1232
- startedAt: payload.startedAt,
1233
- completedAt: payload.completedAt ?? null
1234
- })
1235
- }
1236
- };
1237
- case "ReviewRecorded":
1238
- return {
1239
- status: "applied",
1240
- snapshot: {
1241
- ...base,
1242
- reviews: upsertById(base.reviews, {
1243
- id: payload.id,
1244
- runId: payload.runId,
1245
- taskId: payload.taskId ?? null,
1246
- provider: payload.provider,
1247
- mode: payload.mode,
1248
- status: payload.status,
1249
- summary: payload.summary ?? null,
1250
- output: payload.output,
1251
- createdAt: payload.createdAt,
1252
- completedAt: payload.completedAt ?? null
1253
- })
1254
- }
1255
- };
1256
- case "ArtifactRegistered":
1257
- return {
1258
- status: "applied",
1259
- snapshot: {
1260
- ...base,
1261
- artifacts: upsertById(base.artifacts, {
1262
- id: payload.id,
1263
- runId: payload.runId,
1264
- taskId: payload.taskId ?? null,
1265
- kind: payload.kind,
1266
- label: payload.label,
1267
- path: payload.path ?? null,
1268
- url: payload.url ?? null,
1269
- metadata: payload.metadata ?? {},
1270
- createdAt: payload.createdAt
1271
- })
1272
- }
1273
- };
1274
- case "ApprovalRequested": {
1275
- const run = base.runs.find((item) => item.id === payload.runId);
1276
- const approval = {
1277
- id: payload.requestId,
1278
- runId: payload.runId,
1279
- actionId: payload.actionId ?? null,
1280
- requestKind: payload.requestKind,
1281
- status: "pending",
1282
- payload: payload.payload ?? null,
1283
- createdAt: payload.createdAt,
1284
- resolvedAt: null
1285
- };
1286
- return {
1287
- status: "applied",
1288
- snapshot: {
1289
- ...base,
1290
- approvals: upsertById(base.approvals, approval),
1291
- runs: patchById(base.runs, payload.runId, (item) => ({
1292
- ...item,
1293
- pendingApprovalCount: (run?.pendingApprovalCount ?? 0) + 1,
1294
- status: "waiting-approval",
1295
- updatedAt: payload.createdAt
1296
- }))
1297
- }
1298
- };
1299
- }
1300
- case "ApprovalResolved": {
1301
- const approval = base.approvals.find((item) => item.id === payload.requestId && item.runId === payload.runId);
1302
- if (!approval) {
1303
- return { status: "applied", snapshot: base };
1304
- }
1305
- const run = base.runs.find((item) => item.id === payload.runId);
1306
- const pendingApprovalCount = run ? Math.max(0, Number(run.pendingApprovalCount) - 1) : 0;
1307
- const nextStatus = run?.status === "waiting-approval" && pendingApprovalCount === 0 ? "running" : run?.status;
1308
- return {
1309
- status: "applied",
1310
- snapshot: {
1311
- ...base,
1312
- approvals: patchById(base.approvals, payload.requestId, (item) => ({
1313
- ...item,
1314
- status: "resolved",
1315
- resolvedAt: payload.createdAt
1316
- })),
1317
- runs: patchById(base.runs, payload.runId, (item) => ({
1318
- ...item,
1319
- pendingApprovalCount,
1320
- ...nextStatus ? { status: nextStatus } : {},
1321
- updatedAt: payload.createdAt
1322
- }))
1323
- }
1324
- };
1325
- }
1326
- case "UserInputRequested": {
1327
- const run = base.runs.find((item) => item.id === payload.runId);
1328
- return {
1329
- status: "applied",
1330
- snapshot: {
1331
- ...base,
1332
- userInputs: upsertById(base.userInputs ?? [], {
1333
- id: payload.requestId,
1334
- runId: payload.runId,
1335
- status: "pending",
1336
- payload: payload.payload,
1337
- createdAt: payload.createdAt,
1338
- resolvedAt: null
1339
- }),
1340
- runs: patchById(base.runs, payload.runId, (item) => ({
1341
- ...item,
1342
- pendingUserInputCount: (run?.pendingUserInputCount ?? 0) + 1,
1343
- status: "waiting-user-input",
1344
- updatedAt: payload.createdAt
1345
- }))
1346
- }
1347
- };
1348
- }
1349
- case "UserInputResolved": {
1350
- const run = base.runs.find((item) => item.id === payload.runId);
1351
- const pendingUserInputCount = run ? Math.max(0, Number(run.pendingUserInputCount) - 1) : 0;
1352
- const nextStatus = run?.status === "waiting-user-input" && pendingUserInputCount === 0 ? "running" : run?.status;
1353
- return {
1354
- status: "applied",
1355
- snapshot: {
1356
- ...base,
1357
- userInputs: patchById(base.userInputs ?? [], payload.requestId, (item) => ({
1358
- ...item,
1359
- status: "resolved",
1360
- resolvedAt: payload.createdAt
1361
- })),
1362
- runs: patchById(base.runs, payload.runId, (item) => ({
1363
- ...item,
1364
- pendingUserInputCount,
1365
- ...nextStatus ? { status: nextStatus } : {},
1366
- updatedAt: payload.createdAt
1367
- }))
1368
- }
1369
- };
1370
- }
1371
- case "RuntimePrepared": {
1372
- const runtime = {
1373
- id: payload.runtimeId,
1374
- workspaceId: payload.workspaceId,
1375
- runId: payload.runId,
1376
- adapterKind: payload.adapter,
1377
- executionTarget: payload.executionTarget ?? "local",
1378
- remoteHostId: payload.remoteHostId ?? null,
1379
- status: "prepared",
1380
- sandboxMode: "read-only",
1381
- isolationMode: "none",
1382
- workspaceDir: null,
1383
- homeDir: null,
1384
- tmpDir: null,
1385
- cacheDir: null,
1386
- logsDir: null,
1387
- stateDir: null,
1388
- sessionDir: null,
1389
- sessionLogPath: null,
1390
- pid: null,
1391
- startedAt: null,
1392
- updatedAt: payload.createdAt,
1393
- exitedAt: null
1394
- };
1395
- return {
1396
- status: "applied",
1397
- snapshot: {
1398
- ...base,
1399
- runtimes: upsertById(base.runtimes, runtime),
1400
- runs: patchById(base.runs, payload.runId, (run) => ({
1401
- ...run,
1402
- status: "preparing",
1403
- executionTarget: payload.executionTarget ?? run.executionTarget ?? "local",
1404
- remoteHostId: payload.remoteHostId ?? run.remoteHostId ?? null,
1405
- updatedAt: payload.createdAt
1406
- }))
1407
- }
1408
- };
1409
- }
1410
- case "RunRemoteLeaseClaimed":
1411
- return {
1412
- status: "applied",
1413
- snapshot: {
1414
- ...base,
1415
- runs: patchById(base.runs, payload.runId, (run) => ({
1416
- ...run,
1417
- remoteHostId: payload.hostId,
1418
- remoteLeaseId: payload.leaseId,
1419
- remoteLeaseClaimedAt: payload.createdAt,
1420
- updatedAt: payload.createdAt
1421
- }))
1422
- }
1423
- };
1424
- case "RunRemoteLeaseReleased":
1425
- return {
1426
- status: "applied",
1427
- snapshot: {
1428
- ...base,
1429
- runs: patchById(base.runs, payload.runId, (run) => ({
1430
- ...run,
1431
- remoteLeaseId: null,
1432
- remoteLeaseClaimedAt: null,
1433
- updatedAt: payload.createdAt
1434
- }))
1435
- }
1436
- };
1437
- case "RuntimeAttached": {
1438
- const tasks = payload.taskId == null ? base.tasks : patchById(base.tasks, payload.taskId, (task) => ({
1439
- ...task,
1440
- status: "running",
1441
- updatedAt: payload.createdAt
1442
- }));
1443
- return {
1444
- status: "applied",
1445
- snapshot: {
1446
- ...base,
1447
- runs: patchById(base.runs, payload.runId, (run) => ({
1448
- ...run,
1449
- activeRuntimeId: payload.runtimeId,
1450
- status: "running",
1451
- startedAt: payload.createdAt,
1452
- updatedAt: payload.createdAt
1453
- })),
1454
- runtimes: patchById(base.runtimes, payload.runtimeId, (runtime) => ({
1455
- ...runtime,
1456
- status: "running",
1457
- startedAt: payload.createdAt,
1458
- updatedAt: payload.createdAt
1459
- })),
1460
- tasks
1461
- }
1462
- };
1463
- }
1464
- case "RuntimeDetached":
1465
- return {
1466
- status: "applied",
1467
- snapshot: {
1468
- ...base,
1469
- runs: patchById(base.runs, payload.runId, (run) => ({
1470
- ...run,
1471
- activeRuntimeId: null,
1472
- updatedAt: payload.createdAt
1473
- })),
1474
- runtimes: patchById(base.runtimes, payload.runtimeId, (runtime) => ({
1475
- ...runtime,
1476
- status: "exited",
1477
- exitedAt: payload.createdAt,
1478
- updatedAt: payload.createdAt
1479
- }))
1480
- }
1481
- };
1482
- case "RuntimeMetadataUpdated":
1483
- return {
1484
- status: "applied",
1485
- snapshot: {
1486
- ...base,
1487
- runs: patchById(base.runs, payload.runId, (run) => ({
1488
- ...run,
1489
- ...payload.branch !== undefined ? { branch: payload.branch } : {},
1490
- ...payload.worktreePath !== undefined ? { worktreePath: payload.worktreePath } : {},
1491
- updatedAt: payload.createdAt
1492
- })),
1493
- runtimes: patchById(base.runtimes, payload.runtimeId, (runtime) => ({
1494
- ...runtime,
1495
- ...payload.sandboxMode !== undefined ? { sandboxMode: payload.sandboxMode } : {},
1496
- ...payload.isolationMode !== undefined ? { isolationMode: payload.isolationMode } : {},
1497
- ...payload.workspaceDir !== undefined ? { workspaceDir: payload.workspaceDir } : {},
1498
- ...payload.homeDir !== undefined ? { homeDir: payload.homeDir } : {},
1499
- ...payload.tmpDir !== undefined ? { tmpDir: payload.tmpDir } : {},
1500
- ...payload.cacheDir !== undefined ? { cacheDir: payload.cacheDir } : {},
1501
- ...payload.logsDir !== undefined ? { logsDir: payload.logsDir } : {},
1502
- ...payload.stateDir !== undefined ? { stateDir: payload.stateDir } : {},
1503
- ...payload.sessionDir !== undefined ? { sessionDir: payload.sessionDir } : {},
1504
- ...payload.sessionLogPath !== undefined ? { sessionLogPath: payload.sessionLogPath } : {},
1505
- ...payload.pid !== undefined ? { pid: payload.pid } : {},
1506
- updatedAt: payload.createdAt
1507
- }))
1508
- }
1509
- };
1510
- case "TaskStatusChanged": {
1511
- const queue = payload.status === "queued" ? base.queue : withQueuePositions(base.queue.filter((item) => item.taskId !== payload.taskId));
1512
- return {
1513
- status: "applied",
1514
- snapshot: {
1515
- ...base,
1516
- tasks: patchById(base.tasks, payload.taskId, (task) => ({
1517
- ...task,
1518
- status: payload.status,
1519
- updatedAt: payload.createdAt
1520
- })),
1521
- queue
1522
- }
1523
- };
1524
- }
1525
- case "TaskEnqueued": {
1526
- const entry = {
1527
- taskId: payload.taskId,
1528
- score: payload.score,
1529
- unblockCount: 0,
1530
- position: base.queue.length
1531
- };
1532
- const positionedQueue = withQueuePositions([
1533
- ...base.queue.filter((item) => item.taskId !== payload.taskId),
1534
- entry
1535
- ]);
1536
- return {
1537
- status: "applied",
1538
- snapshot: {
1539
- ...base,
1540
- tasks: patchById(base.tasks, payload.taskId, (task) => ({
1541
- ...task,
1542
- status: "queued",
1543
- updatedAt: payload.createdAt
1544
- })),
1545
- queue: positionedQueue
1546
- }
1547
- };
1548
- }
1549
- case "RemoteEndpointRegistered": {
1550
- const endpoint = payload.endpoint;
1551
- if (!endpoint || !endpoint.id) {
1552
- return { status: "ignored", snapshot: base };
1553
- }
1554
- return {
1555
- status: "applied",
1556
- snapshot: {
1557
- ...base,
1558
- remoteEndpoints: upsertById(base.remoteEndpoints, endpoint)
1559
- }
1560
- };
1561
- }
1562
- case "RemoteEndpointRemoved": {
1563
- const endpointId = payload.endpointId;
1564
- if (!endpointId) {
1565
- return { status: "ignored", snapshot: base };
1566
- }
1567
- return {
1568
- status: "applied",
1569
- snapshot: {
1570
- ...base,
1571
- remoteEndpoints: removeById(base.remoteEndpoints, endpointId),
1572
- remoteConnections: base.remoteConnections.filter((conn) => conn.endpointId !== endpointId),
1573
- remoteOrchestrations: base.remoteOrchestrations.filter((orch) => orch.endpointId !== endpointId)
1574
- }
1575
- };
1576
- }
1577
- case "RemoteEndpointUpdated": {
1578
- const endpointId = payload.endpointId;
1579
- const updates = payload.updates;
1580
- if (!endpointId || !updates) {
1581
- return { status: "ignored", snapshot: base };
1582
- }
1583
- return {
1584
- status: "applied",
1585
- snapshot: {
1586
- ...base,
1587
- remoteEndpoints: patchById(base.remoteEndpoints, endpointId, (endpoint) => ({
1588
- ...endpoint,
1589
- ...updates
1590
- }))
1591
- }
1592
- };
1593
- }
1594
- case "RemoteConnectionChanged": {
1595
- const endpointId = payload.endpointId;
1596
- const status = payload.status;
1597
- if (!endpointId || !status) {
1598
- return { status: "ignored", snapshot: base };
1599
- }
1600
- const existing = base.remoteConnections.find((conn) => conn.endpointId === endpointId);
1601
- const connection = {
1602
- ...existing ?? {
1603
- endpointId,
1604
- status,
1605
- error: null,
1606
- connectedAt: null,
1607
- tokenExpiresAt: null,
1608
- latencyMs: null,
1609
- subscribedEvents: []
1610
- },
1611
- endpointId,
1612
- status,
1613
- ...payload.error !== undefined ? { error: payload.error ?? null } : {},
1614
- ...status === "connected" ? { connectedAt: event.createdAt } : {}
1615
- };
1616
- return {
1617
- status: "applied",
1618
- snapshot: {
1619
- ...base,
1620
- remoteConnections: upsertByKey(base.remoteConnections, connection, "endpointId")
1621
- }
1622
- };
1623
- }
1624
- case "RemoteWorkspaceHydrated": {
1625
- const endpointId = payload.endpointId;
1626
- const workspace = payload.workspace;
1627
- const tasks = payload.tasks;
1628
- const graph = payload.graph;
1629
- if (!endpointId || !workspace) {
1630
- return { status: "ignored", snapshot: base };
1631
- }
1632
- return {
1633
- status: "applied",
1634
- snapshot: {
1635
- ...base,
1636
- workspaces: upsertById(base.workspaces, workspace),
1637
- tasks: Array.isArray(tasks) ? replaceWorkspaceSlice(base.tasks, workspace.id, tasks) : base.tasks,
1638
- graphs: graph ? upsertById(base.graphs, graph) : base.graphs
1639
- }
1640
- };
1641
- }
1642
- case "RemoteStateRefreshed": {
1643
- const endpointId = payload.endpointId;
1644
- const tasks = payload.tasks;
1645
- if (!endpointId) {
1646
- return { status: "ignored", snapshot: base };
1647
- }
1648
- const remoteWorkspace = base.workspaces.find((ws) => ws.id === `remote-workspace:${endpointId}`);
1649
- return {
1650
- status: "applied",
1651
- snapshot: {
1652
- ...base,
1653
- tasks: Array.isArray(tasks) && remoteWorkspace ? replaceWorkspaceSlice(base.tasks, remoteWorkspace.id, tasks) : base.tasks
1654
- }
1655
- };
1656
- }
1657
- case "RemoteEventReceived":
1658
- return { status: "applied", snapshot: base };
1659
- case "RemoteOrchestrationStarted": {
1660
- const orchestration = payload.orchestration;
1661
- if (!orchestration || !orchestration.orchestrationId) {
1662
- return { status: "ignored", snapshot: base };
1663
- }
1664
- return {
1665
- status: "applied",
1666
- snapshot: {
1667
- ...base,
1668
- remoteOrchestrations: upsertByKey(base.remoteOrchestrations, orchestration, "orchestrationId")
1669
- }
1670
- };
1671
- }
1672
- case "RemoteOrchestrationUpdated": {
1673
- const orchestrationId = payload.orchestrationId;
1674
- const state = payload.state;
1675
- if (!orchestrationId) {
1676
- return { status: "ignored", snapshot: base };
1677
- }
1678
- return {
1679
- status: "applied",
1680
- snapshot: {
1681
- ...base,
1682
- remoteOrchestrations: patchByKey(base.remoteOrchestrations, orchestrationId, "orchestrationId", (orch) => ({
1683
- ...orch,
1684
- ...isRecord(state) ? state : {}
1685
- }))
1686
- }
1687
- };
1688
- }
1689
- }
1690
- if (event.type === "workspace.imported") {
1691
- return { status: "requires-resync", snapshot };
1692
- }
1693
- if (event.type === "task.run-linked") {
1694
- const payload2 = isRecord(event.payload) ? event.payload : {};
1695
- const runId = readString(payload2, "runId");
1696
- const taskId = readString(payload2, "taskId");
1697
- const workspaceId = readString(payload2, "workspaceId");
1698
- if (!runId || !taskId || !workspaceId) {
1699
- return { status: "ignored", snapshot };
1700
- }
1701
- const baseRun = snapshot.runs.find((run) => run.id === runId) ?? {
1702
- id: asRunId(runId),
1703
- workspaceId: asWorkspaceId(workspaceId),
1704
- taskId: asTaskId(taskId),
1705
- title: "Task run",
1706
- runKind: "task",
1707
- mode: "interactive",
1708
- runtimeMode: "full-access",
1709
- interactionMode: "default",
1710
- status: "created",
1711
- runtimeAdapter: CANONICAL_RUNTIME_ADAPTER,
1712
- model: null,
1713
- initialPrompt: null,
1714
- activeRuntimeId: runtimeIdFromRunId(asRunId(runId)),
1715
- latestMessageId: null,
1716
- pendingApprovalCount: 0,
1717
- pendingUserInputCount: 0,
1718
- branch: null,
1719
- worktreePath: null,
1720
- errorText: null,
1721
- createdAt: event.createdAt,
1722
- updatedAt: event.createdAt,
1723
- startedAt: event.createdAt,
1724
- completedAt: null
1725
- };
1726
- const nextSnapshot = applyRun(snapshot, {
1727
- ...baseRun,
1728
- workspaceId: asWorkspaceId(workspaceId),
1729
- taskId: asTaskId(taskId),
1730
- runKind: "task",
1731
- runtimeAdapter: CANONICAL_RUNTIME_ADAPTER,
1732
- activeRuntimeId: runtimeIdFromRunId(asRunId(runId)),
1733
- updatedAt: event.createdAt
1734
- });
1735
- return {
1736
- status: "applied",
1737
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
1738
- };
1739
- }
1740
- if (event.type.startsWith("runtime.prepare.")) {
1741
- return applySyntheticRuntimePreparation(snapshot, event);
1742
- }
1743
- if (event.type === "runtime.prepared") {
1744
- return applySyntheticRuntimePrepared(snapshot, event);
1745
- }
1746
- if (event.type.startsWith("legacy.project.")) {
1747
- return applyLegacyProjectEvent(snapshot, event);
1748
- }
1749
- if (event.type.startsWith("legacy.thread.")) {
1750
- return applyLegacyThreadEvent(snapshot, event);
1751
- }
1752
- return { status: "ignored", snapshot: base };
1753
- }
1754
- function applyEngineEvents(snapshot, events) {
1755
- let nextSnapshot = snapshot;
1756
- let requiresResync = false;
1757
- let applied = false;
1758
- for (const event of events) {
1759
- const result = applyEngineEvent(nextSnapshot, event);
1760
- nextSnapshot = result.snapshot;
1761
- if (result.status === "requires-resync") {
1762
- requiresResync = true;
1763
- }
1764
- if (result.status === "applied") {
1765
- applied = true;
1766
- }
1767
- }
1768
- return {
1769
- status: requiresResync ? "requires-resync" : applied ? "applied" : "ignored",
1770
- snapshot: nextSnapshot
1771
- };
1772
- }
1773
- function pruneQueueEntries(snapshot) {
1774
- return snapshot.queue.filter((entry) => {
1775
- const task = snapshot.tasks.find((candidate) => candidate.id === entry.taskId);
1776
- return task?.status !== "running" && task?.status !== "in_progress" && task?.status !== "under_review";
1777
- });
1778
- }
1779
- export {
1780
- pruneQueueEntries,
1781
- mapTaskStatusFromRunStatus,
1782
- applyEngineEvents,
1783
- applyEngineEvent
1784
- };