@adcp/sdk 7.2.0 → 7.4.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.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +58 -14
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +68 -26
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/errors/index.d.ts +8 -3
- package/dist/lib/errors/index.d.ts.map +1 -1
- package/dist/lib/errors/index.js +1 -1
- package/dist/lib/errors/index.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.map +1 -1
- package/dist/lib/protocols/index.d.ts +16 -6
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/responseSizeLimit.js +7 -0
- package/dist/lib/protocols/responseSizeLimit.js.map +1 -1
- package/dist/lib/registry/index.d.ts +36 -3
- package/dist/lib/registry/index.d.ts.map +1 -1
- package/dist/lib/registry/index.js +41 -5
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +95 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +543 -35
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/example-tld-guard.d.ts +9 -0
- package/dist/lib/server/example-tld-guard.d.ts.map +1 -0
- package/dist/lib/server/example-tld-guard.js +25 -0
- package/dist/lib/server/example-tld-guard.js.map +1 -0
- package/dist/lib/server/idempotency/store.d.ts +6 -5
- package/dist/lib/server/idempotency/store.d.ts.map +1 -1
- package/dist/lib/server/idempotency/store.js +11 -14
- package/dist/lib/server/idempotency/store.js.map +1 -1
- package/dist/lib/server/index.d.ts +5 -3
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +17 -4
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/test-controller-bridge.d.ts +885 -1
- package/dist/lib/server/test-controller-bridge.d.ts.map +1 -1
- package/dist/lib/server/test-controller-bridge.js +1502 -2
- package/dist/lib/server/test-controller-bridge.js.map +1 -1
- package/dist/lib/testing/client.d.ts +8 -0
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +73 -2
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +10 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +11 -31
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/index.d.ts +1 -1
- package/dist/lib/testing/compliance/index.d.ts.map +1 -1
- package/dist/lib/testing/compliance/index.js +2 -1
- package/dist/lib/testing/compliance/index.js.map +1 -1
- package/dist/lib/testing/index.d.ts +2 -2
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +17 -3
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.js +11 -1
- package/dist/lib/testing/scenarios/error-compliance.js.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.js +6 -4
- package/dist/lib/testing/storyboard/parallel-dispatch.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +18 -7
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/types/core.generated.d.ts +1212 -20
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +4 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/inline-enums.generated.d.ts +6 -2
- package/dist/lib/types/inline-enums.generated.d.ts.map +1 -1
- package/dist/lib/types/inline-enums.generated.js +11 -5
- package/dist/lib/types/inline-enums.generated.js.map +1 -1
- package/dist/lib/types/schemas.generated.d.ts +26202 -26253
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +1353 -1300
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +1082 -21
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/types/wellknown-schemas.generated.d.ts +2 -2
- package/dist/lib/validation/sync-creatives.d.ts +6 -6
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/examples/hello_creative_adapter_ad_server.ts +5 -0
- package/examples/hello_creative_adapter_template.ts +5 -0
- package/examples/hello_seller_adapter_guaranteed.ts +5 -0
- package/examples/hello_seller_adapter_non_guaranteed.ts +5 -0
- package/examples/hello_seller_adapter_social.ts +5 -0
- package/examples/hello_si_adapter_brand.ts +5 -0
- package/package.json +3 -2
- package/skills/call-adcp-agent/SKILL.md +1 -0
|
@@ -246,6 +246,42 @@ function stampReplayed(response) {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Stamp a non-normative `_bridge` marker on a response that the
|
|
251
|
+
* `testController` bridge augmented with seeded fixtures.
|
|
252
|
+
*
|
|
253
|
+
* Mirrors {@link stampReplayed} — sets `_bridge` on `structuredContent`
|
|
254
|
+
* AND on the parsed JSON in `content[0].text`, so A2A/REST adapters that
|
|
255
|
+
* consume the text body see the same envelope MCP does.
|
|
256
|
+
*
|
|
257
|
+
* Only call this AFTER a successful merge — for singleton-replace tools
|
|
258
|
+
* (`get_account_financials`, `get_brand_identity`, `si_get_offering`,
|
|
259
|
+
* `get_property_list`, `get_collection_list`, `get_content_standards`)
|
|
260
|
+
* gate on `merged !== sc` so the marker only fires when the seeded fixture
|
|
261
|
+
* actually replaced the handler payload.
|
|
262
|
+
*/
|
|
263
|
+
function stampBridge(response, callback, tool, mergedCount) {
|
|
264
|
+
if (!response.structuredContent || typeof response.structuredContent !== 'object')
|
|
265
|
+
return;
|
|
266
|
+
const marker = { callback, tool, merged_count: mergedCount };
|
|
267
|
+
const sc = response.structuredContent;
|
|
268
|
+
sc._bridge = marker;
|
|
269
|
+
if (Array.isArray(response.content)) {
|
|
270
|
+
const first = response.content[0];
|
|
271
|
+
if (first && first.type === 'text' && typeof first.text === 'string') {
|
|
272
|
+
try {
|
|
273
|
+
const parsed = JSON.parse(first.text);
|
|
274
|
+
if (parsed && typeof parsed === 'object') {
|
|
275
|
+
parsed._bridge = marker;
|
|
276
|
+
first.text = JSON.stringify(parsed);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// Text isn't JSON — leave it alone (implausible for AdCP responses).
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
249
285
|
/**
|
|
250
286
|
* Remove per-request echo fields (`context`) from a formatted MCP response
|
|
251
287
|
* before caching. The buyer's `correlation_id` is scoped to the individual
|
|
@@ -1204,6 +1240,29 @@ function buildSupportedVersionsList(capConfig, serverPin) {
|
|
|
1204
1240
|
*/
|
|
1205
1241
|
function createAdcpServer(config) {
|
|
1206
1242
|
const { name, version, adcpVersion: configuredAdcpVersion, resolveAccount, resolveAccountFromAuth, resolveSessionKey, agentRegistry, exposeErrorDetails = process.env.NODE_ENV !== 'production', stateStore = DEFAULT_STATE_STORE, logger = noopLogger, capabilities: capConfig, idempotency: idempotencyConfig, resolveIdempotencyPrincipal, instructions: instructionsOption, onInstructionsError = 'skip', taskStore, taskMessageQueue, webhooks, signedRequests, validation: validationConfig, credentialPolicy, testController: testControllerBridge, } = config;
|
|
1243
|
+
// One-shot construction-time warn when `testController` is wired without
|
|
1244
|
+
// any account resolver. The dispatch-time sandbox gate admits requests
|
|
1245
|
+
// where `ctx.account === undefined`, so without a resolver the only
|
|
1246
|
+
// remaining check is buyer-supplied `account.sandbox` / `context.sandbox`
|
|
1247
|
+
// on the request — caller-controlled, not a trust boundary. Storyboard
|
|
1248
|
+
// runners legitimately have no account scoping and should ignore this
|
|
1249
|
+
// warning; production bindings need to wire `resolveAccount` (or
|
|
1250
|
+
// `resolveAccountFromAuth` for OAuth-passthrough setups) so the gate's
|
|
1251
|
+
// account-side check has teeth.
|
|
1252
|
+
//
|
|
1253
|
+
// Dual-emit: `process.emitWarning` writes to stderr by default so the
|
|
1254
|
+
// signal is visible even when `logger` is the default `noopLogger`
|
|
1255
|
+
// (the day-one case where the misconfig is most likely). `logger.warn`
|
|
1256
|
+
// also fires so adopters with configured logging pipelines see it in
|
|
1257
|
+
// their normal channel. The `code` lets adopters silence it via
|
|
1258
|
+
// `--no-warnings=ADCP_BRIDGE_NO_RESOLVER` if they're knowingly running
|
|
1259
|
+
// a storyboard-runner config. See `AdcpServerConfig.testController`
|
|
1260
|
+
// JSDoc § "Security — trust boundary" and #1784.
|
|
1261
|
+
if (testControllerBridge != null && resolveAccount === undefined && resolveAccountFromAuth === undefined) {
|
|
1262
|
+
const message = '[adcp/createAdcpServer] testController is wired but no account resolver — configure resolveAccount (or resolveAccountFromAuth) for production. Storyboard runners without account scoping can ignore. Details: https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/VALIDATE-YOUR-AGENT.md';
|
|
1263
|
+
process.emitWarning(message, { type: 'AdcpServerConfigWarning', code: 'ADCP_BRIDGE_NO_RESOLVER' });
|
|
1264
|
+
logger.warn(message);
|
|
1265
|
+
}
|
|
1207
1266
|
// Pre-resolve credential-policy patterns once. The patterns config is
|
|
1208
1267
|
// a stable property of `CredentialPolicyConfig`; pulling it out here
|
|
1209
1268
|
// keeps the per-call hot path (`scanArgsForCredentials`) free of the
|
|
@@ -2202,50 +2261,499 @@ function createAdcpServer(config) {
|
|
|
2202
2261
|
else {
|
|
2203
2262
|
formatted = wrap(result);
|
|
2204
2263
|
}
|
|
2205
|
-
// --- Test-controller bridge: augment
|
|
2206
|
-
//
|
|
2207
|
-
//
|
|
2208
|
-
//
|
|
2209
|
-
//
|
|
2210
|
-
//
|
|
2211
|
-
//
|
|
2212
|
-
//
|
|
2213
|
-
//
|
|
2214
|
-
//
|
|
2215
|
-
|
|
2216
|
-
|
|
2264
|
+
// --- Test-controller bridge: augment read-side tools with seeded fixtures. ---
|
|
2265
|
+
// Each per-tool callback (`getSeededProducts`, `getSeededCreatives`, ...)
|
|
2266
|
+
// is opt-in by presence on the bridge interface. All callbacks share the
|
|
2267
|
+
// same triply-gated contract:
|
|
2268
|
+
// 1. The bridge is registered AND has the matching callback;
|
|
2269
|
+
// 2. The handler returned a success envelope (not an `adcp_error`);
|
|
2270
|
+
// 3. The request carries a sandbox marker (account.sandbox === true or
|
|
2271
|
+
// context.sandbox === true) AND, if `resolveAccount` produced a
|
|
2272
|
+
// record, that record is flagged `sandbox: true` too.
|
|
2273
|
+
// For array-collection tools, seeded entries append to the handler's
|
|
2274
|
+
// response with seeded winning on id collision (same as `getSeededProducts`).
|
|
2275
|
+
// `get_account_financials` is the exception — singleton response, so the
|
|
2276
|
+
// seeded entry REPLACES the handler payload when its `account.account_id`
|
|
2277
|
+
// matches the request's `account.account_id`.
|
|
2278
|
+
// Diagnostic for adopters chasing "why aren't my fixtures showing":
|
|
2279
|
+
// when the request carries a sandbox marker but the resolved account
|
|
2280
|
+
// is explicitly non-sandbox, the gate rejects silently. Emit a
|
|
2281
|
+
// `debug` line so the rejection is observable in dev logs without
|
|
2282
|
+
// adding noise to production traffic (where the gate's first check
|
|
2283
|
+
// — `isSandboxRequestForSeeding` — fails first and never reaches
|
|
2284
|
+
// this branch).
|
|
2285
|
+
if (testControllerBridge &&
|
|
2286
|
+
!isErrorResponse(formatted) &&
|
|
2287
|
+
(0, test_controller_bridge_1.isSandboxRequest)(params) &&
|
|
2288
|
+
ctx.account !== undefined &&
|
|
2289
|
+
!(typeof ctx.account === 'object' &&
|
|
2290
|
+
ctx.account !== null &&
|
|
2291
|
+
ctx.account.sandbox === true)) {
|
|
2292
|
+
// Include the resolved account_id so the log line is self-
|
|
2293
|
+
// diagnostic — an adopter chasing "why aren't my fixtures
|
|
2294
|
+
// showing" can match the rejected account against their
|
|
2295
|
+
// resolveAccount source without correlating across log lines.
|
|
2296
|
+
// account_ids appear in normal request logs already; no new
|
|
2297
|
+
// PII surface.
|
|
2298
|
+
const resolvedAccountId = typeof ctx.account === 'object' &&
|
|
2299
|
+
ctx.account !== null &&
|
|
2300
|
+
typeof ctx.account.account_id === 'string'
|
|
2301
|
+
? ctx.account.account_id
|
|
2302
|
+
: undefined;
|
|
2303
|
+
logger.debug('test-controller bridge: request is sandbox-flagged but resolved account is not sandbox; skipping merge', { tool: toolName, resolved_account_id: resolvedAccountId });
|
|
2304
|
+
}
|
|
2305
|
+
if (testControllerBridge &&
|
|
2217
2306
|
!isErrorResponse(formatted) &&
|
|
2218
2307
|
(0, test_controller_bridge_1.isSandboxRequest)(params) &&
|
|
2219
|
-
// If resolveAccount produced a record, require it to be flagged
|
|
2220
|
-
// sandbox too. If no account was resolved, the request-signal
|
|
2221
|
-
// check above is the only line of defense — keep that contract.
|
|
2222
2308
|
(ctx.account === undefined ||
|
|
2223
2309
|
(typeof ctx.account === 'object' &&
|
|
2224
2310
|
ctx.account !== null &&
|
|
2225
2311
|
ctx.account.sandbox === true))) {
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
const
|
|
2234
|
-
if (
|
|
2235
|
-
const
|
|
2236
|
-
|
|
2312
|
+
const bridgeCtx = { input: params };
|
|
2313
|
+
if (ctx.account !== undefined)
|
|
2314
|
+
bridgeCtx.account = ctx.account;
|
|
2315
|
+
// get_products
|
|
2316
|
+
if (toolName === 'get_products' && testControllerBridge.getSeededProducts) {
|
|
2317
|
+
try {
|
|
2318
|
+
const rawSeeded = await testControllerBridge.getSeededProducts(bridgeCtx);
|
|
2319
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededProducts)(rawSeeded, logger);
|
|
2320
|
+
if (seeded.length > 0) {
|
|
2321
|
+
const sc = formatted.structuredContent;
|
|
2322
|
+
if (sc && typeof sc === 'object') {
|
|
2323
|
+
const merged = (0, test_controller_bridge_1.mergeSeededProductsIntoResponse)(sc, seeded);
|
|
2324
|
+
formatted = wrap(merged);
|
|
2325
|
+
stampBridge(formatted, 'getSeededProducts', toolName, seeded.length);
|
|
2326
|
+
}
|
|
2237
2327
|
}
|
|
2238
2328
|
}
|
|
2329
|
+
catch (err) {
|
|
2330
|
+
// Bridge failures are sandbox-only by construction, so logging +
|
|
2331
|
+
// returning the handler's response is the right default — a broken
|
|
2332
|
+
// test fixture shouldn't tank the request under test.
|
|
2333
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2334
|
+
logger.warn('testController.getSeededProducts failed; returning handler response unchanged', {
|
|
2335
|
+
tool: toolName,
|
|
2336
|
+
error: reason,
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2239
2339
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2340
|
+
// list_creatives
|
|
2341
|
+
else if (toolName === 'list_creatives' && testControllerBridge.getSeededCreatives) {
|
|
2342
|
+
try {
|
|
2343
|
+
const rawSeeded = await testControllerBridge.getSeededCreatives(bridgeCtx);
|
|
2344
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededCreatives)(rawSeeded, logger);
|
|
2345
|
+
if (seeded.length > 0) {
|
|
2346
|
+
const sc = formatted.structuredContent;
|
|
2347
|
+
if (sc && typeof sc === 'object') {
|
|
2348
|
+
const merged = (0, test_controller_bridge_1.mergeSeededCreativesIntoResponse)(sc, seeded);
|
|
2349
|
+
formatted = wrap(merged);
|
|
2350
|
+
stampBridge(formatted, 'getSeededCreatives', toolName, seeded.length);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
catch (err) {
|
|
2355
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2356
|
+
logger.warn('testController.getSeededCreatives failed; returning handler response unchanged', {
|
|
2357
|
+
tool: toolName,
|
|
2358
|
+
error: reason,
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
// get_media_buys
|
|
2363
|
+
else if (toolName === 'get_media_buys' && testControllerBridge.getSeededMediaBuys) {
|
|
2364
|
+
try {
|
|
2365
|
+
const rawSeeded = await testControllerBridge.getSeededMediaBuys(bridgeCtx);
|
|
2366
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededMediaBuys)(rawSeeded, logger);
|
|
2367
|
+
if (seeded.length > 0) {
|
|
2368
|
+
const sc = formatted.structuredContent;
|
|
2369
|
+
if (sc && typeof sc === 'object') {
|
|
2370
|
+
const merged = (0, test_controller_bridge_1.mergeSeededMediaBuysIntoResponse)(sc, seeded);
|
|
2371
|
+
formatted = wrap(merged);
|
|
2372
|
+
stampBridge(formatted, 'getSeededMediaBuys', toolName, seeded.length);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
catch (err) {
|
|
2377
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2378
|
+
logger.warn('testController.getSeededMediaBuys failed; returning handler response unchanged', {
|
|
2379
|
+
tool: toolName,
|
|
2380
|
+
error: reason,
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
// get_media_buy_delivery
|
|
2385
|
+
else if (toolName === 'get_media_buy_delivery' && testControllerBridge.getSeededMediaBuyDelivery) {
|
|
2386
|
+
try {
|
|
2387
|
+
const rawSeeded = await testControllerBridge.getSeededMediaBuyDelivery(bridgeCtx);
|
|
2388
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededMediaBuyDeliveries)(rawSeeded, logger);
|
|
2389
|
+
if (seeded.length > 0) {
|
|
2390
|
+
const sc = formatted.structuredContent;
|
|
2391
|
+
if (sc && typeof sc === 'object') {
|
|
2392
|
+
const merged = (0, test_controller_bridge_1.mergeSeededMediaBuyDeliveryIntoResponse)(sc, seeded);
|
|
2393
|
+
formatted = wrap(merged);
|
|
2394
|
+
stampBridge(formatted, 'getSeededMediaBuyDelivery', toolName, seeded.length);
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
catch (err) {
|
|
2399
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2400
|
+
logger.warn('testController.getSeededMediaBuyDelivery failed; returning handler response unchanged', {
|
|
2401
|
+
tool: toolName,
|
|
2402
|
+
error: reason,
|
|
2403
|
+
});
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
// list_accounts
|
|
2407
|
+
else if (toolName === 'list_accounts' && testControllerBridge.getSeededAccounts) {
|
|
2408
|
+
try {
|
|
2409
|
+
const rawSeeded = await testControllerBridge.getSeededAccounts(bridgeCtx);
|
|
2410
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededAccounts)(rawSeeded, logger);
|
|
2411
|
+
if (seeded.length > 0) {
|
|
2412
|
+
const sc = formatted.structuredContent;
|
|
2413
|
+
if (sc && typeof sc === 'object') {
|
|
2414
|
+
const merged = (0, test_controller_bridge_1.mergeSeededAccountsIntoResponse)(sc, seeded);
|
|
2415
|
+
formatted = wrap(merged);
|
|
2416
|
+
stampBridge(formatted, 'getSeededAccounts', toolName, seeded.length);
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
catch (err) {
|
|
2421
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2422
|
+
logger.warn('testController.getSeededAccounts failed; returning handler response unchanged', {
|
|
2423
|
+
tool: toolName,
|
|
2424
|
+
error: reason,
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
// get_account_financials (singleton — replace, not append)
|
|
2429
|
+
else if (toolName === 'get_account_financials' && testControllerBridge.getSeededAccountFinancials) {
|
|
2430
|
+
try {
|
|
2431
|
+
const rawSeeded = await testControllerBridge.getSeededAccountFinancials(bridgeCtx);
|
|
2432
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededAccountFinancials)(rawSeeded, logger);
|
|
2433
|
+
if (seeded.length > 0) {
|
|
2434
|
+
const sc = formatted.structuredContent;
|
|
2435
|
+
if (sc && typeof sc === 'object') {
|
|
2436
|
+
// Brand+operator `AccountReference` variants don't carry
|
|
2437
|
+
// `account_id` on the wire — the framework's resolved
|
|
2438
|
+
// account does, so prefer it for the match key.
|
|
2439
|
+
const resolvedAccountId = ctx.account && typeof ctx.account === 'object' && !Array.isArray(ctx.account)
|
|
2440
|
+
? ctx.account.account_id
|
|
2441
|
+
: undefined;
|
|
2442
|
+
const merged = (0, test_controller_bridge_1.replaceAccountFinancialsIfSeeded)(params, sc, seeded, typeof resolvedAccountId === 'string' && resolvedAccountId.length > 0
|
|
2443
|
+
? resolvedAccountId
|
|
2444
|
+
: undefined);
|
|
2445
|
+
if (merged !== sc) {
|
|
2446
|
+
formatted = wrap(merged);
|
|
2447
|
+
stampBridge(formatted, 'getSeededAccountFinancials', toolName, seeded.length);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
catch (err) {
|
|
2453
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2454
|
+
logger.warn('testController.getSeededAccountFinancials failed; returning handler response unchanged', {
|
|
2455
|
+
tool: toolName,
|
|
2456
|
+
error: reason,
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
// list_creative_formats
|
|
2461
|
+
else if (toolName === 'list_creative_formats' && testControllerBridge.getSeededCreativeFormats) {
|
|
2462
|
+
try {
|
|
2463
|
+
const rawSeeded = await testControllerBridge.getSeededCreativeFormats(bridgeCtx);
|
|
2464
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededCreativeFormats)(rawSeeded, logger);
|
|
2465
|
+
if (seeded.length > 0) {
|
|
2466
|
+
const sc = formatted.structuredContent;
|
|
2467
|
+
if (sc && typeof sc === 'object') {
|
|
2468
|
+
const merged = (0, test_controller_bridge_1.mergeSeededCreativeFormatsIntoResponse)(sc, seeded);
|
|
2469
|
+
formatted = wrap(merged);
|
|
2470
|
+
stampBridge(formatted, 'getSeededCreativeFormats', toolName, seeded.length);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
catch (err) {
|
|
2475
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2476
|
+
logger.warn('testController.getSeededCreativeFormats failed; returning handler response unchanged', {
|
|
2477
|
+
tool: toolName,
|
|
2478
|
+
error: reason,
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
// list_property_lists / get_property_list — one seeded fixture array
|
|
2483
|
+
// feeds both. List path: append-merge with seeded-wins on `list_id`
|
|
2484
|
+
// collision. Get path: pick by request.list_id and replace the
|
|
2485
|
+
// response's `list` field, preserving handler's resolved data
|
|
2486
|
+
// (`identifiers` / `pagination` / `resolved_at` / `cache_valid_until`
|
|
2487
|
+
// / `coverage_gaps` / `context` / `ext`).
|
|
2488
|
+
else if ((toolName === 'list_property_lists' || toolName === 'get_property_list') &&
|
|
2489
|
+
testControllerBridge.getSeededPropertyLists) {
|
|
2490
|
+
try {
|
|
2491
|
+
const rawSeeded = await testControllerBridge.getSeededPropertyLists(bridgeCtx);
|
|
2492
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededPropertyLists)(rawSeeded, logger);
|
|
2493
|
+
if (seeded.length > 0) {
|
|
2494
|
+
if (toolName === 'list_property_lists') {
|
|
2495
|
+
const sc = formatted.structuredContent;
|
|
2496
|
+
if (sc && typeof sc === 'object') {
|
|
2497
|
+
const merged = (0, test_controller_bridge_1.mergeSeededPropertyListsIntoResponse)(sc, seeded);
|
|
2498
|
+
formatted = wrap(merged);
|
|
2499
|
+
stampBridge(formatted, 'getSeededPropertyLists', toolName, seeded.length);
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
else {
|
|
2503
|
+
const sc = formatted.structuredContent;
|
|
2504
|
+
if (sc && typeof sc === 'object') {
|
|
2505
|
+
const merged = (0, test_controller_bridge_1.replacePropertyListIfSeeded)(params, sc, seeded);
|
|
2506
|
+
if (merged !== sc) {
|
|
2507
|
+
formatted = wrap(merged);
|
|
2508
|
+
stampBridge(formatted, 'getSeededPropertyLists', toolName, seeded.length);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
catch (err) {
|
|
2515
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2516
|
+
logger.warn('testController.getSeededPropertyLists failed; returning handler response unchanged', {
|
|
2517
|
+
tool: toolName,
|
|
2518
|
+
error: reason,
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
// list_collection_lists / get_collection_list — symmetric with
|
|
2523
|
+
// property lists.
|
|
2524
|
+
else if ((toolName === 'list_collection_lists' || toolName === 'get_collection_list') &&
|
|
2525
|
+
testControllerBridge.getSeededCollectionLists) {
|
|
2526
|
+
try {
|
|
2527
|
+
const rawSeeded = await testControllerBridge.getSeededCollectionLists(bridgeCtx);
|
|
2528
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededCollectionLists)(rawSeeded, logger);
|
|
2529
|
+
if (seeded.length > 0) {
|
|
2530
|
+
if (toolName === 'list_collection_lists') {
|
|
2531
|
+
const sc = formatted.structuredContent;
|
|
2532
|
+
if (sc && typeof sc === 'object') {
|
|
2533
|
+
const merged = (0, test_controller_bridge_1.mergeSeededCollectionListsIntoResponse)(sc, seeded);
|
|
2534
|
+
formatted = wrap(merged);
|
|
2535
|
+
stampBridge(formatted, 'getSeededCollectionLists', toolName, seeded.length);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
else {
|
|
2539
|
+
const sc = formatted.structuredContent;
|
|
2540
|
+
if (sc && typeof sc === 'object') {
|
|
2541
|
+
const merged = (0, test_controller_bridge_1.replaceCollectionListIfSeeded)(params, sc, seeded);
|
|
2542
|
+
if (merged !== sc) {
|
|
2543
|
+
formatted = wrap(merged);
|
|
2544
|
+
stampBridge(formatted, 'getSeededCollectionLists', toolName, seeded.length);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
catch (err) {
|
|
2551
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2552
|
+
logger.warn('testController.getSeededCollectionLists failed; returning handler response unchanged', {
|
|
2553
|
+
tool: toolName,
|
|
2554
|
+
error: reason,
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
// get_signals — append-merge by signal_id. Works uniformly across
|
|
2559
|
+
// signal-marketplace and signal-owned specialisms (one bridge, both
|
|
2560
|
+
// dispatched on the same tool; the per-signal `signal_type` field
|
|
2561
|
+
// is the marketplace-vs-owned discriminator).
|
|
2562
|
+
else if (toolName === 'get_signals' && testControllerBridge.getSeededSignals) {
|
|
2563
|
+
try {
|
|
2564
|
+
const rawSeeded = await testControllerBridge.getSeededSignals(bridgeCtx);
|
|
2565
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededSignals)(rawSeeded, logger);
|
|
2566
|
+
if (seeded.length > 0) {
|
|
2567
|
+
const sc = formatted.structuredContent;
|
|
2568
|
+
if (sc && typeof sc === 'object') {
|
|
2569
|
+
const merged = (0, test_controller_bridge_1.mergeSeededSignalsIntoResponse)(sc, seeded);
|
|
2570
|
+
formatted = wrap(merged);
|
|
2571
|
+
stampBridge(formatted, 'getSeededSignals', toolName, seeded.length);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
catch (err) {
|
|
2576
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2577
|
+
logger.warn('testController.getSeededSignals failed; returning handler response unchanged', {
|
|
2578
|
+
tool: toolName,
|
|
2579
|
+
error: reason,
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
// get_creative_delivery — append-merge by creative_id;
|
|
2584
|
+
// `pagination.total` (when set by the handler) updates by the
|
|
2585
|
+
// count of new non-colliding seeded entries. No aggregated-totals
|
|
2586
|
+
// recomputation — this response has no top-level totals envelope.
|
|
2587
|
+
else if (toolName === 'get_creative_delivery' && testControllerBridge.getSeededCreativeDelivery) {
|
|
2588
|
+
try {
|
|
2589
|
+
const rawSeeded = await testControllerBridge.getSeededCreativeDelivery(bridgeCtx);
|
|
2590
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededCreativeDelivery)(rawSeeded, logger);
|
|
2591
|
+
if (seeded.length > 0) {
|
|
2592
|
+
const sc = formatted.structuredContent;
|
|
2593
|
+
if (sc && typeof sc === 'object') {
|
|
2594
|
+
const merged = (0, test_controller_bridge_1.mergeSeededCreativeDeliveryIntoResponse)(sc, seeded);
|
|
2595
|
+
formatted = wrap(merged);
|
|
2596
|
+
stampBridge(formatted, 'getSeededCreativeDelivery', toolName, seeded.length);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
catch (err) {
|
|
2601
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2602
|
+
logger.warn('testController.getSeededCreativeDelivery failed; returning handler response unchanged', {
|
|
2603
|
+
tool: toolName,
|
|
2604
|
+
error: reason,
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
// get_creative_features — `oneOf` envelope. Success arm: merge
|
|
2609
|
+
// seeded `CreativeFeatureResult[]` into `results` (dedup by
|
|
2610
|
+
// `feature_id`, seeded wins). Error arm: no-op (the helper
|
|
2611
|
+
// discriminates by presence of `results: []`).
|
|
2612
|
+
else if (toolName === 'get_creative_features' && testControllerBridge.getSeededCreativeFeatures) {
|
|
2613
|
+
try {
|
|
2614
|
+
const rawSeeded = await testControllerBridge.getSeededCreativeFeatures(bridgeCtx);
|
|
2615
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededCreativeFeatures)(rawSeeded, logger);
|
|
2616
|
+
if (seeded.length > 0) {
|
|
2617
|
+
const sc = formatted.structuredContent;
|
|
2618
|
+
if (sc && typeof sc === 'object') {
|
|
2619
|
+
const merged = (0, test_controller_bridge_1.mergeSeededCreativeFeaturesIntoResponse)(sc, seeded);
|
|
2620
|
+
if (merged !== sc) {
|
|
2621
|
+
formatted = wrap(merged);
|
|
2622
|
+
stampBridge(formatted, 'getSeededCreativeFeatures', toolName, seeded.length);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
catch (err) {
|
|
2628
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2629
|
+
logger.warn('testController.getSeededCreativeFeatures failed; returning handler response unchanged', {
|
|
2630
|
+
tool: toolName,
|
|
2631
|
+
error: reason,
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
// get_brand_identity — singleton replace keyed by `brand_id`.
|
|
2636
|
+
// Seeded fixture is authoritative on the GetBrandIdentitySuccess
|
|
2637
|
+
// body; handler's `context` / `ext` round-trip. The response is
|
|
2638
|
+
// a union (success | error) — the dispatcher already gated on
|
|
2639
|
+
// `!isErrorResponse`, so we narrow defensively when reading.
|
|
2640
|
+
else if (toolName === 'get_brand_identity' && testControllerBridge.getSeededBrandIdentity) {
|
|
2641
|
+
try {
|
|
2642
|
+
const rawSeeded = await testControllerBridge.getSeededBrandIdentity(bridgeCtx);
|
|
2643
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededBrandIdentity)(rawSeeded, logger);
|
|
2644
|
+
if (seeded.length > 0) {
|
|
2645
|
+
const sc = formatted.structuredContent;
|
|
2646
|
+
if (sc && typeof sc === 'object') {
|
|
2647
|
+
const merged = (0, test_controller_bridge_1.replaceBrandIdentityIfSeeded)(params, sc, seeded);
|
|
2648
|
+
if (merged !== sc) {
|
|
2649
|
+
formatted = wrap(merged);
|
|
2650
|
+
stampBridge(formatted, 'getSeededBrandIdentity', toolName, seeded.length);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
catch (err) {
|
|
2656
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2657
|
+
logger.warn('testController.getSeededBrandIdentity failed; returning handler response unchanged', {
|
|
2658
|
+
tool: toolName,
|
|
2659
|
+
error: reason,
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
// get_rights — append-merge keyed by `rights_id`. Discovery /
|
|
2664
|
+
// search tool (NL `query`); the response carries `rights[]`.
|
|
2665
|
+
// Drops to a no-op on the error arm of the response union.
|
|
2666
|
+
else if (toolName === 'get_rights' && testControllerBridge.getSeededRights) {
|
|
2667
|
+
try {
|
|
2668
|
+
const rawSeeded = await testControllerBridge.getSeededRights(bridgeCtx);
|
|
2669
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededRights)(rawSeeded, logger);
|
|
2670
|
+
if (seeded.length > 0) {
|
|
2671
|
+
const sc = formatted.structuredContent;
|
|
2672
|
+
if (sc && typeof sc === 'object') {
|
|
2673
|
+
const merged = (0, test_controller_bridge_1.mergeSeededRightsIntoResponse)(sc, seeded);
|
|
2674
|
+
if (merged !== sc) {
|
|
2675
|
+
formatted = wrap(merged);
|
|
2676
|
+
stampBridge(formatted, 'getSeededRights', toolName, seeded.length);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
catch (err) {
|
|
2682
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2683
|
+
logger.warn('testController.getSeededRights failed; returning handler response unchanged', {
|
|
2684
|
+
tool: toolName,
|
|
2685
|
+
error: reason,
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
// si_get_offering — singleton replace keyed by `offering_id`.
|
|
2690
|
+
// Stateless catalog lookup; the response's `offering_token` is
|
|
2691
|
+
// produced for a future session but the lookup itself does not
|
|
2692
|
+
// consume one. Handler's `context` / `ext` round-trip.
|
|
2693
|
+
else if (toolName === 'si_get_offering' && testControllerBridge.getSeededSiOffering) {
|
|
2694
|
+
try {
|
|
2695
|
+
const rawSeeded = await testControllerBridge.getSeededSiOffering(bridgeCtx);
|
|
2696
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededSiOffering)(rawSeeded, logger);
|
|
2697
|
+
if (seeded.length > 0) {
|
|
2698
|
+
const sc = formatted.structuredContent;
|
|
2699
|
+
if (sc && typeof sc === 'object') {
|
|
2700
|
+
const merged = (0, test_controller_bridge_1.replaceSiOfferingIfSeeded)(params, sc, seeded);
|
|
2701
|
+
if (merged !== sc) {
|
|
2702
|
+
formatted = wrap(merged);
|
|
2703
|
+
stampBridge(formatted, 'getSeededSiOffering', toolName, seeded.length);
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
catch (err) {
|
|
2709
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2710
|
+
logger.warn('testController.getSeededSiOffering failed; returning handler response unchanged', {
|
|
2711
|
+
tool: toolName,
|
|
2712
|
+
error: reason,
|
|
2713
|
+
});
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
// list_content_standards / get_content_standards — list returns
|
|
2717
|
+
// `{ standards: ContentStandards[] }` (success arm of a union; the
|
|
2718
|
+
// error arm is gated out upstream). Get returns `ContentStandards`
|
|
2719
|
+
// directly (success arm); replace the ContentStandards body and
|
|
2720
|
+
// round-trip handler's `context` / `ext` (both are framework-
|
|
2721
|
+
// managed envelope fields per the spec).
|
|
2722
|
+
else if ((toolName === 'list_content_standards' || toolName === 'get_content_standards') &&
|
|
2723
|
+
testControllerBridge.getSeededContentStandards) {
|
|
2724
|
+
try {
|
|
2725
|
+
const rawSeeded = await testControllerBridge.getSeededContentStandards(bridgeCtx);
|
|
2726
|
+
const seeded = (0, test_controller_bridge_1.filterValidSeededContentStandards)(rawSeeded, logger);
|
|
2727
|
+
if (seeded.length > 0) {
|
|
2728
|
+
if (toolName === 'list_content_standards') {
|
|
2729
|
+
const sc = formatted.structuredContent;
|
|
2730
|
+
if (sc && typeof sc === 'object') {
|
|
2731
|
+
const merged = (0, test_controller_bridge_1.mergeSeededContentStandardsIntoResponse)(sc, seeded);
|
|
2732
|
+
if (merged !== sc) {
|
|
2733
|
+
formatted = wrap(merged);
|
|
2734
|
+
stampBridge(formatted, 'getSeededContentStandards', toolName, seeded.length);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
else {
|
|
2739
|
+
const sc = formatted.structuredContent;
|
|
2740
|
+
if (sc && typeof sc === 'object') {
|
|
2741
|
+
const merged = (0, test_controller_bridge_1.replaceContentStandardsIfSeeded)(params, sc, seeded);
|
|
2742
|
+
if (merged !== sc) {
|
|
2743
|
+
formatted = wrap(merged);
|
|
2744
|
+
stampBridge(formatted, 'getSeededContentStandards', toolName, seeded.length);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
catch (err) {
|
|
2751
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2752
|
+
logger.warn('testController.getSeededContentStandards failed; returning handler response unchanged', {
|
|
2753
|
+
tool: toolName,
|
|
2754
|
+
error: reason,
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2249
2757
|
}
|
|
2250
2758
|
}
|
|
2251
2759
|
// --- Response schema validation (opt-in) ---
|