Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .changeset/refs-resolve-canonicalize-agent-url.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@adcp/client': patch
---

`refs_resolve` scope: canonicalize `$agent_url` by stripping transport
suffixes instead of comparing raw target URL to bare agent origins.

Before this fix, storyboards using `scope: { key: 'agent_url', equals:
'$agent_url' }` silently graded every source ref `out_of_scope` on MCP
and A2A runners, because `$agent_url` expanded to the runner's target
URL (with `/mcp`, `/a2a`, or `/.well-known/agent.json` suffixes) while
refs carried the bare agent URL per AdCP convention. Net effect: the
check degraded from integrity enforcement to a no-op on every MCP agent.

The scope comparator now mirrors `SingleAgentClient.computeBaseUrl`:
strip `/mcp`, `/a2a`, `/sse`, and `/.well-known/agent[-card].json`
suffixes; lowercase scheme and host; drop default ports; strip
userinfo, query, and fragment. Path below the transport suffix is
preserved, so sibling agents at different subpaths on a shared host
(e.g. `https://publisher.com/.well-known/adcp/sales` vs
`/.well-known/adcp/creative`) remain distinguishable. Closes #710.
31 changes: 31 additions & 0 deletions .changeset/refs-resolve-observation-hygiene.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
'@adcp/client': patch
---

`refs_resolve`: harden grader-visible observation and `actual.missing`
payloads against hostile agent responses.

Compliance reports may be published or forwarded to third parties, so
every ref field emitted by the runner is now:

- **Userinfo-scrubbed** on URL-keyed fields via WHATWG URL parsing plus
a regex fallback that scrubs `scheme://user:pass@` shapes embedded
in non-URL fields. Credentials planted in `agent_url` values can no
longer leak through compliance output.
- **Scheme-restricted** on URL-keyed fields: non-`http(s)` schemes
(e.g. `javascript:`, `data:`, `file:`) are replaced with a
`<non-http scheme: …>` placeholder so downstream UIs rendering
`agent_url` as a link cannot inherit a stored-XSS vector.
- **Length-capped** at 512 code points per string field, with a
code-point-boundary truncation that preserves surrogate pairs.
- **Count-capped** at 50 observations per check, with an
`observations_truncated` marker when the cap fires. Meta
observations (`scope_excluded_all_refs`, `target_paginated`)
precede per-ref entries so the cap never drops primary signal.

Match and dedup behavior is unchanged: the internal projection used
for ref comparison is kept separate from the sanitized projection used
for user-facing output, so truncation never false-collapses dedup
keys. `refsMatch` and `projectRef` also now use `hasOwnProperty` to
prevent storyboard authors from accidentally drawing match keys from
`Object.prototype`. Closes #714.
10 changes: 10 additions & 0 deletions .changeset/refs-resolve-scope-excluded-warning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@adcp/client': patch
---

`refs_resolve`: emit a `scope_excluded_all_refs` meta-observation when
a scope filter partitions every source ref out. The integrity check
enforces nothing when no ref falls in-scope; graders previously got a
silent pass. The meta-observation surfaces the structural smell without
changing pass/fail semantics. Suppressed under `on_out_of_scope: 'ignore'`
(which explicitly opts out of scope warnings). Closes #711.
15 changes: 15 additions & 0 deletions .changeset/refs-resolve-target-paginated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@adcp/client': patch
---

`refs_resolve`: detect paginated current-step targets and demote
unresolved refs to observations instead of failing the check.

Previously, when the target response carried `pagination.has_more:
true`, any ref legitimately defined on a later page graded as
`missing` — a false-positive failure against a conformant paginating
seller. The runner now emits a `target_paginated` meta-observation and
reports each would-be-missing ref as an `unresolved_with_pagination`
observation, letting the check pass until the spec-level resolution
lands (compliance mode requiring sellers to return everything
referenced by products in a single response). Closes #712.
4 changes: 2 additions & 2 deletions docs/TYPE-SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# AdCP Type Summary

> Generated at: 2026-04-20
> @adcp/client v5.7.0
> Generated at: 2026-04-21
> @adcp/client v5.8.0

Curated reference of the types that matter for using the AdCP client. For full generated types see `src/lib/types/tools.generated.ts` and `src/lib/types/core.generated.ts`.

Expand Down
2 changes: 1 addition & 1 deletion docs/llms.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Ad Context Protocol (AdCP)

> Generated at: 2026-04-21
> Library: @adcp/client v5.7.0
> Library: @adcp/client v5.8.0
> AdCP major version: 3
> Canonical URL: https://adcontextprotocol.github.io/adcp-client/llms.txt

Expand Down
6 changes: 4 additions & 2 deletions src/lib/server/test-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ import { toStructuredContent } from './responses';

/** Scenario names advertised via `list_scenarios` (force_* and simulate_*).
* Seed scenarios are NOT advertised — the spec treats them as universal
* request-time capabilities, not a discoverable subset. */
export type ControllerScenario = ListScenariosSuccess['scenarios'][number];
* request-time capabilities, not a discoverable subset. The upstream
* `ListScenariosSuccess['scenarios']` union includes seeds (open-for-
* extension), so we explicitly subtract them here. */
export type ControllerScenario = Exclude<ListScenariosSuccess['scenarios'][number], SeedScenario>;

/** Scenario names accepted in `scenario` requests but not advertised via
* `list_scenarios`. Sellers opt in by implementing the matching store method. */
Expand Down
7 changes: 4 additions & 3 deletions src/lib/testing/storyboard/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,10 @@ export interface RefsResolveSet {
}

/**
* Scope filter for `refs_resolve`. Only source refs whose `key` equals
* (after `$agent_url` substitution + `normalizeAgentUrl` normalization)
* are in scope. Out-of-scope refs are graded by `on_out_of_scope`.
* Scope filter for `refs_resolve`. Only source refs whose `key` matches
* `equals` (after `$agent_url` substitution and, for URL-ending keys,
* transport-suffix-stripping canonicalization) are in scope. Out-of-scope
* refs are graded by `on_out_of_scope`.
*/
export interface RefsResolveScope {
key: string;
Expand Down
Loading
Loading