@volter/supercode-orchestrator 0.4.65 → 0.4.66

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.
@@ -500,11 +500,6 @@ function observeIdle(board, fleet, settings, report, entry, effects) {
500
500
 
501
501
  /** A session effect a committed move asked for: a message to the card's current actor, or closing a slot's pane. */
502
502
  export async function sessionEffect(board, e) {
503
- if (e.kind === 'tell') {
504
- // an ask is sent under the id the card's block waits on
505
- await send(boardAddress(board.label), board.label, e.to, e.text, e.messageId ?? `board-tell-${e.id}-${now()}`, { ask: Boolean(e.messageId) });
506
- return;
507
- }
508
503
  if (e.kind === 'send_to_actor') {
509
504
  const run = tx(board, (db) => currentRun(db, e.id));
510
505
  const session = run?.metadata?.supercode;
@@ -854,6 +849,25 @@ async function send(from, label, to, body, key, { queue = false, ask = false } =
854
849
  if (answer.code !== 0) throw new Error((answer.text || r.stderr || `no answer (exit ${r.code})`).trim().slice(-300));
855
850
  }
856
851
 
852
+ /**
853
+ * The messages the workflow has the board send (`tell`, `ask`: board_message events) that it has not sent yet, sent now
854
+ * and marked board_message_sent. A question goes under the id its card waits on; a resend of a sent one is a no-op
855
+ * (the id is the send's idempotency key). One that fails is tried again next tick.
856
+ */
857
+ async function sendBoardMessages(board, report) {
858
+ const rows = tx(board, (db) => db.prepare(`SELECT id, task_id, payload FROM task_events e WHERE kind = 'board_message' AND NOT EXISTS
859
+ (SELECT 1 FROM task_events s WHERE s.kind = 'board_message_sent' AND s.task_id = e.task_id AND json_extract(s.payload, '$.event') = e.id) ORDER BY id`).all());
860
+ for (const row of rows) {
861
+ const m = json(row.payload, {}) ?? {};
862
+ try {
863
+ await send(boardAddress(board.label), board.label, m.to, m.text, m.message_id ?? `board-message-${row.id}`, { ask: Boolean(m.message_id) });
864
+ tx(board, (db) => event(db, row.task_id, 'board_message_sent', { event: row.id, to: m.to, ...(m.message_id ? { message_id: m.message_id } : {}) }));
865
+ } catch (error) {
866
+ report.undelivered.push([row.task_id, m.to, String(error.message).slice(-200)]);
867
+ }
868
+ }
869
+ }
870
+
857
871
  /** The board's own address: where its notices come from and where card updates are mailed. */
858
872
  export const boardAddress = (label) => `sc:${localMachine()}:board:${label.replace(/[^A-Za-z0-9_.-]/g, '-')}`;
859
873
 
@@ -1216,6 +1230,7 @@ export async function tick({ root, lanes, settings, only = null, slugs = null, d
1216
1230
  supervise(board, fleet, settings, report, entry, effects); closeRequested(board, report); phase('supervise');
1217
1231
  report.unsettled = [];
1218
1232
  report.promoted = recomputeReady(board, effects, report.unsettled);
1233
+ await sendBoardMessages(board, report);
1219
1234
  const before = report.started.length;
1220
1235
  if (dispatch) startCards(board, fleet, lanes, settings, report, entry, effects, { only, hostRunning });
1221
1236
  hostRunning += report.started.length - before; phase('start');
@@ -534,15 +534,16 @@ const ACTIONS = {
534
534
  return;
535
535
  }
536
536
  },
537
- // a message from the board to a session (`to`: its address)
538
- tell(ctx, { to, text }) { if (to) ctx.effects.push({ kind: 'tell', id: ctx.id, to: String(to), text: String(text ?? '') }); },
537
+ // a message from the board to a session (`to`: its address), kept on the card as a board_message event until the
538
+ // dispatcher has sent it: a move settled by any verb (a list settles too) is never lost with its effects
539
+ tell(ctx, { to, text }) { if (to) record(ctx.db, ctx.id, 'board_message', { to: String(to), text: String(text ?? '') }, null); },
539
540
  // a question from the board to a session, sent under a message id chosen now: `{{asked}}` in the actions after it is
540
541
  // that id (what a block waits on), and the answer to it lands in the board's mailbox
541
542
  ask(ctx, { to, text }) {
542
543
  if (!to) throw new Error(`the workflow asks nobody about ${ctx.id} (ask.to is empty)`);
543
544
  const id = `m-${randomBytes(12).toString('hex')}`;
544
545
  ctx.scope.asked = id;
545
- ctx.effects.push({ kind: 'tell', id: ctx.id, to: String(to), text: `${String(text ?? '')}\n\nAnswer with reply_to ${id}.`, messageId: id });
546
+ record(ctx.db, ctx.id, 'board_message', { to: String(to), text: `${String(text ?? '')}\n\nAnswer with reply_to ${id}.`, message_id: id }, null);
546
547
  },
547
548
  send_to_actor(ctx, text) { ctx.effects.push({ kind: 'send_to_actor', id: ctx.id, text }); },
548
549
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.4.65",
3
+ "version": "0.4.66",
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": {