@volter/supercode-orchestrator 0.5.59 → 0.5.61
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 +26 -24
- package/board/workflow.mjs +6 -3
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -192,7 +192,7 @@ function unpublishedAlive(session) {
|
|
|
192
192
|
if (!session.machine) return null;
|
|
193
193
|
if (!session.pane) {
|
|
194
194
|
if (!session.session_id) return null;
|
|
195
|
-
try { return nativeReading(nativeSessions(session.machine, session.harness ?? 'claude-code'), session).alive; } catch { return null; }
|
|
195
|
+
try { return nativeReading(nativeSessions(session.machine, session.harness ?? 'claude-code', session.session_id), session).alive; } catch { return null; }
|
|
196
196
|
}
|
|
197
197
|
let entry = paneLists.get(session.machine);
|
|
198
198
|
if (!entry || Date.now() - entry.at > READING_MS) {
|
|
@@ -205,8 +205,9 @@ function unpublishedAlive(session) {
|
|
|
205
205
|
return entry.panes ? entry.panes.some((p) => p.pane === session.pane && !p.dead) : null;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
/** Native activity comes from the machine's harness discovery, not catalogue age
|
|
209
|
-
*
|
|
208
|
+
/** Native activity comes from the machine's harness discovery, not catalogue age: each session the round reads is
|
|
209
|
+
* discovered by its id (about 0.2 s), once per round (tick clears them). Listing the machine's every session to find
|
|
210
|
+
* them (up to 2000, 1-6 s each) cost more than the sessions a board runs. */
|
|
210
211
|
const discoveries = new Map();
|
|
211
212
|
// A machine's pane list and harness discovery are read before a board write opens, never inside one: a door that
|
|
212
213
|
// takes seconds would hold the board's write lock past a worker's own command's busy wait. A write that needs a
|
|
@@ -217,25 +218,26 @@ const txRead = (board, fn) => { readingsOnly += 1; try { return tx(board, fn); }
|
|
|
217
218
|
/** Take the readings a board write about `session` will need, before the write. */
|
|
218
219
|
function readBefore(fleet, session) {
|
|
219
220
|
if (!session?.session_id || !session.machine || session.surface === 'headless') return;
|
|
220
|
-
try { nativeSessions(session.machine, session.harness ?? 'claude-code'); } catch { /* unread: unknown inside the write */ }
|
|
221
|
+
try { nativeSessions(session.machine, session.harness ?? 'claude-code', session.session_id); } catch { /* unread: unknown inside the write */ }
|
|
221
222
|
sessionAlive(fleet, session);
|
|
222
223
|
}
|
|
223
|
-
function nativeSessions(machine, harness) {
|
|
224
|
-
const key =
|
|
225
|
-
if (seen
|
|
224
|
+
function nativeSessions(machine, harness, sessionId) {
|
|
225
|
+
const key = nativeKey(machine, harness, sessionId), seen = discoveries.get(key);
|
|
226
|
+
if (seen) return seen.sessions;
|
|
226
227
|
if (readingsOnly) throw unavailable('native liveness is read before a board write, not inside it');
|
|
227
|
-
const sessions = discoverNative(machine, harness);
|
|
228
|
+
const sessions = discoverNative(machine, harness, sessionId);
|
|
228
229
|
discoveries.set(key, { at: Date.now(), sessions });
|
|
229
230
|
return sessions;
|
|
230
231
|
}
|
|
231
232
|
// A native discovery is bounded as the fleet reading is: one that does not answer in time is unknown, read next round.
|
|
232
233
|
const NATIVE_DISCOVERY_MS = 15_000;
|
|
233
|
-
|
|
234
|
+
const nativeKey = (machine, harness, sessionId) => `${normalMachine(machine)}\u0000${harness}\u0000${sessionId}`;
|
|
235
|
+
function discoverNative(machine, harness, sessionId) {
|
|
234
236
|
if (offlineMachine(machine)) throw unavailable(`native liveness unavailable: machine ${normalMachine(machine)} is offline`);
|
|
235
237
|
// This machine's discovery is its harness door, as every other harness read here is; another machine's is supercode's fleet arm
|
|
236
238
|
const result = normalMachine(machine) === localMachine()
|
|
237
|
-
? doorOn(machine, 'harness.v1.sessions.discover', { harnesses: [harness], limit: 2000 }, { timeout: NATIVE_DISCOVERY_MS })
|
|
238
|
-
: sc(['discover', '--on', machine, '--harness', harness, '--limit', '2000'], { timeout: NATIVE_DISCOVERY_MS });
|
|
239
|
+
? doorOn(machine, 'harness.v1.sessions.discover', { harnesses: [harness], query: sessionId, limit: 2000 }, { timeout: NATIVE_DISCOVERY_MS })
|
|
240
|
+
: sc(['discover', '--on', machine, '--harness', harness, '--query', sessionId, '--limit', '2000'], { timeout: NATIVE_DISCOVERY_MS });
|
|
239
241
|
const page = result.code === 0 ? lastJson(result.stdout) : null;
|
|
240
242
|
if (!Array.isArray(page?.sessions)) throw unavailable(`native liveness unavailable on ${machine}: ${(result.stderr || result.stdout).trim().slice(-300)}`);
|
|
241
243
|
return page.sessions;
|
|
@@ -525,8 +527,8 @@ function stopSession(board, session) {
|
|
|
525
527
|
const r = sc(['close', addressOf(session)], { timeout: 120_000, as: boardAddress(board.label) });
|
|
526
528
|
if (r.code !== 0) throw new Error(`${doorTransient(r) ? 'close deferred: ' : ''}closing native session ${addressOf(session)}: ${(r.stderr || r.stdout).trim().slice(-500)}`);
|
|
527
529
|
// the close's receipt is a discovery taken after it, never the round's cached one from before
|
|
528
|
-
discoveries.delete(
|
|
529
|
-
const reading = nativeReading(nativeSessions(session.machine, session.harness), session);
|
|
530
|
+
discoveries.delete(nativeKey(session.machine, session.harness, session.session_id));
|
|
531
|
+
const reading = nativeReading(nativeSessions(session.machine, session.harness, session.session_id), session);
|
|
530
532
|
if (reading.alive !== false) throw new Error(`native close has no confirmed stopped receipt for ${addressOf(session)}`);
|
|
531
533
|
return;
|
|
532
534
|
}
|
|
@@ -931,7 +933,7 @@ export function closeRequested(board, report, { taskId = null, caller = null, tt
|
|
|
931
933
|
try {
|
|
932
934
|
const machine = session.machine ?? localMachine();
|
|
933
935
|
if (session.surface === 'native' && !session.pane) {
|
|
934
|
-
const reading = nativeReading(nativeSessions(machine, session.harness), session);
|
|
936
|
+
const reading = nativeReading(nativeSessions(machine, session.harness, session.session_id), session);
|
|
935
937
|
if (reading.alive == null) throw new Error(`close deferred: native liveness unknown for ${addressOf(session)}`);
|
|
936
938
|
if (reading.alive) {
|
|
937
939
|
const protectedNow = active.some(r => addressOf(json(r.metadata, {})?.supercode) === addressOf(session));
|
|
@@ -997,13 +999,12 @@ export function closeRequested(board, report, { taskId = null, caller = null, tt
|
|
|
997
999
|
function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
998
1000
|
if (!fleet.ok) return;
|
|
999
1001
|
// The published fleet says whether a session runs and its turn, not when it last wrote: that is the session's own
|
|
1000
|
-
// record, read from its machine's discovery (
|
|
1002
|
+
// record, read from its machine's discovery (once a round, only when a card needs it).
|
|
1001
1003
|
// Without it every reading was 'no_timestamp' and no session was ever found idle.
|
|
1002
|
-
const discovered = new Map();
|
|
1003
1004
|
const updatedAt = (session) => {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
return Number(
|
|
1005
|
+
let rows = null;
|
|
1006
|
+
try { rows = nativeSessions(session.machine ?? localMachine(), session.harness ?? 'claude-code', session.session_id); } catch { /* unread: no timestamp */ }
|
|
1007
|
+
return Number(rows?.find((row) => row.locator?.session_id === session.session_id)?.updated_at_ms) || null;
|
|
1007
1008
|
};
|
|
1008
1009
|
const managers = [].concat(entry.params.managers ?? []).filter(Boolean);
|
|
1009
1010
|
const running = board.db.prepare(entry.workflow.run ? "SELECT id FROM tasks WHERE current_run_id IS NOT NULL" : "SELECT id FROM tasks WHERE status = 'running'").all();
|
|
@@ -1021,7 +1022,7 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
|
|
|
1021
1022
|
if (session.surface === 'native' && sessionAlive(fleet, session) !== true) return;
|
|
1022
1023
|
// a session in no pane has its turn on its machine's discovery row, which sessionAlive just read
|
|
1023
1024
|
let nativeRow = null;
|
|
1024
|
-
if (session.surface === 'native') try { nativeRow = nativeReading(nativeSessions(session.machine, session.harness ?? 'claude-code'), session).row; } catch { /* unread: no turn */ }
|
|
1025
|
+
if (session.surface === 'native') try { nativeRow = nativeReading(nativeSessions(session.machine, session.harness ?? 'claude-code', session.session_id), session).row; } catch { /* unread: no turn */ }
|
|
1025
1026
|
const live = fleet.sessions.find((x) => x.address === addressOf(session)) ?? (nativeRow ? { address: addressOf(session), turn: nativeRow.activity?.turn ?? null } : undefined);
|
|
1026
1027
|
const observedAt = now();
|
|
1027
1028
|
const lastAtMs = Number(live?.last_message_at_ms) || updatedAt(session);
|
|
@@ -1537,7 +1538,7 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
1537
1538
|
// session it did not launch, left running at its run's end) is its owner's while it runs: it is never resumed beside
|
|
1538
1539
|
// itself, and the launch waits for the owner to end it.
|
|
1539
1540
|
if (prev?.surface === 'native' && !prev.pane && (!prev.close_confirmed_at || prev.released)) {
|
|
1540
|
-
const reading = nativeReading(nativeSessions(prev.machine, prev.harness), prev);
|
|
1541
|
+
const reading = nativeReading(nativeSessions(prev.machine, prev.harness, prev.session_id), prev);
|
|
1541
1542
|
if (reading.alive == null) throw unavailable(`cannot establish native continuation liveness for ${addressOf(prev)}`);
|
|
1542
1543
|
fleet.native ??= new Map(); fleet.native.set(addressOf(prev), reading);
|
|
1543
1544
|
if (reading.alive && prev.released) throw unavailable(`released session ${addressOf(prev)} still runs; the launch waits for its owner to end it`);
|
|
@@ -1789,7 +1790,7 @@ function nativeListing(who) {
|
|
|
1789
1790
|
const m = /^sc:([^:]+):([^:]+):(.+)$/.exec(String(who));
|
|
1790
1791
|
if (!m || !HARNESSES[m[2]]) return null;
|
|
1791
1792
|
const [, host, harness, session_id] = m;
|
|
1792
|
-
try { return nativeReading(nativeSessions(host, harness), { session_id, harness }).alive === true ? { address: who, machine: normalMachine(host), harness, session_id, pane: null, name: null } : null; }
|
|
1793
|
+
try { return nativeReading(nativeSessions(host, harness, session_id), { session_id, harness }).alive === true ? { address: who, machine: normalMachine(host), harness, session_id, pane: null, name: null } : null; }
|
|
1793
1794
|
catch { return null; }
|
|
1794
1795
|
}
|
|
1795
1796
|
|
|
@@ -1816,7 +1817,7 @@ function adoptSession(board, id, who, { request = null, open = request, progress
|
|
|
1816
1817
|
if (!found) throw Object.assign(new Error(`the fleet lists no live session ${who}`), { notListed: true });
|
|
1817
1818
|
const [, host, harness, sessionId] = found.address.split(':');
|
|
1818
1819
|
if (!HARNESSES[harness]) throw new Error(`${found.address} is ${harness}; the board keeps and resumes ${Object.keys(HARNESSES).join(' and ')} sessions`);
|
|
1819
|
-
const native = found.pane ? null : nativeReading(nativeSessions(host, harness), { session_id: sessionId, harness });
|
|
1820
|
+
const native = found.pane ? null : nativeReading(nativeSessions(host, harness, sessionId), { session_id: sessionId, harness });
|
|
1820
1821
|
if (found.pane ? !panePresent(host, found.pane) : native.alive !== true) throw new Error(`${found.address} has no verified live native process or pane`);
|
|
1821
1822
|
const beforeRun = view(board, (db) => currentRun(db, id));
|
|
1822
1823
|
const beforeSession = beforeRun?.metadata?.supercode;
|
|
@@ -1826,7 +1827,7 @@ function adoptSession(board, id, who, { request = null, open = request, progress
|
|
|
1826
1827
|
// the session's own working directory, as discovery records it (where `--resume` finds it), else its pane's
|
|
1827
1828
|
let dir = workspace ?? native?.row?.cwd;
|
|
1828
1829
|
if (!dir) {
|
|
1829
|
-
dir = nativeSessions(host, harness).find((x) => x.locator?.session_id === sessionId)?.cwd ?? null;
|
|
1830
|
+
dir = nativeSessions(host, harness, sessionId).find((x) => x.locator?.session_id === sessionId)?.cwd ?? null;
|
|
1830
1831
|
}
|
|
1831
1832
|
if (!dir && found.pane) {
|
|
1832
1833
|
const r = runtimeDoor(normalMachine(host)===localMachine()?null:host,'harness.v1.panes.list',{}, { timeout: 60_000 });
|
|
@@ -2559,6 +2560,7 @@ export async function deliver({ root, slugs = null }) {
|
|
|
2559
2560
|
*/
|
|
2560
2561
|
export async function tick({ root, lanes, settings, only = null, slugs = null, dispatch = true, params = {}, deploymentReceipt = false }) {
|
|
2561
2562
|
fleetRoot = root;
|
|
2563
|
+
discoveries.clear();
|
|
2562
2564
|
// a verb's flags (dispatch --max, --failure-limit) are params of this tick, read by the workflow as its own
|
|
2563
2565
|
const deployment = deploymentReceipt ? workflowRevision(root) : null;
|
|
2564
2566
|
const loaded = await loadWorkflow(root, { fresh: true });
|
package/board/workflow.mjs
CHANGED
|
@@ -167,9 +167,12 @@ const execFileAsync = promisify(execFile);
|
|
|
167
167
|
export async function refreshRelationAnswers(board) {
|
|
168
168
|
const ids = new Set();
|
|
169
169
|
const entry = workflowFor(board.root);
|
|
170
|
-
for (const row of board.db.prepare("SELECT t.*,c.blocked_by FROM tasks t JOIN supercode_cards c ON c.task_id=t.id WHERE c.blocked_by IS NOT NULL AND c.blocked_by!='[]'").all()) {
|
|
171
|
-
|
|
172
|
-
for (
|
|
170
|
+
for (const row of board.db.prepare("SELECT t.*,c.blocked_by FROM tasks t JOIN supercode_cards c ON c.task_id=t.id WHERE c.blocked_by IS NOT NULL AND c.blocked_by!='[]' AND t.status NOT IN ('done','cancelled','archived') AND NOT COALESCE(c.archived,0)").all()) {
|
|
171
|
+
// only an open card's message has an answer to read: a closed card, or one blocked by cards alone, needs no scope
|
|
172
|
+
// built for its guard (75 walked on 2026-10-04, one of them a live message blocker)
|
|
173
|
+
const messages = (json(row.blocked_by, []) ?? []).filter((block) => block.kind === 'message');
|
|
174
|
+
if (!messages.length || !relationActive(board.db, board, entry, row)) continue;
|
|
175
|
+
for (const block of messages) ids.add(block.id);
|
|
173
176
|
}
|
|
174
177
|
for (const row of board.db.prepare("SELECT e.payload FROM task_events e JOIN (SELECT task_id, MAX(id) id FROM task_events WHERE kind IN ('blocked','unblocked') GROUP BY task_id) last ON e.id=last.id JOIN tasks t ON t.id=e.task_id WHERE e.kind='blocked' AND t.status='blocked'").all()) {
|
|
175
178
|
const id = json(row.payload, {})?.waiting_on; if (id) ids.add(id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.61",
|
|
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": {
|