@adcp/sdk 9.4.0 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/core/AgentClient.d.ts +2 -2
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +4 -4
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AsyncHandler.d.ts +6 -0
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/ConversationTypes.d.ts +21 -0
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +40 -5
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +201 -52
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +1 -1
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +15 -4
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +3 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/protocols/a2a.d.ts +1 -1
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +66 -35
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/abort.d.ts +10 -0
- package/dist/lib/protocols/abort.d.ts.map +1 -0
- package/dist/lib/protocols/abort.js +107 -0
- package/dist/lib/protocols/abort.js.map +1 -0
- package/dist/lib/protocols/index.d.ts +10 -0
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +33 -9
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.d.ts +4 -0
- package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-tasks.js +27 -11
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp.d.ts +14 -2
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +116 -43
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +3 -1
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/signing/capability-priming.d.ts.map +1 -1
- package/dist/lib/signing/capability-priming.js +5 -1
- package/dist/lib/signing/capability-priming.js.map +1 -1
- package/dist/lib/substitution/index.d.ts +2 -0
- package/dist/lib/substitution/index.d.ts.map +1 -1
- package/dist/lib/substitution/index.js +3 -1
- package/dist/lib/substitution/index.js.map +1 -1
- package/dist/lib/substitution/observer/html-parser.d.ts.map +1 -1
- package/dist/lib/substitution/observer/html-parser.js +27 -9
- package/dist/lib/substitution/observer/html-parser.js.map +1 -1
- package/dist/lib/substitution/translate.d.ts +89 -0
- package/dist/lib/substitution/translate.d.ts.map +1 -0
- package/dist/lib/substitution/translate.js +144 -0
- package/dist/lib/substitution/translate.js.map +1 -0
- package/dist/lib/testing/client.js +3 -3
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +36 -9
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/types.d.ts +2 -0
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/package.json +1 -1
|
@@ -46,6 +46,7 @@ const idempotency_1 = require("../utils/idempotency");
|
|
|
46
46
|
const TaskExecutor_1 = require("./TaskExecutor");
|
|
47
47
|
const match_1 = require("./match");
|
|
48
48
|
const auth_1 = require("../auth");
|
|
49
|
+
const abort_1 = require("../protocols/abort");
|
|
49
50
|
const errors_1 = require("../errors");
|
|
50
51
|
const net_1 = require("../net");
|
|
51
52
|
const authorization_required_1 = require("../auth/oauth/authorization-required");
|
|
@@ -55,6 +56,7 @@ const webhooks_1 = require("../webhooks");
|
|
|
55
56
|
const response_unwrapper_1 = require("../utils/response-unwrapper");
|
|
56
57
|
const a2a_discovery_1 = require("../utils/a2a-discovery");
|
|
57
58
|
const crypto = __importStar(require("crypto"));
|
|
59
|
+
const abort_2 = require("../protocols/abort");
|
|
58
60
|
const capabilities_1 = require("../utils/capabilities");
|
|
59
61
|
const request_normalizer_1 = require("../utils/request-normalizer");
|
|
60
62
|
const validate_user_agent_1 = require("../utils/validate-user-agent");
|
|
@@ -179,6 +181,18 @@ function valueMatchesSchemaType(value, propSchema) {
|
|
|
179
181
|
return declared.includes(valueType);
|
|
180
182
|
return false;
|
|
181
183
|
}
|
|
184
|
+
function productHasPricingOptions(product) {
|
|
185
|
+
if (!product || typeof product !== 'object')
|
|
186
|
+
return false;
|
|
187
|
+
const options = product.pricing_options;
|
|
188
|
+
return Array.isArray(options) && options.length > 0;
|
|
189
|
+
}
|
|
190
|
+
function productIdForPricingDiagnostics(product) {
|
|
191
|
+
if (!product || typeof product !== 'object')
|
|
192
|
+
return undefined;
|
|
193
|
+
const id = product.product_id;
|
|
194
|
+
return typeof id === 'string' ? id : undefined;
|
|
195
|
+
}
|
|
182
196
|
function propertyListReferenceFromRequest(params) {
|
|
183
197
|
const value = params.property_list;
|
|
184
198
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -313,7 +327,8 @@ class SingleAgentClient {
|
|
|
313
327
|
* Returns the agent config with the discovered endpoint.
|
|
314
328
|
* Also computes the canonical base URL by stripping /mcp suffix.
|
|
315
329
|
*/
|
|
316
|
-
async ensureEndpointDiscovered() {
|
|
330
|
+
async ensureEndpointDiscovered(options) {
|
|
331
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
317
332
|
const needsDiscovery = this.normalizedAgent._needsDiscovery;
|
|
318
333
|
if (!needsDiscovery) {
|
|
319
334
|
return this.normalizedAgent;
|
|
@@ -330,7 +345,7 @@ class SingleAgentClient {
|
|
|
330
345
|
return this.discoveredAgent;
|
|
331
346
|
}
|
|
332
347
|
// Perform discovery
|
|
333
|
-
this.discoveredEndpoint = await this.discoverMCPEndpoint(this.normalizedAgent.agent_uri);
|
|
348
|
+
this.discoveredEndpoint = await this.discoverMCPEndpoint(this.normalizedAgent.agent_uri, options);
|
|
334
349
|
// Compute canonical base URL by stripping /mcp suffix
|
|
335
350
|
this.canonicalBaseUrl = this.computeBaseUrl(this.discoveredEndpoint);
|
|
336
351
|
this.discoveredAgent = {
|
|
@@ -345,7 +360,8 @@ class SingleAgentClient {
|
|
|
345
360
|
* Fetches the agent card and extracts the canonical URL.
|
|
346
361
|
* Returns the agent config with the canonical URL.
|
|
347
362
|
*/
|
|
348
|
-
async ensureCanonicalUrlResolved() {
|
|
363
|
+
async ensureCanonicalUrlResolved(options) {
|
|
364
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
349
365
|
const needsCanonicalUrl = this.normalizedAgent._needsCanonicalUrl;
|
|
350
366
|
if (!needsCanonicalUrl) {
|
|
351
367
|
return this.normalizedAgent;
|
|
@@ -358,7 +374,7 @@ class SingleAgentClient {
|
|
|
358
374
|
};
|
|
359
375
|
}
|
|
360
376
|
// Fetch agent card to get canonical URL
|
|
361
|
-
const canonicalUrl = await this.fetchA2ACanonicalUrl(this.normalizedAgent.agent_uri);
|
|
377
|
+
const canonicalUrl = await this.fetchA2ACanonicalUrl(this.normalizedAgent.agent_uri, options);
|
|
362
378
|
this.canonicalBaseUrl = canonicalUrl;
|
|
363
379
|
return {
|
|
364
380
|
...this.normalizedAgent,
|
|
@@ -372,7 +388,7 @@ class SingleAgentClient {
|
|
|
372
388
|
* - If the agent card fetch returns 401, throw AuthenticationRequiredError
|
|
373
389
|
* - Check for OAuth metadata to provide helpful guidance
|
|
374
390
|
*/
|
|
375
|
-
async fetchA2ACanonicalUrl(agentUri) {
|
|
391
|
+
async fetchA2ACanonicalUrl(agentUri, readOptions) {
|
|
376
392
|
const clientModule = require('@a2a-js/sdk/client');
|
|
377
393
|
const A2AClient = clientModule.A2AClient;
|
|
378
394
|
// adcp-client#1804 — wrap A2A card discovery in withResponseSizeLimit so
|
|
@@ -381,20 +397,22 @@ class SingleAgentClient {
|
|
|
381
397
|
// active ALS slot enforces the cap on the wire call. Matches the same
|
|
382
398
|
// pattern in `getAgentInfo` (closed #1799 via PR #1802).
|
|
383
399
|
const { withResponseSizeLimit, wrapFetchWithSizeLimit } = await Promise.resolve().then(() => __importStar(require('../protocols/responseSizeLimit')));
|
|
384
|
-
const
|
|
400
|
+
const transport = readOptions?.transport ?? this.config.transport;
|
|
401
|
+
const maxResponseBytes = transport?.maxResponseBytes;
|
|
402
|
+
const requestTimeoutMs = (0, abort_2.resolveRequestTimeoutMs)(transport?.requestTimeoutMs, abort_2.DEFAULT_REQUEST_TIMEOUT_MS);
|
|
385
403
|
const sizeLimitedFetch = wrapFetchWithSizeLimit((input, init) => fetch(input, init));
|
|
386
404
|
const authToken = this.normalizedAgent.auth_token;
|
|
387
405
|
let got401 = false;
|
|
388
|
-
const fetchImpl = async (url,
|
|
406
|
+
const fetchImpl = async (url, requestInit) => {
|
|
389
407
|
const headers = {
|
|
390
|
-
...
|
|
408
|
+
...requestInit?.headers,
|
|
391
409
|
...this.normalizedAgent.headers,
|
|
392
410
|
...(authToken && {
|
|
393
411
|
Authorization: `Bearer ${authToken}`,
|
|
394
412
|
'x-adcp-auth': authToken,
|
|
395
413
|
}),
|
|
396
414
|
};
|
|
397
|
-
const response = await sizeLimitedFetch(url, { ...
|
|
415
|
+
const response = await (0, abort_2.withAbortSignal)([readOptions?.signal, requestInit?.signal], requestTimeoutMs, signal => sizeLimitedFetch(url, { ...requestInit, headers, signal }));
|
|
398
416
|
// Track 401 errors for later handling
|
|
399
417
|
if (response.status === 401) {
|
|
400
418
|
got401 = true;
|
|
@@ -481,18 +499,25 @@ class SingleAgentClient {
|
|
|
481
499
|
*
|
|
482
500
|
* Note: This is async and called lazily on first agent interaction
|
|
483
501
|
*/
|
|
484
|
-
async discoverMCPEndpoint(providedUri) {
|
|
502
|
+
async discoverMCPEndpoint(providedUri, options) {
|
|
503
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
485
504
|
const { connectMCPWithFallback } = await Promise.resolve().then(() => __importStar(require('../protocols/mcp')));
|
|
486
505
|
const authToken = this.agent.auth_token;
|
|
487
506
|
const agentHeaders = this.agent.headers;
|
|
488
507
|
const authHeaders = { ...agentHeaders, ...(0, auth_1.createMCPAuthHeaders)(authToken) };
|
|
489
508
|
const testEndpoint = async (url) => {
|
|
490
509
|
try {
|
|
491
|
-
const client = await connectMCPWithFallback(new URL(url), authHeaders
|
|
510
|
+
const client = await connectMCPWithFallback(new URL(url), authHeaders, [], 'endpoint discovery', undefined, {
|
|
511
|
+
signal: options?.signal,
|
|
512
|
+
requestTimeoutMs: options?.transport?.requestTimeoutMs ?? this.config.transport?.requestTimeoutMs,
|
|
513
|
+
});
|
|
492
514
|
await client.close();
|
|
493
515
|
return { success: true };
|
|
494
516
|
}
|
|
495
517
|
catch (error) {
|
|
518
|
+
if ((0, abort_1.isAbortOrTimeoutError)(error)) {
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
496
521
|
if ((0, errors_1.is401Error)(error)) {
|
|
497
522
|
return { success: false, status: 401, error };
|
|
498
523
|
}
|
|
@@ -1041,6 +1066,7 @@ class SingleAgentClient {
|
|
|
1041
1066
|
* Automatically adapts requests for v2 servers and normalizes responses.
|
|
1042
1067
|
*/
|
|
1043
1068
|
async executeAndHandle(taskType, handlerName, params, inputHandler, options) {
|
|
1069
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
1044
1070
|
// Normalize params for backwards compatibility before validation
|
|
1045
1071
|
let normalizedParams = (0, request_normalizer_1.normalizeRequestParams)(taskType, params, {
|
|
1046
1072
|
skipIdempotencyAutoInject: options?.skipIdempotencyAutoInject,
|
|
@@ -1075,17 +1101,17 @@ class SingleAgentClient {
|
|
|
1075
1101
|
this.validateRequest(taskType, normalizedParams);
|
|
1076
1102
|
}
|
|
1077
1103
|
// Validate required features before sending request
|
|
1078
|
-
await this.validateTaskFeatures(taskType);
|
|
1104
|
+
await this.validateTaskFeatures(taskType, options);
|
|
1079
1105
|
// Guard mutating calls against pre-v3 sellers when opted in.
|
|
1080
1106
|
if (this.config.requireV3ForMutations && (0, idempotency_1.isMutatingTask)(taskType)) {
|
|
1081
|
-
await this.requireSupportedMajor(taskType);
|
|
1107
|
+
await this.requireSupportedMajor(taskType, options);
|
|
1082
1108
|
}
|
|
1083
1109
|
// Check for v3 features used against v2 servers - return empty result if unsupported
|
|
1084
|
-
const earlyResult = await this.getEarlyResultForUnsupportedFeatures(taskType, normalizedParams);
|
|
1110
|
+
const earlyResult = await this.getEarlyResultForUnsupportedFeatures(taskType, normalizedParams, options);
|
|
1085
1111
|
if (earlyResult) {
|
|
1086
1112
|
return (0, match_1.attachMatch)(earlyResult);
|
|
1087
1113
|
}
|
|
1088
|
-
const agent = await this.ensureEndpointDiscovered();
|
|
1114
|
+
const agent = await this.ensureEndpointDiscovered(options);
|
|
1089
1115
|
// Schema-driven pre-send validation runs on the unadapted v3 shape so
|
|
1090
1116
|
// wire-format adapters (e.g. adaptGetProductsRequestForV2) don't strip
|
|
1091
1117
|
// v3-only fields out from under the v3 bundled schema. Skip the entire
|
|
@@ -1095,7 +1121,7 @@ class SingleAgentClient {
|
|
|
1095
1121
|
this.executor.validateRequest(taskType, normalizedParams);
|
|
1096
1122
|
}
|
|
1097
1123
|
// Adapt request for the detected server and AdCP protocol versions.
|
|
1098
|
-
const serverVersion = await this.detectServerVersion();
|
|
1124
|
+
const serverVersion = await this.detectServerVersion(options);
|
|
1099
1125
|
const inputSchemaStripLogs = [];
|
|
1100
1126
|
const { params: adaptedParams, driftLogs: adaptDriftLogs } = this.adaptRequest(taskType, normalizedParams, serverVersion, inputSchemaStripLogs);
|
|
1101
1127
|
// Symmetric to the pre-adapter v3 pass above: when the adapter
|
|
@@ -1145,7 +1171,76 @@ class SingleAgentClient {
|
|
|
1145
1171
|
}
|
|
1146
1172
|
return result;
|
|
1147
1173
|
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Drop products that arrive without a usable `pricing_options[]` array from a
|
|
1176
|
+
* completed `get_products` response. `pricing_options` is required and
|
|
1177
|
+
* non-empty in AdCP 3.1; a product with no pricing model can't be bought, so
|
|
1178
|
+
* the SDK rejects it before callers and completion handlers see the list.
|
|
1179
|
+
*
|
|
1180
|
+
* Controlled by `config.validation.rejectProductsWithoutPricingOptions`
|
|
1181
|
+
* (default `true`) and applied on every completion path via
|
|
1182
|
+
* {@link applyProductPropertyPolicy}, independent of the response validation
|
|
1183
|
+
* mode.
|
|
1184
|
+
*/
|
|
1185
|
+
enforceProductPricingOptions(result, taskType) {
|
|
1186
|
+
if (taskType !== 'get_products')
|
|
1187
|
+
return result;
|
|
1188
|
+
if (this.config.validation?.rejectProductsWithoutPricingOptions === false)
|
|
1189
|
+
return result;
|
|
1190
|
+
if (!result.success || result.status !== 'completed' || !result.data)
|
|
1191
|
+
return result;
|
|
1192
|
+
const response = result.data;
|
|
1193
|
+
const products = response.products;
|
|
1194
|
+
if (!Array.isArray(products) || products.length === 0)
|
|
1195
|
+
return result;
|
|
1196
|
+
const kept = [];
|
|
1197
|
+
const rejected = [];
|
|
1198
|
+
products.forEach((product, index) => {
|
|
1199
|
+
if (productHasPricingOptions(product)) {
|
|
1200
|
+
kept.push(product);
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
const productId = productIdForPricingDiagnostics(product);
|
|
1204
|
+
rejected.push({ index, ...(productId ? { product_id: productId } : {}) });
|
|
1205
|
+
});
|
|
1206
|
+
if (rejected.length === 0)
|
|
1207
|
+
return result;
|
|
1208
|
+
const message = `Rejected ${rejected.length} product${rejected.length === 1 ? '' : 's'} without pricing_options`;
|
|
1209
|
+
// Mutate in place (like the property-policy filter path) so the
|
|
1210
|
+
// non-enumerable `match` accessor and result identity survive.
|
|
1211
|
+
result.data = { ...response, products: kept };
|
|
1212
|
+
result.metadata = {
|
|
1213
|
+
...result.metadata,
|
|
1214
|
+
productPricingPolicy: {
|
|
1215
|
+
ok: true,
|
|
1216
|
+
accepted_count: kept.length,
|
|
1217
|
+
rejected_count: rejected.length,
|
|
1218
|
+
rejected_products: rejected,
|
|
1219
|
+
},
|
|
1220
|
+
};
|
|
1221
|
+
result.debug_logs = [
|
|
1222
|
+
...(result.debug_logs ?? []),
|
|
1223
|
+
{
|
|
1224
|
+
type: 'warning',
|
|
1225
|
+
message,
|
|
1226
|
+
timestamp: new Date().toISOString(),
|
|
1227
|
+
details: {
|
|
1228
|
+
code: 'product_missing_pricing_options',
|
|
1229
|
+
task: taskType,
|
|
1230
|
+
agent_id: this.agent.id,
|
|
1231
|
+
rejected_count: rejected.length,
|
|
1232
|
+
rejected_products: rejected,
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
];
|
|
1236
|
+
return result;
|
|
1237
|
+
}
|
|
1148
1238
|
async applyProductPropertyPolicy(result, taskType, requestParams) {
|
|
1239
|
+
// Reject non-transactable products (no pricing_options) before any
|
|
1240
|
+
// property-policy evaluation, regardless of whether a property policy is
|
|
1241
|
+
// configured. This runs on the same completion chokepoint so it covers the
|
|
1242
|
+
// sync, polling, track, and webhook paths uniformly.
|
|
1243
|
+
result = this.enforceProductPricingOptions(result, taskType);
|
|
1149
1244
|
const policyConfig = this.config.validation?.productPropertyPolicy;
|
|
1150
1245
|
if (policyConfig === false || taskType !== 'get_products')
|
|
1151
1246
|
return result;
|
|
@@ -1431,6 +1526,9 @@ class SingleAgentClient {
|
|
|
1431
1526
|
...(policyResult.metadata.productPropertyPolicy
|
|
1432
1527
|
? { productPropertyPolicy: policyResult.metadata.productPropertyPolicy }
|
|
1433
1528
|
: {}),
|
|
1529
|
+
...(policyResult.metadata.productPricingPolicy
|
|
1530
|
+
? { productPricingPolicy: policyResult.metadata.productPricingPolicy }
|
|
1531
|
+
: {}),
|
|
1434
1532
|
};
|
|
1435
1533
|
return {
|
|
1436
1534
|
result: policyResult.data,
|
|
@@ -1584,13 +1682,13 @@ class SingleAgentClient {
|
|
|
1584
1682
|
*
|
|
1585
1683
|
* @returns TaskResult with empty data if v3 features are unsupported, null to proceed normally
|
|
1586
1684
|
*/
|
|
1587
|
-
async getEarlyResultForUnsupportedFeatures(taskType, params) {
|
|
1685
|
+
async getEarlyResultForUnsupportedFeatures(taskType, params, options) {
|
|
1588
1686
|
// Only check for tasks that have v3-specific features
|
|
1589
1687
|
if (taskType !== 'get_products') {
|
|
1590
1688
|
return null;
|
|
1591
1689
|
}
|
|
1592
1690
|
// Get capabilities to check what the server supports
|
|
1593
|
-
const capabilities = await this.getCapabilities();
|
|
1691
|
+
const capabilities = await this.getCapabilities(options);
|
|
1594
1692
|
// If server is v3, all features are supported - proceed normally
|
|
1595
1693
|
if (capabilities.version === 'v3') {
|
|
1596
1694
|
return null;
|
|
@@ -1867,7 +1965,7 @@ class SingleAgentClient {
|
|
|
1867
1965
|
* @param options - Task execution options
|
|
1868
1966
|
*/
|
|
1869
1967
|
async getAdcpCapabilities(params, inputHandler, options) {
|
|
1870
|
-
const agent = await this.ensureEndpointDiscovered();
|
|
1968
|
+
const agent = await this.ensureEndpointDiscovered(options);
|
|
1871
1969
|
this.executor.validateRequest('get_adcp_capabilities', params);
|
|
1872
1970
|
return this.executor.executeTask(agent, 'get_adcp_capabilities', params, inputHandler, options);
|
|
1873
1971
|
}
|
|
@@ -1996,6 +2094,7 @@ class SingleAgentClient {
|
|
|
1996
2094
|
* ```
|
|
1997
2095
|
*/
|
|
1998
2096
|
async executeTask(taskName, params, inputHandler, options) {
|
|
2097
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
1999
2098
|
const startTime = Date.now();
|
|
2000
2099
|
try {
|
|
2001
2100
|
const normalizedParams = (0, request_normalizer_1.normalizeRequestParams)(taskName, params, {
|
|
@@ -2007,11 +2106,11 @@ class SingleAgentClient {
|
|
|
2007
2106
|
// (get_products / get_signals). `effectiveOptions` carries disableWebhook
|
|
2008
2107
|
// so no push_notification_config reaches a seller that can't accept it.
|
|
2009
2108
|
const { options: effectiveOptions, driftLog: webhookDriftLog } = this.suppressPre31DiscoveryWebhook(taskName, options);
|
|
2010
|
-
await this.validateTaskFeatures(taskName);
|
|
2109
|
+
await this.validateTaskFeatures(taskName, options);
|
|
2011
2110
|
if (this.config.requireV3ForMutations && (0, idempotency_1.isMutatingTask)(taskName)) {
|
|
2012
|
-
await this.requireSupportedMajor(taskName);
|
|
2111
|
+
await this.requireSupportedMajor(taskName, options);
|
|
2013
2112
|
}
|
|
2014
|
-
const agent = await this.ensureEndpointDiscovered();
|
|
2113
|
+
const agent = await this.ensureEndpointDiscovered(options);
|
|
2015
2114
|
// Schema-driven pre-send validation runs on the unadapted v3 shape so
|
|
2016
2115
|
// wire-format adapters (e.g. adaptGetProductsRequestForV2) don't strip
|
|
2017
2116
|
// v3-only fields out from under the v3 bundled schema. Skip the entire
|
|
@@ -2021,7 +2120,7 @@ class SingleAgentClient {
|
|
|
2021
2120
|
this.executor.validateRequest(taskName, normalizedParams);
|
|
2022
2121
|
}
|
|
2023
2122
|
// Adapt request for the detected server and AdCP protocol versions.
|
|
2024
|
-
const serverVersion = await this.detectServerVersion();
|
|
2123
|
+
const serverVersion = await this.detectServerVersion(options);
|
|
2025
2124
|
const inputSchemaStripLogs = [];
|
|
2026
2125
|
const { params: adaptedParams, driftLogs: adaptDriftLogs } = this.adaptRequest(taskName, normalizedParams, serverVersion, inputSchemaStripLogs);
|
|
2027
2126
|
// Symmetric warn-only post-adapter pass against the v2.5 schema bundle.
|
|
@@ -2055,7 +2154,8 @@ class SingleAgentClient {
|
|
|
2055
2154
|
if (error instanceof errors_1.AuthenticationRequiredError ||
|
|
2056
2155
|
error instanceof errors_1.TaskTimeoutError ||
|
|
2057
2156
|
error instanceof errors_1.VersionUnsupportedError ||
|
|
2058
|
-
error instanceof errors_1.FeatureUnsupportedError
|
|
2157
|
+
error instanceof errors_1.FeatureUnsupportedError ||
|
|
2158
|
+
(0, abort_1.isAbortOrTimeoutError)(error)) {
|
|
2059
2159
|
throw error;
|
|
2060
2160
|
}
|
|
2061
2161
|
// Unexpected pre-flight errors (e.g. a TypeError from response parsing
|
|
@@ -2408,17 +2508,25 @@ class SingleAgentClient {
|
|
|
2408
2508
|
* });
|
|
2409
2509
|
* ```
|
|
2410
2510
|
*/
|
|
2411
|
-
async getAgentInfo() {
|
|
2511
|
+
async getAgentInfo(options) {
|
|
2412
2512
|
// adcp-client#1799 — wrap every wire call in the response-size cap so
|
|
2413
2513
|
// `transport.maxResponseBytes` extends to discovery / tools-list bodies.
|
|
2414
2514
|
// `withResponseSizeLimit` is a no-op when no cap is configured.
|
|
2415
2515
|
const { withResponseSizeLimit } = await Promise.resolve().then(() => __importStar(require('../protocols/responseSizeLimit')));
|
|
2416
|
-
|
|
2516
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
2517
|
+
const transport = options?.transport ?? this.config.transport;
|
|
2518
|
+
const maxResponseBytes = transport?.maxResponseBytes;
|
|
2519
|
+
const requestTimeoutMs = (0, abort_2.resolveRequestTimeoutMs)(transport?.requestTimeoutMs, abort_2.DEFAULT_REQUEST_TIMEOUT_MS);
|
|
2520
|
+
const clientRequestTimeoutMs = (0, abort_2.resolveClientRequestTimeoutMs)(transport?.requestTimeoutMs);
|
|
2521
|
+
const mcpRequestOptions = {
|
|
2522
|
+
...(options?.signal && { signal: options.signal }),
|
|
2523
|
+
...(clientRequestTimeoutMs !== undefined && { timeout: clientRequestTimeoutMs }),
|
|
2524
|
+
};
|
|
2417
2525
|
if (this.normalizedAgent.protocol === 'mcp') {
|
|
2418
2526
|
// In-process: use the pre-connected client instead of opening a new HTTP connection
|
|
2419
2527
|
if (this.normalizedAgent._inProcessMcpClient) {
|
|
2420
2528
|
const mcpClient = this.normalizedAgent._inProcessMcpClient;
|
|
2421
|
-
const toolsList = await withResponseSizeLimit(maxResponseBytes, () => mcpClient.listTools());
|
|
2529
|
+
const toolsList = await withResponseSizeLimit(maxResponseBytes, () => mcpClient.listTools(undefined, mcpRequestOptions));
|
|
2422
2530
|
const tools = toolsList.tools.map(tool => ({
|
|
2423
2531
|
name: tool.name,
|
|
2424
2532
|
description: tool.description,
|
|
@@ -2434,7 +2542,7 @@ class SingleAgentClient {
|
|
|
2434
2542
|
};
|
|
2435
2543
|
}
|
|
2436
2544
|
// Discover endpoint if needed
|
|
2437
|
-
const agent = await this.ensureEndpointDiscovered();
|
|
2545
|
+
const agent = await this.ensureEndpointDiscovered(options);
|
|
2438
2546
|
// Use the shared connectMCP path so both static bearer AND saved OAuth
|
|
2439
2547
|
// tokens work. OAuth takes the refresh-capable authProvider branch.
|
|
2440
2548
|
// Header-only auth (basic, x-api-key, custom routing) lives on
|
|
@@ -2443,6 +2551,12 @@ class SingleAgentClient {
|
|
|
2443
2551
|
// SDK doesn't emit a competing `Authorization: Bearer …`.
|
|
2444
2552
|
const { connectMCP } = await Promise.resolve().then(() => __importStar(require('../protocols/mcp')));
|
|
2445
2553
|
const connectOptions = { agentUrl: agent.agent_uri };
|
|
2554
|
+
if (options?.signal) {
|
|
2555
|
+
connectOptions.signal = options.signal;
|
|
2556
|
+
}
|
|
2557
|
+
if (transport?.requestTimeoutMs !== undefined) {
|
|
2558
|
+
connectOptions.requestTimeoutMs = transport.requestTimeoutMs;
|
|
2559
|
+
}
|
|
2446
2560
|
if (this.normalizedAgent.headers && Object.keys(this.normalizedAgent.headers).length > 0) {
|
|
2447
2561
|
connectOptions.customHeaders = this.normalizedAgent.headers;
|
|
2448
2562
|
}
|
|
@@ -2457,7 +2571,7 @@ class SingleAgentClient {
|
|
|
2457
2571
|
}
|
|
2458
2572
|
const { client: mcpClient } = await connectMCP(connectOptions);
|
|
2459
2573
|
try {
|
|
2460
|
-
const toolsList = await withResponseSizeLimit(maxResponseBytes, () => mcpClient.listTools());
|
|
2574
|
+
const toolsList = await withResponseSizeLimit(maxResponseBytes, () => mcpClient.listTools(undefined, mcpRequestOptions));
|
|
2461
2575
|
const tools = toolsList.tools.map(tool => ({
|
|
2462
2576
|
name: tool.name,
|
|
2463
2577
|
description: tool.description,
|
|
@@ -2491,17 +2605,39 @@ class SingleAgentClient {
|
|
|
2491
2605
|
// calls native `fetch` directly and ignores `transport.maxResponseBytes`.
|
|
2492
2606
|
const { wrapFetchWithSizeLimit } = await Promise.resolve().then(() => __importStar(require('../protocols/responseSizeLimit')));
|
|
2493
2607
|
const authToken = this.normalizedAgent.auth_token;
|
|
2608
|
+
const agentHeaders = this.normalizedAgent.headers ?? {};
|
|
2494
2609
|
const sizeLimitedFetch = wrapFetchWithSizeLimit((input, init) => fetch(input, init));
|
|
2495
|
-
const
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2610
|
+
const normalizeHeaders = (headers) => {
|
|
2611
|
+
const normalized = {};
|
|
2612
|
+
if (!headers)
|
|
2613
|
+
return normalized;
|
|
2614
|
+
if (headers instanceof Headers) {
|
|
2615
|
+
headers.forEach((value, key) => {
|
|
2616
|
+
normalized[key] = value;
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
else if (Array.isArray(headers)) {
|
|
2620
|
+
for (const [key, value] of headers) {
|
|
2621
|
+
normalized[key] = value;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
else {
|
|
2625
|
+
Object.assign(normalized, headers);
|
|
2503
2626
|
}
|
|
2504
|
-
|
|
2627
|
+
return normalized;
|
|
2628
|
+
};
|
|
2629
|
+
const buildHeaders = (requestInit) => ({
|
|
2630
|
+
...normalizeHeaders(requestInit?.headers),
|
|
2631
|
+
...agentHeaders,
|
|
2632
|
+
...(authToken && {
|
|
2633
|
+
Authorization: `Bearer ${authToken}`,
|
|
2634
|
+
'x-adcp-auth': authToken,
|
|
2635
|
+
}),
|
|
2636
|
+
});
|
|
2637
|
+
const fetchImpl = async (url, requestInit) => {
|
|
2638
|
+
const headers = buildHeaders(requestInit);
|
|
2639
|
+
return (0, abort_2.withAbortSignal)([options?.signal, requestInit?.signal], requestTimeoutMs, signal => sizeLimitedFetch(url, { ...requestInit, headers, signal }));
|
|
2640
|
+
};
|
|
2505
2641
|
const cardUrls = (0, a2a_discovery_1.buildCardUrls)(this.normalizedAgent.agent_uri);
|
|
2506
2642
|
let client;
|
|
2507
2643
|
let lastCardError = new Error(`A2A agent card not found at ${cardUrls.join(', ')}`);
|
|
@@ -2559,14 +2695,15 @@ class SingleAgentClient {
|
|
|
2559
2695
|
* }
|
|
2560
2696
|
* ```
|
|
2561
2697
|
*/
|
|
2562
|
-
async getCapabilities() {
|
|
2698
|
+
async getCapabilities(options) {
|
|
2699
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
2563
2700
|
// Return cached if available
|
|
2564
2701
|
if (this.cachedCapabilities) {
|
|
2565
2702
|
this.maybeWarnV2Sunset(this.cachedCapabilities);
|
|
2566
2703
|
return this.cachedCapabilities;
|
|
2567
2704
|
}
|
|
2568
2705
|
// First get tool list to support both detection methods
|
|
2569
|
-
const agentInfo = await this.getAgentInfo();
|
|
2706
|
+
const agentInfo = await this.getAgentInfo(options);
|
|
2570
2707
|
const tools = agentInfo.tools.map(t => ({
|
|
2571
2708
|
name: t.name,
|
|
2572
2709
|
description: t.description,
|
|
@@ -2585,8 +2722,15 @@ class SingleAgentClient {
|
|
|
2585
2722
|
// because normalizeAgentConfig returns early when _inProcessMcpClient is set). The
|
|
2586
2723
|
// executor then hits ProtocolClient.callTool which reads _inProcessMcpClient directly,
|
|
2587
2724
|
// so the sentinel adcp-in-process:// URI never reaches validateAgentUrl.
|
|
2588
|
-
const agent = await this.ensureEndpointDiscovered();
|
|
2589
|
-
const result = await this.executor.executeTask(agent, 'get_adcp_capabilities', {}, undefined);
|
|
2725
|
+
const agent = await this.ensureEndpointDiscovered(options);
|
|
2726
|
+
const result = await this.executor.executeTask(agent, 'get_adcp_capabilities', {}, undefined, options);
|
|
2727
|
+
(0, abort_2.throwIfAborted)(options?.signal);
|
|
2728
|
+
const requestTimeoutMs = (0, abort_2.resolveRequestTimeoutMs)(options?.transport?.requestTimeoutMs ?? this.config.transport?.requestTimeoutMs);
|
|
2729
|
+
if (!result.success &&
|
|
2730
|
+
requestTimeoutMs !== undefined &&
|
|
2731
|
+
/\b(requesttimeout|timeout|timed out)\b/i.test(result.error ?? '')) {
|
|
2732
|
+
throw (0, abort_2.createTimeoutError)(requestTimeoutMs);
|
|
2733
|
+
}
|
|
2590
2734
|
if (result.success && result.data) {
|
|
2591
2735
|
this.cachedCapabilities = (0, capabilities_1.augmentCapabilitiesFromTools)((0, capabilities_1.parseCapabilitiesResponse)(result.data), tools);
|
|
2592
2736
|
this.maybeWarnV2Sunset(this.cachedCapabilities);
|
|
@@ -2641,7 +2785,7 @@ class SingleAgentClient {
|
|
|
2641
2785
|
`since the agent has the v3-only discovery tool. ` +
|
|
2642
2786
|
`This client routes to v3 adapters, but calls reading capability details ` +
|
|
2643
2787
|
`(idempotency TTL, supported_versions, feature flags) will fail until the agent ` +
|
|
2644
|
-
`operator fixes the capabilities endpoint
|
|
2788
|
+
`operator fixes the capabilities endpoint.`, {
|
|
2645
2789
|
success: result.success,
|
|
2646
2790
|
hasError: !!result.error,
|
|
2647
2791
|
hasData: !!result.data,
|
|
@@ -2651,15 +2795,16 @@ class SingleAgentClient {
|
|
|
2651
2795
|
// Re-throw errors that indicate real infrastructure problems —
|
|
2652
2796
|
// only fall through for tool-execution failures (the agent
|
|
2653
2797
|
// advertises get_adcp_capabilities but can't actually serve it).
|
|
2654
|
-
if (error instanceof errors_1.AuthenticationRequiredError ||
|
|
2798
|
+
if (error instanceof errors_1.AuthenticationRequiredError ||
|
|
2799
|
+
error instanceof errors_1.TaskTimeoutError ||
|
|
2800
|
+
(0, abort_1.isAbortOrTimeoutError)(error)) {
|
|
2655
2801
|
throw error;
|
|
2656
2802
|
}
|
|
2657
2803
|
console.warn(`[AdCP] Agent "${this.agent.id}" advertises get_adcp_capabilities but the call ` +
|
|
2658
2804
|
`threw — treating as v3 (synthetic) since the agent has the v3-only discovery tool. ` +
|
|
2659
2805
|
`This client routes to v3 adapters, but calls reading capability details ` +
|
|
2660
2806
|
`(idempotency TTL, supported_versions, feature flags) will fail until the agent ` +
|
|
2661
|
-
`operator fixes the capabilities endpoint
|
|
2662
|
-
`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
2807
|
+
`operator fixes the capabilities endpoint.`);
|
|
2663
2808
|
}
|
|
2664
2809
|
// Synthesize v3 capabilities from the tool list. Reached only when
|
|
2665
2810
|
// the executor returned non-v3-shaped data, OR threw a non-auth
|
|
@@ -2750,8 +2895,8 @@ class SingleAgentClient {
|
|
|
2750
2895
|
*
|
|
2751
2896
|
* @returns 'v2' or 'v3' based on server capabilities
|
|
2752
2897
|
*/
|
|
2753
|
-
async detectServerVersion() {
|
|
2754
|
-
const capabilities = await this.getCapabilities();
|
|
2898
|
+
async detectServerVersion(options) {
|
|
2899
|
+
const capabilities = await this.getCapabilities(options);
|
|
2755
2900
|
return capabilities.version;
|
|
2756
2901
|
}
|
|
2757
2902
|
/**
|
|
@@ -2848,13 +2993,17 @@ class SingleAgentClient {
|
|
|
2848
2993
|
*
|
|
2849
2994
|
* Skipped when validateFeatures is false or the task has no feature requirements.
|
|
2850
2995
|
*/
|
|
2851
|
-
async validateTaskFeatures(taskName) {
|
|
2996
|
+
async validateTaskFeatures(taskName, options) {
|
|
2852
2997
|
if (this.config.validateFeatures === false)
|
|
2853
2998
|
return;
|
|
2854
2999
|
const requiredFeatures = capabilities_1.TASK_FEATURE_MAP[taskName];
|
|
2855
3000
|
if (!requiredFeatures || requiredFeatures.length === 0)
|
|
2856
3001
|
return;
|
|
2857
|
-
await this.
|
|
3002
|
+
const capabilities = await this.getCapabilities(options);
|
|
3003
|
+
const missing = requiredFeatures.filter(f => !(0, capabilities_1.resolveFeature)(capabilities, f));
|
|
3004
|
+
if (missing.length > 0) {
|
|
3005
|
+
throw new errors_1.FeatureUnsupportedError(missing, (0, capabilities_1.listDeclaredFeatures)(capabilities), this.agent.agent_uri);
|
|
3006
|
+
}
|
|
2858
3007
|
}
|
|
2859
3008
|
/**
|
|
2860
3009
|
* Fail version-incompatible request shapes before schema validation.
|
|
@@ -2968,10 +3117,10 @@ class SingleAgentClient {
|
|
|
2968
3117
|
*
|
|
2969
3118
|
* Throws `VersionUnsupportedError` with the specific reason on failure.
|
|
2970
3119
|
*/
|
|
2971
|
-
async requireSupportedMajor(taskType = 'request') {
|
|
3120
|
+
async requireSupportedMajor(taskType = 'request', options) {
|
|
2972
3121
|
if (this.isV2Allowed())
|
|
2973
3122
|
return;
|
|
2974
|
-
const capabilities = await this.getCapabilities();
|
|
3123
|
+
const capabilities = await this.getCapabilities(options);
|
|
2975
3124
|
// Synthetic capabilities — no authoritative `get_adcp_capabilities`
|
|
2976
3125
|
// response, so the version + idempotency-TTL fields couldn't be read.
|
|
2977
3126
|
// Route as the synthesized version (v2 when the v3 discovery tool is
|