agentxchain 2.2.0 → 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.
@@ -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;
@@ -25,6 +25,7 @@ import { join } from 'path';
25
25
  const OPERATIONAL_PATH_PREFIXES = [
26
26
  '.agentxchain/dispatch/',
27
27
  '.agentxchain/staging/',
28
+ '.agentxchain/intake/',
28
29
  '.agentxchain/locks/',
29
30
  '.agentxchain/transactions/',
30
31
  ];