@astrasyncai/verification-gateway 5.17.0 → 5.17.1

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 (94) hide show
  1. package/README.md +94 -18
  2. package/dist/adapters/express.d.mts +1 -1
  3. package/dist/adapters/express.d.ts +1 -1
  4. package/dist/adapters/express.js +33 -5
  5. package/dist/adapters/express.js.map +1 -1
  6. package/dist/adapters/express.mjs +33 -5
  7. package/dist/adapters/express.mjs.map +1 -1
  8. package/dist/adapters/mcp.d.mts +1 -1
  9. package/dist/adapters/mcp.d.ts +1 -1
  10. package/dist/adapters/mcp.js +24 -5
  11. package/dist/adapters/mcp.js.map +1 -1
  12. package/dist/adapters/mcp.mjs +24 -5
  13. package/dist/adapters/mcp.mjs.map +1 -1
  14. package/dist/adapters/nextjs.d.mts +1 -1
  15. package/dist/adapters/nextjs.d.ts +1 -1
  16. package/dist/adapters/nextjs.js +24 -5
  17. package/dist/adapters/nextjs.js.map +1 -1
  18. package/dist/adapters/nextjs.mjs +24 -5
  19. package/dist/adapters/nextjs.mjs.map +1 -1
  20. package/dist/adapters/sdk.d.mts +1 -1
  21. package/dist/adapters/sdk.d.ts +1 -1
  22. package/dist/adapters/sdk.js +5 -1
  23. package/dist/adapters/sdk.js.map +1 -1
  24. package/dist/adapters/sdk.mjs +5 -1
  25. package/dist/adapters/sdk.mjs.map +1 -1
  26. package/dist/agent/index.js +1 -1
  27. package/dist/agent/index.js.map +1 -1
  28. package/dist/agent/index.mjs +1 -1
  29. package/dist/agent/index.mjs.map +1 -1
  30. package/dist/bin/astrasync-claude-hook.js +5 -1
  31. package/dist/bin/astrasync-codex-hook.js +5 -1
  32. package/dist/bin/astrasync-guard.js +32 -7
  33. package/dist/bin/astrasync.js +32 -7
  34. package/dist/browser/background.js +5 -1
  35. package/dist/browser/background.js.map +1 -1
  36. package/dist/browser/background.mjs +5 -1
  37. package/dist/browser/background.mjs.map +1 -1
  38. package/dist/cli/index.js +1 -1
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/index.mjs +1 -1
  41. package/dist/cli/index.mjs.map +1 -1
  42. package/dist/codex/index.js +5 -1
  43. package/dist/codex/index.js.map +1 -1
  44. package/dist/codex/index.mjs +5 -1
  45. package/dist/codex/index.mjs.map +1 -1
  46. package/dist/cursor/extension.js +5 -1
  47. package/dist/cursor/extension.js.map +1 -1
  48. package/dist/cursor/extension.mjs +5 -1
  49. package/dist/cursor/extension.mjs.map +1 -1
  50. package/dist/edge-config.d.mts +1 -1
  51. package/dist/edge-config.d.ts +1 -1
  52. package/dist/edge-config.js +1 -1
  53. package/dist/edge-config.js.map +1 -1
  54. package/dist/edge-config.mjs +1 -1
  55. package/dist/edge-config.mjs.map +1 -1
  56. package/dist/edge-core/index.d.mts +1 -1
  57. package/dist/edge-core/index.d.ts +1 -1
  58. package/dist/edge-core/index.js +5 -1
  59. package/dist/edge-core/index.js.map +1 -1
  60. package/dist/edge-core/index.mjs +5 -1
  61. package/dist/edge-core/index.mjs.map +1 -1
  62. package/dist/gateway/gateway.js +5 -1
  63. package/dist/gateway/gateway.js.map +1 -1
  64. package/dist/gateway/gateway.mjs +5 -1
  65. package/dist/gateway/gateway.mjs.map +1 -1
  66. package/dist/git-trigger/git-hooks.d.mts +1 -1
  67. package/dist/git-trigger/git-hooks.d.ts +1 -1
  68. package/dist/index.d.mts +16 -2
  69. package/dist/index.d.ts +16 -2
  70. package/dist/index.js +33 -5
  71. package/dist/index.js.map +1 -1
  72. package/dist/index.mjs +33 -5
  73. package/dist/index.mjs.map +1 -1
  74. package/dist/registration/index.js +1 -1
  75. package/dist/registration/index.js.map +1 -1
  76. package/dist/registration/index.mjs +1 -1
  77. package/dist/registration/index.mjs.map +1 -1
  78. package/dist/transport/index.js +1 -1
  79. package/dist/transport/index.js.map +1 -1
  80. package/dist/transport/index.mjs +1 -1
  81. package/dist/transport/index.mjs.map +1 -1
  82. package/dist/{types-Bm1QvGqI.d.ts → types-DA8LzAOB.d.ts} +6 -0
  83. package/dist/{types-Blz520bR.d.mts → types-DRb_plOh.d.mts} +14 -0
  84. package/dist/{types-Blz520bR.d.ts → types-DRb_plOh.d.ts} +14 -0
  85. package/dist/{types-DBe4hgfT.d.mts → types-vNBvYD7Z.d.mts} +6 -0
  86. package/dist/ui/index.d.mts +1 -1
  87. package/dist/ui/index.d.ts +1 -1
  88. package/dist/verify.d.mts +1 -1
  89. package/dist/verify.d.ts +1 -1
  90. package/dist/verify.js +5 -1
  91. package/dist/verify.js.map +1 -1
  92. package/dist/verify.mjs +5 -1
  93. package/dist/verify.mjs.map +1 -1
  94. package/package.json +1 -1
