@volter/supercode-orchestrator 0.5.124 → 0.5.125

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
@@ -26,7 +26,7 @@ import * as slowLog from '@volter/supercode-harness-sdk/slow-log';
26
26
  import { basename, dirname, join, resolve } from 'node:path';
27
27
  import { fileURLToPath } from 'node:url';
28
28
  import { homedir, tmpdir, userInfo } from 'node:os';
29
- import { actorOf, heldConfigWatched, heldDocumentsChanged, heldDocumentsWatched, holdBoards, retireHeldBoards, releaseHeldBoards, publishMarked, view, backingPaths, layOut, backingOf, boardExists, boardPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, setPrincipal, principalOf, isCaller, tx } from './store.mjs';
29
+ import { actorOf, heldConfigWatched, heldDocumentsChanged, heldDocumentsWatched, holdBoards, retireHeldBoards, releaseHeldBoards, publishMarked, view, backingPaths, layOut, backingOf, boardExists, boardPath, briefPath, DEFAULT_BOARD, event, json, listBoards, logsDir, now, openBoard, setActor, setPrincipal, principalOf, isCaller, tx } from './store.mjs';
30
30
  import { isLocalWorkspace } from './workspace.mjs';
31
31
  import { openZtrackBoard } from './ztrack.mjs';
32
32
  import * as engine from './engine.mjs';
@@ -90,7 +90,7 @@ The board's verbs, with their flags:
90
90
  [--tenant] [--priority] [--triage] [--idempotency-key] [--max-runtime 30m] [--created-by] [--skill]... [--max-retries]
91
91
  [--model] [--provider] [--goal|--no-goal] [--goal-max-turns] [--initial-status blocked|running]
92
92
  swarm <goal> --worker PROFILE:TITLE[:SKILL,SKILL]... --verifier P --synthesizer P
93
- list [--mine] [--assignee] [--status] [--tenant] [--session] [--archived] [--sort] show <id> context <id> runs <id>
93
+ list [--mine] [--assignee] [--status] [--tenant] [--session] [--archived] [--sort] show <id> context <id> brief <id> [--run N] runs <id>
94
94
  set <id> <assignee|machine|model|every> <value|none> [--provider] [--ids ...] assign <id> <profile|none> set-model <id> [model] [--provider] set-goal <id>... [--goal-max-turns N] [--off]
