@volter/supercode-orchestrator 0.4.103 → 0.4.104
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 +24 -7
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -500,7 +500,7 @@ export function closeRequested(board, report, { taskId = null, caller = null } =
|
|
|
500
500
|
if (current.close_error !== error) event(db, row.task_id, 'session_close_failed', { session_id: session.session_id, pane: session.pane, error, retry: true }, row.id);
|
|
501
501
|
} else {
|
|
502
502
|
const { close_error: _error, ...rest } = current;
|
|
503
|
-
setRunSession(db, row.id, { ...rest, closed_at: at, close_confirmed_at: at });
|
|
503
|
+
setRunSession(db, row.id, { ...rest, close_error: null, closed_at: at, close_confirmed_at: at });
|
|
504
504
|
const address = addressOf(session);
|
|
505
505
|
const subscribed = active.some((r) => addressOf(json(r.metadata, {})?.supercode) === address && r.id !== row.id);
|
|
506
506
|
if (!subscribed) db.prepare("DELETE FROM kanban_notify_subs WHERE task_id = ? AND platform = 'supercode' AND chat_id = ?").run(row.task_id, address);
|
|
@@ -651,6 +651,12 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
651
651
|
// the slot's last session: kept when live, resumed when lost, never another slot's (a reviewer's is its own)
|
|
652
652
|
// a close its session's own command asked for is carried out first, so its pane is gone before the slot's next
|
|
653
653
|
// session starts (never two processes on one session)
|
|
654
|
+
// Every old pane in the slot must be settled, including a failed launch with no session id.
|
|
655
|
+
closeRequested(board, { undelivered: [] }, { taskId: task.id });
|
|
656
|
+
const pending = view(board, (db) => db.prepare('SELECT id, ended_at, metadata FROM task_runs WHERE task_id = ? AND id != ?').all(task.id, runId))
|
|
657
|
+
.map((r) => ({ ...r, session: json(r.metadata, {})?.supercode }))
|
|
658
|
+
.find((r) => r.session?.pane && (r.session.slot ?? 'worker') === slot && (r.ended_at || r.session.close_requested) && !r.session.close_confirmed_at);
|
|
659
|
+
if (pending) throw new Error(`slot ${slot} still owns pane ${pending.session.pane} from run ${pending.id}; closure must be confirmed before another session starts: ${pending.session.close_error ?? 'close pending'}`);
|
|
654
660
|
let prev = view(board, (db) => lastSession(db, task.id, slot));
|
|
655
661
|
if (prev?.close_requested && !prev.closed_at) {
|
|
656
662
|
const report = { undelivered: [] };
|
|
@@ -671,7 +677,10 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
671
677
|
};
|
|
672
678
|
let session = null;
|
|
673
679
|
let replaced = null;
|
|
674
|
-
if (prev?.forgotten)
|
|
680
|
+
if (prev?.forgotten) {
|
|
681
|
+
if (prev.pane && panePresent(prev.machine, prev.pane)) throw new Error(`forgotten session ${prev.session_id} still has live pane ${prev.pane}; close it before replacement`);
|
|
682
|
+
replaced = { session_id: prev.session_id, why: 'the workflow let it go' };
|
|
683
|
+
}
|
|
675
684
|
else if (prev && (prev.harness ?? 'claude-code') !== harness) {
|
|
676
685
|
// the card was reassigned to a lane of another harness: that harness starts it fresh (the card's text and thread
|
|
677
686
|
// are its context), and the last session, if still open, is closed so two harnesses never work one card
|
|
@@ -680,7 +689,9 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
680
689
|
} else if (prev && prev.machine === machine) {
|
|
681
690
|
// a session the board closed, or one whose close its own command asked for (the next tick's sweep closes that
|
|
682
691
|
// run's pane), is not kept, whatever a fleet reading taken before the close still lists
|
|
683
|
-
|
|
692
|
+
const paneLive = prev.pane ? panePresent(prev.machine, prev.pane) : false;
|
|
693
|
+
if (paneLive && (prev.closed_at || prev.close_requested || prev.forgotten)) throw new Error(`session ${prev.session_id} still has live pane ${prev.pane}; cannot resume or replace it`);
|
|
694
|
+
if (!prev.closed_at && !prev.close_requested && (prev.pane ? paneLive : sessionAlive(fleet, prev))) {
|
|
684
695
|
// the session is kept; the heartbeat's count belongs to the run that ended, so the new run starts it again
|
|
685
696
|
const { missing_since: _m, goal_turns: _t, goal_nudged_at: _n, close_requested: _q, closed_at: _c, close_confirmed_at: _cc, close_error: _ce, close_attempts: _ca, close_attempted_at: _ct, ...kept } = prev;
|
|
686
697
|
session = { ...kept, mode: 'adopt', launched_at: now(), seen_at: now() };
|
|
@@ -759,12 +770,18 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
|
|
|
759
770
|
return session;
|
|
760
771
|
}
|
|
761
772
|
|
|
762
|
-
/**
|
|
773
|
+
/** A machine reading, not a missing catalog entry, establishes whether a pane is alive. */
|
|
774
|
+
function panePresent(machine, pane) {
|
|
775
|
+
const r = sc(['teams', 'panes', 'ls', '--machine', machine ?? localMachine()], { timeout: 60_000 });
|
|
776
|
+
const panes = r.code === 0 ? json(r.stdout.trim(), null) : null;
|
|
777
|
+
if (!Array.isArray(panes)) throw new Error(`cannot establish pane ${pane} liveness on ${machine}: ${(r.stderr || r.stdout).trim().slice(-300)}`);
|
|
778
|
+
return panes.some((p) => p.pane === pane);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/** Whether a pane just launched is still there after the harness had a moment to start. */
|
|
763
782
|
function paneSurvives(machine, pane) {
|
|
764
783
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 6000);
|
|
765
|
-
|
|
766
|
-
const panes = r.code === 0 ? json(r.stdout.trim(), null) : null;
|
|
767
|
-
return Array.isArray(panes) ? panes.some((p) => p.pane === pane) : true; // no reading: the grace decides
|
|
784
|
+
return panePresent(machine, pane);
|
|
768
785
|
}
|
|
769
786
|
|
|
770
787
|
/** Hermes's own worker argv and task environment, run on this machine; watched by its pid. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.104",
|
|
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": {
|