@adcp/sdk 13.0.4 → 13.1.1
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 +4 -3
- package/bin/adcp.js +18 -0
- package/dist/lib/conformance/invariants/uniformError.d.ts.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.js +21 -0
- package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +22 -1
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.mts +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +1 -1
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +1 -1
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +88 -48
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +88 -48
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +8 -4
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +8 -4
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/net/agent-transport-fetch.d.mts +15 -0
- package/dist/lib/net/agent-transport-fetch.d.ts +15 -0
- package/dist/lib/net/agent-transport-fetch.d.ts.map +1 -1
- package/dist/lib/net/agent-transport-fetch.js +37 -2
- package/dist/lib/net/agent-transport-fetch.js.map +1 -1
- package/dist/lib/net/agent-transport-fetch.mjs +37 -2
- package/dist/lib/net/agent-transport-fetch.mjs.map +1 -1
- package/dist/lib/net/credential-headers.d.mts +1 -0
- package/dist/lib/net/credential-headers.d.ts +2 -0
- package/dist/lib/net/credential-headers.d.ts.map +1 -0
- package/dist/lib/net/credential-headers.js +34 -0
- package/dist/lib/net/credential-headers.js.map +1 -0
- package/dist/lib/net/credential-headers.mjs +10 -0
- package/dist/lib/net/credential-headers.mjs.map +1 -0
- package/dist/lib/protocols/a2a-native-v1.d.mts +18 -0
- package/dist/lib/protocols/a2a-native-v1.d.ts +19 -0
- package/dist/lib/protocols/a2a-native-v1.d.ts.map +1 -0
- package/dist/lib/protocols/a2a-native-v1.js +183 -0
- package/dist/lib/protocols/a2a-native-v1.js.map +1 -0
- package/dist/lib/protocols/a2a-native-v1.mjs +167 -0
- package/dist/lib/protocols/a2a-native-v1.mjs.map +1 -0
- package/dist/lib/protocols/a2a-push-notification.d.mts +13 -0
- package/dist/lib/protocols/a2a-push-notification.d.ts +14 -0
- package/dist/lib/protocols/a2a-push-notification.d.ts.map +1 -0
- package/dist/lib/protocols/a2a-push-notification.js +42 -0
- package/dist/lib/protocols/a2a-push-notification.js.map +1 -0
- package/dist/lib/protocols/a2a-push-notification.mjs +23 -0
- package/dist/lib/protocols/a2a-push-notification.mjs.map +1 -0
- package/dist/lib/protocols/a2a.d.mts +15 -5
- package/dist/lib/protocols/a2a.d.ts +15 -5
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +102 -13
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/a2a.mjs +90 -12
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/index.d.mts +3 -0
- package/dist/lib/protocols/index.d.ts +3 -0
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +18 -6
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +18 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-modern.js +3 -1
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +3 -1
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-tasks.js +2 -1
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +2 -1
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.d.mts +8 -5
- package/dist/lib/protocols/mcp.d.ts +8 -5
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +12 -4
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/protocols/mcp.mjs +12 -4
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.d.mts +9 -1
- package/dist/lib/protocols/rawResponseCapture.d.ts +9 -1
- package/dist/lib/protocols/rawResponseCapture.d.ts.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.js +153 -10
- package/dist/lib/protocols/rawResponseCapture.js.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +153 -10
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/signing/agent-fetch.d.mts +3 -1
- package/dist/lib/signing/agent-fetch.d.ts +3 -1
- package/dist/lib/signing/agent-fetch.d.ts.map +1 -1
- package/dist/lib/signing/agent-fetch.js +4 -2
- package/dist/lib/signing/agent-fetch.js.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +4 -2
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/protocol-methods.d.mts +24 -0
- package/dist/lib/signing/protocol-methods.d.ts +25 -0
- package/dist/lib/signing/protocol-methods.d.ts.map +1 -0
- package/dist/lib/signing/protocol-methods.js +51 -0
- package/dist/lib/signing/protocol-methods.js.map +1 -0
- package/dist/lib/signing/protocol-methods.mjs +25 -0
- package/dist/lib/signing/protocol-methods.mjs.map +1 -0
- package/dist/lib/signing/types.d.mts +2 -0
- package/dist/lib/signing/types.d.ts +2 -0
- package/dist/lib/signing/types.d.ts.map +1 -1
- package/dist/lib/signing/types.js.map +1 -1
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.d.ts.map +1 -1
- package/dist/lib/signing/verifier.js +6 -2
- package/dist/lib/signing/verifier.js.map +1 -1
- package/dist/lib/signing/verifier.mjs +6 -2
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +4 -1
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/client.mjs +4 -1
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +3 -0
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +3 -0
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.d.mts +3 -1
- package/dist/lib/testing/storyboard/agent-routing.d.ts +3 -1
- package/dist/lib/testing/storyboard/agent-routing.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.js +5 -2
- package/dist/lib/testing/storyboard/agent-routing.js.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +4 -2
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.d.mts +1 -1
- package/dist/lib/testing/storyboard/compliance.d.ts +1 -1
- package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/compliance.js +4 -1
- package/dist/lib/testing/storyboard/compliance.js.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +4 -1
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/native-a2a-compliance.d.mts +7 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.d.ts +8 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.js +45 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.js.map +1 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.mjs +21 -0
- package/dist/lib/testing/storyboard/native-a2a-compliance.mjs.map +1 -0
- package/dist/lib/testing/storyboard/probes.d.mts +2 -0
- package/dist/lib/testing/storyboard/probes.d.ts +2 -0
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +3 -2
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +3 -2
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +31 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +32 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +279 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js.map +1 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +244 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs.map +1 -0
- package/dist/lib/testing/storyboard/request-signing/builder.d.mts +4 -1
- package/dist/lib/testing/storyboard/request-signing/builder.d.ts +4 -1
- package/dist/lib/testing/storyboard/request-signing/builder.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.js +19 -0
- package/dist/lib/testing/storyboard/request-signing/builder.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +19 -0
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.d.mts +5 -6
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts +5 -6
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.js +82 -13
- package/dist/lib/testing/storyboard/request-signing/grader.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +86 -13
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.d.mts +1 -0
- package/dist/lib/testing/storyboard/request-signing/index.d.ts +1 -0
- package/dist/lib/testing/storyboard/request-signing/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.js +7 -0
- package/dist/lib/testing/storyboard/request-signing/index.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +8 -0
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.mts +2 -2
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts +2 -2
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js +6 -5
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +6 -5
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.d.mts +1 -0
- package/dist/lib/testing/storyboard/request-signing/types.d.ts +1 -0
- package/dist/lib/testing/storyboard/request-signing/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js +1 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +1 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.mts +28 -1
- package/dist/lib/testing/storyboard/runner.d.ts +28 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +251 -55
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +255 -60
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.d.mts +4 -2
- package/dist/lib/testing/storyboard/types.d.ts +4 -2
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js +1 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +1 -0
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +97 -22
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +98 -22
- package/dist/lib/testing/storyboard/validations.mjs.map +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.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/guides/BUILD-AN-AGENT.md +1 -1
- package/docs/llms.txt +5 -3
- package/package.json +2 -1
|
@@ -25,12 +25,29 @@ __export(rawResponseCapture_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(rawResponseCapture_exports);
|
|
27
27
|
var import_global_async_local_storage = require('../utils/global-async-local-storage.js');
|
|
28
|
+
var import_abort = require('./abort.js');
|
|
28
29
|
const DEFAULT_MAX_BODY_BYTES = 1048576;
|
|
30
|
+
const DEFAULT_REQUEST_METADATA_TIMEOUT_MS = 1e3;
|
|
31
|
+
const DEFAULT_RESPONSE_BODY_TIMEOUT_MS = 1e4;
|
|
29
32
|
const rawResponseCaptureStorage = (0, import_global_async_local_storage.globalAsyncLocalStorage)("rawResponseCapture");
|
|
30
33
|
async function withRawResponseCapture(fn, options = {}) {
|
|
34
|
+
const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
35
|
+
if (!Number.isSafeInteger(maxBodyBytes) || maxBodyBytes <= 0) {
|
|
36
|
+
throw new RangeError("maxBodyBytes must be a finite safe positive integer");
|
|
37
|
+
}
|
|
38
|
+
const requestMetadataTimeoutMs = options.requestMetadataTimeoutMs ?? DEFAULT_REQUEST_METADATA_TIMEOUT_MS;
|
|
39
|
+
if (!Number.isFinite(requestMetadataTimeoutMs) || requestMetadataTimeoutMs <= 0 || requestMetadataTimeoutMs > import_abort.MAX_TIMER_DELAY_MS) {
|
|
40
|
+
throw new RangeError(`requestMetadataTimeoutMs must be a finite positive number <= ${import_abort.MAX_TIMER_DELAY_MS}`);
|
|
41
|
+
}
|
|
42
|
+
const responseBodyTimeoutMs = options.responseBodyTimeoutMs ?? DEFAULT_RESPONSE_BODY_TIMEOUT_MS;
|
|
43
|
+
if (!Number.isFinite(responseBodyTimeoutMs) || responseBodyTimeoutMs <= 0 || responseBodyTimeoutMs > import_abort.MAX_TIMER_DELAY_MS) {
|
|
44
|
+
throw new RangeError(`responseBodyTimeoutMs must be a finite positive number <= ${import_abort.MAX_TIMER_DELAY_MS}`);
|
|
45
|
+
}
|
|
31
46
|
const slot = {
|
|
32
47
|
captures: [],
|
|
33
|
-
maxBodyBytes
|
|
48
|
+
maxBodyBytes,
|
|
49
|
+
requestMetadataTimeoutMs,
|
|
50
|
+
responseBodyTimeoutMs
|
|
34
51
|
};
|
|
35
52
|
try {
|
|
36
53
|
const result = await rawResponseCaptureStorage.run(slot, fn);
|
|
@@ -71,11 +88,22 @@ function wrapFetchWithCapture(upstream) {
|
|
|
71
88
|
if (!slot) return upstream(input, init);
|
|
72
89
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
73
90
|
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
91
|
+
const requestBody = init?.body !== void 0 ? init.body : input instanceof Request ? await readRequestBodyFromClone(
|
|
92
|
+
input,
|
|
93
|
+
slot.maxBodyBytes,
|
|
94
|
+
init?.signal ?? input.signal,
|
|
95
|
+
slot.requestMetadataTimeoutMs
|
|
96
|
+
) : void 0;
|
|
97
|
+
const requestMetadata = extractSafeRequestMetadata(requestBody, slot.maxBodyBytes);
|
|
74
98
|
const startedAt = Date.now();
|
|
75
99
|
const response = await upstream(input, init);
|
|
76
100
|
const latencyMs = Date.now() - startedAt;
|
|
77
101
|
const cloneForRead = response.clone();
|
|
78
|
-
const { body, bodyTruncated } = await readBodyBounded(
|
|
102
|
+
const { body, bodyTruncated, bodyCaptureError } = await readBodyBounded(
|
|
103
|
+
cloneForRead,
|
|
104
|
+
slot.maxBodyBytes,
|
|
105
|
+
slot.responseBodyTimeoutMs
|
|
106
|
+
);
|
|
79
107
|
const headers = {};
|
|
80
108
|
response.headers.forEach((value, key) => {
|
|
81
109
|
const lower = key.toLowerCase();
|
|
@@ -84,29 +112,144 @@ function wrapFetchWithCapture(upstream) {
|
|
|
84
112
|
slot.captures.push({
|
|
85
113
|
url,
|
|
86
114
|
method,
|
|
115
|
+
...requestMetadata,
|
|
87
116
|
status: response.status,
|
|
88
117
|
headers,
|
|
89
118
|
body: redactBearerInBody(body),
|
|
90
119
|
latencyMs,
|
|
91
120
|
timestamp: new Date(startedAt).toISOString(),
|
|
92
|
-
bodyTruncated
|
|
121
|
+
bodyTruncated,
|
|
122
|
+
...bodyCaptureError !== void 0 && { bodyCaptureError }
|
|
93
123
|
});
|
|
94
124
|
return response;
|
|
95
125
|
};
|
|
96
126
|
return wrapped;
|
|
97
127
|
}
|
|
128
|
+
async function readRequestBodyFromClone(request, maxBodyBytes, signal, timeoutMs) {
|
|
129
|
+
if (request.bodyUsed || request.method === "GET" || request.method === "HEAD") return void 0;
|
|
130
|
+
try {
|
|
131
|
+
const body = request.clone().body;
|
|
132
|
+
if (!body) return "";
|
|
133
|
+
const reader = body.getReader();
|
|
134
|
+
return await (0, import_abort.withAbortSignal)([signal], timeoutMs, async (captureSignal) => {
|
|
135
|
+
const cancel = () => void reader.cancel(captureSignal?.reason).catch(() => void 0);
|
|
136
|
+
captureSignal?.addEventListener("abort", cancel, { once: true });
|
|
137
|
+
try {
|
|
138
|
+
const chunks = [];
|
|
139
|
+
let length = 0;
|
|
140
|
+
while (true) {
|
|
141
|
+
const { done, value } = await reader.read();
|
|
142
|
+
if (done) break;
|
|
143
|
+
length += value.byteLength;
|
|
144
|
+
if (length > maxBodyBytes) {
|
|
145
|
+
void reader.cancel().catch(() => void 0);
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
chunks.push(value);
|
|
149
|
+
}
|
|
150
|
+
const bytes = new Uint8Array(length);
|
|
151
|
+
let offset = 0;
|
|
152
|
+
for (const chunk of chunks) {
|
|
153
|
+
bytes.set(chunk, offset);
|
|
154
|
+
offset += chunk.byteLength;
|
|
155
|
+
}
|
|
156
|
+
return new TextDecoder().decode(bytes);
|
|
157
|
+
} finally {
|
|
158
|
+
captureSignal?.removeEventListener("abort", cancel);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
} catch {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function extractSafeRequestMetadata(body, maxBodyBytes) {
|
|
166
|
+
if (typeof body !== "string" || body.length > maxBodyBytes) return {};
|
|
167
|
+
let parsed;
|
|
168
|
+
try {
|
|
169
|
+
parsed = JSON.parse(body);
|
|
170
|
+
} catch {
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
174
|
+
const envelope = parsed;
|
|
175
|
+
const requestJsonRpcMethod = typeof envelope.method === "string" ? envelope.method : void 0;
|
|
176
|
+
let requestAdcpSkill;
|
|
177
|
+
const params = envelope.params != null && typeof envelope.params === "object" && !Array.isArray(envelope.params) ? envelope.params : void 0;
|
|
178
|
+
const message = params?.message != null && typeof params.message === "object" && !Array.isArray(params.message) ? params.message : void 0;
|
|
179
|
+
if (Array.isArray(message?.parts)) {
|
|
180
|
+
for (const part of message.parts) {
|
|
181
|
+
if (part == null || typeof part !== "object" || Array.isArray(part)) continue;
|
|
182
|
+
const data = part.data;
|
|
183
|
+
const content = part.content;
|
|
184
|
+
const nativeData = content != null && typeof content === "object" && !Array.isArray(content) ? content.data : void 0;
|
|
185
|
+
const value = data ?? nativeData;
|
|
186
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
187
|
+
const skill = value.skill;
|
|
188
|
+
if (typeof skill === "string") {
|
|
189
|
+
requestAdcpSkill = skill;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
...requestJsonRpcMethod !== void 0 && { requestJsonRpcMethod },
|
|
196
|
+
...requestAdcpSkill !== void 0 && { requestAdcpSkill }
|
|
197
|
+
};
|
|
198
|
+
}
|
|
98
199
|
function redactBearerInBody(body) {
|
|
99
200
|
return body.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]");
|
|
100
201
|
}
|
|
101
|
-
async function readBodyBounded(response, maxBodyBytes) {
|
|
102
|
-
|
|
202
|
+
async function readBodyBounded(response, maxBodyBytes, timeoutMs) {
|
|
203
|
+
if (!response.body) return { body: "", bodyTruncated: false };
|
|
204
|
+
const reader = response.body.getReader();
|
|
205
|
+
const retained = [];
|
|
206
|
+
let retainedBytes = 0;
|
|
207
|
+
const decodeRetained = () => {
|
|
208
|
+
const bytes = new Uint8Array(retainedBytes);
|
|
209
|
+
let offset = 0;
|
|
210
|
+
for (const chunk of retained) {
|
|
211
|
+
bytes.set(chunk, offset);
|
|
212
|
+
offset += chunk.byteLength;
|
|
213
|
+
}
|
|
214
|
+
return new TextDecoder().decode(bytes);
|
|
215
|
+
};
|
|
103
216
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
217
|
+
return await (0, import_abort.withAbortSignal)([], timeoutMs, async (signal) => {
|
|
218
|
+
const cancel = () => void reader.cancel(signal?.reason).catch(() => void 0);
|
|
219
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
220
|
+
try {
|
|
221
|
+
while (true) {
|
|
222
|
+
const { done, value } = await reader.read();
|
|
223
|
+
if (done) {
|
|
224
|
+
return { body: decodeRetained(), bodyTruncated: false };
|
|
225
|
+
}
|
|
226
|
+
const available = maxBodyBytes - retainedBytes;
|
|
227
|
+
const copyBytes = Math.min(available, value.byteLength);
|
|
228
|
+
if (copyBytes > 0) {
|
|
229
|
+
retained.push(value.slice(0, copyBytes));
|
|
230
|
+
retainedBytes += copyBytes;
|
|
231
|
+
}
|
|
232
|
+
if (copyBytes < value.byteLength) {
|
|
233
|
+
void reader.cancel().catch(() => void 0);
|
|
234
|
+
return {
|
|
235
|
+
body: decodeRetained(),
|
|
236
|
+
bodyTruncated: true,
|
|
237
|
+
bodyCaptureError: `Raw response capture exceeded maxBodyBytes (${maxBodyBytes})`
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} finally {
|
|
242
|
+
signal?.removeEventListener("abort", cancel);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
const timedOut = error instanceof Error && error.name === "TimeoutError";
|
|
247
|
+
return {
|
|
248
|
+
body: decodeRetained(),
|
|
249
|
+
bodyTruncated: true,
|
|
250
|
+
bodyCaptureError: timedOut ? `Raw response capture timed out after ${timeoutMs} ms before the response body completed` : "Raw response capture ended before the response body completed"
|
|
251
|
+
};
|
|
107
252
|
}
|
|
108
|
-
if (text.length <= maxBodyBytes) return { body: text, bodyTruncated: false };
|
|
109
|
-
return { body: text.slice(0, maxBodyBytes), bodyTruncated: true };
|
|
110
253
|
}
|
|
111
254
|
// Annotate the CommonJS export names for ESM import in node:
|
|
112
255
|
0 && (module.exports = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/protocols/rawResponseCapture.ts"],"sourcesContent":["// Raw HTTP response capture for conformance probing.\n//\n// Scoped via AsyncLocalStorage so MCP + A2A protocol adapters can record\n// status, headers, body, and latency without threading options through\n// every call site. When the capture slot is absent, the fetch wrapper is a\n// pass-through — production clients pay only one ALS lookup per request.\n//\n// Consumers call `withRawResponseCapture(fn)` and receive captures for\n// every HTTP request that happened inside `fn`. The uniform-error invariant\n// uses the captures to compare two probes byte-for-byte.\n\nimport { globalAsyncLocalStorage } from '../utils/global-async-local-storage';\n\nexport interface RawHttpCapture {\n url: string;\n method: string;\n status: number;\n headers: Record<string, string>;\n body: string;\n latencyMs: number;\n timestamp: string;\n /** True when body capture hit `maxBodyBytes` and was truncated. */\n bodyTruncated: boolean;\n}\n\ninterface CaptureSlot {\n captures: RawHttpCapture[];\n maxBodyBytes: number;\n}\n\n// Counted as UTF-16 code units (string.length), not UTF-8 bytes. Close\n// enough for ASCII-dominant response payloads and fine as a safety cap\n// against accidentally retaining huge responses.\nconst DEFAULT_MAX_BODY_BYTES = 1_048_576;\n\nexport const rawResponseCaptureStorage = globalAsyncLocalStorage<CaptureSlot>('rawResponseCapture');\n\n/**\n * Run `fn` with a raw-response capture slot active. Every HTTP request made\n * through the wrapped fetch inside `fn` is recorded.\n *\n * When `fn` rejects, the rejection propagates and the partial captures\n * are attached to the thrown error as `error.captures`. Callers that\n * need to inspect partial captures on failure can read that property;\n * callers that don't can ignore it. This lets storyboard validators\n * surface \"the SDK threw before the wire shape parsed\" diagnostics\n * with the actual bytes that arrived before the throw.\n */\nexport async function withRawResponseCapture<T>(\n fn: () => Promise<T>,\n options: { maxBodyBytes?: number } = {}\n): Promise<{ result: T; captures: RawHttpCapture[] }> {\n const slot: CaptureSlot = {\n captures: [],\n maxBodyBytes: options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n };\n try {\n const result = await rawResponseCaptureStorage.run(slot, fn);\n return { result, captures: slot.captures };\n } catch (err) {\n if (err && typeof err === 'object') {\n try {\n Object.defineProperty(err, 'captures', {\n value: slot.captures,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n } catch {\n // Frozen / sealed errors won't accept the property — drop the\n // captures rather than crash on a defineProperty TypeError.\n }\n }\n throw err;\n }\n}\n\n/** Type guard for errors carrying partial captures from `withRawResponseCapture`. */\nexport function getCapturesFromError(err: unknown): RawHttpCapture[] | undefined {\n if (err && typeof err === 'object' && Array.isArray((err as { captures?: unknown }).captures)) {\n return (err as { captures: RawHttpCapture[] }).captures;\n }\n return undefined;\n}\n\n/**\n * Credential-bearing response header names. A misbehaving proxy that\n * echoes caller-supplied auth headers back on the response would\n * otherwise land bearer tokens in the capture — and downstream, in\n * `UniformErrorReport.probes.*.headers` which is written to disk /\n * pasted into tickets. Redact verbatim at capture time.\n */\nconst REDACTED_HEADER_NAMES: ReadonlySet<string> = new Set([\n 'authorization',\n 'proxy-authorization',\n 'cookie',\n 'set-cookie',\n 'x-adcp-auth',\n 'x-api-key',\n]);\n\nconst REDACTED_PLACEHOLDER = '[redacted]';\n\n/**\n * Wrap a fetch implementation so it records raw responses when a capture\n * slot is active. Safe to install unconditionally — pass-through when no\n * slot is set.\n */\nexport function wrapFetchWithCapture(upstream: typeof fetch): typeof fetch {\n const wrapped: typeof fetch = async (input, init) => {\n const slot = rawResponseCaptureStorage.getStore();\n if (!slot) return upstream(input, init);\n\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();\n const startedAt = Date.now();\n const response = await upstream(input, init);\n const latencyMs = Date.now() - startedAt;\n\n // Clone before reading so the SDK still gets a consumable body.\n const cloneForRead = response.clone();\n const { body, bodyTruncated } = await readBodyBounded(cloneForRead, slot.maxBodyBytes);\n\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n headers[key] = REDACTED_HEADER_NAMES.has(lower) ? REDACTED_PLACEHOLDER : value;\n });\n\n slot.captures.push({\n url,\n method,\n status: response.status,\n headers,\n body: redactBearerInBody(body),\n latencyMs,\n timestamp: new Date(startedAt).toISOString(),\n bodyTruncated,\n });\n\n return response;\n };\n return wrapped;\n}\n\n/**\n * Response bodies sometimes echo request headers — e.g., a misbehaving\n * \"debug\" handler that logs the Authorization header into its error\n * payload, or a 500 HTML page that templates the request dump. Strip\n * bearer-shaped tokens so they don't persist into captured output.\n *\n * Conservative: only masks the TOKEN portion of a `Bearer <token>` span\n * (case-insensitive). Doesn't try to detect arbitrary high-entropy\n * strings — false positives on those would damage the comparator's\n * byte-equivalence check.\n */\nfunction redactBearerInBody(body: string): string {\n return body.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]');\n}\n\nasync function readBodyBounded(\n response: Response,\n maxBodyBytes: number\n): Promise<{ body: string; bodyTruncated: boolean }> {\n let text: string;\n try {\n text = await response.text();\n } catch {\n return { body: '', bodyTruncated: false };\n }\n if (text.length <= maxBodyBytes) return { body: text, bodyTruncated: false };\n return { body: text.slice(0, maxBodyBytes), bodyTruncated: true };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,wCAAwC;AAsBxC,MAAM,yBAAyB;AAExB,MAAM,gCAA4B,2DAAqC,oBAAoB;AAalG,eAAsB,uBACpB,IACA,UAAqC,CAAC,GACc;AACpD,QAAM,OAAoB;AAAA,IACxB,UAAU,CAAC;AAAA,IACX,cAAc,QAAQ,gBAAgB;AAAA,EACxC;AACA,MAAI;AACF,UAAM,SAAS,MAAM,0BAA0B,IAAI,MAAM,EAAE;AAC3D,WAAO,EAAE,QAAQ,UAAU,KAAK,SAAS;AAAA,EAC3C,SAAS,KAAK;AACZ,QAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,UAAI;AACF,eAAO,eAAe,KAAK,YAAY;AAAA,UACrC,OAAO,KAAK;AAAA,UACZ,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,QAAQ;AAAA,MAGR;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAGO,SAAS,qBAAqB,KAA4C;AAC/E,MAAI,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAS,IAA+B,QAAQ,GAAG;AAC7F,WAAQ,IAAuC;AAAA,EACjD;AACA,SAAO;AACT;AASA,MAAM,wBAA6C,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,MAAM,uBAAuB;AAOtB,SAAS,qBAAqB,UAAsC;AACzE,QAAM,UAAwB,OAAO,OAAO,SAAS;AACnD,UAAM,OAAO,0BAA0B,SAAS;AAChD,QAAI,CAAC,KAAM,QAAO,SAAS,OAAO,IAAI;AAEtC,UAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,SAAS,IAAI,MAAM;AAChG,UAAM,UAAU,MAAM,WAAW,iBAAiB,UAAU,MAAM,SAAS,QAAQ,YAAY;AAC/F,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,WAAW,MAAM,SAAS,OAAO,IAAI;AAC3C,UAAM,YAAY,KAAK,IAAI,IAAI;AAG/B,UAAM,eAAe,SAAS,MAAM;AACpC,UAAM,EAAE,MAAM,cAAc,IAAI,MAAM,gBAAgB,cAAc,KAAK,YAAY;AAErF,UAAM,UAAkC,CAAC;AACzC,aAAS,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACvC,YAAM,QAAQ,IAAI,YAAY;AAC9B,cAAQ,GAAG,IAAI,sBAAsB,IAAI,KAAK,IAAI,uBAAuB;AAAA,IAC3E,CAAC;AAED,SAAK,SAAS,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB;AAAA,MACA,MAAM,mBAAmB,IAAI;AAAA,MAC7B;AAAA,MACA,WAAW,IAAI,KAAK,SAAS,EAAE,YAAY;AAAA,MAC3C;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAaA,SAAS,mBAAmB,MAAsB;AAChD,SAAO,KAAK,QAAQ,kCAAkC,mBAAmB;AAC3E;AAEA,eAAe,gBACb,UACA,cACmD;AACnD,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,EAAE,MAAM,IAAI,eAAe,MAAM;AAAA,EAC1C;AACA,MAAI,KAAK,UAAU,aAAc,QAAO,EAAE,MAAM,MAAM,eAAe,MAAM;AAC3E,SAAO,EAAE,MAAM,KAAK,MAAM,GAAG,YAAY,GAAG,eAAe,KAAK;AAClE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/protocols/rawResponseCapture.ts"],"sourcesContent":["// Raw HTTP response capture for conformance probing.\n//\n// Scoped via AsyncLocalStorage so MCP + A2A protocol adapters can record\n// status, headers, body, and latency without threading options through\n// every call site. When the capture slot is absent, the fetch wrapper is a\n// pass-through — production clients pay only one ALS lookup per request.\n//\n// Consumers call `withRawResponseCapture(fn)` and receive captures for\n// every HTTP request that happened inside `fn`. The uniform-error invariant\n// uses the captures to compare two probes byte-for-byte.\n\nimport { globalAsyncLocalStorage } from '../utils/global-async-local-storage';\nimport { MAX_TIMER_DELAY_MS, withAbortSignal } from './abort';\n\nexport interface RawHttpCapture {\n url: string;\n method: string;\n requestJsonRpcMethod?: string;\n requestAdcpSkill?: string;\n status: number;\n headers: Record<string, string>;\n body: string;\n latencyMs: number;\n timestamp: string;\n /** True when byte limits, deadlines, or stream errors made the captured body incomplete. */\n bodyTruncated: boolean;\n /** Actionable reason the captured body is incomplete. Never contains response bytes. */\n bodyCaptureError?: string;\n}\n\ninterface CaptureSlot {\n captures: RawHttpCapture[];\n maxBodyBytes: number;\n requestMetadataTimeoutMs: number;\n responseBodyTimeoutMs: number;\n}\n\n// Request and response bodies are bounded as UTF-8 bytes while streaming.\nconst DEFAULT_MAX_BODY_BYTES = 1_048_576;\nconst DEFAULT_REQUEST_METADATA_TIMEOUT_MS = 1_000;\nconst DEFAULT_RESPONSE_BODY_TIMEOUT_MS = 10_000;\n\nexport const rawResponseCaptureStorage = globalAsyncLocalStorage<CaptureSlot>('rawResponseCapture');\n\n/**\n * Run `fn` with a raw-response capture slot active. Every HTTP request made\n * through the wrapped fetch inside `fn` is recorded.\n *\n * When `fn` rejects, the rejection propagates and the partial captures\n * are attached to the thrown error as `error.captures`. Callers that\n * need to inspect partial captures on failure can read that property;\n * callers that don't can ignore it. This lets storyboard validators\n * surface \"the SDK threw before the wire shape parsed\" diagnostics\n * with the actual bytes that arrived before the throw.\n */\nexport async function withRawResponseCapture<T>(\n fn: () => Promise<T>,\n options: { maxBodyBytes?: number; requestMetadataTimeoutMs?: number; responseBodyTimeoutMs?: number } = {}\n): Promise<{ result: T; captures: RawHttpCapture[] }> {\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n if (!Number.isSafeInteger(maxBodyBytes) || maxBodyBytes <= 0) {\n throw new RangeError('maxBodyBytes must be a finite safe positive integer');\n }\n const requestMetadataTimeoutMs = options.requestMetadataTimeoutMs ?? DEFAULT_REQUEST_METADATA_TIMEOUT_MS;\n if (\n !Number.isFinite(requestMetadataTimeoutMs) ||\n requestMetadataTimeoutMs <= 0 ||\n requestMetadataTimeoutMs > MAX_TIMER_DELAY_MS\n ) {\n throw new RangeError(`requestMetadataTimeoutMs must be a finite positive number <= ${MAX_TIMER_DELAY_MS}`);\n }\n const responseBodyTimeoutMs = options.responseBodyTimeoutMs ?? DEFAULT_RESPONSE_BODY_TIMEOUT_MS;\n if (\n !Number.isFinite(responseBodyTimeoutMs) ||\n responseBodyTimeoutMs <= 0 ||\n responseBodyTimeoutMs > MAX_TIMER_DELAY_MS\n ) {\n throw new RangeError(`responseBodyTimeoutMs must be a finite positive number <= ${MAX_TIMER_DELAY_MS}`);\n }\n const slot: CaptureSlot = {\n captures: [],\n maxBodyBytes,\n requestMetadataTimeoutMs,\n responseBodyTimeoutMs,\n };\n try {\n const result = await rawResponseCaptureStorage.run(slot, fn);\n return { result, captures: slot.captures };\n } catch (err) {\n if (err && typeof err === 'object') {\n try {\n Object.defineProperty(err, 'captures', {\n value: slot.captures,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n } catch {\n // Frozen / sealed errors won't accept the property — drop the\n // captures rather than crash on a defineProperty TypeError.\n }\n }\n throw err;\n }\n}\n\n/** Type guard for errors carrying partial captures from `withRawResponseCapture`. */\nexport function getCapturesFromError(err: unknown): RawHttpCapture[] | undefined {\n if (err && typeof err === 'object' && Array.isArray((err as { captures?: unknown }).captures)) {\n return (err as { captures: RawHttpCapture[] }).captures;\n }\n return undefined;\n}\n\n/**\n * Credential-bearing response header names. A misbehaving proxy that\n * echoes caller-supplied auth headers back on the response would\n * otherwise land bearer tokens in the capture — and downstream, in\n * `UniformErrorReport.probes.*.headers` which is written to disk /\n * pasted into tickets. Redact verbatim at capture time.\n */\nconst REDACTED_HEADER_NAMES: ReadonlySet<string> = new Set([\n 'authorization',\n 'proxy-authorization',\n 'cookie',\n 'set-cookie',\n 'x-adcp-auth',\n 'x-api-key',\n]);\n\nconst REDACTED_PLACEHOLDER = '[redacted]';\n\n/**\n * Wrap a fetch implementation so it records raw responses when a capture\n * slot is active. Safe to install unconditionally — pass-through when no\n * slot is set.\n */\nexport function wrapFetchWithCapture(upstream: typeof fetch): typeof fetch {\n const wrapped: typeof fetch = async (input, init) => {\n const slot = rawResponseCaptureStorage.getStore();\n if (!slot) return upstream(input, init);\n\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();\n const requestBody =\n init?.body !== undefined\n ? init.body\n : input instanceof Request\n ? await readRequestBodyFromClone(\n input,\n slot.maxBodyBytes,\n init?.signal ?? input.signal,\n slot.requestMetadataTimeoutMs\n )\n : undefined;\n const requestMetadata = extractSafeRequestMetadata(requestBody, slot.maxBodyBytes);\n const startedAt = Date.now();\n const response = await upstream(input, init);\n const latencyMs = Date.now() - startedAt;\n\n // Clone before reading so the SDK still gets a consumable body.\n const cloneForRead = response.clone();\n const { body, bodyTruncated, bodyCaptureError } = await readBodyBounded(\n cloneForRead,\n slot.maxBodyBytes,\n slot.responseBodyTimeoutMs\n );\n\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n headers[key] = REDACTED_HEADER_NAMES.has(lower) ? REDACTED_PLACEHOLDER : value;\n });\n\n slot.captures.push({\n url,\n method,\n ...requestMetadata,\n status: response.status,\n headers,\n body: redactBearerInBody(body),\n latencyMs,\n timestamp: new Date(startedAt).toISOString(),\n bodyTruncated,\n ...(bodyCaptureError !== undefined && { bodyCaptureError }),\n });\n\n return response;\n };\n return wrapped;\n}\n\nasync function readRequestBodyFromClone(\n request: Request,\n maxBodyBytes: number,\n signal: AbortSignal | null | undefined,\n timeoutMs: number\n): Promise<string | undefined> {\n if (request.bodyUsed || request.method === 'GET' || request.method === 'HEAD') return undefined;\n try {\n const body = request.clone().body;\n if (!body) return '';\n const reader = body.getReader();\n return await withAbortSignal([signal], timeoutMs, async captureSignal => {\n const cancel = () => void reader.cancel(captureSignal?.reason).catch(() => undefined);\n captureSignal?.addEventListener('abort', cancel, { once: true });\n try {\n const chunks: Uint8Array[] = [];\n let length = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n length += value.byteLength;\n if (length > maxBodyBytes) {\n // This reader belongs to a tee created by Request.clone(). Awaiting\n // cancellation can deadlock until the original branch is consumed,\n // which cannot happen until this wrapper calls the upstream fetch.\n void reader.cancel().catch(() => undefined);\n return undefined;\n }\n chunks.push(value);\n }\n const bytes = new Uint8Array(length);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(bytes);\n } finally {\n captureSignal?.removeEventListener('abort', cancel);\n }\n });\n } catch {\n return undefined;\n }\n}\n\nfunction extractSafeRequestMetadata(\n body: BodyInit | null | undefined,\n maxBodyBytes: number\n): Pick<RawHttpCapture, 'requestJsonRpcMethod' | 'requestAdcpSkill'> {\n if (typeof body !== 'string' || body.length > maxBodyBytes) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return {};\n }\n if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) return {};\n const envelope = parsed as { method?: unknown; params?: unknown };\n const requestJsonRpcMethod = typeof envelope.method === 'string' ? envelope.method : undefined;\n let requestAdcpSkill: string | undefined;\n const params =\n envelope.params != null && typeof envelope.params === 'object' && !Array.isArray(envelope.params)\n ? (envelope.params as { message?: unknown })\n : undefined;\n const message =\n params?.message != null && typeof params.message === 'object' && !Array.isArray(params.message)\n ? (params.message as { parts?: unknown })\n : undefined;\n if (Array.isArray(message?.parts)) {\n for (const part of message.parts) {\n if (part == null || typeof part !== 'object' || Array.isArray(part)) continue;\n const data = (part as { data?: unknown; content?: unknown }).data;\n const content = (part as { content?: unknown }).content;\n const nativeData =\n content != null && typeof content === 'object' && !Array.isArray(content)\n ? (content as { data?: unknown }).data\n : undefined;\n const value = data ?? nativeData;\n if (value == null || typeof value !== 'object' || Array.isArray(value)) continue;\n const skill = (value as { skill?: unknown }).skill;\n if (typeof skill === 'string') {\n requestAdcpSkill = skill;\n break;\n }\n }\n }\n return {\n ...(requestJsonRpcMethod !== undefined && { requestJsonRpcMethod }),\n ...(requestAdcpSkill !== undefined && { requestAdcpSkill }),\n };\n}\n\n/**\n * Response bodies sometimes echo request headers — e.g., a misbehaving\n * \"debug\" handler that logs the Authorization header into its error\n * payload, or a 500 HTML page that templates the request dump. Strip\n * bearer-shaped tokens so they don't persist into captured output.\n *\n * Conservative: only masks the TOKEN portion of a `Bearer <token>` span\n * (case-insensitive). Doesn't try to detect arbitrary high-entropy\n * strings — false positives on those would damage the comparator's\n * byte-equivalence check.\n */\nfunction redactBearerInBody(body: string): string {\n return body.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]');\n}\n\nasync function readBodyBounded(\n response: Response,\n maxBodyBytes: number,\n timeoutMs: number\n): Promise<{ body: string; bodyTruncated: boolean; bodyCaptureError?: string }> {\n if (!response.body) return { body: '', bodyTruncated: false };\n const reader = response.body.getReader();\n const retained: Uint8Array[] = [];\n let retainedBytes = 0;\n const decodeRetained = () => {\n const bytes = new Uint8Array(retainedBytes);\n let offset = 0;\n for (const chunk of retained) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(bytes);\n };\n try {\n return await withAbortSignal([], timeoutMs, async signal => {\n const cancel = () => void reader.cancel(signal?.reason).catch(() => undefined);\n signal?.addEventListener('abort', cancel, { once: true });\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return { body: decodeRetained(), bodyTruncated: false };\n }\n const available = maxBodyBytes - retainedBytes;\n const copyBytes = Math.min(available, value.byteLength);\n if (copyBytes > 0) {\n // Copy only bytes actually observed. In particular, accepting a\n // large safe maxBodyBytes must not allocate that full capacity.\n retained.push(value.slice(0, copyBytes));\n retainedBytes += copyBytes;\n }\n if (copyBytes < value.byteLength) {\n // This is a clone branch. Awaiting cancellation can wait for the SDK\n // to consume the original branch, which cannot happen until capture\n // returns, so cancel without awaiting.\n void reader.cancel().catch(() => undefined);\n return {\n body: decodeRetained(),\n bodyTruncated: true,\n bodyCaptureError: `Raw response capture exceeded maxBodyBytes (${maxBodyBytes})`,\n };\n }\n }\n } finally {\n signal?.removeEventListener('abort', cancel);\n }\n });\n } catch (error) {\n // Capture is observational: a stalled or broken clone must not prevent\n // the SDK from consuming the original response. Preserve bytes already\n // observed and mark the capture incomplete.\n const timedOut = error instanceof Error && error.name === 'TimeoutError';\n return {\n body: decodeRetained(),\n bodyTruncated: true,\n bodyCaptureError: timedOut\n ? `Raw response capture timed out after ${timeoutMs} ms before the response body completed`\n : 'Raw response capture ended before the response body completed',\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,12 +4,29 @@ import { createRequire as __adcpCreateRequire } from "node:module";
|
|
|
4
4
|
const __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));
|
|
5
5
|
const require2 = __adcpCreateRequire(import.meta.url);
|
|
6
6
|
import { globalAsyncLocalStorage } from "../utils/global-async-local-storage.mjs";
|
|
7
|
+
import { MAX_TIMER_DELAY_MS, withAbortSignal } from "./abort.mjs";
|
|
7
8
|
const DEFAULT_MAX_BODY_BYTES = 1048576;
|
|
9
|
+
const DEFAULT_REQUEST_METADATA_TIMEOUT_MS = 1e3;
|
|
10
|
+
const DEFAULT_RESPONSE_BODY_TIMEOUT_MS = 1e4;
|
|
8
11
|
const rawResponseCaptureStorage = globalAsyncLocalStorage("rawResponseCapture");
|
|
9
12
|
async function withRawResponseCapture(fn, options = {}) {
|
|
13
|
+
const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
14
|
+
if (!Number.isSafeInteger(maxBodyBytes) || maxBodyBytes <= 0) {
|
|
15
|
+
throw new RangeError("maxBodyBytes must be a finite safe positive integer");
|
|
16
|
+
}
|
|
17
|
+
const requestMetadataTimeoutMs = options.requestMetadataTimeoutMs ?? DEFAULT_REQUEST_METADATA_TIMEOUT_MS;
|
|
18
|
+
if (!Number.isFinite(requestMetadataTimeoutMs) || requestMetadataTimeoutMs <= 0 || requestMetadataTimeoutMs > MAX_TIMER_DELAY_MS) {
|
|
19
|
+
throw new RangeError(`requestMetadataTimeoutMs must be a finite positive number <= ${MAX_TIMER_DELAY_MS}`);
|
|
20
|
+
}
|
|
21
|
+
const responseBodyTimeoutMs = options.responseBodyTimeoutMs ?? DEFAULT_RESPONSE_BODY_TIMEOUT_MS;
|
|
22
|
+
if (!Number.isFinite(responseBodyTimeoutMs) || responseBodyTimeoutMs <= 0 || responseBodyTimeoutMs > MAX_TIMER_DELAY_MS) {
|
|
23
|
+
throw new RangeError(`responseBodyTimeoutMs must be a finite positive number <= ${MAX_TIMER_DELAY_MS}`);
|
|
24
|
+
}
|
|
10
25
|
const slot = {
|
|
11
26
|
captures: [],
|
|
12
|
-
maxBodyBytes
|
|
27
|
+
maxBodyBytes,
|
|
28
|
+
requestMetadataTimeoutMs,
|
|
29
|
+
responseBodyTimeoutMs
|
|
13
30
|
};
|
|
14
31
|
try {
|
|
15
32
|
const result = await rawResponseCaptureStorage.run(slot, fn);
|
|
@@ -50,11 +67,22 @@ function wrapFetchWithCapture(upstream) {
|
|
|
50
67
|
if (!slot) return upstream(input, init);
|
|
51
68
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
52
69
|
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
70
|
+
const requestBody = init?.body !== void 0 ? init.body : input instanceof Request ? await readRequestBodyFromClone(
|
|
71
|
+
input,
|
|
72
|
+
slot.maxBodyBytes,
|
|
73
|
+
init?.signal ?? input.signal,
|
|
74
|
+
slot.requestMetadataTimeoutMs
|
|
75
|
+
) : void 0;
|
|
76
|
+
const requestMetadata = extractSafeRequestMetadata(requestBody, slot.maxBodyBytes);
|
|
53
77
|
const startedAt = Date.now();
|
|
54
78
|
const response = await upstream(input, init);
|
|
55
79
|
const latencyMs = Date.now() - startedAt;
|
|
56
80
|
const cloneForRead = response.clone();
|
|
57
|
-
const { body, bodyTruncated } = await readBodyBounded(
|
|
81
|
+
const { body, bodyTruncated, bodyCaptureError } = await readBodyBounded(
|
|
82
|
+
cloneForRead,
|
|
83
|
+
slot.maxBodyBytes,
|
|
84
|
+
slot.responseBodyTimeoutMs
|
|
85
|
+
);
|
|
58
86
|
const headers = {};
|
|
59
87
|
response.headers.forEach((value, key) => {
|
|
60
88
|
const lower = key.toLowerCase();
|
|
@@ -63,29 +91,144 @@ function wrapFetchWithCapture(upstream) {
|
|
|
63
91
|
slot.captures.push({
|
|
64
92
|
url,
|
|
65
93
|
method,
|
|
94
|
+
...requestMetadata,
|
|
66
95
|
status: response.status,
|
|
67
96
|
headers,
|
|
68
97
|
body: redactBearerInBody(body),
|
|
69
98
|
latencyMs,
|
|
70
99
|
timestamp: new Date(startedAt).toISOString(),
|
|
71
|
-
bodyTruncated
|
|
100
|
+
bodyTruncated,
|
|
101
|
+
...bodyCaptureError !== void 0 && { bodyCaptureError }
|
|
72
102
|
});
|
|
73
103
|
return response;
|
|
74
104
|
};
|
|
75
105
|
return wrapped;
|
|
76
106
|
}
|
|
107
|
+
async function readRequestBodyFromClone(request, maxBodyBytes, signal, timeoutMs) {
|
|
108
|
+
if (request.bodyUsed || request.method === "GET" || request.method === "HEAD") return void 0;
|
|
109
|
+
try {
|
|
110
|
+
const body = request.clone().body;
|
|
111
|
+
if (!body) return "";
|
|
112
|
+
const reader = body.getReader();
|
|
113
|
+
return await withAbortSignal([signal], timeoutMs, async (captureSignal) => {
|
|
114
|
+
const cancel = () => void reader.cancel(captureSignal?.reason).catch(() => void 0);
|
|
115
|
+
captureSignal?.addEventListener("abort", cancel, { once: true });
|
|
116
|
+
try {
|
|
117
|
+
const chunks = [];
|
|
118
|
+
let length = 0;
|
|
119
|
+
while (true) {
|
|
120
|
+
const { done, value } = await reader.read();
|
|
121
|
+
if (done) break;
|
|
122
|
+
length += value.byteLength;
|
|
123
|
+
if (length > maxBodyBytes) {
|
|
124
|
+
void reader.cancel().catch(() => void 0);
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
chunks.push(value);
|
|
128
|
+
}
|
|
129
|
+
const bytes = new Uint8Array(length);
|
|
130
|
+
let offset = 0;
|
|
131
|
+
for (const chunk of chunks) {
|
|
132
|
+
bytes.set(chunk, offset);
|
|
133
|
+
offset += chunk.byteLength;
|
|
134
|
+
}
|
|
135
|
+
return new TextDecoder().decode(bytes);
|
|
136
|
+
} finally {
|
|
137
|
+
captureSignal?.removeEventListener("abort", cancel);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
} catch {
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function extractSafeRequestMetadata(body, maxBodyBytes) {
|
|
145
|
+
if (typeof body !== "string" || body.length > maxBodyBytes) return {};
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = JSON.parse(body);
|
|
149
|
+
} catch {
|
|
150
|
+
return {};
|
|
151
|
+
}
|
|
152
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
153
|
+
const envelope = parsed;
|
|
154
|
+
const requestJsonRpcMethod = typeof envelope.method === "string" ? envelope.method : void 0;
|
|
155
|
+
let requestAdcpSkill;
|
|
156
|
+
const params = envelope.params != null && typeof envelope.params === "object" && !Array.isArray(envelope.params) ? envelope.params : void 0;
|
|
157
|
+
const message = params?.message != null && typeof params.message === "object" && !Array.isArray(params.message) ? params.message : void 0;
|
|
158
|
+
if (Array.isArray(message?.parts)) {
|
|
159
|
+
for (const part of message.parts) {
|
|
160
|
+
if (part == null || typeof part !== "object" || Array.isArray(part)) continue;
|
|
161
|
+
const data = part.data;
|
|
162
|
+
const content = part.content;
|
|
163
|
+
const nativeData = content != null && typeof content === "object" && !Array.isArray(content) ? content.data : void 0;
|
|
164
|
+
const value = data ?? nativeData;
|
|
165
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
166
|
+
const skill = value.skill;
|
|
167
|
+
if (typeof skill === "string") {
|
|
168
|
+
requestAdcpSkill = skill;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
...requestJsonRpcMethod !== void 0 && { requestJsonRpcMethod },
|
|
175
|
+
...requestAdcpSkill !== void 0 && { requestAdcpSkill }
|
|
176
|
+
};
|
|
177
|
+
}
|
|
77
178
|
function redactBearerInBody(body) {
|
|
78
179
|
return body.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]");
|
|
79
180
|
}
|
|
80
|
-
async function readBodyBounded(response, maxBodyBytes) {
|
|
81
|
-
|
|
181
|
+
async function readBodyBounded(response, maxBodyBytes, timeoutMs) {
|
|
182
|
+
if (!response.body) return { body: "", bodyTruncated: false };
|
|
183
|
+
const reader = response.body.getReader();
|
|
184
|
+
const retained = [];
|
|
185
|
+
let retainedBytes = 0;
|
|
186
|
+
const decodeRetained = () => {
|
|
187
|
+
const bytes = new Uint8Array(retainedBytes);
|
|
188
|
+
let offset = 0;
|
|
189
|
+
for (const chunk of retained) {
|
|
190
|
+
bytes.set(chunk, offset);
|
|
191
|
+
offset += chunk.byteLength;
|
|
192
|
+
}
|
|
193
|
+
return new TextDecoder().decode(bytes);
|
|
194
|
+
};
|
|
82
195
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
196
|
+
return await withAbortSignal([], timeoutMs, async (signal) => {
|
|
197
|
+
const cancel = () => void reader.cancel(signal?.reason).catch(() => void 0);
|
|
198
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
199
|
+
try {
|
|
200
|
+
while (true) {
|
|
201
|
+
const { done, value } = await reader.read();
|
|
202
|
+
if (done) {
|
|
203
|
+
return { body: decodeRetained(), bodyTruncated: false };
|
|
204
|
+
}
|
|
205
|
+
const available = maxBodyBytes - retainedBytes;
|
|
206
|
+
const copyBytes = Math.min(available, value.byteLength);
|
|
207
|
+
if (copyBytes > 0) {
|
|
208
|
+
retained.push(value.slice(0, copyBytes));
|
|
209
|
+
retainedBytes += copyBytes;
|
|
210
|
+
}
|
|
211
|
+
if (copyBytes < value.byteLength) {
|
|
212
|
+
void reader.cancel().catch(() => void 0);
|
|
213
|
+
return {
|
|
214
|
+
body: decodeRetained(),
|
|
215
|
+
bodyTruncated: true,
|
|
216
|
+
bodyCaptureError: `Raw response capture exceeded maxBodyBytes (${maxBodyBytes})`
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} finally {
|
|
221
|
+
signal?.removeEventListener("abort", cancel);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
const timedOut = error instanceof Error && error.name === "TimeoutError";
|
|
226
|
+
return {
|
|
227
|
+
body: decodeRetained(),
|
|
228
|
+
bodyTruncated: true,
|
|
229
|
+
bodyCaptureError: timedOut ? `Raw response capture timed out after ${timeoutMs} ms before the response body completed` : "Raw response capture ended before the response body completed"
|
|
230
|
+
};
|
|
86
231
|
}
|
|
87
|
-
if (text.length <= maxBodyBytes) return { body: text, bodyTruncated: false };
|
|
88
|
-
return { body: text.slice(0, maxBodyBytes), bodyTruncated: true };
|
|
89
232
|
}
|
|
90
233
|
export {
|
|
91
234
|
getCapturesFromError,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/protocols/rawResponseCapture.ts"],"sourcesContent":["import { fileURLToPath as __adcpFileURLToPath } from 'node:url';\nimport { dirname as __adcpDirname } from 'node:path';\nimport { createRequire as __adcpCreateRequire } from 'node:module';\nconst __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));\nconst require = __adcpCreateRequire(import.meta.url);\n// Raw HTTP response capture for conformance probing.\n//\n// Scoped via AsyncLocalStorage so MCP + A2A protocol adapters can record\n// status, headers, body, and latency without threading options through\n// every call site. When the capture slot is absent, the fetch wrapper is a\n// pass-through — production clients pay only one ALS lookup per request.\n//\n// Consumers call `withRawResponseCapture(fn)` and receive captures for\n// every HTTP request that happened inside `fn`. The uniform-error invariant\n// uses the captures to compare two probes byte-for-byte.\n\nimport { globalAsyncLocalStorage } from '../utils/global-async-local-storage';\n\nexport interface RawHttpCapture {\n url: string;\n method: string;\n status: number;\n headers: Record<string, string>;\n body: string;\n latencyMs: number;\n timestamp: string;\n /** True when body capture hit `maxBodyBytes` and was truncated. */\n bodyTruncated: boolean;\n}\n\ninterface CaptureSlot {\n captures: RawHttpCapture[];\n maxBodyBytes: number;\n}\n\n// Counted as UTF-16 code units (string.length), not UTF-8 bytes. Close\n// enough for ASCII-dominant response payloads and fine as a safety cap\n// against accidentally retaining huge responses.\nconst DEFAULT_MAX_BODY_BYTES = 1_048_576;\n\nexport const rawResponseCaptureStorage = globalAsyncLocalStorage<CaptureSlot>('rawResponseCapture');\n\n/**\n * Run `fn` with a raw-response capture slot active. Every HTTP request made\n * through the wrapped fetch inside `fn` is recorded.\n *\n * When `fn` rejects, the rejection propagates and the partial captures\n * are attached to the thrown error as `error.captures`. Callers that\n * need to inspect partial captures on failure can read that property;\n * callers that don't can ignore it. This lets storyboard validators\n * surface \"the SDK threw before the wire shape parsed\" diagnostics\n * with the actual bytes that arrived before the throw.\n */\nexport async function withRawResponseCapture<T>(\n fn: () => Promise<T>,\n options: { maxBodyBytes?: number } = {}\n): Promise<{ result: T; captures: RawHttpCapture[] }> {\n const slot: CaptureSlot = {\n captures: [],\n maxBodyBytes: options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n };\n try {\n const result = await rawResponseCaptureStorage.run(slot, fn);\n return { result, captures: slot.captures };\n } catch (err) {\n if (err && typeof err === 'object') {\n try {\n Object.defineProperty(err, 'captures', {\n value: slot.captures,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n } catch {\n // Frozen / sealed errors won't accept the property — drop the\n // captures rather than crash on a defineProperty TypeError.\n }\n }\n throw err;\n }\n}\n\n/** Type guard for errors carrying partial captures from `withRawResponseCapture`. */\nexport function getCapturesFromError(err: unknown): RawHttpCapture[] | undefined {\n if (err && typeof err === 'object' && Array.isArray((err as { captures?: unknown }).captures)) {\n return (err as { captures: RawHttpCapture[] }).captures;\n }\n return undefined;\n}\n\n/**\n * Credential-bearing response header names. A misbehaving proxy that\n * echoes caller-supplied auth headers back on the response would\n * otherwise land bearer tokens in the capture — and downstream, in\n * `UniformErrorReport.probes.*.headers` which is written to disk /\n * pasted into tickets. Redact verbatim at capture time.\n */\nconst REDACTED_HEADER_NAMES: ReadonlySet<string> = new Set([\n 'authorization',\n 'proxy-authorization',\n 'cookie',\n 'set-cookie',\n 'x-adcp-auth',\n 'x-api-key',\n]);\n\nconst REDACTED_PLACEHOLDER = '[redacted]';\n\n/**\n * Wrap a fetch implementation so it records raw responses when a capture\n * slot is active. Safe to install unconditionally — pass-through when no\n * slot is set.\n */\nexport function wrapFetchWithCapture(upstream: typeof fetch): typeof fetch {\n const wrapped: typeof fetch = async (input, init) => {\n const slot = rawResponseCaptureStorage.getStore();\n if (!slot) return upstream(input, init);\n\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();\n const startedAt = Date.now();\n const response = await upstream(input, init);\n const latencyMs = Date.now() - startedAt;\n\n // Clone before reading so the SDK still gets a consumable body.\n const cloneForRead = response.clone();\n const { body, bodyTruncated } = await readBodyBounded(cloneForRead, slot.maxBodyBytes);\n\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n headers[key] = REDACTED_HEADER_NAMES.has(lower) ? REDACTED_PLACEHOLDER : value;\n });\n\n slot.captures.push({\n url,\n method,\n status: response.status,\n headers,\n body: redactBearerInBody(body),\n latencyMs,\n timestamp: new Date(startedAt).toISOString(),\n bodyTruncated,\n });\n\n return response;\n };\n return wrapped;\n}\n\n/**\n * Response bodies sometimes echo request headers — e.g., a misbehaving\n * \"debug\" handler that logs the Authorization header into its error\n * payload, or a 500 HTML page that templates the request dump. Strip\n * bearer-shaped tokens so they don't persist into captured output.\n *\n * Conservative: only masks the TOKEN portion of a `Bearer <token>` span\n * (case-insensitive). Doesn't try to detect arbitrary high-entropy\n * strings — false positives on those would damage the comparator's\n * byte-equivalence check.\n */\nfunction redactBearerInBody(body: string): string {\n return body.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]');\n}\n\nasync function readBodyBounded(\n response: Response,\n maxBodyBytes: number\n): Promise<{ body: string; bodyTruncated: boolean }> {\n let text: string;\n try {\n text = await response.text();\n } catch {\n return { body: '', bodyTruncated: false };\n }\n if (text.length <= maxBodyBytes) return { body: text, bodyTruncated: false };\n return { body: text.slice(0, maxBodyBytes), bodyTruncated: true };\n}\n"],"mappings":"AAAA,SAAS,iBAAiB,2BAA2B;AACrD,SAAS,WAAW,qBAAqB;AACzC,SAAS,iBAAiB,2BAA2B;AACrD,MAAM,YAAY,cAAc,oBAAoB,YAAY,GAAG,CAAC;AACpE,MAAMA,WAAU,oBAAoB,YAAY,GAAG;AAYnD,SAAS,+BAA+B;AAsBxC,MAAM,yBAAyB;AAExB,MAAM,4BAA4B,wBAAqC,oBAAoB;AAalG,eAAsB,uBACpB,IACA,UAAqC,CAAC,GACc;AACpD,QAAM,OAAoB;AAAA,IACxB,UAAU,CAAC;AAAA,IACX,cAAc,QAAQ,gBAAgB;AAAA,EACxC;AACA,MAAI;AACF,UAAM,SAAS,MAAM,0BAA0B,IAAI,MAAM,EAAE;AAC3D,WAAO,EAAE,QAAQ,UAAU,KAAK,SAAS;AAAA,EAC3C,SAAS,KAAK;AACZ,QAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,UAAI;AACF,eAAO,eAAe,KAAK,YAAY;AAAA,UACrC,OAAO,KAAK;AAAA,UACZ,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,QAAQ;AAAA,MAGR;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAGO,SAAS,qBAAqB,KAA4C;AAC/E,MAAI,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAS,IAA+B,QAAQ,GAAG;AAC7F,WAAQ,IAAuC;AAAA,EACjD;AACA,SAAO;AACT;AASA,MAAM,wBAA6C,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,MAAM,uBAAuB;AAOtB,SAAS,qBAAqB,UAAsC;AACzE,QAAM,UAAwB,OAAO,OAAO,SAAS;AACnD,UAAM,OAAO,0BAA0B,SAAS;AAChD,QAAI,CAAC,KAAM,QAAO,SAAS,OAAO,IAAI;AAEtC,UAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,SAAS,IAAI,MAAM;AAChG,UAAM,UAAU,MAAM,WAAW,iBAAiB,UAAU,MAAM,SAAS,QAAQ,YAAY;AAC/F,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,WAAW,MAAM,SAAS,OAAO,IAAI;AAC3C,UAAM,YAAY,KAAK,IAAI,IAAI;AAG/B,UAAM,eAAe,SAAS,MAAM;AACpC,UAAM,EAAE,MAAM,cAAc,IAAI,MAAM,gBAAgB,cAAc,KAAK,YAAY;AAErF,UAAM,UAAkC,CAAC;AACzC,aAAS,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACvC,YAAM,QAAQ,IAAI,YAAY;AAC9B,cAAQ,GAAG,IAAI,sBAAsB,IAAI,KAAK,IAAI,uBAAuB;AAAA,IAC3E,CAAC;AAED,SAAK,SAAS,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB;AAAA,MACA,MAAM,mBAAmB,IAAI;AAAA,MAC7B;AAAA,MACA,WAAW,IAAI,KAAK,SAAS,EAAE,YAAY;AAAA,MAC3C;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAaA,SAAS,mBAAmB,MAAsB;AAChD,SAAO,KAAK,QAAQ,kCAAkC,mBAAmB;AAC3E;AAEA,eAAe,gBACb,UACA,cACmD;AACnD,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,EAAE,MAAM,IAAI,eAAe,MAAM;AAAA,EAC1C;AACA,MAAI,KAAK,UAAU,aAAc,QAAO,EAAE,MAAM,MAAM,eAAe,MAAM;AAC3E,SAAO,EAAE,MAAM,KAAK,MAAM,GAAG,YAAY,GAAG,eAAe,KAAK;AAClE;","names":["require"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/protocols/rawResponseCapture.ts"],"sourcesContent":["import { fileURLToPath as __adcpFileURLToPath } from 'node:url';\nimport { dirname as __adcpDirname } from 'node:path';\nimport { createRequire as __adcpCreateRequire } from 'node:module';\nconst __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));\nconst require = __adcpCreateRequire(import.meta.url);\n// Raw HTTP response capture for conformance probing.\n//\n// Scoped via AsyncLocalStorage so MCP + A2A protocol adapters can record\n// status, headers, body, and latency without threading options through\n// every call site. When the capture slot is absent, the fetch wrapper is a\n// pass-through — production clients pay only one ALS lookup per request.\n//\n// Consumers call `withRawResponseCapture(fn)` and receive captures for\n// every HTTP request that happened inside `fn`. The uniform-error invariant\n// uses the captures to compare two probes byte-for-byte.\n\nimport { globalAsyncLocalStorage } from '../utils/global-async-local-storage';\nimport { MAX_TIMER_DELAY_MS, withAbortSignal } from './abort';\n\nexport interface RawHttpCapture {\n url: string;\n method: string;\n requestJsonRpcMethod?: string;\n requestAdcpSkill?: string;\n status: number;\n headers: Record<string, string>;\n body: string;\n latencyMs: number;\n timestamp: string;\n /** True when byte limits, deadlines, or stream errors made the captured body incomplete. */\n bodyTruncated: boolean;\n /** Actionable reason the captured body is incomplete. Never contains response bytes. */\n bodyCaptureError?: string;\n}\n\ninterface CaptureSlot {\n captures: RawHttpCapture[];\n maxBodyBytes: number;\n requestMetadataTimeoutMs: number;\n responseBodyTimeoutMs: number;\n}\n\n// Request and response bodies are bounded as UTF-8 bytes while streaming.\nconst DEFAULT_MAX_BODY_BYTES = 1_048_576;\nconst DEFAULT_REQUEST_METADATA_TIMEOUT_MS = 1_000;\nconst DEFAULT_RESPONSE_BODY_TIMEOUT_MS = 10_000;\n\nexport const rawResponseCaptureStorage = globalAsyncLocalStorage<CaptureSlot>('rawResponseCapture');\n\n/**\n * Run `fn` with a raw-response capture slot active. Every HTTP request made\n * through the wrapped fetch inside `fn` is recorded.\n *\n * When `fn` rejects, the rejection propagates and the partial captures\n * are attached to the thrown error as `error.captures`. Callers that\n * need to inspect partial captures on failure can read that property;\n * callers that don't can ignore it. This lets storyboard validators\n * surface \"the SDK threw before the wire shape parsed\" diagnostics\n * with the actual bytes that arrived before the throw.\n */\nexport async function withRawResponseCapture<T>(\n fn: () => Promise<T>,\n options: { maxBodyBytes?: number; requestMetadataTimeoutMs?: number; responseBodyTimeoutMs?: number } = {}\n): Promise<{ result: T; captures: RawHttpCapture[] }> {\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n if (!Number.isSafeInteger(maxBodyBytes) || maxBodyBytes <= 0) {\n throw new RangeError('maxBodyBytes must be a finite safe positive integer');\n }\n const requestMetadataTimeoutMs = options.requestMetadataTimeoutMs ?? DEFAULT_REQUEST_METADATA_TIMEOUT_MS;\n if (\n !Number.isFinite(requestMetadataTimeoutMs) ||\n requestMetadataTimeoutMs <= 0 ||\n requestMetadataTimeoutMs > MAX_TIMER_DELAY_MS\n ) {\n throw new RangeError(`requestMetadataTimeoutMs must be a finite positive number <= ${MAX_TIMER_DELAY_MS}`);\n }\n const responseBodyTimeoutMs = options.responseBodyTimeoutMs ?? DEFAULT_RESPONSE_BODY_TIMEOUT_MS;\n if (\n !Number.isFinite(responseBodyTimeoutMs) ||\n responseBodyTimeoutMs <= 0 ||\n responseBodyTimeoutMs > MAX_TIMER_DELAY_MS\n ) {\n throw new RangeError(`responseBodyTimeoutMs must be a finite positive number <= ${MAX_TIMER_DELAY_MS}`);\n }\n const slot: CaptureSlot = {\n captures: [],\n maxBodyBytes,\n requestMetadataTimeoutMs,\n responseBodyTimeoutMs,\n };\n try {\n const result = await rawResponseCaptureStorage.run(slot, fn);\n return { result, captures: slot.captures };\n } catch (err) {\n if (err && typeof err === 'object') {\n try {\n Object.defineProperty(err, 'captures', {\n value: slot.captures,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n } catch {\n // Frozen / sealed errors won't accept the property — drop the\n // captures rather than crash on a defineProperty TypeError.\n }\n }\n throw err;\n }\n}\n\n/** Type guard for errors carrying partial captures from `withRawResponseCapture`. */\nexport function getCapturesFromError(err: unknown): RawHttpCapture[] | undefined {\n if (err && typeof err === 'object' && Array.isArray((err as { captures?: unknown }).captures)) {\n return (err as { captures: RawHttpCapture[] }).captures;\n }\n return undefined;\n}\n\n/**\n * Credential-bearing response header names. A misbehaving proxy that\n * echoes caller-supplied auth headers back on the response would\n * otherwise land bearer tokens in the capture — and downstream, in\n * `UniformErrorReport.probes.*.headers` which is written to disk /\n * pasted into tickets. Redact verbatim at capture time.\n */\nconst REDACTED_HEADER_NAMES: ReadonlySet<string> = new Set([\n 'authorization',\n 'proxy-authorization',\n 'cookie',\n 'set-cookie',\n 'x-adcp-auth',\n 'x-api-key',\n]);\n\nconst REDACTED_PLACEHOLDER = '[redacted]';\n\n/**\n * Wrap a fetch implementation so it records raw responses when a capture\n * slot is active. Safe to install unconditionally — pass-through when no\n * slot is set.\n */\nexport function wrapFetchWithCapture(upstream: typeof fetch): typeof fetch {\n const wrapped: typeof fetch = async (input, init) => {\n const slot = rawResponseCaptureStorage.getStore();\n if (!slot) return upstream(input, init);\n\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();\n const requestBody =\n init?.body !== undefined\n ? init.body\n : input instanceof Request\n ? await readRequestBodyFromClone(\n input,\n slot.maxBodyBytes,\n init?.signal ?? input.signal,\n slot.requestMetadataTimeoutMs\n )\n : undefined;\n const requestMetadata = extractSafeRequestMetadata(requestBody, slot.maxBodyBytes);\n const startedAt = Date.now();\n const response = await upstream(input, init);\n const latencyMs = Date.now() - startedAt;\n\n // Clone before reading so the SDK still gets a consumable body.\n const cloneForRead = response.clone();\n const { body, bodyTruncated, bodyCaptureError } = await readBodyBounded(\n cloneForRead,\n slot.maxBodyBytes,\n slot.responseBodyTimeoutMs\n );\n\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n headers[key] = REDACTED_HEADER_NAMES.has(lower) ? REDACTED_PLACEHOLDER : value;\n });\n\n slot.captures.push({\n url,\n method,\n ...requestMetadata,\n status: response.status,\n headers,\n body: redactBearerInBody(body),\n latencyMs,\n timestamp: new Date(startedAt).toISOString(),\n bodyTruncated,\n ...(bodyCaptureError !== undefined && { bodyCaptureError }),\n });\n\n return response;\n };\n return wrapped;\n}\n\nasync function readRequestBodyFromClone(\n request: Request,\n maxBodyBytes: number,\n signal: AbortSignal | null | undefined,\n timeoutMs: number\n): Promise<string | undefined> {\n if (request.bodyUsed || request.method === 'GET' || request.method === 'HEAD') return undefined;\n try {\n const body = request.clone().body;\n if (!body) return '';\n const reader = body.getReader();\n return await withAbortSignal([signal], timeoutMs, async captureSignal => {\n const cancel = () => void reader.cancel(captureSignal?.reason).catch(() => undefined);\n captureSignal?.addEventListener('abort', cancel, { once: true });\n try {\n const chunks: Uint8Array[] = [];\n let length = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n length += value.byteLength;\n if (length > maxBodyBytes) {\n // This reader belongs to a tee created by Request.clone(). Awaiting\n // cancellation can deadlock until the original branch is consumed,\n // which cannot happen until this wrapper calls the upstream fetch.\n void reader.cancel().catch(() => undefined);\n return undefined;\n }\n chunks.push(value);\n }\n const bytes = new Uint8Array(length);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(bytes);\n } finally {\n captureSignal?.removeEventListener('abort', cancel);\n }\n });\n } catch {\n return undefined;\n }\n}\n\nfunction extractSafeRequestMetadata(\n body: BodyInit | null | undefined,\n maxBodyBytes: number\n): Pick<RawHttpCapture, 'requestJsonRpcMethod' | 'requestAdcpSkill'> {\n if (typeof body !== 'string' || body.length > maxBodyBytes) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return {};\n }\n if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) return {};\n const envelope = parsed as { method?: unknown; params?: unknown };\n const requestJsonRpcMethod = typeof envelope.method === 'string' ? envelope.method : undefined;\n let requestAdcpSkill: string | undefined;\n const params =\n envelope.params != null && typeof envelope.params === 'object' && !Array.isArray(envelope.params)\n ? (envelope.params as { message?: unknown })\n : undefined;\n const message =\n params?.message != null && typeof params.message === 'object' && !Array.isArray(params.message)\n ? (params.message as { parts?: unknown })\n : undefined;\n if (Array.isArray(message?.parts)) {\n for (const part of message.parts) {\n if (part == null || typeof part !== 'object' || Array.isArray(part)) continue;\n const data = (part as { data?: unknown; content?: unknown }).data;\n const content = (part as { content?: unknown }).content;\n const nativeData =\n content != null && typeof content === 'object' && !Array.isArray(content)\n ? (content as { data?: unknown }).data\n : undefined;\n const value = data ?? nativeData;\n if (value == null || typeof value !== 'object' || Array.isArray(value)) continue;\n const skill = (value as { skill?: unknown }).skill;\n if (typeof skill === 'string') {\n requestAdcpSkill = skill;\n break;\n }\n }\n }\n return {\n ...(requestJsonRpcMethod !== undefined && { requestJsonRpcMethod }),\n ...(requestAdcpSkill !== undefined && { requestAdcpSkill }),\n };\n}\n\n/**\n * Response bodies sometimes echo request headers — e.g., a misbehaving\n * \"debug\" handler that logs the Authorization header into its error\n * payload, or a 500 HTML page that templates the request dump. Strip\n * bearer-shaped tokens so they don't persist into captured output.\n *\n * Conservative: only masks the TOKEN portion of a `Bearer <token>` span\n * (case-insensitive). Doesn't try to detect arbitrary high-entropy\n * strings — false positives on those would damage the comparator's\n * byte-equivalence check.\n */\nfunction redactBearerInBody(body: string): string {\n return body.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]');\n}\n\nasync function readBodyBounded(\n response: Response,\n maxBodyBytes: number,\n timeoutMs: number\n): Promise<{ body: string; bodyTruncated: boolean; bodyCaptureError?: string }> {\n if (!response.body) return { body: '', bodyTruncated: false };\n const reader = response.body.getReader();\n const retained: Uint8Array[] = [];\n let retainedBytes = 0;\n const decodeRetained = () => {\n const bytes = new Uint8Array(retainedBytes);\n let offset = 0;\n for (const chunk of retained) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(bytes);\n };\n try {\n return await withAbortSignal([], timeoutMs, async signal => {\n const cancel = () => void reader.cancel(signal?.reason).catch(() => undefined);\n signal?.addEventListener('abort', cancel, { once: true });\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return { body: decodeRetained(), bodyTruncated: false };\n }\n const available = maxBodyBytes - retainedBytes;\n const copyBytes = Math.min(available, value.byteLength);\n if (copyBytes > 0) {\n // Copy only bytes actually observed. In particular, accepting a\n // large safe maxBodyBytes must not allocate that full capacity.\n retained.push(value.slice(0, copyBytes));\n retainedBytes += copyBytes;\n }\n if (copyBytes < value.byteLength) {\n // This is a clone branch. Awaiting cancellation can wait for the SDK\n // to consume the original branch, which cannot happen until capture\n // returns, so cancel without awaiting.\n void reader.cancel().catch(() => undefined);\n return {\n body: decodeRetained(),\n bodyTruncated: true,\n bodyCaptureError: `Raw response capture exceeded maxBodyBytes (${maxBodyBytes})`,\n };\n }\n }\n } finally {\n signal?.removeEventListener('abort', cancel);\n }\n });\n } catch (error) {\n // Capture is observational: a stalled or broken clone must not prevent\n // the SDK from consuming the original response. Preserve bytes already\n // observed and mark the capture incomplete.\n const timedOut = error instanceof Error && error.name === 'TimeoutError';\n return {\n body: decodeRetained(),\n bodyTruncated: true,\n bodyCaptureError: timedOut\n ? `Raw response capture timed out after ${timeoutMs} ms before the response body completed`\n : 'Raw response capture ended before the response body completed',\n };\n }\n}\n"],"mappings":"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","names":["require"]}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
"source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
|
|
5
5
|
"source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
|
|
6
6
|
"upstream_adcp_version": "2.5.3",
|
|
7
|
-
"synced_at": "2026-09-
|
|
7
|
+
"synced_at": "2026-09-23T15:24:56.831Z"
|
|
8
8
|
}
|
|
@@ -7,8 +7,10 @@ type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<
|
|
|
7
7
|
* Extract the AdCP operation name from a JSON-RPC request body, if any.
|
|
8
8
|
*
|
|
9
9
|
* - MCP tool calls: `method === "tools/call"` → `params.name` is the op name.
|
|
10
|
-
* - A2A `message/send` / `message/stream`: the op name lives on the first
|
|
10
|
+
* - A2A 0.3 `message/send` / `message/stream`: the op name lives on the first
|
|
11
11
|
* data-kind part as `data.skill`.
|
|
12
|
+
* - A2A 1.x `SendMessage` / `SendStreamingMessage`: proto-JSON DataParts omit
|
|
13
|
+
* the legacy `kind` discriminator and carry the op at `data.skill`.
|
|
12
14
|
* - All other JSON-RPC methods (`initialize`, `tools/list`, notifications)
|
|
13
15
|
* return `undefined` — those are protocol-layer housekeeping, not AdCP
|
|
14
16
|
* operations subject to request-signing policy.
|
|
@@ -7,8 +7,10 @@ type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<
|
|
|
7
7
|
* Extract the AdCP operation name from a JSON-RPC request body, if any.
|
|
8
8
|
*
|
|
9
9
|
* - MCP tool calls: `method === "tools/call"` → `params.name` is the op name.
|
|
10
|
-
* - A2A `message/send` / `message/stream`: the op name lives on the first
|
|
10
|
+
* - A2A 0.3 `message/send` / `message/stream`: the op name lives on the first
|
|
11
11
|
* data-kind part as `data.skill`.
|
|
12
|
+
* - A2A 1.x `SendMessage` / `SendStreamingMessage`: proto-JSON DataParts omit
|
|
13
|
+
* the legacy `kind` discriminator and carry the op at `data.skill`.
|
|
12
14
|
* - All other JSON-RPC methods (`initialize`, `tools/list`, notifications)
|
|
13
15
|
* return `undefined` — those are protocol-layer housekeeping, not AdCP
|
|
14
16
|
* operations subject to request-signing policy.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-fetch.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/agent-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,+BAA+B,EAC/B,iCAAiC,EAClC,MAAM,eAAe,CAAC;AAGvB,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AA4C1F
|
|
1
|
+
{"version":3,"file":"agent-fetch.d.ts","sourceRoot":"","sources":["../../../src/lib/signing/agent-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,+BAA+B,EAC/B,iCAAiC,EAClC,MAAM,eAAe,CAAC;AAGvB,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AA4C1F;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAkCtE;AAsBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,UAAU,EAAE,kBAAkB,GAAG,SAAS,EAC1C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAQT;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAG1F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,+BAA+B,GAAG,SAAS,CAM9E;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,IAAI,+BAA+B,CAElH;AAED,+CAA+C;AAC/C,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,yBAAyB,GAChC,MAAM,IAAI,iCAAiC,CAE7C;AAED,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,yBAAyB,CAAC;IACnC,+EAA+E;IAC/E,aAAa,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,SAAS,CAiCxF;AAcD,MAAM,WAAW,6BAA6B;IAC5C,iFAAiF;IACjF,OAAO,EAAE,yBAAyB,CAAC;IACnC;;;;;;;;;OASG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,SAAS,CAQxF"}
|
|
@@ -73,14 +73,16 @@ function extractAdcpOperation(body) {
|
|
|
73
73
|
const params = rpc.params;
|
|
74
74
|
return typeof params?.name === "string" ? params.name : void 0;
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
const isLegacyA2a = rpc.method === "message/send" || rpc.method === "message/stream";
|
|
77
|
+
const isNativeA2a = rpc.method === "SendMessage" || rpc.method === "SendStreamingMessage";
|
|
78
|
+
if (isLegacyA2a || isNativeA2a) {
|
|
77
79
|
const params = rpc.params;
|
|
78
80
|
const parts = params?.message?.parts;
|
|
79
81
|
if (!Array.isArray(parts)) return void 0;
|
|
80
82
|
for (const part of parts) {
|
|
81
83
|
if (part && typeof part === "object") {
|
|
82
84
|
const p = part;
|
|
83
|
-
if (p.kind === "data" && typeof p.data?.skill === "string") {
|
|
85
|
+
if ((isNativeA2a || p.kind === "data") && typeof p.data?.skill === "string") {
|
|
84
86
|
return p.data.skill;
|
|
85
87
|
}
|
|
86
88
|
}
|