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 +1 -1
- package/bin/agentxchain.js +24 -0
- package/package.json +1 -1
- package/scripts/render-github-release-body.mjs +1 -1
- package/src/commands/events.js +8 -1
- package/src/commands/inject.js +81 -0
- package/src/commands/resume.js +6 -4
- package/src/commands/run.js +13 -0
- package/src/commands/schedule.js +386 -19
- package/src/commands/status.js +55 -0
- package/src/commands/unblock.js +67 -0
- package/src/lib/continuous-run.js +499 -0
- package/src/lib/governed-state.js +37 -1
- package/src/lib/human-escalations.js +434 -0
- package/src/lib/intake.js +243 -11
- package/src/lib/normalized-config.js +37 -0
- package/src/lib/notification-runner.js +3 -1
- package/src/lib/run-events.js +2 -0
- package/src/lib/run-loop.js +17 -0
- package/src/lib/run-provenance.js +4 -0
- package/src/lib/run-schedule.js +45 -0
- package/src/lib/vision-reader.js +229 -0
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 |
|
package/bin/agentxchain.js
CHANGED
|
@@ -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
|
@@ -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) {
|
package/src/commands/events.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/src/commands/resume.js
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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);
|
package/src/commands/run.js
CHANGED
|
@@ -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'));
|