@h-rig/run-plugin 0.0.6-alpha.186
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/README.md +1 -0
- package/dist/src/plugin.d.ts +4 -0
- package/dist/src/plugin.js +4319 -0
- package/dist/src/read-model/inspect-command.d.ts +19 -0
- package/dist/src/read-model/inspect-command.js +341 -0
- package/dist/src/read-model/plugin.d.ts +4 -0
- package/dist/src/read-model/plugin.js +2550 -0
- package/dist/src/read-model/read-model-backend/diagnostics.d.ts +9 -0
- package/dist/src/read-model/read-model-backend/diagnostics.js +51 -0
- package/dist/src/read-model/read-model-backend/guard.d.ts +4 -0
- package/dist/src/read-model/read-model-backend/guard.js +25 -0
- package/dist/src/read-model/read-model-backend/inbox.d.ts +23 -0
- package/dist/src/read-model/read-model-backend/inbox.js +669 -0
- package/dist/src/read-model/read-model-backend/index.d.ts +8 -0
- package/dist/src/read-model/read-model-backend/index.js +1163 -0
- package/dist/src/read-model/read-model-backend/inspect.d.ts +26 -0
- package/dist/src/read-model/read-model-backend/inspect.js +770 -0
- package/dist/src/read-model/read-model-backend/projection.d.ts +39 -0
- package/dist/src/read-model/read-model-backend/projection.js +669 -0
- package/dist/src/read-model/read-model-backend/run-status.d.ts +27 -0
- package/dist/src/read-model/read-model-backend/run-status.js +237 -0
- package/dist/src/read-model/read-model-backend/stats.d.ts +12 -0
- package/dist/src/read-model/read-model-backend/stats.js +800 -0
- package/dist/src/read-model/read-model-service.d.ts +2 -0
- package/dist/src/read-model/read-model-service.js +1725 -0
- package/dist/src/read-model/reconcile.d.ts +23 -0
- package/dist/src/read-model/reconcile.js +306 -0
- package/dist/src/read-model/run-format.d.ts +23 -0
- package/dist/src/read-model/run-format.js +202 -0
- package/dist/src/read-model/runs-screen.d.ts +14 -0
- package/dist/src/read-model/runs-screen.js +217 -0
- package/dist/src/read-model/session-journal.d.ts +26 -0
- package/dist/src/read-model/session-journal.js +355 -0
- package/dist/src/read-model/stats-command.d.ts +16 -0
- package/dist/src/read-model/stats-command.js +88 -0
- package/dist/src/read-model/stats-format.d.ts +1 -0
- package/dist/src/read-model/stats-format.js +8 -0
- package/dist/src/worker/autohost.d.ts +11 -0
- package/dist/src/worker/autohost.js +858 -0
- package/dist/src/worker/constants.d.ts +3 -0
- package/dist/src/worker/constants.js +10 -0
- package/dist/src/worker/extension.d.ts +14 -0
- package/dist/src/worker/extension.js +881 -0
- package/dist/src/worker/host.d.ts +1 -0
- package/dist/src/worker/host.js +1 -0
- package/dist/src/worker/inbox-command.d.ts +23 -0
- package/dist/src/worker/inbox-command.js +163 -0
- package/dist/src/worker/index.d.ts +2 -0
- package/dist/src/worker/index.js +1767 -0
- package/dist/src/worker/local-run-changes.d.ts +3 -0
- package/dist/src/worker/local-run-changes.js +65 -0
- package/dist/src/worker/notifications.d.ts +1 -0
- package/dist/src/worker/notifications.js +27 -0
- package/dist/src/worker/notify-cap.d.ts +11 -0
- package/dist/src/worker/notify-cap.js +13 -0
- package/dist/src/worker/panel-plugin.d.ts +11 -0
- package/dist/src/worker/panel-plugin.js +37 -0
- package/dist/src/worker/plugin.d.ts +3 -0
- package/dist/src/worker/plugin.js +1761 -0
- package/dist/src/worker/run-control-service.d.ts +2 -0
- package/dist/src/worker/run-control-service.js +210 -0
- package/dist/src/worker/session-journal-writer.d.ts +4 -0
- package/dist/src/worker/session-journal-writer.js +184 -0
- package/dist/src/worker/stall.d.ts +21 -0
- package/dist/src/worker/stall.js +55 -0
- package/dist/src/worker/utils.d.ts +21 -0
- package/dist/src/worker/utils.js +29 -0
- package/dist/src/worker/workflow-journal-writer.d.ts +7 -0
- package/dist/src/worker/workflow-journal-writer.js +76 -0
- package/package.json +47 -0
|
@@ -0,0 +1,4319 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
var __require = import.meta.require;
|
|
18
|
+
|
|
19
|
+
// packages/run-plugin/src/worker/inbox-command.ts
|
|
20
|
+
var exports_inbox_command = {};
|
|
21
|
+
__export(exports_inbox_command, {
|
|
22
|
+
readPendingInboxCounts: () => readPendingInboxCounts,
|
|
23
|
+
listInboxRecords: () => listInboxRecords,
|
|
24
|
+
listInbox: () => listInbox,
|
|
25
|
+
executeInbox: () => executeInbox
|
|
26
|
+
});
|
|
27
|
+
import { RUN_CONTROL, RUN_READ_MODEL } from "@rig/contracts";
|
|
28
|
+
import { defineCapability } from "@rig/core/capability";
|
|
29
|
+
import { requireCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
30
|
+
import { CliError, requireNoExtraArgs, takeOption } from "@rig/std-shared/cli-args";
|
|
31
|
+
import { printFormattedOutput } from "@rig/std-shared/cli-format";
|
|
32
|
+
async function loadRunControl(projectRoot) {
|
|
33
|
+
return requireCapabilityForRoot(projectRoot, RunControlCap, "RUN_CONTROL capability unavailable for this project root: load @rig/run-plugin (standard bundle).");
|
|
34
|
+
}
|
|
35
|
+
async function loadRunReadModel(projectRoot) {
|
|
36
|
+
return requireCapabilityForRoot(projectRoot, RunReadModelCap, "RUN_READ_MODEL capability unavailable for this project root: load @rig/run-plugin (standard bundle).");
|
|
37
|
+
}
|
|
38
|
+
function readModelKind(kind) {
|
|
39
|
+
return kind === "approvals" ? "approval" : "input";
|
|
40
|
+
}
|
|
41
|
+
async function listInboxRecords(context, kind, filters = {}, deps = {}) {
|
|
42
|
+
if (deps.listInbox)
|
|
43
|
+
return deps.listInbox(context.projectRoot, kind, filters);
|
|
44
|
+
const readModel = await loadRunReadModel(context.projectRoot);
|
|
45
|
+
return readModel.listInboxRecords({
|
|
46
|
+
projectRoot: context.projectRoot,
|
|
47
|
+
kind: readModelKind(kind),
|
|
48
|
+
...filters.run ? { runId: filters.run } : {},
|
|
49
|
+
...filters.task ? { taskId: filters.task } : {}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function listInbox(context, kind, filters = {}, deps = {}) {
|
|
53
|
+
return listInboxRecords(context, kind, filters, deps);
|
|
54
|
+
}
|
|
55
|
+
async function readPendingInboxCounts(context, deps = {}) {
|
|
56
|
+
if (deps.getInboxCounts)
|
|
57
|
+
return deps.getInboxCounts(context.projectRoot);
|
|
58
|
+
try {
|
|
59
|
+
const counts = await (await loadRunReadModel(context.projectRoot)).getInboxCounts({ projectRoot: context.projectRoot, kind: "all" });
|
|
60
|
+
return { approvals: counts.approvals, inputs: counts.inputs };
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function resolutionAttachError(runId, verb, error) {
|
|
66
|
+
const target = runId ?? "<run-id>";
|
|
67
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
68
|
+
throw new CliError(`rig inbox ${verb} could not deliver a resolution from this one-shot command.`, 2, {
|
|
69
|
+
hint: `${detail}
|
|
70
|
+
Attach to resolve interactively: rig run attach ${target}`
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function readInboxSnapshot(context, filters, deps) {
|
|
74
|
+
const [approvals, inputs] = await Promise.all([
|
|
75
|
+
listInbox(context, "approvals", filters, deps),
|
|
76
|
+
listInbox(context, "inputs", filters, deps)
|
|
77
|
+
]);
|
|
78
|
+
return { approvals, inputs };
|
|
79
|
+
}
|
|
80
|
+
function renderInboxSnapshot(readModel, snapshot) {
|
|
81
|
+
console.clear();
|
|
82
|
+
console.log(`rig inbox \u2014 watching (Ctrl-C to stop) \u2014 ${new Date().toLocaleTimeString()}`);
|
|
83
|
+
printFormattedOutput(readModel.formatInboxList("approvals", snapshot.approvals.map((entry) => ({ ...entry }))));
|
|
84
|
+
printFormattedOutput(readModel.formatInboxList("inputs", snapshot.inputs.map((entry) => ({ ...entry }))));
|
|
85
|
+
if (snapshot.approvals.length === 0 && snapshot.inputs.length === 0)
|
|
86
|
+
console.log("Nothing pending.");
|
|
87
|
+
}
|
|
88
|
+
function sleep(ms) {
|
|
89
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
90
|
+
setTimeout(resolve, ms);
|
|
91
|
+
return promise;
|
|
92
|
+
}
|
|
93
|
+
async function watchInbox(context, filters, deps) {
|
|
94
|
+
const snapshot = await readInboxSnapshot(context, filters, deps);
|
|
95
|
+
if (context.outputMode !== "text")
|
|
96
|
+
return { ok: true, group: "inbox", command: "watch", details: snapshot };
|
|
97
|
+
const readModel = await loadRunReadModel(context.projectRoot);
|
|
98
|
+
renderInboxSnapshot(readModel, snapshot);
|
|
99
|
+
while (true) {
|
|
100
|
+
await sleep(500);
|
|
101
|
+
renderInboxSnapshot(readModel, await readInboxSnapshot(context, filters, deps));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function deliverInboxResolution(context, runId, resolution, deps) {
|
|
105
|
+
const result = deps.resolveInboxRequest ? await deps.resolveInboxRequest(context.projectRoot, runId, resolution) : await (await loadRunControl(context.projectRoot)).resolveInboxRequest({ projectRoot: context.projectRoot, runId, resolution });
|
|
106
|
+
if (!result.delivered)
|
|
107
|
+
throw new Error(result.detail ?? `Inbox request ${resolution.requestId} was not delivered.`);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
async function executeInbox(context, args, deps = {}) {
|
|
111
|
+
const [command = "approvals", ...rest] = args;
|
|
112
|
+
const text = context.outputMode === "text";
|
|
113
|
+
switch (command) {
|
|
114
|
+
case "approvals":
|
|
115
|
+
case "inputs": {
|
|
116
|
+
const run = takeOption(rest, "--run");
|
|
117
|
+
const task = takeOption(run.rest, "--task");
|
|
118
|
+
requireNoExtraArgs(task.rest, `rig inbox ${command} [--run <id>] [--task <id>]`);
|
|
119
|
+
const kind = command === "approvals" ? "approvals" : "inputs";
|
|
120
|
+
const filters = { ...run.value ? { run: run.value } : {}, ...task.value ? { task: task.value } : {} };
|
|
121
|
+
const entries = await listInbox(context, kind, filters, deps);
|
|
122
|
+
if (text) {
|
|
123
|
+
const readModel = await loadRunReadModel(context.projectRoot);
|
|
124
|
+
printFormattedOutput(readModel.formatInboxList(kind, entries.map((entry) => ({ ...entry }))));
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, group: "inbox", command, details: { entries } };
|
|
127
|
+
}
|
|
128
|
+
case "approve": {
|
|
129
|
+
const run = takeOption(rest, "--run");
|
|
130
|
+
const request = takeOption(run.rest, "--request");
|
|
131
|
+
const decision = takeOption(request.rest, "--decision");
|
|
132
|
+
requireNoExtraArgs(decision.rest, "rig inbox approve --run <id> --request <id> --decision approve|reject");
|
|
133
|
+
if (!run.value || !request.value)
|
|
134
|
+
throw new CliError("rig inbox approve requires --run and --request.", 2);
|
|
135
|
+
const rawDecision = (decision.value ?? "approve").trim().toLowerCase();
|
|
136
|
+
if (rawDecision !== "approve" && rawDecision !== "approved" && rawDecision !== "reject" && rawDecision !== "rejected")
|
|
137
|
+
throw new CliError("--decision must be approve or reject.", 2);
|
|
138
|
+
const normalizedDecision = rawDecision === "approved" ? "approve" : rawDecision === "rejected" ? "reject" : rawDecision;
|
|
139
|
+
try {
|
|
140
|
+
await deliverInboxResolution(context, run.value, { kind: "approval", requestId: request.value, decision: normalizedDecision }, deps);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
resolutionAttachError(run.value, "approve", error);
|
|
143
|
+
}
|
|
144
|
+
if (text)
|
|
145
|
+
printFormattedOutput(`Resolved approval ${request.value} for run ${run.value}.`);
|
|
146
|
+
return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, decision: normalizedDecision } };
|
|
147
|
+
}
|
|
148
|
+
case "respond":
|
|
149
|
+
case "answer": {
|
|
150
|
+
const run = takeOption(rest, "--run");
|
|
151
|
+
const request = takeOption(run.rest, "--request");
|
|
152
|
+
const textResult = takeOption(request.rest, "--text");
|
|
153
|
+
const answerResult = takeOption(textResult.rest, "--answer");
|
|
154
|
+
requireNoExtraArgs(answerResult.rest, `rig inbox ${command} --run <id> --request <id> (--text|--answer) <answer>`);
|
|
155
|
+
if (textResult.value && answerResult.value)
|
|
156
|
+
throw new CliError("Pass only one of --text or --answer.", 2);
|
|
157
|
+
const answer = textResult.value ?? answerResult.value;
|
|
158
|
+
if (!run.value || !request.value || !answer)
|
|
159
|
+
resolutionAttachError(run.value, "respond", new Error(`rig inbox ${command} requires --run, --request, and --text/--answer.`));
|
|
160
|
+
try {
|
|
161
|
+
await deliverInboxResolution(context, run.value, { kind: "input", requestId: request.value, answers: { answer } }, deps);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
resolutionAttachError(run.value, "respond", error);
|
|
164
|
+
}
|
|
165
|
+
if (text)
|
|
166
|
+
printFormattedOutput(`Sent response ${request.value} for run ${run.value}.`);
|
|
167
|
+
return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, responded: true } };
|
|
168
|
+
}
|
|
169
|
+
case "watch": {
|
|
170
|
+
const run = takeOption(rest, "--run");
|
|
171
|
+
const task = takeOption(run.rest, "--task");
|
|
172
|
+
requireNoExtraArgs(task.rest, "rig inbox watch [--run <id>] [--task <id>]");
|
|
173
|
+
const filters = { ...run.value ? { run: run.value } : {}, ...task.value ? { task: task.value } : {} };
|
|
174
|
+
return watchInbox(context, filters, deps);
|
|
175
|
+
}
|
|
176
|
+
default:
|
|
177
|
+
throw new CliError(`Unknown inbox command: ${command}`, 1, { hint: "Run `rig inbox --help` to list inbox commands." });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
var RunControlCap, RunReadModelCap;
|
|
181
|
+
var init_inbox_command = __esm(() => {
|
|
182
|
+
RunControlCap = defineCapability(RUN_CONTROL);
|
|
183
|
+
RunReadModelCap = defineCapability(RUN_READ_MODEL);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// packages/run-plugin/src/worker/session-journal-writer.ts
|
|
187
|
+
var exports_session_journal_writer = {};
|
|
188
|
+
__export(exports_session_journal_writer, {
|
|
189
|
+
runSessionJournalWriterService: () => runSessionJournalWriterService,
|
|
190
|
+
reconcileDeadPid: () => reconcileDeadPid,
|
|
191
|
+
createRunJournal: () => createRunJournal
|
|
192
|
+
});
|
|
193
|
+
import { defineCapability as defineCapability2 } from "@rig/core/capability";
|
|
194
|
+
import { getInstalledCapability, installCapability, loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
195
|
+
import {
|
|
196
|
+
CUSTOM_TYPE_FOR,
|
|
197
|
+
RUN_SESSION_JOURNAL
|
|
198
|
+
} from "@rig/contracts";
|
|
199
|
+
function requireReadCodec() {
|
|
200
|
+
const codec = getInstalledCapability(RunSessionJournalCap);
|
|
201
|
+
if (!codec) {
|
|
202
|
+
throw new Error("RUN_SESSION_JOURNAL codec capability unavailable: the run-worker autohost / run-host preboot install it before binding the journal writer; ensure @rig/run-plugin (default bundle) is installed.");
|
|
203
|
+
}
|
|
204
|
+
return codec;
|
|
205
|
+
}
|
|
206
|
+
function asCustomEntries(entries) {
|
|
207
|
+
return entries.filter((entry) => Boolean(entry && typeof entry === "object" && entry.type === "custom"));
|
|
208
|
+
}
|
|
209
|
+
function dataRecord(entry) {
|
|
210
|
+
return entry.data && typeof entry.data === "object" && !Array.isArray(entry.data) ? entry.data : null;
|
|
211
|
+
}
|
|
212
|
+
function entryBelongsToRun(entry, runId, defaultRunId) {
|
|
213
|
+
const data = dataRecord(entry);
|
|
214
|
+
if (!data)
|
|
215
|
+
return runId === defaultRunId;
|
|
216
|
+
const candidate = data.runId;
|
|
217
|
+
return candidate === undefined ? runId === defaultRunId : candidate === runId;
|
|
218
|
+
}
|
|
219
|
+
function isRunJournalEventInit(event) {
|
|
220
|
+
if (event === null || typeof event !== "object")
|
|
221
|
+
return false;
|
|
222
|
+
const type = event.type;
|
|
223
|
+
return typeof type === "string" && Object.hasOwn(CUSTOM_TYPE_FOR, type);
|
|
224
|
+
}
|
|
225
|
+
function createJournalSessionProvider(options) {
|
|
226
|
+
const now = options.now ?? (() => new Date);
|
|
227
|
+
const appendRunEvent = async (event, runId = options.runId) => {
|
|
228
|
+
options.store.appendCustomEntry(CUSTOM_TYPE_FOR[event.type], { ...event, runId, at: now().toISOString() });
|
|
229
|
+
};
|
|
230
|
+
const readEntries = () => asCustomEntries(options.store.getEntries());
|
|
231
|
+
const entriesForRun = (runId) => readEntries().filter((entry) => entryBelongsToRun(entry, runId, options.runId));
|
|
232
|
+
const readProjection = () => {
|
|
233
|
+
const entries = entriesForRun(options.runId);
|
|
234
|
+
return requireReadCodec().projectRunFromSession(entries, options.runId);
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
runId: options.runId,
|
|
238
|
+
async append(event) {
|
|
239
|
+
if (isRunJournalEventInit(event))
|
|
240
|
+
await appendRunEvent(event);
|
|
241
|
+
},
|
|
242
|
+
appendRunEvent,
|
|
243
|
+
async recordPipeline(runId, pipeline) {
|
|
244
|
+
await appendRunEvent({ type: "pipeline-resolved", pipeline }, runId);
|
|
245
|
+
},
|
|
246
|
+
async recordStageOutcome(runId, outcome) {
|
|
247
|
+
await appendRunEvent({ type: "stage-outcome", outcome }, runId);
|
|
248
|
+
},
|
|
249
|
+
async read(runId) {
|
|
250
|
+
return entriesForRun(runId);
|
|
251
|
+
},
|
|
252
|
+
readEntries,
|
|
253
|
+
readProjection
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
class RunSessionJournal {
|
|
258
|
+
sm;
|
|
259
|
+
runId;
|
|
260
|
+
readProjection;
|
|
261
|
+
constructor(sm, runId, readProjection) {
|
|
262
|
+
this.sm = sm;
|
|
263
|
+
this.runId = runId;
|
|
264
|
+
this.readProjection = readProjection;
|
|
265
|
+
}
|
|
266
|
+
#append(init) {
|
|
267
|
+
this.sm.appendCustomEntry(CUSTOM_TYPE_FOR[init.type], { ...init, at: new Date().toISOString() });
|
|
268
|
+
}
|
|
269
|
+
appendStatus(to, opts = {}) {
|
|
270
|
+
const from = this.readProjection().status;
|
|
271
|
+
if (!opts.force)
|
|
272
|
+
requireReadCodec().assertRunStatusTransition(from, to);
|
|
273
|
+
if (opts.errorText !== undefined)
|
|
274
|
+
this.#append({ type: "record-patch", patch: { errorText: opts.errorText } });
|
|
275
|
+
this.#append({
|
|
276
|
+
type: "status-changed",
|
|
277
|
+
from,
|
|
278
|
+
to,
|
|
279
|
+
...opts.reason !== undefined ? { reason: opts.reason } : {},
|
|
280
|
+
...opts.actor !== undefined ? { actor: opts.actor } : {}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
appendTimeline(payload) {
|
|
284
|
+
this.#append({ type: "timeline-entry", payload });
|
|
285
|
+
}
|
|
286
|
+
appendCloseoutPhase(input) {
|
|
287
|
+
this.#append({
|
|
288
|
+
type: "closeout-phase",
|
|
289
|
+
phase: input.phase,
|
|
290
|
+
outcome: input.outcome,
|
|
291
|
+
...input.detail !== undefined ? { detail: input.detail } : {}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
appendApprovalResolved(input) {
|
|
295
|
+
this.#append({
|
|
296
|
+
type: "approval-resolved",
|
|
297
|
+
requestId: input.requestId,
|
|
298
|
+
decision: input.decision,
|
|
299
|
+
actor: input.actor,
|
|
300
|
+
...input.note !== undefined ? { note: input.note } : {}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
appendInputResolved(input) {
|
|
304
|
+
this.#append({ type: "input-resolved", requestId: input.requestId, answers: input.answers, actor: input.actor });
|
|
305
|
+
}
|
|
306
|
+
appendStall(input) {
|
|
307
|
+
this.#append({ type: "stall-detected", detail: input.detail });
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function createRunJournal(sessionManager, runId) {
|
|
311
|
+
try {
|
|
312
|
+
const writableSessionManager = sessionManager;
|
|
313
|
+
if (typeof writableSessionManager.appendCustomEntry !== "function")
|
|
314
|
+
return null;
|
|
315
|
+
const kernel = createJournalSessionProvider({
|
|
316
|
+
runId,
|
|
317
|
+
store: {
|
|
318
|
+
appendCustomEntry: (customType, data) => writableSessionManager.appendCustomEntry(customType, data),
|
|
319
|
+
getEntries: () => sessionManager.getEntries?.() ?? sessionManager.getBranch?.() ?? []
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
const journal = new RunSessionJournal(writableSessionManager, runId, kernel.readProjection);
|
|
323
|
+
return {
|
|
324
|
+
kernel,
|
|
325
|
+
appendStatus: journal.appendStatus.bind(journal),
|
|
326
|
+
appendTimeline: journal.appendTimeline.bind(journal),
|
|
327
|
+
appendCloseoutPhase: journal.appendCloseoutPhase.bind(journal),
|
|
328
|
+
appendApprovalResolved: journal.appendApprovalResolved.bind(journal),
|
|
329
|
+
appendInputResolved: journal.appendInputResolved.bind(journal),
|
|
330
|
+
appendStall: journal.appendStall.bind(journal)
|
|
331
|
+
};
|
|
332
|
+
} catch (error) {
|
|
333
|
+
console.warn(`[rig-run] RunSessionJournal writer unavailable; run-state arming deferred: ${error instanceof Error ? error.message : String(error)}`);
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function reconcileDeadPid(input) {
|
|
338
|
+
if (!input.sessionPath.trim()) {
|
|
339
|
+
return { runId: input.runId, updated: false, status: "not-found", detail: "session path missing" };
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
const codec = await loadCapabilityForRoot(input.projectRoot, RunSessionJournalCap);
|
|
343
|
+
if (!codec) {
|
|
344
|
+
return { runId: input.runId, updated: false, status: "unavailable", detail: "RUN_SESSION_JOURNAL codec capability unavailable" };
|
|
345
|
+
}
|
|
346
|
+
installCapability(RunSessionJournalCap, codec);
|
|
347
|
+
const { SessionManager } = await import("@oh-my-pi/pi-coding-agent/session/session-manager");
|
|
348
|
+
const session = await SessionManager.open(input.sessionPath, undefined, undefined, { suppressBreadcrumb: true });
|
|
349
|
+
const journal = await createRunJournal(session, input.runId);
|
|
350
|
+
if (!journal) {
|
|
351
|
+
return { runId: input.runId, updated: false, status: "not-found", detail: "run journal not found" };
|
|
352
|
+
}
|
|
353
|
+
journal.appendStatus("failed", {
|
|
354
|
+
reason: input.reason ?? "lazy-reconcile:dead-pid",
|
|
355
|
+
actor: { kind: "agent" },
|
|
356
|
+
force: true
|
|
357
|
+
});
|
|
358
|
+
return { runId: input.runId, updated: true, status: "reconciled" };
|
|
359
|
+
} catch (error) {
|
|
360
|
+
return { runId: input.runId, updated: false, status: "unavailable", detail: error instanceof Error ? error.message : String(error) };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
var RunSessionJournalCap, runSessionJournalWriterService;
|
|
364
|
+
var init_session_journal_writer = __esm(() => {
|
|
365
|
+
RunSessionJournalCap = defineCapability2(RUN_SESSION_JOURNAL);
|
|
366
|
+
runSessionJournalWriterService = {
|
|
367
|
+
createRunJournal,
|
|
368
|
+
reconcileDeadPid
|
|
369
|
+
};
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// packages/run-plugin/src/worker/workflow-journal-writer.ts
|
|
373
|
+
var exports_workflow_journal_writer = {};
|
|
374
|
+
__export(exports_workflow_journal_writer, {
|
|
375
|
+
workflowJournalWriterService: () => workflowJournalWriterService,
|
|
376
|
+
createWorkflowTaskSelected: () => createWorkflowTaskSelected,
|
|
377
|
+
createWorkflowTargetSelected: () => createWorkflowTargetSelected,
|
|
378
|
+
createWorkflowStatusChanged: () => createWorkflowStatusChanged,
|
|
379
|
+
createWorkflowStarted: () => createWorkflowStarted,
|
|
380
|
+
createWorkflowOperatorNote: () => createWorkflowOperatorNote
|
|
381
|
+
});
|
|
382
|
+
import {
|
|
383
|
+
RIG_WORKFLOW_OPERATOR_NOTE,
|
|
384
|
+
RIG_WORKFLOW_STARTED,
|
|
385
|
+
RIG_WORKFLOW_STATUS_CHANGED,
|
|
386
|
+
RIG_WORKFLOW_TARGET_SELECTED,
|
|
387
|
+
RIG_WORKFLOW_TASK_SELECTED
|
|
388
|
+
} from "@rig/contracts";
|
|
389
|
+
function createWorkflowStarted(input) {
|
|
390
|
+
return {
|
|
391
|
+
schemaVersion: 1,
|
|
392
|
+
workflowId: input.workflowId,
|
|
393
|
+
target: input.target,
|
|
394
|
+
selectedRepo: input.selectedRepo,
|
|
395
|
+
owner: input.owner,
|
|
396
|
+
createdAt: input.createdAt ?? new Date().toISOString()
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function createWorkflowTargetSelected(input) {
|
|
400
|
+
return {
|
|
401
|
+
schemaVersion: 1,
|
|
402
|
+
target: input.target,
|
|
403
|
+
...input.reason ? { reason: input.reason } : {},
|
|
404
|
+
selectedAt: input.selectedAt ?? new Date().toISOString()
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function createWorkflowTaskSelected(input) {
|
|
408
|
+
return {
|
|
409
|
+
schemaVersion: 1,
|
|
410
|
+
taskId: input.taskId,
|
|
411
|
+
...input.title ? { title: input.title } : {},
|
|
412
|
+
selectedAt: input.selectedAt ?? new Date().toISOString()
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function createWorkflowStatusChanged(input) {
|
|
416
|
+
return {
|
|
417
|
+
schemaVersion: 1,
|
|
418
|
+
status: input.status,
|
|
419
|
+
...input.detail ? { detail: input.detail } : {},
|
|
420
|
+
changedAt: input.changedAt ?? new Date().toISOString()
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function createWorkflowOperatorNote(input) {
|
|
424
|
+
return {
|
|
425
|
+
schemaVersion: 1,
|
|
426
|
+
...input.noteId ? { noteId: input.noteId } : {},
|
|
427
|
+
note: input.note,
|
|
428
|
+
notedAt: input.notedAt ?? new Date().toISOString()
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
var workflowJournalWriterService;
|
|
432
|
+
var init_workflow_journal_writer = __esm(() => {
|
|
433
|
+
workflowJournalWriterService = {
|
|
434
|
+
appendStarted(appender, input) {
|
|
435
|
+
appender.appendEntry(RIG_WORKFLOW_STARTED, createWorkflowStarted(input));
|
|
436
|
+
},
|
|
437
|
+
appendTargetSelected(appender, input) {
|
|
438
|
+
appender.appendEntry(RIG_WORKFLOW_TARGET_SELECTED, createWorkflowTargetSelected(input));
|
|
439
|
+
},
|
|
440
|
+
appendTaskSelected(appender, input) {
|
|
441
|
+
appender.appendEntry(RIG_WORKFLOW_TASK_SELECTED, createWorkflowTaskSelected(input));
|
|
442
|
+
},
|
|
443
|
+
appendStatusChanged(appender, input) {
|
|
444
|
+
appender.appendEntry(RIG_WORKFLOW_STATUS_CHANGED, createWorkflowStatusChanged(input));
|
|
445
|
+
},
|
|
446
|
+
appendOperatorNote(appender, input) {
|
|
447
|
+
appender.appendEntry(RIG_WORKFLOW_OPERATOR_NOTE, createWorkflowOperatorNote(input));
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// packages/run-plugin/src/worker/run-control-service.ts
|
|
453
|
+
var exports_run_control_service = {};
|
|
454
|
+
__export(exports_run_control_service, {
|
|
455
|
+
runControlService: () => runControlService
|
|
456
|
+
});
|
|
457
|
+
import { defineCapability as defineCapability3 } from "@rig/core/capability";
|
|
458
|
+
import { loadCapabilityForRoot as loadCapabilityForRoot2 } from "@rig/core/capability-loaders";
|
|
459
|
+
import {
|
|
460
|
+
RIG_CONTROL_SENTINEL_END,
|
|
461
|
+
RIG_INBOX_RESOLUTION_SENTINEL,
|
|
462
|
+
RIG_PAUSE_SENTINEL,
|
|
463
|
+
RIG_RESUME_SENTINEL,
|
|
464
|
+
RIG_STOP_SENTINEL,
|
|
465
|
+
RIG_STOP_SENTINEL_END,
|
|
466
|
+
RUN_READ_MODEL as RUN_READ_MODEL2,
|
|
467
|
+
RUN_SESSION_JOURNAL_WRITER
|
|
468
|
+
} from "@rig/contracts";
|
|
469
|
+
async function readModel(projectRoot) {
|
|
470
|
+
const service = await loadCapabilityForRoot2(projectRoot, RunReadModelCap2);
|
|
471
|
+
if (!service)
|
|
472
|
+
throw new Error("RUN_READ_MODEL capability unavailable: load @rig/run-plugin before using run control.");
|
|
473
|
+
return service;
|
|
474
|
+
}
|
|
475
|
+
function buildPauseSentinel(runId) {
|
|
476
|
+
return `${RIG_PAUSE_SENTINEL} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END}`;
|
|
477
|
+
}
|
|
478
|
+
function buildResumeSentinel(runId) {
|
|
479
|
+
return `${RIG_RESUME_SENTINEL} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END}`;
|
|
480
|
+
}
|
|
481
|
+
function buildStopSentinel(runId, reason) {
|
|
482
|
+
return `${RIG_STOP_SENTINEL} runId=${runId} reason=${reason} ${RIG_STOP_SENTINEL_END}`;
|
|
483
|
+
}
|
|
484
|
+
function buildInboxResolutionSentinel(runId, payload) {
|
|
485
|
+
return `${RIG_INBOX_RESOLUTION_SENTINEL} runId=${runId} data=${encodeURIComponent(JSON.stringify(payload))} requestedBy=operator ${RIG_CONTROL_SENTINEL_END}`;
|
|
486
|
+
}
|
|
487
|
+
function collabControlFrames(runId, control) {
|
|
488
|
+
switch (control.kind) {
|
|
489
|
+
case "steer":
|
|
490
|
+
return [{ t: "prompt", text: control.message }];
|
|
491
|
+
case "resume":
|
|
492
|
+
return [{ t: "prompt", text: `${buildResumeSentinel(runId)}
|
|
493
|
+
Continue the run from where it paused.` }];
|
|
494
|
+
case "pause":
|
|
495
|
+
return [
|
|
496
|
+
{ t: "prompt", text: `${buildPauseSentinel(runId)}
|
|
497
|
+
Pause this run after the current interruption settles.` },
|
|
498
|
+
{ t: "abort" }
|
|
499
|
+
];
|
|
500
|
+
case "stop":
|
|
501
|
+
return [
|
|
502
|
+
{ t: "prompt", text: `${buildStopSentinel(runId, control.reason)}
|
|
503
|
+
Stop this run and do not continue after the interruption settles.` },
|
|
504
|
+
{ t: "abort" }
|
|
505
|
+
];
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async function loadCollabControlDeps() {
|
|
509
|
+
const [{ importRoomKey, seal }, { COLLAB_PROTO, packEnvelope, parseCollabLink }] = await Promise.all([
|
|
510
|
+
import("@oh-my-pi/pi-coding-agent/collab/crypto"),
|
|
511
|
+
import("@oh-my-pi/pi-coding-agent/collab/protocol")
|
|
512
|
+
]);
|
|
513
|
+
return { importRoomKey, seal, COLLAB_PROTO, packEnvelope, parseCollabLink };
|
|
514
|
+
}
|
|
515
|
+
async function sendSealedCollabFrames(joinLink, frames) {
|
|
516
|
+
const { importRoomKey, seal, COLLAB_PROTO, packEnvelope, parseCollabLink } = await loadCollabControlDeps();
|
|
517
|
+
const parsed = parseCollabLink(joinLink);
|
|
518
|
+
if ("error" in parsed)
|
|
519
|
+
throw new Error(parsed.error);
|
|
520
|
+
if (!parsed.writeToken)
|
|
521
|
+
throw new Error("Run collab link is read-only; cannot send control.");
|
|
522
|
+
const key = await importRoomKey(parsed.key);
|
|
523
|
+
const ws = new WebSocket(`${parsed.wsUrl}?role=guest`);
|
|
524
|
+
await new Promise((resolveOpen, rejectOpen) => {
|
|
525
|
+
ws.addEventListener("open", () => resolveOpen(), { once: true });
|
|
526
|
+
ws.addEventListener("error", () => rejectOpen(new Error("collab websocket error")), { once: true });
|
|
527
|
+
});
|
|
528
|
+
const hello = {
|
|
529
|
+
t: "hello",
|
|
530
|
+
proto: COLLAB_PROTO,
|
|
531
|
+
name: "rig-operator",
|
|
532
|
+
writeToken: Buffer.from(parsed.writeToken).toString("base64url")
|
|
533
|
+
};
|
|
534
|
+
ws.send(packEnvelope(0, await seal(key, hello)));
|
|
535
|
+
for (const frame of frames)
|
|
536
|
+
ws.send(packEnvelope(0, await seal(key, frame)));
|
|
537
|
+
try {
|
|
538
|
+
ws.close();
|
|
539
|
+
} catch {}
|
|
540
|
+
}
|
|
541
|
+
function isSteerableStatus(status, classification) {
|
|
542
|
+
switch (status) {
|
|
543
|
+
case "needs-attention":
|
|
544
|
+
case "waiting-approval":
|
|
545
|
+
case "waiting-user-input":
|
|
546
|
+
case "paused":
|
|
547
|
+
return false;
|
|
548
|
+
default:
|
|
549
|
+
return classification.isActive;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function controlAffordance(readModel2, run, purpose) {
|
|
553
|
+
const writable = Boolean(run.joinLink && !run.stale);
|
|
554
|
+
if (!writable)
|
|
555
|
+
return { allowed: false, reason: run.stale ? "run is stale" : "run has no join link" };
|
|
556
|
+
const classification = readModel2.classifyRun(run);
|
|
557
|
+
switch (purpose) {
|
|
558
|
+
case "steer":
|
|
559
|
+
return classification.phase === "active" || classification.phase === "starting" || classification.isNeedsAttention && isSteerableStatus(classification.status, classification) ? { allowed: true } : { allowed: false, reason: "run cannot be steered in its current phase" };
|
|
560
|
+
case "stop":
|
|
561
|
+
return classification.isActive && !classification.isTerminal ? { allowed: true } : { allowed: false, reason: "run cannot be stopped in its current phase" };
|
|
562
|
+
case "pause":
|
|
563
|
+
return classification.phase === "active" || classification.phase === "starting" ? { allowed: true } : { allowed: false, reason: "run cannot be paused in its current phase" };
|
|
564
|
+
case "resume":
|
|
565
|
+
return classification.phase === "paused" ? { allowed: true } : { allowed: false, reason: "run cannot be resumed in its current phase" };
|
|
566
|
+
case "restart":
|
|
567
|
+
case "delete":
|
|
568
|
+
case "prune":
|
|
569
|
+
return !run.live || run.stale ? { allowed: true } : { allowed: false, reason: "run is still live" };
|
|
570
|
+
case "attach":
|
|
571
|
+
case "resolve-inbox":
|
|
572
|
+
case undefined:
|
|
573
|
+
return { allowed: true };
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
async function selectedRun(projectRoot, runId, discoveryFilter) {
|
|
577
|
+
const service = await readModel(projectRoot);
|
|
578
|
+
const run = await service.getRun({ projectRoot, selector: { id: runId, kind: "any" }, ...discoveryFilter !== undefined ? { discoveryFilter } : {} });
|
|
579
|
+
return { service, run };
|
|
580
|
+
}
|
|
581
|
+
function targetFromRun(service, run, purpose) {
|
|
582
|
+
const affordance = controlAffordance(service, run, purpose);
|
|
583
|
+
return {
|
|
584
|
+
runId: run.runId,
|
|
585
|
+
taskId: run.taskId,
|
|
586
|
+
sessionId: run.runId,
|
|
587
|
+
sessionPath: run.sessionPath,
|
|
588
|
+
joinLink: run.joinLink,
|
|
589
|
+
webLink: run.webLink,
|
|
590
|
+
relayUrl: run.relayUrl,
|
|
591
|
+
collabCwd: run.collabCwd,
|
|
592
|
+
live: run.live,
|
|
593
|
+
stale: run.stale,
|
|
594
|
+
canDeliver: affordance.allowed,
|
|
595
|
+
reason: affordance.allowed ? null : affordance.reason ?? null
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function inboxResolutionControl(runId, resolution) {
|
|
599
|
+
return { kind: "steer", message: buildInboxResolutionSentinel(runId, resolution) };
|
|
600
|
+
}
|
|
601
|
+
var RunReadModelCap2, runControlService;
|
|
602
|
+
var init_run_control_service = __esm(() => {
|
|
603
|
+
RunReadModelCap2 = defineCapability3(RUN_READ_MODEL2);
|
|
604
|
+
runControlService = {
|
|
605
|
+
async resolveControlTarget(input) {
|
|
606
|
+
const { service, run } = await selectedRun(input.projectRoot, input.runId, input.discoveryFilter);
|
|
607
|
+
return run ? targetFromRun(service, run, input.purpose) : null;
|
|
608
|
+
},
|
|
609
|
+
async deliverControl(input) {
|
|
610
|
+
const { service, run } = await selectedRun(input.projectRoot, input.runId, input.discoveryFilter);
|
|
611
|
+
if (!run)
|
|
612
|
+
return { runId: input.runId, kind: input.control.kind, status: "not-found", delivered: false, detail: "run not found" };
|
|
613
|
+
const affordance = controlAffordance(service, run, input.control.kind);
|
|
614
|
+
if (!affordance.allowed) {
|
|
615
|
+
return { runId: run.runId, kind: input.control.kind, status: run.joinLink && !run.stale ? "unsupported" : "not-live", delivered: false, detail: affordance.reason ?? `Run ${run.runId} is not writable in its current phase.` };
|
|
616
|
+
}
|
|
617
|
+
try {
|
|
618
|
+
if (!run.joinLink)
|
|
619
|
+
throw new Error(`Run ${run.runId} has no writable collab join link; attach interactively to ${input.control.kind}.`);
|
|
620
|
+
await sendSealedCollabFrames(run.joinLink, collabControlFrames(run.runId, input.control));
|
|
621
|
+
return { runId: run.runId, kind: input.control.kind, status: "delivered", delivered: true };
|
|
622
|
+
} catch (error) {
|
|
623
|
+
return { runId: run.runId, kind: input.control.kind, status: "unsupported", delivered: false, detail: error instanceof Error ? error.message : String(error) };
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
async resolveInboxRequest(input) {
|
|
627
|
+
const target = await this.resolveControlTarget({ projectRoot: input.projectRoot, runId: input.runId, purpose: "resolve-inbox", ...input.discoveryFilter !== undefined ? { discoveryFilter: input.discoveryFilter } : {} });
|
|
628
|
+
if (!target)
|
|
629
|
+
return { runId: input.runId, requestId: input.resolution.requestId, kind: input.resolution.kind, status: "not-found", delivered: false, detail: "run not found" };
|
|
630
|
+
if (!target.canDeliver)
|
|
631
|
+
return { runId: target.runId, requestId: input.resolution.requestId, kind: input.resolution.kind, status: "not-live", delivered: false, detail: target.reason ?? "run is not deliverable" };
|
|
632
|
+
const result = await this.deliverControl({ projectRoot: input.projectRoot, runId: target.runId, control: inboxResolutionControl(target.runId, input.resolution), ...input.discoveryFilter !== undefined ? { discoveryFilter: input.discoveryFilter } : {} });
|
|
633
|
+
const status = result.delivered ? "resolved" : result.status === "delivered" ? "resolved" : result.status;
|
|
634
|
+
return { runId: target.runId, requestId: input.resolution.requestId, kind: input.resolution.kind, status, delivered: result.delivered, ...result.detail !== undefined ? { detail: result.detail } : {} };
|
|
635
|
+
},
|
|
636
|
+
async resolveResumePlan(input) {
|
|
637
|
+
const { service, run } = await selectedRun(input.projectRoot, input.runId, input.discoveryFilter);
|
|
638
|
+
if (!run)
|
|
639
|
+
return null;
|
|
640
|
+
const affordance = controlAffordance(service, run, "resume");
|
|
641
|
+
if (affordance.allowed)
|
|
642
|
+
return { kind: "deliver-control", runId: run.runId, run };
|
|
643
|
+
if (!run.live || run.stale) {
|
|
644
|
+
return run.taskId ? { kind: "redispatch-task", runId: run.runId, taskId: run.taskId, title: run.title } : { kind: "unavailable", runId: run.runId, reason: "run is not live and has no task id to re-dispatch" };
|
|
645
|
+
}
|
|
646
|
+
return { kind: "unavailable", runId: run.runId, reason: affordance.reason ?? "run cannot be resumed in its current phase" };
|
|
647
|
+
},
|
|
648
|
+
async reconcileDeadPid(input) {
|
|
649
|
+
const RunSessionJournalWriterCap = defineCapability3(RUN_SESSION_JOURNAL_WRITER);
|
|
650
|
+
const writer = await loadCapabilityForRoot2(input.projectRoot, RunSessionJournalWriterCap);
|
|
651
|
+
if (!writer) {
|
|
652
|
+
return { runId: input.runId, updated: false, status: "unavailable", detail: "RUN_SESSION_JOURNAL_WRITER capability unavailable" };
|
|
653
|
+
}
|
|
654
|
+
return writer.reconcileDeadPid({
|
|
655
|
+
projectRoot: input.projectRoot,
|
|
656
|
+
runId: input.runId,
|
|
657
|
+
sessionPath: input.sessionPath,
|
|
658
|
+
...input.reason !== undefined ? { reason: input.reason } : {}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// packages/run-plugin/src/worker/local-run-changes.ts
|
|
665
|
+
import { existsSync, mkdirSync, watch } from "fs";
|
|
666
|
+
import { dirname, resolve } from "path";
|
|
667
|
+
import { Effect, Queue, Stream } from "effect";
|
|
668
|
+
function findNearestGitCheckoutRoot(startDir) {
|
|
669
|
+
let current = resolve(startDir);
|
|
670
|
+
for (;; ) {
|
|
671
|
+
if (existsSync(resolve(current, ".git")))
|
|
672
|
+
return current;
|
|
673
|
+
const parent = dirname(current);
|
|
674
|
+
if (parent === current)
|
|
675
|
+
return null;
|
|
676
|
+
current = parent;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function resolveCheckoutRoot(projectRoot) {
|
|
680
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
681
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
682
|
+
if (explicit) {
|
|
683
|
+
const explicitRoot = resolve(explicit);
|
|
684
|
+
const gitRoot = findNearestGitCheckoutRoot(explicitRoot);
|
|
685
|
+
if (gitRoot)
|
|
686
|
+
return gitRoot;
|
|
687
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there or above it.`);
|
|
688
|
+
}
|
|
689
|
+
return findNearestGitCheckoutRoot(normalizedProjectRoot) ?? normalizedProjectRoot;
|
|
690
|
+
}
|
|
691
|
+
function isRelevantLocalRunChange(filename) {
|
|
692
|
+
if (filename === null || filename === undefined)
|
|
693
|
+
return true;
|
|
694
|
+
const raw = filename.toString();
|
|
695
|
+
if (!raw.trim())
|
|
696
|
+
return true;
|
|
697
|
+
const segments = raw.split(/[\\/]+/).filter(Boolean);
|
|
698
|
+
if (segments.some((segment) => segment === ".git" || segment === "node_modules" || segment === "build" || segment === "dist" || segment === "out" || segment === ".next" || segment === "coverage")) {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
const rigIndex = segments.lastIndexOf(".rig");
|
|
702
|
+
if (rigIndex < 0)
|
|
703
|
+
return false;
|
|
704
|
+
const rigChild = segments[rigIndex + 1];
|
|
705
|
+
if (rigChild === "session")
|
|
706
|
+
return true;
|
|
707
|
+
return rigChild === "runtime-context.json" && segments[rigIndex + 2] === undefined;
|
|
708
|
+
}
|
|
709
|
+
var localRunChanges = (projectRoot) => Stream.callback((queue) => Effect.gen(function* () {
|
|
710
|
+
const worktreesRoot = resolve(resolveCheckoutRoot(projectRoot), ".worktrees");
|
|
711
|
+
if (!existsSync(worktreesRoot)) {
|
|
712
|
+
try {
|
|
713
|
+
mkdirSync(worktreesRoot, { recursive: true });
|
|
714
|
+
} catch {}
|
|
715
|
+
}
|
|
716
|
+
const watcher = watch(worktreesRoot, { recursive: true }, (_event, filename) => {
|
|
717
|
+
if (!isRelevantLocalRunChange(filename))
|
|
718
|
+
return;
|
|
719
|
+
Queue.offerUnsafe(queue, undefined);
|
|
720
|
+
});
|
|
721
|
+
watcher.on("error", () => {});
|
|
722
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => watcher.close()));
|
|
723
|
+
}), { bufferSize: 1, strategy: "sliding" });
|
|
724
|
+
var init_local_run_changes = () => {};
|
|
725
|
+
|
|
726
|
+
// packages/run-plugin/src/worker/notify-cap.ts
|
|
727
|
+
import { NOTIFY_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
728
|
+
import { defineCapability as defineCapability4 } from "@rig/core/capability";
|
|
729
|
+
import { resolvePluginHost } from "@rig/core/project-plugins";
|
|
730
|
+
async function resolveNotifyService(projectRoot) {
|
|
731
|
+
const { host } = await resolvePluginHost(projectRoot);
|
|
732
|
+
return NotifyCap.resolve(host);
|
|
733
|
+
}
|
|
734
|
+
var NotifyCap;
|
|
735
|
+
var init_notify_cap = __esm(() => {
|
|
736
|
+
NotifyCap = defineCapability4(NOTIFY_SERVICE_CAPABILITY);
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// packages/run-plugin/src/worker/notifications.ts
|
|
740
|
+
async function dispatchRunNotifications(projectRoot, runId, taskId, outcome, detail) {
|
|
741
|
+
try {
|
|
742
|
+
const notifier = await resolveNotifyService(projectRoot);
|
|
743
|
+
if (!notifier)
|
|
744
|
+
return;
|
|
745
|
+
const event = { runId, type: `run.${outcome}`, timestamp: new Date().toISOString(), payload: { taskId, detail } };
|
|
746
|
+
await Promise.race([
|
|
747
|
+
notifier.notify(event),
|
|
748
|
+
new Promise((resolveCap) => setTimeout(resolveCap, 5000))
|
|
749
|
+
]);
|
|
750
|
+
} catch {}
|
|
751
|
+
}
|
|
752
|
+
var init_notifications = __esm(() => {
|
|
753
|
+
init_notify_cap();
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
// packages/run-plugin/src/worker/panel-plugin.ts
|
|
757
|
+
import { createProjectPluginHost } from "@rig/core/project-plugins";
|
|
758
|
+
import { RIG_CAPABILITY_PANEL_SLOT, RIG_RUN_STOP_PANEL_ACTION, RIG_SUPERVISOR_PANEL_ID } from "@rig/contracts";
|
|
759
|
+
async function produceWorkerPanelPayload(producer, context) {
|
|
760
|
+
if (!producer.produce)
|
|
761
|
+
return;
|
|
762
|
+
let timeout;
|
|
763
|
+
try {
|
|
764
|
+
return await Promise.race([
|
|
765
|
+
Promise.resolve(producer.produce(context)),
|
|
766
|
+
new Promise((resolve2) => {
|
|
767
|
+
timeout = setTimeout(() => resolve2(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
768
|
+
})
|
|
769
|
+
]);
|
|
770
|
+
} finally {
|
|
771
|
+
clearTimeout(timeout);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
async function loadWorkerPanelRegistry(projectRoot) {
|
|
775
|
+
const { host } = await createProjectPluginHost(projectRoot, {
|
|
776
|
+
mode: "strict-config-only"
|
|
777
|
+
});
|
|
778
|
+
return {
|
|
779
|
+
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
780
|
+
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels", PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
784
|
+
var init_panel_plugin = () => {};
|
|
785
|
+
|
|
786
|
+
// packages/run-plugin/src/worker/constants.ts
|
|
787
|
+
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.";
|
|
788
|
+
var init_constants = __esm(() => {
|
|
789
|
+
TRACKED_RUN_STALL_MS = 20 * 60 * 1000;
|
|
790
|
+
RUN_PROCESS_STALL_SWEEP_MS = 60 * 1000;
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// packages/run-plugin/src/worker/stall.ts
|
|
794
|
+
function timestampMs(value) {
|
|
795
|
+
if (value === null || value === undefined)
|
|
796
|
+
return null;
|
|
797
|
+
const ms = value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(value);
|
|
798
|
+
return Number.isFinite(ms) ? ms : null;
|
|
799
|
+
}
|
|
800
|
+
function computeRunStall(input) {
|
|
801
|
+
const lastActivityAt = timestampMs(input.lastActivityAt);
|
|
802
|
+
const now = timestampMs(input.now);
|
|
803
|
+
if (lastActivityAt === null || now === null || input.thresholdMs <= 0)
|
|
804
|
+
return false;
|
|
805
|
+
return now - lastActivityAt >= input.thresholdMs;
|
|
806
|
+
}
|
|
807
|
+
function appendRunStallDetected(journal, detail = RUN_PROCESS_STALL_DETAIL) {
|
|
808
|
+
if (!journal)
|
|
809
|
+
return false;
|
|
810
|
+
try {
|
|
811
|
+
journal.appendStall({ detail });
|
|
812
|
+
return true;
|
|
813
|
+
} catch (error) {
|
|
814
|
+
console.warn(`[rig-run] stall-detected append failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function startRunProcessStallMonitor(opts) {
|
|
819
|
+
if (!opts.journal)
|
|
820
|
+
return () => {};
|
|
821
|
+
let stallDetected = opts.alreadyStalled === true;
|
|
822
|
+
const thresholdMs = opts.thresholdMs ?? TRACKED_RUN_STALL_MS;
|
|
823
|
+
const now = opts.now ?? (() => Date.now());
|
|
824
|
+
const timer = setInterval(() => {
|
|
825
|
+
if (stallDetected)
|
|
826
|
+
return;
|
|
827
|
+
if (!computeRunStall({ lastActivityAt: opts.lastActivityAt(), now: now(), thresholdMs }))
|
|
828
|
+
return;
|
|
829
|
+
stallDetected = true;
|
|
830
|
+
appendRunStallDetected(opts.journal);
|
|
831
|
+
}, opts.intervalMs ?? RUN_PROCESS_STALL_SWEEP_MS);
|
|
832
|
+
if (typeof timer.unref === "function")
|
|
833
|
+
timer.unref();
|
|
834
|
+
return () => clearInterval(timer);
|
|
835
|
+
}
|
|
836
|
+
var init_stall = __esm(() => {
|
|
837
|
+
init_constants();
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
// packages/run-plugin/src/worker/utils.ts
|
|
841
|
+
import { RIG_WORKFLOW_STATUS_CHANGED as RIG_WORKFLOW_STATUS_CHANGED2 } from "@rig/contracts";
|
|
842
|
+
import { rigProjectRootFromEnv } from "@rig/core/root-resolver";
|
|
843
|
+
function customEntries(entries) {
|
|
844
|
+
const customs = [];
|
|
845
|
+
for (const entry of entries) {
|
|
846
|
+
if (entry.type === "custom")
|
|
847
|
+
customs.push(entry);
|
|
848
|
+
}
|
|
849
|
+
return customs;
|
|
850
|
+
}
|
|
851
|
+
var init_utils = () => {};
|
|
852
|
+
|
|
853
|
+
// packages/run-plugin/src/worker/autohost.ts
|
|
854
|
+
import {
|
|
855
|
+
PLACEMENT_RUN_TRANSPORT,
|
|
856
|
+
RIG_RUN_STOP_PANEL_ACTION as RIG_RUN_STOP_PANEL_ACTION2,
|
|
857
|
+
RUN_IDENTITY_ENV,
|
|
858
|
+
RUN_REGISTRY_BACKBONE,
|
|
859
|
+
LIFECYCLE_VERIFICATION_SERVICE,
|
|
860
|
+
RUN_READ_MODEL as RUN_READ_MODEL3,
|
|
861
|
+
RUN_SESSION_JOURNAL as RUN_SESSION_JOURNAL2,
|
|
862
|
+
RUN_SESSION_JOURNAL_WRITER as RUN_SESSION_JOURNAL_WRITER2,
|
|
863
|
+
TRANSPORT_CONFIG,
|
|
864
|
+
GITHUB_CLI,
|
|
865
|
+
GITHUB_TOKEN_RESOLVER,
|
|
866
|
+
REPO_CHANGE_SET,
|
|
867
|
+
REPO_GIT_WORKFLOW,
|
|
868
|
+
REPO_NATIVE_GIT,
|
|
869
|
+
TASK_ARTIFACTS,
|
|
870
|
+
TASK_SOURCE_REFLECTION,
|
|
871
|
+
TASK_STATE_STORE
|
|
872
|
+
} from "@rig/contracts";
|
|
873
|
+
import { Duration, Effect as Effect2, Fiber, Stream as Stream2 } from "effect";
|
|
874
|
+
import { defineCapability as defineCapability5, defineServiceCapability } from "@rig/core/capability";
|
|
875
|
+
import { installCapability as installCapability2, loadCapabilityForRoot as loadCapabilityForRoot3, requireInstalledCapability } from "@rig/core/capability-loaders";
|
|
876
|
+
import { adopt as adoptRunWorkerKernel } from "@rig/core/kernel-entrypoint";
|
|
877
|
+
async function resolveRunRegistryNamespace(projectRoot, identityNamespaceKey) {
|
|
878
|
+
if (identityNamespaceKey)
|
|
879
|
+
return identityNamespaceKey;
|
|
880
|
+
const transportConfig = await loadCapabilityForRoot3(projectRoot, TransportConfigCap);
|
|
881
|
+
return transportConfig?.resolveOwnerNamespaceKey({ projectRoot }) ?? "anonymous";
|
|
882
|
+
}
|
|
883
|
+
function syntheticRegistryOwner(namespaceKey) {
|
|
884
|
+
const githubUserId = namespaceKey.startsWith("gh:") ? namespaceKey.slice(3) : namespaceKey;
|
|
885
|
+
return {
|
|
886
|
+
githubUserId: githubUserId || "anonymous",
|
|
887
|
+
login: "anonymous",
|
|
888
|
+
namespaceKey
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
function sanitizeRegistryProjectionValue(value) {
|
|
892
|
+
if (Array.isArray(value))
|
|
893
|
+
return value.map(sanitizeRegistryProjectionValue);
|
|
894
|
+
if (!value || typeof value !== "object")
|
|
895
|
+
return value;
|
|
896
|
+
const sanitized = {};
|
|
897
|
+
for (const [key, child] of Object.entries(value)) {
|
|
898
|
+
sanitized[key] = REGISTRY_SECRET_LINK_FIELDS.has(key) ? null : sanitizeRegistryProjectionValue(child);
|
|
899
|
+
}
|
|
900
|
+
return sanitized;
|
|
901
|
+
}
|
|
902
|
+
function sanitizeRegistryProjection(projection) {
|
|
903
|
+
return sanitizeRegistryProjectionValue(projection);
|
|
904
|
+
}
|
|
905
|
+
function textFromContent(content) {
|
|
906
|
+
if (typeof content === "string")
|
|
907
|
+
return content;
|
|
908
|
+
if (!Array.isArray(content))
|
|
909
|
+
return null;
|
|
910
|
+
const text = content.map((part) => part && typeof part === "object" && ("text" in part) && typeof part.text === "string" ? part.text : "").filter(Boolean).join(`
|
|
911
|
+
`).trim();
|
|
912
|
+
return text || null;
|
|
913
|
+
}
|
|
914
|
+
function textFromSessionEntry(entry) {
|
|
915
|
+
if (!entry || typeof entry !== "object")
|
|
916
|
+
return null;
|
|
917
|
+
const record = entry;
|
|
918
|
+
if (record.type === "custom_message")
|
|
919
|
+
return textFromContent(record.content);
|
|
920
|
+
if (record.type === "message")
|
|
921
|
+
return textFromAgentMessage(record.message);
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
function textFromAgentMessage(message) {
|
|
925
|
+
if (!message || typeof message !== "object")
|
|
926
|
+
return null;
|
|
927
|
+
return textFromContent(message.content);
|
|
928
|
+
}
|
|
929
|
+
function detectRunControlText(codec, text, runId) {
|
|
930
|
+
const stop = codec.parseStopSentinel(text, runId);
|
|
931
|
+
if (stop)
|
|
932
|
+
return { kind: "stop", reason: stop.reason };
|
|
933
|
+
if (codec.parsePauseSentinel(text, runId))
|
|
934
|
+
return { kind: "pause" };
|
|
935
|
+
if (codec.parseResumeSentinel(text, runId))
|
|
936
|
+
return { kind: "resume" };
|
|
937
|
+
const resolution = codec.parseInboxResolutionSentinel(text, runId);
|
|
938
|
+
if (resolution)
|
|
939
|
+
return { kind: "inbox", resolution };
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
async function installIfResolved(projectRoot, capability) {
|
|
943
|
+
const impl = await loadCapabilityForRoot3(projectRoot, capability);
|
|
944
|
+
if (impl)
|
|
945
|
+
installCapability2(capability, impl);
|
|
946
|
+
}
|
|
947
|
+
async function installRunWorkerSemanticCapabilities(projectRoot) {
|
|
948
|
+
await installIfResolved(projectRoot, GitHubCliCap);
|
|
949
|
+
await installIfResolved(projectRoot, GitHubTokenResolverCap);
|
|
950
|
+
await installIfResolved(projectRoot, RepoChangeSetCap);
|
|
951
|
+
await installIfResolved(projectRoot, RepoGitWorkflowCap);
|
|
952
|
+
await installIfResolved(projectRoot, RepoNativeGitCap);
|
|
953
|
+
await installIfResolved(projectRoot, TaskArtifactsCap);
|
|
954
|
+
await installIfResolved(projectRoot, TaskStateStoreCap);
|
|
955
|
+
}
|
|
956
|
+
async function maybeStartRunProcessAutohost(api, ctx) {
|
|
957
|
+
if (process.env.RIG_RUN_PROCESS !== "1")
|
|
958
|
+
return null;
|
|
959
|
+
const envRunId = process.env.RIG_RUN_ID?.trim();
|
|
960
|
+
if (!envRunId)
|
|
961
|
+
throw new Error("RIG_RUN_ID is required when RIG_RUN_PROCESS=1");
|
|
962
|
+
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
963
|
+
const sessionJournal = await loadCapabilityForRoot3(projectRoot, RunSessionJournalCap2);
|
|
964
|
+
if (!sessionJournal) {
|
|
965
|
+
throw new Error("RUN_SESSION_JOURNAL capability unavailable in run-worker autohost: ensure @rig/run-plugin (default bundle) is installed.");
|
|
966
|
+
}
|
|
967
|
+
installCapability2(RunSessionJournalCap2, sessionJournal);
|
|
968
|
+
const sessionManagerRunId = typeof ctx.sessionManager.getSessionId === "function" ? ctx.sessionManager.getSessionId().trim() : "";
|
|
969
|
+
const runId = sessionJournal.sessionIdFromSessionFile(ctx.sessionManager.getSessionFile()) || sessionManagerRunId;
|
|
970
|
+
if (!runId)
|
|
971
|
+
throw new Error("OMP session id is required when RIG_RUN_PROCESS=1; RIG_RUN_ID is only a dispatch handle.");
|
|
972
|
+
await installRunWorkerSemanticCapabilities(projectRoot);
|
|
973
|
+
const verificationService = await loadCapabilityForRoot3(projectRoot, LifecycleVerificationServiceCap);
|
|
974
|
+
if (verificationService) {
|
|
975
|
+
installCapability2(LifecycleVerificationServiceCap, verificationService);
|
|
976
|
+
}
|
|
977
|
+
const taskSourceReflectionService = await loadCapabilityForRoot3(projectRoot, TaskSourceReflectionCap);
|
|
978
|
+
const journalWriter = await loadCapabilityForRoot3(projectRoot, RunSessionJournalWriterCap);
|
|
979
|
+
if (!journalWriter) {
|
|
980
|
+
throw new Error("RUN_SESSION_JOURNAL_WRITER capability unavailable in run-worker autohost: ensure @rig/run-plugin is installed.");
|
|
981
|
+
}
|
|
982
|
+
installCapability2(RunSessionJournalWriterCap, journalWriter);
|
|
983
|
+
const journal = await journalWriter.createRunJournal(ctx.sessionManager, runId);
|
|
984
|
+
const placementTransport = await loadCapabilityForRoot3(projectRoot, PlacementTransportCap);
|
|
985
|
+
if (!placementTransport) {
|
|
986
|
+
throw new Error("placement run transport capability unavailable: ensure @rig/transport-plugin is installed.");
|
|
987
|
+
}
|
|
988
|
+
await adoptRunWorkerKernel(projectRoot, {
|
|
989
|
+
entrypoint: "run-worker",
|
|
990
|
+
hydrateEnv: true,
|
|
991
|
+
transport: placementTransport,
|
|
992
|
+
...journal ? { journal: journal.kernel } : {}
|
|
993
|
+
});
|
|
994
|
+
if (!ctx.hasUI)
|
|
995
|
+
return null;
|
|
996
|
+
const collab = ctx.collab;
|
|
997
|
+
const startHost = collab?.startCollabHost ?? collab?.startHost;
|
|
998
|
+
if (!startHost)
|
|
999
|
+
throw new Error("OMP collab host facade is unavailable for Rig run process.");
|
|
1000
|
+
const identityEnv = await loadCapabilityForRoot3(projectRoot, RunIdentityEnvCap);
|
|
1001
|
+
const identity = identityEnv?.resolveRigIdentity(ctx) ?? null;
|
|
1002
|
+
const runReadModel = await loadCapabilityForRoot3(projectRoot, RunReadModelCap3);
|
|
1003
|
+
if (!runReadModel) {
|
|
1004
|
+
throw new Error("RUN_READ_MODEL capability unavailable in run-worker autohost: ensure @rig/run-plugin (default bundle) is installed.");
|
|
1005
|
+
}
|
|
1006
|
+
const registryBackbone = await loadCapabilityForRoot3(projectRoot, RunRegistryBackboneCap);
|
|
1007
|
+
if (registryBackbone)
|
|
1008
|
+
installCapability2(RunRegistryBackboneCap, registryBackbone);
|
|
1009
|
+
const panelRegistry = await loadWorkerPanelRegistry(projectRoot);
|
|
1010
|
+
let cachedProjectionParts = null;
|
|
1011
|
+
const currentProjectionEntries = () => {
|
|
1012
|
+
const branch = ctx.sessionManager.getBranch();
|
|
1013
|
+
const lastBranchEntry = branch.at(-1);
|
|
1014
|
+
const lastBranchObject = lastBranchEntry && typeof lastBranchEntry === "object" ? lastBranchEntry : null;
|
|
1015
|
+
if (!cachedProjectionParts || cachedProjectionParts.branchLength !== branch.length || cachedProjectionParts.lastBranchEntry !== lastBranchObject) {
|
|
1016
|
+
const entries = customEntries(branch);
|
|
1017
|
+
cachedProjectionParts = {
|
|
1018
|
+
branchLength: branch.length,
|
|
1019
|
+
lastBranchEntry: lastBranchObject,
|
|
1020
|
+
entries
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
return cachedProjectionParts.entries;
|
|
1024
|
+
};
|
|
1025
|
+
let runProjection = runReadModel.buildSessionProjection({ runId, entries: currentProjectionEntries() });
|
|
1026
|
+
const buildWorkerBootstrapProjection = () => {
|
|
1027
|
+
runProjection = runReadModel.buildSessionProjection({ runId, entries: currentProjectionEntries() });
|
|
1028
|
+
return runProjection;
|
|
1029
|
+
};
|
|
1030
|
+
const taskIdAtStart = process.env.RIG_TASK_ID?.trim() || runProjection.record.taskId;
|
|
1031
|
+
const runDisplayTitle = (() => {
|
|
1032
|
+
const base = process.env.RIG_RUN_TITLE?.trim() || `Rig run ${runId}`;
|
|
1033
|
+
return taskIdAtStart && !base.includes(taskIdAtStart) ? `[${taskIdAtStart}] ${base}` : base;
|
|
1034
|
+
})();
|
|
1035
|
+
collab?.stopHost;
|
|
1036
|
+
let closeoutStarted = false;
|
|
1037
|
+
let forcedSteerUsed = false;
|
|
1038
|
+
let lastRunActivityAt = Date.now();
|
|
1039
|
+
const markRunActivity = () => {
|
|
1040
|
+
lastRunActivityAt = Date.now();
|
|
1041
|
+
};
|
|
1042
|
+
let runRegistryHeartbeat = null;
|
|
1043
|
+
let runRegistryRoomId = null;
|
|
1044
|
+
let workerProjectionConnection = null;
|
|
1045
|
+
let workerProjectionFiber = null;
|
|
1046
|
+
const projectRootForTransport = rigProjectRootFromEnv();
|
|
1047
|
+
const transportConfig = await loadCapabilityForRoot3(projectRootForTransport, TransportConfigCap);
|
|
1048
|
+
const runRegistryNamespace = await resolveRunRegistryNamespace(projectRootForTransport, identity?.owner?.namespaceKey);
|
|
1049
|
+
const runRegistryBaseUrl = transportConfig?.resolveRegistryBaseUrl({ projectRoot: projectRootForTransport }) ?? "";
|
|
1050
|
+
const runRelayUrl = transportConfig?.resolveRelayUrl() ?? "";
|
|
1051
|
+
const runRegistryOwner = identity?.owner ?? syntheticRegistryOwner(runRegistryNamespace);
|
|
1052
|
+
const runRegistryRepo = identity?.selectedRepo ?? "";
|
|
1053
|
+
const runRegistry = registryBackbone?.createRegistryClient({ baseUrl: runRegistryBaseUrl, namespaceKey: runRegistryNamespace }) ?? null;
|
|
1054
|
+
let runRegistryLinks = {};
|
|
1055
|
+
const processedControlEntryIds = new Set((ctx.sessionManager.getEntries?.() ?? []).map((entry) => entry && typeof entry === "object" && typeof entry.id === "string" ? entry.id : null).filter((id) => id !== null));
|
|
1056
|
+
const processedControlEntryObjects = new WeakSet;
|
|
1057
|
+
let pendingPause = false;
|
|
1058
|
+
let pendingStopReason;
|
|
1059
|
+
const buildCurrentRegistryProjection = (links = runRegistryLinks) => {
|
|
1060
|
+
const entries = currentProjectionEntries();
|
|
1061
|
+
const cwd = process.cwd();
|
|
1062
|
+
const projectionInput = {
|
|
1063
|
+
runId,
|
|
1064
|
+
entries,
|
|
1065
|
+
title: runDisplayTitle,
|
|
1066
|
+
taskId: taskIdAtStart ?? null,
|
|
1067
|
+
joinLink: links.joinLink ?? null,
|
|
1068
|
+
webLink: links.webLink ?? null,
|
|
1069
|
+
relayUrl: links.relayUrl ?? null,
|
|
1070
|
+
sessionPath: ctx.sessionManager.getSessionFile() ?? null,
|
|
1071
|
+
cwd,
|
|
1072
|
+
collabCwd: cwd,
|
|
1073
|
+
dispatchHandle: process.env.RIG_RUN_ID?.trim() || null,
|
|
1074
|
+
timelineLimit: REGISTRY_PROJECTION_TIMELINE_LIMIT
|
|
1075
|
+
};
|
|
1076
|
+
return sanitizeRegistryProjection(runReadModel.buildRegistryProjection(projectionInput));
|
|
1077
|
+
};
|
|
1078
|
+
let lastRigPanelsSnapshotJson = "";
|
|
1079
|
+
const publishRigPanelsSnapshot = async () => {
|
|
1080
|
+
const appendCustomMessageEntry = ctx.sessionManager.appendCustomMessageEntry?.bind(ctx.sessionManager);
|
|
1081
|
+
if (!appendCustomMessageEntry)
|
|
1082
|
+
return;
|
|
1083
|
+
const folded = buildWorkerBootstrapProjection();
|
|
1084
|
+
const producerContext = {
|
|
1085
|
+
projectRoot,
|
|
1086
|
+
runId,
|
|
1087
|
+
folded,
|
|
1088
|
+
...taskIdAtStart !== undefined ? { taskIdAtStart } : {},
|
|
1089
|
+
runDisplayTitle
|
|
1090
|
+
};
|
|
1091
|
+
const payloadEntries = await Promise.all(panelRegistry.producers.map(async (producer) => {
|
|
1092
|
+
try {
|
|
1093
|
+
const payload = await produceWorkerPanelPayload(producer, producerContext);
|
|
1094
|
+
return payload === undefined ? null : [producer.id, payload];
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
console.error(`[rig-run] panel-producer-failed panel=${producer.id} ${error instanceof Error ? error.message : String(error)}`);
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
}));
|
|
1100
|
+
const payloads = Object.fromEntries(payloadEntries.filter((entry) => entry !== null));
|
|
1101
|
+
const registrations = panelRegistry.registrations.map((panel) => ({
|
|
1102
|
+
id: panel.id,
|
|
1103
|
+
slot: panel.slot,
|
|
1104
|
+
title: panel.title,
|
|
1105
|
+
capabilityId: panel.capabilityId,
|
|
1106
|
+
description: panel.description,
|
|
1107
|
+
badge: panel.badge,
|
|
1108
|
+
disabled: panel.disabled
|
|
1109
|
+
}));
|
|
1110
|
+
const frameBody = {
|
|
1111
|
+
kind: "snapshot",
|
|
1112
|
+
activePanel: null,
|
|
1113
|
+
registrations,
|
|
1114
|
+
payloads
|
|
1115
|
+
};
|
|
1116
|
+
const serialized = JSON.stringify(frameBody);
|
|
1117
|
+
if (serialized === lastRigPanelsSnapshotJson)
|
|
1118
|
+
return;
|
|
1119
|
+
lastRigPanelsSnapshotJson = serialized;
|
|
1120
|
+
appendCustomMessageEntry(RIG_PANELS_CUSTOM_MESSAGE_TYPE, "", false, { ...frameBody, updatedAt: new Date().toISOString() }, "agent");
|
|
1121
|
+
};
|
|
1122
|
+
const publishRigPanelsSnapshotBestEffort = () => {
|
|
1123
|
+
publishRigPanelsSnapshot().catch((error) => {
|
|
1124
|
+
console.error(`[rig-run] panel-snapshot-publish-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
1125
|
+
});
|
|
1126
|
+
};
|
|
1127
|
+
const pushWorkerProjection = (links = runRegistryLinks) => {
|
|
1128
|
+
if (!workerProjectionConnection)
|
|
1129
|
+
return;
|
|
1130
|
+
workerProjectionConnection.push(buildCurrentRegistryProjection(links));
|
|
1131
|
+
};
|
|
1132
|
+
const publishRunProjection = async (status) => {
|
|
1133
|
+
const projection = buildCurrentRegistryProjection();
|
|
1134
|
+
if (workerProjectionConnection)
|
|
1135
|
+
workerProjectionConnection.push(projection);
|
|
1136
|
+
if (runRegistry && runRegistryRoomId) {
|
|
1137
|
+
await runRegistry.heartbeatRoom(runRegistryRoomId, status, projection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
1138
|
+
}
|
|
1139
|
+
publishRigPanelsSnapshotBestEffort();
|
|
1140
|
+
};
|
|
1141
|
+
const stopRunRegistry = (opts = {}) => {
|
|
1142
|
+
if (runRegistryHeartbeat) {
|
|
1143
|
+
clearInterval(runRegistryHeartbeat);
|
|
1144
|
+
runRegistryHeartbeat = null;
|
|
1145
|
+
}
|
|
1146
|
+
if (workerProjectionFiber) {
|
|
1147
|
+
Effect2.runFork(Fiber.interrupt(workerProjectionFiber));
|
|
1148
|
+
workerProjectionFiber = null;
|
|
1149
|
+
}
|
|
1150
|
+
if (workerProjectionConnection) {
|
|
1151
|
+
workerProjectionConnection.close();
|
|
1152
|
+
workerProjectionConnection = null;
|
|
1153
|
+
}
|
|
1154
|
+
if ((opts.removeRoom ?? true) && registryBackbone && runRegistryRoomId) {
|
|
1155
|
+
registryBackbone.removeRoom({ baseUrl: runRegistryBaseUrl, namespaceKey: runRegistryNamespace }, runRegistryRoomId).catch(() => {});
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
const stopRunStallMonitor = startRunProcessStallMonitor({
|
|
1159
|
+
journal,
|
|
1160
|
+
lastActivityAt: () => lastRunActivityAt,
|
|
1161
|
+
alreadyStalled: runProjection.stallCount > 0
|
|
1162
|
+
});
|
|
1163
|
+
const appendRunStatus = (status, reason, force = false) => {
|
|
1164
|
+
journal?.appendStatus(status, { actor: OPERATOR_ACTOR, reason, force });
|
|
1165
|
+
};
|
|
1166
|
+
const applyInboxResolution = (resolution) => {
|
|
1167
|
+
if (resolution.kind === "approval") {
|
|
1168
|
+
journal?.appendApprovalResolved({
|
|
1169
|
+
requestId: resolution.requestId,
|
|
1170
|
+
decision: resolution.decision,
|
|
1171
|
+
...resolution.note !== undefined ? { note: resolution.note } : {},
|
|
1172
|
+
actor: OPERATOR_ACTOR
|
|
1173
|
+
});
|
|
1174
|
+
journal?.appendTimeline({ type: "inbox-resolution", kind: "approval", requestId: resolution.requestId, decision: resolution.decision });
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
journal?.appendInputResolved({ requestId: resolution.requestId, answers: resolution.answers, actor: OPERATOR_ACTOR });
|
|
1178
|
+
journal?.appendTimeline({ type: "inbox-resolution", kind: "input", requestId: resolution.requestId });
|
|
1179
|
+
};
|
|
1180
|
+
const applyRunControlText = (text) => {
|
|
1181
|
+
const control = detectRunControlText(sessionJournal, text, runId);
|
|
1182
|
+
if (!control)
|
|
1183
|
+
return;
|
|
1184
|
+
markRunActivity();
|
|
1185
|
+
if (control.kind === "pause") {
|
|
1186
|
+
pendingPause = true;
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
if (control.kind === "resume") {
|
|
1190
|
+
pendingPause = false;
|
|
1191
|
+
pendingStopReason = undefined;
|
|
1192
|
+
appendRunStatus("running", "operator resumed run", true);
|
|
1193
|
+
publishRunProjection("running");
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
if (control.kind === "stop") {
|
|
1197
|
+
pendingStopReason = control.reason ?? "operator requested stop";
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
applyInboxResolution(control.resolution);
|
|
1201
|
+
};
|
|
1202
|
+
const processRunControlMessages = () => {
|
|
1203
|
+
for (const entry of ctx.sessionManager.getEntries?.() ?? []) {
|
|
1204
|
+
const entryObject = entry && typeof entry === "object" ? entry : null;
|
|
1205
|
+
const id = entryObject && typeof entryObject.id === "string" ? entryObject.id : null;
|
|
1206
|
+
if (id && processedControlEntryIds.has(id))
|
|
1207
|
+
continue;
|
|
1208
|
+
if (!id && entryObject && processedControlEntryObjects.has(entryObject))
|
|
1209
|
+
continue;
|
|
1210
|
+
const text = textFromSessionEntry(entry);
|
|
1211
|
+
if (id)
|
|
1212
|
+
processedControlEntryIds.add(id);
|
|
1213
|
+
else if (entryObject)
|
|
1214
|
+
processedControlEntryObjects.add(entryObject);
|
|
1215
|
+
if (text)
|
|
1216
|
+
applyRunControlText(text);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
let fatalTerminalPublishing = false;
|
|
1220
|
+
const publishFatalTerminal = (error) => {
|
|
1221
|
+
if (fatalTerminalPublishing)
|
|
1222
|
+
return;
|
|
1223
|
+
fatalTerminalPublishing = true;
|
|
1224
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1225
|
+
journal?.appendStatus("failed", { actor: { kind: "agent" }, errorText: detail, force: true });
|
|
1226
|
+
publishRunProjection("failed").catch((publishError) => {
|
|
1227
|
+
console.error(`[rig-run] fatal-terminal-publish-failed ${publishError instanceof Error ? publishError.message : String(publishError)}`);
|
|
1228
|
+
}).finally(() => {
|
|
1229
|
+
stopRunStallMonitor();
|
|
1230
|
+
stopRunRegistry({ removeRoom: false });
|
|
1231
|
+
process.exitCode = 1;
|
|
1232
|
+
setTimeout(() => process.exit(1), 0);
|
|
1233
|
+
});
|
|
1234
|
+
};
|
|
1235
|
+
process.once("uncaughtException", publishFatalTerminal);
|
|
1236
|
+
process.once("unhandledRejection", publishFatalTerminal);
|
|
1237
|
+
const handlePanelAction = async (input) => {
|
|
1238
|
+
if (input.actionId !== RIG_RUN_STOP_PANEL_ACTION2 && input.actionId !== "stop-supervisor") {
|
|
1239
|
+
throw new Error(`Unsupported Rig panel action: ${input.actionId}`);
|
|
1240
|
+
}
|
|
1241
|
+
pendingStopReason = `operator ${input.from.name} requested stop`;
|
|
1242
|
+
journal?.appendTimeline({ type: "interrupted", stage: "panel-action", status: "running", detail: pendingStopReason });
|
|
1243
|
+
ctx.abort();
|
|
1244
|
+
await publishRunProjection("running");
|
|
1245
|
+
};
|
|
1246
|
+
const startRunCollabHost = async () => {
|
|
1247
|
+
try {
|
|
1248
|
+
const projection = await startHost.call(collab, {
|
|
1249
|
+
title: runDisplayTitle,
|
|
1250
|
+
...identity?.owner ? { owner: identity.owner } : {},
|
|
1251
|
+
...identity?.selectedRepo ? { selectedRepo: identity.selectedRepo } : {},
|
|
1252
|
+
relayUrl: runRelayUrl,
|
|
1253
|
+
onPanelAction: handlePanelAction
|
|
1254
|
+
});
|
|
1255
|
+
const roomId = projection.sessionId?.trim() || runId;
|
|
1256
|
+
if (roomId !== runId) {
|
|
1257
|
+
throw new Error(`Collab host session id mismatch: expected ${runId}, got ${roomId}`);
|
|
1258
|
+
}
|
|
1259
|
+
runRegistryLinks = { joinLink: projection.joinLink ?? null, webLink: projection.webLink ?? null, relayUrl: projection.relayUrl ?? runRelayUrl };
|
|
1260
|
+
const timeline = {
|
|
1261
|
+
type: "collab-host-started",
|
|
1262
|
+
roomId,
|
|
1263
|
+
joinLink: projection.joinLink,
|
|
1264
|
+
webLink: projection.webLink,
|
|
1265
|
+
relayUrl: projection.relayUrl
|
|
1266
|
+
};
|
|
1267
|
+
journal?.appendTimeline(timeline);
|
|
1268
|
+
console.log(`[rig-run] collab-host-started joinLink=${projection.joinLink ? "(redacted)" : "(empty)"} relayUrl=${projection.relayUrl || "(empty)"}`);
|
|
1269
|
+
ctx.ui.notify("Rig run collab host started.", "info");
|
|
1270
|
+
publishRigPanelsSnapshotBestEffort();
|
|
1271
|
+
if (!runRegistry || !registryBackbone) {
|
|
1272
|
+
const detail = "Rig run discovery registry is unavailable.";
|
|
1273
|
+
console.error(`[rig-run] registry-unavailable ${detail}`);
|
|
1274
|
+
journal?.appendTimeline({ type: "registry-register-failed", status: "failed", detail });
|
|
1275
|
+
ctx.ui.notify("Rig run discovery registry is unavailable; failing run.", "error");
|
|
1276
|
+
publishFatalTerminal(new Error(detail));
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
if (runRegistry && registryBackbone) {
|
|
1280
|
+
try {
|
|
1281
|
+
runRegistryRoomId = roomId;
|
|
1282
|
+
const initialProjection = buildCurrentRegistryProjection();
|
|
1283
|
+
const sessionPath = ctx.sessionManager.getSessionFile();
|
|
1284
|
+
await registryBackbone.registerRoom({ baseUrl: runRegistryBaseUrl, namespaceKey: runRegistryNamespace }, {
|
|
1285
|
+
roomId,
|
|
1286
|
+
owner: runRegistryOwner,
|
|
1287
|
+
repo: runRegistryRepo,
|
|
1288
|
+
title: runDisplayTitle,
|
|
1289
|
+
status: "running",
|
|
1290
|
+
joinLink: projection.joinLink ?? "",
|
|
1291
|
+
webLink: projection.webLink ?? "",
|
|
1292
|
+
relayUrl: projection.relayUrl ?? runRelayUrl,
|
|
1293
|
+
startedAt: new Date().toISOString(),
|
|
1294
|
+
cwd: process.cwd(),
|
|
1295
|
+
...sessionPath ? { sessionPath } : {},
|
|
1296
|
+
pid: process.pid,
|
|
1297
|
+
projection: initialProjection
|
|
1298
|
+
});
|
|
1299
|
+
workerProjectionConnection = registryBackbone.connectWorkerProjection({ baseUrl: runRegistryBaseUrl, namespaceKey: runRegistryNamespace, runId: roomId });
|
|
1300
|
+
workerProjectionConnection.ready.catch((err) => console.error(`[rig-run] worker-projection-degraded (heartbeat remains authoritative): ${err instanceof Error ? err.message : String(err)}`));
|
|
1301
|
+
pushWorkerProjection();
|
|
1302
|
+
workerProjectionFiber = Effect2.runFork(localRunChanges(projectRoot).pipe(Stream2.debounce(Duration.millis(500)), Stream2.runForEach(() => Effect2.sync(() => pushWorkerProjection()))));
|
|
1303
|
+
journal?.appendTimeline({ type: "registry-registered", roomId });
|
|
1304
|
+
console.log(`[rig-run] registry-registered roomId=${roomId}`);
|
|
1305
|
+
runRegistryHeartbeat = setInterval(() => {
|
|
1306
|
+
const heartbeatProjection = buildCurrentRegistryProjection();
|
|
1307
|
+
runRegistry.heartbeatRoom(roomId, registryBackbone.coerceRegistryStatus(heartbeatProjection.status, "running"), heartbeatProjection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
1308
|
+
}, 15000);
|
|
1309
|
+
if (typeof runRegistryHeartbeat.unref === "function")
|
|
1310
|
+
runRegistryHeartbeat.unref();
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
1313
|
+
const detail = `Rig run discovery registry registration failed: ${cause}`;
|
|
1314
|
+
console.error(`[rig-run] registry-register-failed ${detail}`);
|
|
1315
|
+
journal?.appendTimeline({ type: "registry-register-failed", status: "failed", detail });
|
|
1316
|
+
ctx.ui.notify("Rig run could not register to the discovery registry; failing run.", "error");
|
|
1317
|
+
publishFatalTerminal(new Error(detail));
|
|
1318
|
+
return false;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return true;
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1324
|
+
console.error(`[rig-run] collab-host-start-failed ${detail}`);
|
|
1325
|
+
ctx.ui.notify("Rig run collab host could not reach the relay; failing run.", "error");
|
|
1326
|
+
publishFatalTerminal(error);
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
journal?.appendTimeline({ type: "stage", stage: "Connect", status: "running", detail: "run process session_start" });
|
|
1331
|
+
journal?.appendTimeline({ type: "stage", stage: "Prepare", status: "completed", detail: "run process prepared" });
|
|
1332
|
+
journal?.appendStatus("running", { actor: { kind: "agent" }, reason: "run process session started", force: true });
|
|
1333
|
+
if (!await startRunCollabHost())
|
|
1334
|
+
return null;
|
|
1335
|
+
if (taskIdAtStart) {
|
|
1336
|
+
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "running", detail: "reflecting run start to task source" });
|
|
1337
|
+
try {
|
|
1338
|
+
await taskSourceReflectionService?.reflectRunStarted(projectRoot, {
|
|
1339
|
+
runId,
|
|
1340
|
+
taskId: taskIdAtStart,
|
|
1341
|
+
...runProjection.record.sourceTask !== undefined ? { sourceTask: runProjection.record.sourceTask } : {},
|
|
1342
|
+
worktreePath: process.cwd(),
|
|
1343
|
+
logRoot: runProjection.record.logRoot ?? null,
|
|
1344
|
+
sessionPath: runProjection.record.sessionPath ?? null
|
|
1345
|
+
});
|
|
1346
|
+
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "completed", detail: "reflected run start" });
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "failed", detail: error instanceof Error ? error.message : String(error) });
|
|
1349
|
+
console.error(`[rig-run] start-reflection-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
const steerPi = async (message) => {
|
|
1353
|
+
journal?.appendTimeline({ type: "closeout-steer", message });
|
|
1354
|
+
api.sendUserMessage(message, { deliverAs: "steer" });
|
|
1355
|
+
};
|
|
1356
|
+
const agentEnd = async (event) => {
|
|
1357
|
+
markRunActivity();
|
|
1358
|
+
processRunControlMessages();
|
|
1359
|
+
for (const message of event.messages) {
|
|
1360
|
+
const text = textFromAgentMessage(message);
|
|
1361
|
+
if (text)
|
|
1362
|
+
applyRunControlText(text);
|
|
1363
|
+
}
|
|
1364
|
+
const aborted = event.messages.some((message) => message.role === "assistant" && message.stopReason === "aborted");
|
|
1365
|
+
if (pendingStopReason !== undefined) {
|
|
1366
|
+
const reason = pendingStopReason ?? "operator requested stop";
|
|
1367
|
+
pendingStopReason = undefined;
|
|
1368
|
+
closeoutStarted = true;
|
|
1369
|
+
journal?.appendTimeline({ type: "interrupted", stage: "stopped", status: "stopped", detail: reason });
|
|
1370
|
+
journal?.appendStatus("stopped", { actor: OPERATOR_ACTOR, reason, force: true });
|
|
1371
|
+
if (taskIdAtStart) {
|
|
1372
|
+
journal?.appendTimeline({ type: "stage", stage: "Task-source", status: "running", detail: "reflecting run stop to task source" });
|
|
1373
|
+
try {
|
|
1374
|
+
await taskSourceReflectionService?.reflectRunStopped(projectRoot, {
|
|
1375
|
+
runId,
|
|
1376
|
+
taskId: taskIdAtStart,
|
|
1377
|
+
...runProjection.record.sourceTask !== undefined ? { sourceTask: runProjection.record.sourceTask } : {},
|
|
1378
|
+
worktreePath: process.cwd(),
|
|
1379
|
+
logRoot: runProjection.record.logRoot ?? null,
|
|
1380
|
+
sessionPath: runProjection.record.sessionPath ?? null
|
|
1381
|
+
}, { reason });
|
|
1382
|
+
journal?.appendTimeline({ type: "stage", stage: "Task-source", status: "completed", detail: "reflected run stop" });
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1385
|
+
journal?.appendTimeline({ type: "stage", stage: "Task-source", status: "failed", detail });
|
|
1386
|
+
console.error(`[rig-run] stopped-reflection-failed ${detail}`);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
await publishRunProjection("stopped");
|
|
1390
|
+
stopRunStallMonitor();
|
|
1391
|
+
stopRunRegistry({ removeRoom: false });
|
|
1392
|
+
process.exitCode = 0;
|
|
1393
|
+
setTimeout(() => process.exit(0), 0);
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
if (aborted) {
|
|
1397
|
+
if (pendingPause) {
|
|
1398
|
+
pendingPause = false;
|
|
1399
|
+
appendRunStatus("paused", "operator paused run", true);
|
|
1400
|
+
journal?.appendTimeline({ type: "interrupted", stage: "paused", status: "paused", detail: "operator paused run; parked for native collab resume" });
|
|
1401
|
+
await publishRunProjection("paused");
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
journal?.appendTimeline({ type: "interrupted", stage: "aborted", status: "running", detail: "operator interrupted run without a Rig control sentinel" });
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
if (closeoutStarted)
|
|
1408
|
+
return;
|
|
1409
|
+
if (process.env.RIG_RUN_FORCE_STEER_ONCE === "1" && !forcedSteerUsed) {
|
|
1410
|
+
forcedSteerUsed = true;
|
|
1411
|
+
journal?.appendTimeline({ type: "force-steer-once-started" });
|
|
1412
|
+
await steerPi("now reply with the word FIXED");
|
|
1413
|
+
journal?.appendTimeline({ type: "force-steer-once-completed" });
|
|
1414
|
+
}
|
|
1415
|
+
closeoutStarted = true;
|
|
1416
|
+
buildWorkerBootstrapProjection();
|
|
1417
|
+
const taskId = process.env.RIG_TASK_ID?.trim() || runProjection.record.taskId;
|
|
1418
|
+
if (!taskId) {
|
|
1419
|
+
const reason = "missing task id for closeout";
|
|
1420
|
+
journal?.appendTimeline({ type: "closeout-skipped", reason: "missing-task-id" });
|
|
1421
|
+
journal?.appendStatus("failed", { actor: { kind: "agent" }, reason, errorText: reason, force: true });
|
|
1422
|
+
await publishRunProjection("failed");
|
|
1423
|
+
stopRunStallMonitor();
|
|
1424
|
+
stopRunRegistry({ removeRoom: false });
|
|
1425
|
+
process.exitCode = 1;
|
|
1426
|
+
setTimeout(() => process.exit(1), 0);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
let closeoutStatusAdvanced = false;
|
|
1430
|
+
try {
|
|
1431
|
+
await requireInstalledCapability(LifecycleVerificationServiceCap, "run-closeout capability unavailable in run-worker autohost: ensure @rig/lifecycle-plugin (default bundle) is installed.").closeout({
|
|
1432
|
+
projectRoot,
|
|
1433
|
+
runId,
|
|
1434
|
+
taskId,
|
|
1435
|
+
branch: runProjection.record.branch ?? `rig/${taskId}-${runId}`,
|
|
1436
|
+
workspace: process.cwd(),
|
|
1437
|
+
artifactRoot: runProjection.record.artifactRoot ?? null,
|
|
1438
|
+
sourceTask: runProjection.record.sourceTask && typeof runProjection.record.sourceTask === "object" && !Array.isArray(runProjection.record.sourceTask) ? runProjection.record.sourceTask : null,
|
|
1439
|
+
steerPi,
|
|
1440
|
+
onValidationStart: async () => {
|
|
1441
|
+
journal?.appendStatus("validating", { actor: { kind: "agent" }, reason: "validating task before closeout", force: true });
|
|
1442
|
+
await publishRunProjection("validating");
|
|
1443
|
+
},
|
|
1444
|
+
kernelJournal: journal?.kernel ?? null,
|
|
1445
|
+
journalPhase: async (phase, outcome, detail) => {
|
|
1446
|
+
journal?.appendCloseoutPhase({ phase, outcome, detail: detail ?? null });
|
|
1447
|
+
if (!closeoutStatusAdvanced && phase !== "queued" && outcome === "started") {
|
|
1448
|
+
closeoutStatusAdvanced = true;
|
|
1449
|
+
journal?.appendStatus("closing-out", { actor: { kind: "agent" }, reason: "validation passed; running closeout automation", force: true });
|
|
1450
|
+
await publishRunProjection("closing-out");
|
|
1451
|
+
}
|
|
1452
|
+
},
|
|
1453
|
+
reflect: async (status, summary, opts) => {
|
|
1454
|
+
await taskSourceReflectionService?.updateRunTaskSourceLifecycle(projectRoot, {
|
|
1455
|
+
runId,
|
|
1456
|
+
taskId,
|
|
1457
|
+
...runProjection.record.sourceTask !== undefined ? { sourceTask: runProjection.record.sourceTask } : {},
|
|
1458
|
+
worktreePath: process.cwd(),
|
|
1459
|
+
logRoot: runProjection.record.logRoot ?? null,
|
|
1460
|
+
sessionPath: runProjection.record.sessionPath ?? null
|
|
1461
|
+
}, status, summary, opts);
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
journal?.appendStatus("completed", { actor: { kind: "agent" }, reason: "closeout completed" });
|
|
1465
|
+
await publishRunProjection("completed");
|
|
1466
|
+
await dispatchRunNotifications(projectRoot, runId, taskId, "completed", "closeout completed");
|
|
1467
|
+
stopRunStallMonitor();
|
|
1468
|
+
stopRunRegistry({ removeRoom: false });
|
|
1469
|
+
process.exitCode = 0;
|
|
1470
|
+
setTimeout(() => process.exit(0), 0);
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1473
|
+
const status = error instanceof Error && error.name === "CloseoutValidationError" ? "needs-attention" : "failed";
|
|
1474
|
+
journal?.appendStatus(status, { actor: { kind: "agent" }, errorText: detail, force: true });
|
|
1475
|
+
await publishRunProjection(status);
|
|
1476
|
+
await dispatchRunNotifications(projectRoot, runId, taskId, "failed", detail);
|
|
1477
|
+
stopRunStallMonitor();
|
|
1478
|
+
stopRunRegistry({ removeRoom: false });
|
|
1479
|
+
process.exitCode = 1;
|
|
1480
|
+
setTimeout(() => process.exit(1), 0);
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
const handleRunActivity = () => {
|
|
1484
|
+
markRunActivity();
|
|
1485
|
+
processRunControlMessages();
|
|
1486
|
+
};
|
|
1487
|
+
return { beforeAgentStart: handleRunActivity, messageStart: handleRunActivity, agentEnd };
|
|
1488
|
+
}
|
|
1489
|
+
async function maybeStartSpikeAutohost(ctx) {
|
|
1490
|
+
if (process.env.RIG_SPIKE_AUTOHOST !== "1")
|
|
1491
|
+
return;
|
|
1492
|
+
const relayUrl = process.env.RIG_SPIKE_RELAY?.trim();
|
|
1493
|
+
if (!relayUrl)
|
|
1494
|
+
throw new Error("RIG_SPIKE_RELAY is required when RIG_SPIKE_AUTOHOST=1");
|
|
1495
|
+
const collab = ctx.collab;
|
|
1496
|
+
const startHost = collab?.startHost ?? collab?.startCollabHost;
|
|
1497
|
+
if (!startHost)
|
|
1498
|
+
throw new Error("OMP collab host facade is unavailable for detached PTY spike.");
|
|
1499
|
+
const identity = (await loadCapabilityForRoot3(rigProjectRootFromEnv(), RunIdentityEnvCap))?.resolveRigIdentity(ctx) ?? null;
|
|
1500
|
+
await startHost.call(collab, {
|
|
1501
|
+
relayUrl,
|
|
1502
|
+
title: "Rig detached PTY spike",
|
|
1503
|
+
...identity?.owner ? { owner: identity.owner } : {},
|
|
1504
|
+
...identity?.selectedRepo ? { selectedRepo: identity.selectedRepo } : {}
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
var GitHubCliCap, GitHubTokenResolverCap, RepoChangeSetCap, RepoGitWorkflowCap, RepoNativeGitCap, TaskArtifactsCap, TaskStateStoreCap, PlacementTransportCap, RunIdentityEnvCap, RunRegistryBackboneCap, RunReadModelCap3, RunSessionJournalCap2, RunSessionJournalWriterCap, LifecycleVerificationServiceCap, TransportConfigCap, TaskSourceReflectionCap, REGISTRY_PROJECTION_TIMELINE_LIMIT = 100, OPERATOR_ACTOR, REGISTRY_SECRET_LINK_FIELDS;
|
|
1508
|
+
var init_autohost = __esm(() => {
|
|
1509
|
+
init_local_run_changes();
|
|
1510
|
+
init_notifications();
|
|
1511
|
+
init_panel_plugin();
|
|
1512
|
+
init_stall();
|
|
1513
|
+
init_utils();
|
|
1514
|
+
GitHubCliCap = defineCapability5(GITHUB_CLI);
|
|
1515
|
+
GitHubTokenResolverCap = defineCapability5(GITHUB_TOKEN_RESOLVER);
|
|
1516
|
+
RepoChangeSetCap = defineCapability5(REPO_CHANGE_SET);
|
|
1517
|
+
RepoGitWorkflowCap = defineCapability5(REPO_GIT_WORKFLOW);
|
|
1518
|
+
RepoNativeGitCap = defineCapability5(REPO_NATIVE_GIT);
|
|
1519
|
+
TaskArtifactsCap = defineCapability5(TASK_ARTIFACTS);
|
|
1520
|
+
TaskStateStoreCap = defineCapability5(TASK_STATE_STORE);
|
|
1521
|
+
PlacementTransportCap = defineCapability5(PLACEMENT_RUN_TRANSPORT);
|
|
1522
|
+
RunIdentityEnvCap = defineCapability5(RUN_IDENTITY_ENV);
|
|
1523
|
+
RunRegistryBackboneCap = defineCapability5(RUN_REGISTRY_BACKBONE);
|
|
1524
|
+
RunReadModelCap3 = defineCapability5(RUN_READ_MODEL3);
|
|
1525
|
+
RunSessionJournalCap2 = defineCapability5(RUN_SESSION_JOURNAL2);
|
|
1526
|
+
RunSessionJournalWriterCap = defineCapability5(RUN_SESSION_JOURNAL_WRITER2);
|
|
1527
|
+
LifecycleVerificationServiceCap = defineServiceCapability(LIFECYCLE_VERIFICATION_SERVICE);
|
|
1528
|
+
TransportConfigCap = defineCapability5(TRANSPORT_CONFIG);
|
|
1529
|
+
TaskSourceReflectionCap = defineCapability5(TASK_SOURCE_REFLECTION);
|
|
1530
|
+
OPERATOR_ACTOR = { kind: "operator" };
|
|
1531
|
+
REGISTRY_SECRET_LINK_FIELDS = new Set(["joinLink", "webLink"]);
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
// packages/run-plugin/src/worker/extension.ts
|
|
1535
|
+
var exports_extension = {};
|
|
1536
|
+
__export(exports_extension, {
|
|
1537
|
+
default: () => rigWorkerExtension,
|
|
1538
|
+
__rigRunWorkerTest: () => __rigRunWorkerTest,
|
|
1539
|
+
__rigExtensionTest: () => __rigExtensionTest
|
|
1540
|
+
});
|
|
1541
|
+
function rigWorkerExtension(api) {
|
|
1542
|
+
let hooks = null;
|
|
1543
|
+
api.on("session_start", async (_event, ctx) => {
|
|
1544
|
+
hooks = await maybeStartRunProcessAutohost(api, ctx);
|
|
1545
|
+
await maybeStartSpikeAutohost(ctx);
|
|
1546
|
+
});
|
|
1547
|
+
api.on("before_agent_start", () => {
|
|
1548
|
+
hooks?.beforeAgentStart();
|
|
1549
|
+
});
|
|
1550
|
+
api.on("message_start", () => {
|
|
1551
|
+
hooks?.messageStart();
|
|
1552
|
+
});
|
|
1553
|
+
api.on("agent_end", async (event) => {
|
|
1554
|
+
await hooks?.agentEnd(event);
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
var __rigRunWorkerTest, __rigExtensionTest;
|
|
1558
|
+
var init_extension = __esm(() => {
|
|
1559
|
+
init_autohost();
|
|
1560
|
+
init_stall();
|
|
1561
|
+
__rigRunWorkerTest = {
|
|
1562
|
+
computeRunStall,
|
|
1563
|
+
appendRunStallDetected,
|
|
1564
|
+
detectRunControlText
|
|
1565
|
+
};
|
|
1566
|
+
__rigExtensionTest = __rigRunWorkerTest;
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
// packages/run-plugin/src/read-model/stats-format.ts
|
|
1570
|
+
function formatRatePercent(value) {
|
|
1571
|
+
return value === null ? "\u2014" : `${Math.round(value * 100)}%`;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// packages/run-plugin/src/read-model/session-journal.ts
|
|
1575
|
+
var exports_session_journal = {};
|
|
1576
|
+
__export(exports_session_journal, {
|
|
1577
|
+
timelineEntriesFromCustomEntries: () => timelineEntriesFromCustomEntries,
|
|
1578
|
+
sessionIdFromSessionFile: () => sessionIdFromSessionFile,
|
|
1579
|
+
runSessionJournalCodec: () => runSessionJournalCodec,
|
|
1580
|
+
projectRunFromSession: () => projectRunFromSession,
|
|
1581
|
+
parseStopSentinel: () => parseStopSentinel,
|
|
1582
|
+
parseResumeSentinel: () => parseResumeSentinel,
|
|
1583
|
+
parsePauseSentinel: () => parsePauseSentinel,
|
|
1584
|
+
parseInboxResolutionSentinel: () => parseInboxResolutionSentinel,
|
|
1585
|
+
latestTimelineEntriesFromCustomEntries: () => latestTimelineEntriesFromCustomEntries,
|
|
1586
|
+
isTerminalRunStatus: () => isTerminalRunStatus,
|
|
1587
|
+
foldRunSessionEntries: () => foldRunSessionEntries
|
|
1588
|
+
});
|
|
1589
|
+
import { Schema } from "effect";
|
|
1590
|
+
import {
|
|
1591
|
+
CUSTOM_TYPE_FOR as CUSTOM_TYPE_FOR2,
|
|
1592
|
+
RIG_CONTROL_SENTINEL_END as RIG_CONTROL_SENTINEL_END2,
|
|
1593
|
+
RIG_INBOX_RESOLUTION_SENTINEL as RIG_INBOX_RESOLUTION_SENTINEL2,
|
|
1594
|
+
RIG_PAUSE_SENTINEL as RIG_PAUSE_SENTINEL2,
|
|
1595
|
+
RIG_RESUME_SENTINEL as RIG_RESUME_SENTINEL2,
|
|
1596
|
+
RIG_STOP_SENTINEL as RIG_STOP_SENTINEL2,
|
|
1597
|
+
RIG_STOP_SENTINEL_END as RIG_STOP_SENTINEL_END2,
|
|
1598
|
+
RunJournalEvent,
|
|
1599
|
+
TYPE_FOR_CUSTOM
|
|
1600
|
+
} from "@rig/contracts";
|
|
1601
|
+
function isTerminalRunStatus(status) {
|
|
1602
|
+
return TERMINAL_RUN_STATUSES.includes(status);
|
|
1603
|
+
}
|
|
1604
|
+
function canTransitionRunStatus(from, to) {
|
|
1605
|
+
if (from === null)
|
|
1606
|
+
return true;
|
|
1607
|
+
if (from === to)
|
|
1608
|
+
return true;
|
|
1609
|
+
return RUN_STATUS_TRANSITIONS[from].includes(to);
|
|
1610
|
+
}
|
|
1611
|
+
function assertRunStatusTransition(from, to) {
|
|
1612
|
+
if (!canTransitionRunStatus(from, to)) {
|
|
1613
|
+
throw new Error(`Illegal run status transition: ${from ?? "(none)"} -> ${to}. ` + `Allowed from ${from ?? "(none)"}: ${from ? RUN_STATUS_TRANSITIONS[from].join(", ") : "(any)"}.`);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
function reduceRunJournal(events, runId = null) {
|
|
1617
|
+
let record = {};
|
|
1618
|
+
let status = null;
|
|
1619
|
+
const statusHistory = [];
|
|
1620
|
+
const pendingApprovals = new Map;
|
|
1621
|
+
const resolvedApprovals = [];
|
|
1622
|
+
const pendingUserInputs = new Map;
|
|
1623
|
+
const resolvedUserInputs = [];
|
|
1624
|
+
const closeoutPhases = [];
|
|
1625
|
+
let resolvedPipeline = null;
|
|
1626
|
+
const stageOutcomes = [];
|
|
1627
|
+
const anomalies = [];
|
|
1628
|
+
let steeringCount = 0;
|
|
1629
|
+
let stallCount = 0;
|
|
1630
|
+
let lastSeq = 0;
|
|
1631
|
+
let lastEventAt = null;
|
|
1632
|
+
const projectedRunId = runId ?? events[0]?.runId ?? null;
|
|
1633
|
+
for (const event of events) {
|
|
1634
|
+
lastSeq = event.seq;
|
|
1635
|
+
lastEventAt = event.at;
|
|
1636
|
+
switch (event.type) {
|
|
1637
|
+
case "status-changed": {
|
|
1638
|
+
if (!canTransitionRunStatus(status, event.to)) {
|
|
1639
|
+
anomalies.push({ seq: event.seq, kind: "illegal-transition", detail: `${status ?? "(none)"} -> ${event.to}` });
|
|
1640
|
+
}
|
|
1641
|
+
statusHistory.push({ seq: event.seq, at: event.at, from: status, to: event.to, reason: event.reason ?? null });
|
|
1642
|
+
const wasTerminal = status !== null && isTerminalRunStatus(status);
|
|
1643
|
+
status = event.to;
|
|
1644
|
+
record = { ...record, updatedAt: event.at };
|
|
1645
|
+
if (isTerminalRunStatus(event.to) && !record.completedAt)
|
|
1646
|
+
record = { ...record, completedAt: event.at };
|
|
1647
|
+
if (!isTerminalRunStatus(event.to) && wasTerminal)
|
|
1648
|
+
record = { ...record, completedAt: null };
|
|
1649
|
+
if (event.to === "running" && !record.startedAt)
|
|
1650
|
+
record = { ...record, startedAt: event.at };
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
case "record-patch": {
|
|
1654
|
+
const next = { ...record };
|
|
1655
|
+
for (const [key, value] of Object.entries(event.patch)) {
|
|
1656
|
+
if (value !== undefined)
|
|
1657
|
+
next[key] = value;
|
|
1658
|
+
}
|
|
1659
|
+
next.updatedAt = event.patch.updatedAt ?? event.at;
|
|
1660
|
+
record = next;
|
|
1661
|
+
break;
|
|
1662
|
+
}
|
|
1663
|
+
case "approval-requested": {
|
|
1664
|
+
pendingApprovals.set(event.requestId, {
|
|
1665
|
+
requestId: event.requestId,
|
|
1666
|
+
requestKind: event.requestKind,
|
|
1667
|
+
actionId: event.actionId ?? null,
|
|
1668
|
+
payload: event.payload,
|
|
1669
|
+
requestedAt: event.at
|
|
1670
|
+
});
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
case "approval-resolved": {
|
|
1674
|
+
const pending = pendingApprovals.get(event.requestId);
|
|
1675
|
+
if (!pending) {
|
|
1676
|
+
const alreadyResolved = resolvedApprovals.some((entry) => entry.requestId === event.requestId);
|
|
1677
|
+
anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `approval ${event.requestId}` });
|
|
1678
|
+
break;
|
|
1679
|
+
}
|
|
1680
|
+
pendingApprovals.delete(event.requestId);
|
|
1681
|
+
resolvedApprovals.push({ ...pending, decision: event.decision, note: event.note ?? null, actor: event.actor, resolvedAt: event.at });
|
|
1682
|
+
break;
|
|
1683
|
+
}
|
|
1684
|
+
case "input-requested": {
|
|
1685
|
+
pendingUserInputs.set(event.requestId, {
|
|
1686
|
+
requestId: event.requestId,
|
|
1687
|
+
requestKind: "user-input",
|
|
1688
|
+
actionId: null,
|
|
1689
|
+
payload: event.payload,
|
|
1690
|
+
requestedAt: event.at
|
|
1691
|
+
});
|
|
1692
|
+
break;
|
|
1693
|
+
}
|
|
1694
|
+
case "input-resolved": {
|
|
1695
|
+
const pending = pendingUserInputs.get(event.requestId);
|
|
1696
|
+
if (!pending) {
|
|
1697
|
+
const alreadyResolved = resolvedUserInputs.some((entry) => entry.requestId === event.requestId);
|
|
1698
|
+
anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `user-input ${event.requestId}` });
|
|
1699
|
+
break;
|
|
1700
|
+
}
|
|
1701
|
+
pendingUserInputs.delete(event.requestId);
|
|
1702
|
+
resolvedUserInputs.push({ ...pending, answers: event.answers, actor: event.actor, resolvedAt: event.at });
|
|
1703
|
+
break;
|
|
1704
|
+
}
|
|
1705
|
+
case "steering":
|
|
1706
|
+
steeringCount += 1;
|
|
1707
|
+
break;
|
|
1708
|
+
case "adopted":
|
|
1709
|
+
record = { ...record, pid: event.pid, updatedAt: event.at };
|
|
1710
|
+
break;
|
|
1711
|
+
case "stall-detected":
|
|
1712
|
+
stallCount += 1;
|
|
1713
|
+
break;
|
|
1714
|
+
case "closeout-phase":
|
|
1715
|
+
closeoutPhases.push({ seq: event.seq, at: event.at, phase: event.phase, outcome: event.outcome, detail: event.detail ?? null });
|
|
1716
|
+
break;
|
|
1717
|
+
case "pipeline-resolved":
|
|
1718
|
+
resolvedPipeline = event.pipeline;
|
|
1719
|
+
break;
|
|
1720
|
+
case "stage-outcome":
|
|
1721
|
+
stageOutcomes.push({ seq: event.seq, at: event.at, outcome: event.outcome });
|
|
1722
|
+
break;
|
|
1723
|
+
case "timeline-entry":
|
|
1724
|
+
case "log-entry":
|
|
1725
|
+
break;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return {
|
|
1729
|
+
runId: projectedRunId,
|
|
1730
|
+
record,
|
|
1731
|
+
status,
|
|
1732
|
+
statusHistory,
|
|
1733
|
+
pendingApprovals: [...pendingApprovals.values()],
|
|
1734
|
+
resolvedApprovals,
|
|
1735
|
+
pendingUserInputs: [...pendingUserInputs.values()],
|
|
1736
|
+
resolvedUserInputs,
|
|
1737
|
+
steeringCount,
|
|
1738
|
+
stallCount,
|
|
1739
|
+
closeoutPhases,
|
|
1740
|
+
resolvedPipeline,
|
|
1741
|
+
stageOutcomes,
|
|
1742
|
+
lastSeq,
|
|
1743
|
+
lastEventAt,
|
|
1744
|
+
anomalies
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
function sessionIdFromSessionFile(sessionPath) {
|
|
1748
|
+
if (!sessionPath)
|
|
1749
|
+
return null;
|
|
1750
|
+
const file = sessionPath.split(/[\\/]/).pop() ?? "";
|
|
1751
|
+
const match = file.match(/_([0-9a-fA-F][0-9a-fA-F-]{7,})\.jsonl$/);
|
|
1752
|
+
return match?.[1] ?? null;
|
|
1753
|
+
}
|
|
1754
|
+
function isRunSessionCustomType(customType) {
|
|
1755
|
+
return customType !== undefined && Object.hasOwn(TYPE_FOR_CUSTOM, customType);
|
|
1756
|
+
}
|
|
1757
|
+
function foldRunSessionEntries(entries, runId) {
|
|
1758
|
+
const events = [];
|
|
1759
|
+
entries.forEach((entry, index) => {
|
|
1760
|
+
if (entry.type !== "custom" || !isRunSessionCustomType(entry.customType))
|
|
1761
|
+
return;
|
|
1762
|
+
const data = entry.data !== null && typeof entry.data === "object" ? entry.data : {};
|
|
1763
|
+
const stamped = {
|
|
1764
|
+
v: 1,
|
|
1765
|
+
seq: index + 1,
|
|
1766
|
+
at: typeof data.at === "string" ? data.at : new Date(0).toISOString(),
|
|
1767
|
+
runId,
|
|
1768
|
+
...data,
|
|
1769
|
+
type: TYPE_FOR_CUSTOM[entry.customType]
|
|
1770
|
+
};
|
|
1771
|
+
try {
|
|
1772
|
+
events.push(decodeRunJournalEvent(stamped));
|
|
1773
|
+
} catch {}
|
|
1774
|
+
});
|
|
1775
|
+
return reduceRunJournal(events, runId);
|
|
1776
|
+
}
|
|
1777
|
+
function projectRunFromSession(source, runId) {
|
|
1778
|
+
const entries = "getEntries" in source ? source.getEntries() : source;
|
|
1779
|
+
return foldRunSessionEntries(entries, runId);
|
|
1780
|
+
}
|
|
1781
|
+
function isRecord(value) {
|
|
1782
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1783
|
+
}
|
|
1784
|
+
function timelineEntryFromCustomEntry(entry) {
|
|
1785
|
+
if (entry.customType !== CUSTOM_TYPE_FOR2["timeline-entry"] || !isRecord(entry.data))
|
|
1786
|
+
return null;
|
|
1787
|
+
const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
|
|
1788
|
+
const type = typeof payload.type === "string" ? payload.type : "timeline";
|
|
1789
|
+
const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
|
|
1790
|
+
const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
|
|
1791
|
+
const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
|
|
1792
|
+
const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
|
|
1793
|
+
return { at, type, stage, status, detail };
|
|
1794
|
+
}
|
|
1795
|
+
function timelineEntriesFromCustomEntries(entries) {
|
|
1796
|
+
return entries.flatMap((entry) => {
|
|
1797
|
+
const timelineEntry = timelineEntryFromCustomEntry(entry);
|
|
1798
|
+
return timelineEntry ? [timelineEntry] : [];
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
function latestTimelineEntriesFromCustomEntries(entries, limit) {
|
|
1802
|
+
if (limit <= 0)
|
|
1803
|
+
return [];
|
|
1804
|
+
const timeline = [];
|
|
1805
|
+
for (let index = entries.length - 1;index >= 0 && timeline.length < limit; index -= 1) {
|
|
1806
|
+
const timelineEntry = timelineEntryFromCustomEntry(entries[index]);
|
|
1807
|
+
if (timelineEntry)
|
|
1808
|
+
timeline.push(timelineEntry);
|
|
1809
|
+
}
|
|
1810
|
+
timeline.reverse();
|
|
1811
|
+
return timeline;
|
|
1812
|
+
}
|
|
1813
|
+
function parseStopSentinel(text, expectedRunId) {
|
|
1814
|
+
const start = text.indexOf(RIG_STOP_SENTINEL2);
|
|
1815
|
+
if (start < 0)
|
|
1816
|
+
return null;
|
|
1817
|
+
const end = text.indexOf(RIG_STOP_SENTINEL_END2, start);
|
|
1818
|
+
const body = text.slice(start + RIG_STOP_SENTINEL2.length, end >= 0 ? end : undefined).trim();
|
|
1819
|
+
const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
|
|
1820
|
+
if (runId && runId !== expectedRunId)
|
|
1821
|
+
return null;
|
|
1822
|
+
const reason = body.match(/(?:^|\s)reason=(.+)$/)?.[1]?.trim() ?? null;
|
|
1823
|
+
return { reason };
|
|
1824
|
+
}
|
|
1825
|
+
function parseControlSentinel(marker, text, expectedRunId) {
|
|
1826
|
+
const start = text.indexOf(marker);
|
|
1827
|
+
if (start < 0)
|
|
1828
|
+
return null;
|
|
1829
|
+
const end = text.indexOf(RIG_CONTROL_SENTINEL_END2, start);
|
|
1830
|
+
const body = text.slice(start + marker.length, end >= 0 ? end : undefined).trim();
|
|
1831
|
+
const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
|
|
1832
|
+
if (runId && runId !== expectedRunId)
|
|
1833
|
+
return null;
|
|
1834
|
+
const requestedBy = body.match(/(?:^|\s)requestedBy=([^\s>]+)/)?.[1] ?? null;
|
|
1835
|
+
return { requestedBy };
|
|
1836
|
+
}
|
|
1837
|
+
function parsePauseSentinel(text, expectedRunId) {
|
|
1838
|
+
return parseControlSentinel(RIG_PAUSE_SENTINEL2, text, expectedRunId);
|
|
1839
|
+
}
|
|
1840
|
+
function parseResumeSentinel(text, expectedRunId) {
|
|
1841
|
+
return parseControlSentinel(RIG_RESUME_SENTINEL2, text, expectedRunId);
|
|
1842
|
+
}
|
|
1843
|
+
function decodeInboxResolutionPayload(payload) {
|
|
1844
|
+
try {
|
|
1845
|
+
const parsed = JSON.parse(decodeURIComponent(payload));
|
|
1846
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1847
|
+
return null;
|
|
1848
|
+
const record = parsed;
|
|
1849
|
+
if (record.kind === "approval") {
|
|
1850
|
+
const requestId = typeof record.requestId === "string" ? record.requestId.trim() : "";
|
|
1851
|
+
const decision = record.decision === "approve" || record.decision === "reject" ? record.decision : null;
|
|
1852
|
+
const note = typeof record.note === "string" ? record.note : record.note === null ? null : undefined;
|
|
1853
|
+
if (!requestId || !decision)
|
|
1854
|
+
return null;
|
|
1855
|
+
return { kind: "approval", requestId, decision, ...note !== undefined ? { note } : {} };
|
|
1856
|
+
}
|
|
1857
|
+
if (record.kind === "input") {
|
|
1858
|
+
const requestId = typeof record.requestId === "string" ? record.requestId.trim() : "";
|
|
1859
|
+
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;
|
|
1860
|
+
if (!requestId || !answers)
|
|
1861
|
+
return null;
|
|
1862
|
+
return { kind: "input", requestId, answers };
|
|
1863
|
+
}
|
|
1864
|
+
} catch {
|
|
1865
|
+
return null;
|
|
1866
|
+
}
|
|
1867
|
+
return null;
|
|
1868
|
+
}
|
|
1869
|
+
function parseInboxResolutionSentinel(text, expectedRunId) {
|
|
1870
|
+
const start = text.indexOf(RIG_INBOX_RESOLUTION_SENTINEL2);
|
|
1871
|
+
if (start < 0)
|
|
1872
|
+
return null;
|
|
1873
|
+
const end = text.indexOf(RIG_CONTROL_SENTINEL_END2, start);
|
|
1874
|
+
const body = text.slice(start + RIG_INBOX_RESOLUTION_SENTINEL2.length, end >= 0 ? end : undefined).trim();
|
|
1875
|
+
const runId = body.match(/(?:^|\s)runId=([^\s>]+)/)?.[1] ?? "";
|
|
1876
|
+
if (runId && runId !== expectedRunId)
|
|
1877
|
+
return null;
|
|
1878
|
+
const requestedBy = body.match(/(?:^|\s)requestedBy=([^\s>]+)/)?.[1] ?? null;
|
|
1879
|
+
const payload = body.match(/(?:^|\s)data=([^\s>]+)/)?.[1] ?? "";
|
|
1880
|
+
const decoded = decodeInboxResolutionPayload(payload);
|
|
1881
|
+
if (!decoded)
|
|
1882
|
+
return null;
|
|
1883
|
+
return { ...decoded, requestedBy };
|
|
1884
|
+
}
|
|
1885
|
+
var decodeRunJournalEvent = (value) => Schema.decodeUnknownSync(RunJournalEvent)(value), RUN_STATUS_TRANSITIONS, TERMINAL_RUN_STATUSES, runSessionJournalCodec;
|
|
1886
|
+
var init_session_journal = __esm(() => {
|
|
1887
|
+
RUN_STATUS_TRANSITIONS = {
|
|
1888
|
+
created: ["queued", "preparing", "running", "failed", "stopped"],
|
|
1889
|
+
queued: ["preparing", "running", "failed", "stopped"],
|
|
1890
|
+
preparing: ["queued", "running", "needs-attention", "failed", "stopped"],
|
|
1891
|
+
running: [
|
|
1892
|
+
"queued",
|
|
1893
|
+
"waiting-approval",
|
|
1894
|
+
"waiting-user-input",
|
|
1895
|
+
"paused",
|
|
1896
|
+
"validating",
|
|
1897
|
+
"reviewing",
|
|
1898
|
+
"closing-out",
|
|
1899
|
+
"needs-attention",
|
|
1900
|
+
"completed",
|
|
1901
|
+
"failed",
|
|
1902
|
+
"stopped"
|
|
1903
|
+
],
|
|
1904
|
+
"waiting-approval": ["running", "needs-attention", "failed", "stopped"],
|
|
1905
|
+
"waiting-user-input": ["running", "needs-attention", "failed", "stopped"],
|
|
1906
|
+
paused: ["running", "failed", "stopped"],
|
|
1907
|
+
validating: ["running", "reviewing", "closing-out", "needs-attention", "completed", "failed", "stopped"],
|
|
1908
|
+
reviewing: ["running", "validating", "closing-out", "needs-attention", "completed", "failed", "stopped"],
|
|
1909
|
+
"closing-out": ["running", "needs-attention", "completed", "failed", "stopped"],
|
|
1910
|
+
"needs-attention": ["queued", "preparing", "running", "closing-out", "completed", "failed", "stopped"],
|
|
1911
|
+
completed: [],
|
|
1912
|
+
failed: ["queued", "preparing", "running", "closing-out"],
|
|
1913
|
+
stopped: ["queued", "preparing", "running", "closing-out"]
|
|
1914
|
+
};
|
|
1915
|
+
TERMINAL_RUN_STATUSES = ["completed", "failed", "stopped"];
|
|
1916
|
+
runSessionJournalCodec = {
|
|
1917
|
+
foldRunSessionEntries,
|
|
1918
|
+
projectRunFromSession,
|
|
1919
|
+
isTerminalRunStatus,
|
|
1920
|
+
canTransitionRunStatus,
|
|
1921
|
+
assertRunStatusTransition,
|
|
1922
|
+
sessionIdFromSessionFile,
|
|
1923
|
+
timelineEntriesFromCustomEntries,
|
|
1924
|
+
latestTimelineEntriesFromCustomEntries,
|
|
1925
|
+
parseStopSentinel,
|
|
1926
|
+
parsePauseSentinel,
|
|
1927
|
+
parseResumeSentinel,
|
|
1928
|
+
parseInboxResolutionSentinel
|
|
1929
|
+
};
|
|
1930
|
+
});
|
|
1931
|
+
|
|
1932
|
+
// packages/run-plugin/src/read-model/reconcile.ts
|
|
1933
|
+
import { defineCapability as defineCapability7 } from "@rig/core/capability";
|
|
1934
|
+
import { loadCapabilityForRoot as loadCapabilityForRoot4 } from "@rig/core/capability-loaders";
|
|
1935
|
+
import {
|
|
1936
|
+
RUN_SESSION_JOURNAL_WRITER as RUN_SESSION_JOURNAL_WRITER4
|
|
1937
|
+
} from "@rig/contracts";
|
|
1938
|
+
async function loadCollabApi() {
|
|
1939
|
+
collabApiPromise ??= import("@oh-my-pi/pi-coding-agent/collab-api").then((api) => api).catch(() => null);
|
|
1940
|
+
return collabApiPromise;
|
|
1941
|
+
}
|
|
1942
|
+
function pidExists(pid) {
|
|
1943
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
1944
|
+
return false;
|
|
1945
|
+
try {
|
|
1946
|
+
process.kill(pid, 0);
|
|
1947
|
+
return true;
|
|
1948
|
+
} catch {
|
|
1949
|
+
return false;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
function projectionRunId(item) {
|
|
1953
|
+
if (item.runId?.trim())
|
|
1954
|
+
return item.runId.trim();
|
|
1955
|
+
if (item.collab?.sessionId?.trim())
|
|
1956
|
+
return item.collab.sessionId.trim();
|
|
1957
|
+
if (item.session.id?.trim())
|
|
1958
|
+
return item.session.id.trim();
|
|
1959
|
+
return null;
|
|
1960
|
+
}
|
|
1961
|
+
function isLocallyDiscovered(item) {
|
|
1962
|
+
return item.source === "session-list";
|
|
1963
|
+
}
|
|
1964
|
+
function isActiveStatus(status) {
|
|
1965
|
+
if (status === null)
|
|
1966
|
+
return false;
|
|
1967
|
+
return !isTerminalRunStatus(status);
|
|
1968
|
+
}
|
|
1969
|
+
function shouldFlip(item, status, exists) {
|
|
1970
|
+
if (!isActiveStatus(status))
|
|
1971
|
+
return false;
|
|
1972
|
+
if (item.collab?.stale === true)
|
|
1973
|
+
return true;
|
|
1974
|
+
const pid = item.collab?.pid;
|
|
1975
|
+
return isLocallyDiscovered(item) && pid !== undefined && ACTIVE_LOCAL_RUN_STATUSES.has(status) && !exists(pid);
|
|
1976
|
+
}
|
|
1977
|
+
async function reconcileActiveRuns(input, deps = {}) {
|
|
1978
|
+
const collabApi = deps.listCollabSessionProjections ? null : await loadCollabApi();
|
|
1979
|
+
const listProjections = deps.listCollabSessionProjections ?? collabApi?.listCollabSessionProjections;
|
|
1980
|
+
const journalWriter = deps.journalWriter ?? await loadCapabilityForRoot4(input.workspaceRoot, RunSessionJournalWriterCap3);
|
|
1981
|
+
if (!journalWriter)
|
|
1982
|
+
return { flipped: [], resumable: [] };
|
|
1983
|
+
const exists = deps.processExists ?? pidExists;
|
|
1984
|
+
if (!listProjections)
|
|
1985
|
+
return { flipped: [], resumable: [] };
|
|
1986
|
+
const projections = await listProjections(input.identityFilter);
|
|
1987
|
+
const flipped = [];
|
|
1988
|
+
for (const item of projections) {
|
|
1989
|
+
const runId = projectionRunId(item);
|
|
1990
|
+
const sessionPath = item.session.path || item.collab?.sessionPath || "";
|
|
1991
|
+
if (!runId || !sessionPath)
|
|
1992
|
+
continue;
|
|
1993
|
+
const projection = projectRunFromSession(item.customEntries, runId);
|
|
1994
|
+
const status = projection.status;
|
|
1995
|
+
if (status === null || !shouldFlip(item, status, exists))
|
|
1996
|
+
continue;
|
|
1997
|
+
const result = await journalWriter.reconcileDeadPid({
|
|
1998
|
+
projectRoot: input.workspaceRoot,
|
|
1999
|
+
runId,
|
|
2000
|
+
sessionPath,
|
|
2001
|
+
reason: "lazy-reconcile:dead-pid"
|
|
2002
|
+
});
|
|
2003
|
+
if (result.updated)
|
|
2004
|
+
flipped.push({ runId, sessionPath, reason: "lazy-reconcile:dead-pid" });
|
|
2005
|
+
}
|
|
2006
|
+
return { flipped, resumable: flipped };
|
|
2007
|
+
}
|
|
2008
|
+
var RunSessionJournalWriterCap3, collabApiPromise = null, ACTIVE_LOCAL_RUN_STATUSES;
|
|
2009
|
+
var init_reconcile = __esm(() => {
|
|
2010
|
+
init_session_journal();
|
|
2011
|
+
RunSessionJournalWriterCap3 = defineCapability7(RUN_SESSION_JOURNAL_WRITER4);
|
|
2012
|
+
ACTIVE_LOCAL_RUN_STATUSES = new Set([
|
|
2013
|
+
"created",
|
|
2014
|
+
"preparing",
|
|
2015
|
+
"running",
|
|
2016
|
+
"validating",
|
|
2017
|
+
"reviewing",
|
|
2018
|
+
"closing-out"
|
|
2019
|
+
]);
|
|
2020
|
+
});
|
|
2021
|
+
|
|
2022
|
+
// packages/run-plugin/src/read-model/run-format.ts
|
|
2023
|
+
import pc from "picocolors";
|
|
2024
|
+
import { formatSection, formatSuccessCard, formatNextSteps } from "@rig/std-shared/cli-format";
|
|
2025
|
+
function projectedStatusRole(value) {
|
|
2026
|
+
return typeof value === "string" && STATUS_ROLES.has(value) ? value : null;
|
|
2027
|
+
}
|
|
2028
|
+
function truncate(value, width) {
|
|
2029
|
+
return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
2030
|
+
}
|
|
2031
|
+
function pad(value, width) {
|
|
2032
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
2033
|
+
}
|
|
2034
|
+
function colorForRole(role) {
|
|
2035
|
+
switch (role) {
|
|
2036
|
+
case "success":
|
|
2037
|
+
return pc.green;
|
|
2038
|
+
case "action-yellow":
|
|
2039
|
+
return pc.yellow;
|
|
2040
|
+
case "active-cyan":
|
|
2041
|
+
return pc.cyan;
|
|
2042
|
+
case "failure":
|
|
2043
|
+
return pc.red;
|
|
2044
|
+
case "muted":
|
|
2045
|
+
case "neutral":
|
|
2046
|
+
return pc.dim;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
function inboxStatusRole(record) {
|
|
2050
|
+
const projected = projectedStatusRole(record.statusRole);
|
|
2051
|
+
if (projected)
|
|
2052
|
+
return projected;
|
|
2053
|
+
return record.status === "pending" ? "active-cyan" : "neutral";
|
|
2054
|
+
}
|
|
2055
|
+
function compactDate(value) {
|
|
2056
|
+
const parsed = Date.parse(value);
|
|
2057
|
+
if (!Number.isFinite(parsed))
|
|
2058
|
+
return value;
|
|
2059
|
+
return new Date(parsed).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
|
|
2060
|
+
}
|
|
2061
|
+
function firstString(record, keys, fallback = "") {
|
|
2062
|
+
for (const key of keys) {
|
|
2063
|
+
const value = record[key];
|
|
2064
|
+
if (typeof value === "string" && value.trim())
|
|
2065
|
+
return value;
|
|
2066
|
+
}
|
|
2067
|
+
return fallback;
|
|
2068
|
+
}
|
|
2069
|
+
function runIdOf(run) {
|
|
2070
|
+
return firstString(run, ["runId", "id"], "(unknown-run)");
|
|
2071
|
+
}
|
|
2072
|
+
function taskIdOf(run) {
|
|
2073
|
+
return firstString(run, ["taskId", "task", "task_id"]);
|
|
2074
|
+
}
|
|
2075
|
+
function runTitleOf(run) {
|
|
2076
|
+
return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
|
|
2077
|
+
}
|
|
2078
|
+
function requestIdOf(entry) {
|
|
2079
|
+
return firstString(entry, ["requestId", "id", "approvalId", "inputId"], "(unknown-request)");
|
|
2080
|
+
}
|
|
2081
|
+
function runLikeStatusText(run) {
|
|
2082
|
+
const record = run;
|
|
2083
|
+
const status = typeof record.status === "string" ? record.status.trim() : "";
|
|
2084
|
+
return status || "unclassified";
|
|
2085
|
+
}
|
|
2086
|
+
function runLikeStatusColor(run) {
|
|
2087
|
+
const record = run;
|
|
2088
|
+
return colorForRole(projectedStatusRole(record.statusRole) ?? "neutral");
|
|
2089
|
+
}
|
|
2090
|
+
function formatStatusPill(status, role) {
|
|
2091
|
+
const label = status || "unclassified";
|
|
2092
|
+
return colorForRole(role ?? "neutral")(`\u25CF ${label}`);
|
|
2093
|
+
}
|
|
2094
|
+
function formatLegacyAutomationSurface() {
|
|
2095
|
+
return [];
|
|
2096
|
+
}
|
|
2097
|
+
function formatRunList(runs, options = {}) {
|
|
2098
|
+
if (runs.length === 0) {
|
|
2099
|
+
return [
|
|
2100
|
+
formatSection("Runs", "none recorded"),
|
|
2101
|
+
options.source === "server" ? dim("No runs recorded on the selected server.") : dim("No runs recorded in local state."),
|
|
2102
|
+
"",
|
|
2103
|
+
...formatLegacyAutomationSurface()
|
|
2104
|
+
].join(`
|
|
2105
|
+
`);
|
|
2106
|
+
}
|
|
2107
|
+
const body = runs.map((run) => {
|
|
2108
|
+
const row = run;
|
|
2109
|
+
const runId = runIdOf(row);
|
|
2110
|
+
const status = runLikeStatusText(run);
|
|
2111
|
+
const runtime = firstString(row, ["runtime", "runtimeAdapter"], "");
|
|
2112
|
+
return [
|
|
2113
|
+
pc.bold(runId),
|
|
2114
|
+
runLikeStatusColor(run)(pad(truncate(status, 12), 12)),
|
|
2115
|
+
`${runTitleOf(row)}${runtime ? dim(` ${runtime}`) : ""}`
|
|
2116
|
+
].join(" ");
|
|
2117
|
+
});
|
|
2118
|
+
return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), ...body, "", ...formatLegacyAutomationSurface()].join(`
|
|
2119
|
+
`);
|
|
2120
|
+
}
|
|
2121
|
+
function formatSubmittedRun(input) {
|
|
2122
|
+
return formatSuccessCard("Run submitted", [
|
|
2123
|
+
["run", input.runId],
|
|
2124
|
+
["task", input.taskId ?? undefined],
|
|
2125
|
+
["title", input.title ?? undefined],
|
|
2126
|
+
["queue", input.queuePosition ?? undefined]
|
|
2127
|
+
]);
|
|
2128
|
+
}
|
|
2129
|
+
function formatRunCard(run, options = {}) {
|
|
2130
|
+
const record = run;
|
|
2131
|
+
const lines = [formatSection(options.title ?? "Run")];
|
|
2132
|
+
for (const [label, value] of [
|
|
2133
|
+
["run", runIdOf(record)],
|
|
2134
|
+
["task", taskIdOf(record)],
|
|
2135
|
+
["title", runTitleOf(record)],
|
|
2136
|
+
["status", runLikeStatusText(run)],
|
|
2137
|
+
["runtime", firstString(record, ["runtime", "runtimeAdapter"], "")],
|
|
2138
|
+
["created", firstString(record, ["createdAt"], "")],
|
|
2139
|
+
["updated", firstString(record, ["updatedAt"], "")]
|
|
2140
|
+
]) {
|
|
2141
|
+
if (value)
|
|
2142
|
+
lines.push(`${faintBar} ${dim(label.padEnd(12))} ${label === "status" ? runLikeStatusColor(run)(value) : label === "created" || label === "updated" ? compactDate(value) : value}`);
|
|
2143
|
+
}
|
|
2144
|
+
const errorSummary = firstString(record, ["errorSummary"]);
|
|
2145
|
+
if (errorSummary)
|
|
2146
|
+
lines.push(`${faintBar} Error: ${errorSummary}`);
|
|
2147
|
+
return lines.join(`
|
|
2148
|
+
`);
|
|
2149
|
+
}
|
|
2150
|
+
function formatRunStatus(summary, options = {}) {
|
|
2151
|
+
const activeRuns = summary.activeRuns ?? [];
|
|
2152
|
+
const recentRuns = summary.recentRuns ?? [];
|
|
2153
|
+
const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
|
|
2154
|
+
lines.push(`Active runs (${activeRuns.length})`);
|
|
2155
|
+
if (activeRuns.length === 0)
|
|
2156
|
+
lines.push(dim("No active runs."));
|
|
2157
|
+
else
|
|
2158
|
+
for (const run of activeRuns)
|
|
2159
|
+
lines.push(formatRunSummaryLine(run));
|
|
2160
|
+
lines.push("", `Recent runs (${recentRuns.length})`);
|
|
2161
|
+
if (recentRuns.length === 0)
|
|
2162
|
+
lines.push(dim("No recent terminal runs."));
|
|
2163
|
+
else
|
|
2164
|
+
for (const run of recentRuns.slice(0, 10))
|
|
2165
|
+
lines.push(formatRunSummaryLine(run));
|
|
2166
|
+
lines.push("", ...formatLegacyAutomationSurface());
|
|
2167
|
+
return lines.join(`
|
|
2168
|
+
`);
|
|
2169
|
+
}
|
|
2170
|
+
function formatRunSummaryLine(run) {
|
|
2171
|
+
const record = run;
|
|
2172
|
+
const runId = runIdOf(record);
|
|
2173
|
+
const taskId = taskIdOf(record);
|
|
2174
|
+
const title = runTitleOf(record);
|
|
2175
|
+
const status = runLikeStatusText(run);
|
|
2176
|
+
const runtime = firstString(record, ["runtime", "runtimeAdapter"], "");
|
|
2177
|
+
const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
|
|
2178
|
+
return `${faintBar} ${pc.bold(runId)} ${runLikeStatusColor(run)(`\u25CF ${status}`)} ${descriptor}${runtime ? dim(` ${runtime}`) : ""}`;
|
|
2179
|
+
}
|
|
2180
|
+
function formatInboxList(kind, entries) {
|
|
2181
|
+
const title = kind === "approvals" ? "Pending approvals" : "Pending user input";
|
|
2182
|
+
if (entries.length === 0) {
|
|
2183
|
+
return [
|
|
2184
|
+
formatSection(title, "empty"),
|
|
2185
|
+
dim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
|
|
2186
|
+
"",
|
|
2187
|
+
...formatLegacyAutomationSurface()
|
|
2188
|
+
].join(`
|
|
2189
|
+
`);
|
|
2190
|
+
}
|
|
2191
|
+
const lines = [formatSection(title, `${entries.length}`)];
|
|
2192
|
+
for (const record of entries) {
|
|
2193
|
+
const runId = firstString(record, ["runId"], "(unknown-run)");
|
|
2194
|
+
const taskId = firstString(record, ["taskId"], "");
|
|
2195
|
+
const status = firstString(record, ["status"], "pending");
|
|
2196
|
+
const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
|
|
2197
|
+
lines.push(`${faintBar} ${pc.bold(requestIdOf(record))} ${formatStatusPill(status, inboxStatusRole(record))} ${prompt}`);
|
|
2198
|
+
lines.push(`${faintBar} ${dim("run".padEnd(12))} ${runId}${taskId ? dim(` task ${taskId}`) : ""}`);
|
|
2199
|
+
}
|
|
2200
|
+
lines.push("", ...formatLegacyAutomationSurface(), ...formatNextSteps(kind === "approvals" ? ["Resolve: `rig inbox approve --run <run-id> --request <request-id> --decision approve|reject`"] : ["Reply: `rig inbox answer --run <run-id> --request <request-id> --text ...`"]));
|
|
2201
|
+
return lines.join(`
|
|
2202
|
+
`);
|
|
2203
|
+
}
|
|
2204
|
+
var dim, faintBar, accent, STATUS_ROLES;
|
|
2205
|
+
var init_run_format = __esm(() => {
|
|
2206
|
+
dim = pc.dim;
|
|
2207
|
+
faintBar = pc.dim("\u2502");
|
|
2208
|
+
accent = pc.cyan;
|
|
2209
|
+
STATUS_ROLES = new Set([
|
|
2210
|
+
"success",
|
|
2211
|
+
"action-yellow",
|
|
2212
|
+
"active-cyan",
|
|
2213
|
+
"failure",
|
|
2214
|
+
"muted",
|
|
2215
|
+
"neutral"
|
|
2216
|
+
]);
|
|
2217
|
+
});
|
|
2218
|
+
|
|
2219
|
+
// packages/run-plugin/src/read-model/read-model-backend/guard.ts
|
|
2220
|
+
var TERMINAL;
|
|
2221
|
+
var init_guard = __esm(() => {
|
|
2222
|
+
TERMINAL = new Set(["completed", "failed", "stopped"]);
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
// packages/run-plugin/src/read-model/read-model-backend/run-status.ts
|
|
2226
|
+
function isTerminalRunStatus2(status) {
|
|
2227
|
+
return TERMINAL_RUN_STATUSES2.includes(status);
|
|
2228
|
+
}
|
|
2229
|
+
function isActiveRunStatus(status) {
|
|
2230
|
+
return !isTerminalRunStatus2(status);
|
|
2231
|
+
}
|
|
2232
|
+
function isRuntimeActiveStatus(status) {
|
|
2233
|
+
return RUNTIME_ACTIVE_STATUSES.has(normalizeRunStatusToken(status));
|
|
2234
|
+
}
|
|
2235
|
+
function normalizeRunStatusToken(status) {
|
|
2236
|
+
return String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
2237
|
+
}
|
|
2238
|
+
function isOperatorActiveRunStatus(status) {
|
|
2239
|
+
const normalized = normalizeRunStatusToken(status);
|
|
2240
|
+
if (!normalized)
|
|
2241
|
+
return false;
|
|
2242
|
+
return !OPERATOR_INACTIVE_RUN_STATUSES.has(normalized);
|
|
2243
|
+
}
|
|
2244
|
+
function canonicalStatusToken(status) {
|
|
2245
|
+
const normalized = normalizeRunStatusToken(status);
|
|
2246
|
+
if (normalized === "waiting-input")
|
|
2247
|
+
return "waiting-user-input";
|
|
2248
|
+
return normalized;
|
|
2249
|
+
}
|
|
2250
|
+
function asRunStatus(status) {
|
|
2251
|
+
return KNOWN_RUN_STATUS[status] ? status : null;
|
|
2252
|
+
}
|
|
2253
|
+
function statusColorRole(status) {
|
|
2254
|
+
switch (canonicalStatusToken(status)) {
|
|
2255
|
+
case "done":
|
|
2256
|
+
case "completed":
|
|
2257
|
+
case "ready":
|
|
2258
|
+
case "healthy":
|
|
2259
|
+
case "approved":
|
|
2260
|
+
case "merged":
|
|
2261
|
+
return "success";
|
|
2262
|
+
case "needs-attention":
|
|
2263
|
+
case "waiting-approval":
|
|
2264
|
+
case "waiting-user-input":
|
|
2265
|
+
case "blocked":
|
|
2266
|
+
case "paused":
|
|
2267
|
+
return "action-yellow";
|
|
2268
|
+
case "running":
|
|
2269
|
+
case "adopted":
|
|
2270
|
+
case "preparing":
|
|
2271
|
+
case "created":
|
|
2272
|
+
case "queued":
|
|
2273
|
+
case "starting":
|
|
2274
|
+
case "pending":
|
|
2275
|
+
case "in-progress":
|
|
2276
|
+
case "active":
|
|
2277
|
+
case "booting":
|
|
2278
|
+
case "validating":
|
|
2279
|
+
case "reviewing":
|
|
2280
|
+
case "closing-out":
|
|
2281
|
+
return "active-cyan";
|
|
2282
|
+
case "failed":
|
|
2283
|
+
case "error":
|
|
2284
|
+
case "rejected":
|
|
2285
|
+
return "failure";
|
|
2286
|
+
case "stopped":
|
|
2287
|
+
case "cancelled":
|
|
2288
|
+
case "canceled":
|
|
2289
|
+
case "stale":
|
|
2290
|
+
return "muted";
|
|
2291
|
+
default:
|
|
2292
|
+
return "neutral";
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
function isNeedsAttention(run) {
|
|
2296
|
+
return canonicalStatusToken(run.status) === "needs-attention" || run.pendingApprovals + run.pendingInputs > 0 || run.stallCount > 0;
|
|
2297
|
+
}
|
|
2298
|
+
function phaseForStatus(status, runStatus, needsAttention) {
|
|
2299
|
+
if (runStatus && isTerminalRunStatus2(runStatus))
|
|
2300
|
+
return runStatus === "completed" || runStatus === "failed" || runStatus === "stopped" ? runStatus : "stopped";
|
|
2301
|
+
if (needsAttention)
|
|
2302
|
+
return "needs-attention";
|
|
2303
|
+
switch (status) {
|
|
2304
|
+
case "created":
|
|
2305
|
+
case "queued":
|
|
2306
|
+
case "preparing":
|
|
2307
|
+
case "starting":
|
|
2308
|
+
case "booting":
|
|
2309
|
+
return "starting";
|
|
2310
|
+
case "waiting-approval":
|
|
2311
|
+
case "waiting-user-input":
|
|
2312
|
+
return "waiting";
|
|
2313
|
+
case "paused":
|
|
2314
|
+
return "paused";
|
|
2315
|
+
case "running":
|
|
2316
|
+
case "validating":
|
|
2317
|
+
case "reviewing":
|
|
2318
|
+
case "closing-out":
|
|
2319
|
+
return "active";
|
|
2320
|
+
default:
|
|
2321
|
+
return runStatus && isActiveRunStatus(runStatus) ? "active" : "unknown";
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
function classifyRun(run) {
|
|
2325
|
+
const status = canonicalStatusToken(run.status) || (run.live && !run.stale ? "starting" : "unknown");
|
|
2326
|
+
const runStatus = asRunStatus(status);
|
|
2327
|
+
const isTerminal = runStatus ? isTerminalRunStatus2(runStatus) : false;
|
|
2328
|
+
const isNeedsAttentionValue = isNeedsAttention(run);
|
|
2329
|
+
const phase = phaseForStatus(status, runStatus, isNeedsAttentionValue);
|
|
2330
|
+
return {
|
|
2331
|
+
status,
|
|
2332
|
+
runStatus: runStatus ?? "needs-attention",
|
|
2333
|
+
phase,
|
|
2334
|
+
isActive: runStatus ? isActiveRunStatus(runStatus) : !isTerminal && status !== "unknown",
|
|
2335
|
+
isTerminal,
|
|
2336
|
+
isNeedsAttention: isNeedsAttentionValue,
|
|
2337
|
+
offersRecovery: phase === "failed"
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
function runStatusColorRole(run) {
|
|
2341
|
+
const classification = classifyRun(run);
|
|
2342
|
+
return classification.isNeedsAttention && !classification.isTerminal ? "action-yellow" : statusColorRole(classification.status);
|
|
2343
|
+
}
|
|
2344
|
+
function statusRank(run) {
|
|
2345
|
+
const classification = classifyRun(run);
|
|
2346
|
+
if (classification.isNeedsAttention)
|
|
2347
|
+
return 0;
|
|
2348
|
+
switch (classification.phase) {
|
|
2349
|
+
case "needs-attention":
|
|
2350
|
+
return 0;
|
|
2351
|
+
case "active":
|
|
2352
|
+
case "waiting":
|
|
2353
|
+
case "paused":
|
|
2354
|
+
return 1;
|
|
2355
|
+
case "starting":
|
|
2356
|
+
return 2;
|
|
2357
|
+
case "completed":
|
|
2358
|
+
return 3;
|
|
2359
|
+
case "failed":
|
|
2360
|
+
return 4;
|
|
2361
|
+
case "stopped":
|
|
2362
|
+
return 5;
|
|
2363
|
+
case "unknown":
|
|
2364
|
+
return 6;
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
var OPERATOR_INACTIVE_RUN_STATUSES, TERMINAL_RUN_STATUSES2, RUNTIME_ACTIVE_STATUSES, ACTIVE_RUN_STATUSES, KNOWN_RUN_STATUS;
|
|
2368
|
+
var init_run_status = __esm(() => {
|
|
2369
|
+
OPERATOR_INACTIVE_RUN_STATUSES = new Set([
|
|
2370
|
+
"completed",
|
|
2371
|
+
"complete",
|
|
2372
|
+
"done",
|
|
2373
|
+
"success",
|
|
2374
|
+
"succeeded",
|
|
2375
|
+
"passed",
|
|
2376
|
+
"failed",
|
|
2377
|
+
"failure",
|
|
2378
|
+
"error",
|
|
2379
|
+
"errored",
|
|
2380
|
+
"stopped",
|
|
2381
|
+
"cancelled",
|
|
2382
|
+
"canceled",
|
|
2383
|
+
"aborted",
|
|
2384
|
+
"abort",
|
|
2385
|
+
"merged",
|
|
2386
|
+
"closed",
|
|
2387
|
+
"skipped",
|
|
2388
|
+
"needs-attention",
|
|
2389
|
+
"needs_attention"
|
|
2390
|
+
]);
|
|
2391
|
+
TERMINAL_RUN_STATUSES2 = ["completed", "failed", "stopped"];
|
|
2392
|
+
RUNTIME_ACTIVE_STATUSES = new Set([
|
|
2393
|
+
"created",
|
|
2394
|
+
"preparing",
|
|
2395
|
+
"running",
|
|
2396
|
+
"validating",
|
|
2397
|
+
"reviewing",
|
|
2398
|
+
"closing-out"
|
|
2399
|
+
]);
|
|
2400
|
+
ACTIVE_RUN_STATUSES = [
|
|
2401
|
+
"created",
|
|
2402
|
+
"queued",
|
|
2403
|
+
"preparing",
|
|
2404
|
+
"running",
|
|
2405
|
+
"waiting-approval",
|
|
2406
|
+
"waiting-user-input",
|
|
2407
|
+
"paused",
|
|
2408
|
+
"validating",
|
|
2409
|
+
"reviewing",
|
|
2410
|
+
"closing-out",
|
|
2411
|
+
"needs-attention"
|
|
2412
|
+
];
|
|
2413
|
+
KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES2].map((status) => [status, true]));
|
|
2414
|
+
});
|
|
2415
|
+
|
|
2416
|
+
// packages/run-plugin/src/read-model/read-model-backend/diagnostics.ts
|
|
2417
|
+
function normalizeString(value) {
|
|
2418
|
+
if (typeof value !== "string")
|
|
2419
|
+
return null;
|
|
2420
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
2421
|
+
return normalized.length > 0 ? normalized : null;
|
|
2422
|
+
}
|
|
2423
|
+
function isGenericRunFailure(value) {
|
|
2424
|
+
const text = normalizeString(value);
|
|
2425
|
+
return Boolean(text && /^Task run failed \([^)]*\)$/i.test(text));
|
|
2426
|
+
}
|
|
2427
|
+
function appendCandidate(candidates, value) {
|
|
2428
|
+
const text = normalizeString(value);
|
|
2429
|
+
if (text && !candidates.includes(text))
|
|
2430
|
+
candidates.push(text);
|
|
2431
|
+
}
|
|
2432
|
+
function categorizeUsefulRunError(lines) {
|
|
2433
|
+
const taskSourceFailure = lines.find((line) => /failed to update task source/i.test(line));
|
|
2434
|
+
if (taskSourceFailure)
|
|
2435
|
+
return `Task source update failed: ${taskSourceFailure}`;
|
|
2436
|
+
const moduleFailure = lines.find((line) => /cannot find module/i.test(line));
|
|
2437
|
+
if (moduleFailure)
|
|
2438
|
+
return `Runtime module resolution failed: ${moduleFailure}`;
|
|
2439
|
+
const providerFailure = lines.find((line) => /no api key found|unauthorized|authentication failed|invalid api key/i.test(line));
|
|
2440
|
+
if (providerFailure)
|
|
2441
|
+
return `Provider authentication failed: ${providerFailure}`;
|
|
2442
|
+
return null;
|
|
2443
|
+
}
|
|
2444
|
+
function summarizeRunError(projection) {
|
|
2445
|
+
if (projection.status !== "failed")
|
|
2446
|
+
return null;
|
|
2447
|
+
const candidates = [];
|
|
2448
|
+
for (const anomaly of projection.anomalies) {
|
|
2449
|
+
appendCandidate(candidates, `Journal anomaly (${anomaly.kind}): ${anomaly.detail}`);
|
|
2450
|
+
}
|
|
2451
|
+
for (const phase of projection.closeoutPhases) {
|
|
2452
|
+
if (phase.outcome === "failed")
|
|
2453
|
+
appendCandidate(candidates, phase.detail);
|
|
2454
|
+
}
|
|
2455
|
+
for (const entry of projection.statusHistory) {
|
|
2456
|
+
appendCandidate(candidates, entry.reason);
|
|
2457
|
+
}
|
|
2458
|
+
appendCandidate(candidates, projection.record.statusDetail);
|
|
2459
|
+
appendCandidate(candidates, projection.record.errorText);
|
|
2460
|
+
const nonGeneric = candidates.filter((candidate) => !isGenericRunFailure(candidate));
|
|
2461
|
+
return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
// packages/run-plugin/src/read-model/read-model-backend/projection.ts
|
|
2465
|
+
var exports_projection = {};
|
|
2466
|
+
__export(exports_projection, {
|
|
2467
|
+
selectRunProjection: () => selectRunProjection,
|
|
2468
|
+
runRecordsFromCollab: () => runRecordsFromCollab,
|
|
2469
|
+
runRecordFromRegistryEntry: () => runRecordFromRegistryEntry,
|
|
2470
|
+
resolveRunJoinTarget: () => resolveRunJoinTarget,
|
|
2471
|
+
resolveJoinTarget: () => resolveJoinTarget,
|
|
2472
|
+
readSessionRunEntries: () => readSessionRunEntries,
|
|
2473
|
+
listRuns: () => listRuns,
|
|
2474
|
+
listRunProjections: () => listRunProjections,
|
|
2475
|
+
getRunProjection: () => getRunProjection,
|
|
2476
|
+
getRun: () => getRun,
|
|
2477
|
+
discoveryDiagnosticRunRecord: () => discoveryDiagnosticRunRecord
|
|
2478
|
+
});
|
|
2479
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
2480
|
+
import { isAbsolute, relative, resolve as resolve2 } from "path";
|
|
2481
|
+
import { RUN_DISCOVERY, RUN_REGISTRY_BACKBONE as RUN_REGISTRY_BACKBONE2 } from "@rig/contracts";
|
|
2482
|
+
import { loadCapabilityForRoot as loadCapabilityForRoot5 } from "@rig/core/capability-loaders";
|
|
2483
|
+
import { defineCapability as defineCapability8 } from "@rig/core/capability";
|
|
2484
|
+
function registryEntryFromCollab(collab) {
|
|
2485
|
+
const record = collab;
|
|
2486
|
+
return {
|
|
2487
|
+
roomId: collab.sessionId,
|
|
2488
|
+
title: collab.title,
|
|
2489
|
+
status: record.registryStatus ?? (collab.stale ? "stale" : "running"),
|
|
2490
|
+
startedAt: collab.startedAt ?? null,
|
|
2491
|
+
heartbeatAt: collab.updatedAt ?? null,
|
|
2492
|
+
sessionPath: collab.sessionPath ?? null,
|
|
2493
|
+
cwd: collab.cwd ?? null,
|
|
2494
|
+
joinLink: collab.joinLink ?? null,
|
|
2495
|
+
webLink: collab.webLink ?? null,
|
|
2496
|
+
relayUrl: collab.relayUrl ?? null,
|
|
2497
|
+
stale: collab.stale,
|
|
2498
|
+
repo: collab.selectedRepo ?? null,
|
|
2499
|
+
...collab.pid === undefined ? {} : { pid: collab.pid },
|
|
2500
|
+
projection: record.registryProjection ?? null
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
function readSessionRunEntries(sessionPath) {
|
|
2504
|
+
if (!sessionPath || !existsSync2(sessionPath))
|
|
2505
|
+
return [];
|
|
2506
|
+
try {
|
|
2507
|
+
return parseSessionRunEntries(readFileSync(sessionPath, "utf8"));
|
|
2508
|
+
} catch {
|
|
2509
|
+
return [];
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
function parseSessionRunEntries(raw) {
|
|
2513
|
+
const entries = [];
|
|
2514
|
+
for (const line of raw.split(`
|
|
2515
|
+
`)) {
|
|
2516
|
+
if (!line.trim())
|
|
2517
|
+
continue;
|
|
2518
|
+
try {
|
|
2519
|
+
const parsed = JSON.parse(line);
|
|
2520
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2521
|
+
continue;
|
|
2522
|
+
entries.push({
|
|
2523
|
+
type: "type" in parsed && typeof parsed.type === "string" ? parsed.type : "",
|
|
2524
|
+
..."customType" in parsed && typeof parsed.customType === "string" ? { customType: parsed.customType } : {},
|
|
2525
|
+
..."data" in parsed ? { data: parsed.data } : {}
|
|
2526
|
+
});
|
|
2527
|
+
} catch {
|
|
2528
|
+
continue;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
return entries;
|
|
2532
|
+
}
|
|
2533
|
+
function stringOrNull(value) {
|
|
2534
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
2535
|
+
}
|
|
2536
|
+
function numberOrNull(value) {
|
|
2537
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2538
|
+
}
|
|
2539
|
+
function objectRecord(value) {
|
|
2540
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2541
|
+
}
|
|
2542
|
+
function payloadString(payload, keys) {
|
|
2543
|
+
if (!payload || typeof payload !== "object")
|
|
2544
|
+
return null;
|
|
2545
|
+
const record = payload;
|
|
2546
|
+
for (const key of keys) {
|
|
2547
|
+
const value = record[key];
|
|
2548
|
+
if (typeof value === "string" && value.trim())
|
|
2549
|
+
return value.trim();
|
|
2550
|
+
}
|
|
2551
|
+
return null;
|
|
2552
|
+
}
|
|
2553
|
+
function payloadOptions(payload) {
|
|
2554
|
+
if (!payload || typeof payload !== "object")
|
|
2555
|
+
return;
|
|
2556
|
+
const record = payload;
|
|
2557
|
+
const options = Array.isArray(record.options) ? record.options : Array.isArray(record.choices) ? record.choices : null;
|
|
2558
|
+
const values = options?.filter((value) => typeof value === "string" && value.trim().length > 0) ?? [];
|
|
2559
|
+
return values.length > 0 ? values : undefined;
|
|
2560
|
+
}
|
|
2561
|
+
function inboxRequest(request, kind) {
|
|
2562
|
+
const fallback = kind === "approval" ? "Approval requested" : "Input requested";
|
|
2563
|
+
const body = payloadString(request.payload, ["body", "description", "detail", "details"]);
|
|
2564
|
+
const options = payloadOptions(request.payload);
|
|
2565
|
+
return {
|
|
2566
|
+
requestId: request.requestId,
|
|
2567
|
+
kind,
|
|
2568
|
+
title: payloadString(request.payload, ["title", "message", "reason", "prompt", "summary"]) ?? fallback,
|
|
2569
|
+
...body !== undefined ? { body } : {},
|
|
2570
|
+
...options ? { options } : {},
|
|
2571
|
+
requestedAt: request.requestedAt ?? null,
|
|
2572
|
+
source: "run"
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
function inboxFromProjection(projection) {
|
|
2576
|
+
return [
|
|
2577
|
+
...projection.pendingApprovals.map((request) => inboxRequest(request, "approval")),
|
|
2578
|
+
...projection.pendingUserInputs.map((request) => inboxRequest(request, "input"))
|
|
2579
|
+
].sort((a, b) => (Date.parse(a.requestedAt ?? "") || 0) - (Date.parse(b.requestedAt ?? "") || 0));
|
|
2580
|
+
}
|
|
2581
|
+
function isProjectPath(projectRoot, cwd) {
|
|
2582
|
+
const root = resolve2(projectRoot);
|
|
2583
|
+
const candidate = resolve2(cwd);
|
|
2584
|
+
const pathFromRoot = relative(root, candidate);
|
|
2585
|
+
return pathFromRoot === "" || !pathFromRoot.startsWith("../") && pathFromRoot !== ".." && !isAbsolute(pathFromRoot);
|
|
2586
|
+
}
|
|
2587
|
+
function sourceFromEntry(projectRoot, projection, entry) {
|
|
2588
|
+
const candidates = [
|
|
2589
|
+
stringOrNull(projection.collabCwd),
|
|
2590
|
+
stringOrNull(projection.cwd),
|
|
2591
|
+
stringOrNull(entry.cwd),
|
|
2592
|
+
stringOrNull(projection.worktreePath)
|
|
2593
|
+
].filter((cwd) => cwd !== null);
|
|
2594
|
+
return candidates.some((cwd) => isProjectPath(projectRoot, cwd)) ? "local" : "remote";
|
|
2595
|
+
}
|
|
2596
|
+
function pendingRequests(value, fallbackKind) {
|
|
2597
|
+
if (!Array.isArray(value))
|
|
2598
|
+
return [];
|
|
2599
|
+
return value.flatMap((item) => {
|
|
2600
|
+
const record = objectRecord(item);
|
|
2601
|
+
const requestId = stringOrNull(record?.requestId) ?? stringOrNull(record?.id);
|
|
2602
|
+
if (!record || !requestId)
|
|
2603
|
+
return [];
|
|
2604
|
+
return [{
|
|
2605
|
+
requestId,
|
|
2606
|
+
requestKind: stringOrNull(record.requestKind) ?? stringOrNull(record.kind) ?? fallbackKind,
|
|
2607
|
+
actionId: stringOrNull(record.actionId),
|
|
2608
|
+
payload: "payload" in record ? record.payload : record,
|
|
2609
|
+
requestedAt: stringOrNull(record.requestedAt) ?? stringOrNull(record.at) ?? ""
|
|
2610
|
+
}];
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
function emptyFoldedProjection(runId, projection, toRunStatus) {
|
|
2614
|
+
const projectionRecord = projection;
|
|
2615
|
+
const nestedProjection = objectRecord(projectionRecord.projection);
|
|
2616
|
+
const pendingApprovals = pendingRequests(nestedProjection?.pendingApprovals ?? projectionRecord.pendingApprovals, "approval");
|
|
2617
|
+
const pendingUserInputs = pendingRequests(nestedProjection?.pendingUserInputs ?? nestedProjection?.pendingInputs ?? projectionRecord.pendingUserInputs ?? (Array.isArray(projectionRecord.pendingInputs) ? projectionRecord.pendingInputs : undefined), "user-input");
|
|
2618
|
+
const nestedRecord = objectRecord(nestedProjection?.record);
|
|
2619
|
+
const nestedFolded = nestedProjection;
|
|
2620
|
+
return {
|
|
2621
|
+
...EMPTY_PROJECTION,
|
|
2622
|
+
...nestedFolded ?? {},
|
|
2623
|
+
record: {
|
|
2624
|
+
...nestedRecord ?? {},
|
|
2625
|
+
runId,
|
|
2626
|
+
...projection.taskId ? { taskId: projection.taskId } : {},
|
|
2627
|
+
...projection.title ? { title: projection.title } : {},
|
|
2628
|
+
...projection.startedAt ? { startedAt: projection.startedAt } : {},
|
|
2629
|
+
...projection.updatedAt ? { updatedAt: projection.updatedAt } : {},
|
|
2630
|
+
...projection.completedAt ? { completedAt: projection.completedAt } : {},
|
|
2631
|
+
...projection.sessionPath ? { sessionPath: projection.sessionPath } : {},
|
|
2632
|
+
...projection.worktreePath ? { worktreePath: projection.worktreePath } : {},
|
|
2633
|
+
...projection.prUrl ? { prUrl: projection.prUrl } : {}
|
|
2634
|
+
},
|
|
2635
|
+
status: toRunStatus(projection.status) ?? toRunStatus(nestedProjection?.status),
|
|
2636
|
+
pendingApprovals,
|
|
2637
|
+
pendingUserInputs,
|
|
2638
|
+
steeringCount: projection.steeringCount ?? numberOrNull(nestedProjection?.steeringCount) ?? 0,
|
|
2639
|
+
stallCount: projection.stallCount ?? numberOrNull(nestedProjection?.stallCount) ?? 0,
|
|
2640
|
+
lastEventAt: projection.updatedAt ?? stringOrNull(nestedProjection?.lastEventAt)
|
|
2641
|
+
};
|
|
2642
|
+
}
|
|
2643
|
+
function foldedProjectionForRun(runId, projection, sessionPath, toRunStatus) {
|
|
2644
|
+
const registryFolded = emptyFoldedProjection(runId, projection, toRunStatus);
|
|
2645
|
+
const sessionEntries = readSessionRunEntries(sessionPath);
|
|
2646
|
+
if (sessionEntries.length === 0)
|
|
2647
|
+
return { projection: registryFolded, hasSessionEntries: false };
|
|
2648
|
+
const sessionFolded = foldRunSessionEntries(sessionEntries, runId);
|
|
2649
|
+
if (sessionFolded.lastSeq === 0)
|
|
2650
|
+
return { projection: registryFolded, hasSessionEntries: false };
|
|
2651
|
+
return {
|
|
2652
|
+
projection: {
|
|
2653
|
+
...registryFolded,
|
|
2654
|
+
...sessionFolded,
|
|
2655
|
+
record: {
|
|
2656
|
+
...registryFolded.record,
|
|
2657
|
+
...sessionFolded.record,
|
|
2658
|
+
runId
|
|
2659
|
+
},
|
|
2660
|
+
status: sessionFolded.status ?? registryFolded.status,
|
|
2661
|
+
lastEventAt: sessionFolded.lastEventAt ?? registryFolded.lastEventAt
|
|
2662
|
+
},
|
|
2663
|
+
hasSessionEntries: true
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
function runRecordFromRegistryEntry(projectRoot, entry, toRunStatus) {
|
|
2667
|
+
const projection = entry.projection && typeof entry.projection === "object" ? entry.projection : {};
|
|
2668
|
+
const projectionRecord = projection;
|
|
2669
|
+
const runId = stringOrNull(projection.runId) ?? entry.roomId;
|
|
2670
|
+
const sessionPath = stringOrNull(projection.sessionPath) ?? stringOrNull(entry.sessionPath) ?? null;
|
|
2671
|
+
const foldedResult = foldedProjectionForRun(runId, projection, sessionPath, toRunStatus);
|
|
2672
|
+
const folded = foldedResult.projection;
|
|
2673
|
+
const pushedStatus = folded.status ?? toRunStatus(projection.status) ?? toRunStatus(entry.status);
|
|
2674
|
+
const status = entry.stale ? pushedStatus && isTerminalRunStatus(pushedStatus) ? pushedStatus : "stale" : pushedStatus ?? "running";
|
|
2675
|
+
const collabCwd = stringOrNull(projection.collabCwd) ?? stringOrNull(projection.cwd) ?? stringOrNull(entry.cwd) ?? stringOrNull(projection.worktreePath);
|
|
2676
|
+
const pendingApprovals = foldedResult.hasSessionEntries ? folded.pendingApprovals.length : numberOrNull(projectionRecord.pendingApprovals) ?? folded.pendingApprovals.length;
|
|
2677
|
+
const pendingInputs = foldedResult.hasSessionEntries ? folded.pendingUserInputs.length : numberOrNull(projectionRecord.pendingInputs) ?? folded.pendingUserInputs.length;
|
|
2678
|
+
return {
|
|
2679
|
+
runId,
|
|
2680
|
+
taskId: stringOrNull(projection.taskId),
|
|
2681
|
+
title: stringOrNull(projection.title) ?? entry.title,
|
|
2682
|
+
status,
|
|
2683
|
+
source: sourceFromEntry(projectRoot, projection, entry),
|
|
2684
|
+
live: !entry.stale,
|
|
2685
|
+
stale: entry.stale,
|
|
2686
|
+
startedAt: stringOrNull(projection.startedAt) ?? entry.startedAt ?? null,
|
|
2687
|
+
updatedAt: stringOrNull(projection.updatedAt) ?? entry.heartbeatAt ?? null,
|
|
2688
|
+
completedAt: stringOrNull(projection.completedAt),
|
|
2689
|
+
joinLink: stringOrNull(projection.joinLink) ?? entry.joinLink ?? null,
|
|
2690
|
+
webLink: stringOrNull(projection.webLink) ?? entry.webLink ?? null,
|
|
2691
|
+
relayUrl: stringOrNull(projection.relayUrl) ?? entry.relayUrl ?? null,
|
|
2692
|
+
sessionPath,
|
|
2693
|
+
prUrl: stringOrNull(projection.prUrl),
|
|
2694
|
+
worktreePath: stringOrNull(projection.worktreePath) ?? collabCwd,
|
|
2695
|
+
pendingApprovals,
|
|
2696
|
+
pendingInputs,
|
|
2697
|
+
steeringCount: projection.steeringCount ?? folded.steeringCount ?? 0,
|
|
2698
|
+
stallCount: projection.stallCount ?? folded.stallCount ?? 0,
|
|
2699
|
+
errorSummary: stringOrNull(projection.errorSummary) ?? summarizeRunError(folded),
|
|
2700
|
+
timeline: Array.isArray(projection.timeline) ? [...projection.timeline] : [...timelineEntriesFromCustomEntries([])],
|
|
2701
|
+
inbox: inboxFromProjection(folded),
|
|
2702
|
+
collabCwd: collabCwd ?? null,
|
|
2703
|
+
projection: folded
|
|
2704
|
+
};
|
|
2705
|
+
}
|
|
2706
|
+
function runRecordsFromCollab(projectRoot, collabs, toRunStatus) {
|
|
2707
|
+
return sortByRecency(collabs.map((collab) => runRecordFromRegistryEntry(projectRoot, registryEntryFromCollab(collab), toRunStatus)).filter((record) => record !== null));
|
|
2708
|
+
}
|
|
2709
|
+
function sortByRecency(records) {
|
|
2710
|
+
return [...records].sort((a, b) => {
|
|
2711
|
+
const at = Date.parse(b.updatedAt ?? b.startedAt ?? "") || 0;
|
|
2712
|
+
const bt = Date.parse(a.updatedAt ?? a.startedAt ?? "") || 0;
|
|
2713
|
+
return at - bt;
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
function discoveryDiagnosticRunRecord(projectRoot, error) {
|
|
2717
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2718
|
+
const runId = DISCOVERY_DIAGNOSTIC_RUN_ID;
|
|
2719
|
+
const projection = {
|
|
2720
|
+
...EMPTY_PROJECTION,
|
|
2721
|
+
record: { runId, title: "Registry discovery unavailable" },
|
|
2722
|
+
status: "needs-attention",
|
|
2723
|
+
stallCount: 1
|
|
2724
|
+
};
|
|
2725
|
+
return {
|
|
2726
|
+
runId,
|
|
2727
|
+
taskId: null,
|
|
2728
|
+
title: "Registry discovery unavailable",
|
|
2729
|
+
status: "needs-attention",
|
|
2730
|
+
source: "remote",
|
|
2731
|
+
live: false,
|
|
2732
|
+
stale: true,
|
|
2733
|
+
startedAt: null,
|
|
2734
|
+
updatedAt: null,
|
|
2735
|
+
completedAt: null,
|
|
2736
|
+
joinLink: null,
|
|
2737
|
+
webLink: null,
|
|
2738
|
+
relayUrl: null,
|
|
2739
|
+
sessionPath: null,
|
|
2740
|
+
prUrl: null,
|
|
2741
|
+
worktreePath: null,
|
|
2742
|
+
pendingApprovals: 0,
|
|
2743
|
+
pendingInputs: 0,
|
|
2744
|
+
steeringCount: 0,
|
|
2745
|
+
stallCount: 1,
|
|
2746
|
+
errorSummary: `registry discovery unavailable: ${detail}`,
|
|
2747
|
+
timeline: [],
|
|
2748
|
+
inbox: [],
|
|
2749
|
+
collabCwd: null,
|
|
2750
|
+
projection
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
async function listRunProjections(projectRoot, filter = {}) {
|
|
2754
|
+
try {
|
|
2755
|
+
const discovery = await loadCapabilityForRoot5(projectRoot, RunDiscoveryCap);
|
|
2756
|
+
if (!discovery)
|
|
2757
|
+
return [discoveryDiagnosticRunRecord(projectRoot, "run discovery capability unavailable")];
|
|
2758
|
+
const backbone = await loadCapabilityForRoot5(projectRoot, RunRegistryBackboneCap2);
|
|
2759
|
+
if (!backbone)
|
|
2760
|
+
return [discoveryDiagnosticRunRecord(projectRoot, "run registry backbone capability unavailable")];
|
|
2761
|
+
const collabs = await discovery.listActiveRunCollab(projectRoot, filter, { isRuntimeActiveStatus });
|
|
2762
|
+
return runRecordsFromCollab(projectRoot, collabs, backbone.registryStatusToRunStatus);
|
|
2763
|
+
} catch (error) {
|
|
2764
|
+
return [discoveryDiagnosticRunRecord(projectRoot, error)];
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
function selectRunProjection(runs, runId) {
|
|
2768
|
+
const exactRun = runs.find((run) => run.runId === runId);
|
|
2769
|
+
if (exactRun)
|
|
2770
|
+
return exactRun;
|
|
2771
|
+
const exactTask = runs.find((run) => run.taskId === runId);
|
|
2772
|
+
if (exactTask)
|
|
2773
|
+
return exactTask;
|
|
2774
|
+
const prefixMatches = runs.filter((run) => run.runId.startsWith(runId));
|
|
2775
|
+
if (prefixMatches.length === 1)
|
|
2776
|
+
return prefixMatches[0] ?? null;
|
|
2777
|
+
if (prefixMatches.length > 1) {
|
|
2778
|
+
const matches = prefixMatches.map((run) => run.runId).join(", ");
|
|
2779
|
+
throw new Error(`Ambiguous run id prefix "${runId}" matched ${prefixMatches.length} runs: ${matches}`);
|
|
2780
|
+
}
|
|
2781
|
+
return runs.find((run) => run.runId === DISCOVERY_DIAGNOSTIC_RUN_ID) ?? null;
|
|
2782
|
+
}
|
|
2783
|
+
async function getRunProjection(projectRoot, runId, filter = {}) {
|
|
2784
|
+
const runs = await listRunProjections(projectRoot, filter);
|
|
2785
|
+
return selectRunProjection(runs, runId);
|
|
2786
|
+
}
|
|
2787
|
+
async function resolveRunJoinTarget(projectRoot, runId, filter = {}) {
|
|
2788
|
+
const run = await getRunProjection(projectRoot, runId, filter);
|
|
2789
|
+
if (!run || !run.joinLink)
|
|
2790
|
+
return null;
|
|
2791
|
+
return { runId: run.runId, taskId: run.taskId, joinLink: run.joinLink, cwd: run.collabCwd ?? run.sessionPath, stale: run.stale };
|
|
2792
|
+
}
|
|
2793
|
+
var RunDiscoveryCap, RunRegistryBackboneCap2, EMPTY_PROJECTION, DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__", listRuns, getRun, resolveJoinTarget;
|
|
2794
|
+
var init_projection = __esm(() => {
|
|
2795
|
+
init_session_journal();
|
|
2796
|
+
init_run_status();
|
|
2797
|
+
RunDiscoveryCap = defineCapability8(RUN_DISCOVERY);
|
|
2798
|
+
RunRegistryBackboneCap2 = defineCapability8(RUN_REGISTRY_BACKBONE2);
|
|
2799
|
+
EMPTY_PROJECTION = foldRunSessionEntries([], "");
|
|
2800
|
+
listRuns = listRunProjections;
|
|
2801
|
+
getRun = getRunProjection;
|
|
2802
|
+
resolveJoinTarget = resolveRunJoinTarget;
|
|
2803
|
+
});
|
|
2804
|
+
|
|
2805
|
+
// packages/run-plugin/src/read-model/read-model-backend/inbox.ts
|
|
2806
|
+
function promptFromPayload(payload, fallback) {
|
|
2807
|
+
if (payload && typeof payload === "object") {
|
|
2808
|
+
const record = payload;
|
|
2809
|
+
for (const key of ["title", "message", "reason", "prompt", "summary"]) {
|
|
2810
|
+
const value = record[key];
|
|
2811
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
2812
|
+
return value.trim();
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
return fallback;
|
|
2816
|
+
}
|
|
2817
|
+
function recordsFromRun(run, kind) {
|
|
2818
|
+
const pending = kind === "approvals" ? run.projection.pendingApprovals : run.projection.pendingUserInputs;
|
|
2819
|
+
return pending.map((request) => ({
|
|
2820
|
+
runId: run.runId,
|
|
2821
|
+
taskId: run.taskId,
|
|
2822
|
+
requestId: request.requestId,
|
|
2823
|
+
status: "pending",
|
|
2824
|
+
prompt: promptFromPayload(request.payload, kind === "approvals" ? "Approval requested" : "Input requested"),
|
|
2825
|
+
requestedAt: request.requestedAt,
|
|
2826
|
+
payload: request.payload,
|
|
2827
|
+
kind: kind === "approvals" ? "approval" : "input"
|
|
2828
|
+
}));
|
|
2829
|
+
}
|
|
2830
|
+
function matchesFilters(run, filters) {
|
|
2831
|
+
if (filters.run && run.runId !== filters.run)
|
|
2832
|
+
return false;
|
|
2833
|
+
if (filters.task && run.taskId !== filters.task && run.runId !== filters.task)
|
|
2834
|
+
return false;
|
|
2835
|
+
return true;
|
|
2836
|
+
}
|
|
2837
|
+
async function listInboxRecords2(input, kind, filters = {}, deps = {}) {
|
|
2838
|
+
const runs = await (deps.listRuns ?? listRuns)(input.projectRoot);
|
|
2839
|
+
return runs.filter((run) => matchesFilters(run, filters)).flatMap((run) => recordsFromRun(run, kind));
|
|
2840
|
+
}
|
|
2841
|
+
var init_inbox = __esm(() => {
|
|
2842
|
+
init_projection();
|
|
2843
|
+
});
|
|
2844
|
+
|
|
2845
|
+
// packages/run-plugin/src/read-model/read-model-backend/inspect.ts
|
|
2846
|
+
import { RIG_RUN_LOG_ENTRY } from "@rig/contracts";
|
|
2847
|
+
function asRecord(value) {
|
|
2848
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2849
|
+
}
|
|
2850
|
+
function firstString2(record, keys) {
|
|
2851
|
+
for (const key of keys) {
|
|
2852
|
+
const value = record[key];
|
|
2853
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
2854
|
+
return value;
|
|
2855
|
+
}
|
|
2856
|
+
return null;
|
|
2857
|
+
}
|
|
2858
|
+
function stringifyLogPayload(value) {
|
|
2859
|
+
if (typeof value === "string")
|
|
2860
|
+
return value;
|
|
2861
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
2862
|
+
return String(value);
|
|
2863
|
+
const record = asRecord(value);
|
|
2864
|
+
if (!record)
|
|
2865
|
+
return null;
|
|
2866
|
+
const direct = firstString2(record, ["line", "message", "text", "content", "output", "summary", "detail"]);
|
|
2867
|
+
if (direct)
|
|
2868
|
+
return direct;
|
|
2869
|
+
try {
|
|
2870
|
+
return JSON.stringify(record);
|
|
2871
|
+
} catch {
|
|
2872
|
+
return String(record);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
function logLineFromValue(value) {
|
|
2876
|
+
const record = asRecord(value);
|
|
2877
|
+
if (!record)
|
|
2878
|
+
return stringifyLogPayload(value);
|
|
2879
|
+
const direct = firstString2(record, ["line", "message", "text", "content", "output", "summary", "detail"]);
|
|
2880
|
+
if (direct)
|
|
2881
|
+
return direct;
|
|
2882
|
+
if ("payload" in record)
|
|
2883
|
+
return stringifyLogPayload(record.payload);
|
|
2884
|
+
if ("data" in record)
|
|
2885
|
+
return stringifyLogPayload(record.data);
|
|
2886
|
+
return stringifyLogPayload(record);
|
|
2887
|
+
}
|
|
2888
|
+
function logLineFromSessionEntry(entry) {
|
|
2889
|
+
if (entry.type !== "custom" || entry.customType !== RIG_RUN_LOG_ENTRY)
|
|
2890
|
+
return null;
|
|
2891
|
+
return logLineFromValue(entry.data);
|
|
2892
|
+
}
|
|
2893
|
+
function logLinesFromProjection(projection) {
|
|
2894
|
+
const projected = projection;
|
|
2895
|
+
const lines = [];
|
|
2896
|
+
for (const key of ["logs", "logEntries", "journalLogs", "runLogs"]) {
|
|
2897
|
+
const entries = projected[key];
|
|
2898
|
+
if (!Array.isArray(entries))
|
|
2899
|
+
continue;
|
|
2900
|
+
for (const entry of entries) {
|
|
2901
|
+
const line = logLineFromValue(entry);
|
|
2902
|
+
if (line !== null)
|
|
2903
|
+
lines.push(line);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
return lines;
|
|
2907
|
+
}
|
|
2908
|
+
function extractRunLogs(run, deps = {}) {
|
|
2909
|
+
const projectedLines = logLinesFromProjection(run.projection);
|
|
2910
|
+
if (projectedLines.length > 0)
|
|
2911
|
+
return projectedLines;
|
|
2912
|
+
return (deps.readSessionRunEntries ?? readSessionRunEntries)(run.sessionPath).map(logLineFromSessionEntry).filter((line) => line !== null);
|
|
2913
|
+
}
|
|
2914
|
+
function summarizeRunFailures(run) {
|
|
2915
|
+
const failures = [];
|
|
2916
|
+
const useful = summarizeRunError(run.projection);
|
|
2917
|
+
if (useful)
|
|
2918
|
+
failures.push(`${run.runId}: ${useful}`);
|
|
2919
|
+
if (run.status === "failed" || run.projection.status === "failed")
|
|
2920
|
+
failures.push(`${run.runId}: run status failed`);
|
|
2921
|
+
for (const phase of run.projection.closeoutPhases) {
|
|
2922
|
+
if (phase.outcome === "failed")
|
|
2923
|
+
failures.push(`${run.runId}: closeout ${phase.phase} failed${phase.detail ? ` \u2014 ${phase.detail}` : ""}`);
|
|
2924
|
+
}
|
|
2925
|
+
for (const anomaly of run.projection.anomalies) {
|
|
2926
|
+
failures.push(`${run.runId}: anomaly ${anomaly.kind}${anomaly.detail ? ` \u2014 ${anomaly.detail}` : ""}`);
|
|
2927
|
+
}
|
|
2928
|
+
return failures;
|
|
2929
|
+
}
|
|
2930
|
+
function runInspectSummaryRows(runs, options = {}) {
|
|
2931
|
+
const attachable = runs.filter((run) => Boolean(run.joinLink && !run.stale)).length;
|
|
2932
|
+
const pendingInbox = options.pendingInbox ?? runs.reduce((total, run) => total + run.pendingApprovals + run.pendingInputs, 0);
|
|
2933
|
+
return [
|
|
2934
|
+
{ id: "title", label: "Inspect", currentValue: "runtime", heading: true, description: "session/runtime inspection rows from @rig/client" },
|
|
2935
|
+
{ id: "inspect:sessions", label: "RUNS", currentValue: String(runs.length), heading: true, description: `${attachable} attachable OMP collab links` },
|
|
2936
|
+
{ id: "inspect:cwd", label: "PROJECT ROOT", currentValue: options.projectRoot ?? "", heading: true, description: "selected target/project root used by Rig chrome" },
|
|
2937
|
+
{ id: "inspect:inbox", label: "INBOX", currentValue: String(pendingInbox), values: ["open"], description: "pending run gates; open Inbox to resolve" },
|
|
2938
|
+
{ id: "inspect:runs", label: "RUNS", currentValue: String(runs.length), values: ["open"], description: "open Runs to inspect individual run detail, logs, and attach controls" },
|
|
2939
|
+
{ id: "inspect:audit", label: "AUDIT", currentValue: "unavailable", heading: true, description: "CLI inspect audit has no cockpit equivalent; use rig inspect audit" }
|
|
2940
|
+
];
|
|
2941
|
+
}
|
|
2942
|
+
var init_inspect = __esm(() => {
|
|
2943
|
+
init_projection();
|
|
2944
|
+
});
|
|
2945
|
+
|
|
2946
|
+
// packages/run-plugin/src/read-model/read-model-backend/stats.ts
|
|
2947
|
+
function parseTimestamp(value) {
|
|
2948
|
+
if (!value)
|
|
2949
|
+
return null;
|
|
2950
|
+
const parsed = Date.parse(value);
|
|
2951
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
2952
|
+
}
|
|
2953
|
+
function median(values) {
|
|
2954
|
+
if (values.length === 0)
|
|
2955
|
+
return null;
|
|
2956
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
2957
|
+
const middle = Math.floor(sorted.length / 2);
|
|
2958
|
+
return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
2959
|
+
}
|
|
2960
|
+
function rate(part, total) {
|
|
2961
|
+
return total === 0 ? null : part / total;
|
|
2962
|
+
}
|
|
2963
|
+
function completedDuration(run) {
|
|
2964
|
+
if (run.status !== "completed")
|
|
2965
|
+
return null;
|
|
2966
|
+
const startedAt = parseTimestamp(run.startedAt);
|
|
2967
|
+
const completedAt = parseTimestamp(run.completedAt);
|
|
2968
|
+
if (startedAt === null || completedAt === null || completedAt < startedAt)
|
|
2969
|
+
return null;
|
|
2970
|
+
return completedAt - startedAt;
|
|
2971
|
+
}
|
|
2972
|
+
async function computeStats(projectRootOrRuns, options = {}) {
|
|
2973
|
+
const sinceMs = options.since ? options.since.getTime() : null;
|
|
2974
|
+
const allRuns = typeof projectRootOrRuns === "string" ? await (options.listRuns ?? (await Promise.resolve().then(() => (init_projection(), exports_projection))).listRuns)(projectRootOrRuns) : projectRootOrRuns;
|
|
2975
|
+
const runs = allRuns.filter((run) => {
|
|
2976
|
+
if (sinceMs === null)
|
|
2977
|
+
return true;
|
|
2978
|
+
const startedAt = parseTimestamp(run.startedAt);
|
|
2979
|
+
return startedAt !== null && startedAt >= sinceMs;
|
|
2980
|
+
});
|
|
2981
|
+
const statusCounts = {};
|
|
2982
|
+
const completionDurations = [];
|
|
2983
|
+
let completedRuns = 0;
|
|
2984
|
+
let failedRuns = 0;
|
|
2985
|
+
let needsAttentionRuns = 0;
|
|
2986
|
+
let steeringTotal = 0;
|
|
2987
|
+
let stallTotal = 0;
|
|
2988
|
+
let approvalsPending = 0;
|
|
2989
|
+
let approvalsApproved = 0;
|
|
2990
|
+
let approvalsRejected = 0;
|
|
2991
|
+
for (const run of runs) {
|
|
2992
|
+
const status = run.status || "unknown";
|
|
2993
|
+
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
2994
|
+
if (status === "completed")
|
|
2995
|
+
completedRuns += 1;
|
|
2996
|
+
if (status === "failed")
|
|
2997
|
+
failedRuns += 1;
|
|
2998
|
+
if (isNeedsAttention(run))
|
|
2999
|
+
needsAttentionRuns += 1;
|
|
3000
|
+
const duration = completedDuration(run);
|
|
3001
|
+
if (duration !== null)
|
|
3002
|
+
completionDurations.push(duration);
|
|
3003
|
+
steeringTotal += run.steeringCount;
|
|
3004
|
+
stallTotal += run.stallCount;
|
|
3005
|
+
approvalsPending += run.projection.pendingApprovals.length;
|
|
3006
|
+
for (const approval of run.projection.resolvedApprovals) {
|
|
3007
|
+
if (approval.decision === "approve")
|
|
3008
|
+
approvalsApproved += 1;
|
|
3009
|
+
if (approval.decision === "reject")
|
|
3010
|
+
approvalsRejected += 1;
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
const approvalsRequested = approvalsPending + approvalsApproved + approvalsRejected;
|
|
3014
|
+
const totalRuns = runs.length;
|
|
3015
|
+
return {
|
|
3016
|
+
since: options.since ? options.since.toISOString() : null,
|
|
3017
|
+
totalRuns,
|
|
3018
|
+
statusCounts,
|
|
3019
|
+
completedRuns,
|
|
3020
|
+
failedRuns,
|
|
3021
|
+
needsAttentionRuns,
|
|
3022
|
+
completionRate: rate(completedRuns, totalRuns),
|
|
3023
|
+
failureRate: rate(failedRuns, totalRuns),
|
|
3024
|
+
needsAttentionRate: rate(needsAttentionRuns, totalRuns),
|
|
3025
|
+
medianCompletionMs: median(completionDurations),
|
|
3026
|
+
steeringTotal,
|
|
3027
|
+
steeringPerRun: totalRuns === 0 ? null : steeringTotal / totalRuns,
|
|
3028
|
+
stallTotal,
|
|
3029
|
+
approvalsRequested,
|
|
3030
|
+
approvalsApproved,
|
|
3031
|
+
approvalsRejected,
|
|
3032
|
+
approvalsPending
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
var init_stats = __esm(() => {
|
|
3036
|
+
init_run_status();
|
|
3037
|
+
init_run_status();
|
|
3038
|
+
});
|
|
3039
|
+
|
|
3040
|
+
// packages/run-plugin/src/read-model/read-model-backend/index.ts
|
|
3041
|
+
var init_read_model_backend = __esm(() => {
|
|
3042
|
+
init_projection();
|
|
3043
|
+
init_guard();
|
|
3044
|
+
init_inbox();
|
|
3045
|
+
init_inspect();
|
|
3046
|
+
init_run_status();
|
|
3047
|
+
init_stats();
|
|
3048
|
+
});
|
|
3049
|
+
|
|
3050
|
+
// packages/run-plugin/src/read-model/read-model-service.ts
|
|
3051
|
+
var exports_read_model_service = {};
|
|
3052
|
+
__export(exports_read_model_service, {
|
|
3053
|
+
runReadModelService: () => runReadModelService
|
|
3054
|
+
});
|
|
3055
|
+
import {
|
|
3056
|
+
RUN_REGISTRY_BACKBONE as RUN_REGISTRY_BACKBONE3
|
|
3057
|
+
} from "@rig/contracts";
|
|
3058
|
+
import { defineCapability as defineCapability9 } from "@rig/core/capability";
|
|
3059
|
+
import { requireInstalledCapability as requireInstalledCapability2 } from "@rig/core/capability-loaders";
|
|
3060
|
+
function registryBackbone() {
|
|
3061
|
+
return requireInstalledCapability2(RunRegistryBackboneCap3, "RUN_REGISTRY_BACKBONE capability unavailable: the run-worker autohost installs it before building registry projections; ensure @rig/transport-plugin is installed.");
|
|
3062
|
+
}
|
|
3063
|
+
function discoveryFilter(filter) {
|
|
3064
|
+
return filter ?? {};
|
|
3065
|
+
}
|
|
3066
|
+
function requestedKinds(kind) {
|
|
3067
|
+
if (kind === "approval")
|
|
3068
|
+
return ["approvals"];
|
|
3069
|
+
if (kind === "input")
|
|
3070
|
+
return ["inputs"];
|
|
3071
|
+
return ["approvals", "inputs"];
|
|
3072
|
+
}
|
|
3073
|
+
function sourceMatches(run, source) {
|
|
3074
|
+
return !source || source === "all" || run.source === source;
|
|
3075
|
+
}
|
|
3076
|
+
function failureSummaries(run) {
|
|
3077
|
+
return summarizeRunFailures(run).map((summary) => ({ kind: "run", summary }));
|
|
3078
|
+
}
|
|
3079
|
+
function inboxRecord(record) {
|
|
3080
|
+
return {
|
|
3081
|
+
runId: record.runId,
|
|
3082
|
+
taskId: record.taskId,
|
|
3083
|
+
requestId: record.requestId,
|
|
3084
|
+
kind: record.kind,
|
|
3085
|
+
status: record.status,
|
|
3086
|
+
title: record.prompt,
|
|
3087
|
+
prompt: record.prompt,
|
|
3088
|
+
requestedAt: record.requestedAt,
|
|
3089
|
+
payload: record.payload,
|
|
3090
|
+
source: "run"
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
async function listedRuns(input) {
|
|
3094
|
+
const runs = await listRuns(input.projectRoot, discoveryFilter(input.discoveryFilter));
|
|
3095
|
+
return runs.filter((run) => {
|
|
3096
|
+
if (input.taskId && run.taskId !== input.taskId && run.runId !== input.taskId)
|
|
3097
|
+
return false;
|
|
3098
|
+
return sourceMatches(run, input.source);
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
async function selectedRun2(input) {
|
|
3102
|
+
return getRun(input.projectRoot, input.id, discoveryFilter(input.discoveryFilter));
|
|
3103
|
+
}
|
|
3104
|
+
async function representativeRun(input) {
|
|
3105
|
+
const runs = await listedRuns({
|
|
3106
|
+
projectRoot: input.projectRoot,
|
|
3107
|
+
taskId: input.taskId,
|
|
3108
|
+
discoveryFilter: input.discoveryFilter
|
|
3109
|
+
});
|
|
3110
|
+
for (const run of runs)
|
|
3111
|
+
return run;
|
|
3112
|
+
return selectedRun2({ projectRoot: input.projectRoot, id: input.taskId, discoveryFilter: input.discoveryFilter });
|
|
3113
|
+
}
|
|
3114
|
+
async function readDetails(input) {
|
|
3115
|
+
const run = await selectedRun2(input);
|
|
3116
|
+
if (!run)
|
|
3117
|
+
return null;
|
|
3118
|
+
const pendingInbox = run.inbox;
|
|
3119
|
+
const inboxCounts = {
|
|
3120
|
+
approvals: run.pendingApprovals,
|
|
3121
|
+
inputs: run.pendingInputs,
|
|
3122
|
+
total: run.pendingApprovals + run.pendingInputs
|
|
3123
|
+
};
|
|
3124
|
+
const sessionEntries = run.sessionPath ? readSessionRunEntries(run.sessionPath) : undefined;
|
|
3125
|
+
return {
|
|
3126
|
+
run,
|
|
3127
|
+
projection: run.projection,
|
|
3128
|
+
pendingInbox,
|
|
3129
|
+
inboxCounts,
|
|
3130
|
+
sourceTask: null,
|
|
3131
|
+
...sessionEntries ? { sessionEntries } : {},
|
|
3132
|
+
failures: failureSummaries(run),
|
|
3133
|
+
logs: input.includeLogs ? extractRunLogs(run) : []
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
async function inboxRecords(input) {
|
|
3137
|
+
const out = [];
|
|
3138
|
+
for (const kind of requestedKinds(input.kind)) {
|
|
3139
|
+
const records = await listInboxRecords2({ projectRoot: input.projectRoot }, kind, { ...input.runId ? { run: input.runId } : {}, ...input.taskId ? { task: input.taskId } : {} }, { listRuns: (projectRoot) => listedRuns({ projectRoot, discoveryFilter: input.discoveryFilter }) });
|
|
3140
|
+
out.push(...records.map(inboxRecord));
|
|
3141
|
+
}
|
|
3142
|
+
return out;
|
|
3143
|
+
}
|
|
3144
|
+
function classify(run) {
|
|
3145
|
+
const classification = classifyRun(run);
|
|
3146
|
+
return {
|
|
3147
|
+
...classification,
|
|
3148
|
+
role: runStatusColorRole(run),
|
|
3149
|
+
rank: statusRank(run)
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
function normalizeStageName(value) {
|
|
3153
|
+
const key = value?.toLowerCase().replace(/[_\s]+/g, "-");
|
|
3154
|
+
switch (key) {
|
|
3155
|
+
case "connect":
|
|
3156
|
+
case "collab-host-started":
|
|
3157
|
+
return "Connect";
|
|
3158
|
+
case "github-sync":
|
|
3159
|
+
case "github":
|
|
3160
|
+
case "running-reflection":
|
|
3161
|
+
return "GitHub-sync";
|
|
3162
|
+
case "created":
|
|
3163
|
+
case "queued":
|
|
3164
|
+
case "prepare":
|
|
3165
|
+
case "preparing":
|
|
3166
|
+
return "Prepare";
|
|
3167
|
+
case "launch":
|
|
3168
|
+
case "starting":
|
|
3169
|
+
case "booting":
|
|
3170
|
+
case "running":
|
|
3171
|
+
case "paused":
|
|
3172
|
+
return "Launch";
|
|
3173
|
+
case "plan":
|
|
3174
|
+
case "planning":
|
|
3175
|
+
return "Plan";
|
|
3176
|
+
case "implement":
|
|
3177
|
+
case "implementation":
|
|
3178
|
+
return "Implement";
|
|
3179
|
+
case "validate":
|
|
3180
|
+
case "validating":
|
|
3181
|
+
return "Validate";
|
|
3182
|
+
case "commit":
|
|
3183
|
+
return "Commit";
|
|
3184
|
+
case "push":
|
|
3185
|
+
case "pr-opened":
|
|
3186
|
+
case "open-pr":
|
|
3187
|
+
return "Open-PR";
|
|
3188
|
+
case "pr-review-merge":
|
|
3189
|
+
case "review":
|
|
3190
|
+
case "reviewing":
|
|
3191
|
+
case "review-ci":
|
|
3192
|
+
return "Review/CI";
|
|
3193
|
+
case "merge":
|
|
3194
|
+
return "Merge";
|
|
3195
|
+
case "closing-out":
|
|
3196
|
+
case "completed":
|
|
3197
|
+
case "complete":
|
|
3198
|
+
case "waiting-approval":
|
|
3199
|
+
case "waiting-input":
|
|
3200
|
+
case "waiting-user-input":
|
|
3201
|
+
case "needs-attention":
|
|
3202
|
+
case "failed":
|
|
3203
|
+
case "stopped":
|
|
3204
|
+
return "Complete/Needs-attention/Failed";
|
|
3205
|
+
default:
|
|
3206
|
+
return null;
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
function stageStatusFromRunStatus(value) {
|
|
3210
|
+
const key = value?.toLowerCase().replace(/[_\s]+/g, "-");
|
|
3211
|
+
switch (key) {
|
|
3212
|
+
case "failed":
|
|
3213
|
+
return "failed";
|
|
3214
|
+
case "needs-attention":
|
|
3215
|
+
return "needs-attention";
|
|
3216
|
+
case "stopped":
|
|
3217
|
+
return "stopped";
|
|
3218
|
+
case "paused":
|
|
3219
|
+
return "paused";
|
|
3220
|
+
case "waiting-approval":
|
|
3221
|
+
return "waiting-approval";
|
|
3222
|
+
case "waiting-input":
|
|
3223
|
+
return "waiting-input";
|
|
3224
|
+
case "waiting-user-input":
|
|
3225
|
+
return "waiting-user-input";
|
|
3226
|
+
case "running":
|
|
3227
|
+
case "starting":
|
|
3228
|
+
case "booting":
|
|
3229
|
+
case "started":
|
|
3230
|
+
case "validating":
|
|
3231
|
+
case "reviewing":
|
|
3232
|
+
case "closing-out":
|
|
3233
|
+
return "running";
|
|
3234
|
+
default:
|
|
3235
|
+
return "completed";
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
function terminalStageStatus(classification) {
|
|
3239
|
+
if (classification.isNeedsAttention)
|
|
3240
|
+
return "needs-attention";
|
|
3241
|
+
switch (classification.phase) {
|
|
3242
|
+
case "completed":
|
|
3243
|
+
return "completed";
|
|
3244
|
+
case "failed":
|
|
3245
|
+
return "failed";
|
|
3246
|
+
case "stopped":
|
|
3247
|
+
return "stopped";
|
|
3248
|
+
case "paused":
|
|
3249
|
+
return "paused";
|
|
3250
|
+
default:
|
|
3251
|
+
return classification.isTerminal ? "completed" : null;
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
function projectRunStages(run) {
|
|
3255
|
+
const classification = classify(run);
|
|
3256
|
+
const statuses = new Map;
|
|
3257
|
+
const details = new Map;
|
|
3258
|
+
for (const stage of RUN_DETAIL_STAGES)
|
|
3259
|
+
statuses.set(stage, "pending");
|
|
3260
|
+
for (const history of run.projection.statusHistory ?? []) {
|
|
3261
|
+
const stage = normalizeStageName(history.to);
|
|
3262
|
+
if (stage) {
|
|
3263
|
+
statuses.set(stage, stageStatusFromRunStatus(history.to));
|
|
3264
|
+
if (history.reason)
|
|
3265
|
+
details.set(stage, history.reason);
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
for (const entry of run.timeline ?? []) {
|
|
3269
|
+
const stage = normalizeStageName(entry.stage ?? entry.type);
|
|
3270
|
+
if (!stage)
|
|
3271
|
+
continue;
|
|
3272
|
+
statuses.set(stage, stageStatusFromRunStatus(entry.status ?? entry.type));
|
|
3273
|
+
if (entry.detail)
|
|
3274
|
+
details.set(stage, entry.detail);
|
|
3275
|
+
}
|
|
3276
|
+
for (const phase of run.projection.closeoutPhases ?? []) {
|
|
3277
|
+
const stage = normalizeStageName(phase.phase);
|
|
3278
|
+
if (!stage)
|
|
3279
|
+
continue;
|
|
3280
|
+
statuses.set(stage, phase.outcome === "failed" ? "failed" : phase.outcome === "completed" ? "completed" : "running");
|
|
3281
|
+
if (phase.detail)
|
|
3282
|
+
details.set(stage, phase.detail);
|
|
3283
|
+
}
|
|
3284
|
+
const terminalStatus = terminalStageStatus(classification);
|
|
3285
|
+
if (terminalStatus) {
|
|
3286
|
+
statuses.set("Complete/Needs-attention/Failed", terminalStatus);
|
|
3287
|
+
}
|
|
3288
|
+
return RUN_DETAIL_STAGES.map((stage) => ({
|
|
3289
|
+
id: `stage:${stage}`,
|
|
3290
|
+
label: stage,
|
|
3291
|
+
currentValue: statuses.get(stage) ?? "pending",
|
|
3292
|
+
description: details.get(stage) ?? "rig.run timeline / closeout projection"
|
|
3293
|
+
}));
|
|
3294
|
+
}
|
|
3295
|
+
function buildRegistryProjection(input) {
|
|
3296
|
+
const folded = projectRunFromSession(input.entries, input.runId);
|
|
3297
|
+
const timeline = latestTimelineEntriesFromCustomEntries(input.entries, input.timelineLimit ?? REGISTRY_PROJECTION_TIMELINE_LIMIT2);
|
|
3298
|
+
const cwd = input.cwd ?? process.cwd();
|
|
3299
|
+
const collabCwd = input.collabCwd ?? cwd;
|
|
3300
|
+
const dispatchHandle = input.dispatchHandle?.trim() || null;
|
|
3301
|
+
return {
|
|
3302
|
+
runId: input.runId,
|
|
3303
|
+
taskId: typeof folded.record.taskId === "string" ? folded.record.taskId : input.taskId ?? null,
|
|
3304
|
+
title: typeof folded.record.title === "string" ? folded.record.title : input.title,
|
|
3305
|
+
status: registryBackbone().coerceRegistryStatus(folded.status, "running"),
|
|
3306
|
+
source: "local",
|
|
3307
|
+
startedAt: typeof folded.record.startedAt === "string" ? folded.record.startedAt : null,
|
|
3308
|
+
updatedAt: folded.lastEventAt ?? (typeof folded.record.updatedAt === "string" ? folded.record.updatedAt : new Date().toISOString()),
|
|
3309
|
+
completedAt: typeof folded.record.completedAt === "string" ? folded.record.completedAt : null,
|
|
3310
|
+
joinLink: input.joinLink ?? null,
|
|
3311
|
+
webLink: input.webLink ?? null,
|
|
3312
|
+
relayUrl: input.relayUrl ?? null,
|
|
3313
|
+
sessionPath: typeof folded.record.sessionPath === "string" ? folded.record.sessionPath : input.sessionPath ?? null,
|
|
3314
|
+
prUrl: typeof folded.record.prUrl === "string" ? folded.record.prUrl : null,
|
|
3315
|
+
worktreePath: typeof folded.record.worktreePath === "string" ? folded.record.worktreePath : cwd,
|
|
3316
|
+
collabCwd,
|
|
3317
|
+
cwd,
|
|
3318
|
+
dispatchHandle,
|
|
3319
|
+
pendingApprovals: folded.pendingApprovals.length,
|
|
3320
|
+
pendingInputs: folded.pendingUserInputs.length,
|
|
3321
|
+
steeringCount: folded.steeringCount,
|
|
3322
|
+
stallCount: folded.stallCount,
|
|
3323
|
+
...typeof folded.record.errorText === "string" ? { errorSummary: folded.record.errorText } : { errorSummary: null },
|
|
3324
|
+
timeline: [...timeline]
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
var REGISTRY_PROJECTION_TIMELINE_LIMIT2 = 100, RunRegistryBackboneCap3, RUN_DETAIL_STAGES, runReadModelService;
|
|
3328
|
+
var init_read_model_service = __esm(() => {
|
|
3329
|
+
init_session_journal();
|
|
3330
|
+
init_reconcile();
|
|
3331
|
+
init_run_format();
|
|
3332
|
+
init_read_model_backend();
|
|
3333
|
+
RunRegistryBackboneCap3 = defineCapability9(RUN_REGISTRY_BACKBONE3);
|
|
3334
|
+
RUN_DETAIL_STAGES = [
|
|
3335
|
+
"Connect",
|
|
3336
|
+
"GitHub-sync",
|
|
3337
|
+
"Prepare",
|
|
3338
|
+
"Launch",
|
|
3339
|
+
"Plan",
|
|
3340
|
+
"Implement",
|
|
3341
|
+
"Validate",
|
|
3342
|
+
"Commit",
|
|
3343
|
+
"Open-PR",
|
|
3344
|
+
"Review/CI",
|
|
3345
|
+
"Merge",
|
|
3346
|
+
"Complete/Needs-attention/Failed"
|
|
3347
|
+
];
|
|
3348
|
+
runReadModelService = {
|
|
3349
|
+
async listRuns(input) {
|
|
3350
|
+
return listedRuns(input);
|
|
3351
|
+
},
|
|
3352
|
+
async getRepresentativeRun(input) {
|
|
3353
|
+
return representativeRun(input);
|
|
3354
|
+
},
|
|
3355
|
+
async getRun(input) {
|
|
3356
|
+
return selectedRun2({ projectRoot: input.projectRoot, id: input.selector.id, discoveryFilter: input.discoveryFilter });
|
|
3357
|
+
},
|
|
3358
|
+
async getRunProjection(input) {
|
|
3359
|
+
const run = await selectedRun2({ projectRoot: input.projectRoot, id: input.runId, discoveryFilter: input.discoveryFilter });
|
|
3360
|
+
return run?.projection ?? null;
|
|
3361
|
+
},
|
|
3362
|
+
buildSessionProjection(input) {
|
|
3363
|
+
return projectRunFromSession(input.entries, input.runId);
|
|
3364
|
+
},
|
|
3365
|
+
buildRegistryProjection,
|
|
3366
|
+
async getRunDetails(input) {
|
|
3367
|
+
return readDetails({
|
|
3368
|
+
projectRoot: input.projectRoot,
|
|
3369
|
+
id: input.selector.id,
|
|
3370
|
+
includeLogs: input.includeLogs,
|
|
3371
|
+
discoveryFilter: input.discoveryFilter
|
|
3372
|
+
});
|
|
3373
|
+
},
|
|
3374
|
+
async inspectRun(input) {
|
|
3375
|
+
const details = await readDetails({
|
|
3376
|
+
projectRoot: input.projectRoot,
|
|
3377
|
+
id: input.selector.id,
|
|
3378
|
+
includeLogs: input.includeLogs,
|
|
3379
|
+
discoveryFilter: input.discoveryFilter
|
|
3380
|
+
});
|
|
3381
|
+
if (!details)
|
|
3382
|
+
return null;
|
|
3383
|
+
return {
|
|
3384
|
+
details,
|
|
3385
|
+
classification: classify(details.run),
|
|
3386
|
+
rows: runInspectSummaryRows([details.run], { projectRoot: input.projectRoot, pendingInbox: details.inboxCounts.total })
|
|
3387
|
+
};
|
|
3388
|
+
},
|
|
3389
|
+
listInboxRecords: inboxRecords,
|
|
3390
|
+
async getInboxCounts(input) {
|
|
3391
|
+
const records = await inboxRecords(input);
|
|
3392
|
+
const approvals = records.filter((record) => record.kind === "approval").length;
|
|
3393
|
+
const inputs = records.filter((record) => record.kind === "input").length;
|
|
3394
|
+
return { approvals, inputs, total: approvals + inputs };
|
|
3395
|
+
},
|
|
3396
|
+
async getStats(input) {
|
|
3397
|
+
const since = input.since ? new Date(input.since) : null;
|
|
3398
|
+
const runs = input.runs ?? await listedRuns({ projectRoot: input.projectRoot, discoveryFilter: input.discoveryFilter });
|
|
3399
|
+
return computeStats(runs, { since });
|
|
3400
|
+
},
|
|
3401
|
+
async getInspectRows(input) {
|
|
3402
|
+
const runs = input.runs ?? await listedRuns({ projectRoot: input.projectRoot, discoveryFilter: input.discoveryFilter });
|
|
3403
|
+
return runInspectSummaryRows(runs, { projectRoot: input.projectRoot, pendingInbox: input.pendingInbox ?? null });
|
|
3404
|
+
},
|
|
3405
|
+
isRuntimeActiveStatus,
|
|
3406
|
+
isOperatorActiveRunStatus,
|
|
3407
|
+
classifyRun: classify,
|
|
3408
|
+
projectRunStages,
|
|
3409
|
+
formatRunList,
|
|
3410
|
+
formatRunCard,
|
|
3411
|
+
formatRunStatus,
|
|
3412
|
+
formatSubmittedRun,
|
|
3413
|
+
formatInboxList,
|
|
3414
|
+
reconcileActiveRuns: (input) => reconcileActiveRuns(input)
|
|
3415
|
+
};
|
|
3416
|
+
});
|
|
3417
|
+
|
|
3418
|
+
// packages/run-plugin/src/read-model/stats-command.ts
|
|
3419
|
+
var exports_stats_command = {};
|
|
3420
|
+
__export(exports_stats_command, {
|
|
3421
|
+
parseSinceOption: () => parseSinceOption,
|
|
3422
|
+
formatStatsRows: () => formatStatsRows,
|
|
3423
|
+
executeStats: () => executeStats
|
|
3424
|
+
});
|
|
3425
|
+
import { RUN_READ_MODEL as RUN_READ_MODEL4 } from "@rig/contracts";
|
|
3426
|
+
import { defineCapability as defineCapability10 } from "@rig/core/capability";
|
|
3427
|
+
import { requireCapabilityForRoot as requireCapabilityForRoot2 } from "@rig/core/capability-loaders";
|
|
3428
|
+
import { CliError as CliError2, requireNoExtraArgs as requireNoExtraArgs2, takeOption as takeOption2 } from "@rig/std-shared/cli-args";
|
|
3429
|
+
import { formatStatsTable, printFormattedOutput as printFormattedOutput2 } from "@rig/std-shared/cli-format";
|
|
3430
|
+
function parseSinceOption(value, now = new Date) {
|
|
3431
|
+
if (!value?.trim())
|
|
3432
|
+
return null;
|
|
3433
|
+
const trimmed = value.trim();
|
|
3434
|
+
const relative2 = trimmed.match(/^(\d+)d$/i);
|
|
3435
|
+
if (relative2?.[1]) {
|
|
3436
|
+
const days = Number.parseInt(relative2[1], 10);
|
|
3437
|
+
if (!Number.isFinite(days) || days <= 0)
|
|
3438
|
+
throw new CliError2(`Invalid --since value: ${value}`, 2, { hint: "Use an ISO date or a relative window like `7d`." });
|
|
3439
|
+
return new Date(now.getTime() - days * DAY_MS);
|
|
3440
|
+
}
|
|
3441
|
+
const parsed = Date.parse(trimmed);
|
|
3442
|
+
if (!Number.isFinite(parsed))
|
|
3443
|
+
throw new CliError2(`Invalid --since value: ${value}`, 2, { hint: "Use an ISO date or a relative window like `7d`." });
|
|
3444
|
+
return new Date(parsed);
|
|
3445
|
+
}
|
|
3446
|
+
function formatDuration(ms) {
|
|
3447
|
+
if (ms === null)
|
|
3448
|
+
return "\u2014";
|
|
3449
|
+
const seconds = Math.round(ms / 1000);
|
|
3450
|
+
if (seconds < 60)
|
|
3451
|
+
return `${seconds}s`;
|
|
3452
|
+
const minutes = Math.round(ms / 60000);
|
|
3453
|
+
if (minutes < 60)
|
|
3454
|
+
return `${minutes}m`;
|
|
3455
|
+
const hours = Math.floor(minutes / 60);
|
|
3456
|
+
const remainder = minutes % 60;
|
|
3457
|
+
return remainder === 0 ? `${hours}h` : `${hours}h ${remainder}m`;
|
|
3458
|
+
}
|
|
3459
|
+
function formatStatsRows(stats2) {
|
|
3460
|
+
return [
|
|
3461
|
+
["window", stats2.since ? `since ${stats2.since}` : "all runs"],
|
|
3462
|
+
["total runs", stats2.totalRuns],
|
|
3463
|
+
["completed", `${stats2.completedRuns} (${formatRatePercent(stats2.completionRate)})`],
|
|
3464
|
+
["failed", `${stats2.failedRuns} (${formatRatePercent(stats2.failureRate)})`],
|
|
3465
|
+
["needs attention", `${stats2.needsAttentionRuns} (${formatRatePercent(stats2.needsAttentionRate)})`],
|
|
3466
|
+
["median run time", formatDuration(stats2.medianCompletionMs)],
|
|
3467
|
+
["steering", stats2.steeringTotal],
|
|
3468
|
+
["stalls", stats2.stallTotal],
|
|
3469
|
+
["pending approvals", stats2.approvalsPending]
|
|
3470
|
+
];
|
|
3471
|
+
}
|
|
3472
|
+
async function executeStats(context, args, deps = {}) {
|
|
3473
|
+
const list = [...args];
|
|
3474
|
+
const [first = "show", ...rest] = list;
|
|
3475
|
+
const command = first.startsWith("-") ? "show" : first;
|
|
3476
|
+
const commandArgs = first.startsWith("-") ? list : rest;
|
|
3477
|
+
if (command === "help") {
|
|
3478
|
+
printFormattedOutput2(`Usage: ${STATS_USAGE}`);
|
|
3479
|
+
return { ok: true, group: "stats", command };
|
|
3480
|
+
}
|
|
3481
|
+
if (command !== "show")
|
|
3482
|
+
throw new CliError2(`Unknown stats command: ${command}`, 1, { hint: "Run `rig stats --help` \u2014 the default command is `show`." });
|
|
3483
|
+
const sinceResult = takeOption2(commandArgs, "--since");
|
|
3484
|
+
requireNoExtraArgs2(sinceResult.rest, STATS_USAGE);
|
|
3485
|
+
const since = parseSinceOption(sinceResult.value);
|
|
3486
|
+
const readModel2 = await requireCapabilityForRoot2(context.projectRoot, RunReadModelCap4, "RUN_READ_MODEL capability unavailable for this project root: load @rig/run-plugin (standard bundle).");
|
|
3487
|
+
const details = await readModel2.getStats({
|
|
3488
|
+
projectRoot: context.projectRoot,
|
|
3489
|
+
since: since ? since.toISOString() : null,
|
|
3490
|
+
...deps.listRuns ? { runs: await deps.listRuns(context.projectRoot) } : {}
|
|
3491
|
+
});
|
|
3492
|
+
if ((context.outputMode ?? "text") === "text")
|
|
3493
|
+
printFormattedOutput2(formatStatsTable(formatStatsRows(details)));
|
|
3494
|
+
return { ok: true, group: "stats", command, details };
|
|
3495
|
+
}
|
|
3496
|
+
var DAY_MS, STATS_USAGE = "rig stats [show] [--since <7d|30d|ISO date>]", RunReadModelCap4;
|
|
3497
|
+
var init_stats_command = __esm(() => {
|
|
3498
|
+
DAY_MS = 24 * 60 * 60 * 1000;
|
|
3499
|
+
RunReadModelCap4 = defineCapability10(RUN_READ_MODEL4);
|
|
3500
|
+
});
|
|
3501
|
+
|
|
3502
|
+
// packages/run-plugin/src/read-model/inspect-command.ts
|
|
3503
|
+
var exports_inspect_command = {};
|
|
3504
|
+
__export(exports_inspect_command, {
|
|
3505
|
+
executeInspect: () => executeInspect
|
|
3506
|
+
});
|
|
3507
|
+
import { RUN_READ_MODEL as RUN_READ_MODEL5, TASK_CLI_DATA_CAPABILITY } from "@rig/contracts";
|
|
3508
|
+
import { defineCapability as defineCapability11 } from "@rig/core/capability";
|
|
3509
|
+
import { requireCapabilityForRoot as requireCapabilityForRoot3 } from "@rig/core/capability-loaders";
|
|
3510
|
+
import { CliError as CliError3, requireNoExtraArgs as requireNoExtraArgs3, requireOption, takeOption as takeOption3 } from "@rig/std-shared/cli-args";
|
|
3511
|
+
import { printFormattedOutput as printFormattedOutput3 } from "@rig/std-shared/cli-format";
|
|
3512
|
+
function inspectHelpText() {
|
|
3513
|
+
return [
|
|
3514
|
+
`Usage: ${INSPECT_USAGE}`,
|
|
3515
|
+
"",
|
|
3516
|
+
"Commands:",
|
|
3517
|
+
" logs --task <id> Print log lines from a projected run for a task.",
|
|
3518
|
+
" artifact --task <id> --file <name>",
|
|
3519
|
+
" Preview a single task artifact.",
|
|
3520
|
+
" artifacts --task <id> List task artifacts.",
|
|
3521
|
+
" run-logs --run <id> Print log lines from a run journal.",
|
|
3522
|
+
" runs List projected runs.",
|
|
3523
|
+
" diff --task <id> List files changed by a task.",
|
|
3524
|
+
" graph [--task <id>] Print the task dependency graph.",
|
|
3525
|
+
" failures --task <id> Print run read-model failure summaries for a task."
|
|
3526
|
+
].join(`
|
|
3527
|
+
`);
|
|
3528
|
+
}
|
|
3529
|
+
function printInspectHelp() {
|
|
3530
|
+
printFormattedOutput3(inspectHelpText());
|
|
3531
|
+
}
|
|
3532
|
+
function toRunLike(run, classification) {
|
|
3533
|
+
return {
|
|
3534
|
+
runId: run.runId,
|
|
3535
|
+
taskId: run.taskId,
|
|
3536
|
+
title: run.title,
|
|
3537
|
+
status: classification?.status ?? run.status,
|
|
3538
|
+
runStatus: classification?.runStatus,
|
|
3539
|
+
statusPhase: classification?.phase,
|
|
3540
|
+
statusRole: classification?.role,
|
|
3541
|
+
statusRank: classification?.rank,
|
|
3542
|
+
isActive: classification?.isActive,
|
|
3543
|
+
isTerminal: classification?.isTerminal,
|
|
3544
|
+
isNeedsAttention: classification?.isNeedsAttention,
|
|
3545
|
+
source: run.source,
|
|
3546
|
+
live: run.live,
|
|
3547
|
+
stale: run.stale,
|
|
3548
|
+
startedAt: run.startedAt,
|
|
3549
|
+
updatedAt: run.updatedAt,
|
|
3550
|
+
completedAt: run.completedAt,
|
|
3551
|
+
joinLink: run.joinLink,
|
|
3552
|
+
prUrl: run.prUrl,
|
|
3553
|
+
pendingApprovals: run.pendingApprovals,
|
|
3554
|
+
pendingInputs: run.pendingInputs,
|
|
3555
|
+
steeringCount: run.steeringCount,
|
|
3556
|
+
stallCount: run.stallCount
|
|
3557
|
+
};
|
|
3558
|
+
}
|
|
3559
|
+
function requireRunId(value, usage) {
|
|
3560
|
+
if (!value || !value.trim())
|
|
3561
|
+
throw new CliError3(`${usage} requires a run id.`, 2, { hint: "Run `rig inspect runs` to find run ids." });
|
|
3562
|
+
return value.trim();
|
|
3563
|
+
}
|
|
3564
|
+
async function loadRunReadModel2(projectRoot) {
|
|
3565
|
+
return requireCapabilityForRoot3(projectRoot, RunReadModelCap5, "RUN_READ_MODEL capability unavailable for this project root: load @rig/run-plugin (standard bundle).");
|
|
3566
|
+
}
|
|
3567
|
+
async function listRunRecords(projectRoot, deps) {
|
|
3568
|
+
return deps.listRunProjections ? deps.listRunProjections(projectRoot) : (await loadRunReadModel2(projectRoot)).listRuns({ projectRoot });
|
|
3569
|
+
}
|
|
3570
|
+
async function getRunRecord(projectRoot, runId, deps) {
|
|
3571
|
+
return deps.getRunProjection ? deps.getRunProjection(projectRoot, runId) : (await loadRunReadModel2(projectRoot)).getRun({ projectRoot, selector: { id: runId, kind: "any" } });
|
|
3572
|
+
}
|
|
3573
|
+
async function getRunDetails(projectRoot, runId, deps, options = {}) {
|
|
3574
|
+
return deps.getRunDetails ? deps.getRunDetails(projectRoot, runId, options) : (await loadRunReadModel2(projectRoot)).getRunDetails({ projectRoot, selector: { id: runId, kind: "any" }, ...options.includeLogs === undefined ? {} : { includeLogs: options.includeLogs } });
|
|
3575
|
+
}
|
|
3576
|
+
async function runsForTaskId(projectRoot, taskId, deps) {
|
|
3577
|
+
try {
|
|
3578
|
+
if (!deps.listRunProjections) {
|
|
3579
|
+
const runs = await (await loadRunReadModel2(projectRoot)).listRuns({ projectRoot, taskId });
|
|
3580
|
+
if (runs.length > 0)
|
|
3581
|
+
return runs;
|
|
3582
|
+
} else {
|
|
3583
|
+
const runs = await listRunRecords(projectRoot, deps);
|
|
3584
|
+
const filtered = runs.filter((run2) => run2.taskId === taskId || run2.runId === taskId);
|
|
3585
|
+
if (filtered.length > 0)
|
|
3586
|
+
return filtered;
|
|
3587
|
+
}
|
|
3588
|
+
} catch {}
|
|
3589
|
+
const run = await getRunRecord(projectRoot, taskId, deps);
|
|
3590
|
+
return run ? [run] : [];
|
|
3591
|
+
}
|
|
3592
|
+
async function representativeRunForTaskId(projectRoot, taskId, deps) {
|
|
3593
|
+
if (deps.listRunProjections) {
|
|
3594
|
+
const runs = await runsForTaskId(projectRoot, taskId, deps);
|
|
3595
|
+
for (const run of runs)
|
|
3596
|
+
return run;
|
|
3597
|
+
return null;
|
|
3598
|
+
}
|
|
3599
|
+
return (await loadRunReadModel2(projectRoot)).getRepresentativeRun({ projectRoot, taskId, purpose: "inspect" });
|
|
3600
|
+
}
|
|
3601
|
+
function failureLine(failure) {
|
|
3602
|
+
return failure.detail ? `${failure.summary} \u2014 ${failure.detail}` : failure.summary;
|
|
3603
|
+
}
|
|
3604
|
+
async function executeLogs(context, args, deps) {
|
|
3605
|
+
const taskResult = takeOption3(args, "--task");
|
|
3606
|
+
requireNoExtraArgs3(taskResult.rest, "rig inspect logs --task <id>");
|
|
3607
|
+
const taskId = requireOption(taskResult.value, "--task", "rig inspect logs --task <id>");
|
|
3608
|
+
const projection = await representativeRunForTaskId(context.projectRoot, taskId, deps);
|
|
3609
|
+
if (!projection)
|
|
3610
|
+
throw new CliError3(`No projected runs found for task ${taskId}.`, 1, { hint: "Run `rig run list` to confirm the run journal is discoverable." });
|
|
3611
|
+
const details = await getRunDetails(context.projectRoot, projection.runId, deps, { includeLogs: true });
|
|
3612
|
+
const lines = [...details?.logs ?? []];
|
|
3613
|
+
if (context.outputMode === "text") {
|
|
3614
|
+
if (lines.length === 0)
|
|
3615
|
+
console.log(`No log entries found for task ${taskId}.`);
|
|
3616
|
+
else
|
|
3617
|
+
for (const line of lines)
|
|
3618
|
+
console.log(line);
|
|
3619
|
+
}
|
|
3620
|
+
return { ok: true, group: "inspect", command: "logs", details: { taskId, runId: projection.runId, count: lines.length, lines } };
|
|
3621
|
+
}
|
|
3622
|
+
async function executeArtifact(context, args, deps) {
|
|
3623
|
+
const taskResult = takeOption3(args, "--task");
|
|
3624
|
+
const fileResult = takeOption3(taskResult.rest, "--file");
|
|
3625
|
+
requireNoExtraArgs3(fileResult.rest, "rig inspect artifact --task <id> --file <name>");
|
|
3626
|
+
const taskId = requireOption(taskResult.value, "--task", "rig inspect artifact --task <id> --file <name>");
|
|
3627
|
+
const filename = fileResult.value?.trim();
|
|
3628
|
+
if (!filename)
|
|
3629
|
+
throw new CliError3("rig inspect artifact --task <id> --file <name> requires an artifact file name.", 2);
|
|
3630
|
+
const preview = await (deps.taskArtifactRead ?? (await taskCliData(context.projectRoot)).taskArtifactRead)(context.projectRoot, filename, { taskId, maxBytes: 64 * 1024 });
|
|
3631
|
+
if (context.outputMode === "text") {
|
|
3632
|
+
console.log(preview.contents);
|
|
3633
|
+
if (preview.truncated)
|
|
3634
|
+
console.log(`
|
|
3635
|
+
[truncated at ${preview.maxBytes} bytes; artifact is ${preview.sizeBytes} bytes]`);
|
|
3636
|
+
}
|
|
3637
|
+
return { ok: true, group: "inspect", command: "artifact", details: { taskId, file: filename, ...preview } };
|
|
3638
|
+
}
|
|
3639
|
+
async function executeRunLogs(context, args, deps) {
|
|
3640
|
+
const runResult = takeOption3(args, "--run");
|
|
3641
|
+
requireNoExtraArgs3(runResult.rest, "rig inspect run-logs --run <id>");
|
|
3642
|
+
const runId = requireRunId(runResult.value, "rig inspect run-logs --run <id>");
|
|
3643
|
+
const projection = await getRunRecord(context.projectRoot, runId, deps);
|
|
3644
|
+
if (!projection)
|
|
3645
|
+
throw new CliError3(`Run not found: ${runId}`, 2, { hint: "Run `rig inspect runs` to see runs." });
|
|
3646
|
+
const details = await getRunDetails(context.projectRoot, projection.runId, deps, { includeLogs: true });
|
|
3647
|
+
const lines = [...details?.logs ?? []];
|
|
3648
|
+
if (context.outputMode === "text") {
|
|
3649
|
+
if (lines.length === 0)
|
|
3650
|
+
console.log(`No log entries found for run ${projection.runId}.`);
|
|
3651
|
+
else
|
|
3652
|
+
for (const line of lines)
|
|
3653
|
+
console.log(line);
|
|
3654
|
+
}
|
|
3655
|
+
return { ok: true, group: "inspect", command: "run-logs", details: { runId: projection.runId, taskId: projection.taskId, count: lines.length, lines } };
|
|
3656
|
+
}
|
|
3657
|
+
async function executeRuns(context, args, deps) {
|
|
3658
|
+
requireNoExtraArgs3(args, "rig inspect runs");
|
|
3659
|
+
let formatted;
|
|
3660
|
+
if (deps.listRunProjections) {
|
|
3661
|
+
formatted = (await listRunRecords(context.projectRoot, deps)).map((run) => toRunLike(run));
|
|
3662
|
+
} else {
|
|
3663
|
+
const readModel2 = await loadRunReadModel2(context.projectRoot);
|
|
3664
|
+
const runs = await readModel2.listRuns({ projectRoot: context.projectRoot });
|
|
3665
|
+
formatted = runs.map((run) => toRunLike(run, readModel2.classifyRun(run)));
|
|
3666
|
+
}
|
|
3667
|
+
if (context.outputMode === "text")
|
|
3668
|
+
printFormattedOutput3(formatRunList(formatted));
|
|
3669
|
+
return { ok: true, group: "inspect", command: "runs", details: { runs: formatted } };
|
|
3670
|
+
}
|
|
3671
|
+
async function executeArtifacts(context, args, deps) {
|
|
3672
|
+
const taskResult = takeOption3(args, "--task");
|
|
3673
|
+
requireNoExtraArgs3(taskResult.rest, "rig inspect artifacts --task <id>");
|
|
3674
|
+
const taskId = requireOption(taskResult.value, "--task", "rig inspect artifacts --task <id>");
|
|
3675
|
+
if (context.outputMode === "text")
|
|
3676
|
+
await (deps.taskArtifacts ?? (await taskCliData(context.projectRoot)).taskArtifacts)(context.projectRoot, taskId);
|
|
3677
|
+
return { ok: true, group: "inspect", command: "artifacts", details: { taskId, printed: context.outputMode === "text" } };
|
|
3678
|
+
}
|
|
3679
|
+
async function executeDiff(context, args, deps) {
|
|
3680
|
+
const taskResult = takeOption3(args, "--task");
|
|
3681
|
+
requireNoExtraArgs3(taskResult.rest, "rig inspect diff --task <id>");
|
|
3682
|
+
const taskId = requireOption(taskResult.value, "--task", "rig inspect diff --task <id>");
|
|
3683
|
+
const files = (deps.changedFilesForTask ?? (await taskCliData(context.projectRoot)).changedFilesForTask)(context.projectRoot, taskId, true);
|
|
3684
|
+
if (context.outputMode === "text")
|
|
3685
|
+
for (const file of files)
|
|
3686
|
+
console.log(file);
|
|
3687
|
+
return { ok: true, group: "inspect", command: "diff", details: { taskId, files } };
|
|
3688
|
+
}
|
|
3689
|
+
async function executeGraph(context, args, deps) {
|
|
3690
|
+
const taskResult = takeOption3(args, "--task");
|
|
3691
|
+
requireNoExtraArgs3(taskResult.rest, "rig inspect graph [--task <id>]");
|
|
3692
|
+
if (context.outputMode === "text")
|
|
3693
|
+
await (deps.taskDeps ?? (await taskCliData(context.projectRoot)).taskDeps)(context.projectRoot, taskResult.value);
|
|
3694
|
+
return { ok: true, group: "inspect", command: "graph", details: { taskId: taskResult.value ?? null, printed: context.outputMode === "text" } };
|
|
3695
|
+
}
|
|
3696
|
+
async function executeFailures(context, args, deps) {
|
|
3697
|
+
const taskResult = takeOption3(args, "--task");
|
|
3698
|
+
requireNoExtraArgs3(taskResult.rest, "rig inspect failures --task <id>");
|
|
3699
|
+
const taskId = requireOption(taskResult.value, "--task", "rig inspect failures --task <id>");
|
|
3700
|
+
const runs = await runsForTaskId(context.projectRoot, taskId, deps);
|
|
3701
|
+
const failures = (await Promise.all(runs.map(async (run) => (await getRunDetails(context.projectRoot, run.runId, deps))?.failures ?? []))).flat().map(failureLine);
|
|
3702
|
+
if (context.outputMode === "text") {
|
|
3703
|
+
if (failures.length === 0)
|
|
3704
|
+
console.log(`No failures recorded for task ${taskId}.`);
|
|
3705
|
+
else
|
|
3706
|
+
for (const failure of failures)
|
|
3707
|
+
console.log(failure);
|
|
3708
|
+
}
|
|
3709
|
+
return { ok: true, group: "inspect", command: "failures", details: { taskId, count: failures.length, failures } };
|
|
3710
|
+
}
|
|
3711
|
+
function executeAudit(args) {
|
|
3712
|
+
requireNoExtraArgs3(args, "rig inspect audit");
|
|
3713
|
+
throw new CliError3("rig inspect audit cannot read the controlled-command audit trail yet: current code only writes audit JSONL and exposes no supported reader API.", 2, { hint: "Use the OMP session history for operator audit context until a controlled-bash audit reader is added." });
|
|
3714
|
+
}
|
|
3715
|
+
async function executeInspect(context, args, deps = {}) {
|
|
3716
|
+
const [command = "failures", ...rest] = args;
|
|
3717
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
3718
|
+
if (context.outputMode === "text")
|
|
3719
|
+
printInspectHelp();
|
|
3720
|
+
return { ok: true, group: "inspect", command: "help" };
|
|
3721
|
+
}
|
|
3722
|
+
switch (command) {
|
|
3723
|
+
case "logs":
|
|
3724
|
+
return executeLogs(context, rest, deps);
|
|
3725
|
+
case "artifact":
|
|
3726
|
+
return executeArtifact(context, rest, deps);
|
|
3727
|
+
case "artifacts":
|
|
3728
|
+
return executeArtifacts(context, rest, deps);
|
|
3729
|
+
case "run-logs":
|
|
3730
|
+
return executeRunLogs(context, rest, deps);
|
|
3731
|
+
case "runs":
|
|
3732
|
+
return executeRuns(context, rest, deps);
|
|
3733
|
+
case "diff":
|
|
3734
|
+
return executeDiff(context, rest, deps);
|
|
3735
|
+
case "graph":
|
|
3736
|
+
return executeGraph(context, rest, deps);
|
|
3737
|
+
case "failures":
|
|
3738
|
+
return executeFailures(context, rest, deps);
|
|
3739
|
+
case "audit":
|
|
3740
|
+
return executeAudit(rest);
|
|
3741
|
+
default:
|
|
3742
|
+
throw new CliError3(`Unknown inspect command: ${command}`, 1, { hint: "Run `rig inspect --help` to list inspect commands." });
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
var RunReadModelCap5, TaskCliDataCap, taskCliData = (projectRoot) => requireCapabilityForRoot3(projectRoot, TaskCliDataCap, "CLI task-data capability unavailable: load @rig/tasks-plugin (default bundle)."), INSPECT_USAGE = "rig inspect <logs|artifact|artifacts|run-logs|runs|failures|graph|diff>";
|
|
3746
|
+
var init_inspect_command = __esm(() => {
|
|
3747
|
+
init_run_format();
|
|
3748
|
+
RunReadModelCap5 = defineCapability11(RUN_READ_MODEL5);
|
|
3749
|
+
TaskCliDataCap = defineCapability11(TASK_CLI_DATA_CAPABILITY);
|
|
3750
|
+
});
|
|
3751
|
+
|
|
3752
|
+
// packages/run-plugin/src/read-model/runs-screen.ts
|
|
3753
|
+
var exports_runs_screen = {};
|
|
3754
|
+
__export(exports_runs_screen, {
|
|
3755
|
+
createRunsBoardView: () => createRunsBoardView
|
|
3756
|
+
});
|
|
3757
|
+
import {
|
|
3758
|
+
DRONE_WIDTH,
|
|
3759
|
+
RIG_SPINNER_FRAMES,
|
|
3760
|
+
accent as accent2,
|
|
3761
|
+
bold,
|
|
3762
|
+
ink,
|
|
3763
|
+
ink2,
|
|
3764
|
+
ink3,
|
|
3765
|
+
ink4,
|
|
3766
|
+
phaseGlyph,
|
|
3767
|
+
red,
|
|
3768
|
+
renderDroneFrame,
|
|
3769
|
+
statusRoleColor
|
|
3770
|
+
} from "@rig/std-shared/board-theme";
|
|
3771
|
+
function createRunsBoardView(host, deps) {
|
|
3772
|
+
const { truncateToWidth, matchesKey } = deps;
|
|
3773
|
+
let runs = [];
|
|
3774
|
+
let selected = 0;
|
|
3775
|
+
let loading = true;
|
|
3776
|
+
let error = null;
|
|
3777
|
+
let tick = 0;
|
|
3778
|
+
let filter = "";
|
|
3779
|
+
let sortMode = "active";
|
|
3780
|
+
function visibleRuns() {
|
|
3781
|
+
const query = filter.trim().toLowerCase();
|
|
3782
|
+
const filtered = query ? runs.filter((run) => run.runId.toLowerCase().includes(query) || run.status.toLowerCase().includes(query) || run.title.toLowerCase().includes(query)) : [...runs];
|
|
3783
|
+
if (sortMode === "active") {
|
|
3784
|
+
return filtered.map((run, index) => ({ run, index })).sort((a, b) => a.run.rank - b.run.rank || a.index - b.index).map((entry) => entry.run);
|
|
3785
|
+
}
|
|
3786
|
+
if (sortMode === "status") {
|
|
3787
|
+
return filtered.map((run, index) => ({ run, index })).sort((a, b) => a.run.status.localeCompare(b.run.status) || a.index - b.index).map((entry) => entry.run);
|
|
3788
|
+
}
|
|
3789
|
+
return filtered;
|
|
3790
|
+
}
|
|
3791
|
+
function clampSelection() {
|
|
3792
|
+
selected = Math.max(0, Math.min(selected, Math.max(0, visibleRuns().length - 1)));
|
|
3793
|
+
}
|
|
3794
|
+
function moveSelection(delta) {
|
|
3795
|
+
const visible = visibleRuns();
|
|
3796
|
+
if (visible.length === 0)
|
|
3797
|
+
return;
|
|
3798
|
+
selected = Math.max(0, Math.min(visible.length - 1, selected + delta));
|
|
3799
|
+
}
|
|
3800
|
+
function selectedRun3() {
|
|
3801
|
+
return visibleRuns()[selected] ?? null;
|
|
3802
|
+
}
|
|
3803
|
+
function setFilter(next) {
|
|
3804
|
+
filter = next;
|
|
3805
|
+
clampSelection();
|
|
3806
|
+
}
|
|
3807
|
+
function cycleSort() {
|
|
3808
|
+
sortMode = sortMode === "active" ? "recent" : sortMode === "recent" ? "status" : "active";
|
|
3809
|
+
clampSelection();
|
|
3810
|
+
return sortMode;
|
|
3811
|
+
}
|
|
3812
|
+
function droneBoardLines(width, message, messageWidth) {
|
|
3813
|
+
const spinner = RIG_SPINNER_FRAMES[tick % RIG_SPINNER_FRAMES.length];
|
|
3814
|
+
if (width < DRONE_WIDTH + 4) {
|
|
3815
|
+
return ["", ` ${accent2(spinner)} ${ink3(message)}`, ""];
|
|
3816
|
+
}
|
|
3817
|
+
const pad2 = " ".repeat(Math.max(0, Math.floor((width - DRONE_WIDTH) / 2)));
|
|
3818
|
+
const msgPad = " ".repeat(Math.max(0, Math.floor((width - messageWidth) / 2)));
|
|
3819
|
+
return [
|
|
3820
|
+
"",
|
|
3821
|
+
...renderDroneFrame(tick).map((line) => pad2 + line),
|
|
3822
|
+
"",
|
|
3823
|
+
`${msgPad}${accent2(spinner)} ${ink3(message)}`,
|
|
3824
|
+
""
|
|
3825
|
+
];
|
|
3826
|
+
}
|
|
3827
|
+
function renderLines(width) {
|
|
3828
|
+
if (loading) {
|
|
3829
|
+
return droneBoardLines(width, "contacting the fleet\u2026", 24);
|
|
3830
|
+
}
|
|
3831
|
+
if (error) {
|
|
3832
|
+
return ["", ` ${red("runs unavailable:")} ${ink2(error)}`, ""];
|
|
3833
|
+
}
|
|
3834
|
+
const visible = visibleRuns();
|
|
3835
|
+
if (runs.length === 0) {
|
|
3836
|
+
return droneBoardLines(width, "no runs yet \u2014 n picks a task and launches one", 44);
|
|
3837
|
+
}
|
|
3838
|
+
if (visible.length === 0) {
|
|
3839
|
+
return ["", ` ${ink2("no runs match")} ${accent2(`/${filter}`)} ${ink3("\u2014 esc clears the filter")}`, ""];
|
|
3840
|
+
}
|
|
3841
|
+
const start = Math.max(0, Math.min(selected - Math.floor(MAX_VISIBLE / 2), visible.length - MAX_VISIBLE));
|
|
3842
|
+
const window = visible.slice(start, start + MAX_VISIBLE);
|
|
3843
|
+
const lines = window.map((run, index) => {
|
|
3844
|
+
const isSelected = start + index === selected;
|
|
3845
|
+
const color = statusRoleColor(run.role);
|
|
3846
|
+
const dot = color(phaseGlyph(run.phase));
|
|
3847
|
+
const id = run.runId.slice(0, 8);
|
|
3848
|
+
const status = color(run.status.padEnd(16));
|
|
3849
|
+
const title = truncateToWidth(run.title, Math.max(8, width - 36));
|
|
3850
|
+
const row = ` ${dot} ${isSelected ? bold(ink(id)) : ink3(id)} ${status} ${isSelected ? ink(title) : ink2(title)}`;
|
|
3851
|
+
return isSelected ? accent2("\u258C") + row : " " + row;
|
|
3852
|
+
});
|
|
3853
|
+
const meta = [];
|
|
3854
|
+
if (visible.length > MAX_VISIBLE)
|
|
3855
|
+
meta.push(`${selected + 1}/${visible.length}`);
|
|
3856
|
+
if (filter)
|
|
3857
|
+
meta.push(`filter: ${filter}`);
|
|
3858
|
+
meta.push(`sort: ${sortMode}`);
|
|
3859
|
+
lines.push(ink4(` ${meta.join(" \xB7 ")}`));
|
|
3860
|
+
return ["", ...lines, ""];
|
|
3861
|
+
}
|
|
3862
|
+
function handleInput(data) {
|
|
3863
|
+
const enc = (value) => encodeURIComponent(value);
|
|
3864
|
+
if (matchesKey(data, "up") || data === "k") {
|
|
3865
|
+
moveSelection(-1);
|
|
3866
|
+
host.requestRender();
|
|
3867
|
+
return true;
|
|
3868
|
+
}
|
|
3869
|
+
if (matchesKey(data, "down") || data === "j") {
|
|
3870
|
+
moveSelection(1);
|
|
3871
|
+
host.requestRender();
|
|
3872
|
+
return true;
|
|
3873
|
+
}
|
|
3874
|
+
if (matchesKey(data, "enter") || matchesKey(data, "return")) {
|
|
3875
|
+
const run = selectedRun3();
|
|
3876
|
+
if (run)
|
|
3877
|
+
host.act(`run-detail:${enc(run.runId)}`);
|
|
3878
|
+
return true;
|
|
3879
|
+
}
|
|
3880
|
+
if (data === "a") {
|
|
3881
|
+
const run = selectedRun3();
|
|
3882
|
+
if (run)
|
|
3883
|
+
host.attach(run.runId);
|
|
3884
|
+
return true;
|
|
3885
|
+
}
|
|
3886
|
+
if (data === "s") {
|
|
3887
|
+
const run = selectedRun3();
|
|
3888
|
+
if (run)
|
|
3889
|
+
host.openSteer(run.runId);
|
|
3890
|
+
return true;
|
|
3891
|
+
}
|
|
3892
|
+
if (data === "p") {
|
|
3893
|
+
const run = selectedRun3();
|
|
3894
|
+
if (run) {
|
|
3895
|
+
host.setNotice(`pause requested for ${run.runId.slice(0, 8)}\u2026`);
|
|
3896
|
+
host.act(`run-pause:${enc(run.runId)}`);
|
|
3897
|
+
}
|
|
3898
|
+
return true;
|
|
3899
|
+
}
|
|
3900
|
+
if (data === "u") {
|
|
3901
|
+
const run = selectedRun3();
|
|
3902
|
+
if (run) {
|
|
3903
|
+
host.setNotice(`resume requested for ${run.runId.slice(0, 8)}\u2026`);
|
|
3904
|
+
host.act(`run-resume:${enc(run.runId)}`);
|
|
3905
|
+
}
|
|
3906
|
+
return true;
|
|
3907
|
+
}
|
|
3908
|
+
if (data === "x") {
|
|
3909
|
+
const run = selectedRun3();
|
|
3910
|
+
if (run) {
|
|
3911
|
+
host.setNotice(`stop requested for ${run.runId.slice(0, 8)}\u2026`);
|
|
3912
|
+
host.act(`run-stop:${enc(run.runId)}`);
|
|
3913
|
+
}
|
|
3914
|
+
return true;
|
|
3915
|
+
}
|
|
3916
|
+
if (data === "n") {
|
|
3917
|
+
host.navigate("tasks");
|
|
3918
|
+
return true;
|
|
3919
|
+
}
|
|
3920
|
+
if (data === "/") {
|
|
3921
|
+
host.openSearch(filter, setFilter);
|
|
3922
|
+
return true;
|
|
3923
|
+
}
|
|
3924
|
+
if (data === "o") {
|
|
3925
|
+
const mode = cycleSort();
|
|
3926
|
+
host.setNotice(`sorted: ${mode}`);
|
|
3927
|
+
host.requestRender();
|
|
3928
|
+
return true;
|
|
3929
|
+
}
|
|
3930
|
+
if (matchesKey(data, "escape")) {
|
|
3931
|
+
if (filter) {
|
|
3932
|
+
setFilter("");
|
|
3933
|
+
host.requestRender();
|
|
3934
|
+
return true;
|
|
3935
|
+
}
|
|
3936
|
+
return false;
|
|
3937
|
+
}
|
|
3938
|
+
return false;
|
|
3939
|
+
}
|
|
3940
|
+
return {
|
|
3941
|
+
render(width) {
|
|
3942
|
+
return renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
|
|
3943
|
+
},
|
|
3944
|
+
handleInput,
|
|
3945
|
+
setBoard(board) {
|
|
3946
|
+
if (board.kind !== "runs")
|
|
3947
|
+
return;
|
|
3948
|
+
const previous = selectedRun3()?.runId;
|
|
3949
|
+
runs = [...board.runs];
|
|
3950
|
+
if (previous) {
|
|
3951
|
+
const index = visibleRuns().findIndex((run) => run.runId === previous);
|
|
3952
|
+
if (index >= 0)
|
|
3953
|
+
selected = index;
|
|
3954
|
+
}
|
|
3955
|
+
clampSelection();
|
|
3956
|
+
loading = false;
|
|
3957
|
+
error = null;
|
|
3958
|
+
},
|
|
3959
|
+
setError(next) {
|
|
3960
|
+
error = next;
|
|
3961
|
+
},
|
|
3962
|
+
setTick(next) {
|
|
3963
|
+
tick = next;
|
|
3964
|
+
},
|
|
3965
|
+
invalidate() {}
|
|
3966
|
+
};
|
|
3967
|
+
}
|
|
3968
|
+
var MAX_VISIBLE = 16;
|
|
3969
|
+
var init_runs_screen = () => {};
|
|
3970
|
+
|
|
3971
|
+
// packages/run-plugin/src/plugin.ts
|
|
3972
|
+
import { definePlugin as definePlugin3 } from "@rig/core/config";
|
|
3973
|
+
|
|
3974
|
+
// packages/run-plugin/src/worker/plugin.ts
|
|
3975
|
+
import { defineCapability as defineCapability6 } from "@rig/core/capability";
|
|
3976
|
+
import { definePlugin } from "@rig/core/config";
|
|
3977
|
+
import {
|
|
3978
|
+
RIG_WORKFLOW_JOURNAL_WRITER,
|
|
3979
|
+
RUN_CONTROL as RUN_CONTROL2,
|
|
3980
|
+
RUN_SESSION_JOURNAL_WRITER as RUN_SESSION_JOURNAL_WRITER3,
|
|
3981
|
+
SESSION_RUN_ADAPTER
|
|
3982
|
+
} from "@rig/contracts";
|
|
3983
|
+
async function installWorkerInboxIfPresent(parentPort) {
|
|
3984
|
+
if (!parentPort)
|
|
3985
|
+
return;
|
|
3986
|
+
const { installWorkerInbox } = await import("@oh-my-pi/pi-utils/worker-host");
|
|
3987
|
+
installWorkerInbox(parentPort);
|
|
3988
|
+
}
|
|
3989
|
+
var RunSessionJournalWriterCap2 = defineCapability6(RUN_SESSION_JOURNAL_WRITER3);
|
|
3990
|
+
var WorkflowJournalWriterCap = defineCapability6(RIG_WORKFLOW_JOURNAL_WRITER);
|
|
3991
|
+
var RunControlCap2 = defineCapability6(RUN_CONTROL2);
|
|
3992
|
+
var SessionRunAdapterCap = defineCapability6(SESSION_RUN_ADAPTER);
|
|
3993
|
+
var RUN_WORKER_SEED_ENTRYPOINTS = [
|
|
3994
|
+
{
|
|
3995
|
+
id: "@rig/run-worker:stats-sync-worker",
|
|
3996
|
+
workerArg: "__omp_worker_stats_sync",
|
|
3997
|
+
description: "OMP stats sync self-exec worker.",
|
|
3998
|
+
run: async () => {
|
|
3999
|
+
const scope = globalThis;
|
|
4000
|
+
const pending = [];
|
|
4001
|
+
const buffer = (event) => {
|
|
4002
|
+
pending.push(event);
|
|
4003
|
+
};
|
|
4004
|
+
scope.onmessage = buffer;
|
|
4005
|
+
await import("@oh-my-pi/omp-stats/sync-worker");
|
|
4006
|
+
const handler = scope.onmessage;
|
|
4007
|
+
if (handler && handler !== buffer) {
|
|
4008
|
+
for (const event of pending)
|
|
4009
|
+
handler.call(scope, event);
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
},
|
|
4013
|
+
{
|
|
4014
|
+
id: "@rig/run-worker:tab-worker",
|
|
4015
|
+
workerArg: "__omp_worker_tab",
|
|
4016
|
+
description: "Browser tab tool self-exec worker.",
|
|
4017
|
+
run: async ({ parentPort }) => {
|
|
4018
|
+
await installWorkerInboxIfPresent(parentPort);
|
|
4019
|
+
await import("@oh-my-pi/pi-coding-agent/tools/browser/tab-worker-entry");
|
|
4020
|
+
}
|
|
4021
|
+
},
|
|
4022
|
+
{
|
|
4023
|
+
id: "@rig/run-worker:js-eval-worker",
|
|
4024
|
+
workerArg: "__omp_worker_js_eval",
|
|
4025
|
+
description: "JavaScript eval tool self-exec worker.",
|
|
4026
|
+
run: async ({ parentPort }) => {
|
|
4027
|
+
await installWorkerInboxIfPresent(parentPort);
|
|
4028
|
+
await import("@oh-my-pi/pi-coding-agent/eval/js/worker-entry");
|
|
4029
|
+
}
|
|
4030
|
+
},
|
|
4031
|
+
{
|
|
4032
|
+
id: "@rig/run-worker:tiny-inference-worker",
|
|
4033
|
+
workerArg: "__omp_worker_tiny_inference",
|
|
4034
|
+
description: "Tiny-model (titles) transformers.js IPC subprocess worker.",
|
|
4035
|
+
run: async () => {
|
|
4036
|
+
const { startTinyTitleWorker } = await import("@oh-my-pi/pi-coding-agent/tiny/worker");
|
|
4037
|
+
await runIpcSubprocessWorker(startTinyTitleWorker);
|
|
4038
|
+
}
|
|
4039
|
+
},
|
|
4040
|
+
{
|
|
4041
|
+
id: "@rig/run-worker:stt-worker",
|
|
4042
|
+
workerArg: "__omp_worker_stt",
|
|
4043
|
+
description: "Speech-to-text transformers.js IPC subprocess worker.",
|
|
4044
|
+
run: async () => {
|
|
4045
|
+
const { startSttWorker } = await import("@oh-my-pi/pi-coding-agent/stt/asr-worker");
|
|
4046
|
+
await runIpcSubprocessWorker(startSttWorker);
|
|
4047
|
+
}
|
|
4048
|
+
},
|
|
4049
|
+
{
|
|
4050
|
+
id: "@rig/run-worker:tts-worker",
|
|
4051
|
+
workerArg: "__omp_worker_tts",
|
|
4052
|
+
description: "Text-to-speech transformers.js IPC subprocess worker.",
|
|
4053
|
+
run: async () => {
|
|
4054
|
+
const { startTtsWorker } = await import("@oh-my-pi/pi-coding-agent/tts/tts-worker");
|
|
4055
|
+
await runIpcSubprocessWorker(startTtsWorker);
|
|
4056
|
+
}
|
|
4057
|
+
},
|
|
4058
|
+
{
|
|
4059
|
+
id: "@rig/run-worker:mnemopi-embed-worker",
|
|
4060
|
+
workerArg: "__omp_worker_mnemopi_embed",
|
|
4061
|
+
description: "Mnemopi embedding IPC subprocess worker.",
|
|
4062
|
+
run: async () => {
|
|
4063
|
+
const { startMnemopiEmbedWorker } = await import("@oh-my-pi/pi-coding-agent/mnemopi/embed-worker");
|
|
4064
|
+
await runIpcSubprocessWorker(startMnemopiEmbedWorker);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
];
|
|
4068
|
+
async function runIpcSubprocessWorker(start) {
|
|
4069
|
+
const { promise: shuttingDown, resolve: shutdown } = Promise.withResolvers();
|
|
4070
|
+
const send = (message) => {
|
|
4071
|
+
const sender = process.send;
|
|
4072
|
+
if (!sender) {
|
|
4073
|
+
shutdown();
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4076
|
+
try {
|
|
4077
|
+
sender.call(process, message);
|
|
4078
|
+
} catch {
|
|
4079
|
+
shutdown();
|
|
4080
|
+
}
|
|
4081
|
+
};
|
|
4082
|
+
start({
|
|
4083
|
+
send,
|
|
4084
|
+
onMessage(handler) {
|
|
4085
|
+
const wrap = (data) => handler(data);
|
|
4086
|
+
process.on("message", wrap);
|
|
4087
|
+
return () => {
|
|
4088
|
+
process.off("message", wrap);
|
|
4089
|
+
};
|
|
4090
|
+
}
|
|
4091
|
+
});
|
|
4092
|
+
const keepalive = setInterval(() => {}, 1073741824);
|
|
4093
|
+
process.on("disconnect", () => shutdown());
|
|
4094
|
+
try {
|
|
4095
|
+
await shuttingDown;
|
|
4096
|
+
} finally {
|
|
4097
|
+
clearInterval(keepalive);
|
|
4098
|
+
}
|
|
4099
|
+
process.kill(process.pid, "SIGKILL");
|
|
4100
|
+
}
|
|
4101
|
+
var inboxCommand = {
|
|
4102
|
+
id: "@rig/run-worker:inbox",
|
|
4103
|
+
family: "inbox",
|
|
4104
|
+
description: "Inspect and resolve run inbox items.",
|
|
4105
|
+
projectRequired: true,
|
|
4106
|
+
run: async (context, args) => (await Promise.resolve().then(() => (init_inbox_command(), exports_inbox_command))).executeInbox(context, [...args]),
|
|
4107
|
+
helpDoc: {
|
|
4108
|
+
name: "inbox",
|
|
4109
|
+
summary: "Legacy automation-only request view; normal UX is the OMP Inbox screen.",
|
|
4110
|
+
usage: ["rig inbox <approvals|approve|inputs|respond> [options]"],
|
|
4111
|
+
commands: [
|
|
4112
|
+
{ command: "approvals [--run <id>] [--task <id>]", description: "Legacy automation-only: list pending approval records.", primary: true },
|
|
4113
|
+
{ command: "inputs [--run <id>] [--task <id>]", description: "Legacy automation-only: list pending user-input records.", primary: true },
|
|
4114
|
+
{ command: "approve --run <id> --request <id> --decision approve|reject", description: "Legacy automation-only: resolve an approval record." },
|
|
4115
|
+
{ command: "respond --run <id> --request <id> --text <answer> | --answer <answer>", description: "Legacy automation-only: answer a user-input record." }
|
|
4116
|
+
],
|
|
4117
|
+
examples: [
|
|
4118
|
+
"rig inbox approvals # legacy automation only"
|
|
4119
|
+
],
|
|
4120
|
+
next: ["For normal UX, use bare `rig`, then the OMP Inbox screen."]
|
|
4121
|
+
}
|
|
4122
|
+
};
|
|
4123
|
+
var runWorkerPlugin = definePlugin({
|
|
4124
|
+
name: "@rig/run-plugin",
|
|
4125
|
+
version: "0.0.0-alpha.1",
|
|
4126
|
+
effects: {
|
|
4127
|
+
readsFiles: true,
|
|
4128
|
+
writesFiles: true,
|
|
4129
|
+
opensNetwork: true,
|
|
4130
|
+
contributesCliCommands: true
|
|
4131
|
+
},
|
|
4132
|
+
contributes: {
|
|
4133
|
+
cliCommands: [inboxCommand],
|
|
4134
|
+
capabilities: [
|
|
4135
|
+
RunSessionJournalWriterCap2.provide(async () => (await Promise.resolve().then(() => (init_session_journal_writer(), exports_session_journal_writer))).runSessionJournalWriterService, {
|
|
4136
|
+
title: "Run session-journal writer",
|
|
4137
|
+
description: "Bind writable run journals to live OMP sessions for run-worker lifecycle updates."
|
|
4138
|
+
}),
|
|
4139
|
+
WorkflowJournalWriterCap.provide(async () => (await Promise.resolve().then(() => (init_workflow_journal_writer(), exports_workflow_journal_writer))).workflowJournalWriterService, {
|
|
4140
|
+
title: "Workflow journal writer",
|
|
4141
|
+
description: "Mint and append rig.workflow.* session facts; owned by the run-status writer owner (not the read-only read-model)."
|
|
4142
|
+
}),
|
|
4143
|
+
RunControlCap2.provide(async () => (await Promise.resolve().then(() => (init_run_control_service(), exports_run_control_service))).runControlService, {
|
|
4144
|
+
title: "Run control endpoint",
|
|
4145
|
+
description: "Resolve run action targets, deliver operator control/steer/stop/pause/resume, resolve inbox requests, plan resume, and reconcile dead-pid runs."
|
|
4146
|
+
}),
|
|
4147
|
+
SessionRunAdapterCap.provide(async () => {
|
|
4148
|
+
const { default: rigWorkerExtension2 } = await Promise.resolve().then(() => (init_extension(), exports_extension));
|
|
4149
|
+
return {
|
|
4150
|
+
installRunAdapter: (api) => rigWorkerExtension2(api)
|
|
4151
|
+
};
|
|
4152
|
+
}, {
|
|
4153
|
+
title: "Session\u2192run adapter",
|
|
4154
|
+
description: "Install the run-worker session extension, adapting the configured harness session into a running Rig run."
|
|
4155
|
+
})
|
|
4156
|
+
],
|
|
4157
|
+
seedEntrypoints: RUN_WORKER_SEED_ENTRYPOINTS
|
|
4158
|
+
}
|
|
4159
|
+
});
|
|
4160
|
+
function createRunWorkerPlugin() {
|
|
4161
|
+
return runWorkerPlugin;
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4164
|
+
// packages/run-plugin/src/read-model/plugin.ts
|
|
4165
|
+
import { defineCapability as defineCapability12 } from "@rig/core/capability";
|
|
4166
|
+
import { loadCapabilityForRoot as loadCapabilityForRoot6 } from "@rig/core/capability-loaders";
|
|
4167
|
+
import { definePlugin as definePlugin2 } from "@rig/core/config";
|
|
4168
|
+
import {
|
|
4169
|
+
RIG_COCKPIT_PANEL_SLOT,
|
|
4170
|
+
RUN_IDENTITY_ENV as RUN_IDENTITY_ENV2,
|
|
4171
|
+
RUN_READ_MODEL as RUN_READ_MODEL6,
|
|
4172
|
+
RUN_SESSION_JOURNAL as RUN_SESSION_JOURNAL3,
|
|
4173
|
+
TASK_IO_SERVICE_CAPABILITY,
|
|
4174
|
+
TASK_PROJECTION_CAPABILITY_ID
|
|
4175
|
+
} from "@rig/contracts";
|
|
4176
|
+
var RUN_READ_MODEL_PLUGIN_NAME = "@rig/run-plugin";
|
|
4177
|
+
var RunReadModelCap6 = defineCapability12(RUN_READ_MODEL6);
|
|
4178
|
+
var RunSessionJournalCap3 = defineCapability12(RUN_SESSION_JOURNAL3);
|
|
4179
|
+
var RunIdentityEnvCap2 = defineCapability12(RUN_IDENTITY_ENV2);
|
|
4180
|
+
var TaskIoCap = defineCapability12(TASK_IO_SERVICE_CAPABILITY);
|
|
4181
|
+
var TaskProjectionCap = defineCapability12(TASK_PROJECTION_CAPABILITY_ID);
|
|
4182
|
+
async function resolveDiscoveryFilter(projectRoot, identityContext) {
|
|
4183
|
+
const identityEnv = await loadCapabilityForRoot6(projectRoot, RunIdentityEnvCap2);
|
|
4184
|
+
return (identityContext && identityEnv ? identityEnv.resolveRigIdentityFilter(identityContext) : null) ?? {};
|
|
4185
|
+
}
|
|
4186
|
+
async function produceRunsBoard(context) {
|
|
4187
|
+
const { projectRoot, identityContext, runs: providedRuns, runClassifications } = context ?? {};
|
|
4188
|
+
if (!projectRoot)
|
|
4189
|
+
return { kind: "runs", runs: [] };
|
|
4190
|
+
const service = (await Promise.resolve().then(() => (init_read_model_service(), exports_read_model_service))).runReadModelService;
|
|
4191
|
+
const runs = providedRuns ?? await service.listRuns({ projectRoot, discoveryFilter: await resolveDiscoveryFilter(projectRoot, identityContext) });
|
|
4192
|
+
const rows = runs.map((run) => {
|
|
4193
|
+
const classification = runClassifications?.get(run.runId) ?? service.classifyRun(run);
|
|
4194
|
+
return {
|
|
4195
|
+
runId: run.runId,
|
|
4196
|
+
status: classification.status,
|
|
4197
|
+
role: classification.role,
|
|
4198
|
+
phase: classification.phase,
|
|
4199
|
+
rank: classification.rank,
|
|
4200
|
+
title: run.title
|
|
4201
|
+
};
|
|
4202
|
+
});
|
|
4203
|
+
return { kind: "runs", runs: rows };
|
|
4204
|
+
}
|
|
4205
|
+
var statsCommand = {
|
|
4206
|
+
id: `${RUN_READ_MODEL_PLUGIN_NAME}:stats`,
|
|
4207
|
+
family: "stats",
|
|
4208
|
+
description: "Inspect run statistics.",
|
|
4209
|
+
projectRequired: true,
|
|
4210
|
+
run: async (context, args) => (await Promise.resolve().then(() => (init_stats_command(), exports_stats_command))).executeStats(context, [...args]),
|
|
4211
|
+
helpDoc: {
|
|
4212
|
+
name: "stats",
|
|
4213
|
+
summary: "Legacy automation-only metrics over old run records; not the OMP live UX.",
|
|
4214
|
+
usage: ["rig stats [show] [--since <7d|30d|ISO date>]"],
|
|
4215
|
+
commands: [
|
|
4216
|
+
{ command: "show [--since <window>]", description: "Legacy automation-only: totals, completion/failure rates, run time, steering, stalls, approvals.", primary: true }
|
|
4217
|
+
],
|
|
4218
|
+
examples: [
|
|
4219
|
+
"rig stats --json # legacy automation only"
|
|
4220
|
+
],
|
|
4221
|
+
next: ["For normal UX, use the OMP Runs screen."]
|
|
4222
|
+
}
|
|
4223
|
+
};
|
|
4224
|
+
var inspectCommand = {
|
|
4225
|
+
id: `${RUN_READ_MODEL_PLUGIN_NAME}:inspect`,
|
|
4226
|
+
family: "inspect",
|
|
4227
|
+
description: "Inspect live/project Rig state.",
|
|
4228
|
+
projectRequired: false,
|
|
4229
|
+
run: async (context, args) => (await Promise.resolve().then(() => (init_inspect_command(), exports_inspect_command))).executeInspect(context, [...args]),
|
|
4230
|
+
helpDoc: {
|
|
4231
|
+
name: "inspect",
|
|
4232
|
+
summary: "Legacy automation-only artifact/log inspection; normal UX is OMP session history.",
|
|
4233
|
+
usage: ["rig inspect <logs|artifact|artifacts|run-logs|runs|failures|graph|diff> [options]"],
|
|
4234
|
+
commands: [
|
|
4235
|
+
{ command: "logs --task <id>", description: "Legacy automation-only: latest old run log for a task.", primary: true },
|
|
4236
|
+
{ command: "run-logs --run <id>", description: "Legacy automation-only: log lines for a specific run.", primary: true },
|
|
4237
|
+
{ command: "runs", description: "Legacy automation-only: list projected runs." },
|
|
4238
|
+
{ command: "artifact --task <id> --file <name>", description: "Legacy automation-only: preview one completion artifact." },
|
|
4239
|
+
{ command: "artifacts --task <id>", description: "Legacy automation-only: list completion artifacts.", primary: true },
|
|
4240
|
+
{ command: "diff --task <id>", description: "Legacy automation-only: list task changed files." },
|
|
4241
|
+
{ command: "failures --task <id>", description: "Legacy automation-only: recorded failures for a task.", primary: true },
|
|
4242
|
+
{ command: "graph [--task <id>]", description: "Legacy automation-only: task dependency graph." }
|
|
4243
|
+
],
|
|
4244
|
+
next: ["For normal UX, use the OMP Runs screen and OMP session history."]
|
|
4245
|
+
}
|
|
4246
|
+
};
|
|
4247
|
+
var runReadModelPlugin = definePlugin2({
|
|
4248
|
+
name: RUN_READ_MODEL_PLUGIN_NAME,
|
|
4249
|
+
version: "0.0.0-alpha.1",
|
|
4250
|
+
effects: { readsFiles: true, contributesCliCommands: true, contributesPanels: true },
|
|
4251
|
+
contributes: {
|
|
4252
|
+
capabilities: [
|
|
4253
|
+
RunReadModelCap6.provide(async () => (await Promise.resolve().then(() => (init_read_model_service(), exports_read_model_service))).runReadModelService, {
|
|
4254
|
+
title: "Run read-model service",
|
|
4255
|
+
description: "List, inspect, summarize, and classify canonical run session projections for operator surfaces."
|
|
4256
|
+
}),
|
|
4257
|
+
RunSessionJournalCap3.provide(async () => (await Promise.resolve().then(() => (init_session_journal(), exports_session_journal))).runSessionJournalCodec, {
|
|
4258
|
+
title: "Run session-journal codec",
|
|
4259
|
+
description: "Fold run-session entries into projections, extract timelines, and parse control sentinels."
|
|
4260
|
+
})
|
|
4261
|
+
],
|
|
4262
|
+
cliCommands: [statsCommand, inspectCommand],
|
|
4263
|
+
panels: [
|
|
4264
|
+
{
|
|
4265
|
+
id: "runs",
|
|
4266
|
+
slot: RIG_COCKPIT_PANEL_SLOT,
|
|
4267
|
+
title: "Runs",
|
|
4268
|
+
description: "Dispatched Rig runs and live OMP collaborative sessions.",
|
|
4269
|
+
produceBoard: produceRunsBoard,
|
|
4270
|
+
createView: async (host) => {
|
|
4271
|
+
const { truncateToWidth, matchesKey } = await import("@oh-my-pi/pi-tui");
|
|
4272
|
+
return (await Promise.resolve().then(() => (init_runs_screen(), exports_runs_screen))).createRunsBoardView(host, { truncateToWidth, matchesKey });
|
|
4273
|
+
},
|
|
4274
|
+
screen: {
|
|
4275
|
+
title: "Runs",
|
|
4276
|
+
navOrder: 2,
|
|
4277
|
+
navLabel: "RUNS",
|
|
4278
|
+
navDescription: "attach to dispatched Rig runs and live OMP collaborative sessions",
|
|
4279
|
+
shortcutKey: "r",
|
|
4280
|
+
shortcutDescription: "Rig runs"
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4283
|
+
]
|
|
4284
|
+
}
|
|
4285
|
+
});
|
|
4286
|
+
function createRunReadModelPlugin() {
|
|
4287
|
+
return runReadModelPlugin;
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
// packages/run-plugin/src/plugin.ts
|
|
4291
|
+
var RUN_PLUGIN_NAME = "@rig/run-plugin";
|
|
4292
|
+
function createRunPlugin() {
|
|
4293
|
+
const parts = [createRunWorkerPlugin(), createRunReadModelPlugin()];
|
|
4294
|
+
return definePlugin3({
|
|
4295
|
+
name: RUN_PLUGIN_NAME,
|
|
4296
|
+
version: "0.0.0-alpha.1",
|
|
4297
|
+
effects: {
|
|
4298
|
+
readsFiles: true,
|
|
4299
|
+
writesFiles: true,
|
|
4300
|
+
spawnsProcesses: true,
|
|
4301
|
+
opensNetwork: true,
|
|
4302
|
+
contributesCliCommands: true,
|
|
4303
|
+
contributesPanels: true
|
|
4304
|
+
},
|
|
4305
|
+
contributes: {
|
|
4306
|
+
capabilities: parts.flatMap((p) => [...p.contributes?.capabilities ?? []]),
|
|
4307
|
+
cliCommands: parts.flatMap((p) => [...p.contributes?.cliCommands ?? []]),
|
|
4308
|
+
panels: parts.flatMap((p) => [...p.contributes?.panels ?? []]),
|
|
4309
|
+
seedEntrypoints: parts.flatMap((p) => [...p.contributes?.seedEntrypoints ?? []]),
|
|
4310
|
+
sessionExtensions: parts.flatMap((p) => [...p.contributes?.sessionExtensions ?? []])
|
|
4311
|
+
}
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4314
|
+
var plugin_default = createRunPlugin;
|
|
4315
|
+
export {
|
|
4316
|
+
plugin_default as default,
|
|
4317
|
+
createRunPlugin,
|
|
4318
|
+
RUN_PLUGIN_NAME
|
|
4319
|
+
};
|