@h-rig/run-worker 0.0.6-alpha.157 → 0.0.6-alpha.159

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 (47) hide show
  1. package/dist/src/autohost.d.ts +8 -10
  2. package/dist/src/autohost.js +683 -95
  3. package/dist/src/constants.d.ts +0 -1
  4. package/dist/src/constants.js +0 -2
  5. package/dist/src/extension.js +683 -95
  6. package/dist/src/host-kernel.d.ts +22 -0
  7. package/dist/src/host-kernel.js +78 -0
  8. package/dist/src/host.d.ts +2 -0
  9. package/dist/src/host.js +419 -0
  10. package/dist/src/index.d.ts +0 -6
  11. package/dist/src/index.js +1913 -133
  12. package/dist/src/local-run-changes.d.ts +3 -0
  13. package/dist/src/local-run-changes.js +65 -0
  14. package/dist/src/notifications.js +13 -5
  15. package/dist/src/notify-cap.d.ts +11 -0
  16. package/dist/src/notify-cap.js +13 -0
  17. package/dist/src/panel-plugin.js +3 -7
  18. package/dist/src/plugin.d.ts +0 -11
  19. package/dist/src/plugin.js +1910 -101
  20. package/dist/src/{runs → read-model-backend}/control.d.ts +0 -1
  21. package/dist/src/{runs → read-model-backend}/control.js +361 -38
  22. package/dist/src/{runs → read-model-backend}/diagnostics.d.ts +0 -1
  23. package/dist/src/{runs → read-model-backend}/diagnostics.js +1 -3
  24. package/dist/src/{runs → read-model-backend}/guard.js +2 -3
  25. package/dist/src/{runs → read-model-backend}/inbox.js +366 -36
  26. package/dist/src/{runs → read-model-backend}/index.js +552 -223
  27. package/dist/src/{runs → read-model-backend}/inspect.d.ts +0 -1
  28. package/dist/src/{runs → read-model-backend}/inspect.js +350 -34
  29. package/dist/src/{runs → read-model-backend}/projection.d.ts +21 -7
  30. package/dist/src/{runs → read-model-backend}/projection.js +349 -31
  31. package/dist/src/{runs → read-model-backend}/run-status.d.ts +6 -3
  32. package/dist/src/{runs → read-model-backend}/run-status.js +53 -33
  33. package/dist/src/{runs → read-model-backend}/stats.d.ts +0 -1
  34. package/dist/src/{runs → read-model-backend}/stats.js +373 -58
  35. package/dist/src/read-model-service.d.ts +2 -0
  36. package/dist/src/read-model-service.js +1433 -0
  37. package/dist/src/session-journal.d.ts +60 -0
  38. package/dist/src/session-journal.js +471 -0
  39. package/dist/src/stall.d.ts +8 -3
  40. package/dist/src/stall.js +0 -1
  41. package/dist/src/utils.js +282 -3
  42. package/package.json +9 -12
  43. package/dist/src/journal.d.ts +0 -33
  44. package/dist/src/journal.js +0 -31
  45. /package/dist/src/{runs → read-model-backend}/guard.d.ts +0 -0
  46. /package/dist/src/{runs → read-model-backend}/inbox.d.ts +0 -0
  47. /package/dist/src/{runs → read-model-backend}/index.d.ts +0 -0
@@ -16,7 +16,488 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
  var __require = import.meta.require;
18
18
 
