Conversation
e0d9d97 to
c8c2e9d
Compare
c8c2e9d to
576041e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@adcp/client@5.11.0
Minor Changes
740f609: Add typed factory helpers for creative asset construction that inject the
asset_typediscriminator:imageAsset,videoAsset,audioAsset,textAsset,urlAsset,htmlAsset,javascriptAsset,cssAsset,markdownAsset,webhookAsset, plus a groupedAssetnamespace (Asset.image({...})) over the same functions.Each helper takes the asset shape without
asset_typeand returns an object tagged with the canonical literal —imageAsset({ url, width, height })produces{ url, width, height, asset_type: 'image' }— eliminating the boilerplate at every construction site. The discriminator is written last in the returned object so a runtime bypass (cast that slipsasset_typeinto the input) cannot overwrite it.Return type is
Omit<T, 'asset_type'> & { asset_type: '<literal>' }(intersection) rather than the raw generated interface, so the builders compile regardless of whether the generated TypeScript types currently carry the discriminator — a defensive choice that makes the helpers stable across schema regenerations.828c112: Add
createDefaultTestControllerStoreto@adcp/client/testing— a default factory that wires everyforce_*,simulate_*,seed_*scenario against a genericDefaultSessionShape. Sellers provideloadSession/saveSessionand get a conformance-readyTestControllerStorewithout hand-rolling 300+ lines of boilerplate. Supports partial overrides for sellers who need to customize specific handlers.dd04ae9: Add
@adcp/client/express-mcpmiddleware that rewrites JSON-onlyAcceptheaders so they pass the MCP SDK'sStreamableHTTPServerTransportcheck whenenableJsonResponse: true. Local escape hatch pending upstream SDK fix (StreamableHTTPServerTransport rejects JSON-only Accept with 406 even when enableJsonResponse: true modelcontextprotocol/typescript-sdk#1944).4dc4743: Storyboard cross-step invariants are now default-on. Bundled assertions (
status.monotonic,idempotency.conflict_no_payload_leak,context.no_secret_echo,governance.denial_blocks_mutation) apply to every run unless a storyboard opts out — forks and new specialisms no longer ship with zero cross-step gating silently.Storyboard.invariantsnow accepts an object form{ disable?: string[]; enable?: string[] }.disableis the escape hatch that removes a specific default;enableadds a consumer-registered (non-default) assertion on top of the baseline. The legacyinvariants: [id, ...]array form still works and is treated as additive on top of the defaults.resolveAssertions(['id'])now returns[...defaults, ...named]instead of exactly the named ids. Callers that relied on the array-only return shape (e.g., snapshottingresolveAssertions([...]).length) should switch toresolveAssertions({ enable: [...], disable: listDefaultAssertions() })to reproduce the old semantics.AssertionSpecgained an optionaldefault?: booleanflag. Consumers registering custom assertions viaregisterAssertion(...)can opt their own specs into the default-on path.resolveAssertions(...)fails fast on unknown ids inenable/ the legacy array, and ondisableids that aren't registered as defaults (typo guard — a silent no-op would mask coverage gaps). Errors name the registered set and emit aDid you mean "..."?suggestion when one of the unknown ids is within Levenshtein distance 2 of a known id.invariants: { disabled: [...] }— trailingdtypo) throw instead of silently normalising to an empty disable set.listDefaultAssertions()(re-exported from@adcp/client/testing) enumerates the default-on set for tooling / diagnostics.status.monotonicfailure messages now include the legal next states from the anchor status and a link to the canonical enum schema, e.g.media_buy mb-1: active → pending_creatives (step "create" → step "regress") is not in the lifecycle graph. Legal next states from "active": "canceled", "completed", "paused". See https://adcontextprotocol.org/schemas/latest/enums/media-buy-status.json for the canonical lifecycle.Terminal states render as
(none — terminal state)so the message is unambiguous.6a2c2c5: Add typed factory helpers for
preview_creativerender objects:urlRender,htmlRender,bothRender, plus a groupedRendernamespace. Each helper takes the render payload withoutoutput_formatand returns an object tagged with the canonical discriminator —urlRender({ render_id, preview_url, role })produces a valid url-variant render without repeatingoutput_format: 'url'at every call site.Mirrors the
imageAsset/videoAssetpattern shipped in feat(client): typed asset builders for discriminator injection #771.PreviewRenderis a oneOf onoutput_format(url/html/both) where the discriminator decides which sibling field becomes required. Matrix runs consistently surfaced renders missing eitheroutput_formator its required sibling — the helpers make the wrong shape syntactically harder to express because the input type requires the matchingpreview_url/preview_htmlper variant.Return type uses
Omit<Variant, 'output_format'> & { output_format: <literal> }so the builders stay robust across schema regenerations. Discriminator is spread last so a runtime cast cannot overwrite the canonical tag.Skill pitfall callouts in
build-creative-agentandbuild-generative-seller-agentnow recommend the render helpers alongside the asset helpers.9d583aa: Extend the bundled
status.monotonicdefault assertion to track the audience lifecycle alongside the seven resource types it already guards (spec(enums): extract audience-status enum, formalize lifecycle transitions adcp#2836).sync_audiencesresponses carry per-audiencestatusvalues (processing | ready | too_small) drawn from the newly-named spec enum at/schemas/enums/audience-status.json, and the assertion now rejects off-graph transitions across storyboard steps for every observedaudience_id.Transition graph — fully bidirectional across the three states, matching the spec's permissive "MAY transition" hedging:
processing → ready | too_smallon matching completion.ready ↔ processingon re-sync (new members → re-match).too_small → processing | readyon re-sync (more members → re-match, directly back to ready when the re-matched count clears the minimum).ready ↔ too_smallas counts crossminimum_sizeacross re-syncs.Observations are drawn from
sync_audiencesresponses only — discovery-only calls (request omits theaudiences[]array) still returnaudiences[], so the extractor covers both write and read paths under the single task name. No separatelist_audiencestask exists in the spec. Actionsdeletedandfailedomitstatusentirely on the response envelope; the extractor's id+status guard makes those rows silent (nothing to observe, nothing to check).Resource scoping is
(audience, audience_id), independent from the other tracked resources. Unknown enum values drift-reset the anchor rather than failing —response_schemaremains the gate for enum conformance.8 new unit tests cover the forward flow, the too_small → processing → ready re-sync path, bidirectional
ready ↔ too_small,ready → processingon re-sync, self-edge silent pass, deleted/failed silent pass, per-audience-id scoping, and enum-drift tolerance. The assertion description now enumeratesaudiencealongside the other resource types.Follow-up: wiring
audience-sync/index.yamlwithinvariants: [status.monotonic]in the adcp spec repo once this release lands.eca55c5: Storyboard runner auto-fires
comply_test_controllerseed scenarios from thefixtures:block (adcp-client#778).When a storyboard declares
prerequisites.controller_seeding: trueand carries a top-levelfixtures:block, the runner now issues acomply_test_controllercall per fixture entry before phase 1:fixtures.products[]→seed_productfixtures.pricing_options[]→seed_pricing_optionfixtures.creatives[]→seed_creativefixtures.plans[]→seed_planfixtures.media_buys[]→seed_media_buyEach entry's id field(s) ride on
params; every other field is forwarded verbatim asparams.fixture. The seed pass surfaces as a synthetic__controller_seeding__phase inStoryboardResult.phases[]so compliance reports distinguish pre-flight setup from per-step buyer behavior.Grading semantics:
skip_reason: 'controller_seeding_failed'and canonicalskip.reason: 'prerequisite_failed'— respects the runner-output-contract's six canonical skip reasons (controller_seeding_failedis a newRunnerDetailedSkipReason, not a new canonical value).comply_test_controller→ cascade-skips with canonicalskip.reason: 'missing_test_controller', implementing the spec'sfixture_seed_unsupportednot_applicable grade. No wire calls are issued.runMultiPass) instead of N times inside each pass — avoids inflatingfailed_count/skipped_countby N when a fixture breaks.Closes the spec-side/seller-side gap. The
fixtures:block (Spec: storyboard fixtures block / pattern — replace hardcoded fixture IDs adcp#2585, rolled out in Storyboards: add fixtures block + controller_seeding to 5 storyboards that hardcode fixture IDs adcp#2743) and theseed_*scenarios (Spec: comply_test_controller fixture-seeding scenarios (seed_product, seed_pricing_option, seed_creative, seed_plan, seed_media_buy) adcp#2584, implemented here asSEED_SCENARIOS+createSeedFixtureCache) shipped without runner glue. Storyboards likesales_non_guaranteed,creative_ad_server,governance_delivery_monitor,media_buy_governance_escalation, andgovernance_spend_authoritygo from red to green against sellers that implement the matchingseed*adapters.New
StoryboardRunOptions.skip_controller_seeding. Opt out of the pre-flight for agents that load fixtures via a non-MCP path (HTTP admin, test bootstrap, inline Node state) — the runner then skips the seed loop even when the storyboard declares it.Types.
Storyboard.prerequisites.controller_seeding?: boolean,Storyboard.fixtures?: StoryboardFixtures, andStoryboardFixturesare now part of the public type.RunnerDetailedSkipReasongains'controller_seeding_failed'mapped to canonical'prerequisite_failed'viaDETAILED_SKIP_TO_CANONICAL.5ef797e: Sync generated types to AdCP 3.0 GA and consolidate the 4.x → 5.x migration guide.
Generated-type changes (in
src/lib/types/*.generated.ts, re-exported via@adcp/clientand@adcp/client/types):ImageAsset,VideoAsset,AudioAsset,TextAsset,URLAsset,HTMLAsset,JavaScriptAsset,WebhookAsset,CSSAsset,MarkdownAsset,BriefAsset,CatalogAsset,VASTAsset,DAASTAsset) gain a requiredasset_typeliteral discriminator (e.g.asset_type: 'image'). Handlers that construct asset literals must populate it.GetProductsRequest.refine[]—idrenamed toproduct_id(product scope) /proposal_id(proposal scope);actionis now optional (defaults to'include'). New-in-GA surface — beta.3 clients never sent this.GetProductsResponse.refinement_applied[]— flat object replaced by a discriminatedoneOfunion onscope. Each arm carriesproduct_id/proposal_id(previously a sharedid). New-in-GA surface.vast_version,tracking_events,vpaid_enabled,duration_ms,captions_url,audio_description_url) hoisted from inside eachoneOfarm to the base object. Wire payloads are unchanged; codegen is cleaner.ReportPlanOutcomeRequest,GetPlanAuditLogsRequest,CheckGovernanceRequest) — tightened to reject redundantaccountfields alongsideplan_id. New-in-GA surface.Wire-level compatibility. Against the previously-compatible AdCP
3.0.0-beta.3, the only bidirectional wire breaker is the assetasset_typediscriminator: a GA client strictly validating an asset payload from a beta.3 server will reject, because beta.3 servers don't emit the discriminator. Setvalidation: { requests: 'warn' }if you need that traffic to flow. Every other change is either TS-only (same JSON on the wire) or new-in-GA surface that beta.3 counterparties never exercise. rc.1 / rc.2 clients sending GA servers the oldrefine[].idshape will be rejected (additionalProperties: false) — upgrade the client.If upgrading your handlers: (1) populate
asset_typeon every asset literal your handlers construct ("image","video","vast","daast", …); (2) renamerefine[].id→refine[].product_id/refine[].proposal_idon the scope-matching arm; (3) runtsc --noEmit— tightened brand-rights +DomainHandlerreturn types will point to every drift site. Full walkthrough indocs/migration-4.x-to-5.x.mdPart 4.Migration doc consolidated.
docs/migration-4.30-to-5.2.mdanddocs/migration-5.3-to-5.4.mdare superseded bydocs/migration-4.x-to-5.x.md, which walks the full 4.x → 5.x train release-by-release and includes a wire-interop matrix near the top.New dev tool:
npm run schema-diff. Comparesschemas/cache/latest/against the snapshot captured on the previousnpm run sync-schemasrun (now written toschemas/cache/latest.previous/). Groups wire-level changes by kind (field renames, newly-required fields,additionalPropertiestightened,oneOfarm count changes, enum deltas) so the output surfaces interop concerns without re-reading 700 lines of generated TS. Run with no args for the default before/after pair, or pass two directories:npm run schema-diff -- <dirA> <dirB>.Patch Changes
2cfbb8a: Cycle-A fixes from matrix v12 failure analysis:
SDK:
TaskExecutor.normalizeResponseForValidationnow strips underscore-prefixed client-side annotations (_message, future_*fields) before running AJV schema validation. These are added by the response unwrapper as text-summary hints; they're not part of the wire protocol. Schemas withadditionalProperties: false(create-property-list-response,create-collection-list-response, etc.) would otherwise reject every response reaching the grader's schema check. Fixes 6 v12 failures across governance property-list CRUD.Skills: added a "Cross-cutting pitfalls matrix runs keep catching" block to each
build-*-agent/SKILL.mdinside the existing imperative callout. Each skill lists the specific patterns Claude drifted on in matrix v12 runs — targeted per tool surface:capabilities.specialismsisstring[]of enum ids, NOT[{id, version}]objects (all 8 skills)get_media_buy_deliveryrequires top-levelcurrency: string(seller, retail-media, generative-seller)build_creativereturns{creative_manifest: {format_id, assets}}, not sync_creatives-style fields (creative, generative-seller)creative_manifest.assetsrequires anasset_typediscriminator (creative, generative-seller)additionalProperties: false— don't add extra fields (governance)These live inside the imperative "fetch docs/llms.txt before writing return" callout so they're adjacent to where Claude scans for shape info.
5d823e6: Skill pitfalls for Cycle C — seller-side response-row drift surfaced by matrix v14:
get_media_buy_delivery /media_buy_deliveries[i]/by_package[j]rows require the billing quintet:package_id,spend,pricing_model,rate,currency. Matrix v14 caught 4 failures on mock handlers that returned{package_id, impressions, clicks}without the billing fields. Added to seller + retail-media + generative-seller pitfall callouts.get_media_buys /media_buys[i]rows requiremedia_buy_id,status,currency,total_budget,packages. Matrix v14 caught 2 failures on persist/reconstruct paths. Pitfall callouts now explicitly say: persistcurrency+total_budgetatcreate_media_buytime, echo verbatim.No SDK code change. This closes the last two non-specialism-specific drift classes; residual failures after matrix v15 will be storyboard-specific step expectations (generative quality, governance denial shape).
369aea8: Skill pitfalls for Cycle D — two narrow drift classes matrix v15 surfaced after the 3.0 GA schema sync (feat: sync schemas to AdCP 3.0 GA + consolidate 4.x→5.x migration doc #773):
get_media_buy_delivery /reporting_period/startand/endare ISO 8601 date-time strings (new Date().toISOString()produces the canonical shape), not date-only. GA added strictformat: "date-time"validation;'2026-04-21'now fails. Added to seller, retail-media, generative-seller, and creative-agent skill pitfall callouts.videoAsset({...})now requireswidthandheightper GA (previously optional onVideoAsset). Mocks that passed{url}alone fail validation at/creative_manifest/assets/<name>/width. Added to creative-agent and generative-seller pitfalls with a concrete pixel-values example.No SDK code change. Closes v15's two residual schema-drift classes. Residual failures after this land are storyboard-specific step expectations (generative quality grading, governance denial shape specifics) — the tight-loop per-pair phase.
55c7c3b: Storyboard runner: honor
step.sample_requestinget_rightsrequest builder.
Prior behavior hardcoded
query: 'available rights for advertising'and
uses: ['ai_generated_image'], and injectedbrand_idfrom thecaller's
brand.domain. Storyboards declaring scenario-specificquery text, uses, or a
buyer_brandhit the wire with the genericfallback instead, and rights-holder rosters rejected the
caller-domain
brand_idas unknown — sorights[0]was undefined,$context.rights_iddidn't resolve, and downstreamacquire_rightssteps failed with
rights_not_foundinstead of the error thestoryboard was actually asserting (e.g.,
GOVERNANCE_DENIEDinbrand_rights/governance_denied).Mirrors the pattern used by peer builders (
sync_plans,check_governance,list_creative_formats,create_content_standards, etc.). The generic fallback still runswhen no
sample_requestis authored.Closes adcp#2846.
7fd0948: Fix: response-schema AJV validators now accept envelope fields (
replayed,context,ext, and future envelope additions) at the response root on every tool.The bundled JSON response schemas for the property-list family (
create_property_list,update_property_list,delete_property_list,get_property_list,list_property_lists,validate_property_delivery) ship withadditionalProperties: falseat the root, which rejectedreplayed: false— even though security.mdx specifiesreplayedas a protocol-level envelope field that MAY appear on any response. That left a two-faced contract: the universal-idempotency storyboard requiresreplayed: falseon the initialcreate_media_buy, but emitting the same envelope field on property-list tools tripped strict response validation.schema-loadernow flipsadditionalProperties: falsetotrueat the response root (and at each directoneOf/anyOf/allOfbranch one level deep) when compiling response validators. Nested body objects stay strict so drift inside aProduct,Package, or list body still fails validation. Request schemas remain strict so outgoing drift fails at the edge. Matches the envelope extensibility the Zod generator already expresses via.passthrough(). Fixes Response schemas reject envelope-level replayed field on many mutating tools #774.