package/README.md CHANGED
@@ -112,7 +112,10 @@ if (result.identityVerified && result.policyAllowed) {
112
112
 
113
113
  #### `apiEndpoint` — runtime-challenge URL
114
114
 
115
- `apiEndpoint` is the URL where your agent's verification-gateway SDK is mounted to receive runtime challenges from counterparties. Optional but recommended — if you omit it, your agent declares no runtime-challenge support, which is included in the verification payload and may cause some counterparties to decline access requests.
115
+ `apiEndpoint` is the base URL where your agent's own `ChallengeHandler` is mounted (AstraSync POSTs to `{apiEndpoint}/astrasync/challenge`). Endpoints can require the runtime challenge endpoint-wide or per route; an agent that can't answer it is denied there.
116
+
117
+ - **Your own handler:** mount `ChallengeHandler.expressMiddleware()` at `/astrasync/challenge` and set `apiEndpoint`. Since 5.16.0 the handler answers only for counterparties registered for the call in flight — pass `challengeHandler` to `AgentClient` and each `fetch()` registers its destination, or wrap your own calls in `registerPending()` / `removePending()`.
118
+ - **Through a Workforce Guard checkpoint:** leave `apiEndpoint` blank. The checkpoint answers the challenge for every call made through it (it marks those calls `x-astra-via-checkpoint`). A direct call that bypasses the checkpoint is challenged at the checkpoint, which has nothing pending for it, so it fails — unless the agent also has its own handler, which direct calls ring instead.
116
119
 
117
120
  #### Two registration response modes
118
121
 
@@ -385,18 +388,25 @@ import {
385
388
  <GuidanceCard guidance={verificationResult.guidance} />
386
389
  ```
387
390
 
388
- ## Credential Extraction
391
+ ## What the middleware reads from a request
389
392
 
390
- Agents can provide credentials via:
393
+ The middleware reads the calling agent's **identity** and passes it to
394
+ verify-access, which your server calls with **your own** API key:
391
395
 
392
396
  1. **Headers** (recommended):
393
- - `X-Astra-Id`: Agent ASTRA-ID
394
- - `X-Api-Key`: API key
395
- - `Authorization: Bearer <jwt>`: JWT token
396
-
397
- 2. **Query Parameters** (fallback):
398
- - `?astraId=ASTRA-xxx`
399
- - `?apiKey=xxx`
397
+ - `X-Astra-Id`: the agent's ASTRA-ID. It says which agent is calling; it is
398
+ an identifier, **never a credential**. Anyone can send any agent's id, so
399
+ protect sensitive routes with PDLSS policy, `allowedOwners`, and the
400
+ runtime challenge (which only the real agent's handler, or its Workforce
401
+ Guard checkpoint, can answer).
402
+ - `X-Api-Key` / `Authorization: Bearer …`: forwarded as observed metadata.
403
+ 2. **Query parameters** (fallback): `?astraId=ASTRA-xxx`, `?apiKey=xxx`.
404
+
405
+ Calling the AstraSync API itself is different: only an API key or a full
406
+ dashboard session token authenticates. A request that offers only
407
+ `X-Astra-Id` is refused with `401 agent_identity_not_a_credential`, and
408
+ first-step sign-in tokens (before the second factor), refresh tokens and
409
+ passkey elevation tokens are refused with 401.
400
410
 
401
411
  ## Verification Response
402
412
 
@@ -466,7 +476,22 @@ if (!decision.authorized) {
466
476
 
467
477
  ## Step-Up Approval
468
478
 
469
- When a transaction value is between the agent's Autonomous Limit and Hard Limit, verify-access returns `stepUpApproval` on the result:
479
+ The agent's owner can ask to approve requests ("Ask me") on **any** PDLSS
480
+ dimension, not just spend. verify-access then returns
481
+ `recommendation: 'step_up_required'` with `stepUpApproval` and a `failures[]`
482
+ entry naming the dimension. This is **not** a deny: hold the request and poll.
483
+
484
+ | Dimension | When | `failures[].dimension` |
485
+ | ------------------ | --------------------------------------------------------- | ------------------------------------------- |
486
+ | Limits | value above the Autonomous Limit and below the Hard Limit | `commerce.intent.approval_required` |
487
+ | Purpose | the action is on the owner's approval list | `pdlss.purpose.approval_required` |
488
+ | Duration | outside the agent's time window, set to ask | `pdlss.duration.approval_required` |
489
+ | Scope | a resource, counterparty or jurisdiction set to ask | `pdlss.scope.approval_required` |
490
+ | Self-instantiation | spawning a sub-agent, set to ask | `pdlss.selfInstantiation.approval_required` |
491
+
492
+ The entry names the dimension, never the rule. Once the owner approves, the
493
+ same request (same `attemptId`) passes once. Gate on `recommendation` (or
494
+ `stepUpApproval`), never on a non-empty `failures[]` alone.
470
495
 
471
496
  ```typescript
472
497
  interface StepUpApprovalInfo {
@@ -514,6 +539,26 @@ outlive that budget — behind the bridge, either:
514
539
  Either way the attempt chain above guarantees the post-approval re-drive
515
540
  passes without a second approval.
516
541
 
542
+ ### Routes that hold their own approval (v5.17.1)
543
+
544
+ If a route asks the owner for approval itself — it creates its own hold and
545
+ answers `202` while it waits — set `approvalHeldByRoute` so the owner isn't
546
+ asked twice:
547
+
548
+ ```ts
549
+ createMiddleware({
550
+ // …
551
+ approvalHeldByRoute: (req) => req.method === 'POST' && req.path === '/signin',
552
+ });
553
+ ```
554
+
555
+ For matching requests, a result that only needs the owner's approval reaches
556
+ your handler (`req.agentVerification.requiresStepUp` is true and `failures`
557
+ names each `pdlss.<dimension>.approval_required` or
558
+ `commerce.intent.approval_required`); verify-access creates no approval. Your
559
+ route must then hold the request until the owner approves. Any deny is still
560
+ refused.
561
+
517
562
  ## Checkout & settlement (astra-pay)
518
563
 
519
564
  The first-party commerce rail: an agent shops under its ASTRA-id, holds NO
@@ -823,18 +868,38 @@ The installer registers the `astrasync-claude-hook` binary in Claude Code's `set
823
868
  - **MCP governance** — set `"governMcp": true` to govern MCP tools too; `mcp__{server}__{tool}` maps to purpose `{server}.{tool}` in the policy.
824
869
  - **Uninstall** — `astrasync-guard uninstall-claude-code` removes the hook; policy and traces are left in place.
825
870
 
871
+ ## Fleet & Workforce Guard CLI
872
+
873
+ Organisation-managed commands (Enterprise Governance → Fleet). Run them as
874
+ `astrasync guard <command>`.
875
+
876
+ | Command | What it does |
877
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
878
+ | `enroll <token>` | Enrol this machine in your organisation's fleet. User scope by default; `--system` enrols the whole machine (needs administrator rights; machine scope wins over user scope). `--api-base-url <url>` targets another environment. |
879
+ | `fleet-status` | Show the enrolment, the signed policy and the last heartbeat. |
880
+ | `managed-settings` | Print MDM templates for Claude Code and/or Codex (`--claude`, `--codex`, `--out <dir>` to write files). Machines must also be enrolled with `enroll <token> --system`. |
881
+ | `git-route` | Route this machine's `git fetch` / `git push` to github.com through your Workforce Guard checkpoint (`--checkpoint <url> --slug <connector> --agent <ASTRA-id>`, `--upstream` default `github`; `--undo` removes it). `git commit` and local hooks are unchanged. |
882
+ | `git-credential` | The git credential helper `git-route` installs. Answers with the agent's ASTRA-id and this machine's device credential; no git host token is stored on the machine. Not run by hand. |
883
+ | `browse` | Sign an agent in to its owner's dashboard from this machine and run its device proxy (`--checkpoint <url> --slug <agent>`, `--upstream` default `astrasync-api`, `--port`, `--host`). Needs an enrolled machine and `dashboard.signin` in the agent's policy. See CHANGELOG 5.17.0. |
884
+
885
+ Calls an agent makes through its checkpoint have their runtime challenge
886
+ answered by the checkpoint; the agent needs no `apiEndpoint` of its own.
887
+
826
888
  ## Changelog
827
889
 
828
- ### v4.1.0 — Trusted Agent Gateway naming + MCP metadata capture
890
+ Newest first. Full detail for every release is in [CHANGELOG.md](./CHANGELOG.md).
829
891
 
830
- - The Next.js unverified-agent overlay is now the **verification interstitial**: `showInterstitial` option, `VerificationInterstitial` / `useVerificationInterstitial` / `VerificationInterstitialProps`. The old `showCommerceShield` / `CommerceShield` / `useCommerceShield` / `CommerceShieldProps` names keep working as deprecated aliases until the next major (`showInterstitial` wins when both options are set).
831
- - The MCP middleware now captures header-level `ObservedMetadata` on every verify-access call (parity with the express/nextjs adapters).
832
- - The edge product is the **Trusted Agent Gateway (TAG)** — see the new README sections above.
892
+ ### v5.17.1 — one approval for a route that holds its own
833
893
 
834
- ### v3.8.0 — Settlement voucher wire format v2
894
+ - `approvalHeldByRoute` (Express): a route that holds its own owner approval receives an approval-only result instead of a refusal, and verify-access creates no approval — the owner approves once.
895
+ - A hold on purpose, duration, scope or sub-agents is no longer described as a spending limit; step-up results carry the server's `failures`.
896
+ - `astrasync guard browse` says why a sign-in was refused, and stops at once on a refused status check.
835
897
 
836
- - **BREAKING type change:** `SettlementArtifact.binding` is now a union — `StablecoinSettlementBinding` (v2: `amountMinor` integer minor units + `assetDecimals`, pinned `chainId`/`tokenContract`/CAIP-19 `asset`) or `FiatSettlementBinding` (processor artifacts keep major-unit `amount`). The float `amount` field is gone from stablecoin bindings. Matches the server's `ver: 2` wire format, which redeems/verifies reject other versions of.
837
- - Settlement Artifacts docs rewritten: voucher lifecycle endpoints (verify / redeem / JWKS), expected-value gates on redeem, revocation semantics, and an explicit **guarantee boundary** — what a successful redeem proves and what it doesn't (redeem proves the authorization is live and single-use; it does not check or move funds).
898
+ ### v5.17.0 — `astrasync guard browse`
899
+
900
+ - `astrasync guard browse` signs an agent in to its owner's dashboard through its
901
+ Workforce Guard checkpoint, bound to this machine's device credential, and runs
902
+ the local device proxy its automation browser uses.
838
903
 
839
904
  ### v5.16.0 — ChallengeHandler auto-include removed
840
905
 
@@ -844,6 +909,17 @@ The installer registers the `astrasync-claude-hook` binary in Claude Code's `set
844
909
  registers each call while it is in flight; otherwise call
845
910
  `registerPending()` / `removePending()` around each interaction.
846
911
 
912
+ ### v4.1.0 — Trusted Agent Gateway naming + MCP metadata capture
913
+
914
+ - The Next.js unverified-agent overlay is now the **verification interstitial**: `showInterstitial` option, `VerificationInterstitial` / `useVerificationInterstitial` / `VerificationInterstitialProps`. The old `showCommerceShield` / `CommerceShield` / `useCommerceShield` / `CommerceShieldProps` names keep working as deprecated aliases until the next major (`showInterstitial` wins when both options are set).
915
+ - The MCP middleware now captures header-level `ObservedMetadata` on every verify-access call (parity with the express/nextjs adapters).
916
+ - The edge product is the **Trusted Agent Gateway (TAG)** — see the new README sections above.
917
+
918
+ ### v3.8.0 — Settlement voucher wire format v2
919
+
920
+ - **BREAKING type change:** `SettlementArtifact.binding` is now a union — `StablecoinSettlementBinding` (v2: `amountMinor` integer minor units + `assetDecimals`, pinned `chainId`/`tokenContract`/CAIP-19 `asset`) or `FiatSettlementBinding` (processor artifacts keep major-unit `amount`). The float `amount` field is gone from stablecoin bindings. Matches the server's `ver: 2` wire format, which redeems/verifies reject other versions of.
921
+ - Settlement Artifacts docs rewritten: voucher lifecycle endpoints (verify / redeem / JWKS), expected-value gates on redeem, revocation semantics, and an explicit **guarantee boundary** — what a successful redeem proves and what it doesn't (redeem proves the authorization is live and single-use; it does not check or move funds).
922
+
847
923
  ### v3.7.1 — ChallengeHandler auto-include (removed in 5.16.0)
848
924
 
849
925
  - `ChallengeHandler` auto-includes the incoming `counterpartyId`/`counterpartyUrl` in the challenge response when no counterparties were pre-registered via `registerPending()` — the correct default for agents that never call it. Pre-registered whitelists behave exactly as before.
@@ -1,5 +1,5 @@
1
1
  import { RequestHandler, Request } from 'express';
2
- import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-Blz520bR.mjs';
2
+ import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-DRb_plOh.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 { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-Blz520bR.js';
2
+ import { d as VerificationResult, E as ExpressMiddlewareOptions, a as AstraSyncCredentials } from '../types-DRb_plOh.js';
3
3
 
4
4
  /**
5
5
  * AstraSync Universal Verification Gateway - Express Middleware
@@ -469,7 +469,7 @@ function routePolicyOf(route) {
469
469
  }
470
470
 
471
471
  // src/version.ts
472
- var SDK_VERSION = "5.17.0";
472
+ var SDK_VERSION = "5.17.1";
473
473
 
474
474
  // src/http.ts
475
475
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -771,6 +771,7 @@ function getCacheKey(request, counterpartyId) {
771
771
  request.enableRuntimeChallenge ? "1" : "0",
772
772
  // 5.16.0: route policy changes the verdict (owners / challenge).
773
773
  request.routePolicy ? JSON.stringify(request.routePolicy) : "",
774
+ request.approvalHeldByRoute ? "1" : "0",
774
775
  // callerMetadata fields contribute to risk model; include the ones
775
776
  // backend reads. sourceIp/userAgent/forwardedFor change per-request
776
777
  // so their inclusion effectively forces a re-check for any varying
@@ -978,6 +979,7 @@ async function callVerifyAccessAPI(config, request) {
978
979
  if (requestData.enableRuntimeChallenge)
979
980
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
980
981
  if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
982
+ if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
981
983
  if (requestData.createSession) body.createSession = requestData.createSession;
982
984
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
983
985
  if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
@@ -1309,6 +1311,8 @@ async function verify(config, request, options) {
1309
1311
  } else if (result.recommendation === "step_up_required" && result.identityVerified) {
1310
1312
  result.requiresStepUp = true;
1311
1313
  result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
1314
+ const stepUpFailures = apiResponse.access?.failures;
1315
+ if (stepUpFailures?.length) result.failures = stepUpFailures;
1312
1316
  }
1313
1317
  const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
1314
1318
  if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
@@ -1543,12 +1547,31 @@ function requiresHumanApproval(result) {
1543
1547
  return result.requiresStepUp === true || result.requiresApproval === true;
1544
1548
  }
1545
1549
  function annotateApprovalRequired(result) {
1546
- result.failures = [
1547
- ...result.failures ?? [],
1548
- { dimension: "commerce.intent.approval_required", message: APPROVAL_REASON }
1549
- ];
1550
+ if (isDimensionApproval(result)) {
1551
+ result.denialReasons = [DIMENSION_APPROVAL_REASON, ...result.denialReasons ?? []];
1552
+ return;
1553
+ }
1554
+ const named = (result.failures ?? []).some(
1555
+ (f) => f.dimension === "commerce.intent.approval_required"
1556
+ );
1557
+ if (!named) {
1558
+ result.failures = [
1559
+ ...result.failures ?? [],
1560
+ { dimension: "commerce.intent.approval_required", message: APPROVAL_REASON }
1561
+ ];
1562
+ }
1550
1563
  result.denialReasons = [APPROVAL_REASON, ...result.denialReasons ?? []];
1551
1564
  }
1565
+ var DIMENSION_APPROVAL_REASON = "The agent's owner asked to approve this kind of request. The owner can approve or deny it in the AstraSync dashboard.";
1566
+ var DIMENSION_APPROVAL = /^pdlss\.[a-z_]+\.approval_required$/;
1567
+ function isDimensionApproval(result) {
1568
+ const f = result.failures ?? [];
1569
+ return f.some((x) => DIMENSION_APPROVAL.test(x.dimension)) && !f.some((x) => x.dimension === "commerce.intent.approval_required");
1570
+ }
1571
+ function onlyNeedsApproval(result) {
1572
+ if (!result.policyAllowed || !requiresHumanApproval(result)) return false;
1573
+ return (result.failures ?? []).every((f) => /\.approval_required$/.test(f.dimension));
1574
+ }
1552
1575
 
1553
1576
  // src/pdlss-pre-check.ts
1554
1577
  function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
@@ -2512,6 +2535,7 @@ function createMiddleware(options) {
2512
2535
  const attemptHeader = req.headers["x-astra-attempt-id"];
2513
2536
  const inboundAttemptId = typeof attemptHeader === "string" && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
2514
2537
  const commerceArtifacts = detectExpressCommerce(req);
2538
+ const heldByRoute = options.approvalHeldByRoute?.(req) === true;
2515
2539
  const verificationRequest = {
2516
2540
  credentials,
2517
2541
  ...commerceArtifacts && { commerceArtifacts },
@@ -2523,6 +2547,7 @@ function createMiddleware(options) {
2523
2547
  counterpartyType: config.counterpartyType || "api",
2524
2548
  enableRuntimeChallenge,
2525
2549
  ...routePolicyOf(routeConfig) && { routePolicy: routePolicyOf(routeConfig) },
2550
+ ...heldByRoute && { approvalHeldByRoute: true },
2526
2551
  durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,
2527
2552
  ...txValue !== void 0 && { transactionValue: txValue },
2528
2553
  ...txCurrency && { currency: txCurrency },
@@ -2579,6 +2604,9 @@ function createMiddleware(options) {
2579
2604
  onDenied(result, req, res);
2580
2605
  return;
2581
2606
  }
2607
+ if (heldByRoute && !result.stepUpApproval && onlyNeedsApproval(result)) {
2608
+ return next();
2609
+ }
2582
2610
  if (requiresHumanApproval(result)) {
2583
2611
  annotateApprovalRequired(result);
2584
2612
  if (shouldRecordDecisions && sessionId) {