@volter/supercode-orchestrator 0.4.118 → 0.4.119
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/board/dispatch.mjs +37 -12
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -116,11 +116,13 @@ export function observeFleet() {
|
|
|
116
116
|
const list = sc(['message', 'list', '--json']);
|
|
117
117
|
const sessions = list.code === 0 ? json(list.stdout.trim(), null) : null;
|
|
118
118
|
if (!Array.isArray(sessions)) return { ok: false, alive: new Set(), online: new Set(), error: `message list: ${(list.stderr || list.stdout).trim().slice(-300)}` };
|
|
119
|
-
const machines = sc(['teams', 'machines', '
|
|
120
|
-
const
|
|
119
|
+
const machines = sc(['teams', 'machines', 'events', '--json']);
|
|
120
|
+
const presence = machines.code === 0 ? lastJson(machines.stdout) : null;
|
|
121
121
|
const online = new Set([localMachine()]);
|
|
122
|
-
for (const m of
|
|
123
|
-
return { ok: true, sessions, alive: new Set(sessions.map((s) => s.address).filter(Boolean)), online,
|
|
122
|
+
for (const m of presence?.items ?? []) if (m.linked) online.add(normalMachine(m.name));
|
|
123
|
+
return { ok: true, sessions, alive: new Set(sessions.map((s) => s.address).filter(Boolean)), online,
|
|
124
|
+
presenceKnown: presence?.connected === true, machineEvents: presence,
|
|
125
|
+
error: presence?.connected ? null : 'machine event stream unavailable; retaining existing runs until availability is known' };
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
// how the launcher opens each harness a pane lane can run, and how it names the session: Claude Code is told the id
|
|
@@ -247,6 +249,30 @@ function supervise(board, fleet, settings, report, entry, effects) {
|
|
|
247
249
|
}
|
|
248
250
|
};
|
|
249
251
|
const limits = limitsOf(db, board, entry, task, session);
|
|
252
|
+
// Availability is the same retained linked/unlinked feed subscribers hear.
|
|
253
|
+
// A failed stream is unknown, not evidence that every remote machine left.
|
|
254
|
+
if (session?.session_id && session.surface !== 'headless') {
|
|
255
|
+
const panes = session.pane ? observed.get(session.machine) : null;
|
|
256
|
+
const reachable = panes instanceof Set || fleet.online.has(session.machine);
|
|
257
|
+
if (!reachable && !fleet.presenceKnown) return;
|
|
258
|
+
if (!reachable) {
|
|
259
|
+
const next = {...session, offline_since: session.offline_since ?? t};
|
|
260
|
+
setRunSession(db,runRow.id,next);
|
|
261
|
+
observe('machine_offline',{machine:session.machine,seconds:t-next.offline_since},null);
|
|
262
|
+
const age = Number(entry.params.block_age_seconds ?? 3600);
|
|
263
|
+
if (t-Number(next.offline_notified_at ?? next.offline_since)>=age) {
|
|
264
|
+
setRunSession(db,runRow.id,{...currentRun(db,id)?.metadata?.supercode,offline_notified_at:t});
|
|
265
|
+
observe('machine_wait_aged',{machine:session.machine,seconds:t-next.offline_since,reason:`machine ${session.machine} has been unlinked for ${t-next.offline_since}s`},null);
|
|
266
|
+
}
|
|
267
|
+
if (getTask(db,id)?.current_run_id===runRow.id) extendClaim(db,id,{ttl:settings.ttl,reason:'machine_offline'});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (session.offline_since != null) {
|
|
271
|
+
setRunSession(db,runRow.id,{...session,offline_since:null,offline_notified_at:null});
|
|
272
|
+
observe('machine_online',{machine:session.machine,reason:'machine linked again'},null);
|
|
273
|
+
session=currentRun(db,id)?.metadata?.supercode ?? session;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
250
276
|
// Hermes's orphan reconciliation: a running card whose claim bookkeeping is broken, with nothing live behind it
|
|
251
277
|
const paneReading = session?.pane ? observed.get(session.machine) : undefined;
|
|
252
278
|
const paneLive = paneReading instanceof Set ? paneReading.has(session.pane) : null;
|
|
@@ -286,13 +312,6 @@ function supervise(board, fleet, settings, report, entry, effects) {
|
|
|
286
312
|
const young = t - Number(session.launched_at ?? 0) < Number(limits.grace ?? settings.grace);
|
|
287
313
|
const missing = !alive && machineUp && !young;
|
|
288
314
|
const next = { ...session };
|
|
289
|
-
// a machine that is off the fleet: how long, for the workflow's machine_offline (the claim is kept meanwhile)
|
|
290
|
-
if (machineUp) next.offline_since = null; else next.offline_since ??= t;
|
|
291
|
-
if (!machineUp) {
|
|
292
|
-
setRunSession(db, runRow.id, next);
|
|
293
|
-
observe('machine_offline', { machine: session.machine, seconds: t - next.offline_since }, null);
|
|
294
|
-
if (getTask(db, id)?.current_run_id !== runRow.id) return; // the workflow moved it
|
|
295
|
-
}
|
|
296
315
|
if (alive) { next.seen_at ??= t; next.missing_since = null; }
|
|
297
316
|
if (missing) next.missing_since ??= t;
|
|
298
317
|
if (missing && t - next.missing_since >= Number(limits.grace ?? settings.grace)) {
|
|
@@ -618,7 +637,7 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
618
637
|
if (!(entry.workflow.statuses[status]?.on?.session_idle || entry.workflow.on?.session_idle || entry.workflow.run?.on?.session_idle)) return;
|
|
619
638
|
const runRow = currentRun(db, id);
|
|
620
639
|
const session = runRow?.metadata?.supercode;
|
|
621
|
-
if (!session?.session_id || session.surface !== 'pane') return;
|
|
640
|
+
if (!session?.session_id || session.surface !== 'pane' || session.offline_since != null) return;
|
|
622
641
|
const live = fleet.sessions.find((x) => x.address?.endsWith(`:${session.session_id}`));
|
|
623
642
|
const observedAt = now();
|
|
624
643
|
const lastAt = Math.floor(Number(live?.last_message_at_ms) / 1000);
|
|
@@ -727,6 +746,7 @@ function launcherCheck(db, task, lane, fleet) {
|
|
|
727
746
|
if (!task.machine) return 'the card names no machine; a pane session runs on the machine its card names';
|
|
728
747
|
const machine = normalMachine(task.machine);
|
|
729
748
|
if (!fleet.ok) return `supercode could not read the fleet: ${fleet.error}`;
|
|
749
|
+
if (machine !== localMachine() && !fleet.presenceKnown) return 'machine event stream unavailable; launch waits for a current availability reading';
|
|
730
750
|
if (!fleet.online.has(machine)) return `machine ${machine} is offline; the card starts when it is back`;
|
|
731
751
|
// a card on this machine gets its workspace made here (scratch, dir or worktree, as Hermes makes it); another
|
|
732
752
|
// machine's card names its directory there
|
|
@@ -1438,6 +1458,7 @@ function readMailbox(board, address, report) {
|
|
|
1438
1458
|
try { renameSync(path, join(box, 'cur', name)); } catch { continue; } // another reader took it
|
|
1439
1459
|
// an answer to a question the board asked is what a card waits on (read by the card's workflow), not a verb
|
|
1440
1460
|
if (envelope.in_reply_to) continue;
|
|
1461
|
+
if (envelope.from === `sc:${localMachine()}:operator:teams-events` && json(envelope.body, {})?.machine_event) { report.mailed.push([envelope.from, 'machine_event']); continue; }
|
|
1441
1462
|
const argv = json(envelope.body, {})?.workflow;
|
|
1442
1463
|
let answer;
|
|
1443
1464
|
if (!Array.isArray(argv) || !argv.length || NOT_BY_MAIL.has(argv[0])) answer = `not applied: a mailed card update is one \`supercode workflow\` verb other than ${[...NOT_BY_MAIL].join(', ')}`;
|
|
@@ -1548,6 +1569,10 @@ export async function tick({ root, lanes, settings, only = null, slugs = null, d
|
|
|
1548
1569
|
// where the tick's time goes, phase by phase (ms), printed by `serve --verbose` and in `--json`
|
|
1549
1570
|
let mark = Date.now();
|
|
1550
1571
|
const phase = (name) => { const t = Date.now(); report.ms[name] = t - mark; mark = t; };
|
|
1572
|
+
if (!fleet.machineEvents?.subscriptions?.includes(boardAddress(board.label))) {
|
|
1573
|
+
const subscription = sc(['teams','machines','subscribe',boardAddress(board.label),'--json']);
|
|
1574
|
+
if (subscription.code !== 0) report.failed.push(['machine_events',(subscription.stderr || subscription.stdout).trim().slice(-200)]);
|
|
1575
|
+
}
|
|
1551
1576
|
readMailbox(board, boardAddress(board.label), report); phase('mailbox');
|
|
1552
1577
|
await refreshRelationAnswers(board); phase('answers');
|
|
1553
1578
|
const effects = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.119",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|