@volter/supercode-orchestrator 0.4.25 → 0.4.26
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 +15 -5
- package/board/dispatch.mjs +3 -2
- package/board/engine.mjs +9 -0
- package/package.json +1 -1
package/board/cli.mjs
CHANGED
|
@@ -20,10 +20,10 @@ Hermes Kanban's verbs, with their flags:
|
|
|
20
20
|
init · boards list|create|rm|switch|show|rename|set-default-workdir|export|import
|
|
21
21
|
create <title> [--body] [--assignee] [--parent]... [--workspace scratch|worktree[:<path>]|dir:<path>] [--branch] [--project]
|
|
22
22
|
[--tenant] [--priority] [--triage] [--idempotency-key] [--max-runtime 30m] [--created-by] [--skill]... [--max-retries]
|
|
23
|
-
[--model] [--provider] [--goal] [--goal-max-turns] [--initial-status blocked|running]
|
|
23
|
+
[--model] [--provider] [--goal|--no-goal] [--goal-max-turns] [--initial-status blocked|running]
|
|
24
24
|
swarm <goal> --worker PROFILE:TITLE[:SKILL,SKILL]... --verifier P --synthesizer P
|
|
25
25
|
list [--mine] [--assignee] [--status] [--tenant] [--session] [--archived] [--sort] show <id> context <id> runs <id>
|
|
26
|
-
assign <id> <profile|none> reassign <id> <profile> [--reclaim] [--reason] set-model <id> [model] [--provider]
|
|
26
|
+
assign <id> <profile|none> reassign <id> <profile> [--reclaim] [--reason] set-model <id> [model] [--provider] set-goal <id>... [--goal-max-turns N] [--off]
|
|
27
27
|
claim <id> [--ttl] reclaim <id> [--reason] heartbeat <id> [--note] link|unlink <parent> <child>
|
|
28
28
|
comment <id> <text> [--author] [--max-len] attach <id> <path> [--name] [--content-type] attachments <id> attach-rm <n>
|
|
29
29
|
complete <id>... [--result] [--summary] [--metadata JSON] edit <id> --result [--summary] [--metadata]
|
|
@@ -51,7 +51,7 @@ supercode's own:
|
|
|
51
51
|
|
|
52
52
|
const MULTI = new Set(['parent', 'skill', 'worker', 'child', 'ids', 'rm']);
|
|
53
53
|
const FLAGS = new Set(['json', 'triage', 'no-notify', 'no-start', 'all', 'force', 'help', 'mine', 'archived', 'reclaim', 'dry-run',
|
|
54
|
-
'goal', 'switch', 'delete', 'no-attachments', 'include-logs', 'verbose']);
|
|
54
|
+
'goal', 'no-goal', 'off', 'switch', 'delete', 'no-attachments', 'include-logs', 'verbose']);
|
|
55
55
|
// flags that take every following word up to the next flag (Hermes's nargs='+')
|
|
56
56
|
const GREEDY = new Set(['ids', 'rm']);
|
|
57
57
|
|
|
@@ -235,13 +235,15 @@ export async function runWorkflow(argv) {
|
|
|
235
235
|
const meta = boardMeta(root, slug);
|
|
236
236
|
let workspace = args.workspace;
|
|
237
237
|
if ((workspace === 'dir' || workspace === 'worktree') && meta.default_workdir) workspace = `${workspace}:${meta.default_workdir}`;
|
|
238
|
+
// a home whose `kanban.goal_mode_default` is true makes every new card a goal card (a subtask is never started alone)
|
|
239
|
+
const goalDefault = !args.goal && !args['no-goal'] && !args['subtask-of'] && (await laneState(root)).kanban.goal_mode_default === true;
|
|
238
240
|
const created = withBoard((b) => engine.createCard(b, {
|
|
239
241
|
title: rest.join(' '), body: args.body, assignee: args.assignee === 'none' ? null : args.assignee, machine: args.machine,
|
|
240
242
|
workspace, branch: args.branch, parents: args.parent, priority: args.priority, tenant: args.tenant,
|
|
241
243
|
triage: args.triage, idempotencyKey: args['idempotency-key'], maxRuntime: duration(args['max-runtime']),
|
|
242
244
|
maxRetries, skills: args.skill, model: args.model, provider: args.provider, createdBy: args['created-by'] ?? callerName(),
|
|
243
245
|
sessionId: process.env.CLAUDE_CODE_SESSION_ID ?? null, subtaskOf: args['subtask-of'] ?? null, initialStatus: args['initial-status'] ?? null,
|
|
244
|
-
project: args.project ?? meta.project_id ?? null, goal: args.goal ? { maxTurns: args['goal-max-turns'] ? Number(args['goal-max-turns']) : null } : null,
|
|
246
|
+
project: args.project ?? meta.project_id ?? null, goal: args.goal || goalDefault ? { maxTurns: args['goal-max-turns'] ? Number(args['goal-max-turns']) : null } : null,
|
|
245
247
|
}), true);
|
|
246
248
|
let answer = { id: created.id, existing: created.existing };
|
|
247
249
|
if (!args['no-start'] && !created.existing && !args['subtask-of']) {
|
|
@@ -377,6 +379,14 @@ export async function runWorkflow(argv) {
|
|
|
377
379
|
say(args, `Reassigned ${id} to ${rest[1]}`, { ok: true }); return 0;
|
|
378
380
|
}
|
|
379
381
|
case 'set-model': need(1, 'a card id'); withBoard((b) => engine.setModel(b, id, rest[1] ?? null, args.provider ?? null)); say(args, rest[1] ? `${id} runs on model ${rest[1]}${args.provider ? ` (${args.provider})` : ''} from its next start` : `${id} uses its profile's model`, { ok: true }); return 0;
|
|
382
|
+
case 'set-goal': {
|
|
383
|
+
// a card into goal mode (or out of it with --off) from its next idle turn: the heartbeat keeps its session going
|
|
384
|
+
need(1, 'a card id');
|
|
385
|
+
const maxTurns = args['goal-max-turns'] ? Number(args['goal-max-turns']) : null;
|
|
386
|
+
if (maxTurns !== null && !(Number.isInteger(maxTurns) && maxTurns >= 1)) throw new Error('--goal-max-turns is a whole number of turns, at least 1');
|
|
387
|
+
for (const t of ids()) withBoard((b) => engine.setGoal(b, t, { on: !args.off, maxTurns }));
|
|
388
|
+
say(args, ids().map((t) => args.off ? `${t} is no longer a goal card` : `${t} is a goal card${maxTurns ? ` (${maxTurns} turns)` : ''}`).join('\n'), { ok: true }); return 0;
|
|
389
|
+
}
|
|
380
390
|
case 'move': need(2, 'a card id and a machine (or none)'); withBoard((b) => engine.setMachine(b, id, rest[1] === 'none' ? null : rest[1])); say(args, `${id} runs on ${rest[1]}`, { ok: true }); return 0;
|
|
381
391
|
|
|
382
392
|
case 'claim': {
|
|
@@ -942,7 +952,7 @@ function authorize(db, verb, rest, args, own) {
|
|
|
942
952
|
}
|
|
943
953
|
if (verb === 'boards') { if (!['list', 'ls', 'show', 'current', undefined].includes(rest[0])) refuse('does not change the home\'s boards'); return; }
|
|
944
954
|
// verbs that act on the board rather than on a card are the manager's and the board's, never a card's session's
|
|
945
|
-
if (['pause', 'resume', 'swarm', 'dispatch', 'daemon', 'serve', 'gc', 'repair', 'adopt', 'close', 'move', 'reassign', 'assign', 'set-model', 'claim', 'reclaim', 'archive', 'promote', 'schedule', 'link', 'unlink', 'edit', 'reopen-review', 'reopen', 'attach-rm'].includes(verb)) refuse(`does not ${verb}; the fleet's manager does`);
|
|
955
|
+
if (['pause', 'resume', 'swarm', 'dispatch', 'daemon', 'serve', 'gc', 'repair', 'adopt', 'close', 'move', 'reassign', 'assign', 'set-model', 'set-goal', 'claim', 'reclaim', 'archive', 'promote', 'schedule', 'link', 'unlink', 'edit', 'reopen-review', 'reopen', 'attach-rm'].includes(verb)) refuse(`does not ${verb}; the fleet's manager does`);
|
|
946
956
|
if (args.all) refuse(`does not ${verb} every card; only its own`);
|
|
947
957
|
const card = engine.getTask(db, rest[0]);
|
|
948
958
|
if (!card) return; // the verb itself answers "no card"
|
package/board/dispatch.mjs
CHANGED
|
@@ -474,8 +474,9 @@ function launchPane(board, task, runId, lane, fleet, { input = null } = {}) {
|
|
|
474
474
|
let replaced = null;
|
|
475
475
|
if (prev && prev.machine === machine) {
|
|
476
476
|
if (sessionAlive(fleet, prev)) {
|
|
477
|
-
session
|
|
478
|
-
|
|
477
|
+
// the session is kept; the heartbeat's count belongs to the run that ended, so the new run starts it again
|
|
478
|
+
const { missing_since: _m, goal_turns: _t, goal_nudged_at: _n, idle_since: _i, worked_since_ask: _w, ...kept } = prev;
|
|
479
|
+
session = { ...kept, mode: 'adopt', launched_at: now(), seen_at: now() };
|
|
479
480
|
} else if (prev.resumable !== false) {
|
|
480
481
|
const r = sc([...base, '--key', `board-${task.id}-r${runId}-resume`, '--input',
|
|
481
482
|
`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 });
|
package/board/engine.mjs
CHANGED
|
@@ -768,6 +768,15 @@ export function setModel(board, id, model = null, provider = null) {
|
|
|
768
768
|
});
|
|
769
769
|
}
|
|
770
770
|
|
|
771
|
+
/** Goal mode on a card (Hermes's goal loop, which Hermes sets only at creation): on or off, with its turn budget. */
|
|
772
|
+
export function setGoal(board, id, { on = true, maxTurns = null } = {}) {
|
|
773
|
+
return tx(board, (db) => {
|
|
774
|
+
must(db, id);
|
|
775
|
+
run(db, 'UPDATE tasks SET goal_mode = ?, goal_max_turns = ? WHERE id = ?', on ? 1 : 0, on ? maxTurns : null, id);
|
|
776
|
+
event(db, id, 'goal_set', { goal_mode: Boolean(on), goal_max_turns: on ? maxTurns : null });
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
771
780
|
export function addAttachment(board, id, { file, name, contentType = null, author = 'user', dir }) {
|
|
772
781
|
return tx(board, (db) => {
|
|
773
782
|
must(db, id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.26",
|
|
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": {
|