diff --git a/.changeset/pre.json b/.changeset/pre.json index 6f95fe338..db8e2784f 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -25,6 +25,7 @@ "adcp-5385-community-mirror-lifecycle", "adcp-compat-dist-tags", "advisory-errors-vendor-metrics", + "advisory-flat-error-code-unwrapper", "argus-contributor-ci", "asset-variant-array-slots", "async-discovery-rc8", @@ -80,6 +81,7 @@ "governance-agents-categories-removed", "governance-fixtures-verdict-rename", "harden-conditional-param-codegen", + "input-schema-field-strip-notice", "lazy-idempotency-backend", "legacy-media-buy-status-normalization", "main-ci-3-1-beta-compat", @@ -100,6 +102,7 @@ "portable-upstream-identifier-paths", "post-1902-cleanup", "postgres-task-registry-docs", + "pre-3-1-field-projection", "pre31-signals-wholesale", "preferred-attestation-mode", "preserve-storyboard-asap-start", @@ -108,6 +111,7 @@ "product-schema-object-helpers", "product-schema-zodobject-ergonomics", "promote-query-upstream-traffic", + "proposal-gate-absence", "proxy-seller-snap-bridge", "public-net-ssrf-webhook-docs", "public-schema-root", @@ -118,6 +122,7 @@ "reference-seller-matrix", "reference-seller-typescript-row", "registry-client-hardening", + "registry-logo-save-and-creative-error-code", "remove-rfc9421-response-signing", "remove-schema-root-exports", "request-signing-conformance-cache", @@ -128,7 +133,9 @@ "schema-loader-strip-nested-ids", "scoped-schema-compliance-bundles", "server-handler-payload-types", + "signed-requests-preflight-fix", "signed-requests-specialism-notice", + "skip-absent-equals-capability-gates", "slimy-cars-rest", "source-fixes-1950-union-products-governance", "split-typecheck-build", @@ -163,8 +170,10 @@ "validate-input-response-schema", "webhook-parse-conformance", "webhook-scope-creative-context", + "webhook-verify-accept-request-signing-key", "wholesale-feed-sync-lifecycle", "wholesale-feed-webhook-normalizer", + "ws-security-patch", "zod-object-dx-hardening" ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6534d1130..5dd1be999 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,93 @@ # Changelog +## 9.0.0-beta.31 + +### Minor Changes + +- 1e57767: feat: strip AdCP 3.1-only request fields when the negotiated target is pre-3.1 + + `BrandReference` is a closed object (`additionalProperties: false`) in every AdCP version. The 3.1 inline override `brand_kit_override` was added in AdCP 3.1 and does not exist in the 3.0 schema — 3.0 sellers reject requests carrying it. `industries` and `data_subject_contestation` are declared in AdCP 3.0 GA and are accepted by 3.0 sellers; they are left on the wire. Separately, the `get_products` discovery webhook (`push_notification_config`, a 3.1 feature) caused the SDK to throw for pre-3.1 clients. + + The client now omits 3.1-only fields when the negotiated target is pre-3.1 (the client is pinned below 3.1, or the seller does not advertise 3.1 via `get_adcp_capabilities`), degrading gracefully: + - `brand_kit_override` is stripped from outbound brand references on `create_media_buy`, `sync_accounts`, and `get_products`; identity fields (`domain`, `brand_id`) and 3.0 fields (`industries`, `data_subject_contestation`) are preserved. + - The auto-injected `get_products` discovery webhook is skipped (results are polled via `tasks/get`) instead of throwing. An explicit caller-supplied `push_notification_config` on a pre-3.1 client still throws (unchanged). + - Both are surfaced as `debug_logs` drift entries (`pre31_brand_fields_stripped`, `pre31_webhook_degraded`) so the drops are visible and not silent. + + The brand strip is keyed on `shouldOmit31Fields(clientVersion, sellerCapabilities)` — correct for 3.0-pinned callers today and per-seller when a caller pins to 3.1. The webhook suppression is keyed on the client pin only (`isPre31AdcpVersion`), since suppression runs before `detectServerVersion` populates seller caps. + +- 1b0aa12: Add `RegistryClient.saveBrandLogo()` as the canonical AAO brand-logo helper, normalize list responses to `assets` while preserving the legacy `logos` alias, and bridge creative asset errors to canonical `code` with deprecated `error_code` compatibility. +- 3652403: feat(signing): sign and verify webhooks with the request-signing key + + Webhooks are signed with the agent's `adcp_use: "request-signing"` key — there + is no separate webhook key purpose. The webhook verifier (step 8) accepts a key + whose `adcp_use` is `"request-signing"`; the deprecated `"webhook-signing"` + value is still accepted for backward compatibility (pending removal — adcontextprotocol/adcp#5555). Any other + purpose (`response-signing`, `governance-signing`, unknown), absent `adcp_use`, + or a missing `verify` key_op is rejected with + `webhook_signature_key_purpose_invalid`. `webhook_mode_mismatch` is unchanged — + it remains reserved for the HMAC-vs-9421 auth-mode selector and is not used for + key-purpose failures. The signer helpers (`signWebhook` / `signWebhookAsync`) + accept the same set, and the webhook emitter may reuse the request-signing + provider/key. + + This is safe because cross-protocol confusion is prevented by the RFC 9421 + `tag` (`adcp/webhook-signing/v1`, part of the signed base) and mandatory + `content-digest` coverage — not by the key-purpose discriminator. A captured + request signature (`tag=adcp/request-signing/v1`) can never be replayed + against the webhook verifier because step 3 rejects the tag. + + Webhook key isolation, when wanted, is a second `request-signing` key under a + distinct `kid` — not a distinct `adcp_use`. + + Conformance vectors: positive `008-request-signing-key-reuse` covers a + request-signing key signing a webhook; negative `008-wrong-adcp-use` covers a + `response-signing` key (rejected); the existing `webhook-signing` positive + vectors continue to exercise the deprecated-but-accepted path. Tracks the spec + change in adcontextprotocol/adcp. + +### Patch Changes + +- fcf4622: Treat flat `error_code` fields as advisory when the response also has a tool-specific success payload. +- b4defa5: Surface input-schema field stripping as structured runner notices so compliance JSON output no longer hides stripped request fields in console warnings only. +- eeaa641: fix(conformance): skip proposal storyboards when supports_proposals is absent + + Treat omitted `media_buy.supports_proposals` as unsupported for proposal lifecycle + `requires_capability` gates, including profiles without raw capabilities. + +- 604a956: Unblock 3.1 `signed-requests` adopters. Two coupled fixes; the runner-side fix alone leaves adopters stuck on the boot guard, and vice versa (per #2237 triage). + + **1. Pre-flight `resolveStoryboardsForCapabilities` (`compliance.ts`).** + `resolveStoryboardsForCapabilities` was throwing `unknown_specialism` on the + deprecated `signed-requests` claim because the bundle lives under + `universal/signed-requests.yaml`, not `specialisms/signed-requests/`. That + blocked every other storyboard from running and prevented the + `signed_requests_specialism_deprecated` notice (#2082) from ever firing. + + Adds `DEPRECATED_SPECIALISM_UNIVERSAL_ALIASES` mapping the deprecated + specialism enum value to its universal bundle base name. When the deprecated + alias is declared AND the universal bundle is present in the cache, + resolution continues silently (the universal storyboard is pushed + unconditionally and the deprecation notice fires from runner.ts). + Otherwise the throw is preserved — unknown specialism without a universal + fallback is still a configuration error. + + **2. Boot guard `createAdcpServer` (`create-adcp-server.ts`).** + The previous guard required the deprecated `signed-requests` specialism claim + whenever `signedRequests` was configured, contradicting the universal + storyboard's "drop the now-redundant specialism claim and rely solely on + `request_signing.supported: true`" guidance. Widens the guard to accept + either discovery surface: the canonical 3.1+ form + (`capabilities.request_signing.supported: true`, no deprecated claim) or + the back-compat form (`specialisms: ['signed-requests']`). Still rejects + the "config present, nothing advertised" case so buyers can't be left + unable to discover the signing requirement. JSDoc on `SignedRequestsConfig` + updated to document both paths. + + Closes #2237. + +- 59fb56a: Skip storyboard `requires_capability.equals` gates when the agent omits the capability field. +- 68b8f38: Bump ws to 8.21.0 to resolve high-severity memory exhaustion DoS vulnerability (GHSA-96hv-2xvq-fx4p). + ## 9.0.0-beta.30 ### Minor Changes diff --git a/package.json b/package.json index 7e16eb829..7c6f317b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adcp/sdk", - "version": "9.0.0-beta.30", + "version": "9.0.0-beta.31", "description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)", "workspaces": [ ".", diff --git a/packages/client-shim/package.json b/packages/client-shim/package.json index 228ab9085..311f01ffe 100644 --- a/packages/client-shim/package.json +++ b/packages/client-shim/package.json @@ -106,7 +106,7 @@ "LICENSE" ], "dependencies": { - "@adcp/sdk": "^9.0.0-beta.30" + "@adcp/sdk": "^9.0.0-beta.31" }, "keywords": [ "adcp", diff --git a/src/lib/version.ts b/src/lib/version.ts index 04c9238fd..dc8af731b 100644 --- a/src/lib/version.ts +++ b/src/lib/version.ts @@ -4,7 +4,7 @@ /** * AdCP SDK library version */ -export const LIBRARY_VERSION = '9.0.0-beta.30'; +export const LIBRARY_VERSION = '9.0.0-beta.31'; /** * AdCP specification version this library is built for @@ -97,10 +97,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number]; * Full version information */ export const VERSION_INFO = { - library: '9.0.0-beta.30', + library: '9.0.0-beta.31', adcp: '3.1.0-rc.14', compatibleVersions: COMPATIBLE_ADCP_VERSIONS, - generatedAt: '2026-06-16T20:01:12.379Z', + generatedAt: '2026-06-17T22:00:45.121Z', } as const; /**