agentxchain 2.116.0 → 2.118.0

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/README.md CHANGED
@@ -202,7 +202,7 @@ Partial coordinator artifacts are first-class here too: `audit` and `report` kee
202
202
  | `multi init\|status\|step\|resume\|approve-gate\|resync` | Run the multi-repo coordinator lifecycle, including blocked-state recovery via `multi resume` |
203
203
  | `intake record\|triage\|approve\|plan\|start\|scan\|resolve` | Continuous-delivery intake: turn delivery signals into governed work items |
204
204
  | `intake handoff` | Bridge a planned intake intent to a coordinator workstream for multi-repo execution |
205
- | `schedule list\|run-due\|daemon\|status` | Run repo-local lights-out scheduling: inspect schedules, execute due runs, poll in a local daemon loop, or check daemon heartbeat |
205
+ | `schedule list\|run-due\|daemon\|status` | Run repo-local lights-out scheduling: inspect schedules, execute due runs, poll in a local daemon loop, continue explicitly unblocked schedule-owned runs, or check daemon heartbeat |
206
206
  | `plugin install\|list\|remove` | Install, inspect, or remove governed hook plugins under `.agentxchain/plugins/` |
207
207
  | `plugin list-available` | List bundled built-in plugins installable by short name |
208
208
  | `export [--output <path>]` | Export the portable raw governed/coordinator artifact for continuity or offline review |
@@ -67,6 +67,8 @@ import { rebindCommand } from '../src/commands/rebind.js';
67
67
  import { branchCommand } from '../src/commands/branch.js';
68
68
  import { migrateCommand } from '../src/commands/migrate.js';
69
69
  import { resumeCommand } from '../src/commands/resume.js';
70
+ import { unblockCommand } from '../src/commands/unblock.js';
71
+ import { injectCommand } from '../src/commands/inject.js';
70
72
  import { escalateCommand } from '../src/commands/escalate.js';
71
73
  import { acceptTurnCommand } from '../src/commands/accept-turn.js';
72
74
  import { rejectTurnCommand } from '../src/commands/reject-turn.js';
@@ -600,6 +602,23 @@ program
600
602
  .option('--turn <id>', 'Target a specific retained turn when multiple exist')
601
603
  .action(resumeCommand);
602
604
 
605
+ program
606
+ .command('unblock <escalation-id>')
607
+ .description('Resolve the current human escalation record and continue the governed run')
608
+ .action(unblockCommand);
609
+
610
+ program
611
+ .command('inject <description>')
612
+ .description('Inject a priority work item into the intake queue (composed record + triage + approve)')
613
+ .option('--priority <level>', 'Priority level (p0, p1, p2, p3)', 'p0')
614
+ .option('--template <id>', 'Governed template (generic, api-service, cli-tool, library, web-app, enterprise-app)', 'generic')
615
+ .option('--charter <text>', 'Delivery charter (defaults to description)')
616
+ .option('--acceptance <text>', 'Comma-separated acceptance criteria')
617
+ .option('--approver <name>', 'Approver identity', 'human')
618
+ .option('--no-approve', 'Stop at triaged state instead of auto-approving')
619
+ .option('-j, --json', 'Output as JSON')
620
+ .action(injectCommand);
621
+
603
622
  program
604
623
  .command('escalate')
605
624
  .description('Raise an operator escalation and block the governed run intentionally')
@@ -652,6 +671,11 @@ program
652
671
  .option('--chain-on <statuses>', 'Comma-separated terminal statuses that trigger chaining (default: completed)')
653
672
  .option('--chain-cooldown <seconds>', 'Seconds to wait between chained runs (default: 5)', parseInt)
654
673
  .option('--mission <mission_id>', 'Bind chained runs to a mission (use "latest" for most recent mission)')
674
+ .option('--continuous', 'Enable continuous vision-driven loop: derive work from VISION.md and run until satisfied')
675
+ .option('--vision <path>', 'Path to VISION.md (project-relative or absolute, default: .planning/VISION.md)')
676
+ .option('--max-runs <n>', 'Maximum consecutive governed runs in continuous mode (default: 100)', parseInt)
677
+ .option('--poll-seconds <n>', 'Seconds between idle-detection cycles in continuous mode (default: 30)', parseInt)
678
+ .option('--max-idle-cycles <n>', 'Stop after N consecutive idle cycles with no derivable work (default: 3)', parseInt)
655
679
  .action(runCommand);
656
680
 
657
681
  program
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.116.0",
3
+ "version": "2.118.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -70,7 +70,7 @@ function extractAggregateEvidenceLine(text) {
70
70
  return best;
71
71
  }, null);
72
72
 
73
- return aggregate.line.replace(/\*\*/g, '').replace(/`/g, '').trim();
73
+ return aggregate.line.replace(/\*\*/g, '').replace(/`/g, '').replace(/,/g, '').trim();
74
74
  }
75
75
 