95
95
  reassign <id> --agent AGENT [--role implementer] (moves a card role's assignment to another agent; sessions keep their identity)
96
96
  claim <id> [--ttl] reclaim <id> [--reason] heartbeat <id> [--note] [--until <time|30m>] link|unlink <parent> <child>
@@ -634,6 +634,15 @@ export async function runVerb(argv) {
634
634
  return 0;
635
635
  }
636
636
 
637
+ case 'brief': {
638
+ need(1, 'a card id');
639
+ const run = args.run ?? withBoard((b) => view(b, (db) => engine.getTask(db, id)?.current_run_id));
640
+ const file = run == null ? null : briefPath(root, slug, id, run);
641
+ if (!file || !existsSync(file)) throw new Error(`the board keeps no brief for ${run == null ? 'a run' : `run ${run}`} of ${id}`);
642
+ current().out(readFileSync(file, 'utf8'));
643
+ return 0;
644
+ }
645
+
637
646
  case 'context': need(1, 'a card id'); current().out(`${withBoard((b) => view(b, (db) => engine.cardContext(db, id)))}\n`); return 0;
638
647
 
639
648
  case 'runs': {
@@ -1374,7 +1383,7 @@ function ownCard(db, sessionId) {
1374
1383
  return engine.lastSession(db, hit.task_id)?.session_id === sessionId ? engine.getTask(db, hit.task_id) : null;
1375
1384
  }
1376
1385
 
1377
- const READS = new Set(['list', 'ls', 'show', 'context', 'runs', 'log', 'tail', 'watch', 'stats', 'assignees', 'diagnostics', 'diag', 'attachments', 'notify-list', 'capacity', 'help', 'init']);
1386
+ const READS = new Set(['list', 'ls', 'show', 'context', 'brief', 'runs', 'log', 'tail', 'watch', 'stats', 'assignees', 'diagnostics', 'diag', 'attachments', 'notify-list', 'capacity', 'help', 'init']);
1378
1387
 
1379
1388
  /** The main session of each agent the workflow names a manager (`sc:<machine>:agent:<name>`), asked of this machine's
1380
1389
  * daemon, which keeps agents' records (mail step 1c), once per call before its verb runs: a manager named by its agent
@@ -39,7 +39,7 @@ import { homedir, hostname, tmpdir, totalmem } from 'node:os';
39
39
  import { basename, dirname, join } from 'node:path';
40
40
  import { createHash, randomUUID } from 'node:crypto';
41
41
  import { fileURLToPath } from 'node:url';
42
- import { view, boardCopyOf, boardPath, claimer, claimHolds, copyRefusal, event, json, listBoards, logsDir, newClaim, now, openBoard, setActor, storageFault, tx } from './store.mjs';
42
+ import { view, boardCopyOf, boardPath, briefPath, claimer, claimHolds, copyRefusal, event, json, listBoards, logsDir, newClaim, now, openBoard, setActor, storageFault, tx } from './store.mjs';
43
43
  import { doors, fire, loadWorkflow, resumeOf, runSpec, runWorkingSeconds, requestSessionClose, retireSupersededRuns, refreshCardInputs, refreshRelationAnswers, transitionsFor, scopeOf, statusOf, projectStatus, storedStatus, templateValue, workflowFor } from './workflow.mjs';
44
44
  import { checkWorkspace, assertWorkspaceOwner, notePresence, resolveWorkspace } from './workspace.mjs';
45
45
  import { capacity, harnessDefaults, holdings } from './capacity.mjs';
@@ -922,13 +922,13 @@ async function launchAttempt(board, entry, id, lane, fleet, settings, runId) {
922
922
  agent_profile_intent:structuredClone(agent.intended.resolved),
923
923
  agent_env:agentEnvironment(agent,{root:board.root,context:normalMachine(task.machine??localMachine())===localMachine()?localReaderContext(team.org,agent.identity.id)??undefined:undefined})});
924
924
  // The agent's own memory reaches every session of it: headless runs open its home; a pane, which may sit on
925
- // another machine, gets the home's snapshot in its first prompt. A shared profile's memory is never used.
925
+ // another machine, gets the home's snapshot in its run's brief. A shared profile's memory is never used.
926
926
  const agentHome=materializeAgentHome(board.root,team.org,agent.identity.id,lane);
927
927
  assertRunCurrent(board,id,runId);
928
928
  if(surface==='headless')meta.agent_env.HERMES_HOME=agentHome;
929
929
  else{const memory=await memorySnapshot(agentHome).catch(()=>'');assertRunCurrent(board,id,runId);if(memory)prompt=`${prompt}\n\n${memory}`;}
930
930
  const result = surface === 'pane'
931
- ? await launchPane(board, task, runId, lane, fleet, { input: prompt, slot, meta })
931
+ ? await launchPane(board, task, runId, lane, fleet, { input: briefByReference(board, task, runId, spec.role ?? slot, where, prompt), slot, meta })
932
932
  : await launchHeadless(board, task, runId, lane, settings, { prompt, skills: spec.skills ?? [], meta });
933
933
  tx(board,db=>{
934
934
  if(!runCurrent(db,id,runId))return; // replaced once launched: its session is the ended run's, closed as one
@@ -960,6 +960,24 @@ async function launchAttempt(board, entry, id, lane, fleet, settings, runId) {
960
960
  }
961
961
  }
962
962
 
963
+ /**
964
+ * What the board types into a pane session to start a run: one short line in the board's own words naming the role,
965
+ * the card and where the run's brief is, with the brief itself kept in a file. A terminal harness takes its input as a
966
+ * paste, and Claude Code records a paste of more than two lines or about 800 characters as a `<pasted_content>`
967
+ * attachment with no typed words beside it, which a session rightly reads as material, not as its instruction. The
968
+ * line stays one line and far under that size whatever the card holds; the session on the board's machine reads the
969
+ * file, one elsewhere prints it through the board's door.
970
+ */
971
+ export function briefByReference(board, task, runId, role, where, prompt) {
972
+ const file = briefPath(board.root, board.slug, task.id, runId);
973
+ mkdirSync(dirname(file), { recursive: true });
974
+ writeFileSync(file, `${prompt}\n`, { mode: 0o600 });
975
+ const local = !task.machine || normalMachine(task.machine) === localMachine();
976
+ const from = local ? `in the file ${file}: read the whole file now`
977
+ : `kept by the board: print it with \`supercode workflow brief ${task.id} ${where}\` (into a file outside your repository when it is long) and read all of it now`;
978
+ return `The ${board.label} board started run ${runId} of card ${task.id} and you are its ${role}. Your instructions from the board are the brief ${from}, then carry it out as your own task without waiting for another message.`;
979
+ }
980
+
963
981
  /** Start one card now by a person's word (a triage card's session, a manager's start): the workflow's start event. */
964
982
  export async function startCard(board, id, { roles = ['manager'], lanes, settings } = {}) {
965
983
  // the board's own published fleet projection, as a tick reads it: a manager's or creator's start that continues the
@@ -1898,7 +1916,7 @@ async function launchPane(board, task, runId, lane, fleet, { input = null, slot
1898
1916
  if (monitorsRuns(workflowFor(board.root)) && prev.monitor_held && !authorization && !released) throw new Error(`session ${prev.session_id} is stopped by incident ${prev.incident ?? 'unknown'}; only its manager or creator may restart it with workflow start`);
1899
1917
  // a resumed session is told what this run is for, as a kept one is: the run's own prompt (a reviewer resumed for
1900
1918
  // its next round gets that round's prompt, its arc's context with it), not only that it was resumed
1901
- const told = input ? `The board resumed this session for this run of card ${task.id}.\n\n${input}` : `The board resumed this session after it was lost; you are still on card ${task.id}: ${task.title}. Carry on.`;
1919
+ const told = input ? `The board resumed this session for this run of card ${task.id}. ${input}` : `The board resumed this session after it was lost; you are still on card ${task.id}: ${task.title}. Carry on.`;
1902
1920
  // a session is resumed in its own harness, whatever the lane's is now
1903
1921
  const its = HARNESSES[prev.harness ?? 'claude-code'] ?? HARNESSES['claude-code'];
1904
1922
  const launchKey = `board-${task.id}-r${runId}-resume`;
package/board/store.mjs CHANGED
@@ -124,6 +124,10 @@ export function boardPath(root, slug = DEFAULT_BOARD) {
124
124
  /** The directory of the board's worker logs, as Hermes lays them out. */
125
125
  export const logsDir = (root, slug = DEFAULT_BOARD) => join(boardDirOf(root, slug), 'logs');
126
126
 
127
+ /** The file holding the brief of run `run` of card `id`: what the board hands a pane session by reference, kept with
128
+ * the board's logs and removed with them. */
129
+ export const briefPath = (root, slug, id, run) => join(logsDir(root, slug), `${id}-r${run}.brief.md`);
130
+
127
131
  /** The home's boards that exist: `default` when it does, and every named one. */
128
132
  export function listBoards(root) {
129
133
  const out = boardExists(root) ? [DEFAULT_BOARD] : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-orchestrator",
3
- "version": "0.5.124",
3
+ "version": "0.5.125",
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": {