@agentcash/router 1.10.6 → 1.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -542,7 +542,8 @@ var HEADERS = {
542
542
  X402_PAYMENT_REQUIRED: "PAYMENT-REQUIRED",
543
543
  X402_PAYMENT_RESPONSE: "PAYMENT-RESPONSE",
544
544
  MPP_PAYMENT_RECEIPT: "Payment-Receipt",
545
- REQUEST_ID: "X-Request-ID"
545
+ REQUEST_ID: "X-Request-ID",
546
+ AGENT_IDENTITY: "X-Agent-Identity"
546
547
  };
547
548
  var AUTH_SCHEME = {
548
549
  BEARER: "Bearer ",
@@ -677,6 +678,7 @@ function firePaymentSettled(ctx, event) {
677
678
  }
678
679
  function firePluginResponse(ctx, response, requestBody, responseBody, failure) {
679
680
  attachRequestId(response, ctx.meta.requestId);
681
+ const error = response.status >= 400 && response.status !== 402 ? buildErrorEvent(ctx, response, failure) : void 0;
680
682
  firePluginHook(ctx.deps.plugin, "onResponse", ctx.pluginCtx, {
681
683
  statusCode: response.status,
682
684
  statusText: response.statusText,
@@ -684,10 +686,10 @@ function firePluginResponse(ctx, response, requestBody, responseBody, failure) {
684
686
  contentType: response.headers.get("content-type"),
685
687
  headers: Object.fromEntries(response.headers.entries()),
686
688
  requestBody,
687
- responseBody
689
+ responseBody,
690
+ error
688
691
  });
689
- if (response.status >= 400 && response.status !== 402) {
690
- const error = buildErrorEvent(ctx, response, failure);
692
+ if (error) {
691
693
  if (response.status >= 500) logRouterFailure(error);
692
694
  firePluginHook(ctx.deps.plugin, "onError", ctx.pluginCtx, error);
693
695
  }
@@ -860,6 +862,9 @@ async function runValidate(ctx, body) {
860
862
  // src/pipeline/flows/static/static-invoke.ts
861
863
  var import_server4 = require("next/server");
862
864
 
865
+ // src/auth/agent-identity.ts
866
+ var import_did_auth_challenge = require("did-auth-challenge");
867
+
863
868
  // src/types.ts
864
869
  var HttpError = class extends Error {
865
870
  constructor(message, status) {
@@ -869,18 +874,87 @@ var HttpError = class extends Error {
869
874
  }
870
875
  };
871
876
 
872
- // src/pipeline/flows/static/static-invoke.ts
873
- function invokePaidStatic(ctx, wallet, account, body, payment) {
874
- return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment));
877
+ // src/auth/agent-identity.ts
878
+ var PROTOCOL_VERSION = 1;
879
+ function base64urlEncode(value) {
880
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
881
+ }
882
+ function base64urlDecode(value) {
883
+ return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
884
+ }
885
+ function challengeBindings(request) {
886
+ const url = new URL(request.url);
887
+ return { domain: url.hostname, route: url.pathname };
888
+ }
889
+ async function buildAgentIdentityChallengeHeader(request, nonceStore) {
890
+ const bindings = challengeBindings(request);
891
+ const challenge = await (0, import_did_auth_challenge.createChallenge)(bindings, { nonceStore });
892
+ return base64urlEncode({
893
+ v: PROTOCOL_VERSION,
894
+ challenge
895
+ });
896
+ }
897
+ async function attachAgentIdentityChallenge(response, request, nonceStore) {
898
+ response.headers.set(
899
+ HEADERS.AGENT_IDENTITY,
900
+ await buildAgentIdentityChallengeHeader(request, nonceStore)
901
+ );
902
+ }
903
+ async function resolveActor(request, nonceStore) {
904
+ const header = request.headers.get(HEADERS.AGENT_IDENTITY);
905
+ if (!header) return null;
906
+ let envelope;
907
+ try {
908
+ envelope = base64urlDecode(header);
909
+ } catch {
910
+ throw new HttpError("Malformed X-Agent-Identity header", 401);
911
+ }
912
+ if (envelope.v !== PROTOCOL_VERSION || !envelope.challenge || !envelope.did || !envelope.signature) {
913
+ throw new HttpError("Invalid X-Agent-Identity proof", 401);
914
+ }
915
+ const bindings = challengeBindings(request);
916
+ try {
917
+ await (0, import_did_auth_challenge.verifySignature)({
918
+ did: envelope.did,
919
+ authenticationKey: envelope.authenticationKey,
920
+ challenge: envelope.challenge,
921
+ signature: envelope.signature,
922
+ options: {
923
+ nonceStore,
924
+ expected: bindings
925
+ }
926
+ });
927
+ } catch (err) {
928
+ const message = (0, import_did_auth_challenge.isIdentityError)(err) ? err.code : "Invalid agent identity";
929
+ throw new HttpError(message, 401);
930
+ }
931
+ return envelope.did;
875
932
  }
876
- function invokeUnauthed(ctx, wallet, account, body) {
877
- const base = buildHandlerCtx(ctx, wallet, account, body, null);
933
+
934
+ // src/pipeline/flows/static/static-invoke.ts
935
+ async function invokePaidStatic(ctx, wallet, account, body, payment) {
936
+ const actorResult = await resolveActorForHandler(ctx);
937
+ if (actorResult.kind === "error") return actorResult.result;
938
+ return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment, actorResult.actor));
939
+ }
940
+ async function invokeUnauthed(ctx, wallet, account, body) {
941
+ const actorResult = await resolveActorForHandler(ctx);
942
+ if (actorResult.kind === "error") return actorResult.result;
943
+ const base = buildHandlerCtx(ctx, wallet, account, body, null, actorResult.actor);
878
944
  if (ctx.routeEntry.billing !== "upto") return runHandler(ctx, base);
879
945
  const uptoCtx = { ...base, charge: async () => {
880
946
  } };
881
947
  return runHandler(ctx, uptoCtx);
882
948
  }
883
- function buildHandlerCtx(ctx, wallet, account, body, payment) {
949
+ async function resolveActorForHandler(ctx) {
950
+ try {
951
+ const actor = await resolveActor(ctx.request, ctx.deps.agentIdentityNonceStore);
952
+ return { kind: "ok", actor };
953
+ } catch (error) {
954
+ return { kind: "error", result: errorResult(error) };
955
+ }
956
+ }
957
+ function buildHandlerCtx(ctx, wallet, account, body, payment, actor) {
884
958
  return {
885
959
  body,
886
960
  query: ctx.query,
@@ -888,6 +962,7 @@ function buildHandlerCtx(ctx, wallet, account, body, payment) {
888
962
  requestId: ctx.meta.requestId,
889
963
  route: ctx.routeEntry.key,
890
964
  wallet,
965
+ actor,
891
966
  payment,
892
967
  account,
893
968
  alert: ctx.report,
@@ -2012,7 +2087,7 @@ function tagBareDecimalAsDollars(amount) {
2012
2087
  }
2013
2088
 
2014
2089
  // src/protocols/x402/verify.ts
2015
- var import_types2 = require("@x402/core/types");
2090
+ var import_types3 = require("@x402/core/types");
2016
2091
  async function verifyX402Payment(opts) {
2017
2092
  const { server, request, price, accepts, report } = opts;
2018
2093
  const payment = await readPaymentPayload(request);
@@ -2038,7 +2113,7 @@ async function verifyX402Payment(opts) {
2038
2113
  try {
2039
2114
  verify = await server.verifyPayment(payload, matching);
2040
2115
  } catch (err) {
2041
- if (err instanceof import_types2.VerifyError && err.statusCode >= 400 && err.statusCode < 500) {
2116
+ if (err instanceof import_types3.VerifyError && err.statusCode >= 400 && err.statusCode < 500) {
2042
2117
  return invalidPaymentVerification({
2043
2118
  reason: err.invalidReason ?? "verify_error",
2044
2119
  ...err.invalidMessage ? { message: err.invalidMessage } : {},
@@ -2611,6 +2686,7 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
2611
2686
  }
2612
2687
  }
2613
2688
  }
2689
+ await attachAgentIdentityChallenge(response, ctx.request, ctx.deps.agentIdentityNonceStore);
2614
2690
  firePluginResponse(ctx, response);
2615
2691
  return response;
2616
2692
  }
@@ -2745,7 +2821,7 @@ function createChargeContext(args) {
2745
2821
 
2746
2822
  // src/pipeline/flows/dynamic/dynamic-invoke/shared.ts
2747
2823
  var import_server7 = require("next/server");
2748
- function buildBaseHandlerCtx(ctx, wallet, account, body, payment) {
2824
+ function buildBaseHandlerCtx(ctx, wallet, account, body, payment, actor = null) {
2749
2825
  return {
2750
2826
  body,
2751
2827
  query: ctx.query,
@@ -2753,12 +2829,21 @@ function buildBaseHandlerCtx(ctx, wallet, account, body, payment) {
2753
2829
  requestId: ctx.meta.requestId,
2754
2830
  route: ctx.routeEntry.key,
2755
2831
  wallet,
2832
+ actor,
2756
2833
  payment,
2757
2834
  account,
2758
2835
  alert: ctx.report,
2759
2836
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
2760
2837
  };
2761
2838
  }
2839
+ async function resolveActorOrError(ctx) {
2840
+ try {
2841
+ const actor = await resolveActor(ctx.request, ctx.deps.agentIdentityNonceStore);
2842
+ return { actor };
2843
+ } catch (error) {
2844
+ return errorResult2(error);
2845
+ }
2846
+ }
2762
2847
  function toResponse(rawResult) {
2763
2848
  return rawResult instanceof Response ? rawResult : import_server7.NextResponse.json(rawResult);
2764
2849
  }
@@ -2782,12 +2867,21 @@ function isThenable2(value) {
2782
2867
 
2783
2868
  // src/pipeline/flows/dynamic/dynamic-invoke/metered-invoke.ts
2784
2869
  async function invokeMetered(ctx, wallet, account, body, payment) {
2870
+ const actorResult = await resolveActorOrError(ctx);
2871
+ if ("response" in actorResult) return actorResult;
2785
2872
  const chargeContext = ctx.routeEntry.streaming ? createChargeContext({
2786
2873
  tickCost: ctx.routeEntry.tickCost,
2787
2874
  maxPrice: ctx.routeEntry.maxPrice,
2788
2875
  route: ctx.routeEntry.key
2789
2876
  }) : null;
2790
- const baseHandlerCtx = buildBaseHandlerCtx(ctx, wallet, account, body, payment);
2877
+ const baseHandlerCtx = buildBaseHandlerCtx(
2878
+ ctx,
2879
+ wallet,
2880
+ account,
2881
+ body,
2882
+ payment,
2883
+ actorResult.actor
2884
+ );
2791
2885
  const handlerCtx = chargeContext !== null ? { ...baseHandlerCtx, charge: chargeContext.charge } : baseHandlerCtx;
2792
2886
  let returned;
2793
2887
  try {
@@ -2846,12 +2940,14 @@ function createUptoChargeContext(args) {
2846
2940
 
2847
2941
  // src/pipeline/flows/dynamic/dynamic-invoke/upto-invoke.ts
2848
2942
  async function invokeUpto(ctx, wallet, account, body, payment) {
2943
+ const actorResult = await resolveActorOrError(ctx);
2944
+ if ("response" in actorResult) return actorResult;
2849
2945
  const uptoCtx = createUptoChargeContext({
2850
2946
  maxPrice: ctx.routeEntry.maxPrice,
2851
2947
  route: ctx.routeEntry.key
2852
2948
  });
2853
2949
  const handlerCtx = {
2854
- ...buildBaseHandlerCtx(ctx, wallet, account, body, payment),
2950
+ ...buildBaseHandlerCtx(ctx, wallet, account, body, payment, actorResult.actor),
2855
2951
  charge: uptoCtx.charge
2856
2952
  };
2857
2953
  let returned;
@@ -3384,6 +3480,45 @@ async function createKvMppStore(kv, options) {
3384
3480
  });
3385
3481
  }
3386
3482
 
3483
+ // src/kv-store/agent-identity-nonce.ts
3484
+ var import_did_auth_challenge2 = require("did-auth-challenge");
3485
+ function createKvAgentIdentityNonceStore(kv, options) {
3486
+ const prefix = options?.prefix ?? "actor:nonce:";
3487
+ return {
3488
+ async add(nonce, expiresAt, challengeJson) {
3489
+ const ttl = Math.max(1, Math.ceil((expiresAt.getTime() - Date.now()) / 1e3));
3490
+ const record = { challengeJson, expiresAt: expiresAt.getTime() };
3491
+ await kv.setNxEx(`${prefix}${nonce}`, record, ttl);
3492
+ },
3493
+ async get(nonce) {
3494
+ const raw = await kv.get(`${prefix}${nonce}`);
3495
+ if (!raw) return null;
3496
+ const record = raw;
3497
+ if (Date.now() > record.expiresAt) {
3498
+ await kv.del(`${prefix}${nonce}`);
3499
+ return null;
3500
+ }
3501
+ return record;
3502
+ },
3503
+ async has(nonce) {
3504
+ return await this.get(nonce) !== null;
3505
+ },
3506
+ async consume(nonce) {
3507
+ return kv.update(`${prefix}${nonce}`, (current) => {
3508
+ if (!current) return { op: "noop", result: null };
3509
+ const record = current;
3510
+ if (Date.now() > record.expiresAt) {
3511
+ return { op: "delete", result: null };
3512
+ }
3513
+ return { op: "delete", result: record };
3514
+ });
3515
+ }
3516
+ };
3517
+ }
3518
+ function createAgentIdentityNonceStore(kv) {
3519
+ return kv ? createKvAgentIdentityNonceStore(kv) : new import_did_auth_challenge2.NonceStore();
3520
+ }
3521
+
3387
3522
  // src/protocols/mpp/siwx-mode.ts
3388
3523
  var import_mppx3 = require("mppx");
3389
3524
  async function verifyMppSiwx(request, mppx) {
@@ -3510,6 +3645,7 @@ async function buildSiwxChallenge(ctx) {
3510
3645
  } catch {
3511
3646
  }
3512
3647
  }
3648
+ await attachAgentIdentityChallenge(response, request, deps.agentIdentityNonceStore);
3513
3649
  firePluginResponse(ctx, response);
3514
3650
  return response;
3515
3651
  }
@@ -4526,7 +4662,7 @@ function createNotFoundHandler(baseUrl) {
4526
4662
  headers: {
4527
4663
  "Access-Control-Allow-Origin": "*",
4528
4664
  "Access-Control-Allow-Methods": "GET, POST, DELETE, PUT, PATCH, OPTIONS",
4529
- "Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key, SIGN-IN-WITH-X"
4665
+ "Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key, SIGN-IN-WITH-X, X-Agent-Identity"
4530
4666
  }
4531
4667
  }
4532
4668
  );
@@ -5140,6 +5276,7 @@ function createRouter(config) {
5140
5276
  const registry = new RouteRegistry();
5141
5277
  const kvStore = resolveKvStore(config.kvStore);
5142
5278
  const nonceStore = kvStore ? createKvNonceStore(kvStore) : new MemoryNonceStore();
5279
+ const agentIdentityNonceStore = createAgentIdentityNonceStore(kvStore);
5143
5280
  const entitlementStore = kvStore ? createKvEntitlementStore(kvStore) : new MemoryEntitlementStore();
5144
5281
  const network = config.network ?? BASE_MAINNET_NETWORK;
5145
5282
  const x402Accepts = getConfiguredX402Accepts(config);
@@ -5174,6 +5311,7 @@ function createRouter(config) {
5174
5311
  initPromise: Promise.resolve(),
5175
5312
  plugin: config.plugin,
5176
5313
  nonceStore,
5314
+ agentIdentityNonceStore,
5177
5315
  entitlementStore,
5178
5316
  payeeAddress: config.payeeAddress ?? "",
5179
5317
  mppRecipient: config.mpp?.recipient ?? config.payeeAddress,
package/dist/index.d.cts CHANGED
@@ -4,6 +4,7 @@ import { ZodType } from 'zod';
4
4
  import { PaymentRequirements, PaymentRequired, VerifyResponse, SettleResponse, Network } from '@x402/core/types';
5
5
  import * as viem from 'viem';
6
6
  import { Transport } from 'mppx/server';
7
+ import { NonceStoreInterface } from 'did-auth-challenge';
7
8
 
8
9
  interface KvStore {
9
10
  get(key: string): Promise<unknown>;
@@ -79,6 +80,8 @@ interface ResponseMeta {
79
80
  requestBody?: unknown;
80
81
  /** Handler return value or structured router-generated error body. */
81
82
  responseBody?: unknown;
83
+ /** Rich error details for non-402 failure responses. */
84
+ error?: ErrorEvent;
82
85
  }
83
86
  interface ErrorEvent {
84
87
  status: number;
@@ -320,6 +323,8 @@ interface HandlerContext<TBody = undefined, TQuery = undefined> {
320
323
  requestId: string;
321
324
  route: string;
322
325
  wallet: string | null;
326
+ /** Optional DID from `X-Agent-Identity` proof. Null when the client omits identity. */
327
+ actor: string | null;
323
328
  payment: HandlerPaymentContext | null;
324
329
  account: unknown;
325
330
  alert: AlertFn;
@@ -498,6 +503,7 @@ interface RouterDeps {
498
503
  mppInitError?: string;
499
504
  plugin?: RouterPlugin;
500
505
  nonceStore: NonceStore;
506
+ agentIdentityNonceStore: NonceStoreInterface;
501
507
  entitlementStore: EntitlementStore;
502
508
  payeeAddress: string;
503
509
  mppRecipient?: string;
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { ZodType } from 'zod';
4
4
  import { PaymentRequirements, PaymentRequired, VerifyResponse, SettleResponse, Network } from '@x402/core/types';
5
5
  import * as viem from 'viem';
6
6
  import { Transport } from 'mppx/server';
7
+ import { NonceStoreInterface } from 'did-auth-challenge';
7
8
 
8
9
  interface KvStore {
9
10
  get(key: string): Promise<unknown>;
@@ -79,6 +80,8 @@ interface ResponseMeta {
79
80
  requestBody?: unknown;
80
81
  /** Handler return value or structured router-generated error body. */
81
82
  responseBody?: unknown;
83
+ /** Rich error details for non-402 failure responses. */
84
+ error?: ErrorEvent;
82
85
  }
83
86
  interface ErrorEvent {
84
87
  status: number;
@@ -320,6 +323,8 @@ interface HandlerContext<TBody = undefined, TQuery = undefined> {
320
323
  requestId: string;
321
324
  route: string;
322
325
  wallet: string | null;
326
+ /** Optional DID from `X-Agent-Identity` proof. Null when the client omits identity. */
327
+ actor: string | null;
323
328
  payment: HandlerPaymentContext | null;
324
329
  account: unknown;
325
330
  alert: AlertFn;
@@ -498,6 +503,7 @@ interface RouterDeps {
498
503
  mppInitError?: string;
499
504
  plugin?: RouterPlugin;
500
505
  nonceStore: NonceStore;
506
+ agentIdentityNonceStore: NonceStoreInterface;
501
507
  entitlementStore: EntitlementStore;
502
508
  payeeAddress: string;
503
509
  mppRecipient?: string;
package/dist/index.js CHANGED
@@ -500,7 +500,8 @@ var HEADERS = {
500
500
  X402_PAYMENT_REQUIRED: "PAYMENT-REQUIRED",
501
501
  X402_PAYMENT_RESPONSE: "PAYMENT-RESPONSE",
502
502
  MPP_PAYMENT_RECEIPT: "Payment-Receipt",
503
- REQUEST_ID: "X-Request-ID"
503
+ REQUEST_ID: "X-Request-ID",
504
+ AGENT_IDENTITY: "X-Agent-Identity"
504
505
  };
505
506
  var AUTH_SCHEME = {
506
507
  BEARER: "Bearer ",
@@ -635,6 +636,7 @@ function firePaymentSettled(ctx, event) {
635
636
  }
636
637
  function firePluginResponse(ctx, response, requestBody, responseBody, failure) {
637
638
  attachRequestId(response, ctx.meta.requestId);
639
+ const error = response.status >= 400 && response.status !== 402 ? buildErrorEvent(ctx, response, failure) : void 0;
638
640
  firePluginHook(ctx.deps.plugin, "onResponse", ctx.pluginCtx, {
639
641
  statusCode: response.status,
640
642
  statusText: response.statusText,
@@ -642,10 +644,10 @@ function firePluginResponse(ctx, response, requestBody, responseBody, failure) {
642
644
  contentType: response.headers.get("content-type"),
643
645
  headers: Object.fromEntries(response.headers.entries()),
644
646
  requestBody,
645
- responseBody
647
+ responseBody,
648
+ error
646
649
  });
647
- if (response.status >= 400 && response.status !== 402) {
648
- const error = buildErrorEvent(ctx, response, failure);
650
+ if (error) {
649
651
  if (response.status >= 500) logRouterFailure(error);
650
652
  firePluginHook(ctx.deps.plugin, "onError", ctx.pluginCtx, error);
651
653
  }
@@ -818,6 +820,13 @@ async function runValidate(ctx, body) {
818
820
  // src/pipeline/flows/static/static-invoke.ts
819
821
  import { NextResponse as NextResponse4 } from "next/server";
820
822
 
823
+ // src/auth/agent-identity.ts
824
+ import {
825
+ createChallenge,
826
+ isIdentityError,
827
+ verifySignature
828
+ } from "did-auth-challenge";
829
+
821
830
  // src/types.ts
822
831
  var HttpError = class extends Error {
823
832
  constructor(message, status) {
@@ -827,18 +836,87 @@ var HttpError = class extends Error {
827
836
  }
828
837
  };
829
838
 
830
- // src/pipeline/flows/static/static-invoke.ts
831
- function invokePaidStatic(ctx, wallet, account, body, payment) {
832
- return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment));
839
+ // src/auth/agent-identity.ts
840
+ var PROTOCOL_VERSION = 1;
841
+ function base64urlEncode(value) {
842
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
843
+ }
844
+ function base64urlDecode(value) {
845
+ return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
846
+ }
847
+ function challengeBindings(request) {
848
+ const url = new URL(request.url);
849
+ return { domain: url.hostname, route: url.pathname };
850
+ }
851
+ async function buildAgentIdentityChallengeHeader(request, nonceStore) {
852
+ const bindings = challengeBindings(request);
853
+ const challenge = await createChallenge(bindings, { nonceStore });
854
+ return base64urlEncode({
855
+ v: PROTOCOL_VERSION,
856
+ challenge
857
+ });
858
+ }
859
+ async function attachAgentIdentityChallenge(response, request, nonceStore) {
860
+ response.headers.set(
861
+ HEADERS.AGENT_IDENTITY,
862
+ await buildAgentIdentityChallengeHeader(request, nonceStore)
863
+ );
864
+ }
865
+ async function resolveActor(request, nonceStore) {
866
+ const header = request.headers.get(HEADERS.AGENT_IDENTITY);
867
+ if (!header) return null;
868
+ let envelope;
869
+ try {
870
+ envelope = base64urlDecode(header);
871
+ } catch {
872
+ throw new HttpError("Malformed X-Agent-Identity header", 401);
873
+ }
874
+ if (envelope.v !== PROTOCOL_VERSION || !envelope.challenge || !envelope.did || !envelope.signature) {
875
+ throw new HttpError("Invalid X-Agent-Identity proof", 401);
876
+ }
877
+ const bindings = challengeBindings(request);
878
+ try {
879
+ await verifySignature({
880
+ did: envelope.did,
881
+ authenticationKey: envelope.authenticationKey,
882
+ challenge: envelope.challenge,
883
+ signature: envelope.signature,
884
+ options: {
885
+ nonceStore,
886
+ expected: bindings
887
+ }
888
+ });
889
+ } catch (err) {
890
+ const message = isIdentityError(err) ? err.code : "Invalid agent identity";
891
+ throw new HttpError(message, 401);
892
+ }
893
+ return envelope.did;
833
894
  }
834
- function invokeUnauthed(ctx, wallet, account, body) {
835
- const base = buildHandlerCtx(ctx, wallet, account, body, null);
895
+
896
+ // src/pipeline/flows/static/static-invoke.ts
897
+ async function invokePaidStatic(ctx, wallet, account, body, payment) {
898
+ const actorResult = await resolveActorForHandler(ctx);
899
+ if (actorResult.kind === "error") return actorResult.result;
900
+ return runHandler(ctx, buildHandlerCtx(ctx, wallet, account, body, payment, actorResult.actor));
901
+ }
902
+ async function invokeUnauthed(ctx, wallet, account, body) {
903
+ const actorResult = await resolveActorForHandler(ctx);
904
+ if (actorResult.kind === "error") return actorResult.result;
905
+ const base = buildHandlerCtx(ctx, wallet, account, body, null, actorResult.actor);
836
906
  if (ctx.routeEntry.billing !== "upto") return runHandler(ctx, base);
837
907
  const uptoCtx = { ...base, charge: async () => {
838
908
  } };
839
909
  return runHandler(ctx, uptoCtx);
840
910
  }
841
- function buildHandlerCtx(ctx, wallet, account, body, payment) {
911
+ async function resolveActorForHandler(ctx) {
912
+ try {
913
+ const actor = await resolveActor(ctx.request, ctx.deps.agentIdentityNonceStore);
914
+ return { kind: "ok", actor };
915
+ } catch (error) {
916
+ return { kind: "error", result: errorResult(error) };
917
+ }
918
+ }
919
+ function buildHandlerCtx(ctx, wallet, account, body, payment, actor) {
842
920
  return {
843
921
  body,
844
922
  query: ctx.query,
@@ -846,6 +924,7 @@ function buildHandlerCtx(ctx, wallet, account, body, payment) {
846
924
  requestId: ctx.meta.requestId,
847
925
  route: ctx.routeEntry.key,
848
926
  wallet,
927
+ actor,
849
928
  payment,
850
929
  account,
851
930
  alert: ctx.report,
@@ -2569,6 +2648,7 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
2569
2648
  }
2570
2649
  }
2571
2650
  }
2651
+ await attachAgentIdentityChallenge(response, ctx.request, ctx.deps.agentIdentityNonceStore);
2572
2652
  firePluginResponse(ctx, response);
2573
2653
  return response;
2574
2654
  }
@@ -2703,7 +2783,7 @@ function createChargeContext(args) {
2703
2783
 
2704
2784
  // src/pipeline/flows/dynamic/dynamic-invoke/shared.ts
2705
2785
  import { NextResponse as NextResponse7 } from "next/server";
2706
- function buildBaseHandlerCtx(ctx, wallet, account, body, payment) {
2786
+ function buildBaseHandlerCtx(ctx, wallet, account, body, payment, actor = null) {
2707
2787
  return {
2708
2788
  body,
2709
2789
  query: ctx.query,
@@ -2711,12 +2791,21 @@ function buildBaseHandlerCtx(ctx, wallet, account, body, payment) {
2711
2791
  requestId: ctx.meta.requestId,
2712
2792
  route: ctx.routeEntry.key,
2713
2793
  wallet,
2794
+ actor,
2714
2795
  payment,
2715
2796
  account,
2716
2797
  alert: ctx.report,
2717
2798
  setVerifiedWallet: (addr) => ctx.pluginCtx.setVerifiedWallet(addr)
2718
2799
  };
2719
2800
  }
2801
+ async function resolveActorOrError(ctx) {
2802
+ try {
2803
+ const actor = await resolveActor(ctx.request, ctx.deps.agentIdentityNonceStore);
2804
+ return { actor };
2805
+ } catch (error) {
2806
+ return errorResult2(error);
2807
+ }
2808
+ }
2720
2809
  function toResponse(rawResult) {
2721
2810
  return rawResult instanceof Response ? rawResult : NextResponse7.json(rawResult);
2722
2811
  }
@@ -2740,12 +2829,21 @@ function isThenable2(value) {
2740
2829
 
2741
2830
  // src/pipeline/flows/dynamic/dynamic-invoke/metered-invoke.ts
2742
2831
  async function invokeMetered(ctx, wallet, account, body, payment) {
2832
+ const actorResult = await resolveActorOrError(ctx);
2833
+ if ("response" in actorResult) return actorResult;
2743
2834
  const chargeContext = ctx.routeEntry.streaming ? createChargeContext({
2744
2835
  tickCost: ctx.routeEntry.tickCost,
2745
2836
  maxPrice: ctx.routeEntry.maxPrice,
2746
2837
  route: ctx.routeEntry.key
2747
2838
  }) : null;
2748
- const baseHandlerCtx = buildBaseHandlerCtx(ctx, wallet, account, body, payment);
2839
+ const baseHandlerCtx = buildBaseHandlerCtx(
2840
+ ctx,
2841
+ wallet,
2842
+ account,
2843
+ body,
2844
+ payment,
2845
+ actorResult.actor
2846
+ );
2749
2847
  const handlerCtx = chargeContext !== null ? { ...baseHandlerCtx, charge: chargeContext.charge } : baseHandlerCtx;
2750
2848
  let returned;
2751
2849
  try {
@@ -2804,12 +2902,14 @@ function createUptoChargeContext(args) {
2804
2902
 
2805
2903
  // src/pipeline/flows/dynamic/dynamic-invoke/upto-invoke.ts
2806
2904
  async function invokeUpto(ctx, wallet, account, body, payment) {
2905
+ const actorResult = await resolveActorOrError(ctx);
2906
+ if ("response" in actorResult) return actorResult;
2807
2907
  const uptoCtx = createUptoChargeContext({
2808
2908
  maxPrice: ctx.routeEntry.maxPrice,
2809
2909
  route: ctx.routeEntry.key
2810
2910
  });
2811
2911
  const handlerCtx = {
2812
- ...buildBaseHandlerCtx(ctx, wallet, account, body, payment),
2912
+ ...buildBaseHandlerCtx(ctx, wallet, account, body, payment, actorResult.actor),
2813
2913
  charge: uptoCtx.charge
2814
2914
  };
2815
2915
  let returned;
@@ -3342,6 +3442,45 @@ async function createKvMppStore(kv, options) {
3342
3442
  });
3343
3443
  }
3344
3444
 
3445
+ // src/kv-store/agent-identity-nonce.ts
3446
+ import { NonceStore } from "did-auth-challenge";
3447
+ function createKvAgentIdentityNonceStore(kv, options) {
3448
+ const prefix = options?.prefix ?? "actor:nonce:";
3449
+ return {
3450
+ async add(nonce, expiresAt, challengeJson) {
3451
+ const ttl = Math.max(1, Math.ceil((expiresAt.getTime() - Date.now()) / 1e3));
3452
+ const record = { challengeJson, expiresAt: expiresAt.getTime() };
3453
+ await kv.setNxEx(`${prefix}${nonce}`, record, ttl);
3454
+ },
3455
+ async get(nonce) {
3456
+ const raw = await kv.get(`${prefix}${nonce}`);
3457
+ if (!raw) return null;
3458
+ const record = raw;
3459
+ if (Date.now() > record.expiresAt) {
3460
+ await kv.del(`${prefix}${nonce}`);
3461
+ return null;
3462
+ }
3463
+ return record;
3464
+ },
3465
+ async has(nonce) {
3466
+ return await this.get(nonce) !== null;
3467
+ },
3468
+ async consume(nonce) {
3469
+ return kv.update(`${prefix}${nonce}`, (current) => {
3470
+ if (!current) return { op: "noop", result: null };
3471
+ const record = current;
3472
+ if (Date.now() > record.expiresAt) {
3473
+ return { op: "delete", result: null };
3474
+ }
3475
+ return { op: "delete", result: record };
3476
+ });
3477
+ }
3478
+ };
3479
+ }
3480
+ function createAgentIdentityNonceStore(kv) {
3481
+ return kv ? createKvAgentIdentityNonceStore(kv) : new NonceStore();
3482
+ }
3483
+
3345
3484
  // src/protocols/mpp/siwx-mode.ts
3346
3485
  import { Credential as Credential2 } from "mppx";
3347
3486
  async function verifyMppSiwx(request, mppx) {
@@ -3468,6 +3607,7 @@ async function buildSiwxChallenge(ctx) {
3468
3607
  } catch {
3469
3608
  }
3470
3609
  }
3610
+ await attachAgentIdentityChallenge(response, request, deps.agentIdentityNonceStore);
3471
3611
  firePluginResponse(ctx, response);
3472
3612
  return response;
3473
3613
  }
@@ -4484,7 +4624,7 @@ function createNotFoundHandler(baseUrl) {
4484
4624
  headers: {
4485
4625
  "Access-Control-Allow-Origin": "*",
4486
4626
  "Access-Control-Allow-Methods": "GET, POST, DELETE, PUT, PATCH, OPTIONS",
4487
- "Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key, SIGN-IN-WITH-X"
4627
+ "Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key, SIGN-IN-WITH-X, X-Agent-Identity"
4488
4628
  }
4489
4629
  }
4490
4630
  );
@@ -5098,6 +5238,7 @@ function createRouter(config) {
5098
5238
  const registry = new RouteRegistry();
5099
5239
  const kvStore = resolveKvStore(config.kvStore);
5100
5240
  const nonceStore = kvStore ? createKvNonceStore(kvStore) : new MemoryNonceStore();
5241
+ const agentIdentityNonceStore = createAgentIdentityNonceStore(kvStore);
5101
5242
  const entitlementStore = kvStore ? createKvEntitlementStore(kvStore) : new MemoryEntitlementStore();
5102
5243
  const network = config.network ?? BASE_MAINNET_NETWORK;
5103
5244
  const x402Accepts = getConfiguredX402Accepts(config);
@@ -5132,6 +5273,7 @@ function createRouter(config) {
5132
5273
  initPromise: Promise.resolve(),
5133
5274
  plugin: config.plugin,
5134
5275
  nonceStore,
5276
+ agentIdentityNonceStore,
5135
5277
  entitlementStore,
5136
5278
  payeeAddress: config.payeeAddress ?? "",
5137
5279
  mppRecipient: config.mpp?.recipient ?? config.payeeAddress,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.10.6",
3
+ "version": "1.10.7",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {
@@ -32,6 +32,7 @@
32
32
  "@x402/evm": "^2.13.0",
33
33
  "@x402/extensions": "^2.13.0",
34
34
  "@x402/svm": "^2.13.0",
35
+ "did-auth-challenge": "^0.0.1",
35
36
  "mppx": "^0.6.16",
36
37
  "viem": "^2.47.6",
37
38
  "zod-openapi": "^5.0.0"