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

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
@@ -15,7 +15,335 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
- // packages/run-worker/src/runs/diagnostics.ts
18
+ // packages/run-worker/src/read-model-backend/run-status.ts
19
+ import { OPERATOR_INACTIVE_RUN_STATUSES } from "@rig/contracts";
20
+ function normalizeRunStatusToken(status) {
21
+ return String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
22
+ }
23
+ function canonicalStatusToken(status) {
24
+ const normalized = normalizeRunStatusToken(status);
25
+ if (normalized === "waiting-input")
26
+ return "waiting-user-input";
27
+ return normalized;
28
+ }
29
+ function asRunStatus(status) {
30
+ return KNOWN_RUN_STATUS[status] ? status : null;
31
+ }
32
+ function isNeedsAttention(run) {
33
+ return canonicalStatusToken(run.status) === "needs-attention" || run.pendingApprovals + run.pendingInputs > 0 || run.stallCount > 0;
34
+ }
35
+ var TERMINAL_RUN_STATUSES, ACTIVE_RUN_STATUSES, KNOWN_RUN_STATUS;
36
+ var init_run_status = __esm(() => {
37
+ TERMINAL_RUN_STATUSES = ["completed", "failed", "stopped"];
38
+ ACTIVE_RUN_STATUSES = [
39
+ "created",
40
+ "queued",
41
+ "preparing",
42
+ "running",
43
+ "waiting-approval",
44
+ "waiting-user-input",
45
+ "paused",
46
+ "validating",
47
+ "reviewing",
48
+ "closing-out",
49
+ "needs-attention"
50
+ ];
51
+ KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES].map((status) => [status, true]));
52
+ });
53
+
54
+ // packages/run-worker/src/session-journal.ts
55
+ import { Schema } from "effect";
56
+ import {
57
+ CUSTOM_TYPE_FOR,
58
+ RIG_CONTROL_SENTINEL_END,
59
+ RIG_INBOX_RESOLUTION_SENTINEL,
60
+ RIG_PAUSE_SENTINEL,
61
+ RIG_RESUME_SENTINEL,
62
+ RIG_STOP_SENTINEL,
63
+ RIG_STOP_SENTINEL_END,
64
+ RIG_WORKFLOW_STATUS_CHANGED,
65
+ RunJournalEvent,
66
+ TYPE_FOR_CUSTOM
67
+ } from "@rig/contracts";
68
+ function isTerminalRunStatus(status) {
69
+ return TERMINAL_RUN_STATUSES2.includes(status);
70
+ }
71
+ function canTransitionRunStatus(from, to) {
72
+ if (from === null)
73
+ return true;
74
+ if (from === to)
75
+ return true;
76
+ return RUN_STATUS_TRANSITIONS[from].includes(to);
77
+ }
78
+ function assertRunStatusTransition(from, to) {
79
+ if (!canTransitionRunStatus(from, to)) {
80
+ throw new Error(`Illegal run status transition: ${from ?? "(none)"} -> ${to}. ` + `Allowed from ${from ?? "(none)"}: ${from ? RUN_STATUS_TRANSITIONS[from].join(", ") : "(any)"}.`);
81
+ }
82
+ }
83
+ function reduceRunJournal(events, runId = null) {
84
+ let record = {};
85
+ let status = null;
86
+ const statusHistory = [];
87
+ const pendingApprovals = new Map;
88
+ const resolvedApprovals = [];
89
+ const pendingUserInputs = new Map;
90
+ const resolvedUserInputs = [];
91
+ const closeoutPhases = [];
92
+ let resolvedPipeline = null;
93
+ const stageOutcomes = [];
94
+ const anomalies = [];
95
+ let steeringCount = 0;
96
+ let stallCount = 0;
97
+ let lastSeq = 0;
98
+ let lastEventAt = null;
99
+ const projectedRunId = runId ?? events[0]?.runId ?? null;
100
+ for (const event of events) {
101
+ lastSeq = event.seq;
102
+ lastEventAt = event.at;
103
+ switch (event.type) {
104
+ case "status-changed": {
105
+ if (!canTransitionRunStatus(status, event.to)) {
106
+ anomalies.push({ seq: event.seq, kind: "illegal-transition", detail: `${status ?? "(none)"} -> ${event.to}` });
107
+ }
108
+ statusHistory.push({ seq: event.seq, at: event.at, from: status, to: event.to, reason: event.reason ?? null });
109
+ const wasTerminal = status !== null && isTerminalRunStatus(status);
110
+ status = event.to;
111
+ record = { ...record, updatedAt: event.at };
112
+ if (isTerminalRunStatus(event.to) && !record.completedAt)
113
+ record = { ...record, completedAt: event.at };
114
+ if (!isTerminalRunStatus(event.to) && wasTerminal)
115
+ record = { ...record, completedAt: null };
116
+ if (event.to === "running" && !record.startedAt)
117
+ record = { ...record, startedAt: event.at };
118
+ break;
119
+ }
120
+ case "record-patch": {
121
+ const next = { ...record };
122
+ for (const [key, value] of Object.entries(event.patch)) {
123
+ if (value !== undefined)
124
+ next[key] = value;
125
+ }
126
+ next.updatedAt = event.patch.updatedAt ?? event.at;
127
+ record = next;
128
+ break;
129
+ }
130
+ case "approval-requested": {
131
+ pendingApprovals.set(event.requestId, {
132
+ requestId: event.requestId,
133
+ requestKind: event.requestKind,
134
+ actionId: event.actionId ?? null,
135
+ payload: event.payload,
136
+ requestedAt: event.at
137
+ });
138
+ break;
139
+ }
140
+ case "approval-resolved": {
141
+ const pending = pendingApprovals.get(event.requestId);
142
+ if (!pending) {
143
+ const alreadyResolved = resolvedApprovals.some((entry) => entry.requestId === event.requestId);
144
+ anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `approval ${event.requestId}` });
145
+ break;
146
+ }
147
+ pendingApprovals.delete(event.requestId);
148
+ resolvedApprovals.push({ ...pending, decision: event.decision, note: event.note ?? null, actor: event.actor, resolvedAt: event.at });
149
+ break;
150
+ }
151
+ case "input-requested": {
152
+ pendingUserInputs.set(event.requestId, {
153
+ requestId: event.requestId,
154
+ requestKind: "user-input",
155
+ actionId: null,
156
+ payload: event.payload,
157
+ requestedAt: event.at
158
+ });
159
+ break;
160
+ }
161
+ case "input-resolved": {
162
+ const pending = pendingUserInputs.get(event.requestId);
163
+ if (!pending) {
164
+ const alreadyResolved = resolvedUserInputs.some((entry) => entry.requestId === event.requestId);
165
+ anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `user-input ${event.requestId}` });
166
+ break;
167
+ }
168
+ pendingUserInputs.delete(event.requestId);
169
+ resolvedUserInputs.push({ ...pending, answers: event.answers, actor: event.actor, resolvedAt: event.at });
170
+ break;
171
+ }
172
+ case "steering":
173
+ steeringCount += 1;
174
+ break;
175
+ case "adopted":
176
+ record = { ...record, pid: event.pid, updatedAt: event.at };
177
+ break;
178
+ case "stall-detected":
179
+ stallCount += 1;
180
+ break;
181
+ case "closeout-phase":
182
+ closeoutPhases.push({ seq: event.seq, at: event.at, phase: event.phase, outcome: event.outcome, detail: event.detail ?? null });
183
+ break;
184
+ case "pipeline-resolved":
185
+ resolvedPipeline = event.pipeline;
186
+ break;
187
+ case "stage-outcome":
188
+ stageOutcomes.push({ seq: event.seq, at: event.at, outcome: event.outcome });
189
+ break;
190
+ case "timeline-entry":
191
+ case "log-entry":
192
+ break;
193
+ }
194
+ }
195
+ return {
196
+ runId: projectedRunId,
197
+ record,
198
+ status,
199
+ statusHistory,
200
+ pendingApprovals: [...pendingApprovals.values()],
201
+ resolvedApprovals,
202
+ pendingUserInputs: [...pendingUserInputs.values()],
203
+ resolvedUserInputs,
204
+ steeringCount,
205
+ stallCount,
206
+ closeoutPhases,
207
+ resolvedPipeline,
208
+ stageOutcomes,
209
+ lastSeq,
210
+ lastEventAt,
211
+ anomalies
212
+ };
213
+ }
214
+ function isRunSessionCustomType(customType) {
215
+ return customType !== undefined && Object.hasOwn(TYPE_FOR_CUSTOM, customType);
216
+ }
217
+ function foldRunSessionEntries(entries, runId) {
218
+ const events = [];
219
+ entries.forEach((entry, index) => {
220
+ if (entry.type !== "custom" || !isRunSessionCustomType(entry.customType))
221
+ return;
222
+ const data = entry.data !== null && typeof entry.data === "object" ? entry.data : {};
223
+ const stamped = {
224
+ v: 1,
225
+ seq: index + 1,
226
+ at: typeof data.at === "string" ? data.at : new Date(0).toISOString(),
227
+ runId,
228
+ ...data,
229
+ type: TYPE_FOR_CUSTOM[entry.customType]
230
+ };
231
+ try {
232
+ events.push(decodeRunJournalEvent(stamped));
233
+ } catch {}
234
+ });
235
+ return reduceRunJournal(events, runId);
236
+ }
237
+ function isRecord(value) {
238
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
239
+ }
240
+ function timelineEntryFromCustomEntry(entry) {
241
+ if (entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
242
+ return null;
243
+ const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
244
+ const type = typeof payload.type === "string" ? payload.type : "timeline";
245
+ const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
246
+ const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
247
+ const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
248
+ const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
249
+ return { at, type, stage, status, detail };
250
+ }
251
+ function timelineEntriesFromCustomEntries(entries) {
252
+ return entries.flatMap((entry) => {
253
+ const timelineEntry = timelineEntryFromCustomEntry(entry);
254
+ return timelineEntry ? [timelineEntry] : [];
255
+ });
256
+ }
257
+ function asCustomEntries(entries) {
258
+ return entries.filter((entry) => entry.type === "custom");
259
+ }
260
+
261
+ class RunSessionJournal {
262
+ sm;
263
+ runId;
264
+ constructor(sm, runId) {
265
+ this.sm = sm;
266
+ this.runId = runId;
267
+ }
268
+ #append(init) {
269
+ this.sm.appendCustomEntry(CUSTOM_TYPE_FOR[init.type], { ...init, at: new Date().toISOString() });
270
+ }
271
+ appendStatus(to, opts = {}) {
272
+ const from = foldRunSessionEntries(asCustomEntries(this.sm.getEntries()), this.runId).status;
273
+ if (!opts.force)
274
+ assertRunStatusTransition(from, to);
275
+ if (opts.errorText !== undefined)
276
+ this.#append({ type: "record-patch", patch: { errorText: opts.errorText } });
277
+ this.#append({
278
+ type: "status-changed",
279
+ from,
280
+ to,
281
+ ...opts.reason !== undefined ? { reason: opts.reason } : {},
282
+ ...opts.actor !== undefined ? { actor: opts.actor } : {}
283
+ });
284
+ }
285
+ appendTimeline(payload) {
286
+ this.#append({ type: "timeline-entry", payload });
287
+ }
288
+ appendCloseoutPhase(input) {
289
+ this.#append({
290
+ type: "closeout-phase",
291
+ phase: input.phase,
292
+ outcome: input.outcome,
293
+ ...input.detail !== undefined ? { detail: input.detail } : {}
294
+ });
295
+ }
296
+ appendApprovalResolved(input) {
297
+ this.#append({
298
+ type: "approval-resolved",
299
+ requestId: input.requestId,
300
+ decision: input.decision,
301
+ actor: input.actor,
302
+ ...input.note !== undefined ? { note: input.note } : {}
303
+ });
304
+ }
305
+ appendInputResolved(input) {
306
+ this.#append({ type: "input-resolved", requestId: input.requestId, answers: input.answers, actor: input.actor });
307
+ }
308
+ appendStall(input) {
309
+ this.#append({ type: "stall-detected", detail: input.detail });
310
+ }
311
+ }
312
+ var decodeRunJournalEvent, RUN_STATUS_TRANSITIONS, TERMINAL_RUN_STATUSES2;
313
+ var init_session_journal = __esm(() => {
314
+ decodeRunJournalEvent = Schema.decodeUnknownSync(RunJournalEvent);
315
+ RUN_STATUS_TRANSITIONS = {
316
+ created: ["queued", "preparing", "running", "failed", "stopped"],
317
+ queued: ["preparing", "running", "failed", "stopped"],
318
+ preparing: ["queued", "running", "needs-attention", "failed", "stopped"],
319
+ running: [
320
+ "queued",
321
+ "waiting-approval",
322
+ "waiting-user-input",
323
+ "paused",
324
+ "validating",
325
+ "reviewing",
326
+ "closing-out",
327
+ "needs-attention",
328
+ "completed",
329
+ "failed",
330
+ "stopped"
331
+ ],
332
+ "waiting-approval": ["running", "needs-attention", "failed", "stopped"],
333
+ "waiting-user-input": ["running", "needs-attention", "failed", "stopped"],
334
+ paused: ["running", "failed", "stopped"],
335
+ validating: ["running", "reviewing", "closing-out", "needs-attention", "completed", "failed", "stopped"],
336
+ reviewing: ["running", "validating", "closing-out", "needs-attention", "completed", "failed", "stopped"],
337
+ "closing-out": ["running", "needs-attention", "completed", "failed", "stopped"],
338
+ "needs-attention": ["queued", "preparing", "running", "closing-out", "completed", "failed", "stopped"],
339
+ completed: [],
340
+ failed: ["queued", "preparing", "running", "closing-out"],
341
+ stopped: ["queued", "preparing", "running", "closing-out"]
342
+ };
343
+ TERMINAL_RUN_STATUSES2 = ["completed", "failed", "stopped"];
344
+ });
345
+
346
+ // packages/run-worker/src/read-model-backend/diagnostics.ts
19
347
  function normalizeString(value) {
20
348
  if (typeof value !== "string")
21
349
  return null;
@@ -62,13 +390,12 @@ function summarizeRunError(projection) {
62
390
  const nonGeneric = candidates.filter((candidate) => !isGenericRunFailure(candidate));
63
391
  return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
64
392
  }
65
- var init_diagnostics = () => {};
66
393
 
67
- // packages/run-worker/src/runs/projection.ts
394
+ // packages/run-worker/src/read-model-backend/projection.ts
68
395
  var exports_projection = {};
69
396
  __export(exports_projection, {
70
397
  selectRunProjection: () => selectRunProjection,
71
- runRecordsFromRegistrySnapshot: () => runRecordsFromRegistrySnapshot,
398
+ runRecordsFromCollab: () => runRecordsFromCollab,
72
399
  runRecordFromRegistryEntry: () => runRecordFromRegistryEntry,
73
400
  resolveRunJoinTarget: () => resolveRunJoinTarget,
74
401
  resolveJoinTarget: () => resolveJoinTarget,
@@ -81,13 +408,28 @@ __export(exports_projection, {
81
408
  });
82
409
  import { existsSync, readFileSync } from "fs";
83
410
  import { isAbsolute, relative, resolve } from "path";
84
- import { Duration, Effect, Option, Stream } from "effect";
85
- import {
86
- foldRunSessionEntries,
87
- isTerminalRunStatus as isTerminalRunStatus2,
88
- timelineEntriesFromCustomEntries
89
- } from "@rig/contracts";
90
- import { registrySnapshotStream } from "@rig/runtime/control-plane/discovery";
411
+ import { RUN_DISCOVERY } from "@rig/contracts";
412
+ import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
413
+ import { defineCapability } from "@rig/core/capability";
414
+ function registryEntryFromCollab(collab) {
415
+ const record = collab;
416
+ return {
417
+ roomId: collab.sessionId,
418
+ title: collab.title,
419
+ status: record.registryStatus ?? (collab.stale ? "stale" : "running"),
420
+ startedAt: collab.startedAt ?? null,
421
+ heartbeatAt: collab.updatedAt ?? null,
422
+ sessionPath: collab.sessionPath ?? null,
423
+ cwd: collab.cwd ?? null,
424
+ joinLink: collab.joinLink ?? null,
425
+ webLink: collab.webLink ?? null,
426
+ relayUrl: collab.relayUrl ?? null,
427
+ stale: collab.stale,
428
+ repo: collab.selectedRepo ?? null,
429
+ ...collab.pid === undefined ? {} : { pid: collab.pid },
430
+ projection: record.registryProjection ?? null
431
+ };
432
+ }
91
433
  function readSessionRunEntries(sessionPath) {
92
434
  if (!sessionPath || !existsSync(sessionPath))
93
435
  return [];
@@ -132,22 +474,7 @@ function registryStatusAsRunStatus(status) {
132
474
  return "waiting-user-input";
133
475
  if (status === "starting")
134
476
  return "preparing";
135
- return typeof status === "string" && [
136
- "created",
137
- "queued",
138
- "preparing",
139
- "running",
140
- "waiting-approval",
141
- "waiting-user-input",
142
- "paused",
143
- "validating",
144
- "reviewing",
145
- "closing-out",
146
- "needs-attention",
147
- "completed",
148
- "failed",
149
- "stopped"
150
- ].includes(status) ? status : null;
477
+ return typeof status === "string" ? asRunStatus(status) : null;
151
478
  }
152
479
  function payloadString(payload, keys) {
153
480
  if (!payload || typeof payload !== "object")
@@ -170,12 +497,14 @@ function payloadOptions(payload) {
170
497
  }
171
498
  function inboxRequest(request, kind) {
172
499
  const fallback = kind === "approval" ? "Approval requested" : "Input requested";
500
+ const body = payloadString(request.payload, ["body", "description", "detail", "details"]);
501
+ const options = payloadOptions(request.payload);
173
502
  return {
174
503
  requestId: request.requestId,
175
504
  kind,
176
505
  title: payloadString(request.payload, ["title", "message", "reason", "prompt", "summary"]) ?? fallback,
177
- body: payloadString(request.payload, ["body", "description", "detail", "details"]),
178
- ...payloadOptions(request.payload) ? { options: payloadOptions(request.payload) } : {},
506
+ ...body !== undefined ? { body } : {},
507
+ ...options ? { options } : {},
179
508
  requestedAt: request.requestedAt ?? null,
180
509
  source: "run"
181
510
  };
@@ -253,7 +582,7 @@ function runRecordFromRegistryEntry(projectRoot, entry) {
253
582
  const runId = stringOrNull(projection.runId) ?? entry.roomId;
254
583
  const folded = emptyFoldedProjection(runId, projection);
255
584
  const pushedStatus = registryStatusAsRunStatus(projection.status) ?? folded.status ?? registryStatusAsRunStatus(entry.status);
256
- const status = entry.stale ? pushedStatus && isTerminalRunStatus2(pushedStatus) ? pushedStatus : "stale" : pushedStatus ?? "running";
585
+ const status = entry.stale ? pushedStatus && isTerminalRunStatus(pushedStatus) ? pushedStatus : "stale" : pushedStatus ?? "running";
257
586
  const sessionPath = stringOrNull(projection.sessionPath) ?? stringOrNull(entry.sessionPath) ?? null;
258
587
  const collabCwd = stringOrNull(projection.collabCwd) ?? stringOrNull(projection.cwd) ?? stringOrNull(entry.cwd) ?? stringOrNull(projection.worktreePath);
259
588
  return {
@@ -284,8 +613,8 @@ function runRecordFromRegistryEntry(projectRoot, entry) {
284
613
  projection: folded
285
614
  };
286
615
  }
287
- function runRecordsFromRegistrySnapshot(projectRoot, snapshot) {
288
- return sortByRecency(snapshot.entries.map((entry) => runRecordFromRegistryEntry(projectRoot, entry)).filter((record) => record !== null));
616
+ function runRecordsFromCollab(projectRoot, collabs) {
617
+ return sortByRecency(collabs.map((collab) => runRecordFromRegistryEntry(projectRoot, registryEntryFromCollab(collab))).filter((record) => record !== null));
289
618
  }
290
619
  function sortByRecency(records) {
291
620
  return [...records].sort((a, b) => {
@@ -333,8 +662,11 @@ function discoveryDiagnosticRunRecord(projectRoot, error) {
333
662
  }
334
663
  async function listRunProjections(projectRoot, filter = {}) {
335
664
  try {
336
- const snapshot = await Effect.runPromise(registrySnapshotStream(projectRoot, filter).pipe(Stream.runHead, Effect.timeout(Duration.seconds(15)), Effect.map(Option.getOrNull)));
337
- return snapshot ? runRecordsFromRegistrySnapshot(projectRoot, snapshot) : [discoveryDiagnosticRunRecord(projectRoot, "registry discovery stream ended without a snapshot")];
665
+ const discovery = await loadCapabilityForRoot(projectRoot, RunDiscoveryCap);
666
+ if (!discovery)
667
+ return [discoveryDiagnosticRunRecord(projectRoot, "run discovery capability unavailable")];
668
+ const collabs = await discovery.listActiveRunCollab(projectRoot, filter);
669
+ return runRecordsFromCollab(projectRoot, collabs);
338
670
  } catch (error) {
339
671
  return [discoveryDiagnosticRunRecord(projectRoot, error)];
340
672
  }
@@ -365,35 +697,20 @@ async function resolveRunJoinTarget(projectRoot, runId, filter = {}) {
365
697
  return null;
366
698
  return { runId: run.runId, taskId: run.taskId, joinLink: run.joinLink, cwd: run.collabCwd ?? run.sessionPath, stale: run.stale };
367
699
  }
368
- var EMPTY_PROJECTION, DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__", listRuns, getRun, resolveJoinTarget;
700
+ var RunDiscoveryCap, EMPTY_PROJECTION, DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__", listRuns, getRun, resolveJoinTarget;
369
701
  var init_projection = __esm(() => {
370
- init_diagnostics();
702
+ init_session_journal();
703
+ init_run_status();
704
+ RunDiscoveryCap = defineCapability(RUN_DISCOVERY);
371
705
  EMPTY_PROJECTION = foldRunSessionEntries([], "");
372
706
  listRuns = listRunProjections;
373
707
  getRun = getRunProjection;
374
708
  resolveJoinTarget = resolveRunJoinTarget;
375
709
  });
376
710
 
377
- // packages/run-worker/src/runs/run-status.ts
378
- import {
379
- ACTIVE_RUN_STATUSES,
380
- TERMINAL_RUN_STATUSES,
381
- isActiveRunStatus,
382
- isTerminalRunStatus,
383
- normalizeRunStatusToken
384
- } from "@rig/contracts";
385
- var KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES].map((status) => [status, true]));
386
- function canonicalStatusToken(status) {
387
- const normalized = normalizeRunStatusToken(status);
388
- if (normalized === "waiting-input")
389
- return "waiting-user-input";
390
- return normalized;
391
- }
392
- function isNeedsAttention(run) {
393
- return canonicalStatusToken(run.status) === "needs-attention" || run.pendingApprovals + run.pendingInputs > 0 || run.stallCount > 0;
394
- }
395
-
396
- // packages/run-worker/src/runs/stats.ts
711
+ // packages/run-worker/src/read-model-backend/stats.ts
712
+ init_run_status();
713
+ init_run_status();
397
714
  function parseTimestamp(value) {
398
715
  if (!value)
399
716
  return null;
@@ -473,13 +790,11 @@ async function computeStats(projectRootOrRuns, options = {}) {
473
790
  approvalsPending
474
791
  };
475
792
  }
476
- var computeRigStats = computeStats;
477
793
  export {
478
794
  rate,
479
795
  parseTimestamp,
480
796
  median,
481
797
  isNeedsAttention,
482
798
  computeStats,
483
- computeRigStats,
484
799
  completedDuration
485
800
  };
@@ -0,0 +1,2 @@
1
+ import type { RunReadModelService } from "@rig/contracts";
2
+ export declare const runReadModelService: RunReadModelService;