@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.
- package/dist/src/autohost.d.ts +8 -10
- package/dist/src/autohost.js +683 -95
- package/dist/src/constants.d.ts +0 -1
- package/dist/src/constants.js +0 -2
- package/dist/src/extension.js +683 -95
- package/dist/src/host-kernel.d.ts +22 -0
- package/dist/src/host-kernel.js +78 -0
- package/dist/src/host.d.ts +2 -0
- package/dist/src/host.js +419 -0
- package/dist/src/index.d.ts +0 -6
- package/dist/src/index.js +1913 -133
- package/dist/src/local-run-changes.d.ts +3 -0
- package/dist/src/local-run-changes.js +65 -0
- package/dist/src/notifications.js +13 -5
- package/dist/src/notify-cap.d.ts +11 -0
- package/dist/src/notify-cap.js +13 -0
- package/dist/src/panel-plugin.js +3 -7
- package/dist/src/plugin.d.ts +0 -11
- package/dist/src/plugin.js +1910 -101
- package/dist/src/{runs → read-model-backend}/control.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/control.js +361 -38
- package/dist/src/{runs → read-model-backend}/diagnostics.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/diagnostics.js +1 -3
- package/dist/src/{runs → read-model-backend}/guard.js +2 -3
- package/dist/src/{runs → read-model-backend}/inbox.js +366 -36
- package/dist/src/{runs → read-model-backend}/index.js +552 -223
- package/dist/src/{runs → read-model-backend}/inspect.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/inspect.js +350 -34
- package/dist/src/{runs → read-model-backend}/projection.d.ts +21 -7
- package/dist/src/{runs → read-model-backend}/projection.js +349 -31
- package/dist/src/{runs → read-model-backend}/run-status.d.ts +6 -3
- package/dist/src/{runs → read-model-backend}/run-status.js +53 -33
- package/dist/src/{runs → read-model-backend}/stats.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/stats.js +373 -58
- package/dist/src/read-model-service.d.ts +2 -0
- package/dist/src/read-model-service.js +1433 -0
- package/dist/src/session-journal.d.ts +60 -0
- package/dist/src/session-journal.js +471 -0
- package/dist/src/stall.d.ts +8 -3
- package/dist/src/stall.js +0 -1
- package/dist/src/utils.js +282 -3
- package/package.json +9 -12
- package/dist/src/journal.d.ts +0 -33
- package/dist/src/journal.js +0 -31
- /package/dist/src/{runs → read-model-backend}/guard.d.ts +0 -0
- /package/dist/src/{runs → read-model-backend}/inbox.d.ts +0 -0
- /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/
|
|
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/
|
|
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 {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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
|
|
154
|
-
...
|
|
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
|
|
264
|
-
return sortByRecency(
|
|
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
|
|
313
|
-
|
|
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/
|
|
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
|
-
|
|
3
|
-
|
|
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
|
|
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?:
|
|
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?:
|
|
21
|
-
export declare function resolveRunJoinTarget(projectRoot: string, runId: string, filter?:
|
|
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;
|