agentxchain 2.157.0 → 2.159.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/bin/agentxchain.js +24 -0
- package/package.json +1 -1
- package/src/commands/attention.js +66 -0
- package/src/commands/role.js +69 -1
- package/src/commands/ship-status.js +104 -0
- package/src/lib/dispatch-bundle.js +12 -0
- package/src/lib/human-attention.js +381 -0
- package/src/lib/report.js +17 -0
- package/src/lib/role-charter.js +294 -0
- package/src/lib/ship-status.js +545 -0
- package/src/lib/turn-result-validator.js +42 -6
package/bin/agentxchain.js
CHANGED
|
@@ -124,6 +124,8 @@ import { benchmarkCommand } from '../src/commands/benchmark.js';
|
|
|
124
124
|
import { benchmarkWorkloadsCommand } from '../src/commands/benchmark-workloads.js';
|
|
125
125
|
import { historyCommand } from '../src/commands/history.js';
|
|
126
126
|
import { decisionsCommand } from '../src/commands/decisions.js';
|
|
127
|
+
import { shipStatusCommand } from '../src/commands/ship-status.js';
|
|
128
|
+
import { attentionCommand } from '../src/commands/attention.js';
|
|
127
129
|
import { diffCommand } from '../src/commands/diff.js';
|
|
128
130
|
import { eventsCommand } from '../src/commands/events.js';
|
|
129
131
|
import { connectorCapabilitiesCommand, connectorCheckCommand, connectorValidateCommand } from '../src/commands/connector.js';
|
|
@@ -420,6 +422,22 @@ program
|
|
|
420
422
|
.option('-d, --dir <path>', 'Project directory')
|
|
421
423
|
.action(decisionsCommand);
|
|
422
424
|
|
|
425
|
+
program
|
|
426
|
+
.command('ship-status')
|
|
427
|
+
.description('Evaluate whether the current project is ready to ship')
|
|
428
|
+
.option('-j, --json', 'Machine-readable JSON output')
|
|
429
|
+
.option('-v, --verbose', 'Per-dimension detail breakdown')
|
|
430
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
431
|
+
.action(shipStatusCommand);
|
|
432
|
+
|
|
433
|
+
program
|
|
434
|
+
.command('attention')
|
|
435
|
+
.description('Show the cross-run human-decision queue — what needs you, and nothing else (govern by exception)')
|
|
436
|
+
.option('-j, --json', 'Machine-readable JSON output (full HumanAttentionReport)')
|
|
437
|
+
.option('-a, --all', 'Include informational/non-blocking items (e.g. pending approved intents)')
|
|
438
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
439
|
+
.action(attentionCommand);
|
|
440
|
+
|
|
423
441
|
program
|
|
424
442
|
.command('diff <left_ref> <right_ref>')
|
|
425
443
|
.description('Compare two recorded governed runs or two export artifacts')
|
|
@@ -953,6 +971,12 @@ roleCmd
|
|
|
953
971
|
.option('-j, --json', 'Output as JSON')
|
|
954
972
|
.action((roleId, opts) => roleCommand('show', roleId, opts));
|
|
955
973
|
|
|
974
|
+
roleCmd
|
|
975
|
+
.command('validate [role_id]')
|
|
976
|
+
.description('Validate role charter well-formedness against the four VISION:123–128 invariants (exit 1 if any role incomplete)')
|
|
977
|
+
.option('-j, --json', 'Output as JSON')
|
|
978
|
+
.action((roleId, opts) => roleCommand('validate', roleId, opts));
|
|
979
|
+
|
|
956
980
|
const turnCmd = program
|
|
957
981
|
.command('turn')
|
|
958
982
|
.description('Inspect active governed turn dispatch bundles');
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: agentxchain attention
|
|
3
|
+
*
|
|
4
|
+
* Answers "what needs MY attention right now — and nothing else?" by composing the
|
|
5
|
+
* cross-category human-decision queue into a single HumanAttentionReport. All evaluation
|
|
6
|
+
* logic lives in lib/human-attention.js — this command is presentation only
|
|
7
|
+
* (Architecture Invariant #6).
|
|
8
|
+
*
|
|
9
|
+
* It is a status surface, not a gate: it exits 0 in BOTH the clear and attention states.
|
|
10
|
+
* When the queue is empty it prints "Nothing needs your attention." — the operational
|
|
11
|
+
* proof that the human can step back and let governed autonomy run (VISION.md:51).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { resolve } from 'node:path';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { findProjectRoot } from '../lib/config.js';
|
|
17
|
+
import { evaluateHumanAttention } from '../lib/human-attention.js';
|
|
18
|
+
|
|
19
|
+
export async function attentionCommand(opts) {
|
|
20
|
+
const dir = opts.dir ? resolve(opts.dir) : process.cwd();
|
|
21
|
+
const root = findProjectRoot(dir);
|
|
22
|
+
if (!root) {
|
|
23
|
+
console.error(chalk.red('No agentxchain project found. Run "agentxchain init" first.'));
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const report = evaluateHumanAttention(root);
|
|
29
|
+
|
|
30
|
+
if (opts.json) {
|
|
31
|
+
// --json always emits the full, schema-valid HumanAttentionReport; exit 0 in both states.
|
|
32
|
+
console.log(JSON.stringify(report, null, 2));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (report.overall === 'clear') {
|
|
37
|
+
console.log(chalk.green('Nothing needs your attention.'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default view surfaces blocking + escalation items first; informational pending-intent
|
|
42
|
+
// items are summarized as a count unless --all is passed.
|
|
43
|
+
const visible = opts.all ? report.items : report.items.filter((item) => item.blocking);
|
|
44
|
+
const hidden = report.items.length - visible.length;
|
|
45
|
+
|
|
46
|
+
console.log(
|
|
47
|
+
`\n${chalk.bold('Attention needed')}: ${report.items_count} `
|
|
48
|
+
+ `item${report.items_count === 1 ? '' : 's'} (${report.blocking_count} blocking)`,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
visible.forEach((item, idx) => {
|
|
52
|
+
const tag = chalk.cyan(String(item.category).padEnd(16));
|
|
53
|
+
const run = item.run_id ? `${chalk.dim(item.run_id)} ` : '';
|
|
54
|
+
console.log(` [${idx + 1}] ${tag} ${run}${item.summary}`);
|
|
55
|
+
console.log(` ${chalk.dim('→')} ${item.action_hint}`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (hidden > 0) {
|
|
59
|
+
console.log(
|
|
60
|
+
chalk.dim(` (+${hidden} informational item${hidden === 1 ? '' : 's'} — run with --all to show)`),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('');
|
|
65
|
+
// Status surface, not a gate — no non-zero exit even in the attention state.
|
|
66
|
+
}
|
package/src/commands/role.js
CHANGED
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
getRoleRuntimeCapabilityContract,
|
|
5
5
|
summarizeRuntimeCapabilityContract,
|
|
6
6
|
} from '../lib/runtime-capabilities.js';
|
|
7
|
+
import {
|
|
8
|
+
evaluateRoleCharter,
|
|
9
|
+
evaluateAllRoleCharters,
|
|
10
|
+
} from '../lib/role-charter.js';
|
|
7
11
|
|
|
8
12
|
export function roleCommand(subcommand, roleId, opts) {
|
|
9
13
|
const context = loadProjectContext();
|
|
@@ -12,7 +16,7 @@ export function roleCommand(subcommand, roleId, opts) {
|
|
|
12
16
|
process.exit(1);
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
const { config, version } = context;
|
|
19
|
+
const { config, rawConfig, version } = context;
|
|
16
20
|
|
|
17
21
|
if (version !== 4) {
|
|
18
22
|
console.log(chalk.red(' Not a governed AgentXchain project (requires v4 config).'));
|
|
@@ -27,6 +31,10 @@ export function roleCommand(subcommand, roleId, opts) {
|
|
|
27
31
|
return showRole(roleId, roles, runtimes, roleIds, opts);
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
if (subcommand === 'validate') {
|
|
35
|
+
return validateRoles(roleId, config, rawConfig, roleIds, opts);
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
// Default: list
|
|
31
39
|
return listRoles(roles, runtimes, roleIds, opts);
|
|
32
40
|
}
|
|
@@ -155,3 +163,63 @@ function showRole(roleId, roles, runtimes, roleIds, opts) {
|
|
|
155
163
|
}
|
|
156
164
|
console.log('');
|
|
157
165
|
}
|
|
166
|
+
|
|
167
|
+
function renderCharterReport(report) {
|
|
168
|
+
const mark = report.overall === 'well_formed' ? chalk.green('✓') : chalk.red('✗');
|
|
169
|
+
if (report.overall === 'well_formed') {
|
|
170
|
+
console.log(` ${mark} ${chalk.cyan(report.role_id)} well-formed (4/4 invariants)`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.log(` ${mark} ${chalk.cyan(report.role_id)} incomplete — missing: ${report.missing.join(', ')}`);
|
|
174
|
+
for (const inv of report.invariants) {
|
|
175
|
+
if (!inv.satisfied && inv.fix_hint) {
|
|
176
|
+
console.log(chalk.dim(` → ${inv.fix_hint}`));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function validateRoles(roleId, config, rawConfig, roleIds, opts) {
|
|
182
|
+
// Single-role validation
|
|
183
|
+
if (roleId) {
|
|
184
|
+
if (!config.roles?.[roleId]) {
|
|
185
|
+
console.log(chalk.red(` Unknown role: ${roleId}`));
|
|
186
|
+
console.log(chalk.dim(` Available: ${roleIds.join(', ')}`));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
const report = evaluateRoleCharter(config, rawConfig, roleId);
|
|
190
|
+
if (opts.json) {
|
|
191
|
+
console.log(JSON.stringify(report, null, 2));
|
|
192
|
+
} else {
|
|
193
|
+
console.log('');
|
|
194
|
+
renderCharterReport(report);
|
|
195
|
+
console.log('');
|
|
196
|
+
}
|
|
197
|
+
process.exit(report.overall === 'well_formed' ? 0 : 1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// All-roles validation
|
|
201
|
+
const all = evaluateAllRoleCharters(config, rawConfig);
|
|
202
|
+
if (opts.json) {
|
|
203
|
+
console.log(JSON.stringify(all, null, 2));
|
|
204
|
+
process.exit(all.incomplete === 0 ? 0 : 1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (all.total === 0) {
|
|
208
|
+
console.log(' No roles defined.');
|
|
209
|
+
process.exit(0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (all.incomplete === 0) {
|
|
213
|
+
console.log(chalk.bold(`\n Role charter validation (${all.total} roles): all well-formed (4/4 invariants each).\n`));
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(chalk.bold(`\n Role charter validation (${all.total} roles):\n`));
|
|
218
|
+
for (const report of all.roles) {
|
|
219
|
+
renderCharterReport(report);
|
|
220
|
+
}
|
|
221
|
+
console.log('');
|
|
222
|
+
console.log(` ${all.incomplete} of ${all.total} roles incomplete.`);
|
|
223
|
+
console.log('');
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: agentxchain ship-status
|
|
3
|
+
*
|
|
4
|
+
* Answers "is this ready to ship?" by composing five evidence dimensions into a
|
|
5
|
+
* single ShipStatusReport. All evaluation logic lives in lib/ship-status.js — this
|
|
6
|
+
* command is presentation only (Architecture Invariant #5).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { resolve, join } from 'node:path';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { findProjectRoot } from '../lib/config.js';
|
|
13
|
+
import { COORDINATOR_CONFIG_FILE } from '../lib/coordinator-config.js';
|
|
14
|
+
import { evaluateShipStatus, evaluateCoordinatorShipStatus } from '../lib/ship-status.js';
|
|
15
|
+
|
|
16
|
+
function statusIcon(status) {
|
|
17
|
+
if (status === 'pass') return chalk.green('✓');
|
|
18
|
+
if (status === 'fail') return chalk.red('✗');
|
|
19
|
+
return chalk.yellow('?');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function overallLabel(overall) {
|
|
23
|
+
if (overall === 'pass') return chalk.green.bold('YES');
|
|
24
|
+
if (overall === 'fail') return chalk.red.bold('NO');
|
|
25
|
+
return chalk.yellow.bold('PENDING');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function countPassed(report) {
|
|
29
|
+
return (report.dimensions || []).filter((d) => d.status === 'pass').length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function printDimensions(dimensions, indent) {
|
|
33
|
+
for (const dim of dimensions || []) {
|
|
34
|
+
console.log(`${indent}${statusIcon(dim.status)} ${dim.name}: ${dim.detail}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function printSingle(report, opts) {
|
|
39
|
+
const passed = countPassed(report);
|
|
40
|
+
const total = (report.dimensions || []).length;
|
|
41
|
+
const blocking = report.blocking_reasons || [];
|
|
42
|
+
const suffix = blocking.length ? `, ${blocking.length} blocking` : '';
|
|
43
|
+
console.log(`\nShip Status: ${overallLabel(report.overall)} (${passed}/${total} dimensions pass${suffix})`);
|
|
44
|
+
|
|
45
|
+
if (opts.verbose) {
|
|
46
|
+
console.log('');
|
|
47
|
+
printDimensions(report.dimensions, ' ');
|
|
48
|
+
} else {
|
|
49
|
+
for (const reason of blocking) {
|
|
50
|
+
console.log(` ${chalk.dim('-')} ${reason}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
console.log('');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function shipStatusCommand(opts) {
|
|
57
|
+
const dir = opts.dir ? resolve(opts.dir) : process.cwd();
|
|
58
|
+
const coordinatorConfigPath = join(dir, COORDINATOR_CONFIG_FILE);
|
|
59
|
+
|
|
60
|
+
let report;
|
|
61
|
+
let isCoordinator = false;
|
|
62
|
+
|
|
63
|
+
if (existsSync(coordinatorConfigPath)) {
|
|
64
|
+
isCoordinator = true;
|
|
65
|
+
report = evaluateCoordinatorShipStatus(dir);
|
|
66
|
+
} else {
|
|
67
|
+
const root = findProjectRoot(dir);
|
|
68
|
+
if (!root) {
|
|
69
|
+
console.error(chalk.red('No agentxchain project found. Run "agentxchain init" first.'));
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
report = evaluateShipStatus(root);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (opts.json) {
|
|
77
|
+
console.log(JSON.stringify(report, null, 2));
|
|
78
|
+
} else if (isCoordinator) {
|
|
79
|
+
const blocking = report.blocking_repos || [];
|
|
80
|
+
const total = (report.repos || []).length;
|
|
81
|
+
const shippable = total - blocking.length;
|
|
82
|
+
console.log(`\nCoordinator Ship Status: ${overallLabel(report.overall)} (${shippable}/${total} repos shippable)`);
|
|
83
|
+
for (const entry of report.repos || []) {
|
|
84
|
+
const passed = countPassed(entry.ship_status);
|
|
85
|
+
const dimTotal = (entry.ship_status.dimensions || []).length;
|
|
86
|
+
console.log(`\n ${chalk.bold(entry.repo_id)}: ${overallLabel(entry.ship_status.overall)} (${passed}/${dimTotal})`);
|
|
87
|
+
if (opts.verbose) {
|
|
88
|
+
printDimensions(entry.ship_status.dimensions, ' ');
|
|
89
|
+
} else {
|
|
90
|
+
for (const reason of entry.ship_status.blocking_reasons || []) {
|
|
91
|
+
console.log(` ${chalk.dim('-')} ${reason}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log('');
|
|
96
|
+
} else {
|
|
97
|
+
printSingle(report, opts);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Non-zero exit when not shippable (useful for CI gating).
|
|
101
|
+
if (report.overall === 'fail') {
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -471,6 +471,18 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
471
471
|
lines.push('');
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
+
// Single-shot execution model — prevents ghost turns from async / background-wait patterns.
|
|
475
|
+
lines.push('## Single-Shot Execution (read carefully)');
|
|
476
|
+
lines.push('');
|
|
477
|
+
lines.push('You run as a **single, non-interactive `--print` subprocess**. You get exactly ONE invocation with no "later": there is no async wake-up, no second message, and nothing will notify you after you stop responding. The moment you end your turn the subprocess exits — if you have not written your turn result by then, the entire turn is **discarded as a ghost** (no result, wasted run).');
|
|
478
|
+
lines.push('');
|
|
479
|
+
lines.push('Therefore:');
|
|
480
|
+
lines.push('- Do ALL work **synchronously** within this one turn. Run every command to completion in the foreground and read its output inline before continuing.');
|
|
481
|
+
lines.push('- **NEVER** start a background job and then wait for it. Do not use `ScheduleWakeup`; do not start a `Monitor` or `tail -f` and wait for events; do not background a process with `&` and poll for a notification; do not defer any conclusion to "after the suite/build finishes". You will not be re-invoked to finish.');
|
|
482
|
+
lines.push('- If you need a long command or test suite result, run it **blocking in the foreground** (e.g. `npm test`) and wait for it to return in-line — the per-turn timeout is generous, so budget your time for it.');
|
|
483
|
+
lines.push('- Writing the structured turn result below is your **final action**: gather every piece of evidence first, then write the result before you finish.');
|
|
484
|
+
lines.push('');
|
|
485
|
+
|
|
474
486
|
// Output format with complete JSON template
|
|
475
487
|
lines.push('## Required Output');
|
|
476
488
|
lines.push('');
|