agent-conveyor 0.1.6 → 0.1.7

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.
@@ -76,7 +76,7 @@ export function runTypescriptRuntimeCommand(options) {
76
76
  return errorResult(`unknown command: ${parsed.command}`);
77
77
  }
78
78
  if (parsed.flags.help) {
79
- return textResult([`usage: ${program} ${parsed.command} [-h] [options]`]);
79
+ return textResult(commandHelpText(program, parsed.command));
80
80
  }
81
81
  if (parsed.error) {
82
82
  return errorResult(parsed.error);
@@ -337,6 +337,70 @@ export function runTypescriptRuntimeCommand(options) {
337
337
  return errorResult(error instanceof Error ? error.message : String(error));
338
338
  }
339
339
  }
340
+ function commandHelpText(program, command) {
341
+ const path = "[--path <workerctl.db>]";
342
+ const linesByCommand = {
343
+ criteria: [
344
+ `usage: ${program} criteria <task> [--list|--add --criterion <text> --source <source>|--accept ID|--satisfy ID|--defer ID|--reject ID] ${path} [--json]`,
345
+ "",
346
+ "Examples:",
347
+ ` ${program} criteria my-task --list --json --path /tmp/work/workerctl.db`,
348
+ ` ${program} criteria my-task --add --criterion "Note file exists" --source manager_inferred --status accepted --path /tmp/work/workerctl.db`,
349
+ ` ${program} criteria my-task --satisfy 1 --proof "File exists" --evidence-json '{"artifact":{"path":"docs/note.md"}}' --path /tmp/work/workerctl.db`,
350
+ ],
351
+ "finish-task": [
352
+ `usage: ${program} finish-task <task> --reason <reason> [--require-criteria-audit] ${path}`,
353
+ "",
354
+ "Examples:",
355
+ ` ${program} finish-task my-task --reason "Accepted criteria satisfied" --require-criteria-audit --path /tmp/work/workerctl.db`,
356
+ ],
357
+ "manager-ack": [
358
+ `usage: ${program} manager-ack <task> --from-stdin ${path}`,
359
+ `usage: ${program} manager-ack <task> --json ${path}`,
360
+ "",
361
+ "Example JSON:",
362
+ ` {"task":"my-task","manager_session":"mgr","supervision_contract":"I will supervise through Conveyor and verify criteria before finishing.","will_not_edit_project_files":true}`,
363
+ ],
364
+ nudge: [
365
+ `usage: ${program} nudge <worker-or-session> <message> ${path} [--dry-run]`,
366
+ `usage: ${program} session-nudge <session> <message> ${path} [--dry-run]`,
367
+ "",
368
+ "For task-routed delivery, prefer enqueue-nudge-worker plus dispatch:",
369
+ ` ${program} enqueue-nudge-worker my-task --message "Status and evidence?" --path /tmp/work/workerctl.db`,
370
+ ` ${program} dispatch --once --type nudge_worker --path /tmp/work/workerctl.db`,
371
+ ],
372
+ pair: [
373
+ `usage: ${program} pair --task <task> --worker-name <worker> --manager-name <manager> [options] ${path}`,
374
+ "",
375
+ "Options:",
376
+ " --task-goal <text> Task goal stored in Conveyor state.",
377
+ " --task-prompt <text> Initial worker prompt; defaults to task goal when omitted.",
378
+ " --manager-recipe <recipe> Seed a manager recipe, for example goalbuddy-conveyor.",
379
+ " --manager-acceptance <text> Seed an accepted manager criterion; repeat for multiple criteria.",
380
+ " --manager-tool <tool> Seed an expected manager/worker tool; repeat for multiple tools.",
381
+ " --manager-reference <path> Seed a manager reference path; repeat for multiple references.",
382
+ " --manager-question <text> Seed a manager setup question; repeat for multiple questions.",
383
+ " --manager-guideline <text> Seed a manager guideline; repeat for multiple guidelines.",
384
+ " --cwd <dir> Working directory for both Codex sessions.",
385
+ " --accept-trust Auto-accept the Codex trust prompt for the chosen cwd.",
386
+ " --no-dispatch Do not start Dispatch after launching the pair.",
387
+ " --dry-run Print the launch plan without creating sessions.",
388
+ " --json Emit JSON output.",
389
+ "",
390
+ "Examples:",
391
+ ` ${program} pair --task dogfood --worker-name dogfood-worker --manager-name dogfood-manager --task-goal "Create docs/note.md" --task-prompt "Create docs/note.md" --manager-recipe goalbuddy-conveyor --manager-acceptance "docs/note.md exists" --cwd /tmp/work --path /tmp/work/workerctl.db --accept-trust`,
392
+ ` ${program} pair --task dogfood --worker-name dogfood-worker --manager-name dogfood-manager --path /tmp/work/workerctl.db --dry-run --json`,
393
+ ],
394
+ "worker-ack": [
395
+ `usage: ${program} worker-ack <task> --from-stdin ${path}`,
396
+ `usage: ${program} worker-ack <task> --json ${path}`,
397
+ "",
398
+ "Example JSON:",
399
+ ` {"goal_restatement":"Create docs/dogfood-note.md","proposed_criteria":{"must_have":["note file exists"],"follow_up":[]},"expected_tools":["shell"],"open_questions":[],"ready_to_start":true}`,
400
+ ],
401
+ };
402
+ return linesByCommand[command] ?? [`usage: ${program} ${command} [-h] [options]`];
403
+ }
340
404
  function parseRuntimeArgs(args, env) {
341
405
  const flags = {
342
406
  format: "timeline",
@@ -5394,6 +5458,7 @@ function runStartSessionCommand(parsed, options, role) {
5394
5458
  const initialPrompt = role === "manager"
5395
5459
  ? startManagerBootstrapPrompt(database, {
5396
5460
  cwd,
5461
+ dbPath: runtimeDbPath(parsed, options),
5397
5462
  managerName: name,
5398
5463
  taskGoal: parsed.flags.taskGoal,
5399
5464
  taskName: parsed.flags.taskName,
@@ -5720,7 +5785,7 @@ function runPairCommand(parsed, options) {
5720
5785
  return unsupportedRuntimeResult(parsed, "pair requires --task, --worker-name, and --manager-name.");
5721
5786
  }
5722
5787
  const dbPath = runtimeDbPath(parsed, options);
5723
- const dispatch = pairDispatchPayload(parsed, dbPath);
5788
+ const dispatch = pairDispatchPayload(parsed, dbPath, options);
5724
5789
  const packageRoot = packageRootFromRuntimeModule();
5725
5790
  if (parsed.flags.dryRun) {
5726
5791
  return jsonResult({
@@ -5731,6 +5796,18 @@ function runPairCommand(parsed, options) {
5731
5796
  worker: workerName,
5732
5797
  });
5733
5798
  }
5799
+ const codexPreflight = ensureRequiredTool("codex", options);
5800
+ if (codexPreflight) {
5801
+ return codexPreflight;
5802
+ }
5803
+ const tmuxPreflight = ensureTmuxAvailable(options.tmuxRunner ?? defaultTmuxRunner);
5804
+ if (tmuxPreflight) {
5805
+ return tmuxPreflight;
5806
+ }
5807
+ const tmuxAccessPreflight = ensureTmuxServerAccessible(options.tmuxRunner ?? defaultTmuxRunner);
5808
+ if (tmuxAccessPreflight) {
5809
+ return tmuxAccessPreflight;
5810
+ }
5734
5811
  const cwd = parsed.flags.cwd ?? options.cwd ?? process.cwd();
5735
5812
  const database = openRuntimeDatabase(parsed, options);
5736
5813
  let taskId = null;
@@ -5848,7 +5925,7 @@ function runPairCommand(parsed, options) {
5848
5925
  acceptTrust: parsed.flags.acceptTrust,
5849
5926
  askForApproval: startup.askForApproval,
5850
5927
  cwd,
5851
- initialPrompt: workerAckTaskPrompt(taskName, parsed.flags.taskPrompt),
5928
+ initialPrompt: workerAckTaskPrompt(taskName, parsed.flags.taskPrompt, dbPath),
5852
5929
  name: workerName,
5853
5930
  role: "worker",
5854
5931
  sandbox: startup.sandbox,
@@ -5876,6 +5953,7 @@ function runPairCommand(parsed, options) {
5876
5953
  cwd,
5877
5954
  initialPrompt: startManagerBootstrapPrompt(database, {
5878
5955
  cwd,
5956
+ dbPath,
5879
5957
  managerName,
5880
5958
  taskGoal: task.goal,
5881
5959
  taskName,
@@ -6035,14 +6113,13 @@ function taskRowForPair(database, taskName) {
6035
6113
  `).get(taskName, taskName);
6036
6114
  return row ?? null;
6037
6115
  }
6038
- function pairDispatchPayload(parsed, dbPath) {
6116
+ function pairDispatchPayload(parsed, dbPath, options) {
6039
6117
  const dispatcherId = parsed.flags.dispatcherId;
6040
6118
  const ensureDispatch = dispatcherId !== null && !parsed.flags.noDispatch;
6041
- const packageRoot = packageRootFromRuntimeModule();
6042
6119
  return {
6043
6120
  dispatchCommand: ensureDispatch
6044
6121
  ? [
6045
- join(packageRoot, "scripts", "workerctl"),
6122
+ workerctlDispatchExecutable(options),
6046
6123
  "dispatch",
6047
6124
  "--watch",
6048
6125
  "--dispatcher-id",
@@ -6054,6 +6131,17 @@ function pairDispatchPayload(parsed, dbPath) {
6054
6131
  ensureDispatch,
6055
6132
  };
6056
6133
  }
6134
+ function workerctlDispatchExecutable(options) {
6135
+ const workerctlPath = commandPath("workerctl", options);
6136
+ if (workerctlPath) {
6137
+ return workerctlPath;
6138
+ }
6139
+ const workerctlScript = join(packageRootFromRuntimeModule(), "scripts", "workerctl");
6140
+ if (pathIsExecutable(workerctlScript)) {
6141
+ return workerctlScript;
6142
+ }
6143
+ throw new Error(`Cannot start Dispatch: workerctl is not on PATH and ${workerctlScript} is not executable.`);
6144
+ }
6057
6145
  function emitPairTelemetry(database, options) {
6058
6146
  emitTelemetrySync(database, {
6059
6147
  actor: "workerctl",
@@ -6407,20 +6495,24 @@ function spawnCodexAndRegisterPairSession(database, parsed, options, params) {
6407
6495
  tmux_session: registered.tmux_session,
6408
6496
  };
6409
6497
  }
6410
- function workerAckTaskPrompt(taskName, taskPrompt) {
6498
+ function workerAckTaskPrompt(taskName, taskPrompt, dbPath) {
6411
6499
  if (taskPrompt === null) {
6412
6500
  return null;
6413
6501
  }
6414
6502
  const taskRef = taskName ?? "<task>";
6503
+ const pathSuffix = commandPathSuffix(dbPath);
6415
6504
  return [
6416
6505
  taskPrompt,
6417
6506
  "",
6418
6507
  "Before editing files or running implementation work, acknowledge the task contract:",
6419
6508
  "",
6420
- `conveyor worker-ack ${taskRef} --from-stdin`,
6509
+ `conveyor worker-ack ${taskRef} --from-stdin${pathSuffix}`,
6510
+ "",
6511
+ "Use a JSON object like:",
6421
6512
  "",
6422
- "Use a JSON object with goal_restatement, proposed_criteria, expected_tools,",
6423
- "open_questions, and ready_to_start.",
6513
+ `{"goal_restatement":"Restate the assigned task.","proposed_criteria":{"must_have":["Current-task proof"],"follow_up":[]},"expected_tools":["shell"],"open_questions":[],"ready_to_start":true}`,
6514
+ "",
6515
+ "When your implementation is complete, leave a concise final reply with the files changed and verification you ran. Do not call `conveyor finish-task`; the manager owns criteria satisfaction and audited task closeout.",
6424
6516
  ].join("\n");
6425
6517
  }
6426
6518
  function createPairRunSync(database, options) {
@@ -16660,16 +16752,20 @@ function startManagerBootstrapPrompt(database, options) {
16660
16752
  const goalLine = options.taskGoal ?? context?.goal ?? "No task goal supplied yet.";
16661
16753
  const workerLine = options.workerName ?? "No worker session supplied yet.";
16662
16754
  const workerctl = "conveyor";
16755
+ const pathSuffix = commandPathSuffix(options.dbPath);
16663
16756
  const setupCommand = options.taskName
16664
- ? `${workerctl} manager-config ${taskLine} --questions`
16665
- : `${workerctl} manager-config <task> --questions`;
16666
- const cycleCommand = options.taskName ? `${workerctl} cycle ${taskLine}` : `${workerctl} cycle <task>`;
16757
+ ? `${workerctl} manager-config ${taskLine} --questions${pathSuffix}`
16758
+ : `${workerctl} manager-config <task> --questions${pathSuffix}`;
16759
+ const cycleCommand = options.taskName ? `${workerctl} cycle ${taskLine}${pathSuffix}` : `${workerctl} cycle <task>${pathSuffix}`;
16667
16760
  const managerAckCommand = options.taskName
16668
- ? `${workerctl} manager-ack ${taskLine} --from-stdin`
16669
- : `${workerctl} manager-ack <task> --from-stdin`;
16761
+ ? `${workerctl} manager-ack ${taskLine} --from-stdin${pathSuffix}`
16762
+ : `${workerctl} manager-ack <task> --from-stdin${pathSuffix}`;
16670
16763
  const workerAckCommand = options.taskName
16671
- ? `${workerctl} worker-ack ${taskLine} --json`
16672
- : `${workerctl} worker-ack <task> --json`;
16764
+ ? `${workerctl} worker-ack ${taskLine} --json${pathSuffix}`
16765
+ : `${workerctl} worker-ack <task> --json${pathSuffix}`;
16766
+ const satisfyCriterionCommand = options.taskName
16767
+ ? `${workerctl} criteria ${taskLine} --satisfy <id> --proof "<proof>" --evidence-json '{"status":"pass","command":"<command>","summary":"<what this proved>"}'${pathSuffix}`
16768
+ : `${workerctl} criteria <task> --satisfy <id> --proof "<proof>" --evidence-json '{"status":"pass","command":"<command>","summary":"<what this proved>"}'${pathSuffix}`;
16673
16769
  const config = context ? managerConfigSync(database, context.id) : null;
16674
16770
  const initialSetup = config
16675
16771
  ? seededManagerConfigSetup({ config, cycleCommand, managerAckCommand, workerAckCommand })
@@ -16677,11 +16773,12 @@ function startManagerBootstrapPrompt(database, options) {
16677
16773
  "Initial setup:",
16678
16774
  `1. Run \`${setupCommand}\`.`,
16679
16775
  "2. Ask the user the returned setup questions in this manager Codex chat.",
16680
- `3. Persist the answers with \`${workerctl} manager-config\`.`,
16776
+ `3. Persist the answers with \`${workerctl} manager-config${pathSuffix}\`.`,
16681
16777
  "4. Use `conveyor manager-config --interactive` only when a human is directly running conveyor in a terminal.",
16682
16778
  "",
16683
16779
  "Acknowledgement:",
16684
16780
  `- Before your first cycle, record the supervision contract you are committing to with \`${managerAckCommand}\`.`,
16781
+ ` Example JSON: {"task":"${taskLine}","manager_session":"${options.managerName}","supervision_contract":"I will supervise through Conveyor and verify criteria before finishing.","will_not_edit_project_files":true}`,
16685
16782
  `- Before nudging or finishing, inspect the worker acknowledgement with \`${workerAckCommand}\` when available.`,
16686
16783
  ].join("\n");
16687
16784
  return [
@@ -16704,7 +16801,8 @@ function startManagerBootstrapPrompt(database, options) {
16704
16801
  "- Inspect `manager_context.acceptance_criteria` each cycle.",
16705
16802
  "- If worker progress reveals new edge cases, tests, polish, or scope boundaries, ask the worker to propose must-have vs follow-up criteria.",
16706
16803
  "- Before finishing, compare worker receipts/verification against accepted open criteria.",
16707
- `- When all accepted criteria are satisfied, deferred, or rejected, finish the task with \`${workerctl} finish-task ${taskLine} --reason "Accepted criteria satisfied" --require-criteria-audit\`.`,
16804
+ `- For each accepted criterion that is proven, record evidence with \`${satisfyCriterionCommand}\`.`,
16805
+ `- When all accepted criteria are satisfied, deferred, or rejected, finish the task with \`${workerctl} finish-task ${taskLine} --reason "Accepted criteria satisfied" --require-criteria-audit${pathSuffix}\`.`,
16708
16806
  "- Communicate with the worker only through conveyor session/task commands.",
16709
16807
  "- Do not edit project files unless the user explicitly asks this manager session to change Agent Conveyor itself.",
16710
16808
  ].join("\n");
@@ -16719,7 +16817,7 @@ function seededManagerConfigSetup(options) {
16719
16817
  if (options.config.tools.length > 0) {
16720
16818
  lines.push(`Expected tools: ${options.config.tools.join(", ")}.`);
16721
16819
  }
16722
- lines.push("", "Acknowledgement:", `- Before your first cycle, record the supervision contract you are committing to with \`${options.managerAckCommand}\`.`, `- Before nudging or finishing, inspect the worker acknowledgement with \`${options.workerAckCommand}\` when available.`);
16820
+ lines.push("", "Acknowledgement:", `- Before your first cycle, record the supervision contract you are committing to with \`${options.managerAckCommand}\`.`, ` Example JSON: {"task":"${options.config.task_id}","supervision_contract":"I will supervise through Conveyor and verify criteria before finishing.","will_not_edit_project_files":true}`, `- Before nudging or finishing, inspect the worker acknowledgement with \`${options.workerAckCommand}\` when available.`);
16723
16821
  return lines.join("\n");
16724
16822
  }
16725
16823
  function startManagerTaskContext(database, taskName) {
@@ -16733,6 +16831,9 @@ function startManagerTaskContext(database, taskName) {
16733
16831
  function shellQuote(value) {
16734
16832
  return `'${value.replace(/'/g, "'\"'\"'")}'`;
16735
16833
  }
16834
+ function commandPathSuffix(dbPath) {
16835
+ return dbPath ? ` --path ${shellQuote(dbPath)}` : "";
16836
+ }
16736
16837
  function emitTelemetrySync(database, options) {
16737
16838
  const eventId = `telemetry-${randomUUID()}`;
16738
16839
  const attributesJson = stableJson(options.attributes);
@@ -18191,9 +18292,8 @@ The supported manager/worker setup is session-based:
18191
18292
 
18192
18293
  ${workerctl} worker-ack <task-name> --from-stdin
18193
18294
 
18194
- The JSON should include goal_restatement, proposed_criteria,
18195
- expected_tools, open_questions, and ready_to_start. Proposed criteria should
18196
- separate must-have and follow-up criteria.
18295
+ Example JSON:
18296
+ {"goal_restatement":"Restate the assigned task.","proposed_criteria":{"must_have":["Current-task proof"],"follow_up":[]},"expected_tools":["shell"],"open_questions":[],"ready_to_start":true}
18197
18297
 
18198
18298
  Required fields:
18199
18299
  - worker name
@@ -18280,6 +18380,14 @@ function ensureTmuxAvailable(runner) {
18280
18380
  const detail = (result.stderr || result.stdout || `exit code ${result.status}`).trim();
18281
18381
  return lifecycleWorkerErrorResult(tmuxCommandFailureMessage(["tmux", "-V"], detail));
18282
18382
  }
18383
+ function ensureTmuxServerAccessible(runner) {
18384
+ const result = runner(["tmux", "start-server"], { check: false });
18385
+ if (result.status === 0) {
18386
+ return null;
18387
+ }
18388
+ const detail = (result.stderr || result.stdout || `exit code ${result.status}`).trim();
18389
+ return lifecycleWorkerErrorResult(tmuxCommandFailureMessage(["tmux", "start-server"], detail));
18390
+ }
18283
18391
  const CONTENT_KEYS = new Set(["content", "message", "output", "segment_text", "text"]);
18284
18392
  function redactPayload(value) {
18285
18393
  if (Array.isArray(value)) {