@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
@@ -8,7 +8,6 @@ export declare function extractRunLogs(run: RunRecord, deps?: {
8
8
  readonly readSessionRunEntries?: typeof readSessionRunEntries;
9
9
  }): string[];
10
10
  export declare function summarizeRunFailures(run: RunRecord): string[];
11
- export declare const summarizeProjectionFailures: typeof summarizeRunFailures;
12
11
  export declare function runsForTask(projectRoot: string, taskId: string, deps?: {
13
12
  readonly listRuns?: (projectRoot: string) => Promise<readonly RunRecord[]>;
14
13
  readonly getRun?: (projectRoot: string, runId: string) => Promise<RunRecord | null>;
@@ -1,19 +1,327 @@
1
1
  // @bun
2
- // packages/run-worker/src/runs/inspect.ts
2
+ // packages/run-worker/src/read-model-backend/inspect.ts
3
3
  import { RIG_RUN_LOG_ENTRY } from "@rig/contracts";
4
4
 
5
- // packages/run-worker/src/runs/projection.ts
5
+ // packages/run-worker/src/read-model-backend/projection.ts
6
6
  import { existsSync, readFileSync } from "fs";
7
7
  import { isAbsolute, relative, resolve } from "path";
8
- import { Duration, Effect, Option, Stream } from "effect";
8
+ import { RUN_DISCOVERY } from "@rig/contracts";
9
+
10
+ // packages/run-worker/src/session-journal.ts
11
+ import { Schema } from "effect";
9
12
  import {
10
- foldRunSessionEntries,
11
- isTerminalRunStatus,
12
- timelineEntriesFromCustomEntries
13
+ CUSTOM_TYPE_FOR,
14
+ RIG_CONTROL_SENTINEL_END,
15
+ RIG_INBOX_RESOLUTION_SENTINEL,
16
+ RIG_PAUSE_SENTINEL,
17
+ RIG_RESUME_SENTINEL,
18
+ RIG_STOP_SENTINEL,
19
+ RIG_STOP_SENTINEL_END,
20
+ RIG_WORKFLOW_STATUS_CHANGED,
21
+ RunJournalEvent,
22
+ TYPE_FOR_CUSTOM
13
23
  } from "@rig/contracts";
14
- import { registrySnapshotStream } from "@rig/runtime/control-plane/discovery";
24
+ var decodeRunJournalEvent = Schema.decodeUnknownSync(RunJournalEvent);
25
+ var RUN_STATUS_TRANSITIONS = {
26
+ created: ["queued", "preparing", "running", "failed", "stopped"],
27
+ queued: ["preparing", "running", "failed", "stopped"],
28
+ preparing: ["queued", "running", "needs-attention", "failed", "stopped"],
29
+ running: [
30
+ "queued",
31
+ "waiting-approval",
32
+ "waiting-user-input",
33
+ "paused",
34
+ "validating",
35
+ "reviewing",
36
+ "closing-out",
37
+ "needs-attention",
38
+ "completed",
39
+ "failed",
40
+ "stopped"
41
+ ],
42
+ "waiting-approval": ["running", "needs-attention", "failed", "stopped"],
43
+ "waiting-user-input": ["running", "needs-attention", "failed", "stopped"],
44
+ paused: ["running", "failed", "stopped"],
45
+ validating: ["running", "reviewing", "closing-out", "needs-attention", "completed", "failed", "stopped"],
46
+ reviewing: ["running", "validating", "closing-out", "needs-attention", "completed", "failed", "stopped"],
47
+ "closing-out": ["running", "needs-attention", "completed", "failed", "stopped"],
48
+ "needs-attention": ["queued", "preparing", "running", "closing-out", "completed", "failed", "stopped"],
49
+ completed: [],
50
+ failed: ["queued", "preparing", "running", "closing-out"],
51
+ stopped: ["queued", "preparing", "running", "closing-out"]
52
+ };
53
+ var TERMINAL_RUN_STATUSES = ["completed", "failed", "stopped"];
54
+ function isTerminalRunStatus(status) {
55
+ return TERMINAL_RUN_STATUSES.includes(status);
56
+ }
57
+ function canTransitionRunStatus(from, to) {
58
+ if (from === null)
59
+ return true;
60
+ if (from === to)
61
+ return true;
62
+ return RUN_STATUS_TRANSITIONS[from].includes(to);
63
+ }
64
+ function assertRunStatusTransition(from, to) {
65
+ if (!canTransitionRunStatus(from, to)) {
66
+ throw new Error(`Illegal run status transition: ${from ?? "(none)"} -> ${to}. ` + `Allowed from ${from ?? "(none)"}: ${from ? RUN_STATUS_TRANSITIONS[from].join(", ") : "(any)"}.`);
67
+ }
68
+ }
69
+ function reduceRunJournal(events, runId = null) {
70
+ let record = {};
71
+ let status = null;
72
+ const statusHistory = [];
73
+ const pendingApprovals = new Map;
74
+ const resolvedApprovals = [];
75
+ const pendingUserInputs = new Map;
76
+ const resolvedUserInputs = [];
77
+ const closeoutPhases = [];
78
+ let resolvedPipeline = null;
79
+ const stageOutcomes = [];
80
+ const anomalies = [];
81
+ let steeringCount = 0;
82
+ let stallCount = 0;
83
+ let lastSeq = 0;
84
+ let lastEventAt = null;
85
+ const projectedRunId = runId ?? events[0]?.runId ?? null;
86
+ for (const event of events) {
87
+ lastSeq = event.seq;
88
+ lastEventAt = event.at;
89
+ switch (event.type) {
90
+ case "status-changed": {
91
+ if (!canTransitionRunStatus(status, event.to)) {
92
+ anomalies.push({ seq: event.seq, kind: "illegal-transition", detail: `${status ?? "(none)"} -> ${event.to}` });
93
+ }
94
+ statusHistory.push({ seq: event.seq, at: event.at, from: status, to: event.to, reason: event.reason ?? null });
95
+ const wasTerminal = status !== null && isTerminalRunStatus(status);
96
+ status = event.to;
97
+ record = { ...record, updatedAt: event.at };
98
+ if (isTerminalRunStatus(event.to) && !record.completedAt)
99
+ record = { ...record, completedAt: event.at };
100
+ if (!isTerminalRunStatus(event.to) && wasTerminal)
101
+ record = { ...record, completedAt: null };
102
+ if (event.to === "running" && !record.startedAt)
103
+ record = { ...record, startedAt: event.at };
104
+ break;
105
+ }
106
+ case "record-patch": {
107
+ const next = { ...record };
108
+ for (const [key, value] of Object.entries(event.patch)) {
109
+ if (value !== undefined)
110
+ next[key] = value;
111
+ }
112
+ next.updatedAt = event.patch.updatedAt ?? event.at;
113
+ record = next;
114
+ break;
115
+ }
116
+ case "approval-requested": {
117
+ pendingApprovals.set(event.requestId, {
118
+ requestId: event.requestId,
119
+ requestKind: event.requestKind,
120
+ actionId: event.actionId ?? null,
121
+ payload: event.payload,
122
+ requestedAt: event.at
123
+ });
124
+ break;
125
+ }
126
+ case "approval-resolved": {
127
+ const pending = pendingApprovals.get(event.requestId);
128
+ if (!pending) {
129
+ const alreadyResolved = resolvedApprovals.some((entry) => entry.requestId === event.requestId);
130
+ anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `approval ${event.requestId}` });
131
+ break;
132
+ }
133
+ pendingApprovals.delete(event.requestId);
134
+ resolvedApprovals.push({ ...pending, decision: event.decision, note: event.note ?? null, actor: event.actor, resolvedAt: event.at });
135
+ break;
136
+ }
137
+ case "input-requested": {
138
+ pendingUserInputs.set(event.requestId, {
139
+ requestId: event.requestId,
140
+ requestKind: "user-input",
141
+ actionId: null,
142
+ payload: event.payload,
143
+ requestedAt: event.at
144
+ });
145
+ break;
146
+ }
147
+ case "input-resolved": {
148
+ const pending = pendingUserInputs.get(event.requestId);
149
+ if (!pending) {
150
+ const alreadyResolved = resolvedUserInputs.some((entry) => entry.requestId === event.requestId);
151
+ anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `user-input ${event.requestId}` });
152
+ break;
153
+ }
154
+ pendingUserInputs.delete(event.requestId);
155
+ resolvedUserInputs.push({ ...pending, answers: event.answers, actor: event.actor, resolvedAt: event.at });
156
+ break;
157
+ }
158
+ case "steering":
159
+ steeringCount += 1;
160
+ break;
161
+ case "adopted":
162
+ record = { ...record, pid: event.pid, updatedAt: event.at };
163
+ break;
164
+ case "stall-detected":
165
+ stallCount += 1;
166
+ break;
167
+ case "closeout-phase":
168
+ closeoutPhases.push({ seq: event.seq, at: event.at, phase: event.phase, outcome: event.outcome, detail: event.detail ?? null });
169
+ break;
170
+ case "pipeline-resolved":
171
+ resolvedPipeline = event.pipeline;
172
+ break;
173
+ case "stage-outcome":
174
+ stageOutcomes.push({ seq: event.seq, at: event.at, outcome: event.outcome });
175
+ break;
176
+ case "timeline-entry":
177
+ case "log-entry":
178
+ break;
179
+ }
180
+ }
181
+ return {
182
+ runId: projectedRunId,
183
+ record,
184
+ status,
185
+ statusHistory,
186
+ pendingApprovals: [...pendingApprovals.values()],
187
+ resolvedApprovals,
188
+ pendingUserInputs: [...pendingUserInputs.values()],
189
+ resolvedUserInputs,
190
+ steeringCount,
191
+ stallCount,
192
+ closeoutPhases,
193
+ resolvedPipeline,
194
+ stageOutcomes,
195
+ lastSeq,
196
+ lastEventAt,
197
+ anomalies
198
+ };
199
+ }
200
+ function isRunSessionCustomType(customType) {
201
+ return customType !== undefined && Object.hasOwn(TYPE_FOR_CUSTOM, customType);
202
+ }
203
+ function foldRunSessionEntries(entries, runId) {
204
+ const events = [];
205
+ entries.forEach((entry, index) => {
206
+ if (entry.type !== "custom" || !isRunSessionCustomType(entry.customType))
207
+ return;
208
+ const data = entry.data !== null && typeof entry.data === "object" ? entry.data : {};
209
+ const stamped = {
210
+ v: 1,
211
+ seq: index + 1,
212
+ at: typeof data.at === "string" ? data.at : new Date(0).toISOString(),
213
+ runId,
214
+ ...data,
215
+ type: TYPE_FOR_CUSTOM[entry.customType]
216
+ };
217
+ try {
218
+ events.push(decodeRunJournalEvent(stamped));
219
+ } catch {}
220
+ });
221
+ return reduceRunJournal(events, runId);
222
+ }
223
+ function isRecord(value) {
224
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
225
+ }
226
+ function timelineEntryFromCustomEntry(entry) {
227
+ if (entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
228
+ return null;
229
+ const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
230
+ const type = typeof payload.type === "string" ? payload.type : "timeline";
231
+ const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
232
+ const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
233
+ const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
234
+ const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
235
+ return { at, type, stage, status, detail };
236
+ }
237
+ function timelineEntriesFromCustomEntries(entries) {
238
+ return entries.flatMap((entry) => {
239
+ const timelineEntry = timelineEntryFromCustomEntry(entry);
240
+ return timelineEntry ? [timelineEntry] : [];
241
+ });
242
+ }
243
+ function asCustomEntries(entries) {
244
+ return entries.filter((entry) => entry.type === "custom");
245
+ }
246
+
247
+ class RunSessionJournal {
248
+ sm;
249
+ runId;
250
+ constructor(sm, runId) {
251
+ this.sm = sm;
252
+ this.runId = runId;
253
+ }
254
+ #append(init) {
255
+ this.sm.appendCustomEntry(CUSTOM_TYPE_FOR[init.type], { ...init, at: new Date().toISOString() });
256
+ }
257
+ appendStatus(to, opts = {}) {
258
+ const from = foldRunSessionEntries(asCustomEntries(this.sm.getEntries()), this.runId).status;
259
+ if (!opts.force)
260
+ assertRunStatusTransition(from, to);
261
+ if (opts.errorText !== undefined)
262
+ this.#append({ type: "record-patch", patch: { errorText: opts.errorText } });
263
+ this.#append({
264
+ type: "status-changed",
265
+ from,
266
+ to,
267
+ ...opts.reason !== undefined ? { reason: opts.reason } : {},
268
+ ...opts.actor !== undefined ? { actor: opts.actor } : {}
269
+ });
270
+ }
271
+ appendTimeline(payload) {
272
+ this.#append({ type: "timeline-entry", payload });
273
+ }
274
+ appendCloseoutPhase(input) {
275
+ this.#append({
276
+ type: "closeout-phase",
277
+ phase: input.phase,
278
+ outcome: input.outcome,
279
+ ...input.detail !== undefined ? { detail: input.detail } : {}
280
+ });
281
+ }
282
+ appendApprovalResolved(input) {
283
+ this.#append({
284
+ type: "approval-resolved",
285
+ requestId: input.requestId,
286
+ decision: input.decision,
287
+ actor: input.actor,
288
+ ...input.note !== undefined ? { note: input.note } : {}
289
+ });
290
+ }
291
+ appendInputResolved(input) {
292
+ this.#append({ type: "input-resolved", requestId: input.requestId, answers: input.answers, actor: input.actor });
293
+ }
294
+ appendStall(input) {
295
+ this.#append({ type: "stall-detected", detail: input.detail });
296
+ }
297
+ }
298
+
299
+ // packages/run-worker/src/read-model-backend/projection.ts
300
+ import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
301
+ import { defineCapability } from "@rig/core/capability";
302
+
303
+ // packages/run-worker/src/read-model-backend/run-status.ts
304
+ import { OPERATOR_INACTIVE_RUN_STATUSES } from "@rig/contracts";
305
+ var TERMINAL_RUN_STATUSES2 = ["completed", "failed", "stopped"];
306
+ var ACTIVE_RUN_STATUSES = [
307
+ "created",
308
+ "queued",
309
+ "preparing",
310
+ "running",
311
+ "waiting-approval",
312
+ "waiting-user-input",
313
+ "paused",
314
+ "validating",
315
+ "reviewing",
316
+ "closing-out",
317
+ "needs-attention"
318
+ ];
319
+ var KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES2].map((status) => [status, true]));
320
+ function asRunStatus(status) {
321
+ return KNOWN_RUN_STATUS[status] ? status : null;
322
+ }
15
323
 