19
- // packages/run-worker/src/runs/diagnostics.ts
19
+ // packages/run-worker/src/session-journal.ts
20
+ import { Schema } from "effect";
21
+ import {
22
+ CUSTOM_TYPE_FOR,
23
+ RIG_CONTROL_SENTINEL_END,
24
+ RIG_INBOX_RESOLUTION_SENTINEL,
25
+ RIG_PAUSE_SENTINEL,
26
+ RIG_RESUME_SENTINEL,
27
+ RIG_STOP_SENTINEL,
28
+ RIG_STOP_SENTINEL_END,
29
+ RIG_WORKFLOW_STATUS_CHANGED,
30
+ RunJournalEvent,
31
+ TYPE_FOR_CUSTOM
32
+ } from "@rig/contracts";
33
+ function isTerminalRunStatus(status) {
34
+ return TERMINAL_RUN_STATUSES.includes(status);
35
+ }
36
+ function canTransitionRunStatus(from, to) {
37
+ if (from === null)
38
+ return true;
39
+ if (from === to)
40
+ return true;
41
+ return RUN_STATUS_TRANSITIONS[from].includes(to);
42
+ }
43
+ function assertRunStatusTransition(from, to) {
44
+ if (!canTransitionRunStatus(from, to)) {
45
+ throw new Error(`Illegal run status transition: ${from ?? "(none)"} -> ${to}. ` + `Allowed from ${from ?? "(none)"}: ${from ? RUN_STATUS_TRANSITIONS[from].join(", ") : "(any)"}.`);
46
+ }
47
+ }
48
+ function reduceRunJournal(events, runId = null) {
49
+ let record = {};
50
+ let status = null;
51
+ const statusHistory = [];
52
+ const pendingApprovals = new Map;
53
+ const resolvedApprovals = [];
54
+ const pendingUserInputs = new Map;
55
+ const resolvedUserInputs = [];
56
+ const closeoutPhases = [];
57
+ let resolvedPipeline = null;
58
+ const stageOutcomes = [];
59
+ const anomalies = [];
60
+ let steeringCount = 0;
61
+ let stallCount = 0;
62
+ let lastSeq = 0;
63
+ let lastEventAt = null;
64
+ const projectedRunId = runId ?? events[0]?.runId ?? null;
65
+ for (const event of events) {
66
+ lastSeq = event.seq;
67
+ lastEventAt = event.at;
68
+ switch (event.type) {
69
+ case "status-changed": {
70
+ if (!canTransitionRunStatus(status, event.to)) {
71
+ anomalies.push({ seq: event.seq, kind: "illegal-transition", detail: `${status ?? "(none)"} -> ${event.to}` });
72
+ }
73
+ statusHistory.push({ seq: event.seq, at: event.at, from: status, to: event.to, reason: event.reason ?? null });
74
+ const wasTerminal = status !== null && isTerminalRunStatus(status);
75
+ status = event.to;
76
+ record = { ...record, updatedAt: event.at };
77
+ if (isTerminalRunStatus(event.to) && !record.completedAt)
78
+ record = { ...record, completedAt: event.at };
79
+ if (!isTerminalRunStatus(event.to) && wasTerminal)
80
+ record = { ...record, completedAt: null };
81
+ if (event.to === "running" && !record.startedAt)
82
+ record = { ...record, startedAt: event.at };
83
+ break;
84
+ }
85
+ case "record-patch": {
86
+ const next = { ...record };
87
+ for (const [key, value] of Object.entries(event.patch)) {
88
+ if (value !== undefined)
89
+ next[key] = value;
90
+ }
91
+ next.updatedAt = event.patch.updatedAt ?? event.at;
92
+ record = next;
93
+ break;
94
+ }
95
+ case "approval-requested": {
96
+ pendingApprovals.set(event.requestId, {
97
+ requestId: event.requestId,
98
+ requestKind: event.requestKind,
99
+ actionId: event.actionId ?? null,
100
+ payload: event.payload,
101
+ requestedAt: event.at
102
+ });
103
+ break;
104
+ }
105
+ case "approval-resolved": {
106
+ const pending = pendingApprovals.get(event.requestId);
107
+ if (!pending) {
108
+ const alreadyResolved = resolvedApprovals.some((entry) => entry.requestId === event.requestId);
109
+ anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `approval ${event.requestId}` });
110
+ break;
111
+ }
112
+ pendingApprovals.delete(event.requestId);
113
+ resolvedApprovals.push({ ...pending, decision: event.decision, note: event.note ?? null, actor: event.actor, resolvedAt: event.at });
114
+ break;
115
+ }
116
+ case "input-requested": {
117
+ pendingUserInputs.set(event.requestId, {
118
+ requestId: event.requestId,
119
+ requestKind: "user-input",
120
+ actionId: null,
121
+ payload: event.payload,
122
+ requestedAt: event.at
123
+ });
124
+ break;
125
+ }
126
+ case "input-resolved": {
127
+ const pending = pendingUserInputs.get(event.requestId);
128
+ if (!pending) {
129
+ const alreadyResolved = resolvedUserInputs.some((entry) => entry.requestId === event.requestId);
130
+ anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `user-input ${event.requestId}` });
131
+ break;
132
+ }
133
+ pendingUserInputs.delete(event.requestId);
134
+ resolvedUserInputs.push({ ...pending, answers: event.answers, actor: event.actor, resolvedAt: event.at });
135
+ break;
136
+ }
137
+ case "steering":
138
+ steeringCount += 1;
139
+ break;
140
+ case "adopted":
141
+ record = { ...record, pid: event.pid, updatedAt: event.at };
142
+ break;
143
+ case "stall-detected":
144
+ stallCount += 1;
145
+ break;
146
+ case "closeout-phase":
147
+ closeoutPhases.push({ seq: event.seq, at: event.at, phase: event.phase, outcome: event.outcome, detail: event.detail ?? null });
148
+ break;
149
+ case "pipeline-resolved":
150
+ resolvedPipeline = event.pipeline;
151
+ break;
152
+ case "stage-outcome":
153
+ stageOutcomes.push({ seq: event.seq, at: event.at, outcome: event.outcome });
154
+ break;
155
+ case "timeline-entry":
156
+ case "log-entry":
157
+ break;
158
+ }
159
+ }
160
+ return {
161
+ runId: projectedRunId,
162
+ record,
163
+ status,
164
+ statusHistory,
165
+ pendingApprovals: [...pendingApprovals.values()],
166
+ resolvedApprovals,
167
+ pendingUserInputs: [...pendingUserInputs.values()],
168
+ resolvedUserInputs,
169
+ steeringCount,
170
+ stallCount,
171
+ closeoutPhases,
172
+ resolvedPipeline,
173
+ stageOutcomes,
174
+ lastSeq,
175
+ lastEventAt,
176
+ anomalies
177
+ };
178
+ }
179
+ function isRunSessionCustomType(customType) {
180
+ return customType !== undefined && Object.hasOwn(TYPE_FOR_CUSTOM, customType);
181
+ }
182
+ function foldRunSessionEntries(entries, runId) {
183
+ const events = [];
184
+ entries.forEach((entry, index) => {
185
+ if (entry.type !== "custom" || !isRunSessionCustomType(entry.customType))
186
+ return;
187
+ const data = entry.data !== null && typeof entry.data === "object" ? entry.data : {};
188
+ const stamped = {
189
+ v: 1,
190
+ seq: index + 1,
191
+ at: typeof data.at === "string" ? data.at : new Date(0).toISOString(),
192
+ runId,
193
+ ...data,
194
+ type: TYPE_FOR_CUSTOM[entry.customType]
195
+ };
196
+ try {
197
+ events.push(decodeRunJournalEvent(stamped));
198
+ } catch {}
199
+ });
200
+ return reduceRunJournal(events, runId);
201
+ }
202
+ function isRecord(value) {
203
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
204
+ }
205
+ function timelineEntryFromCustomEntry(entry) {
206
+ if (entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
207
+ return null;
208
+ const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
209
+ const type = typeof payload.type === "string" ? payload.type : "timeline";
210
+ const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
211
+ const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
212
+ const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
213
+ const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
214
+ return { at, type, stage, status, detail };
215
+ }
216
+ function timelineEntriesFromCustomEntries(entries) {
217
+ return entries.flatMap((entry) => {
218
+ const timelineEntry = timelineEntryFromCustomEntry(entry);
219
+ return timelineEntry ? [timelineEntry] : [];
220
+ });
221
+ }
222
+ function asCustomEntries(entries) {
223
+ return entries.filter((entry) => entry.type === "custom");
224
+ }
225
+
226
+ class RunSessionJournal {
227
+ sm;
228
+ runId;
229
+ constructor(sm, runId) {
230
+ this.sm = sm;
231
+ this.runId = runId;
232
+ }
233
+ #append(init) {
234
+ this.sm.appendCustomEntry(CUSTOM_TYPE_FOR[init.type], { ...init, at: new Date().toISOString() });
235
+ }
236
+ appendStatus(to, opts = {}) {
237
+ const from = foldRunSessionEntries(asCustomEntries(this.sm.getEntries()), this.runId).status;
238
+ if (!opts.force)
239
+ assertRunStatusTransition(from, to);
240
+ if (opts.errorText !== undefined)
241
+ this.#append({ type: "record-patch", patch: { errorText: opts.errorText } });
242
+ this.#append({
243
+ type: "status-changed",
244
+ from,
245
+ to,
246
+ ...opts.reason !== undefined ? { reason: opts.reason } : {},
247
+ ...opts.actor !== undefined ? { actor: opts.actor } : {}
248
+ });
249
+ }
250
+ appendTimeline(payload) {
251
+ this.#append({ type: "timeline-entry", payload });
252
+ }
253
+ appendCloseoutPhase(input) {
254
+ this.#append({
255
+ type: "closeout-phase",
256
+ phase: input.phase,
257
+ outcome: input.outcome,
258
+ ...input.detail !== undefined ? { detail: input.detail } : {}
259
+ });
260
+ }
261
+ appendApprovalResolved(input) {
262
+ this.#append({
263
+ type: "approval-resolved",
264
+ requestId: input.requestId,
265
+ decision: input.decision,
266
+ actor: input.actor,
267
+ ...input.note !== undefined ? { note: input.note } : {}
268
+ });
269
+ }
270
+ appendInputResolved(input) {
271
+ this.#append({ type: "input-resolved", requestId: input.requestId, answers: input.answers, actor: input.actor });
272
+ }
273
+ appendStall(input) {
274
+ this.#append({ type: "stall-detected", detail: input.detail });
275
+ }
276
+ }
277
+ var decodeRunJournalEvent, RUN_STATUS_TRANSITIONS, TERMINAL_RUN_STATUSES;
278
+ var init_session_journal = __esm(() => {
279
+ decodeRunJournalEvent = Schema.decodeUnknownSync(RunJournalEvent);
280
+ RUN_STATUS_TRANSITIONS = {
281
+ created: ["queued", "preparing", "running", "failed", "stopped"],
282
+ queued: ["preparing", "running", "failed", "stopped"],
283
+ preparing: ["queued", "running", "needs-attention", "failed", "stopped"],
284
+ running: [
285
+ "queued",
286
+ "waiting-approval",
287
+ "waiting-user-input",
288
+ "paused",
289
+ "validating",
290
+ "reviewing",
291
+ "closing-out",
292
+ "needs-attention",
293
+ "completed",
294
+ "failed",
295
+ "stopped"
296
+ ],
297
+ "waiting-approval": ["running", "needs-attention", "failed", "stopped"],
298
+ "waiting-user-input": ["running", "needs-attention", "failed", "stopped"],
299
+ paused: ["running", "failed", "stopped"],
300
+ validating: ["running", "reviewing", "closing-out", "needs-attention", "completed", "failed", "stopped"],
301
+ reviewing: ["running", "validating", "closing-out", "needs-attention", "completed", "failed", "stopped"],
302
+ "closing-out": ["running", "needs-attention", "completed", "failed", "stopped"],
303
+ "needs-attention": ["queued", "preparing", "running", "closing-out", "completed", "failed", "stopped"],
304
+ completed: [],
305
+ failed: ["queued", "preparing", "running", "closing-out"],
306
+ stopped: ["queued", "preparing", "running", "closing-out"]
307
+ };
308
+ TERMINAL_RUN_STATUSES = ["completed", "failed", "stopped"];
309
+ });
310
+
311
+ // packages/run-worker/src/read-model-backend/run-status.ts
312
+ import { OPERATOR_INACTIVE_RUN_STATUSES } from "@rig/contracts";
313
+ function isTerminalRunStatus2(status) {
314
+ return TERMINAL_RUN_STATUSES2.includes(status);
315
+ }
316
+ function isActiveRunStatus(status) {
317
+ return !isTerminalRunStatus2(status);
318
+ }
319
+ function normalizeRunStatusToken(status) {
320
+ return String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
321
+ }
322
+ function isOperatorActiveRunStatus(status) {
323
+ const normalized = normalizeRunStatusToken(status);
324
+ if (!normalized)
325
+ return false;
326
+ return !OPERATOR_INACTIVE_RUN_STATUSES.has(normalized);
327
+ }
328
+ function canonicalStatusToken(status) {
329
+ const normalized = normalizeRunStatusToken(status);
330
+ if (normalized === "waiting-input")
331
+ return "waiting-user-input";
332
+ return normalized;
333
+ }
334
+ function asRunStatus(status) {
335
+ return KNOWN_RUN_STATUS[status] ? status : null;
336
+ }
337
+ function statusColorRole(status) {
338
+ switch (canonicalStatusToken(status)) {
339
+ case "done":
340
+ case "completed":
341
+ case "ready":
342
+ case "healthy":
343
+ case "approved":
344
+ case "merged":
345
+ return "success";
346
+ case "needs-attention":
347
+ case "waiting-approval":
348
+ case "waiting-user-input":
349
+ case "blocked":
350
+ case "paused":
351
+ return "action-yellow";
352
+ case "running":
353
+ case "adopted":
354
+ case "preparing":
355
+ case "created":
356
+ case "queued":
357
+ case "starting":
358
+ case "pending":
359
+ case "in-progress":
360
+ case "active":
361
+ case "booting":
362
+ case "validating":
363
+ case "reviewing":
364
+ case "closing-out":
365
+ return "active-cyan";
366
+ case "failed":
367
+ case "error":
368
+ case "rejected":
369
+ return "failure";
370
+ case "stopped":
371
+ case "cancelled":
372
+ case "canceled":
373
+ case "stale":
374
+ return "muted";
375
+ default:
376
+ return "neutral";
377
+ }
378
+ }
379
+ function isNeedsAttention(run) {
380
+ return canonicalStatusToken(run.status) === "needs-attention" || run.pendingApprovals + run.pendingInputs > 0 || run.stallCount > 0;
381
+ }
382
+ function phaseForStatus(status, runStatus, needsAttention) {
383
+ if (runStatus && isTerminalRunStatus2(runStatus))
384
+ return runStatus === "completed" || runStatus === "failed" || runStatus === "stopped" ? runStatus : "stopped";
385
+ if (needsAttention)
386
+ return "needs-attention";
387
+ switch (status) {
388
+ case "created":
389
+ case "queued":
390
+ case "preparing":
391
+ case "starting":
392
+ case "booting":
393
+ return "starting";
394
+ case "waiting-approval":
395
+ case "waiting-user-input":
396
+ return "waiting";
397
+ case "paused":
398
+ return "paused";
399
+ case "running":
400
+ case "validating":
401
+ case "reviewing":
402
+ case "closing-out":
403
+ return "active";
404
+ default:
405
+ return runStatus && isActiveRunStatus(runStatus) ? "active" : "unknown";
406
+ }
407
+ }
408
+ function classifyRun(run) {
409
+ const status = canonicalStatusToken(run.status) || (run.live && !run.stale ? "starting" : "unknown");
410
+ const runStatus = asRunStatus(status);
411
+ const isTerminal = runStatus ? isTerminalRunStatus2(runStatus) : false;
412
+ const isNeedsAttentionValue = isNeedsAttention(run);
413
+ return {
414
+ status,
415
+ phase: phaseForStatus(status, runStatus, isNeedsAttentionValue),
416
+ isActive: runStatus ? isActiveRunStatus(runStatus) : !isTerminal && status !== "unknown",
417
+ isTerminal,
418
+ isNeedsAttention: isNeedsAttentionValue
419
+ };
420
+ }
421
+ function runStatusText(run) {
422
+ return classifyRun(run).status;
423
+ }
424
+ function runStatusColorRole(run) {
425
+ const classification = classifyRun(run);
426
+ return classification.isNeedsAttention && !classification.isTerminal ? "action-yellow" : statusColorRole(classification.status);
427
+ }
428
+ function statusRank(run) {
429
+ const classification = classifyRun(run);
430
+ if (classification.isNeedsAttention)
431
+ return 0;
432
+ switch (classification.phase) {
433
+ case "needs-attention":
434
+ return 0;
435
+ case "active":
436
+ case "waiting":
437
+ case "paused":
438
+ return 1;
439
+ case "starting":
440
+ return 2;
441
+ case "completed":
442
+ return 3;
443
+ case "failed":
444
+ return 4;
445
+ case "stopped":
446
+ return 5;
447
+ case "unknown":
448
+ return 6;
449
+ }
450
+ }
451
+ function isSteerableStatus(status) {
452
+ switch (status) {
453
+ case "needs-attention":
454
+ case "waiting-approval":
455
+ case "waiting-user-input":
456
+ case "paused":
457
+ return false;
458
+ default: {
459
+ const runStatus = asRunStatus(status);
460
+ return runStatus ? isActiveRunStatus(runStatus) : false;
461
+ }
462
+ }
463
+ }
464
+ function canSteer(run) {
465
+ const classification = classifyRun(run);
466
+ if (classification.phase === "active" || classification.phase === "starting")
467
+ return true;
468
+ return classification.isNeedsAttention && isSteerableStatus(classification.status);
469
+ }
470
+ function canStop(run) {
471
+ const classification = classifyRun(run);
472
+ return classification.isActive && !classification.isTerminal;
473
+ }
474
+ function canPause(run) {
475
+ const phase = classifyRun(run).phase;
476
+ return phase === "active" || phase === "starting";
477
+ }
478
+ function canResume(run) {
479
+ return classifyRun(run).phase === "paused";
480
+ }
481
+ var TERMINAL_RUN_STATUSES2, ACTIVE_RUN_STATUSES, KNOWN_RUN_STATUS;
482
+ var init_run_status = __esm(() => {
483
+ TERMINAL_RUN_STATUSES2 = ["completed", "failed", "stopped"];
484
+ ACTIVE_RUN_STATUSES = [
485
+ "created",
486
+ "queued",
487
+ "preparing",
488
+ "running",
489
+ "waiting-approval",
490
+ "waiting-user-input",
491
+ "paused",
492
+ "validating",
493
+ "reviewing",
494
+ "closing-out",
495
+ "needs-attention"
496
+ ];
497
+ KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES2].map((status) => [status, true]));
498
+ });
499
+
500
+ // packages/run-worker/src/read-model-backend/diagnostics.ts
20
501
  function normalizeString(value) {
21
502
  if (typeof value !== "string")
22
503
  return null;
@@ -63,13 +544,12 @@ function summarizeRunError(projection) {
63
544
  const nonGeneric = candidates.filter((candidate) => !isGenericRunFailure(candidate));
64
545
  return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
65
546
  }
66
- var init_diagnostics = () => {};
67
547
 
68
- // packages/run-worker/src/runs/projection.ts
548
+ // packages/run-worker/src/read-model-backend/projection.ts
69
549
  var exports_projection = {};
70
550
  __export(exports_projection, {
71
551
  selectRunProjection: () => selectRunProjection,
72
- runRecordsFromRegistrySnapshot: () => runRecordsFromRegistrySnapshot,
552
+ runRecordsFromCollab: () => runRecordsFromCollab,
73
553
  runRecordFromRegistryEntry: () => runRecordFromRegistryEntry,
74
554
  resolveRunJoinTarget: () => resolveRunJoinTarget,
75
555
  resolveJoinTarget: () => resolveJoinTarget,
@@ -82,13 +562,28 @@ __export(exports_projection, {
82
562
  });
83
563
  import { existsSync, readFileSync } from "fs";
84
564
  import { isAbsolute, relative, resolve } from "path";
85
- import { Duration, Effect, Option, Stream } from "effect";
86
- import {
87
- foldRunSessionEntries,
88
- isTerminalRunStatus,
89
- timelineEntriesFromCustomEntries
90
- } from "@rig/contracts";
91
- import { registrySnapshotStream } from "@rig/runtime/control-plane/discovery";
565
+ import { RUN_DISCOVERY } from "@rig/contracts";
566
+ import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
567
+ import { defineCapability } from "@rig/core/capability";
568
+ function registryEntryFromCollab(collab) {
569
+ const record = collab;
570
+ return {
571
+ roomId: collab.sessionId,
572
+ title: collab.title,
573
+ status: record.registryStatus ?? (collab.stale ? "stale" : "running"),
574
+ startedAt: collab.startedAt ?? null,
575
+ heartbeatAt: collab.updatedAt ?? null,
576
+ sessionPath: collab.sessionPath ?? null,
577
+ cwd: collab.cwd ?? null,
578
+ joinLink: collab.joinLink ?? null,
579
+ webLink: collab.webLink ?? null,
580
+ relayUrl: collab.relayUrl ?? null,
581
+ stale: collab.stale,
582
+ repo: collab.selectedRepo ?? null,
583
+ ...collab.pid === undefined ? {} : { pid: collab.pid },
584
+ projection: record.registryProjection ?? null
585
+ };
586
+ }
92
587
  function readSessionRunEntries(sessionPath) {
93
588
  if (!sessionPath || !existsSync(sessionPath))
94
589
  return [];
@@ -133,22 +628,7 @@ function registryStatusAsRunStatus(status) {
133
628
  return "waiting-user-input";
134
629
  if (status === "starting")
135
630
  return "preparing";
136
- return typeof status === "string" && [
137
- "created",
138
- "queued",
139
- "preparing",
140
- "running",
141
- "waiting-approval",
142
- "waiting-user-input",
143
- "paused",
144
- "validating",
145
- "reviewing",
146
- "closing-out",
147
- "needs-attention",
148
- "completed",
149
- "failed",
150
- "stopped"
151
- ].includes(status) ? status : null;
631
+ return typeof status === "string" ? asRunStatus(status) : null;
152
632
  }
153
633
  function payloadString(payload, keys) {
154
634
  if (!payload || typeof payload !== "object")
@@ -171,12 +651,14 @@ function payloadOptions(payload) {
171
651
  }
172
652
  function inboxRequest(request, kind) {
173
653
  const fallback = kind === "approval" ? "Approval requested" : "Input requested";
654
+ const body = payloadString(request.payload, ["body", "description", "detail", "details"]);
655
+ const options = payloadOptions(request.payload);
174
656
  return {
175
657
  requestId: request.requestId,
176
658
  kind,
177
659
  title: payloadString(request.payload, ["title", "message", "reason", "prompt", "summary"]) ?? fallback,
178
- body: payloadString(request.payload, ["body", "description", "detail", "details"]),
179
- ...payloadOptions(request.payload) ? { options: payloadOptions(request.payload) } : {},
660
+ ...body !== undefined ? { body } : {},
661
+ ...options ? { options } : {},
180
662
  requestedAt: request.requestedAt ?? null,
181
663
  source: "run"
182
664
  };
@@ -285,8 +767,8 @@ function runRecordFromRegistryEntry(projectRoot, entry) {
285
767
  projection: folded
286
768
  };
287
769
  }
288
- function runRecordsFromRegistrySnapshot(projectRoot, snapshot) {
289
- return sortByRecency(snapshot.entries.map((entry) => runRecordFromRegistryEntry(projectRoot, entry)).filter((record) => record !== null));
770
+ function runRecordsFromCollab(projectRoot, collabs) {
771
+ return sortByRecency(collabs.map((collab) => runRecordFromRegistryEntry(projectRoot, registryEntryFromCollab(collab))).filter((record) => record !== null));
290
772
  }
291
773
  function sortByRecency(records) {
292
774
  return [...records].sort((a, b) => {
@@ -334,8 +816,11 @@ function discoveryDiagnosticRunRecord(projectRoot, error) {
334
816
  }
335
817
  async function listRunProjections(projectRoot, filter = {}) {
336
818
  try {
337
- const snapshot = await Effect.runPromise(registrySnapshotStream(projectRoot, filter).pipe(Stream.runHead, Effect.timeout(Duration.seconds(15)), Effect.map(Option.getOrNull)));
338
- return snapshot ? runRecordsFromRegistrySnapshot(projectRoot, snapshot) : [discoveryDiagnosticRunRecord(projectRoot, "registry discovery stream ended without a snapshot")];
819
+ const discovery = await loadCapabilityForRoot(projectRoot, RunDiscoveryCap);
820
+ if (!discovery)
821
+ return [discoveryDiagnosticRunRecord(projectRoot, "run discovery capability unavailable")];
822
+ const collabs = await discovery.listActiveRunCollab(projectRoot, filter);
823
+ return runRecordsFromCollab(projectRoot, collabs);
339
824
  } catch (error) {
340
825
  return [discoveryDiagnosticRunRecord(projectRoot, error)];
341
826
  }
@@ -366,18 +851,29 @@ async function resolveRunJoinTarget(projectRoot, runId, filter = {}) {
366
851
  return null;
367
852
  return { runId: run.runId, taskId: run.taskId, joinLink: run.joinLink, cwd: run.collabCwd ?? run.sessionPath, stale: run.stale };
368
853
  }
369
- var EMPTY_PROJECTION, DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__", listRuns, getRun, resolveJoinTarget;
854
+ var RunDiscoveryCap, EMPTY_PROJECTION, DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__", listRuns, getRun, resolveJoinTarget;
370
855
  var init_projection = __esm(() => {
371
- init_diagnostics();
856
+ init_session_journal();
857
+ init_run_status();
858
+ RunDiscoveryCap = defineCapability(RUN_DISCOVERY);
372
859
  EMPTY_PROJECTION = foldRunSessionEntries([], "");
373
860
  listRuns = listRunProjections;
374
861
  getRun = getRunProjection;
375
862
  resolveJoinTarget = resolveRunJoinTarget;
376
863
  });
377
864
 
378
- // packages/run-worker/src/runs/control.ts
865
+ // packages/run-worker/src/read-model-backend/control.ts
379
866
  init_projection();
380
- import { buildPauseSentinel, buildResumeSentinel, buildStopSentinel } from "@rig/contracts";
867
+ import { RIG_CONTROL_SENTINEL_END as RIG_CONTROL_SENTINEL_END2, RIG_PAUSE_SENTINEL as RIG_PAUSE_SENTINEL2, RIG_RESUME_SENTINEL as RIG_RESUME_SENTINEL2, RIG_STOP_SENTINEL as RIG_STOP_SENTINEL2, RIG_STOP_SENTINEL_END as RIG_STOP_SENTINEL_END2 } from "@rig/contracts";
868
+ function buildPauseSentinel(runId) {
869
+ return `${RIG_PAUSE_SENTINEL2} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END2}`;
870
+ }
871
+ function buildResumeSentinel(runId) {
872
+ return `${RIG_RESUME_SENTINEL2} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END2}`;
873
+ }
874
+ function buildStopSentinel(runId, reason) {
875
+ return `${RIG_STOP_SENTINEL2} runId=${runId} reason=${reason} ${RIG_STOP_SENTINEL_END2}`;
876
+ }
381
877
  async function loadCollabControlDeps() {
382
878
  const [{ importRoomKey, seal }, { COLLAB_PROTO, packEnvelope, parseCollabLink }] = await Promise.all([
383
879
  import("@oh-my-pi/pi-coding-agent/collab/crypto"),
@@ -406,9 +902,6 @@ Stop this run and do not continue after the interruption settles.` },
406
902
  ];
407
903
  }
408
904
  }
409
- function collabControlFrame(runId, control) {
410
- return collabControlFrames(runId, control)[0];
411
- }
412
905
  async function sendSealedCollabFrames(joinLink, frames, deps = {}) {
413
906
  const { importRoomKey, seal, COLLAB_PROTO, packEnvelope, parseCollabLink } = await loadCollabControlDeps();
414
907
  const parsed = parseCollabLink(joinLink);
@@ -459,9 +952,8 @@ async function deliverRunControl(projectRoot, target, control, deps = {}) {
459
952
  throw new Error(`Could not ${control.kind} run ${record.runId}. ${detail}`);
460
953
  }
461
954
  }
462
- // packages/run-worker/src/runs/guard.ts
463
- import { TERMINAL_RUN_STATUSES } from "@rig/contracts";
464
- var TERMINAL = new Set(TERMINAL_RUN_STATUSES);
955
+ // packages/run-worker/src/read-model-backend/guard.ts
956
+ var TERMINAL = new Set(["completed", "failed", "stopped"]);
465
957
  function runBlocksNewRunForTask(run) {
466
958
  return run.live && !run.stale && !TERMINAL.has(run.status) && run.status !== "needs-attention" && run.status !== "needs_attention";
467
959
  }
@@ -479,9 +971,12 @@ async function assertNoActiveRunForTask(listRuns2, projectRoot, taskId) {
479
971
  throw new Error(`Task ${taskId} already has an active run: ${existing}. Attach instead: rig run attach ${existing} \u2014 or re-dispatch anyway with --force.`);
480
972
  }
481
973
  }
482
- // packages/run-worker/src/runs/inbox.ts
483
- import { buildInboxResolutionSentinel } from "@rig/contracts";
974
+ // packages/run-worker/src/read-model-backend/inbox.ts
975
+ import { RIG_CONTROL_SENTINEL_END as RIG_CONTROL_SENTINEL_END3, RIG_INBOX_RESOLUTION_SENTINEL as RIG_INBOX_RESOLUTION_SENTINEL2 } from "@rig/contracts";
484
976
  init_projection();
977
+ function buildInboxResolutionSentinel(runId, payload) {
978
+ return `${RIG_INBOX_RESOLUTION_SENTINEL2} runId=${runId} data=${encodeURIComponent(JSON.stringify(payload))} requestedBy=operator ${RIG_CONTROL_SENTINEL_END3}`;
979
+ }
485
980
  function promptFromPayload(payload, fallback) {
486
981
  if (payload && typeof payload === "object") {
487
982
  const record = payload;
@@ -578,9 +1073,8 @@ function decisionText(runId, requestId, decision) {
578
1073
  async function resolveInboxRequest(projectRoot, runId, requestId, decision, deps = {}) {
579
1074
  await (deps.deliverRunControl ?? deliverRunControl)(projectRoot, runId, { kind: "steer", message: decisionText(runId, requestId, decision) });
580
1075
  }
581
- // packages/run-worker/src/runs/inspect.ts
1076
+ // packages/run-worker/src/read-model-backend/inspect.ts
582
1077
  init_projection();
583
- init_diagnostics();
584
1078
  import { RIG_RUN_LOG_ENTRY } from "@rig/contracts";
585
1079
  function asRecord(value) {
586
1080
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -665,7 +1159,6 @@ function summarizeRunFailures(run) {
665
1159
  }
666
1160
  return failures;
667
1161
  }
668
- var summarizeProjectionFailures = summarizeRunFailures;
669
1162
  async function runsForTask(projectRoot, taskId, deps = {}) {
670
1163
  const list = deps.listRuns ?? listRuns;
671
1164
  try {
@@ -689,175 +1182,13 @@ function runInspectSummaryRows(runs, options = {}) {
689
1182
  { id: "inspect:audit", label: "AUDIT", currentValue: "unavailable", heading: true, description: "CLI inspect audit has no cockpit equivalent; use rig inspect audit" }
690
1183
  ];
691
1184
  }
692
- // packages/run-worker/src/runs/run-status.ts
693
- import {
694
- ACTIVE_RUN_STATUSES,
695
- TERMINAL_RUN_STATUSES as TERMINAL_RUN_STATUSES2,
696
- isActiveRunStatus,
697
- isTerminalRunStatus as isTerminalRunStatus2,
698
- normalizeRunStatusToken
699
- } from "@rig/contracts";
700
- var KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES2].map((status) => [status, true]));
701
- function canonicalStatusToken(status) {
702
- const normalized = normalizeRunStatusToken(status);
703
- if (normalized === "waiting-input")
704
- return "waiting-user-input";
705
- return normalized;
706
- }
707
- function asRunStatus(status) {
708
- return KNOWN_RUN_STATUS[status] ? status : null;
709
- }
710
- function statusColorRole(status) {
711
- switch (canonicalStatusToken(status)) {
712
- case "done":
713
- case "completed":
714
- case "ready":
715
- case "healthy":
716
- case "approved":
717
- case "merged":
718
- return "success";
719
- case "needs-attention":
720
- case "needs_attention":
721
- case "waiting-approval":
722
- case "waiting-user-input":
723
- case "waiting-input":
724
- case "waiting_input":
725
- case "blocked":
726
- case "paused":
727
- return "action-yellow";
728
- case "running":
729
- case "adopted":
730
- case "preparing":
731
- case "created":
732
- case "queued":
733
- case "starting":
734
- case "pending":
735
- case "in_progress":
736
- case "in-progress":
737
- case "active":
738
- case "booting":
739
- case "validating":
740
- case "reviewing":
741
- case "closing-out":
742
- case "closing_out":
743
- return "active-cyan";
744
- case "failed":
745
- case "error":
746
- case "rejected":
747
- return "failure";
748
- case "stopped":
749
- case "cancelled":
750
- case "canceled":
751
- case "stale":
752
- return "muted";
753
- default:
754
- return "neutral";
755
- }
756
- }
757
- function runStatusColorRole(run) {
758
- const classification = classifyRun(run);
759
- return classification.isNeedsAttention && !classification.isTerminal ? "action-yellow" : statusColorRole(classification.status);
760
- }
761
- function isSteerableStatus(status) {
762
- switch (status) {
763
- case "needs-attention":
764
- case "waiting-approval":
765
- case "waiting-user-input":
766
- case "paused":
767
- return false;
768
- default: {
769
- const runStatus = asRunStatus(status);
770
- return runStatus ? isActiveRunStatus(runStatus) : false;
771
- }
772
- }
773
- }
774
- function isNeedsAttention(run) {
775
- return canonicalStatusToken(run.status) === "needs-attention" || run.pendingApprovals + run.pendingInputs > 0 || run.stallCount > 0;
776
- }
777
- function phaseForStatus(status, runStatus, needsAttention) {
778
- if (runStatus && isTerminalRunStatus2(runStatus))
779
- return runStatus === "completed" || runStatus === "failed" || runStatus === "stopped" ? runStatus : "stopped";
780
- if (needsAttention)
781
- return "needs-attention";
782
- switch (status) {
783
- case "created":
784
- case "queued":
785
- case "preparing":
786
- case "starting":
787
- case "booting":
788
- return "starting";
789
- case "waiting-approval":
790
- case "waiting-user-input":
791
- return "waiting";
792
- case "paused":
793
- return "paused";
794
- case "running":
795
- case "validating":
796
- case "reviewing":
797
- case "closing-out":
798
- return "active";
799
- default:
800
- return runStatus && isActiveRunStatus(runStatus) ? "active" : "unknown";
801
- }
802
- }
803
- function classifyRun(run) {
804
- const status = canonicalStatusToken(run.status) || (run.live && !run.stale ? "starting" : "unknown");
805
- const runStatus = asRunStatus(status);
806
- const isTerminal = runStatus ? isTerminalRunStatus2(runStatus) : false;
807
- const isNeedsAttentionValue = isNeedsAttention(run);
808
- const phase = phaseForStatus(status, runStatus, isNeedsAttentionValue);
809
- return {
810
- status,
811
- phase,
812
- isActive: runStatus ? isActiveRunStatus(runStatus) : !isTerminal && status !== "unknown",
813
- isTerminal,
814
- isNeedsAttention: isNeedsAttentionValue
815
- };
816
- }
817
- function runStatusText(run) {
818
- return classifyRun(run).status;
819
- }
820
- function statusRank(run) {
821
- const classification = classifyRun(run);
822
- if (classification.isNeedsAttention)
823
- return 0;
824
- switch (classification.phase) {
825
- case "needs-attention":
826
- return 0;
827
- case "active":
828
- case "waiting":
829
- case "paused":
830
- return 1;
831
- case "starting":
832
- return 2;
833
- case "completed":
834
- return 3;
835
- case "failed":
836
- return 4;
837
- case "stopped":
838
- return 5;
839
- case "unknown":
840
- return 6;
841
- }
842
- }
843
- function canSteer(run) {
844
- const classification = classifyRun(run);
845
- if (classification.phase === "active" || classification.phase === "starting")
846
- return true;
847
- return classification.isNeedsAttention && isSteerableStatus(classification.status);
848
- }
849
- function canStop(run) {
850
- const classification = classifyRun(run);
851
- return classification.isActive && !classification.isTerminal;
852
- }
853
- function canPause(run) {
854
- const phase = classifyRun(run).phase;
855
- return phase === "active" || phase === "starting";
856
- }
857
- function canResume(run) {
858
- return classifyRun(run).phase === "paused";
859
- }
860
- // packages/run-worker/src/runs/stats.ts
1185
+
1186
+ // packages/run-worker/src/read-model-backend/index.ts
1187
+ init_run_status();
1188
+
1189
+ // packages/run-worker/src/read-model-backend/stats.ts
1190
+ init_run_status();
1191
+ init_run_status();
861
1192
  function parseTimestamp(value) {
862
1193
  if (!value)
863
1194
  return null;
@@ -937,15 +1268,12 @@ async function computeStats(projectRootOrRuns, options = {}) {
937
1268
  approvalsPending
938
1269
  };
939
1270
  }
940
- var computeRigStats = computeStats;
941
1271
 
942
- // packages/run-worker/src/runs/index.ts
1272
+ // packages/run-worker/src/read-model-backend/index.ts
943
1273
  init_projection();
944
- init_diagnostics();
945
1274
  export {
946
1275
  summarizeRunFailures,
947
1276
  summarizeRunError,
948
- summarizeProjectionFailures,
949
1277
  stringifyLogPayload,
950
1278
  statusRank,
951
1279
  statusColorRole,
@@ -963,6 +1291,7 @@ export {
963
1291
  readInboxCounts,
964
1292
  rate,
965
1293
  parseTimestamp,
1294
+ normalizeRunStatusToken,
966
1295
  median,
967
1296
  logLinesFromProjection,
968
1297
  logLineFromValue,
@@ -970,21 +1299,21 @@ export {
970
1299
  listRuns,
971
1300
  listInboxRecords,
972
1301
  listInbox,
1302
+ isOperatorActiveRunStatus,
973
1303
  isNeedsAttention,
974
1304
  getRun,
975
1305
  extractRunLogs,
976
1306
  deliverRunControl,
977
1307
  deliverRemoteRunControl,
978
1308
  computeStats,
979
- computeRigStats,
980
1309
  completedDuration,
981
1310
  collabControlFrames,
982
- collabControlFrame,
983
1311
  classifyRun,
984
1312
  canStop,
985
1313
  canSteer,
986
1314
  canResume,
987
1315
  canPause,
988
1316
  assertNoActiveRunForTask,
1317
+ asRunStatus,
989
1318
  activeRunByTaskId
990
1319
  };