Conversation
4f22a49 to
e4dab52
Compare
e4dab52 to
e680d6c
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/sdk@6.15.0
Minor Changes
918385f:
adcpError()and the{errors:[...]}typed Error arm now both ship thetwo-layer wire shape required by 18 AdCP response schemas
(
{adcp_error: {...}, errors: [{...}]}instead of envelope-only orpayload-only). Adopters keep calling
adcpError()and returning typedError arms exactly as before — the framework dispatcher derives the
affected tools from the bundled schema cache at server build, then
mirrors
adcp_error↔errors[]in thefinalize()seam on thefailure path so both spec-mandated layers ride on every failing
response.
The wire change is on the failure path only and is strictly additive:
responses that previously failed schema validation against the
*Errorarm of theironeOfnow pass it. Adopters who already emitboth layers manually are detected and pass through unchanged
(idempotent — no duplicate or replacement). Tools whose response
schema does NOT declare an Error arm (e.g.
get_products,get_signals,tasks/get) are untouched. No adopter code changesrequired.
Eighteen tools auto-wrap:
create_media_buy,update_media_buy,provide_performance_feedback,build_creative,sync_audiences,sync_catalogs,sync_event_sources,log_event,activate_signal,sync_creatives,get_creative_features,validate_content_delivery,list_content_standards,get_media_buy_artifacts,get_content_standards,create_content_standards,update_content_standards,calibrate_content. The set is deriveddynamically — future AdCP minors that add Error-arm tools join
automatically.
update_content_standardsis the lone tool whose Error arm carries asuccess: falsediscriminator alongsideerrors[]; the dispatcherstamps the constant when synthesising so the payload satisfies its
oneOfdiscriminator.Migration recipe:
docs/migration-6.14-to-6.15.md. RFC:docs/proposals/adcperror-two-layer-emission.md. Closes fix(server): adcpError() envelope-only emission misses payload-layer errors[] required by AdCP 3.0.7 Error arm #1606.a0ad369: Five new typed-factory namespaces for discriminator-injecting builders, mirroring the asset-builders / render-builders pattern. Each prevents a discriminator-missing wire-shape mistake at write time:
activationKey.{segment, keyValue}—ActivationKeyoneOfontype(SHAPE-GOTCHAS §1)signalId.{catalog, agent}—SignalIDoneOfonsource(SHAPE-GOTCHAS §2)buildCreativeReturn.{single, multi, singleEnveloped, multiEnveloped}—BuildCreativeReturn4-arm union (SHAPE-GOTCHAS §5)previewCreative.{single, batch, variant}—PreviewCreativeResponse3-armoneOfonresponse_type(SHAPE-GOTCHAS §4)mediaBuyDeliveryNotification.{scheduled, final, delayed, adjusted, windowUpdate}— webhooknotification_typediscriminator onGetMediaBuyDeliveryResponseReference adapters (
examples/hello_creative_adapter_*.ts,hello_signals_adapter_marketplace.ts,signals-agent.ts) migrated to use the new factories. Top-levelpreviewCreativeResponsev5 server-helper export retained for backwards compatibility; the new factory ships underpreviewCreativeto avoid collision with the v5 function.Closes feat(builders): typed factories for ActivationKey, BuildCreativeReturn envelopes, MediaBuyDeliveryNotification #1386.
Patch Changes
25af9a0: Bump
ADCP_VERSIONto 3.0.8. Patch release that extends the adcp#4218 storyboard idempotency-key precedent to the rest of the suite (adcp#4230). Fifteen storyboard steps across nine media-buy scenarios still shipped hardcodedidempotency_keyliterals on state-mutating tasks (create_media_buy,sync_creatives,sync_plans,update_media_buy); against a long-running seller the runner's dynamicstart_timesubstitution shifted the canonical body forward while the static key replayed, arming the spec-mandatedIDEMPOTENCY_CONFLICT(or, when the seller's emit shape changed between runs, replaying a now-spec-non-compliant cached payload). Every remaining literal is now$generate:uuid_v4#<scenario>_<step>so each storyboard run mints fresh keys.Affected scenarios:
creative_fate_after_cancellation(5),governance_approved,governance_conditions,governance_denied,governance_denied_recovery(3),invalid_transitions,inventory_list_no_match,inventory_list_targeting,pending_creatives_to_start.COMPATIBLE_ADCP_VERSIONSextended with'3.0.8'for editor autocomplete on theadcpVersionconstructor option. Generated types regenerated; functional schema content is identical to 3.0.7 (this release was a storyboard-only fix — no wire-format or schema change).192ebef: fix(test): retarget proposal-mode allowlist after AdCP 3.0.7 cascade unmask
The 3.0.7 schema bump (chore(deps): bump AdCP 3.0.6 → 3.0.7 (closes #1586) #1595) landed adcp#4088, fixing
proposal_idchaining inproposal_finalize.yaml.get_products_refinenow passes — and the cascade-skip that hidcreate_media_buyis gone, exposing a real adapter bug:create_media_buy's response doesn't satisfy the 3.0.7create-media-buy-response.jsonschema. Allowlist retargeted to maskcreate_media_buyuntil the adapter is fixed (tracked at fix(adapter): hello_seller_adapter_proposal_mode create_media_buy fails schema validation against AdCP 3.0.7 #1600). Unblocks main CI.20fce14: fix(storyboard): proposal-mode
create_media_buyrequest shape (closes adcp-client#1600)After AdCP 3.0.7 (chore(deps): bump AdCP 3.0.6 → 3.0.7 (closes #1586) #1595) landed adcp#4088, the
proposal_finalizestoryboard runs end-to-end throughaccept_proposalfor the first time. The runner'screate_media_buyenricher inrequest-builder.tswas unaware of the proposal-mode request shape — it always returned{ account, brand, start_time, end_time, packages }even when the storyboard'ssample_requestcarriedproposal_id: "$context.proposal_id". Two failures fell out:create-media-buy-request.jsondeclaresdependencies.proposal_id: ["total_budget"]and disallowspackagesalongsideproposal_id. Synthesising packages with the proposal_id elided made the request fail validation against the buyer-side strict gate.create_media_buyis inFIXTURE_AWARE_ENRICHERS, so the enricher's output is used verbatim and the fixture'saccountdoes not flow through the generic merge. The non-proposal path always replacesaccountwithresolveAccount(options)(default brandtest.example); proposal-mode storyboards author a non-default brand (acmeoutdoor.example) that the adapter resolved end-to-end through brief/refine/finalize, so the override producedACCOUNT_NOT_FOUNDat the accept step.The enricher now detects proposal-mode (either
step.sample_request.proposal_idresolving via$context.*orcontext.proposal_idset directly) and returns the fixture spread (withtotal_budgetand other proposal-mode-required fields preserved) plus the harness-normalisedstart_time/end_timeandproposal_id.accountandbrandprefer the fixture when supplied so non-default brands survive to the wire; otherwise the samecontext.account ?? resolveAccount(options)fallback applies.hello_seller_adapter_proposal_moderegression coverage updated:EXPECTED_FAILUREScleared (bothget_products_refine— fixed by adcp#4088 — andcreate_media_buy— fixed here).expectedRoutesextended withPOST /v1/ordersandPOST /v1/orders/{id}/lineitemsso the façade gate now asserts the full proposal lifecycle reaches the upstream's order endpoints.89256d2: fix(task-executor): return descriptive failed result when polling an evicted task
TaskExecutor.pollTaskCompletionnow catches"Task <id> not found"errorsfrom
getTaskStatusand returns aTaskResultFailurewith an actionableerror message rather than letting an opaque exception escape the polling
loop.
A2A 0.3.x defines no minimum retention TTL for completed tasks, so a seller
MAY evict a task between the buyer observing the working-state response and
the first explicit
tasks/getpoll firing. The error suggests using pushnotifications (
reporting_webhook) instead of polling, or configuring alonger task retention TTL on the seller.
Defense-in-depth follow-up to storyboard runner: A2A "Task X not found" on completed-task lookups during batch runs #1585. The cross-storyboard root cause was
already addressed in fix(storyboard-runner): reset retained A2A session ids per storyboard (#1585) #1588 (
resetContext()per storyboard) and fix(agent-client): narrow pendingTaskId auto-thread to same-skill same-context (#1590) #1593(narrowed
pendingTaskIdauto-thread to same-skill same-context); thischange improves the error surface for any residual case where an evicted
task is queried.
8117e93: docs(skills): collapse signal/creative/seller specialism skills onto fork-target pointers
In-scope subset of Tracking: collapse per-specialism skill prose onto fork-target pointers #1385. Skills with a worked-example adapter now reduce to a fork-target pointer plus this-specialism's deltas, instead of duplicating inline pattern teaching.
signal-marketplace+signal-owned: restructured to fork-target + delta sectionscreative-generative: points atcreative-templateadapter; adds delta-only generative sectionsales-broadcast-tv,sales-streaming-tv(new),sales-catalog-driven+sales-retail-media,audience-sync(-46 lines / 60% reduction),sales-proposal-mode: each adopts the fork-target shapeNo behavior change.
6e0bcc3: fix(storyboard-runner): preserve sample_request fields when enriching mutating-tool requests (fix(storyboard-runner): create_media_buy enricher drops proposal_id, total_budget, idempotency_key from sample_request #1604)
Fixture-aware enrichers in
src/lib/testing/storyboard/request-builder.tsrebuilt their request body from scratch and only copied an enumerated set of fields fromstep.sample_request(start_time,end_time,packages). Anything else the storyboard authored at the top level —total_budget,buyer_ref,currency,reporting_webhook, scenario-specific extensions — was silently dropped before the request hit the wire. The non-proposalcreate_media_buypath was the immediate trigger; PR fix(storyboard): proposal-mode create_media_buy request shape (closes #1600) #1603's proposal-mode branch already spread the fixture but the structural fix wasn't applied to the rest of the enricher.The fix spreads
sample_requestfirst (after$contextinjection), then layers the runner's substitutions (account, brand, normalised dates, packages with discovery-derived identifiers) on top. Envelope fields (context,ext,push_notification_config,idempotency_key) are deliberately omitted from the local spread and re-applied by the outerenrichRequestwithrunnerVarsso mustache substitutions like{{runner.webhook_url:<step_id>}}expand correctly. The sameomitEnvelopeFieldsdiscipline is now applied uniformly acrosscreate_media_buy,update_media_buy,get_media_buys,get_media_buy_delivery, andcomply_test_controller.