16
- // packages/run-worker/src/runs/diagnostics.ts
324
+ // packages/run-worker/src/read-model-backend/diagnostics.ts
17
325
  function normalizeString(value) {
18
326
  if (typeof value !== "string")
19
327
  return null;
@@ -61,7 +369,27 @@ function summarizeRunError(projection) {
61
369
  return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
62
370
  }
63
371
 
64
- // packages/run-worker/src/runs/projection.ts
372
+ // packages/run-worker/src/read-model-backend/projection.ts
373
+ var RunDiscoveryCap = defineCapability(RUN_DISCOVERY);
374
+ function registryEntryFromCollab(collab) {
375
+ const record = collab;
376
+ return {
377
+ roomId: collab.sessionId,
378
+ title: collab.title,
379
+ status: record.registryStatus ?? (collab.stale ? "stale" : "running"),
380
+ startedAt: collab.startedAt ?? null,
381
+ heartbeatAt: collab.updatedAt ?? null,
382
+ sessionPath: collab.sessionPath ?? null,
383
+ cwd: collab.cwd ?? null,
384
+ joinLink: collab.joinLink ?? null,
385
+ webLink: collab.webLink ?? null,
386
+ relayUrl: collab.relayUrl ?? null,
387
+ stale: collab.stale,
388
+ repo: collab.selectedRepo ?? null,
389
+ ...collab.pid === undefined ? {} : { pid: collab.pid },
390
+ projection: record.registryProjection ?? null
391
+ };
392
+ }
65
393
  var EMPTY_PROJECTION = foldRunSessionEntries([], "");
66
394
  var DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__";
67
395
  function readSessionRunEntries(sessionPath) {
@@ -108,22 +436,7 @@ function registryStatusAsRunStatus(status) {
108
436
  return "waiting-user-input";
109
437
  if (status === "starting")
110
438
  return "preparing";
111
- return typeof status === "string" && [
112
- "created",
113
- "queued",
114
- "preparing",
115
- "running",
116
- "waiting-approval",
117
- "waiting-user-input",
118
- "paused",
119
- "validating",
120
- "reviewing",
121
- "closing-out",
122
- "needs-attention",
123
- "completed",
124
- "failed",
125
- "stopped"
126
- ].includes(status) ? status : null;
439
+ return typeof status === "string" ? asRunStatus(status) : null;
127
440
  }
128
441
  function payloadString(payload, keys) {
129
442
  if (!payload || typeof payload !== "object")
@@ -146,12 +459,14 @@ function payloadOptions(payload) {
146
459
  }
147
460
  function inboxRequest(request, kind) {
148
461
  const fallback = kind === "approval" ? "Approval requested" : "Input requested";
462
+ const body = payloadString(request.payload, ["body", "description", "detail", "details"]);
463
+ const options = payloadOptions(request.payload);
149
464
  return {
150
465
  requestId: request.requestId,
151
466
  kind,
152
467
  title: payloadString(request.payload, ["title", "message", "reason", "prompt", "summary"]) ?? fallback,
153
- body: payloadString(request.payload, ["body", "description", "detail", "details"]),
154
- ...payloadOptions(request.payload) ? { options: payloadOptions(request.payload) } : {},
468
+ ...body !== undefined ? { body } : {},
469
+ ...options ? { options } : {},
155
470
  requestedAt: request.requestedAt ?? null,
156
471
  source: "run"
157
472
  };
@@ -260,8 +575,8 @@ function runRecordFromRegistryEntry(projectRoot, entry) {
260
575
  projection: folded
261
576
  };
262
577
  }
263
- function runRecordsFromRegistrySnapshot(projectRoot, snapshot) {
264
- return sortByRecency(snapshot.entries.map((entry) => runRecordFromRegistryEntry(projectRoot, entry)).filter((record) => record !== null));
578
+ function runRecordsFromCollab(projectRoot, collabs) {
579
+ return sortByRecency(collabs.map((collab) => runRecordFromRegistryEntry(projectRoot, registryEntryFromCollab(collab))).filter((record) => record !== null));
265
580
  }
266
581
  function sortByRecency(records) {
267
582
  return [...records].sort((a, b) => {
@@ -309,8 +624,11 @@ function discoveryDiagnosticRunRecord(projectRoot, error) {
309
624
  }
310
625
  async function listRunProjections(projectRoot, filter = {}) {
311
626
  try {
312
- const snapshot = await Effect.runPromise(registrySnapshotStream(projectRoot, filter).pipe(Stream.runHead, Effect.timeout(Duration.seconds(15)), Effect.map(Option.getOrNull)));
313
- return snapshot ? runRecordsFromRegistrySnapshot(projectRoot, snapshot) : [discoveryDiagnosticRunRecord(projectRoot, "registry discovery stream ended without a snapshot")];
627
+ const discovery = await loadCapabilityForRoot(projectRoot, RunDiscoveryCap);
628
+ if (!discovery)
629
+ return [discoveryDiagnosticRunRecord(projectRoot, "run discovery capability unavailable")];
630
+ const collabs = await discovery.listActiveRunCollab(projectRoot, filter);
631
+ return runRecordsFromCollab(projectRoot, collabs);
314
632
  } catch (error) {
315
633
  return [discoveryDiagnosticRunRecord(projectRoot, error)];
316
634
  }
@@ -338,7 +656,7 @@ async function getRunProjection(projectRoot, runId, filter = {}) {
338
656
  var listRuns = listRunProjections;
339
657
  var getRun = getRunProjection;
340
658
 
341
- // packages/run-worker/src/runs/inspect.ts
659
+ // packages/run-worker/src/read-model-backend/inspect.ts
342
660
  function asRecord(value) {
343
661
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
344
662
  }
@@ -422,7 +740,6 @@ function summarizeRunFailures(run) {
422
740
  }
423
741
  return failures;
424
742
  }
425
- var summarizeProjectionFailures = summarizeRunFailures;
426
743
  async function runsForTask(projectRoot, taskId, deps = {}) {
427
744
  const list = deps.listRuns ?? listRuns;
428
745
  try {
@@ -448,7 +765,6 @@ function runInspectSummaryRows(runs, options = {}) {
448
765
  }
449
766
  export {
450
767
  summarizeRunFailures,
451
- summarizeProjectionFailures,
452
768
  stringifyLogPayload,
453
769
  runsForTask,
454
770
  runInspectSummaryRows,
@@ -1,6 +1,20 @@
1
- import { type RunRecord, type RunSessionCustomEntry } from "@rig/contracts";
2
- import type { CollabRegistryFilter } from "@oh-my-pi/pi-coding-agent/collab/api";
3
- import type { ListedRegistryEntry, RegistrySnapshotFrame } from "@rig/relay-registry";
1
+ import { type RunDiscoveryFilter, type LiveRunCollabProjection, type RunRecord, type RunSessionCustomEntry } from "@rig/contracts";
2
+ type ListedRegistryEntry = {
3
+ readonly roomId: string;
4
+ readonly title: string;
5
+ readonly status: string;
6
+ readonly startedAt?: string | null;
7
+ readonly heartbeatAt?: string | null;
8
+ readonly sessionPath?: string | null;
9
+ readonly cwd?: string | null;
10
+ readonly joinLink?: string | null;
11
+ readonly webLink?: string | null;
12
+ readonly relayUrl?: string | null;
13
+ readonly stale: boolean;
14
+ readonly repo?: string | null;
15
+ readonly pid?: number;
16
+ readonly projection?: unknown;
17
+ };
4
18
  export type { RunRecord, UnifiedInboxRequest } from "@rig/contracts";
5
19
  export type RunProjectionRecord = RunRecord;
6
20
  /** What an attach/steer caller needs to reach a live run over collab. */
@@ -13,12 +27,12 @@ export interface RunJoinTarget {
13
27
  }
14
28
  export declare function readSessionRunEntries(sessionPath: string | null): RunSessionCustomEntry[];
15
29
  export declare function runRecordFromRegistryEntry(projectRoot: string, entry: ListedRegistryEntry): RunRecord | null;
16
- export declare function runRecordsFromRegistrySnapshot(projectRoot: string, snapshot: RegistrySnapshotFrame): RunRecord[];
30
+ export declare function runRecordsFromCollab(projectRoot: string, collabs: readonly LiveRunCollabProjection[]): RunRecord[];
17
31
  export declare function discoveryDiagnosticRunRecord(projectRoot: string, error: unknown): RunRecord;
18
- export declare function listRunProjections(projectRoot: string, filter?: CollabRegistryFilter): Promise<RunRecord[]>;
32
+ export declare function listRunProjections(projectRoot: string, filter?: RunDiscoveryFilter): Promise<RunRecord[]>;
19
33
  export declare function selectRunProjection(runs: readonly RunRecord[], runId: string): RunRecord | null;
20
- export declare function getRunProjection(projectRoot: string, runId: string, filter?: CollabRegistryFilter): Promise<RunRecord | null>;
21
- export declare function resolveRunJoinTarget(projectRoot: string, runId: string, filter?: CollabRegistryFilter): Promise<RunJoinTarget | null>;
34
+ export declare function getRunProjection(projectRoot: string, runId: string, filter?: RunDiscoveryFilter): Promise<RunRecord | null>;
35
+ export declare function resolveRunJoinTarget(projectRoot: string, runId: string, filter?: RunDiscoveryFilter): Promise<RunJoinTarget | null>;
22
36
  export declare const listRuns: typeof listRunProjections;
23
37
  export declare const getRun: typeof getRunProjection;
24
38
  export declare const resolveJoinTarget: typeof resolveRunJoinTarget;