v5.9.1
Patch Changes
-
b1497f9: ci: consolidate pipeline and drop redundant jobs
CI-only change, no runtime/library behaviour affected. Published package contents are unchanged.
ci.yml: collapsetest/quality/securityinto a single job. Each was re-runningcheckout + setup-node + npm ci, wasting ~1–2 min of setup per PR. Also removes theclean && build:libre-build in the old quality job and the redundantbuildstep (alias ofbuild:lib).ci.yml: droppublish-dry-run.release.yml'sprepublishOnlyalready validates packaging on the actual release PR.ci.yml: drop deaddevelopbranch from the push trigger.schema-sync.yml: drop the PR-triggeredvalidate-schemasjob —ci.ymlalready syncs schemas and diffs generated files on every PR. Scheduled auto-update job preserved.commitlint.yml: usenpm ciinstead ofnpm install --save-dev; the@commitlint/*packages are already indevDependencies.
-
933eb2d: Two response-layer fixes for agents built from partial skill coverage:
buildCreativeResponse/buildCreativeMultiResponseno longer crash on missing fields. The default summary previously dereferenceddata.creative_manifest.format_id.idwithout guards — handlers that dropformat_id(required bycreative-manifest.json) crashed the dispatcher withCannot read properties of undefined (reading 'id'), swallowing the real schema violation behind an opaqueSERVICE_UNAVAILABLE. Now the summary optional-chains through the field chain and falls back to a generic string, so the response reaches wire-level validation and the buyer sees the actual missing-field error.replayed: falseis no longer injected on fresh executions.protocol-envelope.jsonpermits the field to be "omitted when the request was executed fresh"; emittingfalseviolates strict task response schemas that declareadditionalProperties: false(create-property-list-response, etc.). Fresh responses now drop any priorreplayedmarker; replays still carryreplayed: true. The existingtest/lib/idempotency-client.test.js"replayed omitted is surfaced as undefined" test aligns with this shift.Surfaced by matrix v10: six
creative_generativepairs crashed with the dereference, and everyproperty_listspair hit theadditionalPropertiesviolation. -
5eb2ae9: fix(testing):
context.no_secret_echowalks structuredTestOptions.auth, andregisterAssertionaccepts{ override: true }-
The default
context.no_secret_echoassertion in@adcp/client/testing
previously treatedoptions.authas a string and added the whole
discriminated-union object to its secret set.String.includes(obj)
against[object Object]matched nothing, so the assertion was
effectively a no-op for every consumer passing structured auth (bearer,
basic, oauth, oauth_client_credentials). It now extracts the leaf
secrets across every variant:- bearer:
token - basic:
username,password, and the base64user:passblob an
Authorization: Basicheader would carry - oauth:
tokens.access_token,tokens.refresh_token,
client.client_secret(confidential clients) - oauth_client_credentials:
credentials.client_idand
credentials.client_secret— resolving$ENV:VARreferences to their
runtime values so echoes of the real secret (not the reference string)
are caught — plustokens.access_token/tokens.refresh_token
A minimum-length guard (8 chars) skips substring matching on fixture
values that would otherwise collide with benign JSON. - bearer:
-
registerAssertion(spec, { override: true })now replaces an existing
registration instead of throwing. Lets consumers swap in a stricter
version of an SDK default (e.g. their owncontext.no_secret_echo)
without callingclearAssertionRegistry()and re-registering every other
default. Default behaviour ({ override: false }/ no options) is
unchanged and still throws on duplicate ids.
-
-
afc01f1: Widen two bundled default assertions per security-review feedback on adcontextprotocol/adcp#2769.
idempotency.conflict_no_payload_leak— flip the denylist-of-5-fields to an allowlist of 7 envelope keys (code,message,status,retry_after,correlation_id,request_id,operation_id). The previous implementation only flaggedpayload,stored_payload,request_body,original_request,original_response— a seller inliningbudget,start_time,product_id, oraccount_idat theadcp_errorroot slipped past, turning idempotency-key reuse into a read oracle for stolen-key attackers. Allowlisting closes the hole: anything a seller adds beyond the 7 envelope fields now fails the assertion.context.no_secret_echo— scan the full response body recursively (not just.context), add a bearer-token literal regex (/\bbearer\s+[A-Za-z0-9._~+/=-]{10,}/i), add recursive suspect-property-name match (authorization,api_key,apikey,bearer,x-api-key), and pick upoptions.test_kit.auth.api_keyas a verbatim-secret source. The previous scope (response.contextonly, verbatimoptions.auth_token/.auth/.secrets[]only) missed the common cases where sellers echo credentials intoerror.message,audit.incoming_auth, nested debug fields, or as header-shaped properties. All caller-supplied secrets gate on a minimum length (8 chars) to avoid false positives on placeholder values.Both changes are patch-level — the assertion ids, public registration API, and passing-case behavior are unchanged; the narrowing on main was fresh in 5.9 and had no adopters broad enough for the strictening to break in practice.
governance.denial_blocks_mutationis unchanged.16 new unit tests cover both widenings: allowlist hits (valid envelope passes), denylist vestigial names still fail, non-allowlisted field leaks (including stable sorted error output), plus bearer literals, verbatim
options.auth_tokenecho,options.secrets[]echo,test_kit.auth.api_keyecho, suspect property names at any depth, array walking, short-value false-positive guard, and prose-"bearer" ignore.