@astrasyncai/verification-gateway 5.19.0 → 5.21.0

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +53 -4
  3. package/dist/adapter-interface/interface.d.mts +2 -2
  4. package/dist/adapter-interface/interface.d.ts +2 -2
  5. package/dist/adapters/express.d.mts +1 -1
  6. package/dist/adapters/express.d.ts +1 -1
  7. package/dist/adapters/express.js +65 -16
  8. package/dist/adapters/express.js.map +1 -1
  9. package/dist/adapters/express.mjs +65 -16
  10. package/dist/adapters/express.mjs.map +1 -1
  11. package/dist/adapters/mcp.d.mts +23 -1
  12. package/dist/adapters/mcp.d.ts +23 -1
  13. package/dist/adapters/mcp.js +91 -16
  14. package/dist/adapters/mcp.js.map +1 -1
  15. package/dist/adapters/mcp.mjs +91 -16
  16. package/dist/adapters/mcp.mjs.map +1 -1
  17. package/dist/adapters/nextjs.d.mts +1 -1
  18. package/dist/adapters/nextjs.d.ts +1 -1
  19. package/dist/adapters/nextjs.js +57 -11
  20. package/dist/adapters/nextjs.js.map +1 -1
  21. package/dist/adapters/nextjs.mjs +57 -11
  22. package/dist/adapters/nextjs.mjs.map +1 -1
  23. package/dist/adapters/sdk.d.mts +35 -2
  24. package/dist/adapters/sdk.d.ts +35 -2
  25. package/dist/adapters/sdk.js +110 -11
  26. package/dist/adapters/sdk.js.map +1 -1
  27. package/dist/adapters/sdk.mjs +110 -11
  28. package/dist/adapters/sdk.mjs.map +1 -1
  29. package/dist/agent/index.d.mts +1 -1
  30. package/dist/agent/index.d.ts +1 -1
  31. package/dist/agent/index.js +1 -1
  32. package/dist/agent/index.js.map +1 -1
  33. package/dist/agent/index.mjs +1 -1
  34. package/dist/agent/index.mjs.map +1 -1
  35. package/dist/bin/astrasync-claude-hook.js +50 -12
  36. package/dist/bin/astrasync-codex-hook.js +50 -12
  37. package/dist/bin/astrasync-guard.js +50 -12
  38. package/dist/bin/astrasync.js +65 -13
  39. package/dist/browser/background.js +50 -12
  40. package/dist/browser/background.js.map +1 -1
  41. package/dist/browser/background.mjs +50 -12
  42. package/dist/browser/background.mjs.map +1 -1
  43. package/dist/cli/index.js +1 -1
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/cli/index.mjs +1 -1
  46. package/dist/cli/index.mjs.map +1 -1
  47. package/dist/codex/index.js +50 -12
  48. package/dist/codex/index.js.map +1 -1
  49. package/dist/codex/index.mjs +50 -12
  50. package/dist/codex/index.mjs.map +1 -1
  51. package/dist/cursor/extension.js +50 -12
  52. package/dist/cursor/extension.js.map +1 -1
  53. package/dist/cursor/extension.mjs +50 -12
  54. package/dist/cursor/extension.mjs.map +1 -1
  55. package/dist/edge-config.d.mts +1 -1
  56. package/dist/edge-config.d.ts +1 -1
  57. package/dist/edge-config.js +1 -1
  58. package/dist/edge-config.js.map +1 -1
  59. package/dist/edge-config.mjs +1 -1
  60. package/dist/edge-config.mjs.map +1 -1
  61. package/dist/edge-core/index.d.mts +1 -1
  62. package/dist/edge-core/index.d.ts +1 -1
  63. package/dist/edge-core/index.js +49 -11
  64. package/dist/edge-core/index.js.map +1 -1
  65. package/dist/edge-core/index.mjs +49 -11
  66. package/dist/edge-core/index.mjs.map +1 -1
  67. package/dist/gateway/gateway.d.mts +2 -2
  68. package/dist/gateway/gateway.d.ts +2 -2
  69. package/dist/gateway/gateway.js +50 -12
  70. package/dist/gateway/gateway.js.map +1 -1
  71. package/dist/gateway/gateway.mjs +50 -12
  72. package/dist/gateway/gateway.mjs.map +1 -1
  73. package/dist/git-trigger/git-hooks.d.mts +1 -1
  74. package/dist/git-trigger/git-hooks.d.ts +1 -1
  75. package/dist/index.d.mts +286 -8
  76. package/dist/index.d.ts +286 -8
  77. package/dist/index.js +532 -49
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +522 -49
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/local-evaluator/evaluator.d.mts +2 -2
  82. package/dist/local-evaluator/evaluator.d.ts +2 -2
  83. package/dist/registration/index.d.mts +11 -2
  84. package/dist/registration/index.d.ts +11 -2
  85. package/dist/registration/index.js +1 -1
  86. package/dist/registration/index.js.map +1 -1
  87. package/dist/registration/index.mjs +1 -1
  88. package/dist/registration/index.mjs.map +1 -1
  89. package/dist/transport/index.d.mts +1 -1
  90. package/dist/transport/index.d.ts +1 -1
  91. package/dist/transport/index.js +1 -1
  92. package/dist/transport/index.js.map +1 -1
  93. package/dist/transport/index.mjs +1 -1
  94. package/dist/transport/index.mjs.map +1 -1
  95. package/dist/{types-BoGjyon0.d.mts → types-B4zhOAlq.d.mts} +30 -1
  96. package/dist/{types-BRz2U0Pn.d.ts → types-B_HLhZ9n.d.ts} +1 -1
  97. package/dist/{types-BfILnheI.d.mts → types-CU17R4Nr.d.mts} +11 -0
  98. package/dist/{types-BfILnheI.d.ts → types-CU17R4Nr.d.ts} +11 -0
  99. package/dist/{types-eWyZuz-3.d.ts → types-CtBXnWuV.d.ts} +30 -1
  100. package/dist/{types-DqWPkiYl.d.mts → types-DHHyXtuZ.d.mts} +30 -1
  101. package/dist/{types-DqWPkiYl.d.ts → types-DHHyXtuZ.d.ts} +30 -1
  102. package/dist/{types-CfBpm3w5.d.mts → types-Djpz-L0w.d.mts} +1 -1
  103. package/dist/ui/index.d.mts +1 -1
  104. package/dist/ui/index.d.ts +1 -1
  105. package/dist/verify.d.mts +28 -3
  106. package/dist/verify.d.ts +28 -3
  107. package/dist/verify.js +71 -11
  108. package/dist/verify.js.map +1 -1
  109. package/dist/verify.mjs +69 -11
  110. package/dist/verify.mjs.map +1 -1
  111. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,136 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
