@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
package/dist/src/index.js
CHANGED
|
@@ -14,17 +14,604 @@ var __export = (target, all) => {
|
|
|
14
14
|
});
|
|
15
15
|
};
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
var __require = import.meta.require;
|
|
17
18
|
|
|
18
|
-
// packages/run-worker/src/
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
// packages/run-worker/src/host-kernel.ts
|
|
20
|
+
import { existsSync, readFileSync } from "fs";
|
|
21
|
+
import { resolve } from "path";
|
|
22
|
+
import { applyConfigEnv } from "@rig/core/config-env";
|
|
23
|
+
import { loadConfig } from "@rig/core/load-config";
|
|
24
|
+
import { bootDefaultKernelIntoProcess } from "@rig/kernel-seed/boot-default";
|
|
25
|
+
import { createPlacementTransportPlugin } from "@rig/kernel-seed/default-kernel";
|
|
26
|
+
function unquoteDotenvValue(value) {
|
|
27
|
+
const trimmed = value.trim();
|
|
28
|
+
if (trimmed.length >= 2 && (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
29
|
+
return trimmed.slice(1, -1);
|
|
30
|
+
}
|
|
31
|
+
return trimmed;
|
|
32
|
+
}
|
|
33
|
+
function hydrateDotenv(path, env) {
|
|
34
|
+
if (!existsSync(path))
|
|
35
|
+
return false;
|
|
36
|
+
const source = readFileSync(path, "utf8");
|
|
37
|
+
for (const rawLine of source.split(/\r?\n/)) {
|
|
38
|
+
const line = rawLine.trim();
|
|
39
|
+
if (!line || line.startsWith("#"))
|
|
40
|
+
continue;
|
|
41
|
+
const assignment = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
|
|
42
|
+
const equals = assignment.indexOf("=");
|
|
43
|
+
if (equals <= 0)
|
|
44
|
+
continue;
|
|
45
|
+
const key = assignment.slice(0, equals).trim();
|
|
46
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) || env[key])
|
|
47
|
+
continue;
|
|
48
|
+
env[key] = unquoteDotenvValue(assignment.slice(equals + 1));
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
function createRunWorkerPlacementTransportPlugin(transport) {
|
|
53
|
+
return createPlacementTransportPlugin(transport);
|
|
54
|
+
}
|
|
55
|
+
async function hydrateRunWorkerProjectEnv(projectRoot, options = {}) {
|
|
56
|
+
const normalizedRoot = resolve(projectRoot);
|
|
57
|
+
const env = options.env ?? process.env;
|
|
58
|
+
const errors = [];
|
|
59
|
+
let dotenvLoaded = false;
|
|
60
|
+
let configLoaded = false;
|
|
61
|
+
try {
|
|
62
|
+
dotenvLoaded = hydrateDotenv(resolve(normalizedRoot, options.dotenvPath ?? ".env"), env);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
errors.push(`dotenv: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
applyConfigEnv(await loadConfig(normalizedRoot), env);
|
|
68
|
+
configLoaded = true;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
if (!message.includes("no rig.config"))
|
|
72
|
+
errors.push(`config: ${message}`);
|
|
73
|
+
}
|
|
74
|
+
return { projectRoot: normalizedRoot, dotenvLoaded, configLoaded, errors };
|
|
75
|
+
}
|
|
76
|
+
async function adoptRunWorkerKernel(root, options = {}) {
|
|
77
|
+
if (options.hydrateEnv === true && root !== undefined) {
|
|
78
|
+
await hydrateRunWorkerProjectEnv(root, options);
|
|
79
|
+
}
|
|
80
|
+
const bootInput = {
|
|
81
|
+
...options.entrypoint !== undefined ? { entrypoint: options.entrypoint } : {},
|
|
82
|
+
...options.journal !== undefined ? { journal: options.journal } : {}
|
|
83
|
+
};
|
|
84
|
+
if (options.transport === undefined)
|
|
85
|
+
return await bootDefaultKernelIntoProcess(bootInput);
|
|
86
|
+
return await bootDefaultKernelIntoProcess({
|
|
87
|
+
...bootInput,
|
|
88
|
+
extraPlugins: [createRunWorkerPlacementTransportPlugin(options.transport)]
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
var init_host_kernel = () => {};
|
|
92
|
+
|
|
93
|
+
// packages/run-worker/src/local-run-changes.ts
|
|
94
|
+
import { existsSync as existsSync2, mkdirSync, watch } from "fs";
|
|
95
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
96
|
+
import { Effect, Queue, Stream } from "effect";
|
|
97
|
+
function findNearestGitCheckoutRoot(startDir) {
|
|
98
|
+
let current = resolve2(startDir);
|
|
99
|
+
for (;; ) {
|
|
100
|
+
if (existsSync2(resolve2(current, ".git")))
|
|
101
|
+
return current;
|
|
102
|
+
const parent = dirname(current);
|
|
103
|
+
if (parent === current)
|
|
104
|
+
return null;
|
|
105
|
+
current = parent;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function resolveCheckoutRoot(projectRoot) {
|
|
109
|
+
const normalizedProjectRoot = resolve2(projectRoot);
|
|
110
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
111
|
+
if (explicit) {
|
|
112
|
+
const explicitRoot = resolve2(explicit);
|
|
113
|
+
const gitRoot = findNearestGitCheckoutRoot(explicitRoot);
|
|
114
|
+
if (gitRoot)
|
|
115
|
+
return gitRoot;
|
|
116
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there or above it.`);
|
|
117
|
+
}
|
|
118
|
+
return findNearestGitCheckoutRoot(normalizedProjectRoot) ?? normalizedProjectRoot;
|
|
119
|
+
}
|
|
120
|
+
function isRelevantLocalRunChange(filename) {
|
|
121
|
+
if (filename === null || filename === undefined)
|
|
122
|
+
return true;
|
|
123
|
+
const raw = filename.toString();
|
|
124
|
+
if (!raw.trim())
|
|
125
|
+
return true;
|
|
126
|
+
const segments = raw.split(/[\\/]+/).filter(Boolean);
|
|
127
|
+
if (segments.some((segment) => segment === ".git" || segment === "node_modules" || segment === "build" || segment === "dist" || segment === "out" || segment === ".next" || segment === "coverage")) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const rigIndex = segments.lastIndexOf(".rig");
|
|
131
|
+
if (rigIndex < 0)
|
|
132
|
+
return false;
|
|
133
|
+
const rigChild = segments[rigIndex + 1];
|
|
134
|
+
if (rigChild === "session")
|
|
135
|
+
return true;
|
|
136
|
+
return rigChild === "runtime-context.json" && segments[rigIndex + 2] === undefined;
|
|
137
|
+
}
|
|
138
|
+
var localRunChanges = (projectRoot) => Stream.callback((queue) => Effect.gen(function* () {
|
|
139
|
+
const worktreesRoot = resolve2(resolveCheckoutRoot(projectRoot), ".worktrees");
|
|
140
|
+
if (!existsSync2(worktreesRoot)) {
|
|
141
|
+
try {
|
|
142
|
+
mkdirSync(worktreesRoot, { recursive: true });
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
const watcher = watch(worktreesRoot, { recursive: true }, (_event, filename) => {
|
|
146
|
+
if (!isRelevantLocalRunChange(filename))
|
|
147
|
+
return;
|
|
148
|
+
Queue.offerUnsafe(queue, undefined);
|
|
149
|
+
});
|
|
150
|
+
watcher.on("error", () => {});
|
|
151
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => watcher.close()));
|
|
152
|
+
}), { bufferSize: 1, strategy: "sliding" });
|
|
153
|
+
var init_local_run_changes = () => {};
|
|
154
|
+
|
|
155
|
+
// packages/run-worker/src/notify-cap.ts
|
|
156
|
+
import { NOTIFY_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
157
|
+
import { defineCapability } from "@rig/core/capability";
|
|
158
|
+
import { resolvePluginHost } from "@rig/core/project-plugins";
|
|
159
|
+
async function resolveNotifyService(projectRoot) {
|
|
160
|
+
const { host } = await resolvePluginHost(projectRoot);
|
|
161
|
+
return NotifyCap.resolve(host);
|
|
162
|
+
}
|
|
163
|
+
var NotifyCap;
|
|
164
|
+
var init_notify_cap = __esm(() => {
|
|
165
|
+
NotifyCap = defineCapability(NOTIFY_SERVICE_CAPABILITY);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// packages/run-worker/src/notifications.ts
|
|
169
|
+
async function dispatchRunNotifications(projectRoot, runId, taskId, outcome, detail) {
|
|
170
|
+
try {
|
|
171
|
+
const notifier = await resolveNotifyService(projectRoot);
|
|
172
|
+
if (!notifier)
|
|
173
|
+
return;
|
|
174
|
+
const event = { runId, type: `run.${outcome}`, timestamp: new Date().toISOString(), payload: { taskId, detail } };
|
|
175
|
+
await Promise.race([
|
|
176
|
+
notifier.notify(event),
|
|
177
|
+
new Promise((resolveCap) => setTimeout(resolveCap, 5000))
|
|
178
|
+
]);
|
|
179
|
+
} catch {}
|
|
180
|
+
}
|
|
181
|
+
var init_notifications = __esm(() => {
|
|
182
|
+
init_notify_cap();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// packages/run-worker/src/panel-plugin.ts
|
|
186
|
+
import { createProjectPluginHost } from "@rig/core/project-plugins";
|
|
187
|
+
import { RIG_CAPABILITY_PANEL_SLOT, RIG_RUN_STOP_PANEL_ACTION, RIG_SUPERVISOR_PANEL_ID } from "@rig/contracts";
|
|
188
|
+
async function produceWorkerPanelPayload(producer, context) {
|
|
189
|
+
if (!producer.produce)
|
|
190
|
+
return;
|
|
191
|
+
let timeout;
|
|
192
|
+
try {
|
|
193
|
+
return await Promise.race([
|
|
194
|
+
Promise.resolve(producer.produce(context)),
|
|
195
|
+
new Promise((resolve3) => {
|
|
196
|
+
timeout = setTimeout(() => resolve3(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
197
|
+
})
|
|
198
|
+
]);
|
|
199
|
+
} finally {
|
|
200
|
+
clearTimeout(timeout);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function loadWorkerPanelRegistry(projectRoot) {
|
|
204
|
+
const { host } = await createProjectPluginHost(projectRoot, {
|
|
205
|
+
mode: "strict-config-only"
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
209
|
+
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels", PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
213
|
+
var init_panel_plugin = () => {};
|
|
214
|
+
|
|
215
|
+
// packages/run-worker/src/session-journal.ts
|
|
216
|
+
import { Schema } from "effect";
|
|
217
|
+
import {
|
|
218
|
+
CUSTOM_TYPE_FOR,
|
|
219
|
+
RIG_CONTROL_SENTINEL_END,
|
|
220
|
+
RIG_INBOX_RESOLUTION_SENTINEL,
|
|
221
|
+
RIG_PAUSE_SENTINEL,
|
|
222
|
+
RIG_RESUME_SENTINEL,
|
|
223
|
+
RIG_STOP_SENTINEL,
|
|
224
|
+
RIG_STOP_SENTINEL_END,
|
|
225
|
+
RIG_WORKFLOW_STATUS_CHANGED,
|
|
226
|
+
RunJournalEvent,
|
|
227
|
+
TYPE_FOR_CUSTOM
|
|
228
|
+
} from "@rig/contracts";
|
|
229
|
+
function isTerminalRunStatus(status) {
|
|
230
|
+
return TERMINAL_RUN_STATUSES.includes(status);
|
|
231
|
+
}
|
|
232
|
+
function canTransitionRunStatus(from, to) {
|
|
233
|
+
if (from === null)
|
|
234
|
+
return true;
|
|
235
|
+
if (from === to)
|
|
236
|
+
return true;
|
|
237
|
+
return RUN_STATUS_TRANSITIONS[from].includes(to);
|
|
238
|
+
}
|
|
239
|
+
function assertRunStatusTransition(from, to) {
|
|
240
|
+
if (!canTransitionRunStatus(from, to)) {
|
|
241
|
+
throw new Error(`Illegal run status transition: ${from ?? "(none)"} -> ${to}. ` + `Allowed from ${from ?? "(none)"}: ${from ? RUN_STATUS_TRANSITIONS[from].join(", ") : "(any)"}.`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function reduceRunJournal(events, runId = null) {
|
|
245
|
+
let record = {};
|
|
246
|
+
let status = null;
|
|
247
|
+
const statusHistory = [];
|
|
248
|
+
const pendingApprovals = new Map;
|
|
249
|
+
const resolvedApprovals = [];
|
|
250
|
+
const pendingUserInputs = new Map;
|
|
251
|
+
const resolvedUserInputs = [];
|
|
252
|
+
const closeoutPhases = [];
|
|
253
|
+
let resolvedPipeline = null;
|
|
254
|
+
const stageOutcomes = [];
|
|
255
|
+
const anomalies = [];
|
|
256
|
+
let steeringCount = 0;
|
|
257
|
+
let stallCount = 0;
|
|
258
|
+
let lastSeq = 0;
|
|
259
|
+
let lastEventAt = null;
|
|
260
|
+
const projectedRunId = runId ?? events[0]?.runId ?? null;
|
|
261
|
+
for (const event of events) {
|
|
262
|
+
lastSeq = event.seq;
|
|
263
|
+
lastEventAt = event.at;
|
|
264
|
+
switch (event.type) {
|
|
265
|
+
case "status-changed": {
|
|
266
|
+
if (!canTransitionRunStatus(status, event.to)) {
|
|
267
|
+
anomalies.push({ seq: event.seq, kind: "illegal-transition", detail: `${status ?? "(none)"} -> ${event.to}` });
|
|
268
|
+
}
|
|
269
|
+
statusHistory.push({ seq: event.seq, at: event.at, from: status, to: event.to, reason: event.reason ?? null });
|
|
270
|
+
const wasTerminal = status !== null && isTerminalRunStatus(status);
|
|
271
|
+
status = event.to;
|
|
272
|
+
record = { ...record, updatedAt: event.at };
|
|
273
|
+
if (isTerminalRunStatus(event.to) && !record.completedAt)
|
|
274
|
+
record = { ...record, completedAt: event.at };
|
|
275
|
+
if (!isTerminalRunStatus(event.to) && wasTerminal)
|
|
276
|
+
record = { ...record, completedAt: null };
|
|
277
|
+
if (event.to === "running" && !record.startedAt)
|
|
278
|
+
record = { ...record, startedAt: event.at };
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case "record-patch": {
|
|
282
|
+
const next = { ...record };
|
|
283
|
+
for (const [key, value] of Object.entries(event.patch)) {
|
|
284
|
+
if (value !== undefined)
|
|
285
|
+
next[key] = value;
|
|
286
|
+
}
|
|
287
|
+
next.updatedAt = event.patch.updatedAt ?? event.at;
|
|
288
|
+
record = next;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case "approval-requested": {
|
|
292
|
+
pendingApprovals.set(event.requestId, {
|
|
293
|
+
requestId: event.requestId,
|
|
294
|
+
requestKind: event.requestKind,
|
|
295
|
+
actionId: event.actionId ?? null,
|
|
296
|
+
payload: event.payload,
|
|
297
|
+
requestedAt: event.at
|
|
298
|
+
});
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
case "approval-resolved": {
|
|
302
|
+
const pending = pendingApprovals.get(event.requestId);
|
|
303
|
+
if (!pending) {
|
|
304
|
+
const alreadyResolved = resolvedApprovals.some((entry) => entry.requestId === event.requestId);
|
|
305
|
+
anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `approval ${event.requestId}` });
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
pendingApprovals.delete(event.requestId);
|
|
309
|
+
resolvedApprovals.push({ ...pending, decision: event.decision, note: event.note ?? null, actor: event.actor, resolvedAt: event.at });
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case "input-requested": {
|
|
313
|
+
pendingUserInputs.set(event.requestId, {
|
|
314
|
+
requestId: event.requestId,
|
|
315
|
+
requestKind: "user-input",
|
|
316
|
+
actionId: null,
|
|
317
|
+
payload: event.payload,
|
|
318
|
+
requestedAt: event.at
|
|
319
|
+
});
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
case "input-resolved": {
|
|
323
|
+
const pending = pendingUserInputs.get(event.requestId);
|
|
324
|
+
if (!pending) {
|
|
325
|
+
const alreadyResolved = resolvedUserInputs.some((entry) => entry.requestId === event.requestId);
|
|
326
|
+
anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `user-input ${event.requestId}` });
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
pendingUserInputs.delete(event.requestId);
|
|
330
|
+
resolvedUserInputs.push({ ...pending, answers: event.answers, actor: event.actor, resolvedAt: event.at });
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
case "steering":
|
|
334
|
+
steeringCount += 1;
|
|
335
|
+
break;
|
|
336
|
+
case "adopted":
|
|
337
|
+
record = { ...record, pid: event.pid, updatedAt: event.at };
|
|
338
|
+
break;
|
|
339
|
+
case "stall-detected":
|
|
340
|
+
stallCount += 1;
|
|
341
|
+
break;
|
|
342
|
+
case "closeout-phase":
|
|
343
|
+
closeoutPhases.push({ seq: event.seq, at: event.at, phase: event.phase, outcome: event.outcome, detail: event.detail ?? null });
|
|
344
|
+
break;
|
|
345
|
+
case "pipeline-resolved":
|
|
346
|
+
resolvedPipeline = event.pipeline;
|
|
347
|
+
break;
|
|
348
|
+
case "stage-outcome":
|
|
349
|
+
stageOutcomes.push({ seq: event.seq, at: event.at, outcome: event.outcome });
|
|
350
|
+
break;
|
|
351
|
+
case "timeline-entry":
|
|
352
|
+
case "log-entry":
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
runId: projectedRunId,
|
|
358
|
+
record,
|
|
359
|
+
status,
|
|
360
|
+
statusHistory,
|
|
361
|
+
pendingApprovals: [...pendingApprovals.values()],
|
|
362
|
+
resolvedApprovals,
|
|
363
|
+
pendingUserInputs: [...pendingUserInputs.values()],
|
|
364
|
+
resolvedUserInputs,
|
|
365
|
+
steeringCount,
|
|
366
|
+
stallCount,
|
|
367
|
+
closeoutPhases,
|
|
368
|
+
resolvedPipeline,
|
|
369
|
+
stageOutcomes,
|
|
370
|
+
lastSeq,
|
|
371
|
+
lastEventAt,
|
|
372
|
+
anomalies
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function sessionIdFromSessionFile(sessionPath) {
|
|
376
|
+
if (!sessionPath)
|
|
377
|
+
return null;
|
|
378
|
+
const file = sessionPath.split(/[\\/]/).pop() ?? "";
|
|
379
|
+
const match = file.match(/_([0-9a-fA-F][0-9a-fA-F-]{7,})\.jsonl$/);
|
|
380
|
+
return match?.[1] ?? null;
|
|
381
|
+
}
|
|
382
|
+
function isRunSessionCustomType(customType) {
|
|
383
|
+
return customType !== undefined && Object.hasOwn(TYPE_FOR_CUSTOM, customType);
|
|
384
|
+
}
|
|
385
|
+
function foldRunSessionEntries(entries, runId) {
|
|
386
|
+
const events = [];
|
|
387
|
+
entries.forEach((entry, index) => {
|
|
388
|
+
if (entry.type !== "custom" || !isRunSessionCustomType(entry.customType))
|
|
389
|
+
return;
|
|
390
|
+
const data = entry.data !== null && typeof entry.data === "object" ? entry.data : {};
|
|
391
|
+
const stamped = {
|
|
392
|
+
v: 1,
|
|
393
|
+
seq: index + 1,
|
|
394
|
+
at: typeof data.at === "string" ? data.at : new Date(0).toISOString(),
|
|
395
|
+
runId,
|
|
396
|
+
...data,
|
|
397
|
+
type: TYPE_FOR_CUSTOM[entry.customType]
|
|
398
|
+
};
|
|
399
|
+
try {
|
|
400
|
+
events.push(decodeRunJournalEvent(stamped));
|
|
401
|
+
} catch {}
|
|
402
|
+
});
|
|
403
|
+
return reduceRunJournal(events, runId);
|
|
404
|
+
}
|
|
405
|
+
function projectRunFromSession(source, runId) {
|
|
406
|
+
const entries = "getEntries" in source ? source.getEntries() : source;
|
|
407
|
+
return foldRunSessionEntries(entries, runId);
|
|
408
|
+
}
|
|
409
|
+
function isRecord(value) {
|
|
410
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
411
|
+
}
|
|
412
|
+
function timelineEntryFromCustomEntry(entry) {
|
|
413
|
+
if (entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
|
|
414
|
+
return null;
|
|
415
|
+
const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
|
|
416
|
+
const type = typeof payload.type === "string" ? payload.type : "timeline";
|
|
417
|
+
const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
|
|
418
|
+
const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
|
|
419
|
+
const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
|
|
420
|
+
const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
|
|
421
|
+
return { at, type, stage, status, detail };
|
|
422
|
+
}
|
|
423
|
+
function timelineEntriesFromCustomEntries(entries) {
|
|
424
|
+
return entries.flatMap((entry) => {
|
|
425
|
+
const timelineEntry = timelineEntryFromCustomEntry(entry);
|
|
426
|
+
return timelineEntry ? [timelineEntry] : [];
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
function latestTimelineEntriesFromCustomEntries(entries, limit) {
|
|
430
|
+
if (limit <= 0)
|
|
431
|
+
return [];
|
|
432
|
+
const timeline = [];
|
|
433
|
+
for (let index = entries.length - 1;index >= 0 && timeline.length < limit; index -= 1) {
|
|
434
|
+
const timelineEntry = timelineEntryFromCustomEntry(entries[index]);
|
|
435
|
+
if (timelineEntry)
|
|
436
|
+
timeline.push(timelineEntry);
|
|
437
|
+
}
|
|
438
|
+
timeline.reverse();
|
|
439
|
+
return timeline;
|
|
440
|
+
}
|
|
441
|
+
function parseStopSentinel(text, expectedRunId) {
|
|
442
|
+
const start = text.indexOf(RIG_STOP_SENTINEL);
|
|
443
|
+
if (start < 0)
|
|
444
|
+
return null;
|
|
445
|
+
const end = text.indexOf(RIG_STOP_SENTINEL_END, start);
|
|
446
|
+
const body = text.slice(start + RIG_STOP_SENTINEL.length, end >= 0 ? end : undefined).trim();
|
|
447
|
+
const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
|
|
448
|
+
if (runId && runId !== expectedRunId)
|
|
449
|
+
return null;
|
|
450
|
+
const reason = body.match(/(?:^|\s)reason=(.+)$/)?.[1]?.trim() ?? null;
|
|
451
|
+
return { reason };
|
|
452
|
+
}
|
|
453
|
+
function parseControlSentinel(marker, text, expectedRunId) {
|
|
454
|
+
const start = text.indexOf(marker);
|
|
455
|
+
if (start < 0)
|
|
456
|
+
return null;
|
|
457
|
+
const end = text.indexOf(RIG_CONTROL_SENTINEL_END, start);
|
|
458
|
+
const body = text.slice(start + marker.length, end >= 0 ? end : undefined).trim();
|
|
459
|
+
const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
|
|
460
|
+
if (runId && runId !== expectedRunId)
|
|
461
|
+
return null;
|
|
462
|
+
const requestedBy = body.match(/(?:^|\s)requestedBy=([^\s>]+)/)?.[1] ?? null;
|
|
463
|
+
return { requestedBy };
|
|
464
|
+
}
|
|
465
|
+
function parsePauseSentinel(text, expectedRunId) {
|
|
466
|
+
return parseControlSentinel(RIG_PAUSE_SENTINEL, text, expectedRunId);
|
|
467
|
+
}
|
|
468
|
+
function parseResumeSentinel(text, expectedRunId) {
|
|
469
|
+
return parseControlSentinel(RIG_RESUME_SENTINEL, text, expectedRunId);
|
|
470
|
+
}
|
|
471
|
+
function decodeInboxResolutionPayload(payload) {
|
|
472
|
+
try {
|
|
473
|
+
const parsed = JSON.parse(decodeURIComponent(payload));
|
|
474
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
475
|
+
return null;
|
|
476
|
+
const record = parsed;
|
|
477
|
+
if (record.kind === "approval") {
|
|
478
|
+
const requestId = typeof record.requestId === "string" ? record.requestId.trim() : "";
|
|
479
|
+
const decision = record.decision === "approve" || record.decision === "reject" ? record.decision : null;
|
|
480
|
+
const note = typeof record.note === "string" ? record.note : record.note === null ? null : undefined;
|
|
481
|
+
if (!requestId || !decision)
|
|
482
|
+
return null;
|
|
483
|
+
return { kind: "approval", requestId, decision, ...note !== undefined ? { note } : {} };
|
|
484
|
+
}
|
|
485
|
+
if (record.kind === "input") {
|
|
486
|
+
const requestId = typeof record.requestId === "string" ? record.requestId.trim() : "";
|
|
487
|
+
const answers = record.answers && typeof record.answers === "object" && !Array.isArray(record.answers) ? Object.fromEntries(Object.entries(record.answers).filter((entry) => typeof entry[1] === "string")) : null;
|
|
488
|
+
if (!requestId || !answers)
|
|
489
|
+
return null;
|
|
490
|
+
return { kind: "input", requestId, answers };
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
function parseInboxResolutionSentinel(text, expectedRunId) {
|
|
498
|
+
const start = text.indexOf(RIG_INBOX_RESOLUTION_SENTINEL);
|
|
499
|
+
if (start < 0)
|
|
500
|
+
return null;
|
|
501
|
+
const end = text.indexOf(RIG_CONTROL_SENTINEL_END, start);
|
|
502
|
+
const body = text.slice(start + RIG_INBOX_RESOLUTION_SENTINEL.length, end >= 0 ? end : undefined).trim();
|
|
503
|
+
const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
|
|
504
|
+
if (runId && runId !== expectedRunId)
|
|
505
|
+
return null;
|
|
506
|
+
const requestedBy = body.match(/(?:^|\s)requestedBy=([^\s>]+)/)?.[1] ?? null;
|
|
507
|
+
const payload = body.match(/(?:^|\s)data=([^\s>]+)/)?.[1] ?? "";
|
|
508
|
+
const decoded = decodeInboxResolutionPayload(payload);
|
|
509
|
+
if (!decoded)
|
|
510
|
+
return null;
|
|
511
|
+
return { ...decoded, requestedBy };
|
|
512
|
+
}
|
|
513
|
+
function asCustomEntries(entries) {
|
|
514
|
+
return entries.filter((entry) => entry.type === "custom");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
class RunSessionJournal {
|
|
518
|
+
sm;
|
|
519
|
+
runId;
|
|
520
|
+
constructor(sm, runId) {
|
|
521
|
+
this.sm = sm;
|
|
522
|
+
this.runId = runId;
|
|
523
|
+
}
|
|
524
|
+
#append(init) {
|
|
525
|
+
this.sm.appendCustomEntry(CUSTOM_TYPE_FOR[init.type], { ...init, at: new Date().toISOString() });
|
|
526
|
+
}
|
|
527
|
+
appendStatus(to, opts = {}) {
|
|
528
|
+
const from = foldRunSessionEntries(asCustomEntries(this.sm.getEntries()), this.runId).status;
|
|
529
|
+
if (!opts.force)
|
|
530
|
+
assertRunStatusTransition(from, to);
|
|
531
|
+
if (opts.errorText !== undefined)
|
|
532
|
+
this.#append({ type: "record-patch", patch: { errorText: opts.errorText } });
|
|
533
|
+
this.#append({
|
|
534
|
+
type: "status-changed",
|
|
535
|
+
from,
|
|
536
|
+
to,
|
|
537
|
+
...opts.reason !== undefined ? { reason: opts.reason } : {},
|
|
538
|
+
...opts.actor !== undefined ? { actor: opts.actor } : {}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
appendTimeline(payload) {
|
|
542
|
+
this.#append({ type: "timeline-entry", payload });
|
|
543
|
+
}
|
|
544
|
+
appendCloseoutPhase(input) {
|
|
545
|
+
this.#append({
|
|
546
|
+
type: "closeout-phase",
|
|
547
|
+
phase: input.phase,
|
|
548
|
+
outcome: input.outcome,
|
|
549
|
+
...input.detail !== undefined ? { detail: input.detail } : {}
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
appendApprovalResolved(input) {
|
|
553
|
+
this.#append({
|
|
554
|
+
type: "approval-resolved",
|
|
555
|
+
requestId: input.requestId,
|
|
556
|
+
decision: input.decision,
|
|
557
|
+
actor: input.actor,
|
|
558
|
+
...input.note !== undefined ? { note: input.note } : {}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
appendInputResolved(input) {
|
|
562
|
+
this.#append({ type: "input-resolved", requestId: input.requestId, answers: input.answers, actor: input.actor });
|
|
563
|
+
}
|
|
564
|
+
appendStall(input) {
|
|
565
|
+
this.#append({ type: "stall-detected", detail: input.detail });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function isRunJournalEventInit(event) {
|
|
569
|
+
if (event === null || typeof event !== "object")
|
|
570
|
+
return false;
|
|
571
|
+
const type = event.type;
|
|
572
|
+
return typeof type === "string" && Object.hasOwn(CUSTOM_TYPE_FOR, type);
|
|
573
|
+
}
|
|
574
|
+
function createJournalSessionProvider(options) {
|
|
575
|
+
const now = options.now ?? (() => new Date);
|
|
576
|
+
const appendRunEvent = async (event, runId = options.runId) => {
|
|
577
|
+
options.store.appendCustomEntry(CUSTOM_TYPE_FOR[event.type], { ...event, runId, at: now().toISOString() });
|
|
578
|
+
};
|
|
579
|
+
const readEntries = () => asCustomEntries(options.store.getEntries());
|
|
580
|
+
const entriesForRun = (runId) => readEntries().filter((entry) => {
|
|
581
|
+
const data = entry.data;
|
|
582
|
+
if (data === null || typeof data !== "object")
|
|
583
|
+
return runId === options.runId;
|
|
584
|
+
const candidate = data.runId;
|
|
585
|
+
return candidate === undefined ? runId === options.runId : candidate === runId;
|
|
586
|
+
});
|
|
587
|
+
return {
|
|
588
|
+
runId: options.runId,
|
|
589
|
+
async append(event) {
|
|
590
|
+
if (isRunJournalEventInit(event))
|
|
591
|
+
await appendRunEvent(event);
|
|
592
|
+
},
|
|
593
|
+
appendRunEvent,
|
|
594
|
+
async recordPipeline(runId, pipeline) {
|
|
595
|
+
await appendRunEvent({ type: "pipeline-resolved", pipeline }, runId);
|
|
596
|
+
},
|
|
597
|
+
async recordStageOutcome(runId, outcome) {
|
|
598
|
+
await appendRunEvent({ type: "stage-outcome", outcome }, runId);
|
|
599
|
+
},
|
|
600
|
+
async read(runId) {
|
|
601
|
+
return entriesForRun(runId);
|
|
602
|
+
},
|
|
603
|
+
readEntries,
|
|
604
|
+
readProjection: () => foldRunSessionEntries(entriesForRun(options.runId), options.runId)
|
|
605
|
+
};
|
|
606
|
+
}
|
|
21
607
|
async function createRunJournal(sessionManager, runId) {
|
|
22
608
|
try {
|
|
23
|
-
const
|
|
609
|
+
const writableSessionManager = sessionManager;
|
|
610
|
+
const journal = new RunSessionJournal(writableSessionManager, runId);
|
|
24
611
|
const kernel = createJournalSessionProvider({
|
|
25
612
|
runId,
|
|
26
613
|
store: {
|
|
27
|
-
appendCustomEntry: (customType, data) =>
|
|
614
|
+
appendCustomEntry: (customType, data) => writableSessionManager.appendCustomEntry(customType, data),
|
|
28
615
|
getEntries: () => sessionManager.getEntries?.() ?? sessionManager.getBranch?.() ?? []
|
|
29
616
|
}
|
|
30
617
|
});
|
|
@@ -42,29 +629,43 @@ async function createRunJournal(sessionManager, runId) {
|
|
|
42
629
|
return null;
|
|
43
630
|
}
|
|
44
631
|
}
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
632
|
+
var decodeRunJournalEvent, RUN_STATUS_TRANSITIONS, TERMINAL_RUN_STATUSES;
|
|
633
|
+
var init_session_journal = __esm(() => {
|
|
634
|
+
decodeRunJournalEvent = Schema.decodeUnknownSync(RunJournalEvent);
|
|
635
|
+
RUN_STATUS_TRANSITIONS = {
|
|
636
|
+
created: ["queued", "preparing", "running", "failed", "stopped"],
|
|
637
|
+
queued: ["preparing", "running", "failed", "stopped"],
|
|
638
|
+
preparing: ["queued", "running", "needs-attention", "failed", "stopped"],
|
|
639
|
+
running: [
|
|
640
|
+
"queued",
|
|
641
|
+
"waiting-approval",
|
|
642
|
+
"waiting-user-input",
|
|
643
|
+
"paused",
|
|
644
|
+
"validating",
|
|
645
|
+
"reviewing",
|
|
646
|
+
"closing-out",
|
|
647
|
+
"needs-attention",
|
|
648
|
+
"completed",
|
|
649
|
+
"failed",
|
|
650
|
+
"stopped"
|
|
651
|
+
],
|
|
652
|
+
"waiting-approval": ["running", "needs-attention", "failed", "stopped"],
|
|
653
|
+
"waiting-user-input": ["running", "needs-attention", "failed", "stopped"],
|
|
654
|
+
paused: ["running", "failed", "stopped"],
|
|
655
|
+
validating: ["running", "reviewing", "closing-out", "needs-attention", "completed", "failed", "stopped"],
|
|
656
|
+
reviewing: ["running", "validating", "closing-out", "needs-attention", "completed", "failed", "stopped"],
|
|
657
|
+
"closing-out": ["running", "needs-attention", "completed", "failed", "stopped"],
|
|
658
|
+
"needs-attention": ["queued", "preparing", "running", "closing-out", "completed", "failed", "stopped"],
|
|
659
|
+
completed: [],
|
|
660
|
+
failed: ["queued", "preparing", "running", "closing-out"],
|
|
661
|
+
stopped: ["queued", "preparing", "running", "closing-out"]
|
|
662
|
+
};
|
|
663
|
+
TERMINAL_RUN_STATUSES = ["completed", "failed", "stopped"];
|
|
664
|
+
});
|
|
63
665
|
|
|
64
666
|
// packages/run-worker/src/constants.ts
|
|
65
|
-
var
|
|
667
|
+
var TRACKED_RUN_STALL_MS, RUN_PROCESS_STALL_SWEEP_MS, RUN_PROCESS_STALL_DETAIL = "Run process made no OMP session progress for 20+ minutes; recording a stall so recovery can requeue or resume the session.";
|
|
66
668
|
var init_constants = __esm(() => {
|
|
67
|
-
RUN_PROCESS_STEER_TIMEOUT_MS = 10 * 60 * 1000;
|
|
68
669
|
TRACKED_RUN_STALL_MS = 20 * 60 * 1000;
|
|
69
670
|
RUN_PROCESS_STALL_SWEEP_MS = 60 * 1000;
|
|
70
671
|
});
|
|
@@ -116,43 +717,9 @@ var init_stall = __esm(() => {
|
|
|
116
717
|
init_constants();
|
|
117
718
|
});
|
|
118
719
|
|
|
119
|
-
// packages/run-worker/src/panel-plugin.ts
|
|
120
|
-
import { createProjectPluginHost, projectPluginResolutionWarningMessages } from "@rig/core/project-plugins";
|
|
121
|
-
import { RIG_CAPABILITY_PANEL_SLOT, RIG_RUN_STOP_PANEL_ACTION, RIG_SUPERVISOR_PANEL_ID } from "@rig/contracts";
|
|
122
|
-
async function produceWorkerPanelPayload(producer, context) {
|
|
123
|
-
if (!producer.produce)
|
|
124
|
-
return;
|
|
125
|
-
let timeout;
|
|
126
|
-
try {
|
|
127
|
-
return await Promise.race([
|
|
128
|
-
Promise.resolve(producer.produce(context)),
|
|
129
|
-
new Promise((resolve2) => {
|
|
130
|
-
timeout = setTimeout(() => resolve2(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
131
|
-
})
|
|
132
|
-
]);
|
|
133
|
-
} finally {
|
|
134
|
-
clearTimeout(timeout);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async function loadWorkerPanelRegistry(projectRoot) {
|
|
138
|
-
const { host, resolved } = await createProjectPluginHost(projectRoot, {
|
|
139
|
-
mode: "strict-config-only",
|
|
140
|
-
surfaceName: "run-worker-panel-registry"
|
|
141
|
-
});
|
|
142
|
-
for (const message of projectPluginResolutionWarningMessages(resolved)) {
|
|
143
|
-
console.warn(`[rig-run] ${message}`);
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
147
|
-
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels", PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
151
|
-
var init_panel_plugin = () => {};
|
|
152
|
-
|
|
153
720
|
// packages/run-worker/src/utils.ts
|
|
154
|
-
import {
|
|
155
|
-
import { resolveRegistryBaseUrl, resolveRelayUrl } from "@rig/
|
|
721
|
+
import { RIG_WORKFLOW_STATUS_CHANGED as RIG_WORKFLOW_STATUS_CHANGED2 } from "@rig/contracts";
|
|
722
|
+
import { resolveRegistryBaseUrl, resolveRelayUrl } from "@rig/core/remote-config";
|
|
156
723
|
function customEntries(entries) {
|
|
157
724
|
const customs = [];
|
|
158
725
|
for (const entry of entries) {
|
|
@@ -170,22 +737,24 @@ function registryBaseUrl() {
|
|
|
170
737
|
function rigRelayUrl() {
|
|
171
738
|
return resolveRelayUrl();
|
|
172
739
|
}
|
|
173
|
-
var init_utils = () => {
|
|
740
|
+
var init_utils = __esm(() => {
|
|
741
|
+
init_session_journal();
|
|
742
|
+
});
|
|
174
743
|
|
|
175
744
|
// packages/run-worker/src/autohost.ts
|
|
176
|
-
import {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
import {
|
|
186
|
-
import {
|
|
187
|
-
import {
|
|
188
|
-
import {
|
|
745
|
+
import {
|
|
746
|
+
PLACEMENT_RUN_TRANSPORT,
|
|
747
|
+
RIG_RUN_STOP_PANEL_ACTION as RIG_RUN_STOP_PANEL_ACTION2,
|
|
748
|
+
RIG_SUPERVISOR_PANEL_ID as RIG_SUPERVISOR_PANEL_ID2,
|
|
749
|
+
RUN_IDENTITY_ENV,
|
|
750
|
+
RUN_REGISTRY_BACKBONE,
|
|
751
|
+
RUN_CLOSEOUT_CAPABILITY,
|
|
752
|
+
TASK_DATA_SERVICE_CAPABILITY
|
|
753
|
+
} from "@rig/contracts";
|
|
754
|
+
import { Duration, Effect as Effect2, Fiber, Stream as Stream2 } from "effect";
|
|
755
|
+
import { defineCapability as defineCapability2 } from "@rig/core/capability";
|
|
756
|
+
import { requireInstalledCapability, loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
757
|
+
import { resolveOwnerNamespaceKey } from "@rig/core/remote-config";
|
|
189
758
|
function syntheticRegistryOwner(namespaceKey) {
|
|
190
759
|
const githubUserId = namespaceKey.startsWith("gh:") ? namespaceKey.slice(3) : namespaceKey;
|
|
191
760
|
return {
|
|
@@ -194,18 +763,25 @@ function syntheticRegistryOwner(namespaceKey) {
|
|
|
194
763
|
namespaceKey
|
|
195
764
|
};
|
|
196
765
|
}
|
|
766
|
+
function coerceRegistryStatus(value, fallback) {
|
|
767
|
+
if (value === "starting")
|
|
768
|
+
return "preparing";
|
|
769
|
+
if (value === "waiting-input")
|
|
770
|
+
return "waiting-user-input";
|
|
771
|
+
return typeof value === "string" && REGISTRY_STATUS[value] ? value : fallback;
|
|
772
|
+
}
|
|
197
773
|
async function reflectStoppedRunTaskSource(input) {
|
|
198
774
|
const taskId = input.taskId?.trim();
|
|
199
775
|
if (!taskId)
|
|
200
776
|
return false;
|
|
201
777
|
const reason = input.reason?.trim();
|
|
202
|
-
await (input.updateLifecycle ?? updateRunTaskSourceLifecycle)(input.projectRoot, {
|
|
778
|
+
await (input.updateLifecycle ?? taskData().updateRunTaskSourceLifecycle)(input.projectRoot, {
|
|
203
779
|
runId: input.runId,
|
|
204
780
|
taskId,
|
|
205
|
-
sourceTask: input.sourceTask,
|
|
206
|
-
worktreePath: input.worktreePath,
|
|
207
|
-
logRoot: input.logRoot,
|
|
208
|
-
sessionPath: input.sessionPath
|
|
781
|
+
...input.sourceTask !== undefined ? { sourceTask: input.sourceTask } : {},
|
|
782
|
+
...input.worktreePath !== undefined ? { worktreePath: input.worktreePath } : {},
|
|
783
|
+
...input.logRoot !== undefined ? { logRoot: input.logRoot } : {},
|
|
784
|
+
...input.sessionPath !== undefined ? { sessionPath: input.sessionPath } : {}
|
|
209
785
|
}, "cancelled", reason ? `Rig stopped by operator: ${reason}` : "Rig stopped by operator.");
|
|
210
786
|
return true;
|
|
211
787
|
}
|
|
@@ -286,8 +862,14 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
286
862
|
throw new Error("OMP session id is required when RIG_RUN_PROCESS=1; RIG_RUN_ID is only a dispatch handle.");
|
|
287
863
|
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
288
864
|
const journal = await createRunJournal(ctx.sessionManager, runId);
|
|
289
|
-
await
|
|
865
|
+
const placementTransport = await loadCapabilityForRoot(projectRoot, PlacementTransportCap);
|
|
866
|
+
if (!placementTransport) {
|
|
867
|
+
throw new Error("placement run transport capability unavailable: ensure @rig/transport-plugin is installed.");
|
|
868
|
+
}
|
|
869
|
+
await adoptRunWorkerKernel(projectRoot, {
|
|
290
870
|
entrypoint: "run-worker",
|
|
871
|
+
hydrateEnv: true,
|
|
872
|
+
transport: placementTransport,
|
|
291
873
|
...journal ? { journal: journal.kernel } : {}
|
|
292
874
|
});
|
|
293
875
|
if (!ctx.hasUI)
|
|
@@ -296,7 +878,9 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
296
878
|
const startHost = collab?.startCollabHost ?? collab?.startHost;
|
|
297
879
|
if (!startHost)
|
|
298
880
|
throw new Error("OMP collab host facade is unavailable for Rig run process.");
|
|
299
|
-
const
|
|
881
|
+
const identityEnv = await loadCapabilityForRoot(projectRoot, RunIdentityEnvCap);
|
|
882
|
+
const identity = identityEnv?.resolveRigIdentity(ctx) ?? null;
|
|
883
|
+
const registryBackbone = await loadCapabilityForRoot(projectRoot, RunRegistryBackboneCap);
|
|
300
884
|
const panelRegistry = await loadWorkerPanelRegistry(projectRoot);
|
|
301
885
|
let runProjection = projectRunFromSession(customEntries(ctx.sessionManager.getBranch()), runId);
|
|
302
886
|
const taskIdAtStart = process.env.RIG_TASK_ID?.trim() || runProjection.record.taskId;
|
|
@@ -318,7 +902,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
318
902
|
const runRegistryNamespace = identity?.owner?.namespaceKey ?? resolveOwnerNamespaceKey(rigProjectRoot()) ?? "anonymous";
|
|
319
903
|
const runRegistryOwner = identity?.owner ?? syntheticRegistryOwner(runRegistryNamespace);
|
|
320
904
|
const runRegistryRepo = identity?.selectedRepo ?? "";
|
|
321
|
-
const runRegistry = createRegistryClient({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace });
|
|
905
|
+
const runRegistry = registryBackbone?.createRegistryClient({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace }) ?? null;
|
|
322
906
|
let runRegistryLinks = {};
|
|
323
907
|
const processedControlEntryIds = new Set((ctx.sessionManager.getEntries?.() ?? []).map((entry) => entry && typeof entry === "object" && typeof entry.id === "string" ? entry.id : null).filter((id) => id !== null));
|
|
324
908
|
const processedControlEntryObjects = new WeakSet;
|
|
@@ -352,7 +936,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
352
936
|
joinLink: links.joinLink,
|
|
353
937
|
webLink: links.webLink,
|
|
354
938
|
relayUrl: links.relayUrl,
|
|
355
|
-
sessionPath: ctx.sessionManager.getSessionFile(),
|
|
939
|
+
sessionPath: ctx.sessionManager.getSessionFile() ?? null,
|
|
356
940
|
timeline
|
|
357
941
|
});
|
|
358
942
|
};
|
|
@@ -366,7 +950,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
366
950
|
projectRoot,
|
|
367
951
|
runId,
|
|
368
952
|
folded,
|
|
369
|
-
taskIdAtStart,
|
|
953
|
+
...taskIdAtStart !== undefined ? { taskIdAtStart } : {},
|
|
370
954
|
runDisplayTitle
|
|
371
955
|
};
|
|
372
956
|
const payloadEntries = await Promise.all(panelRegistry.producers.map(async (producer) => {
|
|
@@ -426,7 +1010,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
426
1010
|
runRegistryHeartbeat = null;
|
|
427
1011
|
}
|
|
428
1012
|
if (workerProjectionFiber) {
|
|
429
|
-
|
|
1013
|
+
Effect2.runFork(Fiber.interrupt(workerProjectionFiber));
|
|
430
1014
|
workerProjectionFiber = null;
|
|
431
1015
|
}
|
|
432
1016
|
if (workerProjectionConnection) {
|
|
@@ -538,7 +1122,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
538
1122
|
if (roomId !== runId) {
|
|
539
1123
|
throw new Error(`Collab host session id mismatch: expected ${runId}, got ${roomId}`);
|
|
540
1124
|
}
|
|
541
|
-
runRegistryLinks = { joinLink: projection.joinLink, webLink: projection.webLink, relayUrl: projection.relayUrl ?? rigRelayUrl() };
|
|
1125
|
+
runRegistryLinks = { joinLink: projection.joinLink ?? null, webLink: projection.webLink ?? null, relayUrl: projection.relayUrl ?? rigRelayUrl() };
|
|
542
1126
|
const timeline = {
|
|
543
1127
|
type: "collab-host-started",
|
|
544
1128
|
roomId,
|
|
@@ -550,10 +1134,11 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
550
1134
|
console.log(`[rig-run] collab-host-started joinLink=${projection.joinLink || "(empty)"} relayUrl=${projection.relayUrl || "(empty)"}`);
|
|
551
1135
|
ctx.ui.notify("Rig run collab host started.", "info");
|
|
552
1136
|
publishRigPanelsSnapshotBestEffort();
|
|
553
|
-
if (runRegistry) {
|
|
1137
|
+
if (runRegistry && registryBackbone) {
|
|
554
1138
|
try {
|
|
555
1139
|
runRegistryRoomId = roomId;
|
|
556
1140
|
const initialProjection = buildCurrentRegistryProjection();
|
|
1141
|
+
const sessionPath = ctx.sessionManager.getSessionFile();
|
|
557
1142
|
await runRegistry.registerRoom({
|
|
558
1143
|
roomId,
|
|
559
1144
|
owner: runRegistryOwner,
|
|
@@ -565,14 +1150,14 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
565
1150
|
relayUrl: projection.relayUrl ?? rigRelayUrl(),
|
|
566
1151
|
startedAt: new Date().toISOString(),
|
|
567
1152
|
cwd: process.cwd(),
|
|
568
|
-
sessionPath
|
|
1153
|
+
...sessionPath ? { sessionPath } : {},
|
|
569
1154
|
pid: process.pid,
|
|
570
1155
|
projection: initialProjection
|
|
571
1156
|
});
|
|
572
|
-
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId: roomId });
|
|
1157
|
+
workerProjectionConnection = registryBackbone.connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId: roomId });
|
|
573
1158
|
workerProjectionConnection.ready.catch((err) => console.error(`[rig-run] worker-projection-degraded (heartbeat remains authoritative): ${err instanceof Error ? err.message : String(err)}`));
|
|
574
1159
|
pushWorkerProjection();
|
|
575
|
-
workerProjectionFiber =
|
|
1160
|
+
workerProjectionFiber = Effect2.runFork(localRunChanges(projectRoot).pipe(Stream2.debounce(Duration.millis(500)), Stream2.runForEach(() => Effect2.sync(() => pushWorkerProjection()))));
|
|
576
1161
|
journal?.appendTimeline({ type: "registry-registered", roomId });
|
|
577
1162
|
console.log(`[rig-run] registry-registered roomId=${roomId}`);
|
|
578
1163
|
runRegistryHeartbeat = setInterval(() => {
|
|
@@ -605,7 +1190,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
605
1190
|
if (taskIdAtStart) {
|
|
606
1191
|
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "running", detail: "reflecting rig:running to task source" });
|
|
607
1192
|
try {
|
|
608
|
-
await updateRunTaskSourceLifecycle(projectRoot, {
|
|
1193
|
+
await taskData().updateRunTaskSourceLifecycle(projectRoot, {
|
|
609
1194
|
runId,
|
|
610
1195
|
taskId: taskIdAtStart,
|
|
611
1196
|
sourceTask: runProjection.record.sourceTask,
|
|
@@ -698,10 +1283,9 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
698
1283
|
setTimeout(() => process.exit(1), 0);
|
|
699
1284
|
return;
|
|
700
1285
|
}
|
|
701
|
-
const { command, gitCommand } = createEnvCloseoutRunners(process.env);
|
|
702
1286
|
let closeoutStatusAdvanced = false;
|
|
703
1287
|
try {
|
|
704
|
-
await
|
|
1288
|
+
await requireInstalledCapability(RunCloseoutCap, "run-closeout capability unavailable in run-worker autohost: ensure @rig/bundle-default-lifecycle (default bundle) is installed.")({
|
|
705
1289
|
projectRoot,
|
|
706
1290
|
runId,
|
|
707
1291
|
taskId,
|
|
@@ -709,8 +1293,6 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
709
1293
|
workspace: process.cwd(),
|
|
710
1294
|
artifactRoot: runProjection.record.artifactRoot ?? null,
|
|
711
1295
|
sourceTask: runProjection.record.sourceTask && typeof runProjection.record.sourceTask === "object" && !Array.isArray(runProjection.record.sourceTask) ? runProjection.record.sourceTask : null,
|
|
712
|
-
command,
|
|
713
|
-
gitCommand,
|
|
714
1296
|
steerPi,
|
|
715
1297
|
onValidationStart: async () => {
|
|
716
1298
|
journal?.appendStatus("validating", { actor: { kind: "agent" }, reason: "validating task before closeout", force: true });
|
|
@@ -726,7 +1308,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
726
1308
|
}
|
|
727
1309
|
},
|
|
728
1310
|
reflect: async (status, summary, opts) => {
|
|
729
|
-
await updateRunTaskSourceLifecycle(projectRoot, {
|
|
1311
|
+
await taskData().updateRunTaskSourceLifecycle(projectRoot, {
|
|
730
1312
|
runId,
|
|
731
1313
|
taskId,
|
|
732
1314
|
sourceTask: runProjection.record.sourceTask,
|
|
@@ -745,7 +1327,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
745
1327
|
setTimeout(() => process.exit(0), 0);
|
|
746
1328
|
} catch (error) {
|
|
747
1329
|
const detail = error instanceof Error ? error.message : String(error);
|
|
748
|
-
const status = error instanceof CloseoutValidationError ? "needs-attention" : "failed";
|
|
1330
|
+
const status = error instanceof Error && error.name === "CloseoutValidationError" ? "needs-attention" : "failed";
|
|
749
1331
|
journal?.appendStatus(status, { actor: { kind: "agent" }, errorText: detail, force: true });
|
|
750
1332
|
await publishRunProjection(status);
|
|
751
1333
|
await dispatchRunNotifications(projectRoot, runId, taskId, "failed", detail);
|
|
@@ -771,7 +1353,7 @@ async function maybeStartSpikeAutohost(ctx) {
|
|
|
771
1353
|
const startHost = collab?.startHost ?? collab?.startCollabHost;
|
|
772
1354
|
if (!startHost)
|
|
773
1355
|
throw new Error("OMP collab host facade is unavailable for detached PTY spike.");
|
|
774
|
-
const identity = resolveRigIdentity(ctx);
|
|
1356
|
+
const identity = (await loadCapabilityForRoot(rigProjectRoot(), RunIdentityEnvCap))?.resolveRigIdentity(ctx) ?? null;
|
|
775
1357
|
await startHost.call(collab, {
|
|
776
1358
|
relayUrl,
|
|
777
1359
|
title: "Rig detached PTY spike",
|
|
@@ -779,13 +1361,38 @@ async function maybeStartSpikeAutohost(ctx) {
|
|
|
779
1361
|
...identity?.selectedRepo ? { selectedRepo: identity.selectedRepo } : {}
|
|
780
1362
|
});
|
|
781
1363
|
}
|
|
782
|
-
var REGISTRY_PROJECTION_TIMELINE_LIMIT = 100, OPERATOR_ACTOR;
|
|
1364
|
+
var TaskDataCap, PlacementTransportCap, RunIdentityEnvCap, RunRegistryBackboneCap, RunCloseoutCap, taskData = () => requireInstalledCapability(TaskDataCap, "task-data capability unavailable in run-exec autohost: ensure @rig/task-sources-plugin (default bundle) is installed."), REGISTRY_STATUS, REGISTRY_PROJECTION_TIMELINE_LIMIT = 100, OPERATOR_ACTOR;
|
|
783
1365
|
var init_autohost = __esm(() => {
|
|
784
|
-
|
|
1366
|
+
init_host_kernel();
|
|
1367
|
+
init_local_run_changes();
|
|
785
1368
|
init_notifications();
|
|
786
|
-
init_stall();
|
|
787
1369
|
init_panel_plugin();
|
|
1370
|
+
init_session_journal();
|
|
1371
|
+
init_stall();
|
|
788
1372
|
init_utils();
|
|
1373
|
+
TaskDataCap = defineCapability2(TASK_DATA_SERVICE_CAPABILITY);
|
|
1374
|
+
PlacementTransportCap = defineCapability2(PLACEMENT_RUN_TRANSPORT);
|
|
1375
|
+
RunIdentityEnvCap = defineCapability2(RUN_IDENTITY_ENV);
|
|
1376
|
+
RunRegistryBackboneCap = defineCapability2(RUN_REGISTRY_BACKBONE);
|
|
1377
|
+
RunCloseoutCap = defineCapability2(RUN_CLOSEOUT_CAPABILITY);
|
|
1378
|
+
REGISTRY_STATUS = {
|
|
1379
|
+
created: true,
|
|
1380
|
+
queued: true,
|
|
1381
|
+
preparing: true,
|
|
1382
|
+
running: true,
|
|
1383
|
+
"waiting-approval": true,
|
|
1384
|
+
"waiting-user-input": true,
|
|
1385
|
+
paused: true,
|
|
1386
|
+
validating: true,
|
|
1387
|
+
reviewing: true,
|
|
1388
|
+
"closing-out": true,
|
|
1389
|
+
"needs-attention": true,
|
|
1390
|
+
completed: true,
|
|
1391
|
+
failed: true,
|
|
1392
|
+
stopped: true,
|
|
1393
|
+
starting: true,
|
|
1394
|
+
"waiting-input": true
|
|
1395
|
+
};
|
|
789
1396
|
OPERATOR_ACTOR = { kind: "operator" };
|
|
790
1397
|
});
|
|
791
1398
|
|
|
@@ -824,15 +1431,1216 @@ var init_extension = __esm(() => {
|
|
|
824
1431
|
__rigExtensionTest = __rigRunWorkerTest;
|
|
825
1432
|
});
|
|
826
1433
|
|
|
1434
|
+
// packages/run-worker/src/read-model-backend/run-status.ts
|
|
1435
|
+
import { OPERATOR_INACTIVE_RUN_STATUSES } from "@rig/contracts";
|
|
1436
|
+
function isTerminalRunStatus2(status) {
|
|
1437
|
+
return TERMINAL_RUN_STATUSES2.includes(status);
|
|
1438
|
+
}
|
|
1439
|
+
function isActiveRunStatus(status) {
|
|
1440
|
+
return !isTerminalRunStatus2(status);
|
|
1441
|
+
}
|
|
1442
|
+
function normalizeRunStatusToken(status) {
|
|
1443
|
+
return String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
1444
|
+
}
|
|
1445
|
+
function canonicalStatusToken(status) {
|
|
1446
|
+
const normalized = normalizeRunStatusToken(status);
|
|
1447
|
+
if (normalized === "waiting-input")
|
|
1448
|
+
return "waiting-user-input";
|
|
1449
|
+
return normalized;
|
|
1450
|
+
}
|
|
1451
|
+
function asRunStatus(status) {
|
|
1452
|
+
return KNOWN_RUN_STATUS[status] ? status : null;
|
|
1453
|
+
}
|
|
1454
|
+
function statusColorRole(status) {
|
|
1455
|
+
switch (canonicalStatusToken(status)) {
|
|
1456
|
+
case "done":
|
|
1457
|
+
case "completed":
|
|
1458
|
+
case "ready":
|
|
1459
|
+
case "healthy":
|
|
1460
|
+
case "approved":
|
|
1461
|
+
case "merged":
|
|
1462
|
+
return "success";
|
|
1463
|
+
case "needs-attention":
|
|
1464
|
+
case "waiting-approval":
|
|
1465
|
+
case "waiting-user-input":
|
|
1466
|
+
case "blocked":
|
|
1467
|
+
case "paused":
|
|
1468
|
+
return "action-yellow";
|
|
1469
|
+
case "running":
|
|
1470
|
+
case "adopted":
|
|
1471
|
+
case "preparing":
|
|
1472
|
+
case "created":
|
|
1473
|
+
case "queued":
|
|
1474
|
+
case "starting":
|
|
1475
|
+
case "pending":
|
|
1476
|
+
case "in-progress":
|
|
1477
|
+
case "active":
|
|
1478
|
+
case "booting":
|
|
1479
|
+
case "validating":
|
|
1480
|
+
case "reviewing":
|
|
1481
|
+
case "closing-out":
|
|
1482
|
+
return "active-cyan";
|
|
1483
|
+
case "failed":
|
|
1484
|
+
case "error":
|
|
1485
|
+
case "rejected":
|
|
1486
|
+
return "failure";
|
|
1487
|
+
case "stopped":
|
|
1488
|
+
case "cancelled":
|
|
1489
|
+
case "canceled":
|
|
1490
|
+
case "stale":
|
|
1491
|
+
return "muted";
|
|
1492
|
+
default:
|
|
1493
|
+
return "neutral";
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
function isNeedsAttention(run) {
|
|
1497
|
+
return canonicalStatusToken(run.status) === "needs-attention" || run.pendingApprovals + run.pendingInputs > 0 || run.stallCount > 0;
|
|
1498
|
+
}
|
|
1499
|
+
function phaseForStatus(status, runStatus, needsAttention) {
|
|
1500
|
+
if (runStatus && isTerminalRunStatus2(runStatus))
|
|
1501
|
+
return runStatus === "completed" || runStatus === "failed" || runStatus === "stopped" ? runStatus : "stopped";
|
|
1502
|
+
if (needsAttention)
|
|
1503
|
+
return "needs-attention";
|
|
1504
|
+
switch (status) {
|
|
1505
|
+
case "created":
|
|
1506
|
+
case "queued":
|
|
1507
|
+
case "preparing":
|
|
1508
|
+
case "starting":
|
|
1509
|
+
case "booting":
|
|
1510
|
+
return "starting";
|
|
1511
|
+
case "waiting-approval":
|
|
1512
|
+
case "waiting-user-input":
|
|
1513
|
+
return "waiting";
|
|
1514
|
+
case "paused":
|
|
1515
|
+
return "paused";
|
|
1516
|
+
case "running":
|
|
1517
|
+
case "validating":
|
|
1518
|
+
case "reviewing":
|
|
1519
|
+
case "closing-out":
|
|
1520
|
+
return "active";
|
|
1521
|
+
default:
|
|
1522
|
+
return runStatus && isActiveRunStatus(runStatus) ? "active" : "unknown";
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function classifyRun(run) {
|
|
1526
|
+
const status = canonicalStatusToken(run.status) || (run.live && !run.stale ? "starting" : "unknown");
|
|
1527
|
+
const runStatus = asRunStatus(status);
|
|
1528
|
+
const isTerminal = runStatus ? isTerminalRunStatus2(runStatus) : false;
|
|
1529
|
+
const isNeedsAttentionValue = isNeedsAttention(run);
|
|
1530
|
+
return {
|
|
1531
|
+
status,
|
|
1532
|
+
phase: phaseForStatus(status, runStatus, isNeedsAttentionValue),
|
|
1533
|
+
isActive: runStatus ? isActiveRunStatus(runStatus) : !isTerminal && status !== "unknown",
|
|
1534
|
+
isTerminal,
|
|
1535
|
+
isNeedsAttention: isNeedsAttentionValue
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
function runStatusColorRole(run) {
|
|
1539
|
+
const classification = classifyRun(run);
|
|
1540
|
+
return classification.isNeedsAttention && !classification.isTerminal ? "action-yellow" : statusColorRole(classification.status);
|
|
1541
|
+
}
|
|
1542
|
+
function statusRank(run) {
|
|
1543
|
+
const classification = classifyRun(run);
|
|
1544
|
+
if (classification.isNeedsAttention)
|
|
1545
|
+
return 0;
|
|
1546
|
+
switch (classification.phase) {
|
|
1547
|
+
case "needs-attention":
|
|
1548
|
+
return 0;
|
|
1549
|
+
case "active":
|
|
1550
|
+
case "waiting":
|
|
1551
|
+
case "paused":
|
|
1552
|
+
return 1;
|
|
1553
|
+
case "starting":
|
|
1554
|
+
return 2;
|
|
1555
|
+
case "completed":
|
|
1556
|
+
return 3;
|
|
1557
|
+
case "failed":
|
|
1558
|
+
return 4;
|
|
1559
|
+
case "stopped":
|
|
1560
|
+
return 5;
|
|
1561
|
+
case "unknown":
|
|
1562
|
+
return 6;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
var TERMINAL_RUN_STATUSES2, ACTIVE_RUN_STATUSES, KNOWN_RUN_STATUS;
|
|
1566
|
+
var init_run_status = __esm(() => {
|
|
1567
|
+
TERMINAL_RUN_STATUSES2 = ["completed", "failed", "stopped"];
|
|
1568
|
+
ACTIVE_RUN_STATUSES = [
|
|
1569
|
+
"created",
|
|
1570
|
+
"queued",
|
|
1571
|
+
"preparing",
|
|
1572
|
+
"running",
|
|
1573
|
+
"waiting-approval",
|
|
1574
|
+
"waiting-user-input",
|
|
1575
|
+
"paused",
|
|
1576
|
+
"validating",
|
|
1577
|
+
"reviewing",
|
|
1578
|
+
"closing-out",
|
|
1579
|
+
"needs-attention"
|
|
1580
|
+
];
|
|
1581
|
+
KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES2].map((status) => [status, true]));
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
// packages/run-worker/src/read-model-backend/diagnostics.ts
|
|
1585
|
+
function normalizeString(value) {
|
|
1586
|
+
if (typeof value !== "string")
|
|
1587
|
+
return null;
|
|
1588
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
1589
|
+
return normalized.length > 0 ? normalized : null;
|
|
1590
|
+
}
|
|
1591
|
+
function isGenericRunFailure(value) {
|
|
1592
|
+
const text = normalizeString(value);
|
|
1593
|
+
return Boolean(text && /^Task run failed \([^)]*\)$/i.test(text));
|
|
1594
|
+
}
|
|
1595
|
+
function appendCandidate(candidates, value) {
|
|
1596
|
+
const text = normalizeString(value);
|
|
1597
|
+
if (text && !candidates.includes(text))
|
|
1598
|
+
candidates.push(text);
|
|
1599
|
+
}
|
|
1600
|
+
function categorizeUsefulRunError(lines) {
|
|
1601
|
+
const taskSourceFailure = lines.find((line) => /failed to update task source/i.test(line));
|
|
1602
|
+
if (taskSourceFailure)
|
|
1603
|
+
return `Task source update failed: ${taskSourceFailure}`;
|
|
1604
|
+
const moduleFailure = lines.find((line) => /cannot find module/i.test(line));
|
|
1605
|
+
if (moduleFailure)
|
|
1606
|
+
return `Runtime module resolution failed: ${moduleFailure}`;
|
|
1607
|
+
const providerFailure = lines.find((line) => /no api key found|unauthorized|authentication failed|invalid api key/i.test(line));
|
|
1608
|
+
if (providerFailure)
|
|
1609
|
+
return `Provider authentication failed: ${providerFailure}`;
|
|
1610
|
+
return null;
|
|
1611
|
+
}
|
|
1612
|
+
function summarizeRunError(projection) {
|
|
1613
|
+
if (projection.status !== "failed")
|
|
1614
|
+
return null;
|
|
1615
|
+
const candidates = [];
|
|
1616
|
+
for (const anomaly of projection.anomalies) {
|
|
1617
|
+
appendCandidate(candidates, `Journal anomaly (${anomaly.kind}): ${anomaly.detail}`);
|
|
1618
|
+
}
|
|
1619
|
+
for (const phase of projection.closeoutPhases) {
|
|
1620
|
+
if (phase.outcome === "failed")
|
|
1621
|
+
appendCandidate(candidates, phase.detail);
|
|
1622
|
+
}
|
|
1623
|
+
for (const entry of projection.statusHistory) {
|
|
1624
|
+
appendCandidate(candidates, entry.reason);
|
|
1625
|
+
}
|
|
1626
|
+
appendCandidate(candidates, projection.record.statusDetail);
|
|
1627
|
+
appendCandidate(candidates, projection.record.errorText);
|
|
1628
|
+
const nonGeneric = candidates.filter((candidate) => !isGenericRunFailure(candidate));
|
|
1629
|
+
return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// packages/run-worker/src/read-model-backend/projection.ts
|
|
1633
|
+
var exports_projection = {};
|
|
1634
|
+
__export(exports_projection, {
|
|
1635
|
+
selectRunProjection: () => selectRunProjection,
|
|
1636
|
+
runRecordsFromCollab: () => runRecordsFromCollab,
|
|
1637
|
+
runRecordFromRegistryEntry: () => runRecordFromRegistryEntry,
|
|
1638
|
+
resolveRunJoinTarget: () => resolveRunJoinTarget,
|
|
1639
|
+
resolveJoinTarget: () => resolveJoinTarget,
|
|
1640
|
+
readSessionRunEntries: () => readSessionRunEntries,
|
|
1641
|
+
listRuns: () => listRuns,
|
|
1642
|
+
listRunProjections: () => listRunProjections,
|
|
1643
|
+
getRunProjection: () => getRunProjection,
|
|
1644
|
+
getRun: () => getRun,
|
|
1645
|
+
discoveryDiagnosticRunRecord: () => discoveryDiagnosticRunRecord
|
|
1646
|
+
});
|
|
1647
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
1648
|
+
import { isAbsolute, relative, resolve as resolve3 } from "path";
|
|
1649
|
+
import { RUN_DISCOVERY } from "@rig/contracts";
|
|
1650
|
+
import { loadCapabilityForRoot as loadCapabilityForRoot2 } from "@rig/core/capability-loaders";
|
|
1651
|
+
import { defineCapability as defineCapability3 } from "@rig/core/capability";
|
|
1652
|
+
function registryEntryFromCollab(collab) {
|
|
1653
|
+
const record = collab;
|
|
1654
|
+
return {
|
|
1655
|
+
roomId: collab.sessionId,
|
|
1656
|
+
title: collab.title,
|
|
1657
|
+
status: record.registryStatus ?? (collab.stale ? "stale" : "running"),
|
|
1658
|
+
startedAt: collab.startedAt ?? null,
|
|
1659
|
+
heartbeatAt: collab.updatedAt ?? null,
|
|
1660
|
+
sessionPath: collab.sessionPath ?? null,
|
|
1661
|
+
cwd: collab.cwd ?? null,
|
|
1662
|
+
joinLink: collab.joinLink ?? null,
|
|
1663
|
+
webLink: collab.webLink ?? null,
|
|
1664
|
+
relayUrl: collab.relayUrl ?? null,
|
|
1665
|
+
stale: collab.stale,
|
|
1666
|
+
repo: collab.selectedRepo ?? null,
|
|
1667
|
+
...collab.pid === undefined ? {} : { pid: collab.pid },
|
|
1668
|
+
projection: record.registryProjection ?? null
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
function readSessionRunEntries(sessionPath) {
|
|
1672
|
+
if (!sessionPath || !existsSync3(sessionPath))
|
|
1673
|
+
return [];
|
|
1674
|
+
try {
|
|
1675
|
+
return parseSessionRunEntries(readFileSync2(sessionPath, "utf8"));
|
|
1676
|
+
} catch {
|
|
1677
|
+
return [];
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
function parseSessionRunEntries(raw) {
|
|
1681
|
+
const entries = [];
|
|
1682
|
+
for (const line of raw.split(`
|
|
1683
|
+
`)) {
|
|
1684
|
+
if (!line.trim())
|
|
1685
|
+
continue;
|
|
1686
|
+
try {
|
|
1687
|
+
const parsed = JSON.parse(line);
|
|
1688
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1689
|
+
continue;
|
|
1690
|
+
entries.push({
|
|
1691
|
+
type: "type" in parsed && typeof parsed.type === "string" ? parsed.type : "",
|
|
1692
|
+
..."customType" in parsed && typeof parsed.customType === "string" ? { customType: parsed.customType } : {},
|
|
1693
|
+
..."data" in parsed ? { data: parsed.data } : {}
|
|
1694
|
+
});
|
|
1695
|
+
} catch {
|
|
1696
|
+
continue;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
return entries;
|
|
1700
|
+
}
|
|
1701
|
+
function stringOrNull(value) {
|
|
1702
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1703
|
+
}
|
|
1704
|
+
function numberOrNull(value) {
|
|
1705
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1706
|
+
}
|
|
1707
|
+
function objectRecord(value) {
|
|
1708
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1709
|
+
}
|
|
1710
|
+
function registryStatusAsRunStatus(status) {
|
|
1711
|
+
if (status === "waiting-input")
|
|
1712
|
+
return "waiting-user-input";
|
|
1713
|
+
if (status === "starting")
|
|
1714
|
+
return "preparing";
|
|
1715
|
+
return typeof status === "string" ? asRunStatus(status) : null;
|
|
1716
|
+
}
|
|
1717
|
+
function payloadString(payload, keys) {
|
|
1718
|
+
if (!payload || typeof payload !== "object")
|
|
1719
|
+
return null;
|
|
1720
|
+
const record = payload;
|
|
1721
|
+
for (const key of keys) {
|
|
1722
|
+
const value = record[key];
|
|
1723
|
+
if (typeof value === "string" && value.trim())
|
|
1724
|
+
return value.trim();
|
|
1725
|
+
}
|
|
1726
|
+
return null;
|
|
1727
|
+
}
|
|
1728
|
+
function payloadOptions(payload) {
|
|
1729
|
+
if (!payload || typeof payload !== "object")
|
|
1730
|
+
return;
|
|
1731
|
+
const record = payload;
|
|
1732
|
+
const options = Array.isArray(record.options) ? record.options : Array.isArray(record.choices) ? record.choices : null;
|
|
1733
|
+
const values = options?.filter((value) => typeof value === "string" && value.trim().length > 0) ?? [];
|
|
1734
|
+
return values.length > 0 ? values : undefined;
|
|
1735
|
+
}
|
|
1736
|
+
function inboxRequest(request, kind) {
|
|
1737
|
+
const fallback = kind === "approval" ? "Approval requested" : "Input requested";
|
|
1738
|
+
const body = payloadString(request.payload, ["body", "description", "detail", "details"]);
|
|
1739
|
+
const options = payloadOptions(request.payload);
|
|
1740
|
+
return {
|
|
1741
|
+
requestId: request.requestId,
|
|
1742
|
+
kind,
|
|
1743
|
+
title: payloadString(request.payload, ["title", "message", "reason", "prompt", "summary"]) ?? fallback,
|
|
1744
|
+
...body !== undefined ? { body } : {},
|
|
1745
|
+
...options ? { options } : {},
|
|
1746
|
+
requestedAt: request.requestedAt ?? null,
|
|
1747
|
+
source: "run"
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
function inboxFromProjection(projection) {
|
|
1751
|
+
return [
|
|
1752
|
+
...projection.pendingApprovals.map((request) => inboxRequest(request, "approval")),
|
|
1753
|
+
...projection.pendingUserInputs.map((request) => inboxRequest(request, "input"))
|
|
1754
|
+
].sort((a, b) => (Date.parse(a.requestedAt ?? "") || 0) - (Date.parse(b.requestedAt ?? "") || 0));
|
|
1755
|
+
}
|
|
1756
|
+
function isProjectPath(projectRoot, cwd) {
|
|
1757
|
+
const root = resolve3(projectRoot);
|
|
1758
|
+
const candidate = resolve3(cwd);
|
|
1759
|
+
const pathFromRoot = relative(root, candidate);
|
|
1760
|
+
return pathFromRoot === "" || !pathFromRoot.startsWith("../") && pathFromRoot !== ".." && !isAbsolute(pathFromRoot);
|
|
1761
|
+
}
|
|
1762
|
+
function sourceFromEntry(projectRoot, projection, entry) {
|
|
1763
|
+
const candidates = [
|
|
1764
|
+
stringOrNull(projection.collabCwd),
|
|
1765
|
+
stringOrNull(projection.cwd),
|
|
1766
|
+
stringOrNull(entry.cwd),
|
|
1767
|
+
stringOrNull(projection.worktreePath)
|
|
1768
|
+
].filter((cwd) => cwd !== null);
|
|
1769
|
+
return candidates.some((cwd) => isProjectPath(projectRoot, cwd)) ? "local" : "remote";
|
|
1770
|
+
}
|
|
1771
|
+
function pendingRequests(value, fallbackKind) {
|
|
1772
|
+
if (!Array.isArray(value))
|
|
1773
|
+
return [];
|
|
1774
|
+
return value.flatMap((item) => {
|
|
1775
|
+
const record = objectRecord(item);
|
|
1776
|
+
const requestId = stringOrNull(record?.requestId) ?? stringOrNull(record?.id);
|
|
1777
|
+
if (!record || !requestId)
|
|
1778
|
+
return [];
|
|
1779
|
+
return [{
|
|
1780
|
+
requestId,
|
|
1781
|
+
requestKind: stringOrNull(record.requestKind) ?? stringOrNull(record.kind) ?? fallbackKind,
|
|
1782
|
+
actionId: stringOrNull(record.actionId),
|
|
1783
|
+
payload: "payload" in record ? record.payload : record,
|
|
1784
|
+
requestedAt: stringOrNull(record.requestedAt) ?? stringOrNull(record.at) ?? ""
|
|
1785
|
+
}];
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
function emptyFoldedProjection(runId, projection) {
|
|
1789
|
+
const projectionRecord = projection;
|
|
1790
|
+
const nestedProjection = objectRecord(projectionRecord.projection);
|
|
1791
|
+
const pendingApprovals = pendingRequests(nestedProjection?.pendingApprovals ?? projectionRecord.pendingApprovals, "approval");
|
|
1792
|
+
const pendingUserInputs = pendingRequests(nestedProjection?.pendingUserInputs ?? nestedProjection?.pendingInputs ?? projectionRecord.pendingUserInputs ?? (Array.isArray(projectionRecord.pendingInputs) ? projectionRecord.pendingInputs : undefined), "user-input");
|
|
1793
|
+
const nestedRecord = objectRecord(nestedProjection?.record);
|
|
1794
|
+
const nestedFolded = nestedProjection;
|
|
1795
|
+
return {
|
|
1796
|
+
...EMPTY_PROJECTION,
|
|
1797
|
+
...nestedFolded ?? {},
|
|
1798
|
+
record: {
|
|
1799
|
+
...nestedRecord ?? {},
|
|
1800
|
+
runId,
|
|
1801
|
+
...projection.taskId ? { taskId: projection.taskId } : {},
|
|
1802
|
+
...projection.title ? { title: projection.title } : {},
|
|
1803
|
+
...projection.startedAt ? { startedAt: projection.startedAt } : {},
|
|
1804
|
+
...projection.updatedAt ? { updatedAt: projection.updatedAt } : {},
|
|
1805
|
+
...projection.completedAt ? { completedAt: projection.completedAt } : {},
|
|
1806
|
+
...projection.sessionPath ? { sessionPath: projection.sessionPath } : {},
|
|
1807
|
+
...projection.worktreePath ? { worktreePath: projection.worktreePath } : {},
|
|
1808
|
+
...projection.prUrl ? { prUrl: projection.prUrl } : {}
|
|
1809
|
+
},
|
|
1810
|
+
status: registryStatusAsRunStatus(projection.status) ?? registryStatusAsRunStatus(nestedProjection?.status),
|
|
1811
|
+
pendingApprovals,
|
|
1812
|
+
pendingUserInputs,
|
|
1813
|
+
steeringCount: projection.steeringCount ?? numberOrNull(nestedProjection?.steeringCount) ?? 0,
|
|
1814
|
+
stallCount: projection.stallCount ?? numberOrNull(nestedProjection?.stallCount) ?? 0,
|
|
1815
|
+
lastEventAt: projection.updatedAt ?? stringOrNull(nestedProjection?.lastEventAt)
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
function runRecordFromRegistryEntry(projectRoot, entry) {
|
|
1819
|
+
const projection = entry.projection && typeof entry.projection === "object" ? entry.projection : {};
|
|
1820
|
+
const runId = stringOrNull(projection.runId) ?? entry.roomId;
|
|
1821
|
+
const folded = emptyFoldedProjection(runId, projection);
|
|
1822
|
+
const pushedStatus = registryStatusAsRunStatus(projection.status) ?? folded.status ?? registryStatusAsRunStatus(entry.status);
|
|
1823
|
+
const status = entry.stale ? pushedStatus && isTerminalRunStatus(pushedStatus) ? pushedStatus : "stale" : pushedStatus ?? "running";
|
|
1824
|
+
const sessionPath = stringOrNull(projection.sessionPath) ?? stringOrNull(entry.sessionPath) ?? null;
|
|
1825
|
+
const collabCwd = stringOrNull(projection.collabCwd) ?? stringOrNull(projection.cwd) ?? stringOrNull(entry.cwd) ?? stringOrNull(projection.worktreePath);
|
|
1826
|
+
return {
|
|
1827
|
+
runId,
|
|
1828
|
+
taskId: stringOrNull(projection.taskId),
|
|
1829
|
+
title: stringOrNull(projection.title) ?? entry.title,
|
|
1830
|
+
status,
|
|
1831
|
+
source: sourceFromEntry(projectRoot, projection, entry),
|
|
1832
|
+
live: !entry.stale,
|
|
1833
|
+
stale: entry.stale,
|
|
1834
|
+
startedAt: stringOrNull(projection.startedAt) ?? entry.startedAt ?? null,
|
|
1835
|
+
updatedAt: stringOrNull(projection.updatedAt) ?? entry.heartbeatAt ?? null,
|
|
1836
|
+
completedAt: stringOrNull(projection.completedAt),
|
|
1837
|
+
joinLink: stringOrNull(projection.joinLink) ?? entry.joinLink ?? null,
|
|
1838
|
+
webLink: stringOrNull(projection.webLink) ?? entry.webLink ?? null,
|
|
1839
|
+
relayUrl: stringOrNull(projection.relayUrl) ?? entry.relayUrl ?? null,
|
|
1840
|
+
sessionPath,
|
|
1841
|
+
prUrl: stringOrNull(projection.prUrl),
|
|
1842
|
+
worktreePath: stringOrNull(projection.worktreePath) ?? collabCwd,
|
|
1843
|
+
pendingApprovals: numberOrNull(projection.pendingApprovals) ?? folded.pendingApprovals.length,
|
|
1844
|
+
pendingInputs: numberOrNull(projection.pendingInputs) ?? folded.pendingUserInputs.length,
|
|
1845
|
+
steeringCount: projection.steeringCount ?? 0,
|
|
1846
|
+
stallCount: projection.stallCount ?? 0,
|
|
1847
|
+
errorSummary: stringOrNull(projection.errorSummary) ?? summarizeRunError(folded),
|
|
1848
|
+
timeline: Array.isArray(projection.timeline) ? [...projection.timeline] : [...timelineEntriesFromCustomEntries([])],
|
|
1849
|
+
inbox: inboxFromProjection(folded),
|
|
1850
|
+
collabCwd: collabCwd ?? null,
|
|
1851
|
+
projection: folded
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
function runRecordsFromCollab(projectRoot, collabs) {
|
|
1855
|
+
return sortByRecency(collabs.map((collab) => runRecordFromRegistryEntry(projectRoot, registryEntryFromCollab(collab))).filter((record) => record !== null));
|
|
1856
|
+
}
|
|
1857
|
+
function sortByRecency(records) {
|
|
1858
|
+
return [...records].sort((a, b) => {
|
|
1859
|
+
const at = Date.parse(b.updatedAt ?? b.startedAt ?? "") || 0;
|
|
1860
|
+
const bt = Date.parse(a.updatedAt ?? a.startedAt ?? "") || 0;
|
|
1861
|
+
return at - bt;
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
function discoveryDiagnosticRunRecord(projectRoot, error) {
|
|
1865
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1866
|
+
const runId = DISCOVERY_DIAGNOSTIC_RUN_ID;
|
|
1867
|
+
const projection = {
|
|
1868
|
+
...EMPTY_PROJECTION,
|
|
1869
|
+
record: { runId, title: "Registry discovery unavailable" },
|
|
1870
|
+
status: "needs-attention",
|
|
1871
|
+
stallCount: 1
|
|
1872
|
+
};
|
|
1873
|
+
return {
|
|
1874
|
+
runId,
|
|
1875
|
+
taskId: null,
|
|
1876
|
+
title: "Registry discovery unavailable",
|
|
1877
|
+
status: "needs-attention",
|
|
1878
|
+
source: "remote",
|
|
1879
|
+
live: false,
|
|
1880
|
+
stale: true,
|
|
1881
|
+
startedAt: null,
|
|
1882
|
+
updatedAt: null,
|
|
1883
|
+
completedAt: null,
|
|
1884
|
+
joinLink: null,
|
|
1885
|
+
webLink: null,
|
|
1886
|
+
relayUrl: null,
|
|
1887
|
+
sessionPath: null,
|
|
1888
|
+
prUrl: null,
|
|
1889
|
+
worktreePath: null,
|
|
1890
|
+
pendingApprovals: 0,
|
|
1891
|
+
pendingInputs: 0,
|
|
1892
|
+
steeringCount: 0,
|
|
1893
|
+
stallCount: 1,
|
|
1894
|
+
errorSummary: `registry discovery unavailable: ${detail}`,
|
|
1895
|
+
timeline: [],
|
|
1896
|
+
inbox: [],
|
|
1897
|
+
collabCwd: null,
|
|
1898
|
+
projection
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
async function listRunProjections(projectRoot, filter = {}) {
|
|
1902
|
+
try {
|
|
1903
|
+
const discovery = await loadCapabilityForRoot2(projectRoot, RunDiscoveryCap);
|
|
1904
|
+
if (!discovery)
|
|
1905
|
+
return [discoveryDiagnosticRunRecord(projectRoot, "run discovery capability unavailable")];
|
|
1906
|
+
const collabs = await discovery.listActiveRunCollab(projectRoot, filter);
|
|
1907
|
+
return runRecordsFromCollab(projectRoot, collabs);
|
|
1908
|
+
} catch (error) {
|
|
1909
|
+
return [discoveryDiagnosticRunRecord(projectRoot, error)];
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
function selectRunProjection(runs, runId) {
|
|
1913
|
+
const exactRun = runs.find((run) => run.runId === runId);
|
|
1914
|
+
if (exactRun)
|
|
1915
|
+
return exactRun;
|
|
1916
|
+
const exactTask = runs.find((run) => run.taskId === runId);
|
|
1917
|
+
if (exactTask)
|
|
1918
|
+
return exactTask;
|
|
1919
|
+
const prefixMatches = runs.filter((run) => run.runId.startsWith(runId));
|
|
1920
|
+
if (prefixMatches.length === 1)
|
|
1921
|
+
return prefixMatches[0] ?? null;
|
|
1922
|
+
if (prefixMatches.length > 1) {
|
|
1923
|
+
const matches = prefixMatches.map((run) => run.runId).join(", ");
|
|
1924
|
+
throw new Error(`Ambiguous run id prefix "${runId}" matched ${prefixMatches.length} runs: ${matches}`);
|
|
1925
|
+
}
|
|
1926
|
+
return runs.find((run) => run.runId === DISCOVERY_DIAGNOSTIC_RUN_ID) ?? null;
|
|
1927
|
+
}
|
|
1928
|
+
async function getRunProjection(projectRoot, runId, filter = {}) {
|
|
1929
|
+
const runs = await listRunProjections(projectRoot, filter);
|
|
1930
|
+
return selectRunProjection(runs, runId);
|
|
1931
|
+
}
|
|
1932
|
+
async function resolveRunJoinTarget(projectRoot, runId, filter = {}) {
|
|
1933
|
+
const run = await getRunProjection(projectRoot, runId, filter);
|
|
1934
|
+
if (!run || !run.joinLink)
|
|
1935
|
+
return null;
|
|
1936
|
+
return { runId: run.runId, taskId: run.taskId, joinLink: run.joinLink, cwd: run.collabCwd ?? run.sessionPath, stale: run.stale };
|
|
1937
|
+
}
|
|
1938
|
+
var RunDiscoveryCap, EMPTY_PROJECTION, DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__", listRuns, getRun, resolveJoinTarget;
|
|
1939
|
+
var init_projection = __esm(() => {
|
|
1940
|
+
init_session_journal();
|
|
1941
|
+
init_run_status();
|
|
1942
|
+
RunDiscoveryCap = defineCapability3(RUN_DISCOVERY);
|
|
1943
|
+
EMPTY_PROJECTION = foldRunSessionEntries([], "");
|
|
1944
|
+
listRuns = listRunProjections;
|
|
1945
|
+
getRun = getRunProjection;
|
|
1946
|
+
resolveJoinTarget = resolveRunJoinTarget;
|
|
1947
|
+
});
|
|
1948
|
+
|
|
1949
|
+
// packages/run-worker/src/read-model-backend/control.ts
|
|
1950
|
+
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";
|
|
1951
|
+
function buildPauseSentinel(runId) {
|
|
1952
|
+
return `${RIG_PAUSE_SENTINEL2} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END2}`;
|
|
1953
|
+
}
|
|
1954
|
+
function buildResumeSentinel(runId) {
|
|
1955
|
+
return `${RIG_RESUME_SENTINEL2} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END2}`;
|
|
1956
|
+
}
|
|
1957
|
+
function buildStopSentinel(runId, reason) {
|
|
1958
|
+
return `${RIG_STOP_SENTINEL2} runId=${runId} reason=${reason} ${RIG_STOP_SENTINEL_END2}`;
|
|
1959
|
+
}
|
|
1960
|
+
async function loadCollabControlDeps() {
|
|
1961
|
+
const [{ importRoomKey, seal }, { COLLAB_PROTO, packEnvelope, parseCollabLink }] = await Promise.all([
|
|
1962
|
+
import("@oh-my-pi/pi-coding-agent/collab/crypto"),
|
|
1963
|
+
import("@oh-my-pi/pi-coding-agent/collab/protocol")
|
|
1964
|
+
]);
|
|
1965
|
+
return { importRoomKey, seal, COLLAB_PROTO, packEnvelope, parseCollabLink };
|
|
1966
|
+
}
|
|
1967
|
+
function collabControlFrames(runId, control) {
|
|
1968
|
+
switch (control.kind) {
|
|
1969
|
+
case "steer":
|
|
1970
|
+
return [{ t: "prompt", text: control.message }];
|
|
1971
|
+
case "resume":
|
|
1972
|
+
return [{ t: "prompt", text: `${buildResumeSentinel(runId)}
|
|
1973
|
+
Continue the run from where it paused.` }];
|
|
1974
|
+
case "pause":
|
|
1975
|
+
return [
|
|
1976
|
+
{ t: "prompt", text: `${buildPauseSentinel(runId)}
|
|
1977
|
+
Pause this run after the current interruption settles.` },
|
|
1978
|
+
{ t: "abort" }
|
|
1979
|
+
];
|
|
1980
|
+
case "stop":
|
|
1981
|
+
return [
|
|
1982
|
+
{ t: "prompt", text: `${buildStopSentinel(runId, control.reason)}
|
|
1983
|
+
Stop this run and do not continue after the interruption settles.` },
|
|
1984
|
+
{ t: "abort" }
|
|
1985
|
+
];
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
async function sendSealedCollabFrames(joinLink, frames, deps = {}) {
|
|
1989
|
+
const { importRoomKey, seal, COLLAB_PROTO, packEnvelope, parseCollabLink } = await loadCollabControlDeps();
|
|
1990
|
+
const parsed = parseCollabLink(joinLink);
|
|
1991
|
+
if ("error" in parsed)
|
|
1992
|
+
throw new Error(parsed.error);
|
|
1993
|
+
if (!parsed.writeToken)
|
|
1994
|
+
throw new Error("Run collab link is read-only; cannot send control.");
|
|
1995
|
+
const key = await importRoomKey(parsed.key);
|
|
1996
|
+
const WebSocketCtor = deps.WebSocket ?? WebSocket;
|
|
1997
|
+
const ws = new WebSocketCtor(`${parsed.wsUrl}?role=guest`);
|
|
1998
|
+
await new Promise((resolveOpen, rejectOpen) => {
|
|
1999
|
+
ws.addEventListener("open", () => resolveOpen(), { once: true });
|
|
2000
|
+
ws.addEventListener("error", () => rejectOpen(new Error("collab websocket error")), { once: true });
|
|
2001
|
+
});
|
|
2002
|
+
const hello = {
|
|
2003
|
+
t: "hello",
|
|
2004
|
+
proto: COLLAB_PROTO,
|
|
2005
|
+
name: "rig-operator",
|
|
2006
|
+
writeToken: Buffer.from(parsed.writeToken).toString("base64url")
|
|
2007
|
+
};
|
|
2008
|
+
ws.send(packEnvelope(0, await seal(key, hello)));
|
|
2009
|
+
for (const frame of frames)
|
|
2010
|
+
ws.send(packEnvelope(0, await seal(key, frame)));
|
|
2011
|
+
try {
|
|
2012
|
+
ws.close();
|
|
2013
|
+
} catch {}
|
|
2014
|
+
}
|
|
2015
|
+
async function deliverRemoteRunControl(projectRoot, target, control, deps = {}) {
|
|
2016
|
+
const record = typeof target === "string" ? await getRun(projectRoot, target) : target;
|
|
2017
|
+
const runId = typeof target === "string" ? target : target.runId;
|
|
2018
|
+
if (!record)
|
|
2019
|
+
throw new Error(`Run not found: ${runId}`);
|
|
2020
|
+
if (!record.joinLink)
|
|
2021
|
+
throw new Error(`Run ${record.runId} has no writable collab join link.`);
|
|
2022
|
+
await sendSealedCollabFrames(record.joinLink, collabControlFrames(record.runId, control), deps);
|
|
2023
|
+
}
|
|
2024
|
+
async function deliverRunControl(projectRoot, target, control, deps = {}) {
|
|
2025
|
+
const record = typeof target === "string" ? await (deps.getRun ?? getRun)(projectRoot, target) : target;
|
|
2026
|
+
const runId = typeof target === "string" ? target : target.runId;
|
|
2027
|
+
if (!record)
|
|
2028
|
+
throw new Error(`Run not found: ${runId}`);
|
|
2029
|
+
if (!record.joinLink)
|
|
2030
|
+
throw new Error(`Run ${record.runId} has no writable collab join link; attach interactively to ${control.kind}.`);
|
|
2031
|
+
try {
|
|
2032
|
+
await (deps.deliverRemote ?? deliverRemoteRunControl)(projectRoot, record, control);
|
|
2033
|
+
} catch (error) {
|
|
2034
|
+
const detail = error instanceof Error ? error.message : `Attach to ${control.kind} interactively: rig run attach ${record.runId}`;
|
|
2035
|
+
throw new Error(`Could not ${control.kind} run ${record.runId}. ${detail}`);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
var init_control = __esm(() => {
|
|
2039
|
+
init_projection();
|
|
2040
|
+
});
|
|
2041
|
+
|
|
2042
|
+
// packages/run-worker/src/read-model-backend/guard.ts
|
|
2043
|
+
var TERMINAL;
|
|
2044
|
+
var init_guard = __esm(() => {
|
|
2045
|
+
TERMINAL = new Set(["completed", "failed", "stopped"]);
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
// packages/run-worker/src/read-model-backend/inbox.ts
|
|
2049
|
+
import { RIG_CONTROL_SENTINEL_END as RIG_CONTROL_SENTINEL_END3, RIG_INBOX_RESOLUTION_SENTINEL as RIG_INBOX_RESOLUTION_SENTINEL2 } from "@rig/contracts";
|
|
2050
|
+
function buildInboxResolutionSentinel(runId, payload) {
|
|
2051
|
+
return `${RIG_INBOX_RESOLUTION_SENTINEL2} runId=${runId} data=${encodeURIComponent(JSON.stringify(payload))} requestedBy=operator ${RIG_CONTROL_SENTINEL_END3}`;
|
|
2052
|
+
}
|
|
2053
|
+
function promptFromPayload(payload, fallback) {
|
|
2054
|
+
if (payload && typeof payload === "object") {
|
|
2055
|
+
const record = payload;
|
|
2056
|
+
for (const key of ["title", "message", "reason", "prompt", "summary"]) {
|
|
2057
|
+
const value = record[key];
|
|
2058
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
2059
|
+
return value.trim();
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
return fallback;
|
|
2063
|
+
}
|
|
2064
|
+
function recordsFromRun(run, kind) {
|
|
2065
|
+
const pending = kind === "approvals" ? run.projection.pendingApprovals : run.projection.pendingUserInputs;
|
|
2066
|
+
return pending.map((request) => ({
|
|
2067
|
+
runId: run.runId,
|
|
2068
|
+
taskId: run.taskId,
|
|
2069
|
+
requestId: request.requestId,
|
|
2070
|
+
status: "pending",
|
|
2071
|
+
prompt: promptFromPayload(request.payload, kind === "approvals" ? "Approval requested" : "Input requested"),
|
|
2072
|
+
requestedAt: request.requestedAt,
|
|
2073
|
+
payload: request.payload,
|
|
2074
|
+
kind: kind === "approvals" ? "approval" : "input"
|
|
2075
|
+
}));
|
|
2076
|
+
}
|
|
2077
|
+
async function listInboxRecords(context, kind, filters = {}, deps = {}) {
|
|
2078
|
+
const projectRoot = typeof context === "string" ? context : context.projectRoot;
|
|
2079
|
+
const runs = await (deps.listRuns ?? listRuns)(projectRoot);
|
|
2080
|
+
const out = [];
|
|
2081
|
+
for (const run of runs) {
|
|
2082
|
+
if (filters.run && run.runId !== filters.run)
|
|
2083
|
+
continue;
|
|
2084
|
+
if (filters.task && run.taskId !== filters.task)
|
|
2085
|
+
continue;
|
|
2086
|
+
out.push(...recordsFromRun(run, kind));
|
|
2087
|
+
}
|
|
2088
|
+
return out;
|
|
2089
|
+
}
|
|
2090
|
+
function normalizedApprovalDecision(decision) {
|
|
2091
|
+
return decision === "approved" || decision === "approve" ? "approve" : "reject";
|
|
2092
|
+
}
|
|
2093
|
+
function inputAnswers(answer) {
|
|
2094
|
+
return typeof answer === "string" ? { answer } : answer;
|
|
2095
|
+
}
|
|
2096
|
+
function decisionText(runId, requestId, decision) {
|
|
2097
|
+
if (decision.kind === "approval") {
|
|
2098
|
+
const normalized = normalizedApprovalDecision(decision.decision);
|
|
2099
|
+
const label = normalized === "approve" ? "approved" : "rejected";
|
|
2100
|
+
return [
|
|
2101
|
+
buildInboxResolutionSentinel(runId, { kind: "approval", requestId, decision: normalized, note: decision.note ?? null }),
|
|
2102
|
+
`Rig inbox approval ${requestId} was ${label}.`,
|
|
2103
|
+
decision.note ? `Operator note: ${decision.note}` : null,
|
|
2104
|
+
"Continue using this approval decision."
|
|
2105
|
+
].filter((line) => Boolean(line)).join(`
|
|
2106
|
+
`);
|
|
2107
|
+
}
|
|
2108
|
+
const answers = inputAnswers(decision.answer);
|
|
2109
|
+
return [
|
|
2110
|
+
buildInboxResolutionSentinel(runId, { kind: "input", requestId, answers }),
|
|
2111
|
+
`Rig inbox input ${requestId} was answered with:`,
|
|
2112
|
+
JSON.stringify(answers),
|
|
2113
|
+
"Continue using this input answer."
|
|
2114
|
+
].join(`
|
|
2115
|
+
`);
|
|
2116
|
+
}
|
|
2117
|
+
async function resolveInboxRequest(projectRoot, runId, requestId, decision, deps = {}) {
|
|
2118
|
+
await (deps.deliverRunControl ?? deliverRunControl)(projectRoot, runId, { kind: "steer", message: decisionText(runId, requestId, decision) });
|
|
2119
|
+
}
|
|
2120
|
+
var init_inbox = __esm(() => {
|
|
2121
|
+
init_control();
|
|
2122
|
+
init_projection();
|
|
2123
|
+
});
|
|
2124
|
+
|
|
2125
|
+
// packages/run-worker/src/read-model-backend/inspect.ts
|
|
2126
|
+
import { RIG_RUN_LOG_ENTRY } from "@rig/contracts";
|
|
2127
|
+
function asRecord(value) {
|
|
2128
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2129
|
+
}
|
|
2130
|
+
function firstString(record, keys) {
|
|
2131
|
+
for (const key of keys) {
|
|
2132
|
+
const value = record[key];
|
|
2133
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
2134
|
+
return value;
|
|
2135
|
+
}
|
|
2136
|
+
return null;
|
|
2137
|
+
}
|
|
2138
|
+
function stringifyLogPayload(value) {
|
|
2139
|
+
if (typeof value === "string")
|
|
2140
|
+
return value;
|
|
2141
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
2142
|
+
return String(value);
|
|
2143
|
+
const record = asRecord(value);
|
|
2144
|
+
if (!record)
|
|
2145
|
+
return null;
|
|
2146
|
+
const direct = firstString(record, ["line", "message", "text", "content", "output", "summary", "detail"]);
|
|
2147
|
+
if (direct)
|
|
2148
|
+
return direct;
|
|
2149
|
+
try {
|
|
2150
|
+
return JSON.stringify(record);
|
|
2151
|
+
} catch {
|
|
2152
|
+
return String(record);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
function logLineFromValue(value) {
|
|
2156
|
+
const record = asRecord(value);
|
|
2157
|
+
if (!record)
|
|
2158
|
+
return stringifyLogPayload(value);
|
|
2159
|
+
const direct = firstString(record, ["line", "message", "text", "content", "output", "summary", "detail"]);
|
|
2160
|
+
if (direct)
|
|
2161
|
+
return direct;
|
|
2162
|
+
if ("payload" in record)
|
|
2163
|
+
return stringifyLogPayload(record.payload);
|
|
2164
|
+
if ("data" in record)
|
|
2165
|
+
return stringifyLogPayload(record.data);
|
|
2166
|
+
return stringifyLogPayload(record);
|
|
2167
|
+
}
|
|
2168
|
+
function logLineFromSessionEntry(entry) {
|
|
2169
|
+
if (entry.type !== "custom" || entry.customType !== RIG_RUN_LOG_ENTRY)
|
|
2170
|
+
return null;
|
|
2171
|
+
return logLineFromValue(entry.data);
|
|
2172
|
+
}
|
|
2173
|
+
function logLinesFromProjection(projection) {
|
|
2174
|
+
const projected = projection;
|
|
2175
|
+
const lines = [];
|
|
2176
|
+
for (const key of ["logs", "logEntries", "journalLogs", "runLogs"]) {
|
|
2177
|
+
const entries = projected[key];
|
|
2178
|
+
if (!Array.isArray(entries))
|
|
2179
|
+
continue;
|
|
2180
|
+
for (const entry of entries) {
|
|
2181
|
+
const line = logLineFromValue(entry);
|
|
2182
|
+
if (line !== null)
|
|
2183
|
+
lines.push(line);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
return lines;
|
|
2187
|
+
}
|
|
2188
|
+
function extractRunLogs(run, deps = {}) {
|
|
2189
|
+
const projectedLines = logLinesFromProjection(run.projection);
|
|
2190
|
+
if (projectedLines.length > 0)
|
|
2191
|
+
return projectedLines;
|
|
2192
|
+
return (deps.readSessionRunEntries ?? readSessionRunEntries)(run.sessionPath).map(logLineFromSessionEntry).filter((line) => line !== null);
|
|
2193
|
+
}
|
|
2194
|
+
function summarizeRunFailures(run) {
|
|
2195
|
+
const failures = [];
|
|
2196
|
+
const useful = summarizeRunError(run.projection);
|
|
2197
|
+
if (useful)
|
|
2198
|
+
failures.push(`${run.runId}: ${useful}`);
|
|
2199
|
+
if (run.status === "failed" || run.projection.status === "failed")
|
|
2200
|
+
failures.push(`${run.runId}: run status failed`);
|
|
2201
|
+
for (const phase of run.projection.closeoutPhases) {
|
|
2202
|
+
if (phase.outcome === "failed")
|
|
2203
|
+
failures.push(`${run.runId}: closeout ${phase.phase} failed${phase.detail ? ` \u2014 ${phase.detail}` : ""}`);
|
|
2204
|
+
}
|
|
2205
|
+
for (const anomaly of run.projection.anomalies) {
|
|
2206
|
+
failures.push(`${run.runId}: anomaly ${anomaly.kind}${anomaly.detail ? ` \u2014 ${anomaly.detail}` : ""}`);
|
|
2207
|
+
}
|
|
2208
|
+
return failures;
|
|
2209
|
+
}
|
|
2210
|
+
function runInspectSummaryRows(runs, options = {}) {
|
|
2211
|
+
const attachable = runs.filter((run) => Boolean(run.joinLink && !run.stale)).length;
|
|
2212
|
+
const pendingInbox = options.pendingInbox ?? runs.reduce((total, run) => total + run.pendingApprovals + run.pendingInputs, 0);
|
|
2213
|
+
return [
|
|
2214
|
+
{ id: "title", label: "Inspect", currentValue: "runtime", heading: true, description: "session/runtime inspection rows from @rig/client" },
|
|
2215
|
+
{ id: "inspect:sessions", label: "RUNS", currentValue: String(runs.length), heading: true, description: `${attachable} attachable OMP collab links` },
|
|
2216
|
+
{ id: "inspect:cwd", label: "PROJECT ROOT", currentValue: options.projectRoot ?? "", heading: true, description: "selected target/project root used by Rig chrome" },
|
|
2217
|
+
{ id: "inspect:inbox", label: "INBOX", currentValue: String(pendingInbox), values: ["open"], description: "pending run gates; open Inbox to resolve" },
|
|
2218
|
+
{ id: "inspect:runs", label: "RUNS", currentValue: String(runs.length), values: ["open"], description: "open Runs to inspect individual run detail, logs, and attach controls" },
|
|
2219
|
+
{ id: "inspect:audit", label: "AUDIT", currentValue: "unavailable", heading: true, description: "CLI inspect audit has no cockpit equivalent; use rig inspect audit" }
|
|
2220
|
+
];
|
|
2221
|
+
}
|
|
2222
|
+
var init_inspect = __esm(() => {
|
|
2223
|
+
init_projection();
|
|
2224
|
+
});
|
|
2225
|
+
|
|
2226
|
+
// packages/run-worker/src/read-model-backend/stats.ts
|
|
2227
|
+
function parseTimestamp(value) {
|
|
2228
|
+
if (!value)
|
|
2229
|
+
return null;
|
|
2230
|
+
const parsed = Date.parse(value);
|
|
2231
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
2232
|
+
}
|
|
2233
|
+
function median(values) {
|
|
2234
|
+
if (values.length === 0)
|
|
2235
|
+
return null;
|
|
2236
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
2237
|
+
const middle = Math.floor(sorted.length / 2);
|
|
2238
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
2239
|
+
}
|
|
2240
|
+
function rate(part, total) {
|
|
2241
|
+
return total === 0 ? null : part / total;
|
|
2242
|
+
}
|
|
2243
|
+
function completedDuration(run) {
|
|
2244
|
+
if (run.status !== "completed")
|
|
2245
|
+
return null;
|
|
2246
|
+
const startedAt = parseTimestamp(run.startedAt);
|
|
2247
|
+
const completedAt = parseTimestamp(run.completedAt);
|
|
2248
|
+
if (startedAt === null || completedAt === null || completedAt < startedAt)
|
|
2249
|
+
return null;
|
|
2250
|
+
return completedAt - startedAt;
|
|
2251
|
+
}
|
|
2252
|
+
async function computeStats(projectRootOrRuns, options = {}) {
|
|
2253
|
+
const sinceMs = options.since ? options.since.getTime() : null;
|
|
2254
|
+
const allRuns = typeof projectRootOrRuns === "string" ? await (options.listRuns ?? (await Promise.resolve().then(() => (init_projection(), exports_projection))).listRuns)(projectRootOrRuns) : projectRootOrRuns;
|
|
2255
|
+
const runs = allRuns.filter((run) => {
|
|
2256
|
+
if (sinceMs === null)
|
|
2257
|
+
return true;
|
|
2258
|
+
const startedAt = parseTimestamp(run.startedAt);
|
|
2259
|
+
return startedAt !== null && startedAt >= sinceMs;
|
|
2260
|
+
});
|
|
2261
|
+
const statusCounts = {};
|
|
2262
|
+
const completionDurations = [];
|
|
2263
|
+
let completedRuns = 0;
|
|
2264
|
+
let failedRuns = 0;
|
|
2265
|
+
let needsAttentionRuns = 0;
|
|
2266
|
+
let steeringTotal = 0;
|
|
2267
|
+
let stallTotal = 0;
|
|
2268
|
+
let approvalsPending = 0;
|
|
2269
|
+
for (const run of runs) {
|
|
2270
|
+
const status = run.status || "unknown";
|
|
2271
|
+
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
2272
|
+
if (status === "completed")
|
|
2273
|
+
completedRuns += 1;
|
|
2274
|
+
if (status === "failed")
|
|
2275
|
+
failedRuns += 1;
|
|
2276
|
+
if (isNeedsAttention(run))
|
|
2277
|
+
needsAttentionRuns += 1;
|
|
2278
|
+
const duration = completedDuration(run);
|
|
2279
|
+
if (duration !== null)
|
|
2280
|
+
completionDurations.push(duration);
|
|
2281
|
+
steeringTotal += run.steeringCount;
|
|
2282
|
+
stallTotal += run.stallCount;
|
|
2283
|
+
approvalsPending += run.pendingApprovals;
|
|
2284
|
+
}
|
|
2285
|
+
const totalRuns = runs.length;
|
|
2286
|
+
return {
|
|
2287
|
+
since: options.since ? options.since.toISOString() : null,
|
|
2288
|
+
totalRuns,
|
|
2289
|
+
statusCounts,
|
|
2290
|
+
completedRuns,
|
|
2291
|
+
failedRuns,
|
|
2292
|
+
needsAttentionRuns,
|
|
2293
|
+
completionRate: rate(completedRuns, totalRuns),
|
|
2294
|
+
failureRate: rate(failedRuns, totalRuns),
|
|
2295
|
+
needsAttentionRate: rate(needsAttentionRuns, totalRuns),
|
|
2296
|
+
medianCompletionMs: median(completionDurations),
|
|
2297
|
+
steeringTotal,
|
|
2298
|
+
steeringPerRun: totalRuns === 0 ? null : steeringTotal / totalRuns,
|
|
2299
|
+
stallTotal,
|
|
2300
|
+
approvalsRequested: approvalsPending,
|
|
2301
|
+
approvalsApproved: 0,
|
|
2302
|
+
approvalsRejected: 0,
|
|
2303
|
+
approvalsPending
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
var init_stats = __esm(() => {
|
|
2307
|
+
init_run_status();
|
|
2308
|
+
init_run_status();
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
// packages/run-worker/src/read-model-backend/index.ts
|
|
2312
|
+
var init_read_model_backend = __esm(() => {
|
|
2313
|
+
init_projection();
|
|
2314
|
+
init_control();
|
|
2315
|
+
init_guard();
|
|
2316
|
+
init_inbox();
|
|
2317
|
+
init_inspect();
|
|
2318
|
+
init_run_status();
|
|
2319
|
+
init_stats();
|
|
2320
|
+
});
|
|
2321
|
+
|
|
2322
|
+
// packages/run-worker/src/read-model-service.ts
|
|
2323
|
+
var exports_read_model_service = {};
|
|
2324
|
+
__export(exports_read_model_service, {
|
|
2325
|
+
runReadModelService: () => runReadModelService
|
|
2326
|
+
});
|
|
2327
|
+
import { RUN_REGISTRY_BACKBONE as RUN_REGISTRY_BACKBONE2 } from "@rig/contracts";
|
|
2328
|
+
import { loadCapabilityForRoot as loadCapabilityForRoot3 } from "@rig/core/capability-loaders";
|
|
2329
|
+
import { defineCapability as defineCapability4 } from "@rig/core/capability";
|
|
2330
|
+
import { resolveOwnerNamespaceKey as resolveOwnerNamespaceKey2, resolveRegistryBaseUrl as resolveRegistryBaseUrl2 } from "@rig/core/remote-config";
|
|
2331
|
+
function discoveryFilter(filter) {
|
|
2332
|
+
return filter ?? {};
|
|
2333
|
+
}
|
|
2334
|
+
function requestedKinds(kind) {
|
|
2335
|
+
if (kind === "approval")
|
|
2336
|
+
return ["approvals"];
|
|
2337
|
+
if (kind === "input")
|
|
2338
|
+
return ["inputs"];
|
|
2339
|
+
return ["approvals", "inputs"];
|
|
2340
|
+
}
|
|
2341
|
+
function sourceMatches(run, source) {
|
|
2342
|
+
return !source || source === "all" || run.source === source;
|
|
2343
|
+
}
|
|
2344
|
+
function failureSummaries(run) {
|
|
2345
|
+
return summarizeRunFailures(run).map((summary) => ({ kind: "run", summary }));
|
|
2346
|
+
}
|
|
2347
|
+
function inboxRecord(record) {
|
|
2348
|
+
return {
|
|
2349
|
+
runId: record.runId,
|
|
2350
|
+
taskId: record.taskId,
|
|
2351
|
+
requestId: record.requestId,
|
|
2352
|
+
kind: record.kind,
|
|
2353
|
+
status: record.status,
|
|
2354
|
+
title: record.prompt,
|
|
2355
|
+
prompt: record.prompt,
|
|
2356
|
+
requestedAt: record.requestedAt,
|
|
2357
|
+
payload: record.payload,
|
|
2358
|
+
source: "run"
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
function inboxDecision(resolution) {
|
|
2362
|
+
if (resolution.kind === "approval") {
|
|
2363
|
+
return { kind: "approval", decision: resolution.decision, ...resolution.note === undefined ? {} : { note: resolution.note } };
|
|
2364
|
+
}
|
|
2365
|
+
return { kind: "input", answer: resolution.answers };
|
|
2366
|
+
}
|
|
2367
|
+
async function listedRuns(input) {
|
|
2368
|
+
const runs = await listRuns(input.projectRoot, discoveryFilter(input.discoveryFilter));
|
|
2369
|
+
return runs.filter((run) => {
|
|
2370
|
+
if (input.taskId && run.taskId !== input.taskId && run.runId !== input.taskId)
|
|
2371
|
+
return false;
|
|
2372
|
+
return sourceMatches(run, input.source);
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2375
|
+
async function selectedRun(input) {
|
|
2376
|
+
return getRun(input.projectRoot, input.id, discoveryFilter(input.discoveryFilter));
|
|
2377
|
+
}
|
|
2378
|
+
async function readDetails(input) {
|
|
2379
|
+
const run = await selectedRun(input);
|
|
2380
|
+
if (!run)
|
|
2381
|
+
return null;
|
|
2382
|
+
const pendingInbox = run.inbox;
|
|
2383
|
+
const inboxCounts = {
|
|
2384
|
+
approvals: run.pendingApprovals,
|
|
2385
|
+
inputs: run.pendingInputs,
|
|
2386
|
+
total: run.pendingApprovals + run.pendingInputs
|
|
2387
|
+
};
|
|
2388
|
+
const sessionEntries = run.sessionPath ? readSessionRunEntries(run.sessionPath) : undefined;
|
|
2389
|
+
return {
|
|
2390
|
+
run,
|
|
2391
|
+
projection: run.projection,
|
|
2392
|
+
pendingInbox,
|
|
2393
|
+
inboxCounts,
|
|
2394
|
+
sourceTask: null,
|
|
2395
|
+
...sessionEntries ? { sessionEntries } : {},
|
|
2396
|
+
failures: failureSummaries(run),
|
|
2397
|
+
logs: input.includeLogs ? extractRunLogs(run) : []
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
async function inboxRecords(input) {
|
|
2401
|
+
const out = [];
|
|
2402
|
+
for (const kind of requestedKinds(input.kind)) {
|
|
2403
|
+
const records = await listInboxRecords({ projectRoot: input.projectRoot }, kind, { ...input.runId ? { run: input.runId } : {}, ...input.taskId ? { task: input.taskId } : {} }, { listRuns: (projectRoot) => listedRuns({ projectRoot, discoveryFilter: input.discoveryFilter }) });
|
|
2404
|
+
out.push(...records.map(inboxRecord));
|
|
2405
|
+
}
|
|
2406
|
+
return out;
|
|
2407
|
+
}
|
|
2408
|
+
function classify(run) {
|
|
2409
|
+
const classification = classifyRun(run);
|
|
2410
|
+
return {
|
|
2411
|
+
...classification,
|
|
2412
|
+
role: runStatusColorRole(run),
|
|
2413
|
+
rank: statusRank(run)
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
async function controlTarget(input) {
|
|
2417
|
+
const run = await selectedRun({ projectRoot: input.projectRoot, id: input.runId, discoveryFilter: input.discoveryFilter });
|
|
2418
|
+
if (!run)
|
|
2419
|
+
return null;
|
|
2420
|
+
const canDeliver = Boolean(run.joinLink && !run.stale);
|
|
2421
|
+
return {
|
|
2422
|
+
runId: run.runId,
|
|
2423
|
+
taskId: run.taskId,
|
|
2424
|
+
sessionId: run.runId,
|
|
2425
|
+
sessionPath: run.sessionPath,
|
|
2426
|
+
joinLink: run.joinLink,
|
|
2427
|
+
webLink: run.webLink,
|
|
2428
|
+
relayUrl: run.relayUrl,
|
|
2429
|
+
collabCwd: run.collabCwd,
|
|
2430
|
+
live: run.live,
|
|
2431
|
+
stale: run.stale,
|
|
2432
|
+
canDeliver,
|
|
2433
|
+
reason: canDeliver ? null : run.stale ? "run is stale" : "run has no join link"
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
async function removeRegistryEntry(projectRoot, runId) {
|
|
2437
|
+
const namespaceKey = resolveOwnerNamespaceKey2(projectRoot);
|
|
2438
|
+
if (!namespaceKey)
|
|
2439
|
+
return false;
|
|
2440
|
+
try {
|
|
2441
|
+
const registryBackbone = await loadCapabilityForRoot3(projectRoot, RunRegistryBackboneCap2);
|
|
2442
|
+
if (!registryBackbone)
|
|
2443
|
+
return false;
|
|
2444
|
+
await registryBackbone.createRegistryClient({ baseUrl: resolveRegistryBaseUrl2(projectRoot), namespaceKey }).removeRoom(runId);
|
|
2445
|
+
return true;
|
|
2446
|
+
} catch {
|
|
2447
|
+
return false;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
var RunRegistryBackboneCap2, runReadModelService;
|
|
2451
|
+
var init_read_model_service = __esm(() => {
|
|
2452
|
+
init_read_model_backend();
|
|
2453
|
+
RunRegistryBackboneCap2 = defineCapability4(RUN_REGISTRY_BACKBONE2);
|
|
2454
|
+
runReadModelService = {
|
|
2455
|
+
async listRuns(input) {
|
|
2456
|
+
return listedRuns(input);
|
|
2457
|
+
},
|
|
2458
|
+
async getRun(input) {
|
|
2459
|
+
return selectedRun({ projectRoot: input.projectRoot, id: input.selector.id, discoveryFilter: input.discoveryFilter });
|
|
2460
|
+
},
|
|
2461
|
+
async getRunProjection(input) {
|
|
2462
|
+
const run = await selectedRun({ projectRoot: input.projectRoot, id: input.runId, discoveryFilter: input.discoveryFilter });
|
|
2463
|
+
return run?.projection ?? null;
|
|
2464
|
+
},
|
|
2465
|
+
async getRunDetails(input) {
|
|
2466
|
+
return readDetails({
|
|
2467
|
+
projectRoot: input.projectRoot,
|
|
2468
|
+
id: input.selector.id,
|
|
2469
|
+
includeLogs: input.includeLogs,
|
|
2470
|
+
discoveryFilter: input.discoveryFilter
|
|
2471
|
+
});
|
|
2472
|
+
},
|
|
2473
|
+
async inspectRun(input) {
|
|
2474
|
+
const details = await readDetails({
|
|
2475
|
+
projectRoot: input.projectRoot,
|
|
2476
|
+
id: input.selector.id,
|
|
2477
|
+
includeLogs: input.includeLogs,
|
|
2478
|
+
discoveryFilter: input.discoveryFilter
|
|
2479
|
+
});
|
|
2480
|
+
if (!details)
|
|
2481
|
+
return null;
|
|
2482
|
+
return {
|
|
2483
|
+
details,
|
|
2484
|
+
classification: classify(details.run),
|
|
2485
|
+
rows: runInspectSummaryRows([details.run], { projectRoot: input.projectRoot, pendingInbox: details.inboxCounts.total })
|
|
2486
|
+
};
|
|
2487
|
+
},
|
|
2488
|
+
listInboxRecords: inboxRecords,
|
|
2489
|
+
async getInboxCounts(input) {
|
|
2490
|
+
const records = await inboxRecords(input);
|
|
2491
|
+
const approvals = records.filter((record) => record.kind === "approval").length;
|
|
2492
|
+
const inputs = records.filter((record) => record.kind === "input").length;
|
|
2493
|
+
return { approvals, inputs, total: approvals + inputs };
|
|
2494
|
+
},
|
|
2495
|
+
async resolveInboxRequest(input) {
|
|
2496
|
+
const target = await controlTarget({ projectRoot: input.projectRoot, runId: input.runId, discoveryFilter: input.discoveryFilter });
|
|
2497
|
+
if (!target) {
|
|
2498
|
+
return {
|
|
2499
|
+
runId: input.runId,
|
|
2500
|
+
requestId: input.resolution.requestId,
|
|
2501
|
+
kind: input.resolution.kind,
|
|
2502
|
+
status: "not-found",
|
|
2503
|
+
delivered: false,
|
|
2504
|
+
detail: "run not found"
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
if (!target.canDeliver) {
|
|
2508
|
+
return {
|
|
2509
|
+
runId: target.runId,
|
|
2510
|
+
requestId: input.resolution.requestId,
|
|
2511
|
+
kind: input.resolution.kind,
|
|
2512
|
+
status: "not-live",
|
|
2513
|
+
delivered: false,
|
|
2514
|
+
detail: target.reason ?? "run is not deliverable"
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
await resolveInboxRequest(input.projectRoot, target.runId, input.resolution.requestId, inboxDecision(input.resolution));
|
|
2518
|
+
return {
|
|
2519
|
+
runId: target.runId,
|
|
2520
|
+
requestId: input.resolution.requestId,
|
|
2521
|
+
kind: input.resolution.kind,
|
|
2522
|
+
status: "resolved",
|
|
2523
|
+
delivered: true
|
|
2524
|
+
};
|
|
2525
|
+
},
|
|
2526
|
+
async getStats(input) {
|
|
2527
|
+
const since = input.since ? new Date(input.since) : null;
|
|
2528
|
+
const runs = input.runs ?? await listedRuns({ projectRoot: input.projectRoot, discoveryFilter: input.discoveryFilter });
|
|
2529
|
+
return computeStats(runs, { since });
|
|
2530
|
+
},
|
|
2531
|
+
async getInspectRows(input) {
|
|
2532
|
+
const runs = input.runs ?? await listedRuns({ projectRoot: input.projectRoot, discoveryFilter: input.discoveryFilter });
|
|
2533
|
+
return runInspectSummaryRows(runs, { projectRoot: input.projectRoot, pendingInbox: input.pendingInbox ?? null });
|
|
2534
|
+
},
|
|
2535
|
+
async deliverControl(input) {
|
|
2536
|
+
const run = await selectedRun({ projectRoot: input.projectRoot, id: input.runId, discoveryFilter: input.discoveryFilter });
|
|
2537
|
+
if (!run) {
|
|
2538
|
+
return {
|
|
2539
|
+
runId: input.runId,
|
|
2540
|
+
kind: input.control.kind,
|
|
2541
|
+
status: "not-found",
|
|
2542
|
+
delivered: false,
|
|
2543
|
+
detail: "run not found"
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2546
|
+
if (!run.joinLink) {
|
|
2547
|
+
return {
|
|
2548
|
+
runId: run.runId,
|
|
2549
|
+
kind: input.control.kind,
|
|
2550
|
+
status: "not-live",
|
|
2551
|
+
delivered: false,
|
|
2552
|
+
detail: `Run ${run.runId} has no writable collab join link.`
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
try {
|
|
2556
|
+
await deliverRunControl(input.projectRoot, run, input.control);
|
|
2557
|
+
return {
|
|
2558
|
+
runId: run.runId,
|
|
2559
|
+
kind: input.control.kind,
|
|
2560
|
+
status: "delivered",
|
|
2561
|
+
delivered: true
|
|
2562
|
+
};
|
|
2563
|
+
} catch (error) {
|
|
2564
|
+
return {
|
|
2565
|
+
runId: run.runId,
|
|
2566
|
+
kind: input.control.kind,
|
|
2567
|
+
status: "unsupported",
|
|
2568
|
+
delivered: false,
|
|
2569
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
},
|
|
2573
|
+
classifyRun: classify,
|
|
2574
|
+
resolveControlTarget: controlTarget,
|
|
2575
|
+
async removeRunRegistryEntry(input) {
|
|
2576
|
+
return { runId: input.runId, removed: await removeRegistryEntry(input.projectRoot, input.runId) };
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2579
|
+
});
|
|
2580
|
+
|
|
827
2581
|
// packages/run-worker/src/index.ts
|
|
828
2582
|
init_extension();
|
|
829
2583
|
|
|
830
2584
|
// packages/run-worker/src/plugin.ts
|
|
2585
|
+
import { defineCapability as defineCapability5 } from "@rig/core/capability";
|
|
831
2586
|
import { definePlugin } from "@rig/core/config";
|
|
2587
|
+
import { RUN_READ_MODEL } from "@rig/contracts";
|
|
2588
|
+
async function installWorkerInboxIfPresent(parentPort) {
|
|
2589
|
+
if (!parentPort)
|
|
2590
|
+
return;
|
|
2591
|
+
const { installWorkerInbox } = await import("@oh-my-pi/pi-utils/worker-host");
|
|
2592
|
+
installWorkerInbox(parentPort);
|
|
2593
|
+
}
|
|
2594
|
+
var RunReadModelCap = defineCapability5(RUN_READ_MODEL);
|
|
2595
|
+
var RUN_WORKER_SEED_ENTRYPOINTS = [
|
|
2596
|
+
{
|
|
2597
|
+
id: "@rig/run-worker:stats-sync-worker",
|
|
2598
|
+
workerArg: "__omp_worker_stats_sync",
|
|
2599
|
+
description: "OMP stats sync self-exec worker.",
|
|
2600
|
+
run: async () => {
|
|
2601
|
+
const scope = globalThis;
|
|
2602
|
+
const pending = [];
|
|
2603
|
+
const buffer = (event) => {
|
|
2604
|
+
pending.push(event);
|
|
2605
|
+
};
|
|
2606
|
+
scope.onmessage = buffer;
|
|
2607
|
+
await import("@oh-my-pi/omp-stats/sync-worker");
|
|
2608
|
+
const handler = scope.onmessage;
|
|
2609
|
+
if (handler && handler !== buffer) {
|
|
2610
|
+
for (const event of pending)
|
|
2611
|
+
handler.call(scope, event);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
},
|
|
2615
|
+
{
|
|
2616
|
+
id: "@rig/run-worker:tab-worker",
|
|
2617
|
+
workerArg: "__omp_worker_tab",
|
|
2618
|
+
description: "Browser tab tool self-exec worker.",
|
|
2619
|
+
run: async ({ parentPort }) => {
|
|
2620
|
+
await installWorkerInboxIfPresent(parentPort);
|
|
2621
|
+
await import("@oh-my-pi/pi-coding-agent/tools/browser/tab-worker-entry");
|
|
2622
|
+
}
|
|
2623
|
+
},
|
|
2624
|
+
{
|
|
2625
|
+
id: "@rig/run-worker:js-eval-worker",
|
|
2626
|
+
workerArg: "__omp_worker_js_eval",
|
|
2627
|
+
description: "JavaScript eval tool self-exec worker.",
|
|
2628
|
+
run: async ({ parentPort }) => {
|
|
2629
|
+
await installWorkerInboxIfPresent(parentPort);
|
|
2630
|
+
await import("@oh-my-pi/pi-coding-agent/eval/js/worker-entry");
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
];
|
|
832
2634
|
var runWorkerPlugin = definePlugin({
|
|
833
2635
|
name: "@rig/run-worker",
|
|
834
2636
|
version: "0.0.0-alpha.1",
|
|
835
2637
|
contributes: {
|
|
2638
|
+
capabilities: [
|
|
2639
|
+
RunReadModelCap.provide(async () => (await Promise.resolve().then(() => (init_read_model_service(), exports_read_model_service))).runReadModelService, {
|
|
2640
|
+
title: "Run read-model service",
|
|
2641
|
+
description: "List, inspect, summarize, and target canonical run session projections for operator surfaces."
|
|
2642
|
+
})
|
|
2643
|
+
],
|
|
836
2644
|
sessionExtensions: [
|
|
837
2645
|
{
|
|
838
2646
|
id: "rig:run-worker",
|
|
@@ -842,45 +2650,17 @@ var runWorkerPlugin = definePlugin({
|
|
|
842
2650
|
rigWorkerExtension2(api);
|
|
843
2651
|
}
|
|
844
2652
|
}
|
|
845
|
-
]
|
|
2653
|
+
],
|
|
2654
|
+
seedEntrypoints: RUN_WORKER_SEED_ENTRYPOINTS
|
|
846
2655
|
}
|
|
847
2656
|
});
|
|
848
2657
|
function createRunWorkerPlugin() {
|
|
849
2658
|
return runWorkerPlugin;
|
|
850
2659
|
}
|
|
851
|
-
|
|
852
|
-
// packages/run-worker/src/index.ts
|
|
853
|
-
init_autohost();
|
|
854
|
-
init_constants();
|
|
855
|
-
init_journal();
|
|
856
|
-
init_panel_plugin();
|
|
857
|
-
init_notifications();
|
|
858
|
-
init_stall();
|
|
859
2660
|
export {
|
|
860
|
-
timestampMs,
|
|
861
|
-
startRunProcessStallMonitor,
|
|
862
2661
|
runWorkerPlugin,
|
|
863
|
-
registryRunProjection,
|
|
864
|
-
reflectStoppedRunTaskSource,
|
|
865
|
-
produceWorkerPanelPayload,
|
|
866
|
-
maybeStartSpikeAutohost,
|
|
867
|
-
maybeStartRunProcessAutohost,
|
|
868
|
-
loadWorkerPanelRegistry,
|
|
869
|
-
dispatchRunNotifications,
|
|
870
|
-
detectRunControlText,
|
|
871
2662
|
rigWorkerExtension as default,
|
|
872
2663
|
createRunWorkerPlugin,
|
|
873
|
-
createRunJournal,
|
|
874
|
-
computeRunStall,
|
|
875
|
-
appendRunStallDetected,
|
|
876
2664
|
__rigRunWorkerTest,
|
|
877
|
-
__rigExtensionTest
|
|
878
|
-
TRACKED_RUN_STALL_MS,
|
|
879
|
-
RUN_PROCESS_STEER_TIMEOUT_MS,
|
|
880
|
-
RUN_PROCESS_STALL_SWEEP_MS,
|
|
881
|
-
RUN_PROCESS_STALL_DETAIL,
|
|
882
|
-
RIG_SUPERVISOR_PANEL_ID,
|
|
883
|
-
RIG_RUN_STOP_PANEL_ACTION,
|
|
884
|
-
RIG_PANELS_CUSTOM_MESSAGE_TYPE,
|
|
885
|
-
REGISTRY_PROJECTION_TIMELINE_LIMIT
|
|
2665
|
+
__rigExtensionTest
|
|
886
2666
|
};
|