@volter/supercode-orchestrator 0.4.44 → 0.4.45

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/cli.mjs CHANGED
@@ -12,7 +12,7 @@ import { read } from '../orchestration.mjs';
12
12
  import { holdDispatcherLock } from '../hermes-locks.mjs';
13
13
  import { backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, tx } from './store.mjs';
14
14
  import * as engine from './engine.mjs';
15
- import { adoptSession, boardLabel, boardSettings, lanesOf, localMachine, observeFleet, paused, plan, startCard, tick } from './dispatch.mjs';
15
+ import { adoptSession, boardAddress, boardLabel, boardSettings, lanesOf, localMachine, observeFleet, paused, plan, startCard, tick } from './dispatch.mjs';
16
16
  import { loadWorkflow, statusOf, useWorkflow, workflowEntry, workflowFor } from './workflow.mjs';
17
17
  import { evaluate } from './expr.mjs';
18
18
 
@@ -1050,7 +1050,8 @@ function closeSession(board, id) {
1050
1050
  if (!task) throw new Error(`no card ${id}`);
1051
1051
  if (task.status === 'running') throw new Error(`${id} is running; block, complete or reclaim it before closing its session`);
1052
1052
  if (!session?.pane) return null;
1053
- const r = spawnSync(process.env.SUPERCODE_BIN || 'supercode', ['teams', 'panes', 'close', session.pane, '--machine', session.machine], { cwd: tmpdir(), encoding: 'utf8', timeout: 60_000 });
1053
+ // the board closes its card's pane, whichever session ran `workflow close`
1054
+ const r = spawnSync(process.env.SUPERCODE_BIN || 'supercode', ['teams', 'panes', 'close', session.pane, '--machine', session.machine], { cwd: tmpdir(), encoding: 'utf8', timeout: 60_000, env: { ...process.env, SUPERCODE_CALLER: boardAddress(board.label) } });
1054
1055
  if (r.status !== 0) throw new Error(`closing pane ${session.pane} on ${session.machine}: ${(r.stderr || r.stdout).trim().slice(-300)}`);
1055
1056
  tx(board, (db) => event(db, id, 'session_closed', { session_id: session.session_id, machine: session.machine, pane: session.pane }));
1056
1057
  return session;
@@ -54,9 +54,11 @@ export function localMachine() {
54
54
  /** The supercode command the dispatcher drives. */
55
55
  export const supercodeBin = () => process.env.SUPERCODE_BIN || 'supercode';
56
56
 
57
- function sc(args, { input = null, timeout = 120_000 } = {}) {
58
- // a neutral directory: `supercode teams …` resolves its entry from a checkout at the cwd
59
- const r = spawnSync(supercodeBin(), args, { cwd: tmpdir(), input: input ?? undefined, encoding: 'utf8', timeout, env: process.env, maxBuffer: 512 * 1024 * 1024 });
57
+ function sc(args, { input = null, timeout = 120_000, as = null } = {}) {
58
+ // a neutral directory: `supercode teams …` resolves its entry from a checkout at the cwd. `as`: the board this
59
+ // command acts for, so a card's pane is started and closed by its board, whichever session ran the board's verb
60
+ const env = as ? { ...process.env, SUPERCODE_CALLER: as } : process.env;
61
+ const r = spawnSync(supercodeBin(), args, { cwd: tmpdir(), input: input ?? undefined, encoding: 'utf8', timeout, env, maxBuffer: 512 * 1024 * 1024 });
60
62
  return { code: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error ? r.error.message : '') };
61
63
  }
62
64
 
@@ -236,7 +238,7 @@ function limitsOf(db, board, entry, task, session) {
236
238
  return out;
237
239
  }
238
240
 
239
- function stopSession(session) {
241
+ function stopSession(board, session) {
240
242
  if (!session) return;
241
243
  if (session.surface === 'headless' && pidAlive(session.pid)) {
242
244
  // SIGTERM to the worker's process group, then SIGKILL if it is still there after a grace, as Hermes stops its own
@@ -246,7 +248,7 @@ function stopSession(session) {
246
248
  if (pidAlive(session.pid)) signal('SIGKILL');
247
249
  return;
248
250
  }
249
- if (session.pane) sc(['teams', 'panes', 'close', session.pane, ...(session.machine ? ['--machine', session.machine] : [])], { timeout: 60_000 });
251
+ if (session.pane) sc(['teams', 'panes', 'close', session.pane, ...(session.machine ? ['--machine', session.machine] : [])], { timeout: 60_000, as: boardAddress(board.label) });
250
252
  }
251
253
 
252
254
  // ------------------------------------------------------------------ starting, as the workflow's dispatch says
@@ -416,7 +418,7 @@ function closeRequested(board, report) {
416
418
  for (const row of rows) {
417
419
  const session = json(row.metadata, {})?.supercode;
418
420
  if (!session?.pane) continue;
419
- try { stopSession(session); } catch (error) { report.undelivered.push([row.task_id, session.address, `closing it: ${String(error.message).slice(-200)}`]); continue; }
421
+ try { stopSession(board, session); } catch (error) { report.undelivered.push([row.task_id, session.address, `closing it: ${String(error.message).slice(-200)}`]); continue; }
420
422
  tx(board, (db) => setRunSession(db, row.id, { ...session, closed_at: now() }));
421
423
  }
422
424
  }
@@ -470,7 +472,7 @@ export async function sessionEffect(board, e) {
470
472
  // the session that sent the event is not closed under its own command: the dispatcher closes it next tick
471
473
  const caller = process.env.CLAUDE_CODE_SESSION_ID || process.env.CODEX_THREAD_ID;
472
474
  if (caller && caller === session.session_id) { tx(board, (db) => setRunSession(db, row.id, { ...session, close_requested: now() })); return; }
473
- stopSession(session);
475
+ stopSession(board, session);
474
476
  tx(board, (db) => setRunSession(db, row.id, { ...session, closed_at: now() }));
475
477
  return;
476
478
  }
@@ -576,7 +578,7 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
576
578
  if (input) deliverNow(board, prev.address ?? `sc:${prev.machine}:claude-code:${prev.session_id}`, input, `board-run-${task.id}-r${runId}`);
577
579
  } else if (prev.resumable !== false) {
578
580
  const r = sc([...base, '--key', `board-${task.id}-r${runId}-resume`, '--input',
579
- `The board resumed this session after it was lost; you are still on card ${task.id}: ${task.title}. Carry on.`, '--', '--resume', prev.session_id, ...tail], { timeout: 180_000 });
581
+ `The board resumed this session after it was lost; you are still on card ${task.id}: ${task.title}. Carry on.`, '--', '--resume', prev.session_id, ...tail], { timeout: 180_000, as: boardAddress(board.label) });
580
582
  const pane = r.code === 0 ? lastJson(r.stdout)?.pane ?? null : null;
581
583
  // a resume the harness refuses (its transcript is gone) ends the pane at once; another machine's launch
582
584
  // answers before that shows, so the pane is looked for again a moment later
@@ -586,7 +588,7 @@ function launchPane(board, task, runId, lane, fleet, { input = null, slot = 'wor
586
588
  } else if (prev) replaced = { session_id: prev.session_id, why: `the card moved from ${prev.machine} to ${machine}` };
587
589
  if (!session) {
588
590
  const sid = randomUUID();
589
- const r = sc([...base, '--key', `board-${task.id}-r${runId}-${sid.slice(0, 8)}`, '--input', input ?? `Card ${task.id}: ${task.title}`, '--', '--session-id', sid, ...tail], { timeout: 180_000 });
591
+ const r = sc([...base, '--key', `board-${task.id}-r${runId}-${sid.slice(0, 8)}`, '--input', input ?? `Card ${task.id}: ${task.title}`, '--', '--session-id', sid, ...tail], { timeout: 180_000, as: boardAddress(board.label) });
590
592
  if (r.code !== 0) throw new Error(`supercode open --on ${task.machine}: ${(r.stderr || r.stdout).trim().slice(-500)}`);
591
593
  session = { surface: 'pane', session_id: sid, machine, pane: lastJson(r.stdout)?.pane ?? null, mode: 'fresh', launched_at: now(), ...(replaced ? { replaces: replaced.session_id } : {}) };
592
594
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.44",
3
+ "version": "0.4.45",
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": {