8
8
  Full architectural context for each release lives in the platform spec at
9
9
  `docs/research/adapter-architecture-technical-requirements.md`.
10
10
 
11
+ ## [5.21.0] — 2026-10-26 — endpoint changes are dashboard-only, agent commerce studio rules
12
+
13
+ ### Removed (breaking, security — shops' item 48)
14
+
15
+ - **`VerificationGatewayClient.updateEndpoint`** and the `EndpointOperationalPatch`
16
+ type. After registration, endpoint settings (checkout, catalogue, events,
17
+ skill and health check URLs) change in the dashboard only, behind the
18
+ owner's login and MFA; the platform now refuses API keys on
19
+ `PATCH /api/endpoints/:id` (403). Why: a merchant's API key lives in its
20
+ host's environment, and a host compromise must not let a leaked key point
21
+ agents at another checkout or catalogue under the merchant's verified
22
+ identity. Every dashboard change to one of these URLs alerts the owner.
23
+ - **`sendTestEvent(id, { approvalId })`.** From code the test event is
24
+ synthetic only (an `approvalId` is no longer sent; the platform answers 403
25
+ `dashboard_only` to an API key that sends one). A real held approval's
26
+ payload releases or re-drives that checkout, so it is sent from the
27
+ endpoint page in the dashboard.
28
+
29
+ ### Changed
30
+
31
+ - **The default AstraSync API base is `https://astrasync.ai/api`** (gateway
32
+ online mode, the transport's default verify URL, README examples), the same
33
+ as `verify()`'s documented base. `api.astrasync.ai` still answers; pass
34
+ `apiBaseUrl` explicitly as before if you set one.
35
+
36
+ ### Added — agent commerce studio rules
37
+
38
+ - **`createAgentCommerce({ apiBaseUrl, apiKey, counterpartyId })`** fetches
39
+ the rules you set in the dashboard (endpoint → Agent commerce) every 5
40
+ minutes and keeps the last good copy when a fetch fails: product
41
+ visibility, agent price, the image agents get, a verified-agent discount,
42
+ and card issuer offers. AstraSync stores rules only; it hosts nothing.
43
+ `report({ catalogUrl, checkoutUrl, eventsUrl })` tells the dashboard where
44
+ your kit lives — shown as suggestions you confirm; it never sets a URL.
45
+ - **`serveAgentCatalog({ commerce, products })`** serves your catalogue to
46
+ agents priced and filtered by those rules (`issuer_offer` marks a
47
+ cardholder-priced line). **`fromFeed(url)`** reads a Shopify
48
+ `products.json`, Google Merchant XML, CSV or JSON feed you already have.
49
+ - **Kits: `agentCommerce`.** The UCP and ACP kits price lines with the same
50
+ rules, so the agent's price is the price you charge. A cardholder price
51
+ applies on `X-Astra-Issuer-Program` (sent by the AstraSync bridge) and the
52
+ confirm carries `metadata.issuerProgram`; AstraSync refuses the purchase
53
+ unless the buyer is that programme's cardholder. Pass `metadata` through
54
+ in your `verifyPurchase`.
55
+ - `issuerOfferPrice` / `priceForAgent`: the shared price rule (percent off,
56
+ half up, clamped).
57
+
58
+ ## [5.20.0] — 2026-10-19 — fewer backend calls, recoverable declined holds, endpoint ops from code
59
+
60
+ ### Changed (behaviour)
61
+
62
+ - **Declined kit holds are `recoverable`.** After a `step_up.denied`,
63
+ `step_up.expired` or `step_up.lapsed` event the UCP kit's
64
+ `approval_<reason>` message has severity `recoverable` and the ACP kit's
65
+ resolution is `recoverable` (both were `requires_buyer_review`): the
66
+ session is back in `ready_for_complete` / `ready_for_payment` and payable
67
+ with no further buyer step, matching the 5.19.0 `payment_failed` fix. The
68
+ code is the signal — platforms must not auto-retry on `approval_denied`.
69
+ The held response keeps `requires_buyer_review`.
70
+ - **No decision POST on a shared verdict.** The express and MCP middlewares
71
+ no longer record a second decision when the verdict came from the cache or
72
+ from a concurrent identical call (the session already has its decision).
73
+ - **Single-flight verify.** Concurrent identical cache misses share one
74
+ verify-access call; followers get a copy with their own `attemptId`.
75
+ `bypassCache` re-verifies and uncacheable requests never join a flight.
76
+
77
+ ### Added
78
+
79
+ - `createMcpMiddleware({ skipEvaluation: { tools?, methods? } })`: unenforced
80
+ listed tools / methods pass through with no verify-access call, even with
81
+ `evaluateAlwaysIfCredentialed` (handshake, listing, `resources/read`, and
82
+ observe tools that verify in their own handler).
83
+ - `createMcpMiddleware` warns once when its `counterpartyId` is not an
84
+ `mcp_server` endpoint (the routes fetch now carries the endpoint's
85
+ `counterpartyType`; `fetchRoutes(…, { onEndpointType })`). Tool rules need a
86
+ separate `mcp_server` endpoint for the site's MCP server.
87
+ - `isSharedVerdict(result)` and `onVerificationResult(listener)` (root
88
+ exports).
89
+ - **MCP middleware carries the attempt chain** (express / Next.js / edge
90
+ parity): `X-Astra-Attempt-Id`, else `params._meta.astrasync.attemptId`, is
91
+ sent as the verify `attemptId` (malformed values dropped). A tool call the
92
+ AstraSync bridge proved (acting proof bound to the attempt) is no longer
93
+ held at an `mcp_server` venue.
94
+ - `GatewayConfig.actingProof?: { connectorToken }` is sent as `actingProof`
95
+ in the verify-access body (honoured only from a first-party AstraSync
96
+ service caller; a verify carrying it is never cached).
97
+ - `VerificationGatewayClient.updateEndpoint(id, { eventsUrl, healthCheckUrl,
98
+ skillUrl, catalogUrl, checkoutUrl })` → `PATCH /api/endpoints/:id`
99
+ (operational URLs only, API key of the owner or an org member) and
100
+ `sendTestEvent(id, { type, approvalId? })` → `POST
101
+ /api/endpoints/:id/events/test` (one signed step-up event delivered now;
102
+ returns `{ delivered, status, responseCode, error, eventId, type,
103
+ approvalId }`). Types `EndpointOperationalPatch`, `TestEventType`,
104
+ `TestEventResult`.
105
+ - Kits' `pollHeldSessions` releases a `lapsed` approval (UCP and ACP);
106
+ `StepUpApprovalStatus` gains `'lapsed'`.
107
+ - ACP: `approval_required` / `approval_denied` / `approval_expired` /
108
+ `approval_lapsed` join the passthrough message codes.
109
+ - UCP profiles' `signing_keys` are accepted alongside `keys` (the kit's
110
+ platform-signature check and the profile parser read both, skipping
111
+ `use: "enc"` keys); `ucpProfileKeys(profile)` is exported from the root.
112
+ - **Kits' catalog item carries the product image.** `CatalogItem` (exported
113
+ as `UcpCatalogItem`) gains optional `image_url` (an absolute http(s) URL)
114
+ and `description`. The UCP kit keeps them on `line_items[].item`; the ACP
115
+ kit carries them on each line's `item` and on the order's line items. A
116
+ relative or invalid `image_url` is dropped when the kit prices the line.
117
+ - **`fetchRoutes` sends an `X-AstraSync-SDK-Version` header** on the routes
118
+ fetch, so the endpoint's SDK version on the dashboard updates from the
119
+ 5-minute routes refresh (MCP traffic may never send a verify-access naming
120
+ the `mcp_server` endpoint). The URL is unchanged, so mocks matching
121
+ `…/routes` keep working. Round 29.1 backends also read the version from
122
+ the `User-Agent` for older SDKs.
123
+ - `VerificationWarning` type (root export) for the result's `warnings[]`.
124
+
125
+ ### Fixed
126
+
127
+ - **The verification result carries verify-access `warnings[]`** (e.g.
128
+ `events_url_missing`). It reached the wire, but both result builders
129
+ dropped it.
130
+ - **The UCP kit verifies signed platform requests with `signing_keys` too.**
131
+ Only the mandate-key path read them, so a platform profile that published
132
+ only `signing_keys` got `key_not_found` on its request signatures.
133
+
134
+ ### Changed (types)
135
+
136
+ - `registration/types.ts`: the trust-score fields on registration and
137
+ verification responses are optional — the platform no longer returns an
138
+ agent's score to the agent or the public (counterparties still see it in
139
+ verify-access).
140
+
11
141
  ## [5.19.0] — 2026-10-18 — approval events for held kit checkouts, MCP tool rules, fail-closed default
