@adcp/sdk 15.0.0 → 15.2.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/adcp-grade.js +46 -2
- package/bin/adcp.js +7 -2
- package/dist/lib/canonical-references/index.d.mts +19 -1
- package/dist/lib/canonical-references/index.d.ts +19 -1
- package/dist/lib/canonical-references/index.js +81 -9
- package/dist/lib/canonical-references/index.mjs +80 -9
- package/dist/lib/client/core.d.mts +1 -0
- package/dist/lib/client/core.d.ts +1 -0
- package/dist/lib/core/AgentClient.d.mts +9 -1
- package/dist/lib/core/AgentClient.d.ts +9 -1
- package/dist/lib/core/AgentClient.js +10 -0
- package/dist/lib/core/AgentClient.mjs +10 -0
- package/dist/lib/core/SingleAgentClient.d.mts +13 -2
- package/dist/lib/core/SingleAgentClient.d.ts +13 -2
- package/dist/lib/core/SingleAgentClient.js +23 -5
- package/dist/lib/core/SingleAgentClient.mjs +23 -5
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/reporting/gcs/adapter.d.mts +31 -0
- package/dist/lib/reporting/gcs/adapter.d.ts +31 -0
- package/dist/lib/reporting/gcs/adapter.js +342 -0
- package/dist/lib/reporting/gcs/adapter.mjs +325 -0
- package/dist/lib/reporting/gcs/bucket-policy.d.mts +3 -0
- package/dist/lib/reporting/gcs/bucket-policy.d.ts +3 -0
- package/dist/lib/reporting/gcs/bucket-policy.js +31 -0
- package/dist/lib/reporting/gcs/bucket-policy.mjs +7 -0
- package/dist/lib/reporting/gcs/errors.d.mts +5 -0
- package/dist/lib/reporting/gcs/errors.d.ts +5 -0
- package/dist/lib/reporting/gcs/errors.js +41 -0
- package/dist/lib/reporting/gcs/errors.mjs +16 -0
- package/dist/lib/reporting/gcs/fence.d.mts +36 -0
- package/dist/lib/reporting/gcs/fence.d.ts +36 -0
- package/dist/lib/reporting/gcs/fence.js +250 -0
- package/dist/lib/reporting/gcs/fence.mjs +230 -0
- package/dist/lib/reporting/gcs/index.d.mts +7 -0
- package/dist/lib/reporting/gcs/index.d.ts +7 -0
- package/dist/lib/reporting/gcs/index.js +41 -0
- package/dist/lib/reporting/gcs/index.mjs +12 -0
- package/dist/lib/reporting/gcs/io.d.mts +7 -0
- package/dist/lib/reporting/gcs/io.d.ts +7 -0
- package/dist/lib/reporting/gcs/io.js +95 -0
- package/dist/lib/reporting/gcs/io.mjs +69 -0
- package/dist/lib/reporting/gcs/reader.d.mts +43 -0
- package/dist/lib/reporting/gcs/reader.d.ts +43 -0
- package/dist/lib/reporting/gcs/reader.js +193 -0
- package/dist/lib/reporting/gcs/reader.mjs +169 -0
- package/dist/lib/reporting/gcs/validation.d.mts +5 -0
- package/dist/lib/reporting/gcs/validation.d.ts +5 -0
- package/dist/lib/reporting/gcs/validation.js +49 -0
- package/dist/lib/reporting/gcs/validation.mjs +25 -0
- package/dist/lib/reporting/ledger/handler.d.mts +1 -0
- package/dist/lib/reporting/ledger/handler.d.ts +1 -0
- package/dist/lib/reporting/ledger/index.d.mts +1 -0
- package/dist/lib/reporting/ledger/index.d.ts +1 -0
- package/dist/lib/reporting/ledger/index.js +2 -0
- package/dist/lib/reporting/ledger/index.mjs +1 -0
- package/dist/lib/reporting/ledger/managed-postgres.d.mts +16 -1
- package/dist/lib/reporting/ledger/managed-postgres.d.ts +16 -1
- package/dist/lib/reporting/ledger/managed-postgres.js +243 -2
- package/dist/lib/reporting/ledger/managed-postgres.mjs +248 -2
- package/dist/lib/reporting/ledger/object-writes.d.mts +69 -0
- package/dist/lib/reporting/ledger/object-writes.d.ts +69 -0
- package/dist/lib/reporting/ledger/object-writes.js +147 -0
- package/dist/lib/reporting/ledger/object-writes.mjs +114 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.d.mts +50 -1
- package/dist/lib/server/a2a-adapter.d.ts +50 -1
- package/dist/lib/server/a2a-adapter.js +230 -7
- package/dist/lib/server/a2a-adapter.mjs +237 -7
- package/dist/lib/server/auth-signature.d.mts +84 -46
- package/dist/lib/server/auth-signature.d.ts +84 -46
- package/dist/lib/server/auth-signature.js +48 -14
- package/dist/lib/server/auth-signature.mjs +58 -15
- package/dist/lib/server/create-adcp-server.js +5 -16
- package/dist/lib/server/create-adcp-server.mjs +5 -16
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +3 -1
- package/dist/lib/signing/agent-fetch.js +3 -33
- package/dist/lib/signing/agent-fetch.mjs +3 -33
- package/dist/lib/signing/middleware.d.mts +9 -1
- package/dist/lib/signing/middleware.d.ts +9 -1
- package/dist/lib/signing/operation-resolution.d.mts +37 -0
- package/dist/lib/signing/operation-resolution.d.ts +37 -0
- package/dist/lib/signing/operation-resolution.js +154 -0
- package/dist/lib/signing/operation-resolution.mjs +125 -0
- package/dist/lib/signing/postgres-replay-store.d.mts +1 -1
- package/dist/lib/signing/postgres-replay-store.d.ts +1 -1
- package/dist/lib/signing/redis-replay-store.d.mts +1 -1
- package/dist/lib/signing/redis-replay-store.d.ts +1 -1
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/server.js +7 -0
- package/dist/lib/signing/server.mjs +8 -0
- package/dist/lib/signing/verifier.d.mts +36 -1
- package/dist/lib/signing/verifier.d.ts +36 -1
- package/dist/lib/signing/verifier.js +66 -34
- package/dist/lib/signing/verifier.mjs +61 -34
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +12 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +12 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +29 -2
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +27 -1
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.mts +61 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.ts +61 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.js +298 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.mjs +276 -0
- package/dist/lib/testing/storyboard/request-signing/grader.d.mts +31 -1
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts +31 -1
- package/dist/lib/testing/storyboard/request-signing/grader.js +11 -0
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +11 -0
- package/dist/lib/testing/storyboard/request-signing/index.d.mts +5 -4
- package/dist/lib/testing/storyboard/request-signing/index.d.ts +5 -4
- package/dist/lib/testing/storyboard/request-signing/index.js +13 -0
- package/dist/lib/testing/storyboard/request-signing/index.mjs +18 -2
- package/dist/lib/testing/storyboard/request-signing/types.d.mts +43 -0
- package/dist/lib/testing/storyboard/request-signing/types.d.ts +43 -0
- package/dist/lib/testing/storyboard/request-signing/types.js +3 -0
- package/dist/lib/testing/storyboard/request-signing/types.mjs +2 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +42 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +42 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js +100 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +104 -3
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +9 -4
- package/docs/guides/CREATIVE-DELIVERY.md +34 -0
- package/docs/guides/REPORTING-GCS-FENCE.md +52 -0
- package/docs/guides/REPORTING-GCS-MANAGED.md +56 -0
- package/docs/guides/REPORTING-OPERATIONS.md +4 -0
- package/docs/guides/SIGNING-GUIDE.md +20 -6
- package/docs/guides/VALIDATE-YOUR-AGENT.md +4 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +5 -4
- package/docs/migration-buyer-agent-registry.md +1 -1
- package/package.json +19 -1
- package/skills/build-seller-agent/specialisms/signed-requests.md +2 -0
|
@@ -116,3 +116,15 @@ export declare function operationFromVectorUrl(vectorUrl: string): string;
|
|
|
116
116
|
export declare function resolveA2aDispatchTarget(agentUrl: string, options?: A2aDispatchOptions): Promise<{
|
|
117
117
|
endpoint: string;
|
|
118
118
|
}>;
|
|
119
|
+
/**
|
|
120
|
+
* The A2A HTTP+JSON binding base URL the agent card declares, if any.
|
|
121
|
+
*
|
|
122
|
+
* Reads the raw card (not the SDK's normalization, which exposes only the
|
|
123
|
+
* interfaces its own transports drive) and accepts both card dialects:
|
|
124
|
+
* `supportedInterfaces[]` with `protocolBinding` (1.0) and `url` /
|
|
125
|
+
* `preferredTransport` / `additionalInterfaces[]` with `transport` (0.3).
|
|
126
|
+
* Returns `undefined` when the card does not resolve or declares no
|
|
127
|
+
* HTTP+JSON interface; the caller treats that as "this binding is not served"
|
|
128
|
+
* rather than guessing a path.
|
|
129
|
+
*/
|
|
130
|
+
export declare function resolveA2aHttpJsonEndpoint(agentUrl: string, options?: A2aDispatchOptions): Promise<string | undefined>;
|
|
@@ -116,3 +116,15 @@ export declare function operationFromVectorUrl(vectorUrl: string): string;
|
|
|
116
116
|
export declare function resolveA2aDispatchTarget(agentUrl: string, options?: A2aDispatchOptions): Promise<{
|
|
117
117
|
endpoint: string;
|
|
118
118
|
}>;
|
|
119
|
+
/**
|
|
120
|
+
* The A2A HTTP+JSON binding base URL the agent card declares, if any.
|
|
121
|
+
*
|
|
122
|
+
* Reads the raw card (not the SDK's normalization, which exposes only the
|
|
123
|
+
* interfaces its own transports drive) and accepts both card dialects:
|
|
124
|
+
* `supportedInterfaces[]` with `protocolBinding` (1.0) and `url` /
|
|
125
|
+
* `preferredTransport` / `additionalInterfaces[]` with `transport` (0.3).
|
|
126
|
+
* Returns `undefined` when the card does not resolve or declares no
|
|
127
|
+
* HTTP+JSON interface; the caller treats that as "this binding is not served"
|
|
128
|
+
* rather than guessing a path.
|
|
129
|
+
*/
|
|
130
|
+
export declare function resolveA2aHttpJsonEndpoint(agentUrl: string, options?: A2aDispatchOptions): Promise<string | undefined>;
|
|
@@ -30,7 +30,8 @@ var a2a_dispatch_exports = {};
|
|
|
30
30
|
__export(a2a_dispatch_exports, {
|
|
31
31
|
captureA2aRequest: () => captureA2aRequest,
|
|
32
32
|
operationFromVectorUrl: () => operationFromVectorUrl,
|
|
33
|
-
resolveA2aDispatchTarget: () => resolveA2aDispatchTarget
|
|
33
|
+
resolveA2aDispatchTarget: () => resolveA2aDispatchTarget,
|
|
34
|
+
resolveA2aHttpJsonEndpoint: () => resolveA2aHttpJsonEndpoint
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(a2a_dispatch_exports);
|
|
36
37
|
var import_agent_transport_fetch = require('../../../net/agent-transport-fetch.js');
|
|
@@ -172,9 +173,35 @@ async function resolveA2aDispatchTarget(agentUrl, options = {}) {
|
|
|
172
173
|
}
|
|
173
174
|
throw new Error("the A2A client returned without issuing a dispatch probe; no endpoint was resolved");
|
|
174
175
|
}
|
|
176
|
+
async function resolveA2aHttpJsonEndpoint(agentUrl, options = {}) {
|
|
177
|
+
const cardFetch = buildGuardedCardFetch(agentUrl, options);
|
|
178
|
+
const isHttpJson = (value) => typeof value === "string" && value.replace(/\s+/g, "").toUpperCase() === "HTTP+JSON";
|
|
179
|
+
for (const cardUrl of (0, import_a2a_discovery.buildCardUrls)(agentUrl)) {
|
|
180
|
+
let card;
|
|
181
|
+
try {
|
|
182
|
+
const res = await cardFetch(cardUrl, { headers: { accept: "application/json" }, redirect: "manual" });
|
|
183
|
+
if (!res.ok) continue;
|
|
184
|
+
card = await res.json();
|
|
185
|
+
} catch {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const interfaces = [];
|
|
189
|
+
for (const key of ["supportedInterfaces", "additionalInterfaces"]) {
|
|
190
|
+
const list = card[key];
|
|
191
|
+
if (Array.isArray(list)) interfaces.push(...list.filter((i) => !!i));
|
|
192
|
+
}
|
|
193
|
+
if (typeof card.url === "string") interfaces.push({ url: card.url, transport: card.preferredTransport });
|
|
194
|
+
const match = interfaces.find(
|
|
195
|
+
(i) => typeof i.url === "string" && (isHttpJson(i.protocolBinding) || isHttpJson(i.transport))
|
|
196
|
+
);
|
|
197
|
+
if (match) return match.url;
|
|
198
|
+
}
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
175
201
|
// Annotate the CommonJS export names for ESM import in node:
|
|
176
202
|
0 && (module.exports = {
|
|
177
203
|
captureA2aRequest,
|
|
178
204
|
operationFromVectorUrl,
|
|
179
|
-
resolveA2aDispatchTarget
|
|
205
|
+
resolveA2aDispatchTarget,
|
|
206
|
+
resolveA2aHttpJsonEndpoint
|
|
180
207
|
});
|
|
@@ -137,8 +137,34 @@ async function resolveA2aDispatchTarget(agentUrl, options = {}) {
|
|
|
137
137
|
}
|
|
138
138
|
throw new Error("the A2A client returned without issuing a dispatch probe; no endpoint was resolved");
|
|
139
139
|
}
|
|
140
|
+
async function resolveA2aHttpJsonEndpoint(agentUrl, options = {}) {
|
|
141
|
+
const cardFetch = buildGuardedCardFetch(agentUrl, options);
|
|
142
|
+
const isHttpJson = (value) => typeof value === "string" && value.replace(/\s+/g, "").toUpperCase() === "HTTP+JSON";
|
|
143
|
+
for (const cardUrl of buildCardUrls(agentUrl)) {
|
|
144
|
+
let card;
|
|
145
|
+
try {
|
|
146
|
+
const res = await cardFetch(cardUrl, { headers: { accept: "application/json" }, redirect: "manual" });
|
|
147
|
+
if (!res.ok) continue;
|
|
148
|
+
card = await res.json();
|
|
149
|
+
} catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const interfaces = [];
|
|
153
|
+
for (const key of ["supportedInterfaces", "additionalInterfaces"]) {
|
|
154
|
+
const list = card[key];
|
|
155
|
+
if (Array.isArray(list)) interfaces.push(...list.filter((i) => !!i));
|
|
156
|
+
}
|
|
157
|
+
if (typeof card.url === "string") interfaces.push({ url: card.url, transport: card.preferredTransport });
|
|
158
|
+
const match = interfaces.find(
|
|
159
|
+
(i) => typeof i.url === "string" && (isHttpJson(i.protocolBinding) || isHttpJson(i.transport))
|
|
160
|
+
);
|
|
161
|
+
if (match) return match.url;
|
|
162
|
+
}
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
140
165
|
export {
|
|
141
166
|
captureA2aRequest,
|
|
142
167
|
operationFromVectorUrl,
|
|
143
|
-
resolveA2aDispatchTarget
|
|
168
|
+
resolveA2aDispatchTarget,
|
|
169
|
+
resolveA2aHttpJsonEndpoint
|
|
144
170
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grading of the A2A operation-resolution vectors (adcp#7945, GHSA-frxv-c96c-4vqw).
|
|
3
|
+
*
|
|
4
|
+
* `request_signing.required_for` matches the AdCP operation a verifier resolves
|
|
5
|
+
* from the request. Over A2A that operation is the `skill` of the Message's sole
|
|
6
|
+
* DataPart, not the JSON-RPC method (`SendMessage`, `message/send`, ...). A
|
|
7
|
+
* verifier that matched only `tools/call` waved unsigned `create_media_buy`
|
|
8
|
+
* through. These vectors grade that rule black-box, against a live agent.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is not routed through the official-client capture
|
|
11
|
+
*
|
|
12
|
+
* The root vectors are framed by `@a2a-js/sdk` (`a2a-dispatch.ts`) because the
|
|
13
|
+
* request must be one a real buyer emits. These vectors are the opposite: the
|
|
14
|
+
* defect under test lives IN the bytes (a duplicate DataPart, a case-variant
|
|
15
|
+
* method, a batch array, a lone `Signature` header), and a well-behaved client
|
|
16
|
+
* refuses to emit most of them. So each vector's body and headers go out
|
|
17
|
+
* verbatim. Only the destination changes: the vector's URL is a placeholder, so
|
|
18
|
+
* the request is retargeted at the agent's real JSON-RPC endpoint, discovered
|
|
19
|
+
* from the agent card the same way the official-client path does. The four
|
|
20
|
+
* signed vectors are re-signed against that real URL with the shared test key
|
|
21
|
+
* (their recorded signatures are bound to the placeholder URL and cannot be
|
|
22
|
+
* replayed).
|
|
23
|
+
*/
|
|
24
|
+
import { type A2aVectorSource } from './vector-loader.mjs';
|
|
25
|
+
import type { SignedRequestsRunnerContract } from './test-kit.mjs';
|
|
26
|
+
import type { GradeOptions, VectorGradeResult } from './grader.mjs';
|
|
27
|
+
import type { A2aVectorTier, TestKeyset } from './types.mjs';
|
|
28
|
+
/** Id of the synthetic skipped row reported when no A2A vectors are available. */
|
|
29
|
+
export declare const A2A_UNAVAILABLE_ROW_ID = "a2a/operation-resolution-vectors";
|
|
30
|
+
export interface A2aTierSummary {
|
|
31
|
+
passed: number;
|
|
32
|
+
failed: number;
|
|
33
|
+
skipped: number;
|
|
34
|
+
/** Ids of failed vectors in this tier. */
|
|
35
|
+
failed_vectors: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface A2aOperationResolutionSummary {
|
|
38
|
+
/** `false` when the compliance bundle (and any override) carried no A2A vectors. */
|
|
39
|
+
vectors_available: boolean;
|
|
40
|
+
source: A2aVectorSource;
|
|
41
|
+
source_dir?: string;
|
|
42
|
+
/** Set when `vectors_available` is `false`: why nothing was graded. */
|
|
43
|
+
message?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Per-tier counts. `contradiction-resolution` is MUST for a 3.2 verifier;
|
|
46
|
+
* `hardening` is SHOULD in 3.2.x and MUST from 3.3. Both count toward the
|
|
47
|
+
* report's pass/fail; the split tells an operator which one failed.
|
|
48
|
+
*/
|
|
49
|
+
tiers: Record<A2aVectorTier, A2aTierSummary>;
|
|
50
|
+
}
|
|
51
|
+
export interface A2aGradeOutcome {
|
|
52
|
+
positive: VectorGradeResult[];
|
|
53
|
+
negative: VectorGradeResult[];
|
|
54
|
+
summary: A2aOperationResolutionSummary;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Grade every A2A operation-resolution vector against *agentUrl*. Returns
|
|
58
|
+
* `undefined` when the caller restricted the run (`onlyVectors`) to ids outside
|
|
59
|
+
* this corpus, so a single-vector regression run is not buried in skip rows.
|
|
60
|
+
*/
|
|
61
|
+
export declare function gradeA2aOperationResolution(agentUrl: string, keys: TestKeyset, contract: SignedRequestsRunnerContract | undefined, options: GradeOptions): Promise<A2aGradeOutcome | undefined>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grading of the A2A operation-resolution vectors (adcp#7945, GHSA-frxv-c96c-4vqw).
|
|
3
|
+
*
|
|
4
|
+
* `request_signing.required_for` matches the AdCP operation a verifier resolves
|
|
5
|
+
* from the request. Over A2A that operation is the `skill` of the Message's sole
|
|
6
|
+
* DataPart, not the JSON-RPC method (`SendMessage`, `message/send`, ...). A
|
|
7
|
+
* verifier that matched only `tools/call` waved unsigned `create_media_buy`
|
|
8
|
+
* through. These vectors grade that rule black-box, against a live agent.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is not routed through the official-client capture
|
|
11
|
+
*
|
|
12
|
+
* The root vectors are framed by `@a2a-js/sdk` (`a2a-dispatch.ts`) because the
|
|
13
|
+
* request must be one a real buyer emits. These vectors are the opposite: the
|
|
14
|
+
* defect under test lives IN the bytes (a duplicate DataPart, a case-variant
|
|
15
|
+
* method, a batch array, a lone `Signature` header), and a well-behaved client
|
|
16
|
+
* refuses to emit most of them. So each vector's body and headers go out
|
|
17
|
+
* verbatim. Only the destination changes: the vector's URL is a placeholder, so
|
|
18
|
+
* the request is retargeted at the agent's real JSON-RPC endpoint, discovered
|
|
19
|
+
* from the agent card the same way the official-client path does. The four
|
|
20
|
+
* signed vectors are re-signed against that real URL with the shared test key
|
|
21
|
+
* (their recorded signatures are bound to the placeholder URL and cannot be
|
|
22
|
+
* replayed).
|
|
23
|
+
*/
|
|
24
|
+
import { type A2aVectorSource } from './vector-loader';
|
|
25
|
+
import type { SignedRequestsRunnerContract } from './test-kit';
|
|
26
|
+
import type { GradeOptions, VectorGradeResult } from './grader';
|
|
27
|
+
import type { A2aVectorTier, TestKeyset } from './types';
|
|
28
|
+
/** Id of the synthetic skipped row reported when no A2A vectors are available. */
|
|
29
|
+
export declare const A2A_UNAVAILABLE_ROW_ID = "a2a/operation-resolution-vectors";
|
|
30
|
+
export interface A2aTierSummary {
|
|
31
|
+
passed: number;
|
|
32
|
+
failed: number;
|
|
33
|
+
skipped: number;
|
|
34
|
+
/** Ids of failed vectors in this tier. */
|
|
35
|
+
failed_vectors: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface A2aOperationResolutionSummary {
|
|
38
|
+
/** `false` when the compliance bundle (and any override) carried no A2A vectors. */
|
|
39
|
+
vectors_available: boolean;
|
|
40
|
+
source: A2aVectorSource;
|
|
41
|
+
source_dir?: string;
|
|
42
|
+
/** Set when `vectors_available` is `false`: why nothing was graded. */
|
|
43
|
+
message?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Per-tier counts. `contradiction-resolution` is MUST for a 3.2 verifier;
|
|
46
|
+
* `hardening` is SHOULD in 3.2.x and MUST from 3.3. Both count toward the
|
|
47
|
+
* report's pass/fail; the split tells an operator which one failed.
|
|
48
|
+
*/
|
|
49
|
+
tiers: Record<A2aVectorTier, A2aTierSummary>;
|
|
50
|
+
}
|
|
51
|
+
export interface A2aGradeOutcome {
|
|
52
|
+
positive: VectorGradeResult[];
|
|
53
|
+
negative: VectorGradeResult[];
|
|
54
|
+
summary: A2aOperationResolutionSummary;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Grade every A2A operation-resolution vector against *agentUrl*. Returns
|
|
58
|
+
* `undefined` when the caller restricted the run (`onlyVectors`) to ids outside
|
|
59
|
+
* this corpus, so a single-vector regression run is not buried in skip rows.
|
|
60
|
+
*/
|
|
61
|
+
export declare function gradeA2aOperationResolution(agentUrl: string, keys: TestKeyset, contract: SignedRequestsRunnerContract | undefined, options: GradeOptions): Promise<A2aGradeOutcome | undefined>;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var a2a_operation_resolution_exports = {};
|
|
20
|
+
__export(a2a_operation_resolution_exports, {
|
|
21
|
+
A2A_UNAVAILABLE_ROW_ID: () => A2A_UNAVAILABLE_ROW_ID,
|
|
22
|
+
gradeA2aOperationResolution: () => gradeA2aOperationResolution
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(a2a_operation_resolution_exports);
|
|
25
|
+
var import_builder = require('./builder.js');
|
|
26
|
+
var import_probe = require('./probe.js');
|
|
27
|
+
var import_a2a_dispatch = require('./a2a-dispatch.js');
|
|
28
|
+
var import_vector_loader = require('./vector-loader.js');
|
|
29
|
+
const A2A_UNAVAILABLE_ROW_ID = "a2a/operation-resolution-vectors";
|
|
30
|
+
const ALTERNATE_ACCEPTED_CODES = {
|
|
31
|
+
"a2a/negative/023-batch-body": ["request_signature_required"]
|
|
32
|
+
};
|
|
33
|
+
function emptyTiers() {
|
|
34
|
+
return {
|
|
35
|
+
"contradiction-resolution": { passed: 0, failed: 0, skipped: 0, failed_vectors: [] },
|
|
36
|
+
hardening: { passed: 0, failed: 0, skipped: 0, failed_vectors: [] }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function gradeA2aOperationResolution(agentUrl, keys, contract, options) {
|
|
40
|
+
if (options.onlyVectors && !options.onlyVectors.some((id) => id.startsWith("a2a/"))) return void 0;
|
|
41
|
+
const loaded = (0, import_vector_loader.loadA2aOperationResolutionVectors)(options);
|
|
42
|
+
const tiers = emptyTiers();
|
|
43
|
+
if (loaded.source === "none") {
|
|
44
|
+
return {
|
|
45
|
+
positive: [],
|
|
46
|
+
negative: [
|
|
47
|
+
{
|
|
48
|
+
vector_id: A2A_UNAVAILABLE_ROW_ID,
|
|
49
|
+
kind: "negative",
|
|
50
|
+
passed: true,
|
|
51
|
+
skipped: true,
|
|
52
|
+
skip_reason: "a2a_vectors_unavailable",
|
|
53
|
+
diagnostic: import_vector_loader.A2A_VECTORS_UNAVAILABLE_MESSAGE,
|
|
54
|
+
http_status: 0,
|
|
55
|
+
probe_duration_ms: 0
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
summary: {
|
|
59
|
+
vectors_available: false,
|
|
60
|
+
source: "none",
|
|
61
|
+
message: import_vector_loader.A2A_VECTORS_UNAVAILABLE_MESSAGE,
|
|
62
|
+
tiers
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const dispatchOptions = {
|
|
67
|
+
allowPrivateIp: options.allowPrivateIp === true,
|
|
68
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
69
|
+
...options.cardFetch ? { cardFetch: options.cardFetch } : {}
|
|
70
|
+
};
|
|
71
|
+
const probeOptions = {
|
|
72
|
+
allowPrivateIp: options.allowPrivateIp === true,
|
|
73
|
+
timeoutMs: options.timeoutMs
|
|
74
|
+
};
|
|
75
|
+
let jsonRpcEndpoint;
|
|
76
|
+
const jsonRpc = () => jsonRpcEndpoint ??= (0, import_a2a_dispatch.resolveA2aDispatchTarget)(agentUrl, dispatchOptions).then((target) => target.endpoint);
|
|
77
|
+
let httpJsonEndpoint;
|
|
78
|
+
const httpJson = () => httpJsonEndpoint ??= (0, import_a2a_dispatch.resolveA2aHttpJsonEndpoint)(agentUrl, dispatchOptions);
|
|
79
|
+
const requiredFor = options.agentCapability?.required_for ?? options.agentRequiredFor;
|
|
80
|
+
const protocolMethodsRequiredFor = options.agentCapability !== void 0 ? options.agentCapability.protocol_methods_required_for ?? [] : options.agentProtocolMethodsRequiredFor;
|
|
81
|
+
const contentDigestPolicy = options.agentCapability?.covers_content_digest ?? options.agentContentDigestPolicy;
|
|
82
|
+
const results = [];
|
|
83
|
+
const grade = async (vector) => {
|
|
84
|
+
const skip = preflightSkip(vector, {
|
|
85
|
+
options,
|
|
86
|
+
contract,
|
|
87
|
+
requiredFor,
|
|
88
|
+
protocolMethodsRequiredFor,
|
|
89
|
+
contentDigestPolicy
|
|
90
|
+
});
|
|
91
|
+
if (skip) return skip;
|
|
92
|
+
let url;
|
|
93
|
+
if (isHttpJsonBinding(vector)) {
|
|
94
|
+
const base = await httpJson();
|
|
95
|
+
if (!base) {
|
|
96
|
+
return skipped(
|
|
97
|
+
vector,
|
|
98
|
+
"transport_ungradable",
|
|
99
|
+
`Vector ${vector.id} grades the A2A HTTP+JSON binding (POST \u2026/message:send), but the agent card declares no HTTP+JSON interface. An agent that does not serve that binding cannot be bypassed through it.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
url = `${base.replace(/\/+$/, "")}/${lastPathSegment(vector.request.url)}`;
|
|
103
|
+
} else {
|
|
104
|
+
url = await jsonRpc();
|
|
105
|
+
}
|
|
106
|
+
const request = buildRequest(vector, url, keys);
|
|
107
|
+
const probe = await (0, import_probe.probeSignedRequest)(request, probeOptions);
|
|
108
|
+
return vector.kind === "positive" ? gradePositive(vector, probe) : gradeNegative(vector, probe);
|
|
109
|
+
};
|
|
110
|
+
for (const vector of [...loaded.positive, ...loaded.negative]) {
|
|
111
|
+
const result = await grade(vector);
|
|
112
|
+
results.push(result);
|
|
113
|
+
const tier = tiers[vector.tier];
|
|
114
|
+
if (result.skipped) tier.skipped++;
|
|
115
|
+
else if (result.passed) tier.passed++;
|
|
116
|
+
else {
|
|
117
|
+
tier.failed++;
|
|
118
|
+
tier.failed_vectors.push(vector.id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
positive: results.filter((r) => r.kind === "positive"),
|
|
123
|
+
negative: results.filter((r) => r.kind === "negative"),
|
|
124
|
+
summary: {
|
|
125
|
+
vectors_available: true,
|
|
126
|
+
source: loaded.source,
|
|
127
|
+
...loaded.sourceDir ? { source_dir: loaded.sourceDir } : {},
|
|
128
|
+
tiers
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function skipped(vector, skip_reason, diagnostic) {
|
|
133
|
+
return {
|
|
134
|
+
vector_id: vector.id,
|
|
135
|
+
kind: vector.kind,
|
|
136
|
+
tier: vector.tier,
|
|
137
|
+
passed: true,
|
|
138
|
+
// skipped is not failed; overall pass/fail excludes skipped
|
|
139
|
+
skipped: true,
|
|
140
|
+
skip_reason,
|
|
141
|
+
...vector.kind === "negative" && vector.expected_outcome.error_code ? { expected_error_code: vector.expected_outcome.error_code } : {},
|
|
142
|
+
...diagnostic ? { diagnostic } : {},
|
|
143
|
+
http_status: 0,
|
|
144
|
+
probe_duration_ms: 0
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function preflightSkip(vector, ctx) {
|
|
148
|
+
const { options } = ctx;
|
|
149
|
+
if (options.onlyVectors && !options.onlyVectors.includes(vector.id)) {
|
|
150
|
+
return skipped(vector, "not_in_only_vectors");
|
|
151
|
+
}
|
|
152
|
+
if (options.skipVectors?.includes(vector.id)) return skipped(vector, "operator_skip");
|
|
153
|
+
const mismatch = capabilityMismatch(vector, ctx.requiredFor, ctx.protocolMethodsRequiredFor);
|
|
154
|
+
if (mismatch) return skipped(vector, "capability_profile_mismatch", mismatch);
|
|
155
|
+
if (vector.kind === "positive" && isSigned(vector) && ctx.contentDigestPolicy === "forbidden") {
|
|
156
|
+
return skipped(
|
|
157
|
+
vector,
|
|
158
|
+
"capability_profile_mismatch",
|
|
159
|
+
`Vector ${vector.id} is signed with content-digest covered, but the agent declares covers_content_digest='forbidden'.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
if (vector.kind === "positive" && vector.expected_outcome.status === "verified" && !options.allowLiveSideEffects && ctx.contract?.endpoint_scope !== "sandbox") {
|
|
163
|
+
return skipped(
|
|
164
|
+
vector,
|
|
165
|
+
"live_side_effect_opt_in_required",
|
|
166
|
+
`Vector ${vector.id} sends a validly signed ${vector.expected_outcome.resolved_operation ?? "operation"} request the agent will accept and run. Pass allowLiveSideEffects: true (or point the grader at an endpoint whose signed-requests-runner contract declares endpoint_scope: sandbox) to run it.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
function capabilityMismatch(vector, requiredFor, protocolMethodsRequiredFor) {
|
|
172
|
+
if (vector.kind === "negative") {
|
|
173
|
+
if (vector.expected_outcome.error_code !== "request_signature_required") return void 0;
|
|
174
|
+
const methods = vector.verifier_capability.protocol_methods_required_for ?? [];
|
|
175
|
+
if (methods.length > 0) {
|
|
176
|
+
if (!protocolMethodsRequiredFor) return void 0;
|
|
177
|
+
const missing = methods.filter((m) => !protocolMethodsRequiredFor.includes(m));
|
|
178
|
+
return missing.length === 0 ? void 0 : `Vector asserts protocol_methods_required_for includes [${missing.join(", ")}] but the agent declares [${protocolMethodsRequiredFor.join(", ")}]. Add the method to request_signing.protocol_methods_required_for to grade it.`;
|
|
179
|
+
}
|
|
180
|
+
const operation2 = vector.expected_outcome.resolved_operation;
|
|
181
|
+
if (!requiredFor || typeof operation2 !== "string" || requiredFor.includes(operation2)) return void 0;
|
|
182
|
+
return `Vector asserts ${operation2} is in required_for but the agent declares required_for [${requiredFor.join(", ")}]. The vector tests a rejection path the agent has not opted into; add the operation to request_signing.required_for to grade it.`;
|
|
183
|
+
}
|
|
184
|
+
if (vector.expected_outcome.status !== "unsigned") return void 0;
|
|
185
|
+
const operation = vector.expected_outcome.resolved_operation;
|
|
186
|
+
if (requiredFor && typeof operation === "string" && requiredFor.includes(operation)) {
|
|
187
|
+
return `Vector asserts ${operation} needs no signature, but the agent declares it in required_for.`;
|
|
188
|
+
}
|
|
189
|
+
const method = jsonRpcMethodOf(vector.request.body);
|
|
190
|
+
if (protocolMethodsRequiredFor && method !== void 0 && protocolMethodsRequiredFor.includes(method)) {
|
|
191
|
+
return `Vector asserts an unsigned ${method} is accepted, but the agent declares ${method} in protocol_methods_required_for.`;
|
|
192
|
+
}
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
function hasHeader(headers, name) {
|
|
196
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === name);
|
|
197
|
+
}
|
|
198
|
+
function isSigned(vector) {
|
|
199
|
+
return hasHeader(vector.request.headers, "signature-input");
|
|
200
|
+
}
|
|
201
|
+
function isHttpJsonBinding(vector) {
|
|
202
|
+
try {
|
|
203
|
+
return /(?:^|\/)message:(?:send|stream)$/.test(new URL(vector.request.url).pathname);
|
|
204
|
+
} catch {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function lastPathSegment(url) {
|
|
209
|
+
return new URL(url).pathname.split("/").filter(Boolean).pop() ?? "";
|
|
210
|
+
}
|
|
211
|
+
function jsonRpcMethodOf(body) {
|
|
212
|
+
if (!body) return void 0;
|
|
213
|
+
try {
|
|
214
|
+
const parsed = JSON.parse(body);
|
|
215
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
216
|
+
const method = parsed.method;
|
|
217
|
+
return typeof method === "string" ? method : void 0;
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
return void 0;
|
|
222
|
+
}
|
|
223
|
+
function buildRequest(vector, url, keys) {
|
|
224
|
+
const request = { ...vector.request, url, headers: { ...vector.request.headers } };
|
|
225
|
+
if (!isSigned(vector)) return request;
|
|
226
|
+
const asPositive = {
|
|
227
|
+
kind: "positive",
|
|
228
|
+
id: vector.id,
|
|
229
|
+
name: vector.name,
|
|
230
|
+
signing_profile_version: vector.signing_profile_version,
|
|
231
|
+
reference_now: vector.reference_now,
|
|
232
|
+
request,
|
|
233
|
+
verifier_capability: vector.verifier_capability,
|
|
234
|
+
jwks_ref: vector.jwks_ref
|
|
235
|
+
};
|
|
236
|
+
return (0, import_builder.buildPositiveRequest)(asPositive, keys, { transport: "raw" });
|
|
237
|
+
}
|
|
238
|
+
function isSignatureChallenge(probe) {
|
|
239
|
+
if (probe.status !== 401) return false;
|
|
240
|
+
if (probe.wwwAuthenticateErrorCode !== void 0) return true;
|
|
241
|
+
return /(?:^|,)\s*Signature\b/i.test(probe.headers["www-authenticate"] ?? "");
|
|
242
|
+
}
|
|
243
|
+
function gradePositive(vector, probe) {
|
|
244
|
+
const rejected = isSignatureChallenge(probe);
|
|
245
|
+
const passed = !probe.error && !rejected;
|
|
246
|
+
let diagnostic;
|
|
247
|
+
if (probe.error) diagnostic = `probe error: ${probe.error}`;
|
|
248
|
+
else if (rejected) {
|
|
249
|
+
const code = probe.wwwAuthenticateErrorCode ?? "(none)";
|
|
250
|
+
diagnostic = vector.expected_outcome.status === "verified" ? `expected the signed request to be accepted past the signature layer, got 401 with error="${code}". Check the grader test key is registered with the agent. Vector: ${vector.name}` : `expected no signature rejection (the resolved operation does not require one), got 401 with error="${code}". Vector: ${vector.name}`;
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
vector_id: vector.id,
|
|
254
|
+
kind: "positive",
|
|
255
|
+
tier: vector.tier,
|
|
256
|
+
passed,
|
|
257
|
+
http_status: probe.status,
|
|
258
|
+
probe_url: probe.url,
|
|
259
|
+
...probe.wwwAuthenticateErrorCode ? { actual_error_code: probe.wwwAuthenticateErrorCode } : {},
|
|
260
|
+
...diagnostic ? { diagnostic } : {},
|
|
261
|
+
...probe.error !== void 0 && { transport_error: true },
|
|
262
|
+
probe_duration_ms: probe.duration_ms
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function gradeNegative(vector, probe) {
|
|
266
|
+
const expected = vector.expected_error_code;
|
|
267
|
+
const accepted = [expected, ...ALTERNATE_ACCEPTED_CODES[vector.id] ?? []];
|
|
268
|
+
const actual = probe.wwwAuthenticateErrorCode;
|
|
269
|
+
const passed = !probe.error && probe.status === 401 && actual !== void 0 && accepted.includes(actual);
|
|
270
|
+
let diagnostic;
|
|
271
|
+
if (!passed) {
|
|
272
|
+
const wanted = accepted.map((code) => `"${code}"`).join(" or ");
|
|
273
|
+
if (probe.error) diagnostic = `probe error: ${probe.error}`;
|
|
274
|
+
else if (probe.status !== 401) {
|
|
275
|
+
diagnostic = `expected 401 with error=${wanted}, got ${probe.status}. The request reached the agent's dispatcher: the verifier did not resolve the operation from the A2A DataPart skill (GHSA-frxv-c96c-4vqw). Vector: ${vector.name}`;
|
|
276
|
+
} else {
|
|
277
|
+
diagnostic = `expected error=${wanted}, got error="${actual ?? "(none)"}". Vector: ${vector.name}`;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
vector_id: vector.id,
|
|
282
|
+
kind: "negative",
|
|
283
|
+
tier: vector.tier,
|
|
284
|
+
passed,
|
|
285
|
+
http_status: probe.status,
|
|
286
|
+
probe_url: probe.url,
|
|
287
|
+
expected_error_code: expected,
|
|
288
|
+
...actual ? { actual_error_code: actual } : {},
|
|
289
|
+
...diagnostic ? { diagnostic } : {},
|
|
290
|
+
...probe.error !== void 0 && { transport_error: true },
|
|
291
|
+
probe_duration_ms: probe.duration_ms
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
295
|
+
0 && (module.exports = {
|
|
296
|
+
A2A_UNAVAILABLE_ROW_ID,
|
|
297
|
+
gradeA2aOperationResolution
|
|
298
|
+
});
|