@adcp/sdk 13.0.0-rc.4 → 13.0.0-rc.6
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/README.md +7 -1
- package/bin/adcp.js +7 -1
- package/dist/lib/adapters/property-list-adapter.d.mts +5 -1
- package/dist/lib/adapters/property-list-adapter.d.ts +5 -1
- package/dist/lib/adapters/property-list-adapter.d.ts.map +1 -1
- package/dist/lib/adapters/property-list-adapter.js +7 -7
- package/dist/lib/adapters/property-list-adapter.js.map +1 -1
- package/dist/lib/adapters/property-list-adapter.mjs +7 -7
- package/dist/lib/adapters/property-list-adapter.mjs.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.d.mts +10 -3
- package/dist/lib/auth/oauth/CLIFlowHandler.d.ts +10 -3
- package/dist/lib/auth/oauth/CLIFlowHandler.d.ts.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.js +40 -4
- package/dist/lib/auth/oauth/CLIFlowHandler.js.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs +40 -4
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/index.d.mts +1 -1
- package/dist/lib/auth/oauth/index.d.ts +1 -1
- package/dist/lib/auth/oauth/index.d.ts.map +1 -1
- package/dist/lib/auth/oauth/index.js +2 -0
- package/dist/lib/auth/oauth/index.js.map +1 -1
- package/dist/lib/auth/oauth/index.mjs +2 -0
- package/dist/lib/auth/oauth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/web-flow.d.mts +12 -0
- package/dist/lib/auth/oauth/web-flow.d.ts +12 -0
- package/dist/lib/auth/oauth/web-flow.d.ts.map +1 -1
- package/dist/lib/auth/oauth/web-flow.js +20 -2
- package/dist/lib/auth/oauth/web-flow.js.map +1 -1
- package/dist/lib/auth/oauth/web-flow.mjs +19 -2
- package/dist/lib/auth/oauth/web-flow.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +27 -1
- package/dist/lib/core/SingleAgentClient.d.ts +27 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +46 -18
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +46 -18
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/net/address-guards.d.ts.map +1 -1
- package/dist/lib/net/address-guards.js +4 -0
- package/dist/lib/net/address-guards.js.map +1 -1
- package/dist/lib/net/address-guards.mjs +4 -0
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/protocols/a2a.js +2 -2
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/a2a.mjs +2 -2
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +28 -7
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +28 -7
- 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 +18 -5
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +18 -5
- 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 -12
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +2 -12
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.js +2 -2
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/protocols/mcp.mjs +2 -2
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/registry/types.generated.d.mts +554 -15
- package/dist/lib/registry/types.generated.d.ts +554 -15
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/decisioning/account.d.mts +6 -4
- package/dist/lib/server/decisioning/account.d.ts +6 -4
- package/dist/lib/server/decisioning/account.d.ts.map +1 -1
- package/dist/lib/server/decisioning/account.js.map +1 -1
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.d.mts +6 -5
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +6 -5
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +4 -2
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +4 -2
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.d.mts +26 -14
- package/dist/lib/server/decisioning/tenant-store.d.ts +26 -14
- package/dist/lib/server/decisioning/tenant-store.d.ts.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.js +6 -1
- package/dist/lib/server/decisioning/tenant-store.js.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.mjs +6 -1
- package/dist/lib/server/decisioning/tenant-store.mjs.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.d.mts +2 -0
- package/dist/lib/server/pin-and-bind-fetch.d.ts +2 -0
- package/dist/lib/server/pin-and-bind-fetch.d.ts.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.js +2 -0
- package/dist/lib/server/pin-and-bind-fetch.js.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.mjs +2 -0
- package/dist/lib/server/pin-and-bind-fetch.mjs.map +1 -1
- package/dist/lib/server/structured-serialize.d.ts.map +1 -1
- package/dist/lib/server/structured-serialize.js +10 -2
- package/dist/lib/server/structured-serialize.js.map +1 -1
- package/dist/lib/server/structured-serialize.mjs +10 -2
- package/dist/lib/server/structured-serialize.mjs.map +1 -1
- package/dist/lib/server/webhook-emitter.d.ts.map +1 -1
- package/dist/lib/server/webhook-emitter.js +4 -2
- package/dist/lib/server/webhook-emitter.js.map +1 -1
- package/dist/lib/server/webhook-emitter.mjs +4 -2
- package/dist/lib/server/webhook-emitter.mjs.map +1 -1
- package/dist/lib/signing/middleware.d.mts +2 -1
- package/dist/lib/signing/middleware.d.ts +2 -1
- package/dist/lib/signing/middleware.d.ts.map +1 -1
- package/dist/lib/signing/middleware.js +26 -4
- package/dist/lib/signing/middleware.js.map +1 -1
- package/dist/lib/signing/middleware.mjs +26 -4
- package/dist/lib/signing/middleware.mjs.map +1 -1
- package/dist/lib/signing/webhook-verifier.d.ts.map +1 -1
- package/dist/lib/signing/webhook-verifier.js +4 -2
- package/dist/lib/signing/webhook-verifier.js.map +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +4 -2
- package/dist/lib/signing/webhook-verifier.mjs.map +1 -1
- package/dist/lib/testing/agent-tester.d.ts.map +1 -1
- package/dist/lib/testing/agent-tester.js +46 -1
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/agent-tester.mjs +46 -1
- package/dist/lib/testing/agent-tester.mjs.map +1 -1
- package/dist/lib/types/asset-instances.d.mts +8 -6
- package/dist/lib/types/asset-instances.d.ts +8 -6
- package/dist/lib/types/asset-instances.d.ts.map +1 -1
- package/dist/lib/types/asset-instances.js.map +1 -1
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/utils/capabilities.d.mts +2 -0
- package/dist/lib/utils/capabilities.d.ts +2 -0
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +2 -1
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/capabilities.mjs +2 -1
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/probe-policy.d.mts +0 -16
- package/dist/lib/utils/probe-policy.d.ts +0 -16
- package/dist/lib/utils/probe-policy.d.ts.map +1 -1
- package/dist/lib/utils/probe-policy.js +15 -1
- package/dist/lib/utils/probe-policy.js.map +1 -1
- package/dist/lib/utils/probe-policy.mjs +15 -1
- package/dist/lib/utils/probe-policy.mjs.map +1 -1
- package/dist/lib/utils/redact-args.d.mts +36 -0
- package/dist/lib/utils/redact-args.d.ts +37 -0
- package/dist/lib/utils/redact-args.d.ts.map +1 -0
- package/dist/lib/utils/redact-args.js +46 -0
- package/dist/lib/utils/redact-args.js.map +1 -0
- package/dist/lib/utils/redact-args.mjs +22 -0
- package/dist/lib/utils/redact-args.mjs.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.js +1 -5
- package/dist/lib/v2/projection/creative-delivery.js.map +1 -1
- package/dist/lib/v2/projection/creative-delivery.mjs +1 -5
- package/dist/lib/v2/projection/creative-delivery.mjs.map +1 -1
- package/dist/lib/validation/index.d.mts +15 -1
- package/dist/lib/validation/index.d.ts +15 -1
- package/dist/lib/validation/index.d.ts.map +1 -1
- package/dist/lib/validation/index.js +4 -8
- package/dist/lib/validation/index.js.map +1 -1
- package/dist/lib/validation/index.mjs +4 -8
- package/dist/lib/validation/index.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/examples/hello_seller_adapter_multi_tenant.ts +7 -0
- package/package.json +3 -2
- package/skills/build-holdco-agent/SKILL.md +19 -0
- package/skills/cross-cutting.md +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/validation/index.ts"],"sourcesContent":["/**\n * AdCP Validation Utilities\n * Functions for validating URLs, responses, and schemas\n */\n\nexport { validateRequest, validateResponse, formatIssues } from './schema-validator';\nexport type { ValidationIssue, ValidationOutcome } from './schema-validator';\nexport { buildValidationError, buildAdcpValidationErrorPayload } from './schema-errors';\nexport type { ValidationErrorDetails, AdcpValidationErrorDetails } from './schema-errors';\nexport {\n getValidator,\n listValidatorKeys,\n resolveBundleKey,\n hasSchemaBundle,\n registerExternalSchemaRoot,\n unregisterExternalSchemaRoot,\n withExternalSchemaRoot,\n} from './schema-loader';\nexport type { Direction, ResponseVariant } from './schema-loader';\nexport { validateOutgoingRequest, validateIncomingResponse, resolveValidationModes } from './client-hooks';\nexport type { ValidationMode, ValidationHookConfig } from './client-hooks';\n\n/**\n * Get expected response schema type for a given tool\n */\nexport function getExpectedSchema(toolName: string): string {\n switch (toolName) {\n case 'get_products':\n return 'products';\n case 'list_creative_formats':\n return 'formats';\n case 'create_media_buy':\n return 'media_buy';\n case 'manage_creative_assets':\n return 'creative_management';\n case 'sync_creatives':\n return 'sync_response';\n case 'list_creatives':\n return 'creative_list';\n case 'add_creative_assets':\n return 'creative_upload';\n default:\n return 'generic';\n }\n}\n\n/**\n * Validate agent URL to prevent SSRF attacks\n */\nexport function validateAgentUrl(url: string): void {\n // Handle edge cases first\n if (!url || typeof url !== 'string') {\n throw new Error('Agent URL is required and must be a string');\n }\n\n if (url.trim() === '') {\n throw new Error('Agent URL cannot be empty');\n }\n\n // Ensure reasonable URL length\n if (url.length > 2048) {\n throw new Error('Agent URL is too long (max 2048 characters)');\n }\n\n try {\n const parsedUrl = new URL(url.trim());\n\n // Only allow HTTP/HTTPS protocols\n if (!['http:', 'https:'].includes(parsedUrl.protocol)) {\n throw new Error(`Protocol '${parsedUrl.protocol}' not allowed (only HTTP/HTTPS)`);\n }\n\n // Ensure URL has a valid hostname\n if (!parsedUrl.hostname) {\n throw new Error('URL must have a valid hostname');\n }\n\n // Block private IP ranges and localhost in production\n if (process.env.NODE_ENV === 'production') {\n const hostname = parsedUrl.hostname.toLowerCase();\n if (\n ['localhost', '127.0.0.1', '0.0.0.0', '::1'].includes(hostname) ||\n hostname.startsWith('192.168.') ||\n hostname.startsWith('10.') ||\n hostname.match(/^172\\.(1[6-9]|2[0-9]|3[01])\\./)\n ) {\n throw new Error('Private network access not allowed in production');\n }\n\n // Block metadata endpoints\n if (hostname === '169.254.169.254' || hostname === 'metadata.google.internal') {\n throw new Error('Metadata endpoint access not allowed');\n }\n }\n } catch (e) {\n if (e instanceof Error) {\n // Don't double-wrap our own errors\n if (e.message.includes('not allowed') || e.message.includes('required') || e.message.includes('cannot be')) {\n throw e;\n }\n // Only wrap URL constructor errors with more context\n throw new Error(`Invalid agent URL format: ${e.message}`);\n }\n throw new Error('Invalid agent URL format');\n }\n}\n\n/**\n * Validate AdCP response format and content\n */\nexport function validateAdCPResponse(response: any, expectedSchema: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Basic response structure validation\n if (!response || typeof response !== 'object') {\n errors.push('Response is not a valid object');\n return { valid: false, errors };\n }\n\n // Check for AdCP-specific fields based on expected schema\n if (expectedSchema === 'products') {\n if (!Array.isArray(response.products)) {\n errors.push('Missing or invalid products array');\n } else {\n response.products.forEach((product: any, index: number) => {\n if (!product.id) errors.push(`Product ${index}: Missing id field`);\n if (!product.name) errors.push(`Product ${index}: Missing name field`);\n if (!product.pricing_model) errors.push(`Product ${index}: Missing pricing_model field`);\n });\n }\n }\n\n if (expectedSchema === 'formats') {\n if (!Array.isArray(response.formats) && !Array.isArray(response.creative_formats)) {\n errors.push('Missing formats/creative_formats array');\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Handle AdCP response with comprehensive error checking\n */\nexport async function handleAdCPResponse(\n response: Response,\n expectedSchema: string,\n agentName: string\n): Promise<{ success: boolean; data?: any; error?: string; warnings?: string[] }> {\n const warnings: string[] = [];\n\n // Check AdCP-specific response headers\n const adcpVersion = response.headers.get('AdCP-Version');\n if (!adcpVersion) {\n warnings.push('Missing AdCP-Version header in response');\n } else if (adcpVersion !== '1.0') {\n warnings.push(`Unexpected AdCP version: ${adcpVersion} (expected 1.0)`);\n }\n\n const responseId = response.headers.get('AdCP-Response-ID');\n if (!responseId) {\n warnings.push('Missing AdCP-Response-ID header in response');\n }\n\n const contentType = response.headers.get('Content-Type');\n if (!contentType?.includes('application/vnd.adcp+json') && !contentType?.includes('application/json')) {\n warnings.push(`Unexpected content type: ${contentType} (expected application/vnd.adcp+json)`);\n }\n\n // Parse response body\n let responseData: unknown;\n try {\n const textResponse = await response.text();\n if (!textResponse.trim()) {\n return {\n success: false,\n error: `Empty response from ${agentName}`,\n warnings,\n };\n }\n\n responseData = JSON.parse(textResponse);\n } catch (parseError) {\n return {\n success: false,\n error: `Invalid JSON response from ${agentName}: ${\n parseError instanceof Error ? parseError.message : 'Parse error'\n }`,\n warnings,\n };\n }\n\n // Check for JSON-RPC error response\n const parsed = responseData as Record<string, unknown>;\n if (parsed?.error || (parsed?.jsonrpc && parsed?.id !== undefined && !parsed?.result)) {\n const errorObj = parsed.error as Record<string, unknown> | undefined;\n return {\n success: false,\n error: `Agent returned JSON-RPC error: ${errorObj?.message || JSON.stringify(errorObj)}`,\n warnings,\n data: responseData, // Include raw data for debugging\n };\n }\n\n // Validate response schema\n const validation = validateAdCPResponse(responseData, expectedSchema);\n if (!validation.valid) {\n return {\n success: false,\n error: `Schema validation failed for ${agentName}: ${validation.errors.join(', ')}`,\n warnings,\n data: responseData, // Include raw data for debugging\n };\n }\n\n return {\n success: true,\n data: responseData,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/validation/index.ts"],"sourcesContent":["/**\n * AdCP Validation Utilities\n * Functions for validating URLs, responses, and schemas\n */\n\nimport { classifyProbeUrl } from '../utils/probe-policy';\n\nexport { validateRequest, validateResponse, formatIssues } from './schema-validator';\nexport type { ValidationIssue, ValidationOutcome } from './schema-validator';\nexport { buildValidationError, buildAdcpValidationErrorPayload } from './schema-errors';\nexport type { ValidationErrorDetails, AdcpValidationErrorDetails } from './schema-errors';\nexport {\n getValidator,\n listValidatorKeys,\n resolveBundleKey,\n hasSchemaBundle,\n registerExternalSchemaRoot,\n unregisterExternalSchemaRoot,\n withExternalSchemaRoot,\n} from './schema-loader';\nexport type { Direction, ResponseVariant } from './schema-loader';\nexport { validateOutgoingRequest, validateIncomingResponse, resolveValidationModes } from './client-hooks';\nexport type { ValidationMode, ValidationHookConfig } from './client-hooks';\n\n/**\n * Get expected response schema type for a given tool\n */\nexport function getExpectedSchema(toolName: string): string {\n switch (toolName) {\n case 'get_products':\n return 'products';\n case 'list_creative_formats':\n return 'formats';\n case 'create_media_buy':\n return 'media_buy';\n case 'manage_creative_assets':\n return 'creative_management';\n case 'sync_creatives':\n return 'sync_response';\n case 'list_creatives':\n return 'creative_list';\n case 'add_creative_assets':\n return 'creative_upload';\n default:\n return 'generic';\n }\n}\n\n/**\n * Validate an agent URL before it reaches a protocol transport.\n *\n * The private/metadata decision delegates to {@link classifyProbeUrl} so agent\n * URLs and discovery probes share one policy: loopback allowed (a dev loop\n * already requires on-host access to abuse), RFC-1918/link-local/ULA refused\n * unless the operator sets `ADCP_ALLOW_INTERNAL_PROBES=1`, cloud metadata\n * refused even then. Deliberately NOT keyed on `NODE_ENV` — a staging image\n * running `NODE_ENV=test` must not inherit a looser SSRF posture than\n * production.\n *\n * SCOPE: this is a synchronous check on the URL's literal scheme and hostname.\n * It does NOT resolve DNS, so it cannot stop a public hostname that resolves —\n * or rebinds — to a private address. That gap is the same TOCTOU one tracked\n * for `classifyProbeUrl`; DNS-level defense requires resolving and pinning at\n * connect time, as `ssrfSafeFetch` and `createPinAndBindFetch` do.\n */\nexport function validateAgentUrl(url: string): void {\n // Handle edge cases first\n if (!url || typeof url !== 'string') {\n throw new Error('Agent URL is required and must be a string');\n }\n\n if (url.trim() === '') {\n throw new Error('Agent URL cannot be empty');\n }\n\n // Ensure reasonable URL length\n if (url.length > 2048) {\n throw new Error('Agent URL is too long (max 2048 characters)');\n }\n\n try {\n const parsedUrl = new URL(url.trim());\n\n // Only allow HTTP/HTTPS protocols\n if (!['http:', 'https:'].includes(parsedUrl.protocol)) {\n throw new Error(`Protocol '${parsedUrl.protocol}' not allowed (only HTTP/HTTPS)`);\n }\n\n // Ensure URL has a valid hostname\n if (!parsedUrl.hostname) {\n throw new Error('URL must have a valid hostname');\n }\n\n // Metadata and private-network destinations. One policy, shared with\n // discovery probes — see `classifyProbeUrl` for the range table and the\n // single `ADCP_ALLOW_INTERNAL_PROBES` opt-out. It does real CIDR matching\n // over `net/address-guards`, so bracketed `[::1]`, `127.0.0.2`, CGNAT, and\n // IPv4-mapped IPv6 are all classified correctly and registered names like\n // `10.example.com` are not false positives.\n const probe = classifyProbeUrl(parsedUrl.toString());\n if (!probe.allowed) {\n throw new Error(`${probe.reason} (agent URL not allowed)`);\n }\n } catch (e) {\n if (e instanceof Error) {\n // Don't double-wrap our own errors\n if (e.message.includes('not allowed') || e.message.includes('required') || e.message.includes('cannot be')) {\n throw e;\n }\n // Only wrap URL constructor errors with more context\n throw new Error(`Invalid agent URL format: ${e.message}`);\n }\n throw new Error('Invalid agent URL format');\n }\n}\n\n/**\n * Validate AdCP response format and content\n */\nexport function validateAdCPResponse(response: any, expectedSchema: string): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Basic response structure validation\n if (!response || typeof response !== 'object') {\n errors.push('Response is not a valid object');\n return { valid: false, errors };\n }\n\n // Check for AdCP-specific fields based on expected schema\n if (expectedSchema === 'products') {\n if (!Array.isArray(response.products)) {\n errors.push('Missing or invalid products array');\n } else {\n response.products.forEach((product: any, index: number) => {\n if (!product.id) errors.push(`Product ${index}: Missing id field`);\n if (!product.name) errors.push(`Product ${index}: Missing name field`);\n if (!product.pricing_model) errors.push(`Product ${index}: Missing pricing_model field`);\n });\n }\n }\n\n if (expectedSchema === 'formats') {\n if (!Array.isArray(response.formats) && !Array.isArray(response.creative_formats)) {\n errors.push('Missing formats/creative_formats array');\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Handle AdCP response with comprehensive error checking\n */\nexport async function handleAdCPResponse(\n response: Response,\n expectedSchema: string,\n agentName: string\n): Promise<{ success: boolean; data?: any; error?: string; warnings?: string[] }> {\n const warnings: string[] = [];\n\n // Check AdCP-specific response headers\n const adcpVersion = response.headers.get('AdCP-Version');\n if (!adcpVersion) {\n warnings.push('Missing AdCP-Version header in response');\n } else if (adcpVersion !== '1.0') {\n warnings.push(`Unexpected AdCP version: ${adcpVersion} (expected 1.0)`);\n }\n\n const responseId = response.headers.get('AdCP-Response-ID');\n if (!responseId) {\n warnings.push('Missing AdCP-Response-ID header in response');\n }\n\n const contentType = response.headers.get('Content-Type');\n if (!contentType?.includes('application/vnd.adcp+json') && !contentType?.includes('application/json')) {\n warnings.push(`Unexpected content type: ${contentType} (expected application/vnd.adcp+json)`);\n }\n\n // Parse response body\n let responseData: unknown;\n try {\n const textResponse = await response.text();\n if (!textResponse.trim()) {\n return {\n success: false,\n error: `Empty response from ${agentName}`,\n warnings,\n };\n }\n\n responseData = JSON.parse(textResponse);\n } catch (parseError) {\n return {\n success: false,\n error: `Invalid JSON response from ${agentName}: ${\n parseError instanceof Error ? parseError.message : 'Parse error'\n }`,\n warnings,\n };\n }\n\n // Check for JSON-RPC error response\n const parsed = responseData as Record<string, unknown>;\n if (parsed?.error || (parsed?.jsonrpc && parsed?.id !== undefined && !parsed?.result)) {\n const errorObj = parsed.error as Record<string, unknown> | undefined;\n return {\n success: false,\n error: `Agent returned JSON-RPC error: ${errorObj?.message || JSON.stringify(errorObj)}`,\n warnings,\n data: responseData, // Include raw data for debugging\n };\n }\n\n // Validate response schema\n const validation = validateAdCPResponse(responseData, expectedSchema);\n if (!validation.valid) {\n return {\n success: false,\n error: `Schema validation failed for ${agentName}: ${validation.errors.join(', ')}`,\n warnings,\n data: responseData, // Include raw data for debugging\n };\n }\n\n return {\n success: true,\n data: responseData,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/lib/version.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "13.0.0-rc.
|
|
4
|
+
export declare const LIBRARY_VERSION = "13.0.0-rc.6";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "13.0.0-rc.
|
|
36
|
+
readonly library: "13.0.0-rc.6";
|
|
37
37
|
readonly adcp: "3.1.8";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0-beta.1", "3.0-beta.1", "3.0-beta", "3.0.0-beta.3", "3.0-beta.3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8"];
|
|
39
|
-
readonly generatedAt: "2026-
|
|
39
|
+
readonly generatedAt: "2026-08-03T12:04:02.372Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "13.0.0-rc.
|
|
4
|
+
export declare const LIBRARY_VERSION = "13.0.0-rc.6";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "13.0.0-rc.
|
|
36
|
+
readonly library: "13.0.0-rc.6";
|
|
37
37
|
readonly adcp: "3.1.8";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0-beta.1", "3.0-beta.1", "3.0-beta", "3.0.0-beta.3", "3.0-beta.3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8"];
|
|
39
|
-
readonly generatedAt: "2026-
|
|
39
|
+
readonly generatedAt: "2026-08-03T12:04:02.372Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.js
CHANGED
|
@@ -31,7 +31,7 @@ __export(version_exports, {
|
|
|
31
31
|
toReleasePrecisionVersion: () => toReleasePrecisionVersion
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(version_exports);
|
|
34
|
-
const LIBRARY_VERSION = "13.0.0-rc.
|
|
34
|
+
const LIBRARY_VERSION = "13.0.0-rc.6";
|
|
35
35
|
const ADCP_VERSION = "3.1.8";
|
|
36
36
|
const ADCP_MAJOR_VERSION = 3;
|
|
37
37
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -69,10 +69,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
69
69
|
"3.1.8"
|
|
70
70
|
];
|
|
71
71
|
const VERSION_INFO = {
|
|
72
|
-
library: "13.0.0-rc.
|
|
72
|
+
library: "13.0.0-rc.6",
|
|
73
73
|
adcp: "3.1.8",
|
|
74
74
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
75
|
-
generatedAt: "2026-
|
|
75
|
+
generatedAt: "2026-08-03T12:04:02.372Z"
|
|
76
76
|
};
|
|
77
77
|
function getAdcpVersion() {
|
|
78
78
|
return ADCP_VERSION;
|
package/dist/lib/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.
|
|
1
|
+
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.6';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.8';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.6',\n adcp: '3.1.8',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-03T12:04:02.372Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
|
package/dist/lib/version.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const LIBRARY_VERSION = "13.0.0-rc.
|
|
1
|
+
const LIBRARY_VERSION = "13.0.0-rc.6";
|
|
2
2
|
const ADCP_VERSION = "3.1.8";
|
|
3
3
|
const ADCP_MAJOR_VERSION = 3;
|
|
4
4
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -36,10 +36,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
36
36
|
"3.1.8"
|
|
37
37
|
];
|
|
38
38
|
const VERSION_INFO = {
|
|
39
|
-
library: "13.0.0-rc.
|
|
39
|
+
library: "13.0.0-rc.6",
|
|
40
40
|
adcp: "3.1.8",
|
|
41
41
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
42
|
-
generatedAt: "2026-
|
|
42
|
+
generatedAt: "2026-08-03T12:04:02.372Z"
|
|
43
43
|
};
|
|
44
44
|
function getAdcpVersion() {
|
|
45
45
|
return ADCP_VERSION;
|
package/dist/lib/version.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.
|
|
1
|
+
{"version":3,"sources":["../../src/lib/version.ts"],"sourcesContent":["// Generated version information\n// This file is auto-generated by sync-version.ts\n\n/**\n * AdCP SDK library version\n */\nexport const LIBRARY_VERSION = '13.0.0-rc.6';\n\n/**\n * AdCP specification version this library is built for\n */\nexport const ADCP_VERSION = '3.1.8';\n\n/**\n * AdCP major version sent with every request (adcp_major_version field).\n * Sellers validate this against their supported versions and return\n * VERSION_UNSUPPORTED if the version is not in range.\n */\nexport const ADCP_MAJOR_VERSION = 3;\n\n/**\n * AdCP versions this library maintains backward compatibility with.\n *\n * Auto-derived from `ADCP_VERSION` by scripts/sync-version.ts. Do not edit\n * this list by hand; bumping the AdCP pin via `npm run sync-version`\n * extends it.\n */\nexport const COMPATIBLE_ADCP_VERSIONS = [\n 'v2.5',\n 'v2.6',\n 'v3',\n '3.0.0-beta.1',\n '3.0-beta.1',\n '3.0-beta',\n '3.0.0-beta.3',\n '3.0-beta.3',\n '3.0.0',\n '3.0',\n '3.0.1',\n '3.0.2',\n '3.0.3',\n '3.0.4',\n '3.0.5',\n '3.0.6',\n '3.0.7',\n '3.0.8',\n '3.0.9',\n '3.0.10',\n '3.0.11',\n '3.0.12',\n '3.1.0',\n '3.1',\n '3.1.1',\n '3.1.2',\n '3.1.3',\n '3.1.4',\n '3.1.5',\n '3.1.6',\n '3.1.7',\n '3.1.8',\n] as const;\n\n/**\n * String literal union of every AdCP version the SDK formally supports.\n *\n * Used by the per-instance `adcpVersion` constructor option to give callers\n * autocomplete in editors. The intersection with `(string & {})` in the\n * config type preserves the escape hatch — any string is still accepted at\n * the type level — while the literal union surfaces canonical values first.\n */\nexport type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];\n\n/**\n * Full version information\n */\nexport const VERSION_INFO = {\n library: '13.0.0-rc.6',\n adcp: '3.1.8',\n compatibleVersions: COMPATIBLE_ADCP_VERSIONS,\n generatedAt: '2026-08-03T12:04:02.372Z',\n} as const;\n\n/**\n * Get the AdCP specification version this library is built for\n */\nexport function getAdcpVersion(): string {\n return ADCP_VERSION;\n}\n\n/**\n * Get the library version\n */\nexport function getLibraryVersion(): string {\n return LIBRARY_VERSION;\n}\n\n/**\n * Check if this library version is compatible with a given AdCP version\n */\nexport function isCompatibleWith(adcpVersion: string): boolean {\n return (COMPATIBLE_ADCP_VERSIONS as readonly string[]).includes(adcpVersion);\n}\n\n/**\n * Get all AdCP versions this library is compatible with\n */\nexport function getCompatibleVersions(): readonly string[] {\n return COMPATIBLE_ADCP_VERSIONS;\n}\n\n/**\n * Extract the major version number from an AdCP version string.\n *\n * Accepts:\n * - Semver: '3.0.0', '3.0.1', '3.1.0-beta.1' → 3\n * - Legacy aliases: 'v3' → 3, 'v2.5' / 'v2.6' → 2\n *\n * Returns NaN for unrecognized strings — callers should validate before passing.\n */\nexport function parseAdcpMajorVersion(version: string): number {\n const trimmed = version.trim();\n const semverLike = trimmed.startsWith('v') ? trimmed.slice(1) : trimmed;\n const major = parseInt(semverLike.split('.')[0] ?? '', 10);\n return Number.isFinite(major) ? major : NaN;\n}\n\n/**\n * Normalize a full-semver AdCP version (`MAJOR.MINOR.PATCH[-prerelease]`) to\n * the release-precision form that AdCP 3.1+ requires on the wire:\n * `MAJOR.MINOR[-prerelease]` — the patch digit is dropped.\n *\n * Per the spec note on `adcp_version`: \"SDKs that read full-semver values\n * from bundle metadata (e.g. `ComplianceIndex.published_version =\n * \"3.1.0-beta.1\"`) MUST normalize to release-precision (`\"3.1-beta.1\"`)\n * before emitting on the wire — meta-field values are NOT valid wire\n * values.\" The wire regex (`^\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$`) rejects strings\n * with a patch digit.\n *\n * Behavior:\n * - `\"3.1.0-beta.7\"` → `\"3.1-beta.7\"`\n * - `\"3.1.0\"` → `\"3.1\"`\n * - `\"3.0.12\"` → `\"3.0\"`\n * - Already-release-precision input (`\"3.1\"`, `\"3.1-beta.7\"`) passes through\n * - Legacy aliases (`\"v2.5\"`, `\"v3\"`) pass through unchanged — the wire\n * regex doesn't accept them anyway; the v2.5 path uses\n * `adcp_major_version` instead of `adcp_version` for transport.\n * - Unrecognized strings pass through unchanged so callers can detect drift\n * via the wire validator rather than have it masked by this helper.\n */\nexport function toReleasePrecisionVersion(version: string): string {\n const trimmed = version.trim();\n // Pre-release form `MAJOR.MINOR.PATCH-prerelease` → `MAJOR.MINOR-prerelease`\n const semverMatch = trimmed.match(/^(\\d+)\\.(\\d+)\\.\\d+(-[A-Za-z0-9.-]+)?$/);\n if (semverMatch) {\n const [, major, minor, pre = ''] = semverMatch;\n return `${major}.${minor}${pre}`;\n }\n // Already release-precision (no patch digit). Includes `3.1`, `3.1-beta.7`.\n if (/^\\d+\\.\\d+(-[A-Za-z0-9.-]+)?$/.test(trimmed)) return trimmed;\n // Legacy aliases (`v3`, `v2.5`, `v2.6`) and anything we don't recognize —\n // pass through so the wire validator can flag genuine drift.\n return version;\n}\n"],"mappings":"AAMO,MAAM,kBAAkB;AAKxB,MAAM,eAAe;AAOrB,MAAM,qBAAqB;AAS3B,MAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAeO,MAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,oBAAoB;AAAA,EACpB,aAAa;AACf;AAKO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,iBAAiB,aAA8B;AAC7D,SAAQ,yBAA+C,SAAS,WAAW;AAC7E;AAKO,SAAS,wBAA2C;AACzD,SAAO;AACT;AAWO,SAAS,sBAAsB,SAAyB;AAC7D,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,QAAM,QAAQ,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACzD,SAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C;AAyBO,SAAS,0BAA0B,SAAyB;AACjE,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,cAAc,QAAQ,MAAM,uCAAuC;AACzE,MAAI,aAAa;AACf,UAAM,CAAC,EAAE,OAAO,OAAO,MAAM,EAAE,IAAI;AACnC,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,GAAG;AAAA,EAChC;AAEA,MAAI,+BAA+B,KAAK,OAAO,EAAG,QAAO;AAGzD,SAAO;AACT;","names":[]}
|
|
@@ -461,6 +461,13 @@ class MultiTenantAdapter implements DecisioningPlatform<Record<string, never>, T
|
|
|
461
461
|
* disabled isolation for credentials lacking a home tenant.
|
|
462
462
|
*/
|
|
463
463
|
accounts: AccountStore<TenantMeta> = createTenantStore<TenantState, TenantMeta>({
|
|
464
|
+
// Required, no default — the safe value depends on whether one credential
|
|
465
|
+
// is *supposed* to span tenants. Here it is not: each buyer agent_url maps
|
|
466
|
+
// to exactly one home tenant via BUYER_HOME_TENANT, so a ref naming another
|
|
467
|
+
// tenant must fail closed rather than resolve. An agency hub whose single
|
|
468
|
+
// credential legitimately spans tenants would pass 'ref-routed' instead and
|
|
469
|
+
// layer a `resolve-presets` guard on top.
|
|
470
|
+
refAccess: 'auth-scoped',
|
|
464
471
|
resolveByRef: ref => {
|
|
465
472
|
const r = narrowAccountRef(ref);
|
|
466
473
|
if (!r.operator) return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adcp/sdk",
|
|
3
|
-
"version": "13.0.0-rc.
|
|
3
|
+
"version": "13.0.0-rc.6",
|
|
4
4
|
"description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -615,7 +615,7 @@
|
|
|
615
615
|
"@types/pg": "^8.20.0",
|
|
616
616
|
"@types/tar": "^6.1.13",
|
|
617
617
|
"actionlint": "^2.0.6",
|
|
618
|
-
"esbuild": "^0.
|
|
618
|
+
"esbuild": "^0.28.1",
|
|
619
619
|
"esbuild-fix-imports-plugin": "^1.0.23",
|
|
620
620
|
"eslint": "^10.0.3",
|
|
621
621
|
"json-schema-to-typescript": "^15.0.4",
|
|
@@ -639,6 +639,7 @@
|
|
|
639
639
|
"node": ">=20.0.0"
|
|
640
640
|
},
|
|
641
641
|
"overrides": {
|
|
642
|
+
"esbuild": "$esbuild",
|
|
642
643
|
"rollup": ">=4.59.0",
|
|
643
644
|
"@types/glob": "^8.1.0",
|
|
644
645
|
"js-yaml": "^4.1.1",
|
|
@@ -71,6 +71,7 @@ The framework calls `accounts.resolve(ref, ctx)` once per request, and hub adapt
|
|
|
71
71
|
1. **Path 1** — operator-routed resolution for tools that carry `account` on the wire (`sync_accounts`, `sync_governance`, governance, property-lists)
|
|
72
72
|
2. **Path 2** — auth-derived resolution for no-account tools (`get_brand_identity`, `get_rights`)
|
|
73
73
|
3. **Per-entry tenant-isolation gate** on `sync_accounts` / `sync_governance` — verify each entry's `operator` maps to the buyer's authenticated home tenant before persisting, fail-closed when the auth principal isn't registered
|
|
74
|
+
4. **A resolve posture** — decide whether a buyer-supplied ref naming *another* tenant resolves or fails closed. This is the `refAccess` field and it is **required with no default**, because both answers are correct for some hubs. It is a separate decision from (3): the gate in (3) covers the sync tools only, while `resolve` is the account path for `create_media_buy` and `update_media_buy`
|
|
74
75
|
|
|
75
76
|
All three live in `createTenantStore<TTenant, TCtxMeta>` (`@adcp/sdk/server`). Callbacks the adopter provides; gating logic the SDK owns:
|
|
76
77
|
|
|
@@ -78,6 +79,9 @@ All three live in `createTenantStore<TTenant, TCtxMeta>` (`@adcp/sdk/server`). C
|
|
|
78
79
|
import { createTenantStore, narrowAccountRef } from '@adcp/sdk/server';
|
|
79
80
|
|
|
80
81
|
accounts: AccountStore<TenantMeta> = createTenantStore<TenantState, TenantMeta>({
|
|
82
|
+
// REQUIRED, no default. 'auth-scoped' = a ref naming another tenant fails
|
|
83
|
+
// closed. Use it unless one credential is *supposed* to span tenants.
|
|
84
|
+
refAccess: 'auth-scoped',
|
|
81
85
|
resolveByRef: ref => { // Path 1 — operator (or account_id) on the wire
|
|
82
86
|
const r = narrowAccountRef(ref);
|
|
83
87
|
const tenantId = OPERATOR_TO_TENANT.get(r.operator ?? '');
|
|
@@ -110,10 +114,25 @@ accounts: AccountStore<TenantMeta> = createTenantStore<TenantState, TenantMeta>(
|
|
|
110
114
|
});
|
|
111
115
|
```
|
|
112
116
|
|
|
117
|
+
**Pick your `refAccess` deliberately — it is the one field with no safe default:**
|
|
118
|
+
|
|
119
|
+
| | `resolve` with a ref naming another tenant | Use when |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `'auth-scoped'` | returns `null` → `ACCOUNT_NOT_FOUND` | Tenants are unrelated clients / competing agencies. **Most hubs.** |
|
|
122
|
+
| `'ref-routed'` | returns that tenant's `Account` | One credential is *supposed* to span tenants (a single agency operating all of them) |
|
|
123
|
+
|
|
124
|
+
Getting this wrong in the `'ref-routed'` direction means any authenticated tenant can name another tenant's `account_id` or `operator` and have `create_media_buy` run scoped to that account — cross-tenant **spend**, not just cross-tenant reads. If you genuinely need `'ref-routed'`, layer a `resolve-presets` guard (`requireAccountMatch` / `requireAdvertiserMatch` / `requireOrgScope`) on top via `composeMethod`.
|
|
125
|
+
|
|
126
|
+
**What the helper does NOT guarantee:**
|
|
127
|
+
|
|
128
|
+
- **`refAccess` governs `resolve` only.** Verifying the sync-tool gate tells you nothing about `resolve`. Under `'ref-routed'` there is no isolation on `resolve` at all — that is the mode's purpose, not a bug.
|
|
129
|
+
- **It does not scope your data layer.** `tenantToAccount` returns whatever your callbacks hand it; if `resolveByRef` reads a shared table without a tenant predicate, the helper cannot see that.
|
|
130
|
+
|
|
113
131
|
**What the helper guarantees:**
|
|
114
132
|
|
|
115
133
|
- **Cross-tenant entries never reach `upsertRow` / `syncGovernanceRow`.** The helper resolves `authTenant = resolveFromAuth(ctx)` once per request, then for each entry resolves `entryTenant = resolveByRef(ref)` and emits a `'failed'` row with `code: 'PERMISSION_DENIED'` when `tenantId(authTenant) !== tenantId(entryTenant)`. Adopter callbacks see only in-tenant entries.
|
|
116
134
|
- **Fail-closed when `resolveFromAuth` returns null** (unknown principal, no `agentRegistry`, agent_url not in your home-tenant map): every entry fails `PERMISSION_DENIED`. Don't fork around this — the prior fail-OPEN shape (`if (homeTenantId && entryTenant !== homeTenantId)`) silently disabled isolation for credentials lacking a home-tenant binding.
|
|
135
|
+
- **Construction refuses a missing or unrecognized `refAccess`.** TypeScript flags it, and the helper also throws at runtime so a JS adopter or an `as any` cast can't fall through to the permissive branch silently.
|
|
117
136
|
- **`accounts.upsert` and `accounts.syncGovernance` are non-writable** on the returned store. An adopter who writes `accounts.upsert = customHandler` after construction gets a `TypeError` (in strict mode) instead of silently bypassing the gate. To extend with `list` / `reportUsage` / `getAccountFinancials`, use `Object.assign`:
|
|
118
137
|
|
|
119
138
|
```ts
|
package/skills/cross-cutting.md
CHANGED
|
@@ -66,10 +66,12 @@ The hello adapters use simple in-memory `accounts.resolution: 'lookup'` against
|
|
|
66
66
|
- `createOAuthPassthroughResolver` — buyer-OAuth-passes-through (Shape B)
|
|
67
67
|
- `createRosterAccountStore` — pre-loaded roster (Shape C)
|
|
68
68
|
- `createDerivedAccountStore` — single-tenant `'derived'` mode (Shape D)
|
|
69
|
-
- `createTenantStore` — multi-tenant with built-in isolation gate
|
|
69
|
+
- `createTenantStore` — multi-tenant, with a built-in isolation gate on the account-sync tools and a **required** `refAccess` choice for `resolve`
|
|
70
70
|
|
|
71
71
|
`createTenantStore` is the right default for any adopter handling more than one advertiser. It refuses inline `{account_id}` references unless your store explicitly lists them — that's a hard security gate, not a soft warning.
|
|
72
72
|
|
|
73
|
+
Its `refAccess` field is required and has no default: `'auth-scoped'` makes a ref naming another tenant fail closed, `'ref-routed'` lets one credential span tenants on purpose. The sync-tool isolation gate is always on and is a *separate* decision — `refAccess` governs `accounts.resolve`, which is the account path for `create_media_buy` / `update_media_buy`. See `skills/build-holdco-agent/SKILL.md`.
|
|
74
|
+
|
|
73
75
|
## Webhooks: stable `operation_id` across retries
|
|
74
76
|
|
|
75
77
|
Async tasks (creative review, IO approval, signal activation, plan outcome reporting) emit completion webhooks. Use `ctx.emitWebhook({ url, payload, operation_id })` — the framework handles RFC 9421 signing, stable `idempotency_key` across retries, backoff, terminal-error handling. Pass `webhooks: { signerKey }` to `createAdcpServerFromPlatform(platform, { webhooks })`.
|