agentxchain 2.100.0 → 2.102.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 +28 -1
- package/package.json +1 -1
- package/src/commands/benchmark-workloads.js +206 -0
- package/src/commands/benchmark.js +775 -0
- package/src/commands/doctor.js +17 -0
- package/src/commands/verify.js +136 -0
- package/src/lib/admission-control.js +247 -0
- package/src/lib/export-diff.js +96 -0
- package/src/lib/export-verifier.js +49 -0
- package/src/lib/export.js +7 -0
- package/src/lib/normalized-config.js +3 -72
- package/src/lib/run-loop.js +8 -0
- package/src/lib/validation.js +5 -3
|
@@ -556,82 +556,13 @@ export function validateV4Config(data, projectRoot) {
|
|
|
556
556
|
errors.push(...timeoutValidation.errors);
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
-
|
|
559
|
+
// Admission control (ADM-001..004) is handled by the validate, doctor, and
|
|
560
|
+
// run-loop paths which call runAdmissionControl() directly. Config schema
|
|
561
|
+
// validation here should not duplicate that surface.
|
|
560
562
|
|
|
561
563
|
return { ok: errors.length === 0, errors, warnings };
|
|
562
564
|
}
|
|
563
565
|
|
|
564
|
-
export function collectRemoteReviewOnlyGateWarnings(data) {
|
|
565
|
-
const warnings = [];
|
|
566
|
-
const routing = data?.routing;
|
|
567
|
-
const gates = data?.gates;
|
|
568
|
-
const roles = data?.roles;
|
|
569
|
-
const runtimes = data?.runtimes;
|
|
570
|
-
|
|
571
|
-
if (!routing || !gates || !roles || !runtimes) {
|
|
572
|
-
return warnings;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
for (const [phase, route] of Object.entries(routing)) {
|
|
576
|
-
const exitGateId = route?.exit_gate;
|
|
577
|
-
if (!exitGateId || !gates[exitGateId]) {
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
const requiredFiles = Array.isArray(gates[exitGateId]?.requires_files)
|
|
582
|
-
? gates[exitGateId].requires_files.filter(filePath => typeof filePath === 'string' && filePath.trim())
|
|
583
|
-
: [];
|
|
584
|
-
if (requiredFiles.length === 0) {
|
|
585
|
-
continue;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const candidateRoleIds = [
|
|
589
|
-
route?.entry_role,
|
|
590
|
-
...(Array.isArray(route?.allowed_next_roles) ? route.allowed_next_roles : []),
|
|
591
|
-
].filter((roleId) => roleId && roleId !== 'human');
|
|
592
|
-
|
|
593
|
-
if (candidateRoleIds.length === 0) {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const candidateRoles = [...new Set(candidateRoleIds)]
|
|
598
|
-
.map((roleId) => {
|
|
599
|
-
const role = roles[roleId];
|
|
600
|
-
const runtime = role?.runtime ? runtimes[role.runtime] : null;
|
|
601
|
-
if (!role || !runtime) {
|
|
602
|
-
return null;
|
|
603
|
-
}
|
|
604
|
-
return { roleId, role, runtime };
|
|
605
|
-
})
|
|
606
|
-
.filter(Boolean);
|
|
607
|
-
|
|
608
|
-
if (candidateRoles.length === 0) {
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const hasFileProducingRole = candidateRoles.some(({ role }) =>
|
|
613
|
-
role.write_authority === 'authoritative' || role.write_authority === 'proposed');
|
|
614
|
-
if (hasFileProducingRole) {
|
|
615
|
-
continue;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
const allRemoteReviewOnly = candidateRoles.every(({ role, runtime }) =>
|
|
619
|
-
role.write_authority === 'review_only' && (runtime.type === 'api_proxy' || runtime.type === 'remote_agent'));
|
|
620
|
-
if (!allRemoteReviewOnly) {
|
|
621
|
-
continue;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const roleSummary = candidateRoles
|
|
625
|
-
.map(({ roleId, runtime }) => `${roleId}:${runtime.type}`)
|
|
626
|
-
.join(', ');
|
|
627
|
-
warnings.push(
|
|
628
|
-
`Routing "${phase}" exits through gate "${exitGateId}" with requires_files (${requiredFiles.join(', ')}) but all participating roles are review_only remote runtimes (${roleSummary}). Those files cannot be produced through governed turns; add a proposed/authoritative writer, remove the gate files, or expect operator-managed out-of-band artifacts.`,
|
|
629
|
-
);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
return warnings;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
566
|
export function validateBudgetConfig(budget) {
|
|
636
567
|
const errors = [];
|
|
637
568
|
|
package/src/lib/run-loop.js
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
RUNNER_INTERFACE_VERSION,
|
|
33
33
|
} from './runner-interface.js';
|
|
34
34
|
|
|
35
|
+
import { runAdmissionControl } from './admission-control.js';
|
|
35
36
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
36
37
|
import { join, dirname } from 'path';
|
|
37
38
|
|
|
@@ -65,6 +66,13 @@ export async function runLoop(root, config, callbacks, options = {}) {
|
|
|
65
66
|
}
|
|
66
67
|
};
|
|
67
68
|
|
|
69
|
+
// ── Admission control — reject provably dead-end configs ────────────────
|
|
70
|
+
const admission = runAdmissionControl(config, config);
|
|
71
|
+
if (!admission.ok) {
|
|
72
|
+
return makeResult(false, 'admission_rejected', null, 0, [], 0,
|
|
73
|
+
admission.errors.map(e => `Admission control: ${e}`));
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
// ── Initialize if idle ──────────────────────────────────────────────────
|
|
69
77
|
let state = loadState(root, config);
|
|
70
78
|
const shouldRestartCompleted = state?.status === 'completed' && options.startNewRunFromCompleted === true;
|
package/src/lib/validation.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
validateAcceptanceHintCompletion,
|
|
10
10
|
validateGovernedWorkflowKit,
|
|
11
11
|
} from './governed-templates.js';
|
|
12
|
-
import {
|
|
12
|
+
import { runAdmissionControl } from './admission-control.js';
|
|
13
13
|
|
|
14
14
|
const DEFAULT_REQUIRED_FILES = [
|
|
15
15
|
'.planning/PROJECT.md',
|
|
@@ -116,8 +116,10 @@ export function validateGovernedProject(root, rawConfig, config, opts = {}) {
|
|
|
116
116
|
errors.push(...workflowKit.errors);
|
|
117
117
|
warnings.push(...workflowKit.warnings);
|
|
118
118
|
|
|
119
|
-
//
|
|
120
|
-
|
|
119
|
+
// Admission control — reject provably dead-end configs
|
|
120
|
+
const admission = runAdmissionControl(config, rawConfig);
|
|
121
|
+
errors.push(...admission.errors);
|
|
122
|
+
warnings.push(...admission.warnings);
|
|
121
123
|
|
|
122
124
|
const mustExist = [
|
|
123
125
|
config.files?.state || '.agentxchain/state.json',
|