@h-rig/run-worker 0.0.6-alpha.157 → 0.0.6-alpha.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/autohost.d.ts +8 -10
- package/dist/src/autohost.js +683 -95
- package/dist/src/constants.d.ts +0 -1
- package/dist/src/constants.js +0 -2
- package/dist/src/extension.js +683 -95
- package/dist/src/host-kernel.d.ts +22 -0
- package/dist/src/host-kernel.js +78 -0
- package/dist/src/host.d.ts +2 -0
- package/dist/src/host.js +419 -0
- package/dist/src/index.d.ts +0 -6
- package/dist/src/index.js +1913 -133
- package/dist/src/local-run-changes.d.ts +3 -0
- package/dist/src/local-run-changes.js +65 -0
- package/dist/src/notifications.js +13 -5
- package/dist/src/notify-cap.d.ts +11 -0
- package/dist/src/notify-cap.js +13 -0
- package/dist/src/panel-plugin.js +3 -7
- package/dist/src/plugin.d.ts +0 -11
- package/dist/src/plugin.js +1910 -101
- package/dist/src/{runs → read-model-backend}/control.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/control.js +361 -38
- package/dist/src/{runs → read-model-backend}/diagnostics.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/diagnostics.js +1 -3
- package/dist/src/{runs → read-model-backend}/guard.js +2 -3
- package/dist/src/{runs → read-model-backend}/inbox.js +366 -36
- package/dist/src/{runs → read-model-backend}/index.js +552 -223
- package/dist/src/{runs → read-model-backend}/inspect.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/inspect.js +350 -34
- package/dist/src/{runs → read-model-backend}/projection.d.ts +21 -7
- package/dist/src/{runs → read-model-backend}/projection.js +349 -31
- package/dist/src/{runs → read-model-backend}/run-status.d.ts +6 -3
- package/dist/src/{runs → read-model-backend}/run-status.js +53 -33
- package/dist/src/{runs → read-model-backend}/stats.d.ts +0 -1
- package/dist/src/{runs → read-model-backend}/stats.js +373 -58
- package/dist/src/read-model-service.d.ts +2 -0
- package/dist/src/read-model-service.js +1433 -0
- package/dist/src/session-journal.d.ts +60 -0
- package/dist/src/session-journal.js +471 -0
- package/dist/src/stall.d.ts +8 -3
- package/dist/src/stall.js +0 -1
- package/dist/src/utils.js +282 -3
- package/package.json +9 -12
- package/dist/src/journal.d.ts +0 -33
- package/dist/src/journal.js +0 -31
- /package/dist/src/{runs → read-model-backend}/guard.d.ts +0 -0
- /package/dist/src/{runs → read-model-backend}/inbox.d.ts +0 -0
- /package/dist/src/{runs → read-model-backend}/index.d.ts +0 -0
|
@@ -16,7 +16,6 @@ export interface DeliverRemoteControlDeps {
|
|
|
16
16
|
}
|
|
17
17
|
export type RemoteControlTarget = Pick<RunRecord, "runId" | "joinLink">;
|
|
18
18
|
export declare function collabControlFrames(runId: string, control: RunControl): readonly CollabFrame[];
|
|
19
|
-
export declare function collabControlFrame(runId: string, control: RunControl): CollabFrame;
|
|
20
19
|
export declare function deliverRemoteRunControl(projectRoot: string, target: RemoteControlTarget | string, control: RunControl, deps?: DeliverRemoteControlDeps): Promise<void>;
|
|
21
20
|
export declare function deliverRunControl(projectRoot: string, target: RunRecord | string, control: RunControl, deps?: DeliverRemoteControlDeps & {
|
|
22
21
|
readonly getRun?: (projectRoot: string, runId: string) => Promise<RunRecord | null>;
|
|
@@ -1,20 +1,328 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
|
-
// packages/run-worker/src/
|
|
5
|
-
import {
|
|
4
|
+
// packages/run-worker/src/read-model-backend/control.ts
|
|
5
|
+
import { RIG_CONTROL_SENTINEL_END as RIG_CONTROL_SENTINEL_END2, RIG_PAUSE_SENTINEL as RIG_PAUSE_SENTINEL2, RIG_RESUME_SENTINEL as RIG_RESUME_SENTINEL2, RIG_STOP_SENTINEL as RIG_STOP_SENTINEL2, RIG_STOP_SENTINEL_END as RIG_STOP_SENTINEL_END2 } from "@rig/contracts";
|
|
6
6
|
|
|
7
|
-
// packages/run-worker/src/
|
|
7
|
+
// packages/run-worker/src/read-model-backend/projection.ts
|
|
8
8
|
import { isAbsolute, relative, resolve } from "path";
|
|
9
|
-
import {
|
|
9
|
+
import { RUN_DISCOVERY } from "@rig/contracts";
|
|
10
|
+
|
|
11
|
+
// packages/run-worker/src/session-journal.ts
|
|
12
|
+
import { Schema } from "effect";
|
|
10
13
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
CUSTOM_TYPE_FOR,
|
|
15
|
+
RIG_CONTROL_SENTINEL_END,
|
|
16
|
+
RIG_INBOX_RESOLUTION_SENTINEL,
|
|
17
|
+
RIG_PAUSE_SENTINEL,
|
|
18
|
+
RIG_RESUME_SENTINEL,
|
|
19
|
+
RIG_STOP_SENTINEL,
|
|
20
|
+
RIG_STOP_SENTINEL_END,
|
|
21
|
+
RIG_WORKFLOW_STATUS_CHANGED,
|
|
22
|
+
RunJournalEvent,
|
|
23
|
+
TYPE_FOR_CUSTOM
|
|
14
24
|
} from "@rig/contracts";
|
|
15
|
-
|
|
25
|
+
var decodeRunJournalEvent = Schema.decodeUnknownSync(RunJournalEvent);
|
|
26
|
+
var RUN_STATUS_TRANSITIONS = {
|
|
27
|
+
created: ["queued", "preparing", "running", "failed", "stopped"],
|
|
28
|
+
queued: ["preparing", "running", "failed", "stopped"],
|
|
29
|
+
preparing: ["queued", "running", "needs-attention", "failed", "stopped"],
|
|
30
|
+
running: [
|
|
31
|
+
"queued",
|
|
32
|
+
"waiting-approval",
|
|
33
|
+
"waiting-user-input",
|
|
34
|
+
"paused",
|
|
35
|
+
"validating",
|
|
36
|
+
"reviewing",
|
|
37
|
+
"closing-out",
|
|
38
|
+
"needs-attention",
|
|
39
|
+
"completed",
|
|
40
|
+
"failed",
|
|
41
|
+
"stopped"
|
|
42
|
+
],
|
|
43
|
+
"waiting-approval": ["running", "needs-attention", "failed", "stopped"],
|
|
44
|
+
"waiting-user-input": ["running", "needs-attention", "failed", "stopped"],
|
|
45
|
+
paused: ["running", "failed", "stopped"],
|
|
46
|
+
validating: ["running", "reviewing", "closing-out", "needs-attention", "completed", "failed", "stopped"],
|
|
47
|
+
reviewing: ["running", "validating", "closing-out", "needs-attention", "completed", "failed", "stopped"],
|
|
48
|
+
"closing-out": ["running", "needs-attention", "completed", "failed", "stopped"],
|
|
49
|
+
"needs-attention": ["queued", "preparing", "running", "closing-out", "completed", "failed", "stopped"],
|
|
50
|
+
completed: [],
|
|
51
|
+
failed: ["queued", "preparing", "running", "closing-out"],
|
|
52
|
+
stopped: ["queued", "preparing", "running", "closing-out"]
|
|
53
|
+
};
|
|
54
|
+
var TERMINAL_RUN_STATUSES = ["completed", "failed", "stopped"];
|
|
55
|
+
function isTerminalRunStatus(status) {
|
|
56
|
+
return TERMINAL_RUN_STATUSES.includes(status);
|
|
57
|
+
}
|
|
58
|
+
function canTransitionRunStatus(from, to) {
|
|
59
|
+
if (from === null)
|
|
60
|
+
return true;
|
|
61
|
+
if (from === to)
|
|
62
|
+
return true;
|
|
63
|
+
return RUN_STATUS_TRANSITIONS[from].includes(to);
|
|
64
|
+
}
|
|
65
|
+
function assertRunStatusTransition(from, to) {
|
|
66
|
+
if (!canTransitionRunStatus(from, to)) {
|
|
67
|
+
throw new Error(`Illegal run status transition: ${from ?? "(none)"} -> ${to}. ` + `Allowed from ${from ?? "(none)"}: ${from ? RUN_STATUS_TRANSITIONS[from].join(", ") : "(any)"}.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function reduceRunJournal(events, runId = null) {
|
|
71
|
+
let record = {};
|
|
72
|
+
let status = null;
|
|
73
|
+
const statusHistory = [];
|
|
74
|
+
const pendingApprovals = new Map;
|
|
75
|
+
const resolvedApprovals = [];
|
|
76
|
+
const pendingUserInputs = new Map;
|
|
77
|
+
const resolvedUserInputs = [];
|
|
78
|
+
const closeoutPhases = [];
|
|
79
|
+
let resolvedPipeline = null;
|
|
80
|
+
const stageOutcomes = [];
|
|
81
|
+
const anomalies = [];
|
|
82
|
+
let steeringCount = 0;
|
|
83
|
+
let stallCount = 0;
|
|
84
|
+
let lastSeq = 0;
|
|
85
|
+
let lastEventAt = null;
|
|
86
|
+
const projectedRunId = runId ?? events[0]?.runId ?? null;
|
|
87
|
+
for (const event of events) {
|
|
88
|
+
lastSeq = event.seq;
|
|
89
|
+
lastEventAt = event.at;
|
|
90
|
+
switch (event.type) {
|
|
91
|
+
case "status-changed": {
|
|
92
|
+
if (!canTransitionRunStatus(status, event.to)) {
|
|
93
|
+
anomalies.push({ seq: event.seq, kind: "illegal-transition", detail: `${status ?? "(none)"} -> ${event.to}` });
|
|
94
|
+
}
|
|
95
|
+
statusHistory.push({ seq: event.seq, at: event.at, from: status, to: event.to, reason: event.reason ?? null });
|
|
96
|
+
const wasTerminal = status !== null && isTerminalRunStatus(status);
|
|
97
|
+
status = event.to;
|
|
98
|
+
record = { ...record, updatedAt: event.at };
|
|
99
|
+
if (isTerminalRunStatus(event.to) && !record.completedAt)
|
|
100
|
+
record = { ...record, completedAt: event.at };
|
|
101
|
+
if (!isTerminalRunStatus(event.to) && wasTerminal)
|
|
102
|
+
record = { ...record, completedAt: null };
|
|
103
|
+
if (event.to === "running" && !record.startedAt)
|
|
104
|
+
record = { ...record, startedAt: event.at };
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case "record-patch": {
|
|
108
|
+
const next = { ...record };
|
|
109
|
+
for (const [key, value] of Object.entries(event.patch)) {
|
|
110
|
+
if (value !== undefined)
|
|
111
|
+
next[key] = value;
|
|
112
|
+
}
|
|
113
|
+
next.updatedAt = event.patch.updatedAt ?? event.at;
|
|
114
|
+
record = next;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "approval-requested": {
|
|
118
|
+
pendingApprovals.set(event.requestId, {
|
|
119
|
+
requestId: event.requestId,
|
|
120
|
+
requestKind: event.requestKind,
|
|
121
|
+
actionId: event.actionId ?? null,
|
|
122
|
+
payload: event.payload,
|
|
123
|
+
requestedAt: event.at
|
|
124
|
+
});
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "approval-resolved": {
|
|
128
|
+
const pending = pendingApprovals.get(event.requestId);
|
|
129
|
+
if (!pending) {
|
|
130
|
+
const alreadyResolved = resolvedApprovals.some((entry) => entry.requestId === event.requestId);
|
|
131
|
+
anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `approval ${event.requestId}` });
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
pendingApprovals.delete(event.requestId);
|
|
135
|
+
resolvedApprovals.push({ ...pending, decision: event.decision, note: event.note ?? null, actor: event.actor, resolvedAt: event.at });
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "input-requested": {
|
|
139
|
+
pendingUserInputs.set(event.requestId, {
|
|
140
|
+
requestId: event.requestId,
|
|
141
|
+
requestKind: "user-input",
|
|
142
|
+
actionId: null,
|
|
143
|
+
payload: event.payload,
|
|
144
|
+
requestedAt: event.at
|
|
145
|
+
});
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case "input-resolved": {
|
|
149
|
+
const pending = pendingUserInputs.get(event.requestId);
|
|
150
|
+
if (!pending) {
|
|
151
|
+
const alreadyResolved = resolvedUserInputs.some((entry) => entry.requestId === event.requestId);
|
|
152
|
+
anomalies.push({ seq: event.seq, kind: alreadyResolved ? "duplicate-resolution" : "unknown-request", detail: `user-input ${event.requestId}` });
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
pendingUserInputs.delete(event.requestId);
|
|
156
|
+
resolvedUserInputs.push({ ...pending, answers: event.answers, actor: event.actor, resolvedAt: event.at });
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case "steering":
|
|
160
|
+
steeringCount += 1;
|
|
161
|
+
break;
|
|
162
|
+
case "adopted":
|
|
163
|
+
record = { ...record, pid: event.pid, updatedAt: event.at };
|
|
164
|
+
break;
|
|
165
|
+
case "stall-detected":
|
|
166
|
+
stallCount += 1;
|
|
167
|
+
break;
|
|
168
|
+
case "closeout-phase":
|
|
169
|
+
closeoutPhases.push({ seq: event.seq, at: event.at, phase: event.phase, outcome: event.outcome, detail: event.detail ?? null });
|
|
170
|
+
break;
|
|
171
|
+
case "pipeline-resolved":
|
|
172
|
+
resolvedPipeline = event.pipeline;
|
|
173
|
+
break;
|
|
174
|
+
case "stage-outcome":
|
|
175
|
+
stageOutcomes.push({ seq: event.seq, at: event.at, outcome: event.outcome });
|
|
176
|
+
break;
|
|
177
|
+
case "timeline-entry":
|
|
178
|
+
case "log-entry":
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
runId: projectedRunId,
|
|
184
|
+
record,
|
|
185
|
+
status,
|
|
186
|
+
statusHistory,
|
|
187
|
+
pendingApprovals: [...pendingApprovals.values()],
|
|
188
|
+
resolvedApprovals,
|
|
189
|
+
pendingUserInputs: [...pendingUserInputs.values()],
|
|
190
|
+
resolvedUserInputs,
|
|
191
|
+
steeringCount,
|
|
192
|
+
stallCount,
|
|
193
|
+
closeoutPhases,
|
|
194
|
+
resolvedPipeline,
|
|
195
|
+
stageOutcomes,
|
|
196
|
+
lastSeq,
|
|
197
|
+
lastEventAt,
|
|
198
|
+
anomalies
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function isRunSessionCustomType(customType) {
|
|
202
|
+
return customType !== undefined && Object.hasOwn(TYPE_FOR_CUSTOM, customType);
|
|
203
|
+
}
|
|
204
|
+
function foldRunSessionEntries(entries, runId) {
|
|
205
|
+
const events = [];
|
|
206
|
+
entries.forEach((entry, index) => {
|
|
207
|
+
if (entry.type !== "custom" || !isRunSessionCustomType(entry.customType))
|
|
208
|
+
return;
|
|
209
|
+
const data = entry.data !== null && typeof entry.data === "object" ? entry.data : {};
|
|
210
|
+
const stamped = {
|
|
211
|
+
v: 1,
|
|
212
|
+
seq: index + 1,
|
|
213
|
+
at: typeof data.at === "string" ? data.at : new Date(0).toISOString(),
|
|
214
|
+
runId,
|
|
215
|
+
...data,
|
|
216
|
+
type: TYPE_FOR_CUSTOM[entry.customType]
|
|
217
|
+
};
|
|
218
|
+
try {
|
|
219
|
+
events.push(decodeRunJournalEvent(stamped));
|
|
220
|
+
} catch {}
|
|
221
|
+
});
|
|
222
|
+
return reduceRunJournal(events, runId);
|
|
223
|
+
}
|
|
224
|
+
function isRecord(value) {
|
|
225
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
226
|
+
}
|
|
227
|
+
function timelineEntryFromCustomEntry(entry) {
|
|
228
|
+
if (entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
|
|
229
|
+
return null;
|
|
230
|
+
const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
|
|
231
|
+
const type = typeof payload.type === "string" ? payload.type : "timeline";
|
|
232
|
+
const stage = typeof payload.stage === "string" ? payload.stage : typeof payload.name === "string" ? payload.name : null;
|
|
233
|
+
const status = typeof payload.status === "string" ? payload.status : typeof payload.outcome === "string" ? payload.outcome : null;
|
|
234
|
+
const detail = typeof payload.detail === "string" ? payload.detail : typeof payload.message === "string" ? payload.message : null;
|
|
235
|
+
const at = typeof entry.data.at === "string" ? entry.data.at : typeof payload.at === "string" ? payload.at : null;
|
|
236
|
+
return { at, type, stage, status, detail };
|
|
237
|
+
}
|
|
238
|
+
function timelineEntriesFromCustomEntries(entries) {
|
|
239
|
+
return entries.flatMap((entry) => {
|
|
240
|
+
const timelineEntry = timelineEntryFromCustomEntry(entry);
|
|
241
|
+
return timelineEntry ? [timelineEntry] : [];
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function asCustomEntries(entries) {
|
|
245
|
+
return entries.filter((entry) => entry.type === "custom");
|
|
246
|
+
}
|
|
16
247
|
|
|
17
|
-
|
|
248
|
+
class RunSessionJournal {
|
|
249
|
+
sm;
|
|
250
|
+
runId;
|
|
251
|
+
constructor(sm, runId) {
|
|
252
|
+
this.sm = sm;
|
|
253
|
+
this.runId = runId;
|
|
254
|
+
}
|
|
255
|
+
#append(init) {
|
|
256
|
+
this.sm.appendCustomEntry(CUSTOM_TYPE_FOR[init.type], { ...init, at: new Date().toISOString() });
|
|
257
|
+
}
|
|
258
|
+
appendStatus(to, opts = {}) {
|
|
259
|
+
const from = foldRunSessionEntries(asCustomEntries(this.sm.getEntries()), this.runId).status;
|
|
260
|
+
if (!opts.force)
|
|
261
|
+
assertRunStatusTransition(from, to);
|
|
262
|
+
if (opts.errorText !== undefined)
|
|
263
|
+
this.#append({ type: "record-patch", patch: { errorText: opts.errorText } });
|
|
264
|
+
this.#append({
|
|
265
|
+
type: "status-changed",
|
|
266
|
+
from,
|
|
267
|
+
to,
|
|
268
|
+
...opts.reason !== undefined ? { reason: opts.reason } : {},
|
|
269
|
+
...opts.actor !== undefined ? { actor: opts.actor } : {}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
appendTimeline(payload) {
|
|
273
|
+
this.#append({ type: "timeline-entry", payload });
|
|
274
|
+
}
|
|
275
|
+
appendCloseoutPhase(input) {
|
|
276
|
+
this.#append({
|
|
277
|
+
type: "closeout-phase",
|
|
278
|
+
phase: input.phase,
|
|
279
|
+
outcome: input.outcome,
|
|
280
|
+
...input.detail !== undefined ? { detail: input.detail } : {}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
appendApprovalResolved(input) {
|
|
284
|
+
this.#append({
|
|
285
|
+
type: "approval-resolved",
|
|
286
|
+
requestId: input.requestId,
|
|
287
|
+
decision: input.decision,
|
|
288
|
+
actor: input.actor,
|
|
289
|
+
...input.note !== undefined ? { note: input.note } : {}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
appendInputResolved(input) {
|
|
293
|
+
this.#append({ type: "input-resolved", requestId: input.requestId, answers: input.answers, actor: input.actor });
|
|
294
|
+
}
|
|
295
|
+
appendStall(input) {
|
|
296
|
+
this.#append({ type: "stall-detected", detail: input.detail });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// packages/run-worker/src/read-model-backend/projection.ts
|
|
301
|
+
import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
302
|
+
import { defineCapability } from "@rig/core/capability";
|
|
303
|
+
|
|
304
|
+
// packages/run-worker/src/read-model-backend/run-status.ts
|
|
305
|
+
import { OPERATOR_INACTIVE_RUN_STATUSES } from "@rig/contracts";
|
|
306
|
+
var TERMINAL_RUN_STATUSES2 = ["completed", "failed", "stopped"];
|
|
307
|
+
var ACTIVE_RUN_STATUSES = [
|
|
308
|
+
"created",
|
|
309
|
+
"queued",
|
|
310
|
+
"preparing",
|
|
311
|
+
"running",
|
|
312
|
+
"waiting-approval",
|
|
313
|
+
"waiting-user-input",
|
|
314
|
+
"paused",
|
|
315
|
+
"validating",
|
|
316
|
+
"reviewing",
|
|
317
|
+
"closing-out",
|
|
318
|
+
"needs-attention"
|
|
319
|
+
];
|
|
320
|
+
var KNOWN_RUN_STATUS = Object.fromEntries([...ACTIVE_RUN_STATUSES, ...TERMINAL_RUN_STATUSES2].map((status) => [status, true]));
|
|
321
|
+
function asRunStatus(status) {
|
|
322
|
+
return KNOWN_RUN_STATUS[status] ? status : null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// packages/run-worker/src/read-model-backend/diagnostics.ts
|
|
18
326
|
function normalizeString(value) {
|
|
19
327
|
if (typeof value !== "string")
|
|
20
328
|
return null;
|
|
@@ -62,7 +370,27 @@ function summarizeRunError(projection) {
|
|
|
62
370
|
return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
|
|
63
371
|
}
|
|
64
372
|
|
|
65
|
-
// packages/run-worker/src/
|
|
373
|
+
// packages/run-worker/src/read-model-backend/projection.ts
|
|
374
|
+
var RunDiscoveryCap = defineCapability(RUN_DISCOVERY);
|
|
375
|
+
function registryEntryFromCollab(collab) {
|
|
376
|
+
const record = collab;
|
|
377
|
+
return {
|
|
378
|
+
roomId: collab.sessionId,
|
|
379
|
+
title: collab.title,
|
|
380
|
+
status: record.registryStatus ?? (collab.stale ? "stale" : "running"),
|
|
381
|
+
startedAt: collab.startedAt ?? null,
|
|
382
|
+
heartbeatAt: collab.updatedAt ?? null,
|
|
383
|
+
sessionPath: collab.sessionPath ?? null,
|
|
384
|
+
cwd: collab.cwd ?? null,
|
|
385
|
+
joinLink: collab.joinLink ?? null,
|
|
386
|
+
webLink: collab.webLink ?? null,
|
|
387
|
+
relayUrl: collab.relayUrl ?? null,
|
|
388
|
+
stale: collab.stale,
|
|
389
|
+
repo: collab.selectedRepo ?? null,
|
|
390
|
+
...collab.pid === undefined ? {} : { pid: collab.pid },
|
|
391
|
+
projection: record.registryProjection ?? null
|
|
392
|
+
};
|
|
393
|
+
}
|
|
66
394
|
var EMPTY_PROJECTION = foldRunSessionEntries([], "");
|
|
67
395
|
var DISCOVERY_DIAGNOSTIC_RUN_ID = "__registry_discovery_error__";
|
|
68
396
|
function stringOrNull(value) {
|
|
@@ -79,22 +407,7 @@ function registryStatusAsRunStatus(status) {
|
|
|
79
407
|
return "waiting-user-input";
|
|
80
408
|
if (status === "starting")
|
|
81
409
|
return "preparing";
|
|
82
|
-
return typeof status === "string"
|
|
83
|
-
"created",
|
|
84
|
-
"queued",
|
|
85
|
-
"preparing",
|
|
86
|
-
"running",
|
|
87
|
-
"waiting-approval",
|
|
88
|
-
"waiting-user-input",
|
|
89
|
-
"paused",
|
|
90
|
-
"validating",
|
|
91
|
-
"reviewing",
|
|
92
|
-
"closing-out",
|
|
93
|
-
"needs-attention",
|
|
94
|
-
"completed",
|
|
95
|
-
"failed",
|
|
96
|
-
"stopped"
|
|
97
|
-
].includes(status) ? status : null;
|
|
410
|
+
return typeof status === "string" ? asRunStatus(status) : null;
|
|
98
411
|
}
|
|
99
412
|
function payloadString(payload, keys) {
|
|
100
413
|
if (!payload || typeof payload !== "object")
|
|
@@ -117,12 +430,14 @@ function payloadOptions(payload) {
|
|
|
117
430
|
}
|
|
118
431
|
function inboxRequest(request, kind) {
|
|
119
432
|
const fallback = kind === "approval" ? "Approval requested" : "Input requested";
|
|
433
|
+
const body = payloadString(request.payload, ["body", "description", "detail", "details"]);
|
|
434
|
+
const options = payloadOptions(request.payload);
|
|
120
435
|
return {
|
|
121
436
|
requestId: request.requestId,
|
|
122
437
|
kind,
|
|
123
438
|
title: payloadString(request.payload, ["title", "message", "reason", "prompt", "summary"]) ?? fallback,
|
|
124
|
-
body
|
|
125
|
-
...
|
|
439
|
+
...body !== undefined ? { body } : {},
|
|
440
|
+
...options ? { options } : {},
|
|
126
441
|
requestedAt: request.requestedAt ?? null,
|
|
127
442
|
source: "run"
|
|
128
443
|
};
|
|
@@ -231,8 +546,8 @@ function runRecordFromRegistryEntry(projectRoot, entry) {
|
|
|
231
546
|
projection: folded
|
|
232
547
|
};
|
|
233
548
|
}
|
|
234
|
-
function
|
|
235
|
-
return sortByRecency(
|
|
549
|
+
function runRecordsFromCollab(projectRoot, collabs) {
|
|
550
|
+
return sortByRecency(collabs.map((collab) => runRecordFromRegistryEntry(projectRoot, registryEntryFromCollab(collab))).filter((record) => record !== null));
|
|
236
551
|
}
|
|
237
552
|
function sortByRecency(records) {
|
|
238
553
|
return [...records].sort((a, b) => {
|
|
@@ -280,8 +595,11 @@ function discoveryDiagnosticRunRecord(projectRoot, error) {
|
|
|
280
595
|
}
|
|
281
596
|
async function listRunProjections(projectRoot, filter = {}) {
|
|
282
597
|
try {
|
|
283
|
-
const
|
|
284
|
-
|
|
598
|
+
const discovery = await loadCapabilityForRoot(projectRoot, RunDiscoveryCap);
|
|
599
|
+
if (!discovery)
|
|
600
|
+
return [discoveryDiagnosticRunRecord(projectRoot, "run discovery capability unavailable")];
|
|
601
|
+
const collabs = await discovery.listActiveRunCollab(projectRoot, filter);
|
|
602
|
+
return runRecordsFromCollab(projectRoot, collabs);
|
|
285
603
|
} catch (error) {
|
|
286
604
|
return [discoveryDiagnosticRunRecord(projectRoot, error)];
|
|
287
605
|
}
|
|
@@ -308,7 +626,16 @@ async function getRunProjection(projectRoot, runId, filter = {}) {
|
|
|
308
626
|
}
|
|
309
627
|
var getRun = getRunProjection;
|
|
310
628
|
|
|
311
|
-
// packages/run-worker/src/
|
|
629
|
+
// packages/run-worker/src/read-model-backend/control.ts
|
|
630
|
+
function buildPauseSentinel(runId) {
|
|
631
|
+
return `${RIG_PAUSE_SENTINEL2} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END2}`;
|
|
632
|
+
}
|
|
633
|
+
function buildResumeSentinel(runId) {
|
|
634
|
+
return `${RIG_RESUME_SENTINEL2} runId=${runId} requestedBy=operator ${RIG_CONTROL_SENTINEL_END2}`;
|
|
635
|
+
}
|
|
636
|
+
function buildStopSentinel(runId, reason) {
|
|
637
|
+
return `${RIG_STOP_SENTINEL2} runId=${runId} reason=${reason} ${RIG_STOP_SENTINEL_END2}`;
|
|
638
|
+
}
|
|
312
639
|
async function loadCollabControlDeps() {
|
|
313
640
|
const [{ importRoomKey, seal }, { COLLAB_PROTO, packEnvelope, parseCollabLink }] = await Promise.all([
|
|
314
641
|
import("@oh-my-pi/pi-coding-agent/collab/crypto"),
|
|
@@ -337,9 +664,6 @@ Stop this run and do not continue after the interruption settles.` },
|
|
|
337
664
|
];
|
|
338
665
|
}
|
|
339
666
|
}
|
|
340
|
-
function collabControlFrame(runId, control) {
|
|
341
|
-
return collabControlFrames(runId, control)[0];
|
|
342
|
-
}
|
|
343
667
|
async function sendSealedCollabFrames(joinLink, frames, deps = {}) {
|
|
344
668
|
const { importRoomKey, seal, COLLAB_PROTO, packEnvelope, parseCollabLink } = await loadCollabControlDeps();
|
|
345
669
|
const parsed = parseCollabLink(joinLink);
|
|
@@ -393,6 +717,5 @@ async function deliverRunControl(projectRoot, target, control, deps = {}) {
|
|
|
393
717
|
export {
|
|
394
718
|
deliverRunControl,
|
|
395
719
|
deliverRemoteRunControl,
|
|
396
|
-
collabControlFrames
|
|
397
|
-
collabControlFrame
|
|
720
|
+
collabControlFrames
|
|
398
721
|
};
|
|
@@ -7,4 +7,3 @@ import type { RunJournalProjection } from "@rig/contracts";
|
|
|
7
7
|
* status reasons, closeout details, and reducer anomalies.
|
|
8
8
|
*/
|
|
9
9
|
export declare function summarizeRunError(projection: RunJournalProjection): string | null;
|
|
10
|
-
export declare const summarizeUsefulRunError: typeof summarizeRunError;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// packages/run-worker/src/
|
|
2
|
+
// packages/run-worker/src/read-model-backend/diagnostics.ts
|
|
3
3
|
function normalizeString(value) {
|
|
4
4
|
if (typeof value !== "string")
|
|
5
5
|
return null;
|
|
@@ -46,8 +46,6 @@ function summarizeRunError(projection) {
|
|
|
46
46
|
const nonGeneric = candidates.filter((candidate) => !isGenericRunFailure(candidate));
|
|
47
47
|
return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
|
|
48
48
|
}
|
|
49
|
-
var summarizeUsefulRunError = summarizeRunError;
|
|
50
49
|
export {
|
|
51
|
-
summarizeUsefulRunError,
|
|
52
50
|
summarizeRunError
|
|
53
51
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// packages/run-worker/src/
|
|
3
|
-
|
|
4
|
-
var TERMINAL = new Set(TERMINAL_RUN_STATUSES);
|
|
2
|
+
// packages/run-worker/src/read-model-backend/guard.ts
|
|
3
|
+
var TERMINAL = new Set(["completed", "failed", "stopped"]);
|
|
5
4
|
function runBlocksNewRunForTask(run) {
|
|
6
5
|
return run.live && !run.stale && !TERMINAL.has(run.status) && run.status !== "needs-attention" && run.status !== "needs_attention";
|
|
7
6
|
}
|