agentxchain 2.1.1 → 2.3.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 +41 -9
- package/bin/agentxchain.js +89 -0
- package/package.json +7 -2
- package/scripts/publish-from-tag.sh +14 -9
- package/scripts/release-postflight.sh +42 -2
- package/src/commands/intake-approve.js +44 -0
- package/src/commands/intake-plan.js +62 -0
- package/src/commands/intake-record.js +86 -0
- package/src/commands/intake-resolve.js +45 -0
- package/src/commands/intake-scan.js +87 -0
- package/src/commands/intake-start.js +53 -0
- package/src/commands/intake-status.js +113 -0
- package/src/commands/intake-triage.js +54 -0
- package/src/commands/verify.js +8 -3
- package/src/lib/adapters/api-proxy-adapter.js +125 -27
- package/src/lib/intake.js +924 -0
- package/src/lib/normalized-config.js +10 -0
- package/src/lib/protocol-conformance.js +28 -4
- package/src/lib/reference-conformance-adapter.js +141 -0
- package/src/lib/repo-observer.js +1 -0
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { validateHooksConfig } from './hook-runner.js';
|
|
16
|
+
import { SUPPORTED_TOKEN_COUNTER_PROVIDERS } from './token-counter.js';
|
|
16
17
|
|
|
17
18
|
const VALID_WRITE_AUTHORITIES = ['authoritative', 'proposed', 'review_only'];
|
|
18
19
|
const VALID_RUNTIME_TYPES = ['manual', 'local_cli', 'api_proxy'];
|
|
20
|
+
const VALID_API_PROXY_PROVIDERS = ['anthropic', 'openai'];
|
|
19
21
|
const VALID_PROMPT_TRANSPORTS = ['argv', 'stdin', 'dispatch_bundle_only'];
|
|
20
22
|
const VALID_PHASES = ['planning', 'implementation', 'qa'];
|
|
21
23
|
const VALID_API_PROXY_RETRY_JITTER = ['none', 'full'];
|
|
@@ -146,6 +148,12 @@ function validateApiProxyPreflightTokenization(runtimeId, runtime, errors) {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
if (preflight.enabled === true) {
|
|
151
|
+
if (!SUPPORTED_TOKEN_COUNTER_PROVIDERS.includes(runtime.provider)) {
|
|
152
|
+
errors.push(
|
|
153
|
+
`Runtime "${runtimeId}": preflight_tokenization tokenizer "provider_local" is not supported for provider "${runtime.provider}". Supported providers: ${SUPPORTED_TOKEN_COUNTER_PROVIDERS.join(', ')}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
149
157
|
if (!Number.isInteger(runtime.context_window_tokens) || runtime.context_window_tokens <= 0) {
|
|
150
158
|
errors.push(`Runtime "${runtimeId}": context_window_tokens is required when preflight_tokenization.enabled is true`);
|
|
151
159
|
return;
|
|
@@ -239,6 +247,8 @@ export function validateV4Config(data, projectRoot) {
|
|
|
239
247
|
if (rt.type === 'api_proxy') {
|
|
240
248
|
if (typeof rt.provider !== 'string' || !rt.provider.trim()) {
|
|
241
249
|
errors.push(`Runtime "${id}": api_proxy requires "provider" (e.g. "anthropic", "openai")`);
|
|
250
|
+
} else if (!VALID_API_PROXY_PROVIDERS.includes(rt.provider)) {
|
|
251
|
+
errors.push(`Runtime "${id}": api_proxy provider must be one of: ${VALID_API_PROXY_PROVIDERS.join(', ')}`);
|
|
242
252
|
}
|
|
243
253
|
if (typeof rt.model !== 'string' || !rt.model.trim()) {
|
|
244
254
|
errors.push(`Runtime "${id}": api_proxy requires "model" (e.g. "claude-sonnet-4-6")`);
|
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const DEFAULT_FIXTURE_ROOT = resolve(__dirname, '..', '..', '..', '.agentxchain-conformance', 'fixtures');
|
|
8
|
-
const VALID_RESPONSE_STATUSES = new Set(['pass', 'fail', 'error']);
|
|
8
|
+
const VALID_RESPONSE_STATUSES = new Set(['pass', 'fail', 'error', 'not_implemented']);
|
|
9
9
|
const VALID_TIERS = new Set([1, 2, 3]);
|
|
10
10
|
|
|
11
11
|
function readJsonFile(filePath) {
|
|
@@ -119,16 +119,18 @@ function createTierSummary(status = 'skipped', note = null) {
|
|
|
119
119
|
fixtures_passed: 0,
|
|
120
120
|
fixtures_failed: 0,
|
|
121
121
|
fixtures_errored: 0,
|
|
122
|
+
fixtures_not_implemented: 0,
|
|
122
123
|
surfaces: {},
|
|
123
124
|
failures: [],
|
|
124
125
|
errors: [],
|
|
126
|
+
not_implemented: [],
|
|
125
127
|
...(note ? { note } : {}),
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
function ensureSurfaceSummary(tierSummary, surface) {
|
|
130
132
|
if (!tierSummary.surfaces[surface]) {
|
|
131
|
-
tierSummary.surfaces[surface] = { passed: 0, failed: 0, errored: 0 };
|
|
133
|
+
tierSummary.surfaces[surface] = { passed: 0, failed: 0, errored: 0, not_implemented: 0 };
|
|
132
134
|
}
|
|
133
135
|
return tierSummary.surfaces[surface];
|
|
134
136
|
}
|
|
@@ -149,7 +151,7 @@ function executeFixture(targetRoot, adapterCommand, fixture) {
|
|
|
149
151
|
};
|
|
150
152
|
}
|
|
151
153
|
|
|
152
|
-
if (![0, 1, 2].includes(result.status ?? -1)) {
|
|
154
|
+
if (![0, 1, 2, 3].includes(result.status ?? -1)) {
|
|
153
155
|
return {
|
|
154
156
|
status: 'error',
|
|
155
157
|
message: `Adapter exited with unsupported status ${result.status}`,
|
|
@@ -182,7 +184,7 @@ function executeFixture(targetRoot, adapterCommand, fixture) {
|
|
|
182
184
|
};
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
const expectedExitCode = parsed.status === 'pass' ? 0 : parsed.status === 'fail' ? 1 : 2;
|
|
187
|
+
const expectedExitCode = parsed.status === 'pass' ? 0 : parsed.status === 'fail' ? 1 : parsed.status === 'not_implemented' ? 3 : 2;
|
|
186
188
|
if (result.status !== expectedExitCode) {
|
|
187
189
|
return {
|
|
188
190
|
status: 'error',
|
|
@@ -210,6 +212,17 @@ export function verifyProtocolConformance({
|
|
|
210
212
|
|
|
211
213
|
const resolvedTargetRoot = resolve(targetRoot);
|
|
212
214
|
const capabilities = loadCapabilities(resolvedTargetRoot);
|
|
215
|
+
|
|
216
|
+
// Enforce surface claims when capabilities.surfaces exists and --surface is requested
|
|
217
|
+
if (surface && capabilities.surfaces && typeof capabilities.surfaces === 'object') {
|
|
218
|
+
if (!capabilities.surfaces[surface]) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Surface "${surface}" is not claimed in capabilities.json. ` +
|
|
221
|
+
`Claimed surfaces: ${Object.keys(capabilities.surfaces).join(', ')}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
213
226
|
const fixtureEntries = selectFixtureFiles(fixtureRoot, requestedTier, surface);
|
|
214
227
|
const claimedTiers = new Set(capabilities.tiers);
|
|
215
228
|
const report = {
|
|
@@ -250,6 +263,17 @@ export function verifyProtocolConformance({
|
|
|
250
263
|
continue;
|
|
251
264
|
}
|
|
252
265
|
|
|
266
|
+
if (adapterResult.status === 'not_implemented') {
|
|
267
|
+
tierSummary.fixtures_not_implemented += 1;
|
|
268
|
+
surfaceSummary.not_implemented += 1;
|
|
269
|
+
tierSummary.not_implemented.push({
|
|
270
|
+
fixture_id: fixture.fixture_id,
|
|
271
|
+
surface: fixture.surface,
|
|
272
|
+
message: adapterResult.message || 'Not implemented',
|
|
273
|
+
});
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
253
277
|
if (adapterResult.status === 'fail') {
|
|
254
278
|
tierSummary.fixtures_failed += 1;
|
|
255
279
|
surfaceSummary.failed += 1;
|
|
@@ -13,6 +13,9 @@ import { validateStagedTurnResult } from './turn-result-validator.js';
|
|
|
13
13
|
import { finalizeDispatchManifest, verifyDispatchManifest } from './dispatch-manifest.js';
|
|
14
14
|
import { getDispatchTurnDir } from './turn-paths.js';
|
|
15
15
|
import { runHooks } from './hook-runner.js';
|
|
16
|
+
import { validateCoordinatorConfig, normalizeCoordinatorConfig } from './coordinator-config.js';
|
|
17
|
+
import { projectRepoAcceptance, evaluateBarriers } from './coordinator-acceptance.js';
|
|
18
|
+
import { readBarriers, saveCoordinatorState, readCoordinatorHistory } from './coordinator-state.js';
|
|
16
19
|
|
|
17
20
|
const VALID_DECISION_CATEGORIES = ['implementation', 'architecture', 'scope', 'process', 'quality', 'release'];
|
|
18
21
|
const FULL_STAGE_PIPELINE = ['schema', 'assignment', 'artifact', 'verification', 'protocol'];
|
|
@@ -213,6 +216,69 @@ function materializeFixtureWorkspace(fixture) {
|
|
|
213
216
|
};
|
|
214
217
|
}
|
|
215
218
|
|
|
219
|
+
// ── Tier 3: Multi-workspace materialization ─────────────────────────────────
|
|
220
|
+
|
|
221
|
+
function materializeTier3Workspace(fixture) {
|
|
222
|
+
const root = mkdtempSync(join(tmpdir(), 'agentxchain-conformance-multi-'));
|
|
223
|
+
const setup = fixture.setup || {};
|
|
224
|
+
|
|
225
|
+
// Write coordinator config
|
|
226
|
+
if (setup.coordinator_config) {
|
|
227
|
+
writeJson(join(root, 'agentxchain-multi.json'), setup.coordinator_config);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Materialize governed repo roots
|
|
231
|
+
for (const [repoId, repoSetup] of Object.entries(setup.repos || {})) {
|
|
232
|
+
const repoPath = join(root, repoSetup.path || `./repos/${repoId}`);
|
|
233
|
+
mkdirSync(join(repoPath, '.agentxchain'), { recursive: true });
|
|
234
|
+
|
|
235
|
+
// Write repo-local agentxchain.json
|
|
236
|
+
if (repoSetup.config) {
|
|
237
|
+
writeJson(join(repoPath, 'agentxchain.json'), repoSetup.config);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Write repo-local state
|
|
241
|
+
if (repoSetup.state) {
|
|
242
|
+
const repoConfig = repoSetup.config || {};
|
|
243
|
+
const inflatedRepoConfig = inflateConfig(repoConfig);
|
|
244
|
+
const repoState = inflateState(repoSetup.state, inflatedRepoConfig);
|
|
245
|
+
writeJson(join(repoPath, '.agentxchain', 'state.json'), repoState);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Write repo-local history
|
|
249
|
+
if (repoSetup.history) {
|
|
250
|
+
writeJsonl(join(repoPath, '.agentxchain', 'history.jsonl'), repoSetup.history);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Write repo-local files
|
|
254
|
+
for (const [filePath, content] of Object.entries(repoSetup.files || {})) {
|
|
255
|
+
const absPath = join(repoPath, filePath);
|
|
256
|
+
mkdirSync(dirname(absPath), { recursive: true });
|
|
257
|
+
writeFileSync(absPath, content);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Write coordinator multirepo state if provided
|
|
262
|
+
if (setup.coordinator_state || setup.barriers) {
|
|
263
|
+
const multiDir = join(root, '.agentxchain', 'multirepo');
|
|
264
|
+
mkdirSync(multiDir, { recursive: true });
|
|
265
|
+
|
|
266
|
+
if (setup.coordinator_state) {
|
|
267
|
+
writeJson(join(multiDir, 'state.json'), setup.coordinator_state);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (setup.barriers) {
|
|
271
|
+
writeJson(join(multiDir, 'barriers.json'), setup.barriers);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Write coordinator history
|
|
275
|
+
writeJsonl(join(multiDir, 'history.jsonl'), setup.coordinator_history || []);
|
|
276
|
+
writeJsonl(join(multiDir, 'barrier-ledger.jsonl'), setup.barrier_ledger || []);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return root;
|
|
280
|
+
}
|
|
281
|
+
|
|
216
282
|
function isAssertionObject(value) {
|
|
217
283
|
return value && typeof value === 'object' && !Array.isArray(value) && typeof value.assert === 'string';
|
|
218
284
|
}
|
|
@@ -699,6 +765,74 @@ function executeFixtureOperation(workspace, fixture) {
|
|
|
699
765
|
}
|
|
700
766
|
}
|
|
701
767
|
|
|
768
|
+
function executeTier3Operation(fixture) {
|
|
769
|
+
const root = materializeTier3Workspace(fixture);
|
|
770
|
+
try {
|
|
771
|
+
const operation = fixture.input.operation;
|
|
772
|
+
|
|
773
|
+
if (operation === 'validate_coordinator_config') {
|
|
774
|
+
const configPath = join(root, 'agentxchain-multi.json');
|
|
775
|
+
const raw = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
776
|
+
const validation = validateCoordinatorConfig(raw);
|
|
777
|
+
if (!validation.ok) {
|
|
778
|
+
const firstError = validation.errors[0] || '';
|
|
779
|
+
let errorType = 'invalid_coordinator_config';
|
|
780
|
+
if (firstError.startsWith('workstream_cycle:')) {
|
|
781
|
+
errorType = 'workstream_cycle';
|
|
782
|
+
}
|
|
783
|
+
return { result: 'error', error_type: errorType, errors: validation.errors };
|
|
784
|
+
}
|
|
785
|
+
return { result: 'success', errors: [] };
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (operation === 'project_repo_acceptance') {
|
|
789
|
+
const args = fixture.input.args;
|
|
790
|
+
const configPath = join(root, 'agentxchain-multi.json');
|
|
791
|
+
const raw = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
792
|
+
const normalized = normalizeCoordinatorConfig(raw);
|
|
793
|
+
|
|
794
|
+
// Resolve repo paths against the materialized workspace
|
|
795
|
+
for (const [repoId, repo] of Object.entries(normalized.repos)) {
|
|
796
|
+
const resolvedPath = join(root, repo.path);
|
|
797
|
+
normalized.repos[repoId] = { ...repo, resolved_path: resolvedPath };
|
|
798
|
+
}
|
|
799
|
+
normalized.repo_order = Object.keys(normalized.repos);
|
|
800
|
+
|
|
801
|
+
const stateDir = join(root, '.agentxchain', 'multirepo');
|
|
802
|
+
const state = JSON.parse(readFileSync(join(stateDir, 'state.json'), 'utf8'));
|
|
803
|
+
|
|
804
|
+
const projectionResult = projectRepoAcceptance(
|
|
805
|
+
root, state, normalized,
|
|
806
|
+
args.repo_id, args.accepted_turn, args.workstream_id,
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
if (!projectionResult.ok) {
|
|
810
|
+
let errorType = 'projection_failed';
|
|
811
|
+
if (projectionResult.error && projectionResult.error.includes('Cross-repo write violation')) {
|
|
812
|
+
errorType = 'cross_repo_write_violation';
|
|
813
|
+
}
|
|
814
|
+
return { result: 'error', error_type: errorType, error: projectionResult.error };
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Evaluate barriers after projection
|
|
818
|
+
const barrierResult = evaluateBarriers(root, state, normalized);
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
result: 'success',
|
|
822
|
+
projection_ref: projectionResult.projection_ref,
|
|
823
|
+
barrier_effects: projectionResult.barrier_effects || [],
|
|
824
|
+
barrier_snapshot: Object.fromEntries(
|
|
825
|
+
Object.entries(barrierResult.barriers).map(([id, b]) => [id, { status: b.status }]),
|
|
826
|
+
),
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return { result: 'error', error_type: 'unsupported_operation', operation };
|
|
831
|
+
} finally {
|
|
832
|
+
rmSync(root, { recursive: true, force: true });
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
702
836
|
function compareActualToExpected(fixture, actual) {
|
|
703
837
|
if (matchExpected(fixture.expected, actual)) {
|
|
704
838
|
return buildPass(actual);
|
|
@@ -707,6 +841,13 @@ function compareActualToExpected(fixture, actual) {
|
|
|
707
841
|
}
|
|
708
842
|
|
|
709
843
|
export function runReferenceFixture(fixture) {
|
|
844
|
+
// Tier 3 fixtures use multi-workspace materialization; route before creating a Tier 1/2 workspace
|
|
845
|
+
const operation = fixture.input?.operation;
|
|
846
|
+
if (operation === 'validate_coordinator_config' || operation === 'project_repo_acceptance') {
|
|
847
|
+
const actual = executeTier3Operation(fixture);
|
|
848
|
+
return compareActualToExpected(fixture, actual);
|
|
849
|
+
}
|
|
850
|
+
|
|
710
851
|
const workspace = materializeFixtureWorkspace(fixture);
|
|
711
852
|
try {
|
|
712
853
|
const actual = executeFixtureOperation(workspace, fixture);
|