Releases: Merit-Systems/agentcash-router
Release list
v1.20.0
Minor Changes
- e01487b: Remove the non-spec
info.guidancekey from OpenAPI output. Guidance is now exposed only via the spec-compliantinfo.x-guidanceextension (plus/llms.txtand the well-knowninstructionsfield). Consumers readinginfo.guidanceshould switch toinfo["x-guidance"].
v1.19.1
Patch Changes
- d2052fd: Use mppx's public SSE session-controller type for metered streaming routes.
v1.19.0
Minor Changes
- c1f6bb4: Enforce CAIP-2 network identifiers at the type level and document the keyless local-dev path.
- New exported
X402Networktype (`eip155:${string}` | `solana:${string}`), now used byRouterConfig.networkandX402AcceptConfig.network. Friendly names like'base-sepolia'are rejected at compile time instead of throwingunsupported_x402_networkat construction. TS consumers passing a plainstringvariable fornetworkwill need to narrow it (or use the exported constantsBASE_MAINNET_NETWORK/SOLANA_MAINNET_NETWORK); the runtime validator is unchanged. - Fixed JSDoc on
networkfields that incorrectly suggested friendly names (base,base-sepolia,solana-mainnet) and a wrong@default 'base'. - The
missing_cdp_keyserror now notes that Coinbase CDP signup requires phone verification and that placeholder key values are a supported local-dev path (paid routes serve correct 402 challenges via the hardcoded facilitator baseline; real keys are only needed to verify/settle payments). Same guidance added to the README quickstart,createRouter/createRouterFromEnvJSDoc,AGENTS.md, and.env.example.
- New exported
v1.18.0
Minor Changes
-
dab4458: Add conditional settlement via
beforeSettlereturning'skip'. A paid route can now return its 2xx handler body without charging when the hook returns'skip';'continue'or void proceeds to settlement as before, and throw still fails the request without settling.Caveat:
'skip'only applies to post-handler settlement paths (x402 exact/upto, MPP transaction/pull). MPP hash/push mode settles at verify, beforebeforeSettleruns —'skip'cannot un-charge a push-mode client.
v1.17.0
Minor Changes
-
2d4c51d:
routerConfigFromEnv/createRouterFromEnvnow require one ofMPP_SECRET_KEYor the CDP key pair, and infer enabled protocols from whichever credentials are present — MPP-only services no longer need Coinbase credentials.- x402 is auto-enabled when
CDP_API_KEY_ID+CDP_API_KEY_SECRETare set, mirroring howMPP_SECRET_KEYtoggles MPP. Default protocols: CDP keys only →['x402'], MPP secret only →['mpp'], both →['x402', 'mpp']. An explicitprotocolsoption still overrides inference. - Neither credential set → new
missing_payment_credentialsissue in the up-frontRouterConfigError(previously an MPP-less env failed later atcreateRouterwithmissing_cdp_keys). - A partial CDP pair is treated as x402 intent and fails fast with
missing_cdp_keysnaming the missing variable, as does an explicitprotocolsincluding'x402'without CDP keys. - Soft
console.warnwhenSOLANA_PAYEE_ADDRESSis set while x402 is disabled, since the Solana accept would otherwise silently never be served. - Programmatic
createRouter(config)validation is unchanged: CDP keys are required only when the config has EVM x402 accepts.
- x402 is auto-enabled when
-
bdbcddb: Deprecate
RouterConfig.prices(andCreateRouterFromEnvOptions.prices); fix prototype-chain key lookup in the prices map.pricesis marked@deprecated(JSDoc only — no runtime change; auto-pricing and barrel validation keep working until removal in the next major). Every fleet service already prices inline with.paid(); auto-priced routes can't take pricing options, and the map is the only reasoncreateRouteris generic. Migration:.route('search').paid(PRICES.search)with an optional centralPRICESconst; for the map's barrel-validation side effect, use a consumer-side test that globs route files and assertsrouter.registry.has(key), or the catch-all adapter where a missing import 404s in dev.- Fixed: the auto-pricing lookup used
key in config.prices, which walks the prototype chain — a route namedtoString/valueOf/etc. on any router with a prices map picked up the inherited function as its "price" and threw at registration. NowObject.hasOwn.
-
ec3722c: Type-performance pass: eliminate the consumer-side "Type instantiation is excessively deep" hazard and shrink per-route check cost.
createRouter/createRouterFromEnvno longer capture the whole config as aconstgeneric. Inference is scoped to thepricesmap (PriceKeysOf<P>), so the router's exported type is a small named type instead of a deferred conditional over the entire config literal (guidance strings, accepts tuples, plugin closures). Large configs previously left that conditional unresolved until some consumer file forced it at the bottom of an already-deep check stack — tripping TS's instantiation-depth limit check-order-dependently (seen on Vercel builds). Therouter: ServiceRouterannotation workaround is no longer needed. Note: an explicit type argument (createRouter<typeof cfg>(cfg)) now names the prices map, not the config — drop the type argument and let it infer. A prices map typed as plainRecord<string, string>(built at runtime) no longer types its routes as pre-priced; use a literal map or price inline with.paid()..body()/.query()/.output()are now generic over the schema (S extends ZodType, output via lazyz.output<S>) instead of extractingTfromZodType<T>, which structurally walked zod's internals per call. Router-heavy route files check 2–8× faster (e.g. 13.3ms → 1.7ms) in a traced consumer build. Handlerctx.body/ctx.querytypes are unchanged. Note: an explicit type argument to these methods now names the schema type, not the output type.zodis now declared as a peer dependency (^4.0.0). It was previously undeclared (dev-only) while being imported at runtime, which resolved by hoisting luck and gave consumers a second zod copy — paying cross-copy structural comparisons on every.body(schema)chain.
v1.16.0
Minor Changes
-
5fd92ff: Framework-agnostic core: the router now speaks Web-standard
Request/Responseand dispatches through an embedded Hono app.nextis no longer a peer dependency (peers are justzod).Heads-up for TypeScript consumers: handler and discovery signatures are now typed against Web-standard
Request/Responseinstead ofNextRequest/NextResponse, andnextis no longer a peer dependency. Runtime behavior in Next.js apps is unchanged (Next accepts standard fetch handlers), but handler code that uses NextRequest-only APIs (request.nextUrl,request.cookies) needs a cast — or better,new URL(request.url)/ the newctx.params.New:
router.fetch(request)— standard fetch handler serving all registered routes at/{basePath}/{path}plus discovery surfaces; unmatched paths get thenotFound()envelope.router.hono()— the internal Hono app, mountable into a larger app.@agentcash/router/nextsubpath —nextHandlers(router)for one-file Next.js catch-all hosting (app/api/[[...route]]/route.ts), replacing per-route files and the discovery barrel.{param}path templates withctx.params, extracted identically in catch-all and per-file modes.RouterConfig.basePath(default'api') — controls the mounted and advertised URL prefix..path()values are normalized like.route()paths (leading slashes /api/prefix stripped), so a leading slash no longer produces a//URL in discovery.
Per-file Next.js hosting (
export const POST = router.route(...)...handler(...)) is unchanged. -
3b9cecb: Rename
.metered()to.session()andtickCosttounitCost, aligning the builder with MPP terminology (thesessionintent prices per-unitamount×unitType; "tick" was mppx SDK slang)..metered()andtickCostremain as deprecated aliases with identical behavior and will be removed in a future release. Registration-time and type-level error messages now reference.session()/unitCost. New exported types:SessionOptions(plusMeteredOptions/UpToOptionsare now exported). -
5b76428: Two x402 2.14–2.15 features surfaced through router config:
Base Builder Codes (ERC-8021 attribution). Set
RouterConfig.x402.builderCodeorX402_BUILDER_CODE(register at dashboard.base.org → Settings → Builder Codes) and the router declares thebuilder-codeextension with your app code on every x402 payment challenge; the facilitator appends it to settlement calldata, attributing every settled payment to your service on-chain. Malformed codes fail at startup with a structuredinvalid_builder_codeissue.Bazaar catalog metadata.
DiscoveryConfig(andcreateRouterFromEnvoptions) gainserviceName,tags, andiconUrl, forwarded intoPaymentRequired.resourceon every x402 challenge — facilitators persist them into the Bazaar discovery catalog at settlement.serviceNamedefaults to the discoverytitlewhen the title fits the 32-char printable-ASCII constraint, andtagsdefaults per-resource to the same route-derived tag the OpenAPI document advertises. Explicit values that violate the catalog limits fail at startup (invalid_discovery_service_name/invalid_discovery_tags/invalid_discovery_icon_url) instead of being silently dropped by the facilitator.
Patch Changes
- 821e508: MPP session channels now use one shared store across the request-mode and streaming middlewares (previously each defaulted to a private in-memory store, so a channel opened through one was
channel-not-foundthrough the other). Protocol-heavy dependencies (mppx,viem/tempo,@x402/evm, …) are now loaded lazily at their call sites instead of statically, so x402-only deployments no longer bundle the MPP dependency tree (and vice versa) — this also removes the webpack "Critical dependency" warning fromox/tempoin Next.js apps that don't use MPP. - ef798ea: Bump
@x402/core/@x402/evm/@x402/svm/@x402/extensionsfrom 2.13.0 to 2.17.0. Notable upstream changes for router deployments: the SIWX extension'snonce/issuedAt/expirationTimeare now excluded from client-echo validation (fixes spuriousextension_echo_mismatchon.siwx()routes, since those fields regenerate per 402), hardened wildcard route/network pattern matching, andx402ResourceServer.initialize()now fail-fasts on scheme/facilitator capability mismatches (surfaced through the router's existingx402InitErrordegraded-mode path, not a crash).
v1.15.0
Minor Changes
- 83cf6a5: Republish of 1.14.0. The
1.14.0and1.14.1version slots on npm were burned by mid-review canary builds of PR #300 (published before the review fixes landed) and should not be used. This release contains no code changes over the repo's 1.14.0 — it is the canonical release of the SolanaGET /supportedenrichment migration.
v1.13.0
Minor Changes
-
0e27642: Developer/agent-experience audit fixes: real compile-time builder safety, typed registration errors, and doc corrections.
Builder invariants are now compile-time errors. The
RouteBuilderphantom generics previously only enforced "pick an auth mode before.handler()"; every other documented mutual-exclusion rule compiled clean and threw at module-import time. The builder now tracks identity mode (IdentMode:'none' | 'siwx' | 'apiKey' | 'open') and pricing mode (BillingModegains'exact'), so all of these are TypeScript errors with readableRouteError<'…'>messages, matching the runtime throws: repeat pricing calls (.paid().upTo()),.unprotected()combined with anything,.siwx()+.apiKey(),.siwx()+.metered(), and.stream()off.metered(). Type-level regression tests live intests/builder.test-d.ts, run by vitest's typecheck pass. Code that compiled before but threw at import time may now failtsc— that's the point; runtime behavior is unchanged, with one fix below.Fix:
.unprotected().apiKey(...)no longer silently ignored.unprotected()..apiKey()was missing the mutual-exclusion guard every sibling method has; the combination now throws at registration (and fails to compile), in both call orders.New:
RouteDefinitionError. All registration-time builder throws (invalid combos, malformed prices, missing protocol config) are now instances of the exportedRouteDefinitionError(with a.routefield) instead of plainError— the registration-time sibling ofRouterConfigError. Messages are unchanged.Deprecated:
.wellKnown()//.well-known/x402as a discovery surface. The handler keeps working — existing deployments and legacy x402-native clients are unaffected — but it is no longer recommended:ServiceRouter.wellKnown()carries an@deprecatedJSDoc tag, the README no longer suggests mounting it, the example apps no longer mount it at all, and therouter.notFound()404 body'sdiscoveryhint now lists onlyopenapiandllmsTxt(thewellKnownfield was removed). Recommended discovery surfaces are/openapi.jsonand/llms.txt.Docs. Fixed contradictory discovery paths in shipped doc comments (
/api/openapiand.well-known/agentcash→ the real/openapi.json); README documents the recommended discovery route files (openapi(),llmsTxt()), adds.method('GET')to the health example (the exported const name never affects the advertised discovery verb), documents the 402 challenge shape per auth mode (header-only for payment routes, JSON body for SIWX), theX-Agent-IdentityDID-auth header,.upTo()'sCHARGE_OVER_CAPbehavior, the init-time facilitator fetch, when the body is validated relative to the 402 challenge, and a local-dev MPP keypair recipe.Internal. De-duplicated
getConfiguredX402Accepts(config/schema now imports the canonical copy), protocol header detection (hasX402Payment/hasMppPaymentshared betweendetectProtocoland the strategies), and the verbatim static/dynamic paid-flow prefix (newrunPaidPreamble/runPaidVerifyinpipeline/flows/paid-preamble.ts). -
3586d3e: Migrate to mppx 0.8.x (TIP-1034 sessions) and viem ≥2.54.
mppx ^0.6.16 → ^0.8.5: MPP session challenges are now TIP-1034 reserve-precompile sessions (sessionProtocol: "v2", escrow0x4d5050…) — the format current clients expect. TheagentcashCLI ≥0.16 (mppx 0.8.x) can now open sessions against.metered()routes; on the old server it failed session negotiation entirely. One-shot MPP charge, x402 (exact/upto), SIWX, and entitlement replay are wire-unchanged.viem ^2.47.6 → ^2.54.0: mppx 0.8.3+ requires viem ≥2.54 (Tempo transfer call builders moved to the two-argument convention); pnpm silently satisfies the peer range with the host copy, so the router's own floor must be ≥2.54 or every non-zero Tempo charge fails at credential verify.Compatibility notes:
- Clients on mppx <0.7 (e.g.
agentcashCLI ≤0.15) can no longer open MPP sessions against the router — they sign the legacy v1 flow against the v2 precompile and revert. Those CLI versions also fail one-shot MPP charge due to a client-side response-clone bug fixed in newer releases. x402 routes are unaffected for all clients. - Fee-sponsored (gas-sponsored) flows now pass mppx's sponsor policy checks (0.6.16 rejected current clients' fee budgets outright). Sponsorship requires the
MPP_FEE_PAYER_KEYaccount to hold the Tempo fee token (pathUSD) — with an unfunded sponsor, verification fails at broadcast withinsufficient funds for gas.
New options adopted from the 0.6.17→0.8.5 changelog review:
mpp.session.settlementSchedule— server-owned automatic settlement cadence for session channels ({ units?, amount?, intervalMs? }; whichever threshold trips first). Omitted, channels settle only on client close, as before.mpp.feePayerPolicy— partial override of mppx's sponsor fee-budget ceilings (maxGas,maxFeePerGas,maxPriorityFeePerGas,maxTotalFee,maxValidityWindowSeconds) for fee-sponsored charge co-signs and session open/topUp/close.- mppx
payment.failedserver events are now forwarded toplugin.onAlert(levelwarn, orerrorfor status ≥500, with method/error-type/hint/payer metadata) — previously these details only appeared in mppx's ownconsole.error. - Tempo chain config now imports from
viem/tempo/chains, the canonical entrypoint mppx itself uses.
Internal: mppx stopped exporting the SSE
SessionControllertype from a public subpath; the router now declares the structural equivalent locally. The middleware contract (charge/session → 402 challenge | 200 withReceipt),Credential.fromRequest, session credential actions (open/topUp/voucher/close), and theStore.upstashatomic-store adapter are all unchanged. - Clients on mppx <0.7 (e.g.
v1.12.0
Minor Changes
- 5b5c8ca: Add optional
emailtodiscovery.contact. It is published verbatim in the generated OpenAPIinfo.contact.email, letting origins expose a contact address for ownership verification, user contact, and merchant-page customization (e.g. on Poncho). Omit it to keep the existing behavior.
v1.11.0
Minor Changes
- 2e826be: Add
mpp.settleBeforeHandleron.paid()/.mpp()for per-route eager MPP transaction (pull) settlement before the handler runs. x402 on the same route is unaffected.