acp-devtools 0.4.0 → 0.5.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/dist/index.js
CHANGED
|
@@ -7278,8 +7278,12 @@ function createAcpValidator(schema3) {
|
|
|
7278
7278
|
const ajv = new Ajv2020({ strict: false, allErrors: true, validateFormats: false });
|
|
7279
7279
|
ajv.addSchema(schema3, SCHEMA_ID);
|
|
7280
7280
|
const methodIndex = /* @__PURE__ */ new Map();
|
|
7281
|
+
const softRequired = /* @__PURE__ */ new Map();
|
|
7281
7282
|
const defs = schema3.$defs ?? {};
|
|
7282
7283
|
for (const [name, def] of Object.entries(defs)) {
|
|
7284
|
+
const soon = Object.entries(def.properties ?? {}).filter(([, prop]) => SOON_REQUIRED_RE.test(prop.description ?? "")).map(([field]) => field);
|
|
7285
|
+
if (soon.length > 0)
|
|
7286
|
+
softRequired.set(name, soon);
|
|
7283
7287
|
const method = def["x-method"];
|
|
7284
7288
|
if (typeof method !== "string")
|
|
7285
7289
|
continue;
|
|
@@ -7332,22 +7336,41 @@ function createAcpValidator(schema3) {
|
|
|
7332
7336
|
}
|
|
7333
7337
|
if (!defName)
|
|
7334
7338
|
return { valid: true, errors: [], skipped: "no-schema-for-kind" };
|
|
7339
|
+
const advisories = collectAdvisories(defName, target);
|
|
7335
7340
|
const validator2 = getValidator(defName);
|
|
7336
7341
|
const ok = validator2(target);
|
|
7337
|
-
|
|
7338
|
-
return { valid: true, errors: [], schemaName: defName };
|
|
7339
|
-
return {
|
|
7342
|
+
const base = ok ? { valid: true, errors: [], schemaName: defName } : {
|
|
7340
7343
|
valid: false,
|
|
7341
7344
|
errors: (validator2.errors ?? []).map(toError),
|
|
7342
7345
|
schemaName: defName
|
|
7343
7346
|
};
|
|
7347
|
+
if (advisories.length > 0)
|
|
7348
|
+
base.advisories = advisories;
|
|
7349
|
+
return base;
|
|
7350
|
+
}
|
|
7351
|
+
function collectAdvisories(defName, target) {
|
|
7352
|
+
const fields = softRequired.get(defName);
|
|
7353
|
+
if (!fields || typeof target !== "object" || target === null)
|
|
7354
|
+
return [];
|
|
7355
|
+
const obj = target;
|
|
7356
|
+
const out = [];
|
|
7357
|
+
for (const field of fields) {
|
|
7358
|
+
if (obj[field] === void 0 || obj[field] === null) {
|
|
7359
|
+
out.push({
|
|
7360
|
+
path: field,
|
|
7361
|
+
message: `missing ${field} \u2014 will be required in a future ACP version`,
|
|
7362
|
+
kind: "soon-required"
|
|
7363
|
+
});
|
|
7364
|
+
}
|
|
7365
|
+
}
|
|
7366
|
+
return out;
|
|
7344
7367
|
}
|
|
7345
7368
|
function knownAcpMethods2() {
|
|
7346
7369
|
return [...methodIndex.keys()].sort();
|
|
7347
7370
|
}
|
|
7348
7371
|
return { validateAcpMessage: validateAcpMessage2, knownAcpMethods: knownAcpMethods2 };
|
|
7349
7372
|
}
|
|
7350
|
-
var import__, ajvModule, Ajv2020, SCHEMA_ID;
|
|
7373
|
+
var import__, ajvModule, Ajv2020, SCHEMA_ID, SOON_REQUIRED_RE;
|
|
7351
7374
|
var init_validate_factory = __esm({
|
|
7352
7375
|
"../core/dist/acp/validate-factory.js"() {
|
|
7353
7376
|
"use strict";
|
|
@@ -7355,6 +7378,7 @@ var init_validate_factory = __esm({
|
|
|
7355
7378
|
ajvModule = import__.default;
|
|
7356
7379
|
Ajv2020 = ajvModule.default ?? ajvModule;
|
|
7357
7380
|
SCHEMA_ID = "acp";
|
|
7381
|
+
SOON_REQUIRED_RE = /will be required/i;
|
|
7358
7382
|
}
|
|
7359
7383
|
});
|
|
7360
7384
|
|
|
@@ -8430,6 +8454,7 @@ function detectAgentShortcut(agentCommand) {
|
|
|
8430
8454
|
const trimmed = agentCommand.trim();
|
|
8431
8455
|
if (trimmed.length === 0)
|
|
8432
8456
|
return null;
|
|
8457
|
+
const candidates = [trimmed, basenameFirstToken(trimmed)];
|
|
8433
8458
|
for (const def of Object.values(AGENT_REGISTRY)) {
|
|
8434
8459
|
const variants = [
|
|
8435
8460
|
[def.command, ...def.args],
|
|
@@ -8437,14 +8462,23 @@ function detectAgentShortcut(agentCommand) {
|
|
|
8437
8462
|
];
|
|
8438
8463
|
for (const variant of variants) {
|
|
8439
8464
|
const exact = variant.join(" ");
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8465
|
+
for (const candidate of candidates) {
|
|
8466
|
+
if (candidate === exact)
|
|
8467
|
+
return def;
|
|
8468
|
+
if (candidate.startsWith(exact + " "))
|
|
8469
|
+
return def;
|
|
8470
|
+
}
|
|
8444
8471
|
}
|
|
8445
8472
|
}
|
|
8446
8473
|
return null;
|
|
8447
8474
|
}
|
|
8475
|
+
function basenameFirstToken(command) {
|
|
8476
|
+
const spaceAt = command.indexOf(" ");
|
|
8477
|
+
const head = spaceAt === -1 ? command : command.slice(0, spaceAt);
|
|
8478
|
+
const rest = spaceAt === -1 ? "" : command.slice(spaceAt);
|
|
8479
|
+
const base = head.split(/[/\\]/).pop() ?? head;
|
|
8480
|
+
return base + rest;
|
|
8481
|
+
}
|
|
8448
8482
|
var AGENT_REGISTRY, DEFAULT_AGENT;
|
|
8449
8483
|
var init_registry = __esm({
|
|
8450
8484
|
"../core/dist/agents/registry.js"() {
|
|
@@ -10853,7 +10887,7 @@ var CLI_VERSION;
|
|
|
10853
10887
|
var init_version = __esm({
|
|
10854
10888
|
"src/version.ts"() {
|
|
10855
10889
|
"use strict";
|
|
10856
|
-
CLI_VERSION = "0.
|
|
10890
|
+
CLI_VERSION = "0.5.0";
|
|
10857
10891
|
}
|
|
10858
10892
|
});
|
|
10859
10893
|
|
|
@@ -11668,7 +11702,7 @@ function buildMcpServer(opts) {
|
|
|
11668
11702
|
server.registerTool(
|
|
11669
11703
|
"find_spec_violations",
|
|
11670
11704
|
{
|
|
11671
|
-
description: "Validate every captured frame in a session against the ACP JSON schema (Draft 2020-12, ajv-compiled). Returns every offender with its seq, method, and per-error path + keyword + message. Schema-skipped frames (unknown methods, parse errors, generic envelopes) are not counted. Use the returned seq with get_message to inspect the offending raw payload. Start here when an agent is misbehaving but you don't yet know how.",
|
|
11705
|
+
description: "Validate every captured frame in a session against the ACP JSON schema (Draft 2020-12, ajv-compiled). Returns every offender with its seq, method, and per-error path + keyword + message. Schema-skipped frames (unknown methods, parse errors, generic envelopes) are not counted. Also returns `advisories`: frames valid under today's schema that omit a field the spec says will become mandatory (e.g. `agentInfo` on an initialize response, `clientInfo` on the request) \u2014 these never count as violations. Use the returned seq with get_message to inspect the offending raw payload. Start here when an agent is misbehaving but you don't yet know how.",
|
|
11672
11706
|
inputSchema: {
|
|
11673
11707
|
session_id: z.number().int().positive().describe("session id from list_sessions")
|
|
11674
11708
|
},
|
|
@@ -11690,6 +11724,14 @@ function buildMcpServer(opts) {
|
|
|
11690
11724
|
message: z.string().optional()
|
|
11691
11725
|
}))
|
|
11692
11726
|
}).loose()
|
|
11727
|
+
),
|
|
11728
|
+
advisories: z.array(
|
|
11729
|
+
z.object({
|
|
11730
|
+
seq: z.number(),
|
|
11731
|
+
method: z.string().nullable(),
|
|
11732
|
+
path: z.string(),
|
|
11733
|
+
message: z.string()
|
|
11734
|
+
})
|
|
11693
11735
|
)
|
|
11694
11736
|
},
|
|
11695
11737
|
annotations: annotations({ title: "Validate session against ACP schema" })
|
|
@@ -11700,6 +11742,7 @@ function buildMcpServer(opts) {
|
|
|
11700
11742
|
const seqToMethod = /* @__PURE__ */ new Map();
|
|
11701
11743
|
for (const m of messages) if (m.method) seqToMethod.set(m.seq, m.method);
|
|
11702
11744
|
const violations = [];
|
|
11745
|
+
const advisories = [];
|
|
11703
11746
|
let checked = 0;
|
|
11704
11747
|
for (const m of messages) {
|
|
11705
11748
|
const pair = pairs.get(m.seq);
|
|
@@ -11709,15 +11752,15 @@ function buildMcpServer(opts) {
|
|
|
11709
11752
|
const result = validateAcpMessage(m, validationOpts);
|
|
11710
11753
|
if (result.skipped) continue;
|
|
11711
11754
|
checked += 1;
|
|
11755
|
+
const method = m.method ?? pairedMethod ?? null;
|
|
11712
11756
|
if (!result.valid) {
|
|
11713
|
-
violations.push({
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
});
|
|
11757
|
+
violations.push({ seq: m.seq, method, errors: result.errors });
|
|
11758
|
+
}
|
|
11759
|
+
for (const a of result.advisories ?? []) {
|
|
11760
|
+
advisories.push({ seq: m.seq, method, path: a.path, message: a.message });
|
|
11718
11761
|
}
|
|
11719
11762
|
}
|
|
11720
|
-
return structuredResult({ session_id, checked, violations });
|
|
11763
|
+
return structuredResult({ session_id, checked, violations, advisories });
|
|
11721
11764
|
}
|
|
11722
11765
|
);
|
|
11723
11766
|
server.registerTool(
|
|
@@ -13597,6 +13640,7 @@ function registerValidateCommand(program) {
|
|
|
13597
13640
|
}
|
|
13598
13641
|
const methodNeedle = opts.method ? opts.method.toLowerCase() : null;
|
|
13599
13642
|
const violations = [];
|
|
13643
|
+
const advisories = [];
|
|
13600
13644
|
let checked = 0;
|
|
13601
13645
|
let skipped = 0;
|
|
13602
13646
|
for (const m of messages) {
|
|
@@ -13610,11 +13654,20 @@ function registerValidateCommand(program) {
|
|
|
13610
13654
|
continue;
|
|
13611
13655
|
}
|
|
13612
13656
|
checked += 1;
|
|
13613
|
-
if (result.valid) continue;
|
|
13614
13657
|
const method = m.method ?? pairedMethod ?? null;
|
|
13615
|
-
|
|
13616
|
-
|
|
13658
|
+
const methodMatch = !methodNeedle || !!method && method.toLowerCase().includes(methodNeedle);
|
|
13659
|
+
if (methodMatch && result.advisories) {
|
|
13660
|
+
for (const a of result.advisories) {
|
|
13661
|
+
advisories.push({
|
|
13662
|
+
seq: m.seq,
|
|
13663
|
+
method,
|
|
13664
|
+
schemaName: result.schemaName ?? "?",
|
|
13665
|
+
path: a.path,
|
|
13666
|
+
message: a.message
|
|
13667
|
+
});
|
|
13668
|
+
}
|
|
13617
13669
|
}
|
|
13670
|
+
if (result.valid || !methodMatch) continue;
|
|
13618
13671
|
for (const e of result.errors) {
|
|
13619
13672
|
if (violations.length >= limit) break;
|
|
13620
13673
|
const v = {
|
|
@@ -13633,7 +13686,7 @@ function registerValidateCommand(program) {
|
|
|
13633
13686
|
if (opts.json) {
|
|
13634
13687
|
process.stdout.write(
|
|
13635
13688
|
JSON.stringify(
|
|
13636
|
-
{ sessionId: id, checked, skipped, violations },
|
|
13689
|
+
{ sessionId: id, checked, skipped, violations, advisories },
|
|
13637
13690
|
null,
|
|
13638
13691
|
2
|
|
13639
13692
|
) + "\n"
|
|
@@ -13644,18 +13697,25 @@ function registerValidateCommand(program) {
|
|
|
13644
13697
|
const errStyler = createStyler(colorEnabled(process.stderr));
|
|
13645
13698
|
const affected = new Set(violations.map((v) => v.method ?? "?"));
|
|
13646
13699
|
const vCount = violations.length === 0 ? errStyler.green("0 violations") : errStyler.red(`${violations.length} violations`);
|
|
13700
|
+
const aCount = advisories.length === 0 ? "" : ` \xB7 ${errStyler.yellow(`${advisories.length} advisor${advisories.length === 1 ? "y" : "ies"}`)}`;
|
|
13647
13701
|
process.stderr.write(
|
|
13648
|
-
`${errStyler.bold(errStyler.cyan(`session #${id}`))} \xB7 ${checked} checked \xB7 ${skipped} skipped (no schema) \xB7 ${vCount} in ${affected.size} method${affected.size === 1 ? "" : "s"}
|
|
13702
|
+
`${errStyler.bold(errStyler.cyan(`session #${id}`))} \xB7 ${checked} checked \xB7 ${skipped} skipped (no schema) \xB7 ${vCount} in ${affected.size} method${affected.size === 1 ? "" : "s"}${aCount}
|
|
13649
13703
|
|
|
13650
13704
|
`
|
|
13651
13705
|
);
|
|
13652
|
-
|
|
13706
|
+
const s = createStyler(colorEnabled(process.stdout));
|
|
13707
|
+
if (violations.length > 0) {
|
|
13708
|
+
process.stdout.write(renderViolations(s, violations));
|
|
13709
|
+
}
|
|
13710
|
+
if (advisories.length > 0) {
|
|
13711
|
+
if (violations.length > 0) process.stdout.write("\n");
|
|
13712
|
+
process.stdout.write(renderAdvisories(s, advisories));
|
|
13713
|
+
}
|
|
13714
|
+
if (violations.length === 0 && advisories.length === 0) {
|
|
13653
13715
|
process.stdout.write(errStyler.green("no violations") + "\n");
|
|
13654
13716
|
return;
|
|
13655
13717
|
}
|
|
13656
|
-
|
|
13657
|
-
process.stdout.write(renderViolations(s, violations));
|
|
13658
|
-
process.exit(1);
|
|
13718
|
+
if (violations.length > 0) process.exit(1);
|
|
13659
13719
|
});
|
|
13660
13720
|
}
|
|
13661
13721
|
function renderViolations(s, violations) {
|
|
@@ -13668,11 +13728,22 @@ function renderViolations(s, violations) {
|
|
|
13668
13728
|
]);
|
|
13669
13729
|
return renderTable(s, COLUMNS3, body);
|
|
13670
13730
|
}
|
|
13731
|
+
function renderAdvisories(s, advisories) {
|
|
13732
|
+
const heading5 = s.yellow(`${advisories.length} advisor${advisories.length === 1 ? "y" : "ies"}`) + s.dim(" \u2014 valid today, but a future ACP version will require these fields") + "\n";
|
|
13733
|
+
const body = advisories.map((a) => [
|
|
13734
|
+
s.dim(`#${a.seq}`),
|
|
13735
|
+
truncate3(a.method ?? "\u2014", METHOD_MAX4),
|
|
13736
|
+
s.dim(truncate3(a.schemaName, SCHEMA_MAX)),
|
|
13737
|
+
s.yellow(truncate3(a.path, PATH_MAX)),
|
|
13738
|
+
s.yellow(a.message)
|
|
13739
|
+
]);
|
|
13740
|
+
return heading5 + renderTable(s, ADVISORY_COLUMNS, body);
|
|
13741
|
+
}
|
|
13671
13742
|
function truncate3(s, max) {
|
|
13672
13743
|
if (s.length <= max) return s;
|
|
13673
13744
|
return s.slice(0, Math.max(1, max - 1)) + "\u2026";
|
|
13674
13745
|
}
|
|
13675
|
-
var METHOD_MAX4, SCHEMA_MAX, PATH_MAX, COLUMNS3;
|
|
13746
|
+
var METHOD_MAX4, SCHEMA_MAX, PATH_MAX, COLUMNS3, ADVISORY_COLUMNS;
|
|
13676
13747
|
var init_validate2 = __esm({
|
|
13677
13748
|
"src/commands/validate.ts"() {
|
|
13678
13749
|
"use strict";
|
|
@@ -13690,6 +13761,13 @@ var init_validate2 = __esm({
|
|
|
13690
13761
|
{ title: "PATH", align: "left" },
|
|
13691
13762
|
{ title: "ERROR", align: "left" }
|
|
13692
13763
|
];
|
|
13764
|
+
ADVISORY_COLUMNS = [
|
|
13765
|
+
{ title: "SEQ", align: "right" },
|
|
13766
|
+
{ title: "METHOD", align: "left" },
|
|
13767
|
+
{ title: "SCHEMA", align: "left" },
|
|
13768
|
+
{ title: "FIELD", align: "left" },
|
|
13769
|
+
{ title: "ADVISORY", align: "left" }
|
|
13770
|
+
];
|
|
13693
13771
|
}
|
|
13694
13772
|
});
|
|
13695
13773
|
|