76
76
  function getPreviousVersionTag(repoRoot, version) {
@@ -73,7 +73,12 @@ function printEvent(evt) {
73
73
  const gateFailedDetail = evt.event_type === 'gate_failed' && evt.payload?.from_phase
74
74
  ? ` ${evt.payload.from_phase} → ${evt.payload.to_phase || '?'}${evt.payload.reasons?.length ? ` — ${evt.payload.reasons[0]}` : ''}${evt.payload.gate_id ? ` (${evt.payload.gate_id})` : ''}`
75
75
  : '';
76
- console.log(`${chalk.dim(ts)} ${type} ${chalk.cyan(runId)} ${phase}${turnInfo}${conflictDetail}${rejectionDetail}${phaseTransitionDetail}${gateFailedDetail}`);
76
+ const humanEscalationDetail = evt.event_type === 'human_escalation_raised' && evt.payload?.escalation_id
77
+ ? ` ${evt.payload.escalation_id} [${evt.payload.type || '?'}]${evt.payload.service ? ` (${evt.payload.service})` : ''}`
78
+ : evt.event_type === 'human_escalation_resolved' && evt.payload?.escalation_id
79
+ ? ` ${evt.payload.escalation_id} via ${evt.payload.resolved_via || '?'}`
80
+ : '';
81
+ console.log(`${chalk.dim(ts)} ${type} ${chalk.cyan(runId)} ${phase}${turnInfo}${conflictDetail}${rejectionDetail}${phaseTransitionDetail}${gateFailedDetail}${humanEscalationDetail}`);
77
82
  }
78
83
 
79
84
  function formatConflictDetail(evt) {
@@ -115,6 +120,8 @@ function colorEventType(type) {
115
120
  phase_entered: chalk.magenta,
116
121
  escalation_raised: chalk.red.bold,
117
122
  escalation_resolved: chalk.green,
123
+ human_escalation_raised: chalk.red.bold,
124
+ human_escalation_resolved: chalk.green,
118
125
  gate_pending: chalk.yellow,
119
126
  gate_approved: chalk.green,
120
127
  gate_failed: chalk.red,
@@ -0,0 +1,81 @@
1
+ import chalk from 'chalk';
2
+ import { loadProjectContext } from '../lib/config.js';
3
+ import { injectIntent } from '../lib/intake.js';
4
+
5
+ export async function injectCommand(description, opts) {
6
+ const context = loadProjectContext();
7
+ if (!context) {
8
+ console.log(chalk.red('No agentxchain.json found. Run `agentxchain init` first.'));
9
+ process.exit(1);
10
+ }
11
+
12
+ const { root, config } = context;
13
+
14
+ if (config.protocol_mode !== 'governed') {
15
+ console.log(chalk.red('The inject command is only available for governed projects.'));
16
+ process.exit(1);
17
+ }
18
+
19
+ if (!description || !String(description).trim()) {
20
+ console.log(chalk.red('A description is required. Example: agentxchain inject "Fix the sidebar ordering"'));
21
+ process.exit(1);
22
+ }
23
+
24
+ const result = injectIntent(root, String(description).trim(), {
25
+ priority: opts.priority,
26
+ template: opts.template,
27
+ charter: opts.charter,
28
+ acceptance: opts.acceptance,
29
+ approver: opts.approver,
30
+ noApprove: opts.approve === false,
31
+ });
32
+
33
+ if (!result.ok) {
34
+ if (opts.json) {
35
+ console.log(JSON.stringify({ ok: false, error: result.error }, null, 2));
36
+ } else {
37
+ console.log(chalk.red(result.error));
38
+ }
39
+ process.exit(result.exitCode || 1);
40
+ }
41
+
42
+ if (opts.json) {
43
+ console.log(JSON.stringify({
44
+ ok: true,
45
+ intent_id: result.intent.intent_id,
46
+ event_id: result.event.event_id,
47
+ status: result.intent.status,
48
+ priority: result.intent.priority,
49
+ deduplicated: result.deduplicated,
50
+ preemption_marker: result.preemption_marker,
51
+ }, null, 2));
52
+ return;
53
+ }
54
+
55
+ console.log('');
56
+ if (result.deduplicated) {
57
+ console.log(chalk.yellow(' ⚠ Duplicate injection — existing intent returned'));
58
+ console.log(chalk.dim(` Intent: ${result.intent?.intent_id || 'unknown'}`));
59
+ console.log(chalk.dim(` Status: ${result.intent?.status || 'unknown'}`));
60
+ console.log('');
61
+ return;
62
+ }
63
+
64
+ const priority = result.intent.priority || 'p0';
65
+ const priorityColor = priority === 'p0' ? chalk.red.bold : priority === 'p1' ? chalk.yellow.bold : chalk.dim;
66
+
67
+ console.log(chalk.green.bold(' Injected'));
68
+ console.log(chalk.dim(` Intent: ${result.intent.intent_id}`));
69
+ console.log(` Priority: ${priorityColor(priority)}`);
70
+ console.log(chalk.dim(` Status: ${result.intent.status}`));
71
+ console.log(chalk.dim(` Charter: ${result.intent.charter || description}`));
72
+
73
+ if (result.preemption_marker) {
74
+ console.log('');
75
+ console.log(chalk.red.bold(' ⚡ Preemption marker written'));
76
+ console.log(chalk.dim(' The current run will yield after the active turn completes.'));
77
+ console.log(chalk.dim(' The scheduler/continuous loop will pick up this intent next.'));
78
+ }
79
+
80
+ console.log('');
81
+ }
@@ -77,6 +77,8 @@ export async function resumeCommand(opts) {
77
77
  // §47: active + turns present → reject (resume assigns new turns, not re-dispatches)
78
78
  const activeCount = getActiveTurnCount(state);
79
79
  const activeTurns = getActiveTurns(state);
80
+ const resumeVia = opts?._via || 'resume';
81
+ const turnResumeVia = opts?._via || 'resume --turn';
80
82
 
81
83
  if (state.status === 'active' && activeCount > 0) {
82
84
  if (activeCount === 1) {
@@ -135,7 +137,7 @@ export async function resumeCommand(opts) {
135
137
  console.log(` Attempt: ${retainedTurn.attempt}`);
136
138
  console.log('');
137
139
 
138
- const reactivated = reactivateGovernedRun(root, state, { via: 'resume --turn', notificationConfig: config });
140
+ const reactivated = reactivateGovernedRun(root, state, { via: turnResumeVia, notificationConfig: config });
139
141
  if (!reactivated.ok) {
140
142
  console.log(chalk.red(`Failed to reactivate run: ${reactivated.error}`));
141
143
  process.exit(1);
@@ -195,7 +197,7 @@ export async function resumeCommand(opts) {
195
197
  console.log(` Attempt: ${retainedTurn.attempt}`);
196
198
  console.log('');
197
199
 
198
- const reactivated = reactivateGovernedRun(root, state, { via: 'resume --turn', notificationConfig: config });
200
+ const reactivated = reactivateGovernedRun(root, state, { via: turnResumeVia, notificationConfig: config });
199
201
  if (!reactivated.ok) {
200
202
  console.log(chalk.red(`Failed to reactivate blocked run: ${reactivated.error}`));
201
203
  process.exit(1);
@@ -234,7 +236,7 @@ export async function resumeCommand(opts) {
234
236
 
235
237
  // §47: paused + run_id exists → resume same run
236
238
  if (state.status === 'blocked' && state.run_id) {
237
- const reactivated = reactivateGovernedRun(root, state, { via: 'resume', notificationConfig: config });
239
+ const reactivated = reactivateGovernedRun(root, state, { via: resumeVia, notificationConfig: config });
238
240
  if (!reactivated.ok) {
239
241
  console.log(chalk.red(`Failed to reactivate blocked run: ${reactivated.error}`));
240
242
  process.exit(1);
@@ -245,7 +247,7 @@ export async function resumeCommand(opts) {
245
247
 
246
248
  // §47: paused + run_id exists → resume same run
247
249
  if (state.status === 'paused' && state.run_id) {
248
- const reactivated = reactivateGovernedRun(root, state, { via: 'resume', notificationConfig: config });
250
+ const reactivated = reactivateGovernedRun(root, state, { via: resumeVia, notificationConfig: config });
249
251
  if (!reactivated.ok) {
250
252
  console.log(chalk.red(`Failed to reactivate run: ${reactivated.error}`));
251
253
  process.exit(1);
@@ -45,6 +45,7 @@ import {
45
45
  getTurnStagingResultPath,
46
46
  } from '../lib/turn-paths.js';
47
47
  import { resolveChainOptions, executeChainedRun } from '../lib/run-chain.js';
48
+ import { resolveContinuousOptions, executeContinuousRun } from '../lib/continuous-run.js';
48
49
 
49
50
  export async function runCommand(opts) {
50
51
  const context = loadProjectContext();
@@ -53,6 +54,18 @@ export async function runCommand(opts) {
53
54
  process.exit(1);
54
55
  }
55
56
 
57
+ // Continuous vision-driven mode
58
+ const contOpts = resolveContinuousOptions(opts, context.config);
59
+ if (contOpts.enabled) {
60
+ console.log(chalk.cyan.bold('agentxchain run --continuous'));
61
+ console.log(chalk.dim(` Vision: ${contOpts.visionPath}`));
62
+ console.log(chalk.dim(` Max runs: ${contOpts.maxRuns}, Poll: ${contOpts.pollSeconds}s, Idle limit: ${contOpts.maxIdleCycles}`));
63
+ console.log(chalk.dim(` Triage approval: ${contOpts.triageApproval}`));
64
+ console.log('');
65
+ const { exitCode } = await executeContinuousRun(context, contOpts, executeGovernedRun);
66
+ process.exit(exitCode);
67
+ }
68
+
56
69
  const chainOpts = resolveChainOptions(opts, context.config);
57
70
  if (chainOpts.enabled) {
58
71
  console.log(chalk.cyan.bold('agentxchain run --chain'));