12
142
 
13
143
  ### Changed (behaviour)
package/README.md CHANGED
@@ -73,7 +73,7 @@ import { createMiddleware } from '@astrasyncai/verification-gateway/nextjs';
73
73
  // v2.3.7+ — per-route policy lives in the AstraSync dashboard.
74
74
  // The SDK fetches it on init via counterpartyId; do NOT pass routes here.
75
75
  export const middleware = createMiddleware({
76
- apiBaseUrl: 'https://api.astrasync.ai',
76
+ apiBaseUrl: 'https://astrasync.ai/api', // always includes /api
77
77
  apiKey: process.env.ASTRASYNC_API_KEY,
78
78
  counterpartyId: 'ASTRAE-...',
79
79
  showInterstitial: true,
@@ -90,7 +90,7 @@ export const config = {
90
90
  import { createClient } from '@astrasyncai/verification-gateway/sdk';
91
91
 
92
92
  const gateway = createClient({
93
- apiBaseUrl: 'https://api.astrasync.ai',
93
+ apiBaseUrl: 'https://astrasync.ai/api', // always includes /api
94
94
  });
95
95
 
96
96
  // Verify another agent before interacting
@@ -439,6 +439,27 @@ refresh, with no deploy.
439
439
  - The middleware sends its code-gated tool names on the fetch
440
440
  (`?codeGates=a,b`), so the dashboard marks those tools "gated in code — a
441
441
  rule here can only add checks".
442
+ - **One endpoint per surface (5.20.0).** Tool rules live on an `mcp_server`
443
+ endpoint. If your site and its MCP server share one (website) endpoint,
444
+ register a separate MCP endpoint for the MCP server — same owner; an API key
445
+ that operates your site endpoint may create it with `POST /api/endpoints` —
446
+ and use its id as the middleware's `counterpartyId`. The middleware warns
447
+ once (`console.warn`) when its `counterpartyId` is not an `mcp_server`
448
+ endpoint.
449
+
450
+ ## Fewer backend calls (5.20.0)
451
+
452
+ - **Single-flight:** concurrent identical cache misses share one
453
+ verify-access call.
454
+ - **No decision POST on a shared verdict:** a cache hit (or a single-flight
455
+ follower) no longer re-records the decision for the original session
456
+ (`isSharedVerdict(result)`).
457
+ - **`skipEvaluation: { tools?, methods? }`** (MCP middleware): unenforced
458
+ listed calls pass through with no backend call — handshake / listing /
459
+ `resources/read`, and observe tools whose handler calls verify-access
460
+ itself.
461
+ - **`onVerificationResult(listener)`** observes each verdict this process
462
+ fetched (the AstraSync bridge sizes its per-agent rate limit from it).
442
463
 
443
464
  ## Route purpose on a paid leg (5.19.0)
444
465
 
@@ -766,7 +787,13 @@ falling back to `store.findByCheckoutSessionId(checkoutSessionId)`:
766
787
  price change can't break the exact-value redemption), then settle → report
767
788
  → fulfil → order webhook. No agent call.
768
789
  - **denied / expired / lapsed** → `kit.resumeDeclined(sessionId, reason)`:
769
- back to `ready_for_complete` / `ready_for_payment` with a message.
790
+ back to `ready_for_complete` / `ready_for_payment` with an
791
+ `approval_denied` / `approval_expired` / `approval_lapsed` message. 5.20.0:
792
+ the message is `recoverable` (UCP severity, ACP resolution) — the session
793
+ is payable with no further buyer step. **Platforms should not auto-retry
794
+ on `approval_denied`** (the buyer said no; the code is the signal); after
795
+ `approval_expired` / `approval_lapsed` completing again requests a new
796
+ approval. The held response itself keeps `requires_buyer_review`.
770
797
  - **Card declined after redemption:** the session is payable again with the
771
798
  decline message and the owner sees the failed outcome. Re-completing the
772
799
  same cart at the same amount before the approval's `redeemBy` passes on the
@@ -784,7 +811,29 @@ checkout_awaiting_approval`), and held responses are never stored under an
784
811
  `Idempotency-Key`, so a re-drive after the approval reaches the kit.
785
812
  - `kit.pollHeldSessions({ sessionIds?, apiBaseUrl? })` polls held approvals
786
813
  and resumes decided ones — a **fallback only** for merchants that can't
787
- receive events (needs `store.listHeld()` when called without ids).
814
+ receive events (needs `store.listHeld()` when called without ids). 5.20.0:
815
+ a `lapsed` approval releases the session too.
816
+ - **Set the Events URL and test it from code (5.20.0).** With the endpoint's
817
+ API key (the owner's, or a personal key of a member of the owning
818
+ organisation):
819
+
820
+ ```ts
821
+ import { createClient } from '@astrasyncai/verification-gateway/sdk';
822
+ const client = createClient({ apiBaseUrl, apiKey, counterpartyId: 'ASTRAE-…' });
823
+ await client.updateEndpoint(undefined, { eventsUrl: 'https://shop.example/astrasync/events' });
824
+ const r = await client.sendTestEvent(undefined, { type: 'step_up.lapsed' });
825
+ // r = { delivered, status: 'delivered'|'failed'|'not_configured', responseCode, error, eventId, … }
826
+ ```
827
+
828
+ `updateEndpoint` (`PATCH /api/endpoints/:id`) changes only operational
829
+ URLs — `eventsUrl`, `healthCheckUrl`, `skillUrl`, `catalogUrl`,
830
+ `checkoutUrl` (`null` clears); rules and other policy stay in the
831
+ dashboard. The owner is alerted when the Events URL changes.
832
+ `sendTestEvent` (`POST /api/endpoints/:id/events/test`) delivers one signed
833
+ event now, outside the retrying outbox. Pass `approvalId` (a held kit
834
+ approval at this endpoint) to get that approval's real payload so your
835
+ resume path runs: `approved` re-confirms (and is held again — nothing is
836
+ charged); the others release the session.
788
837
 
789
838
  **Agent attribution (5.19.0).** `verifyPurchase` receives `agentArtifacts`:
790
839
  for a UCP request signed with RFC 9421, the covered headers (`signature`,
@@ -1,6 +1,6 @@
1
1
  import { AstraSyncGateway } from '../gateway/gateway.mjs';
2
- import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-CfBpm3w5.mjs';
3
- import '../types-BfILnheI.mjs';
2
+ import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-Djpz-L0w.mjs';
3
+ import '../types-CU17R4Nr.mjs';
4
4
 
5
5
  /**
6
6
  * PlatformAdapter Interface
@@ -1,6 +1,6 @@
1
1
  import { AstraSyncGateway } from '../gateway/gateway.js';
2
- import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-BRz2U0Pn.js';
3
- import '../types-BfILnheI.js';
2
+ import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-B_HLhZ9n.js';
3
+ import '../types-CU17R4Nr.js';
4
4
 
5
5
  /**
6
6
  * PlatformAdapter Interface
@@ -1,5 +1,5 @@
1
1
  import { RequestHandler, Request } from 'express';
2
- import { e as VerificationResult, E as ExpressMiddlewareOptions, b as AstraSyncCredentials } from '../types-DqWPkiYl.mjs';
2
+ import { e as VerificationResult, E as ExpressMiddlewareOptions, b as AstraSyncCredentials } from '../types-DHHyXtuZ.mjs';
3
3
 
4
4
  /**
5
5
  * AstraSync Universal Verification Gateway - Express Middleware
@@ -1,5 +1,5 @@
1
1
  import { RequestHandler, Request } from 'express';
2
- import { e as VerificationResult, E as ExpressMiddlewareOptions, b as AstraSyncCredentials } from '../types-DqWPkiYl.js';
2
+ import { e as VerificationResult, E as ExpressMiddlewareOptions, b as AstraSyncCredentials } from '../types-DHHyXtuZ.js';
3
3
 
4
4
  /**
5
5
  * AstraSync Universal Verification Gateway - Express Middleware
@@ -479,7 +479,7 @@ function routePolicyOf(route) {
479
479
  }
480
480
 
481
481
  // src/version.ts
482
- var SDK_VERSION = "5.19.0";
482
+ var SDK_VERSION = "5.21.0";
483
483
 
484
484
  // src/http.ts
485
485
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -994,6 +994,9 @@ async function callVerifyAccessAPI(config, request) {
994
994
  if (requestData.enableRuntimeChallenge)
995
995
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
996
996
  if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
997
+ if (config.actingProof?.connectorToken) {
998
+ body.actingProof = { connectorToken: config.actingProof.connectorToken };
999
+ }
997
1000
  if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
998
1001
  if (requestData.createSession) body.createSession = requestData.createSession;
999
1002
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
@@ -1161,22 +1164,46 @@ async function verify(config, request, options) {
1161
1164
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
1162
1165
  );
1163
1166
  }
1164
- const uncacheable = Boolean(request.commerceArtifacts);
1167
+ const uncacheable = Boolean(request.commerceArtifacts) || Boolean(mergedConfig.actingProof);
1165
1168
  if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
1166
1169
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
1167
1170
  if (cached) {
1168
1171
  if (mergedConfig.debug) {
1169
1172
  console.log("[VerificationGateway] Returning cached result");
1170
1173
  }
1171
- const hit = { ...cached };
1172
- if (request.attemptId) {
1173
- hit.attemptId = request.attemptId;
1174
- } else {
1175
- delete hit.attemptId;
1176
- }
1177
- return hit;
1174
+ return sharedVerdict(cached, request);
1175
+ }
1176
+ }
1177
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
1178
+ const key = getCacheKey(request, mergedConfig.counterpartyId);
1179
+ const pending = inflightVerifies.get(key);
1180
+ if (pending) return sharedVerdict(await pending, request);
1181
+ const flight = verifyViaApi(mergedConfig, request, urls, uncacheable);
1182
+ inflightVerifies.set(key, flight);
1183
+ try {
1184
+ return await flight;
1185
+ } finally {
1186
+ inflightVerifies.delete(key);
1178
1187
  }
1179
1188
  }
1189
+ return verifyViaApi(mergedConfig, request, urls, uncacheable);
1190
+ }
1191
+ var inflightVerifies = /* @__PURE__ */ new Map();
1192
+ var sharedVerdicts = /* @__PURE__ */ new WeakSet();
1193
+ function isSharedVerdict(result) {
1194
+ return sharedVerdicts.has(result);
1195
+ }
1196
+ function sharedVerdict(source, request) {
1197
+ const hit = { ...source };
1198
+ if (request.attemptId) {
1199
+ hit.attemptId = request.attemptId;
1200
+ } else {
1201
+ delete hit.attemptId;
1202
+ }
1203
+ sharedVerdicts.add(hit);
1204
+ return hit;
1205
+ }
1206
+ async function verifyViaApi(mergedConfig, request, urls, uncacheable) {
1180
1207
  const enrichedRequest = { ...request };
1181
1208
  if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
1182
1209
  enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
@@ -1255,7 +1282,9 @@ async function verify(config, request, options) {
1255
1282
  fulfilment: apiResponse.fulfilment,
1256
1283
  mandates: apiResponse.mandates,
1257
1284
  kyaToken: apiResponse.kyaToken,
1258
- aep: apiResponse.aep
1285
+ aep: apiResponse.aep,
1286
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
1287
+ warnings: apiResponse.warnings
1259
1288
  };
1260
1289
  return result2;
1261
1290
  }
@@ -1323,7 +1352,9 @@ async function verify(config, request, options) {
1323
1352
  // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
1324
1353
  mandates: apiResponse.mandates,
1325
1354
  kyaToken: apiResponse.kyaToken,
1326
- aep: apiResponse.aep
1355
+ aep: apiResponse.aep,
1356
+ // 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
1357
+ warnings: apiResponse.warnings
1327
1358
  };
1328
1359
  if (result.recommendation === "deny") {
1329
1360
  result.policyAllowed = false;
@@ -1349,8 +1380,18 @@ async function verify(config, request, options) {
1349
1380
  cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
1350
1381
  }
1351
1382
  }
1383
+ notifyVerificationListeners(request, result);
1352
1384
  return result;
1353
1385
  }
1386
+ var verificationListeners = /* @__PURE__ */ new Set();
1387
+ function notifyVerificationListeners(request, result) {
1388
+ for (const listener of verificationListeners) {
1389
+ try {
1390
+ listener(result, request);
1391
+ } catch {
1392
+ }
1393
+ }
1394
+ }
1354
1395
  async function recordDecision(config, sessionId, decision, reason) {
1355
1396
  const headers = { "Content-Type": "application/json" };
1356
1397
  if (config.apiKey) {
@@ -1373,6 +1414,7 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
1373
1414
  headers["X-API-Key"] = config.apiKey;
1374
1415
  }
1375
1416
  try {
1417
+ headers["X-AstraSync-SDK-Version"] = SDK_VERSION;
1376
1418
  const response = await sdkFetch(
1377
1419
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes` + (options.codeGates?.length ? `?codeGates=${options.codeGates.map(encodeURIComponent).join(",")}` : ""),
1378
1420
  { method: "GET", headers }
@@ -1387,6 +1429,13 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
1387
1429
  return null;
1388
1430
  }
1389
1431
  const body = await response.json();
1432
+ const type = body.data?.counterpartyType;
1433
+ if (typeof type === "string" && options.onEndpointType) {
1434
+ try {
1435
+ options.onEndpointType(type);
1436
+ } catch {
1437
+ }
1438
+ }
1390
1439
  return body.data?.routes ?? [];
1391
1440
  } catch {
1392
1441
  return null;
@@ -2665,14 +2714,14 @@ function createMiddleware(options) {
2665
2714
  res.setHeader("X-Astra-Gateway-Mode", "enforced");
2666
2715
  res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
2667
2716
  }
2668
- if (shouldRecordDecisions && sessionId) {
2717
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
2669
2718
  recordDecision(config, sessionId, "granted").catch(() => {
2670
2719
  });
2671
2720
  }
2672
2721
  return next();
2673
2722
  }
2674
2723
  if (!result.policyAllowed) {
2675
- if (shouldRecordDecisions && sessionId) {
2724
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
2676
2725
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
2677
2726
  });
2678
2727
  }
@@ -2685,7 +2734,7 @@ function createMiddleware(options) {
2685
2734
  }
2686
2735
  if (requiresHumanApproval(result)) {
2687
2736
  annotateApprovalRequired(result);
2688
- if (shouldRecordDecisions && sessionId) {
2737
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
2689
2738
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
2690
2739
  });
2691
2740
  }
@@ -2710,7 +2759,7 @@ function createMiddleware(options) {
2710
2759
  documentationUrl: wellKnownUrls.documentationUrl
2711
2760
  };
2712
2761
  }
2713
- if (shouldRecordDecisions && sessionId) {
2762
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
2714
2763
  recordDecision(config, sessionId, "denied", trustFailure.message).catch(() => {
2715
2764
  });
2716
2765
  }
@@ -2719,7 +2768,7 @@ function createMiddleware(options) {
2719
2768
  return;
2720
2769
  }
2721
2770
  }
2722
- if (shouldRecordDecisions && sessionId) {
2771
+ if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
2723
2772
  recordDecision(config, sessionId, "granted").catch(() => {
2724
2773
  });
2725
2774
  }