@volter/supercode-orchestrator 0.4.30 → 0.4.32

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.
Files changed (2) hide show
  1. package/board/dispatch.mjs +19 -2
  2. package/package.json +1 -1
@@ -411,6 +411,17 @@ export async function startCard(board, id, { roles = ['manager'], lanes, setting
411
411
  * The heartbeat, observed: a running pane session whose last message is a whole tick old is sent `session_idle` with
412
412
  * how many times it has been asked this run and whether it worked since the last ask; the workflow decides the rest.
413
413
  */
414
+ /** Panes whose close was asked by their own session's command (see sessionEffect), closed now. */
415
+ function closeRequested(board, report) {
416
+ const rows = board.db.prepare(`SELECT id, task_id, metadata FROM task_runs WHERE metadata LIKE '%"close_requested"%' AND metadata NOT LIKE '%"closed_at"%'`).all();
417
+ for (const row of rows) {
418
+ const session = json(row.metadata, {})?.supercode;
419
+ if (!session?.pane) continue;
420
+ try { stopSession(session); } catch (error) { report.undelivered.push([row.task_id, session.address, `closing it: ${String(error.message).slice(-200)}`]); continue; }
421
+ tx(board, (db) => setRunSession(db, row.id, { ...session, closed_at: now() }));
422
+ }
423
+ }
424
+
414
425
  function observeIdle(board, fleet, settings, report, entry, effects) {
415
426
  if (!fleet.ok) return;
416
427
  for (const { id } of board.db.prepare("SELECT id FROM tasks WHERE status = 'running'").all()) {
@@ -429,7 +440,10 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
429
440
  if (session.goal_nudged_at && now() - session.goal_nudged_at < settings.interval * 2) return;
430
441
  const worked = Boolean(session.goal_nudged_at) && lastAt >= session.goal_nudged_at + settings.interval;
431
442
  try {
432
- const r = fire(db, board, entry, id, 'session_idle', { roles: ['platform'], payload: { turns: Number(session.goal_turns ?? 0), worked }, extra: { where: whereOf(board, task) } });
443
+ const where = whereOf(board, task);
444
+ // the ask names what the session may do, as its run's prompt did
445
+ const said = doors(entry.workflow, status, session.role ?? entry.workflow.statuses[status]?.run?.role ?? 'implementer', id, where, session.surface === 'pane' ? 'pane' : 'headless');
446
+ const r = fire(db, board, entry, id, 'session_idle', { roles: ['platform'], payload: { turns: Number(session.goal_turns ?? 0), worked }, extra: { where, doors: said } });
433
447
  effects.push(...r.effects);
434
448
  const after = currentRun(db, id);
435
449
  if (after?.id === runRow.id) setRunSession(db, runRow.id, { ...(after.metadata?.supercode ?? session), goal_nudged_at: now() });
@@ -454,6 +468,9 @@ export async function sessionEffect(board, e) {
454
468
  for (const row of rows) {
455
469
  const session = json(row.metadata, {})?.supercode;
456
470
  if ((session?.slot ?? 'worker') !== e.slot || !session?.pane || session.closed_at) continue;
471
+ // the session that sent the event is not closed under its own command: the dispatcher closes it next tick
472
+ const caller = process.env.CLAUDE_CODE_SESSION_ID || process.env.CODEX_THREAD_ID;
473
+ if (caller && caller === session.session_id) { tx(board, (db) => setRunSession(db, row.id, { ...session, close_requested: now() })); return; }
457
474
  stopSession(session);
458
475
  tx(board, (db) => setRunSession(db, row.id, { ...session, closed_at: now() }));
459
476
  return;
@@ -1077,7 +1094,7 @@ export async function tick({ root, lanes, settings, only = null, slugs = null, d
1077
1094
  const phase = (name) => { const t = Date.now(); report.ms[name] = t - mark; mark = t; };
1078
1095
  readMailbox(board, boardAddress(board.label), report); phase('mailbox');
1079
1096
  const effects = [];
1080
- supervise(board, fleet, settings, report, entry, effects); phase('supervise');
1097
+ supervise(board, fleet, settings, report, entry, effects); closeRequested(board, report); phase('supervise');
1081
1098
  report.promoted = recomputeReady(board);
1082
1099
  const before = report.started.length;
1083
1100
  if (dispatch) startCards(board, fleet, lanes, settings, report, entry, effects, { only, hostRunning });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.30",
3
+ "version": "0.4.32",
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": {