@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.
- package/CHANGELOG.md +130 -0
- package/README.md +53 -4
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +65 -16
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +65 -16
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +23 -1
- package/dist/adapters/mcp.d.ts +23 -1
- package/dist/adapters/mcp.js +91 -16
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +91 -16
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +57 -11
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +57 -11
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +35 -2
- package/dist/adapters/sdk.d.ts +35 -2
- package/dist/adapters/sdk.js +110 -11
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +110 -11
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +50 -12
- package/dist/bin/astrasync-codex-hook.js +50 -12
- package/dist/bin/astrasync-guard.js +50 -12
- package/dist/bin/astrasync.js +65 -13
- package/dist/browser/background.js +50 -12
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +50 -12
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +50 -12
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +50 -12
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +50 -12
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +50 -12
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +49 -11
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +49 -11
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +50 -12
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +50 -12
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +286 -8
- package/dist/index.d.ts +286 -8
- package/dist/index.js +532 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +522 -49
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/registration/index.d.mts +11 -2
- package/dist/registration/index.d.ts +11 -2
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +1 -1
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +1 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-BoGjyon0.d.mts → types-B4zhOAlq.d.mts} +30 -1
- package/dist/{types-BRz2U0Pn.d.ts → types-B_HLhZ9n.d.ts} +1 -1
- package/dist/{types-BfILnheI.d.mts → types-CU17R4Nr.d.mts} +11 -0
- package/dist/{types-BfILnheI.d.ts → types-CU17R4Nr.d.ts} +11 -0
- package/dist/{types-eWyZuz-3.d.ts → types-CtBXnWuV.d.ts} +30 -1
- package/dist/{types-DqWPkiYl.d.mts → types-DHHyXtuZ.d.mts} +30 -1
- package/dist/{types-DqWPkiYl.d.ts → types-DHHyXtuZ.d.ts} +30 -1
- package/dist/{types-CfBpm3w5.d.mts → types-Djpz-L0w.d.mts} +1 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +28 -3
- package/dist/verify.d.ts +28 -3
- package/dist/verify.js +71 -11
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +69 -11
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -30,7 +30,7 @@ function getTrustLevel(score) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// src/version.ts
|
|
33
|
-
var SDK_VERSION = "5.
|
|
33
|
+
var SDK_VERSION = "5.21.0";
|
|
34
34
|
|
|
35
35
|
// src/http.ts
|
|
36
36
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -312,6 +312,7 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
|
312
312
|
}
|
|
313
313
|
function clearCache() {
|
|
314
314
|
verificationCache.clear();
|
|
315
|
+
inflightVerifies.clear();
|
|
315
316
|
}
|
|
316
317
|
function extractCredentials(headers, query) {
|
|
317
318
|
const credentials = {};
|
|
@@ -485,6 +486,9 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
485
486
|
if (requestData.enableRuntimeChallenge)
|
|
486
487
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
487
488
|
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
489
|
+
if (config.actingProof?.connectorToken) {
|
|
490
|
+
body.actingProof = { connectorToken: config.actingProof.connectorToken };
|
|
491
|
+
}
|
|
488
492
|
if (requestData.approvalHeldByRoute) body.approvalHeldByRoute = true;
|
|
489
493
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
490
494
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
@@ -652,22 +656,46 @@ async function verify(config, request, options) {
|
|
|
652
656
|
"[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."
|
|
653
657
|
);
|
|
654
658
|
}
|
|
655
|
-
const uncacheable = Boolean(request.commerceArtifacts);
|
|
659
|
+
const uncacheable = Boolean(request.commerceArtifacts) || Boolean(mergedConfig.actingProof);
|
|
656
660
|
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
657
661
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
658
662
|
if (cached) {
|
|
659
663
|
if (mergedConfig.debug) {
|
|
660
664
|
console.log("[VerificationGateway] Returning cached result");
|
|
661
665
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
666
|
+
return sharedVerdict(cached, request);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
670
|
+
const key = getCacheKey(request, mergedConfig.counterpartyId);
|
|
671
|
+
const pending = inflightVerifies.get(key);
|
|
672
|
+
if (pending) return sharedVerdict(await pending, request);
|
|
673
|
+
const flight = verifyViaApi(mergedConfig, request, urls, uncacheable);
|
|
674
|
+
inflightVerifies.set(key, flight);
|
|
675
|
+
try {
|
|
676
|
+
return await flight;
|
|
677
|
+
} finally {
|
|
678
|
+
inflightVerifies.delete(key);
|
|
669
679
|
}
|
|
670
680
|
}
|
|
681
|
+
return verifyViaApi(mergedConfig, request, urls, uncacheable);
|
|
682
|
+
}
|
|
683
|
+
var inflightVerifies = /* @__PURE__ */ new Map();
|
|
684
|
+
var sharedVerdicts = /* @__PURE__ */ new WeakSet();
|
|
685
|
+
function isSharedVerdict(result) {
|
|
686
|
+
return sharedVerdicts.has(result);
|
|
687
|
+
}
|
|
688
|
+
function sharedVerdict(source, request) {
|
|
689
|
+
const hit = { ...source };
|
|
690
|
+
if (request.attemptId) {
|
|
691
|
+
hit.attemptId = request.attemptId;
|
|
692
|
+
} else {
|
|
693
|
+
delete hit.attemptId;
|
|
694
|
+
}
|
|
695
|
+
sharedVerdicts.add(hit);
|
|
696
|
+
return hit;
|
|
697
|
+
}
|
|
698
|
+
async function verifyViaApi(mergedConfig, request, urls, uncacheable) {
|
|
671
699
|
const enrichedRequest = { ...request };
|
|
672
700
|
if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {
|
|
673
701
|
enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;
|
|
@@ -746,7 +774,9 @@ async function verify(config, request, options) {
|
|
|
746
774
|
fulfilment: apiResponse.fulfilment,
|
|
747
775
|
mandates: apiResponse.mandates,
|
|
748
776
|
kyaToken: apiResponse.kyaToken,
|
|
749
|
-
aep: apiResponse.aep
|
|
777
|
+
aep: apiResponse.aep,
|
|
778
|
+
// 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
|
|
779
|
+
warnings: apiResponse.warnings
|
|
750
780
|
};
|
|
751
781
|
return result2;
|
|
752
782
|
}
|
|
@@ -814,7 +844,9 @@ async function verify(config, request, options) {
|
|
|
814
844
|
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
815
845
|
mandates: apiResponse.mandates,
|
|
816
846
|
kyaToken: apiResponse.kyaToken,
|
|
817
|
-
aep: apiResponse.aep
|
|
847
|
+
aep: apiResponse.aep,
|
|
848
|
+
// 5.20.0 (round 29.1): non-fatal integration warnings (e.g. events_url_missing).
|
|
849
|
+
warnings: apiResponse.warnings
|
|
818
850
|
};
|
|
819
851
|
if (result.recommendation === "deny") {
|
|
820
852
|
result.policyAllowed = false;
|
|
@@ -840,8 +872,24 @@ async function verify(config, request, options) {
|
|
|
840
872
|
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
841
873
|
}
|
|
842
874
|
}
|
|
875
|
+
notifyVerificationListeners(request, result);
|
|
843
876
|
return result;
|
|
844
877
|
}
|
|
878
|
+
var verificationListeners = /* @__PURE__ */ new Set();
|
|
879
|
+
function onVerificationResult(listener) {
|
|
880
|
+
verificationListeners.add(listener);
|
|
881
|
+
return () => {
|
|
882
|
+
verificationListeners.delete(listener);
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function notifyVerificationListeners(request, result) {
|
|
886
|
+
for (const listener of verificationListeners) {
|
|
887
|
+
try {
|
|
888
|
+
listener(result, request);
|
|
889
|
+
} catch {
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
845
893
|
async function recordDecision(config, sessionId, decision, reason) {
|
|
846
894
|
const headers = { "Content-Type": "application/json" };
|
|
847
895
|
if (config.apiKey) {
|
|
@@ -906,6 +954,7 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
|
|
|
906
954
|
headers["X-API-Key"] = config.apiKey;
|
|
907
955
|
}
|
|
908
956
|
try {
|
|
957
|
+
headers["X-AstraSync-SDK-Version"] = SDK_VERSION;
|
|
909
958
|
const response = await sdkFetch(
|
|
910
959
|
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes` + (options.codeGates?.length ? `?codeGates=${options.codeGates.map(encodeURIComponent).join(",")}` : ""),
|
|
911
960
|
{ method: "GET", headers }
|
|
@@ -920,6 +969,13 @@ async function fetchRoutes(config, counterpartyId, options = {}) {
|
|
|
920
969
|
return null;
|
|
921
970
|
}
|
|
922
971
|
const body = await response.json();
|
|
972
|
+
const type2 = body.data?.counterpartyType;
|
|
973
|
+
if (typeof type2 === "string" && options.onEndpointType) {
|
|
974
|
+
try {
|
|
975
|
+
options.onEndpointType(type2);
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
978
|
+
}
|
|
923
979
|
return body.data?.routes ?? [];
|
|
924
980
|
} catch {
|
|
925
981
|
return null;
|
|
@@ -2843,14 +2899,14 @@ function createMiddleware(options) {
|
|
|
2843
2899
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
2844
2900
|
res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
|
|
2845
2901
|
}
|
|
2846
|
-
if (shouldRecordDecisions && sessionId) {
|
|
2902
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
2847
2903
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
2848
2904
|
});
|
|
2849
2905
|
}
|
|
2850
2906
|
return next();
|
|
2851
2907
|
}
|
|
2852
2908
|
if (!result.policyAllowed) {
|
|
2853
|
-
if (shouldRecordDecisions && sessionId) {
|
|
2909
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
2854
2910
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
2855
2911
|
});
|
|
2856
2912
|
}
|
|
@@ -2863,7 +2919,7 @@ function createMiddleware(options) {
|
|
|
2863
2919
|
}
|
|
2864
2920
|
if (requiresHumanApproval(result)) {
|
|
2865
2921
|
annotateApprovalRequired(result);
|
|
2866
|
-
if (shouldRecordDecisions && sessionId) {
|
|
2922
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
2867
2923
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
2868
2924
|
});
|
|
2869
2925
|
}
|
|
@@ -2888,7 +2944,7 @@ function createMiddleware(options) {
|
|
|
2888
2944
|
documentationUrl: wellKnownUrls.documentationUrl
|
|
2889
2945
|
};
|
|
2890
2946
|
}
|
|
2891
|
-
if (shouldRecordDecisions && sessionId) {
|
|
2947
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
2892
2948
|
recordDecision(config, sessionId, "denied", trustFailure.message).catch(() => {
|
|
2893
2949
|
});
|
|
2894
2950
|
}
|
|
@@ -2897,7 +2953,7 @@ function createMiddleware(options) {
|
|
|
2897
2953
|
return;
|
|
2898
2954
|
}
|
|
2899
2955
|
}
|
|
2900
|
-
if (shouldRecordDecisions && sessionId) {
|
|
2956
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
2901
2957
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
2902
2958
|
});
|
|
2903
2959
|
}
|
|
@@ -3745,6 +3801,66 @@ var VerificationGatewayClient = class {
|
|
|
3745
3801
|
}
|
|
3746
3802
|
return json4.data;
|
|
3747
3803
|
}
|
|
3804
|
+
/**
|
|
3805
|
+
* 5.20.0 (round 29.1): ask AstraSync to send ONE signed step-up event to the
|
|
3806
|
+
* endpoint's Events URL now — `POST /api/endpoints/:id/events/test`.
|
|
3807
|
+
* Delivered synchronously, outside the event outbox (random `eventId`, no
|
|
3808
|
+
* retries), signed with the endpoint's webhook secret like a real event.
|
|
3809
|
+
* The payload is synthetic (`approvalId: "test_…"`); a kit acknowledges and
|
|
3810
|
+
* ignores it.
|
|
3811
|
+
*
|
|
3812
|
+
* 5.21.0 (shops' item 48): synthetic only. Sending a real held approval's
|
|
3813
|
+
* payload (which releases or re-drives that checkout) is dashboard-only, so
|
|
3814
|
+
* a leaked API key can't do it.
|
|
3815
|
+
*
|
|
3816
|
+
* Resolves even when your endpoint refused the event (`delivered: false`,
|
|
3817
|
+
* with `responseCode` / `error`).
|
|
3818
|
+
*/
|
|
3819
|
+
async sendTestEvent(endpointId, options) {
|
|
3820
|
+
return this.endpointCall("POST", endpointId, "/events/test", { type: options.type });
|
|
3821
|
+
}
|
|
3822
|
+
async endpointCall(method, endpointId, suffix, body) {
|
|
3823
|
+
const id = endpointId ?? this.config.counterpartyId;
|
|
3824
|
+
if (!id) {
|
|
3825
|
+
throw new AstraSyncError(
|
|
3826
|
+
"An endpoint id (ASTRAE-\u2026) is required: pass it, or set counterpartyId on the client",
|
|
3827
|
+
400,
|
|
3828
|
+
"VALIDATION"
|
|
3829
|
+
);
|
|
3830
|
+
}
|
|
3831
|
+
if (!this.config.apiKey) {
|
|
3832
|
+
throw new AstraSyncError("This call needs the client apiKey", 401, "AUTH_REQUIRED");
|
|
3833
|
+
}
|
|
3834
|
+
const base = this.config.apiBaseUrl.replace(/\/+$/, "");
|
|
3835
|
+
const url = `${base}/endpoints/${encodeURIComponent(id)}${suffix}`;
|
|
3836
|
+
let res;
|
|
3837
|
+
try {
|
|
3838
|
+
res = await sdkFetch(url, {
|
|
3839
|
+
method,
|
|
3840
|
+
headers: {
|
|
3841
|
+
"Content-Type": "application/json",
|
|
3842
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
3843
|
+
"X-API-Key": this.config.apiKey
|
|
3844
|
+
},
|
|
3845
|
+
body: JSON.stringify(body),
|
|
3846
|
+
signal: AbortSignal.timeout(Math.max(this.timeout, 15e3))
|
|
3847
|
+
});
|
|
3848
|
+
} catch (err) {
|
|
3849
|
+
throw new AstraSyncError(
|
|
3850
|
+
`${method} ${suffix || "/endpoints/:id"} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
3851
|
+
0,
|
|
3852
|
+
"network_error"
|
|
3853
|
+
);
|
|
3854
|
+
}
|
|
3855
|
+
const json4 = await res.json().catch(() => ({}));
|
|
3856
|
+
if (!res.ok || json4.success === false || json4.data === void 0) {
|
|
3857
|
+
const errObj = typeof json4.error === "object" ? json4.error : void 0;
|
|
3858
|
+
const code = json4.code ?? errObj?.code ?? (typeof json4.error === "string" ? json4.error : "unknown_error");
|
|
3859
|
+
const message = json4.message ?? errObj?.message ?? `HTTP ${res.status}`;
|
|
3860
|
+
throw new AstraSyncError(`${message} (${code})`, res.status, code);
|
|
3861
|
+
}
|
|
3862
|
+
return json4.data;
|
|
3863
|
+
}
|
|
3748
3864
|
/**
|
|
3749
3865
|
* Quick verification — checks credentials and policy in one call.
|
|
3750
3866
|
*
|
|
@@ -6709,16 +6825,30 @@ function createMcpMiddleware(options) {
|
|
|
6709
6825
|
failOnError = "closed",
|
|
6710
6826
|
toolGatePrecedence = "code",
|
|
6711
6827
|
routesRefreshMs,
|
|
6828
|
+
skipEvaluation,
|
|
6712
6829
|
...config
|
|
6713
6830
|
} = options;
|
|
6831
|
+
const skipTools = new Set(skipEvaluation?.tools ?? []);
|
|
6832
|
+
const skipMethods = new Set(skipEvaluation?.methods ?? []);
|
|
6714
6833
|
if (config.apiBaseUrl) {
|
|
6715
6834
|
prefetchWellKnown(config.apiBaseUrl).catch(() => {
|
|
6716
6835
|
});
|
|
6717
6836
|
}
|
|
6718
6837
|
const codeGatedTools = Object.entries(toolGates ?? {}).filter(([, g]) => g !== "observe").map(([name]) => name);
|
|
6838
|
+
let warnedEndpointType = false;
|
|
6839
|
+
const onEndpointType = (counterpartyType) => {
|
|
6840
|
+
if (counterpartyType === "mcp_server" || warnedEndpointType) return;
|
|
6841
|
+
warnedEndpointType = true;
|
|
6842
|
+
console.warn(
|
|
6843
|
+
`[VerificationGateway/MCP] counterpartyId ${config.counterpartyId} is a '${counterpartyType}' endpoint, not an 'mcp_server' endpoint. Dashboard tool rules are read from an mcp_server endpoint only: register a separate MCP endpoint for this server (same owner) and use its id here. See https://astrasync.ai/docs/mcp-integration#tool-rules.`
|
|
6844
|
+
);
|
|
6845
|
+
};
|
|
6719
6846
|
const rulesCache = config.apiKey && config.counterpartyId ? createRoutesCache(config, {
|
|
6720
6847
|
...routesRefreshMs !== void 0 && { refreshMs: routesRefreshMs },
|
|
6721
|
-
|
|
6848
|
+
fetchOptions: {
|
|
6849
|
+
...codeGatedTools.length && { codeGates: codeGatedTools },
|
|
6850
|
+
onEndpointType
|
|
6851
|
+
}
|
|
6722
6852
|
}) : void 0;
|
|
6723
6853
|
return async (req, res, next) => {
|
|
6724
6854
|
try {
|
|
@@ -6768,6 +6898,9 @@ function createMcpMiddleware(options) {
|
|
|
6768
6898
|
return next();
|
|
6769
6899
|
}
|
|
6770
6900
|
}
|
|
6901
|
+
if (!parsed.toolName && skipMethods.has(parsed.method) && methodGates?.[parsed.method] === void 0) {
|
|
6902
|
+
return next();
|
|
6903
|
+
}
|
|
6771
6904
|
const dashboardRules = rulesCache ? dashboardToolRules(await rulesCache.current()) : void 0;
|
|
6772
6905
|
const gateDecision = resolveGate(parsed, {
|
|
6773
6906
|
toolGates,
|
|
@@ -6775,6 +6908,9 @@ function createMcpMiddleware(options) {
|
|
|
6775
6908
|
dashboardRules,
|
|
6776
6909
|
toolGatePrecedence
|
|
6777
6910
|
});
|
|
6911
|
+
if (!gateDecision.enforce && (parsed.toolName && skipTools.has(parsed.toolName) || !parsed.toolName && skipMethods.has(parsed.method))) {
|
|
6912
|
+
return next();
|
|
6913
|
+
}
|
|
6778
6914
|
const credentials = extractCredentials(
|
|
6779
6915
|
req.headers,
|
|
6780
6916
|
req.query
|
|
@@ -6831,8 +6967,14 @@ function createMcpMiddleware(options) {
|
|
|
6831
6967
|
const forwardedFor = req.headers["x-forwarded-for"];
|
|
6832
6968
|
const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(", ") : forwardedFor;
|
|
6833
6969
|
const sourceIp = req.ip ?? (forwardedForStr ? forwardedForStr.split(",")[0].trim() : void 0);
|
|
6970
|
+
const attemptRaw = readSingleHeader(req.headers["x-astra-attempt-id"]);
|
|
6971
|
+
const metaAttempt = req.body?.params?._meta?.astrasync?.attemptId;
|
|
6972
|
+
const attemptId = [attemptRaw, metaAttempt].find(
|
|
6973
|
+
(v) => typeof v === "string" && /^att_[0-9a-f]{32}$/.test(v)
|
|
6974
|
+
);
|
|
6834
6975
|
const verificationRequest = {
|
|
6835
6976
|
credentials,
|
|
6977
|
+
...attemptId && { attemptId },
|
|
6836
6978
|
purpose: pdlss.purpose,
|
|
6837
6979
|
action: pdlss.action,
|
|
6838
6980
|
resource: pdlss.resource,
|
|
@@ -6894,14 +7036,14 @@ function createMcpMiddleware(options) {
|
|
|
6894
7036
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
6895
7037
|
res.setHeader("X-Astra-Gateway-Reason", "evaluated-not-enforced");
|
|
6896
7038
|
}
|
|
6897
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7039
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
6898
7040
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
6899
7041
|
});
|
|
6900
7042
|
}
|
|
6901
7043
|
return next();
|
|
6902
7044
|
}
|
|
6903
7045
|
if (!result.policyAllowed) {
|
|
6904
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7046
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
6905
7047
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
6906
7048
|
});
|
|
6907
7049
|
}
|
|
@@ -6911,7 +7053,7 @@ function createMcpMiddleware(options) {
|
|
|
6911
7053
|
}
|
|
6912
7054
|
if (requiresHumanApproval(result)) {
|
|
6913
7055
|
annotateApprovalRequired(result);
|
|
6914
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7056
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
6915
7057
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
6916
7058
|
});
|
|
6917
7059
|
}
|
|
@@ -6929,7 +7071,7 @@ function createMcpMiddleware(options) {
|
|
|
6929
7071
|
})
|
|
6930
7072
|
);
|
|
6931
7073
|
}
|
|
6932
|
-
if (shouldRecordDecisions && sessionId) {
|
|
7074
|
+
if (shouldRecordDecisions && sessionId && !isSharedVerdict(result)) {
|
|
6933
7075
|
recordDecision(config, sessionId, "granted").catch(() => {
|
|
6934
7076
|
});
|
|
6935
7077
|
}
|
|
@@ -8869,13 +9011,13 @@ function requireLoader() {
|
|
|
8869
9011
|
throwError(state, "ill-formed argument of the YAML directive");
|
|
8870
9012
|
}
|
|
8871
9013
|
const major = parseInt(match[1], 10);
|
|
8872
|
-
const
|
|
9014
|
+
const minor2 = parseInt(match[2], 10);
|
|
8873
9015
|
if (major !== 1) {
|
|
8874
9016
|
throwError(state, "unacceptable YAML version of the document");
|
|
8875
9017
|
}
|
|
8876
9018
|
state.version = args[0];
|
|
8877
|
-
state.checkLineBreaks =
|
|
8878
|
-
if (
|
|
9019
|
+
state.checkLineBreaks = minor2 < 2;
|
|
9020
|
+
if (minor2 !== 1 && minor2 !== 2) {
|
|
8879
9021
|
throwWarning(state, "unsupported YAML version of the document");
|
|
8880
9022
|
}
|
|
8881
9023
|
},
|
|
@@ -10938,6 +11080,16 @@ function checkUcpAgentBinding(header, identities) {
|
|
|
10938
11080
|
|
|
10939
11081
|
// src/ucp/profile.ts
|
|
10940
11082
|
var UCP_VERSION = "2026-08-25";
|
|
11083
|
+
function ucpProfileKeys(profile) {
|
|
11084
|
+
const out = [];
|
|
11085
|
+
for (const list of [profile?.keys, profile?.signing_keys]) {
|
|
11086
|
+
if (!Array.isArray(list)) continue;
|
|
11087
|
+
for (const k of list) {
|
|
11088
|
+
if (k && typeof k === "object" && k.use !== "enc") out.push(k);
|
|
11089
|
+
}
|
|
11090
|
+
}
|
|
11091
|
+
return out;
|
|
11092
|
+
}
|
|
10941
11093
|
var SPEC = `https://ucp.dev/${UCP_VERSION}/specification`;
|
|
10942
11094
|
var SCHEMAS = `https://ucp.dev/${UCP_VERSION}/schemas`;
|
|
10943
11095
|
function astrasyncPaymentHandlers(handlers, specBase = "https://astrasync.ai/ucp/handlers") {
|
|
@@ -11361,6 +11513,228 @@ async function sendUcpWebhook(opts) {
|
|
|
11361
11513
|
return { ok: false, status: last, attempts };
|
|
11362
11514
|
}
|
|
11363
11515
|
|
|
11516
|
+
// src/ucp/agent-commerce.ts
|
|
11517
|
+
var ISSUER_PROGRAM_HEADER = "X-Astra-Issuer-Program";
|
|
11518
|
+
function issuerOfferPrice(priceMinor, percentOff) {
|
|
11519
|
+
if (!percentOff) return priceMinor;
|
|
11520
|
+
const pct = Math.min(100, Math.max(0, percentOff));
|
|
11521
|
+
return Math.max(0, Math.round(priceMinor * (1 - pct / 100)));
|
|
11522
|
+
}
|
|
11523
|
+
function priceForAgent(rules, sku, basePriceMinor, ctx) {
|
|
11524
|
+
const rule = rules?.products?.[sku];
|
|
11525
|
+
let price = rule?.priceMinor ?? basePriceMinor;
|
|
11526
|
+
if (ctx.verified && rules?.verifiedPercentOff) {
|
|
11527
|
+
price = issuerOfferPrice(price, rules.verifiedPercentOff);
|
|
11528
|
+
}
|
|
11529
|
+
const offer = ctx.verified && ctx.issuerProgram && rule?.issuerEligible !== false ? rules?.offers?.find((o) => o.program === ctx.issuerProgram) : void 0;
|
|
11530
|
+
if (offer?.pricePercentOff) {
|
|
11531
|
+
return {
|
|
11532
|
+
priceMinor: issuerOfferPrice(price, offer.pricePercentOff),
|
|
11533
|
+
regularMinor: price,
|
|
11534
|
+
issuerProgram: offer.program
|
|
11535
|
+
};
|
|
11536
|
+
}
|
|
11537
|
+
return { priceMinor: price };
|
|
11538
|
+
}
|
|
11539
|
+
function visibleToAgent(rules, sku, ctx) {
|
|
11540
|
+
if (!ctx.verified && rules?.hideFromUnverified) return false;
|
|
11541
|
+
return rules?.products?.[sku]?.visible !== false;
|
|
11542
|
+
}
|
|
11543
|
+
function createAgentCommerce(opts) {
|
|
11544
|
+
const f = opts.fetch ?? sdkFetch;
|
|
11545
|
+
let current = null;
|
|
11546
|
+
let kitReport;
|
|
11547
|
+
const refreshMs = opts.refreshMs ?? 5 * 6e4;
|
|
11548
|
+
async function refresh() {
|
|
11549
|
+
try {
|
|
11550
|
+
const headers = {
|
|
11551
|
+
Accept: "application/json",
|
|
11552
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
11553
|
+
"X-AstraSync-SDK-Version": SDK_VERSION
|
|
11554
|
+
};
|
|
11555
|
+
if (kitReport) {
|
|
11556
|
+
headers["X-AstraSync-Kit-Report"] = Buffer.from(JSON.stringify(kitReport)).toString(
|
|
11557
|
+
"base64url"
|
|
11558
|
+
);
|
|
11559
|
+
}
|
|
11560
|
+
const res = await f(
|
|
11561
|
+
`${opts.apiBaseUrl.replace(/\/+$/, "")}/endpoints/${encodeURIComponent(
|
|
11562
|
+
opts.counterpartyId
|
|
11563
|
+
)}/agent-commerce`,
|
|
11564
|
+
{ method: "GET", headers }
|
|
11565
|
+
);
|
|
11566
|
+
if (!res.ok) return;
|
|
11567
|
+
const body = await res.json();
|
|
11568
|
+
if (body.data && typeof body.data.version === "number") current = body.data;
|
|
11569
|
+
} catch {
|
|
11570
|
+
}
|
|
11571
|
+
}
|
|
11572
|
+
const timer = refreshMs > 0 ? setInterval(() => {
|
|
11573
|
+
void refresh();
|
|
11574
|
+
}, refreshMs) : null;
|
|
11575
|
+
timer?.unref?.();
|
|
11576
|
+
void refresh();
|
|
11577
|
+
return {
|
|
11578
|
+
rules: () => current,
|
|
11579
|
+
refresh,
|
|
11580
|
+
price: (sku, base, ctx) => priceForAgent(current, sku, base, ctx),
|
|
11581
|
+
visible: (sku, ctx) => visibleToAgent(current, sku, ctx),
|
|
11582
|
+
report: (r) => {
|
|
11583
|
+
kitReport = { ...kitReport, ...r };
|
|
11584
|
+
},
|
|
11585
|
+
stop: () => {
|
|
11586
|
+
if (timer) clearInterval(timer);
|
|
11587
|
+
}
|
|
11588
|
+
};
|
|
11589
|
+
}
|
|
11590
|
+
function pricingContextFrom(headers) {
|
|
11591
|
+
const astraId = headers.get("x-astra-id");
|
|
11592
|
+
const hop = parseVerifiedHop(headers.get(MCP_VERIFIED_HOP_HEADER.toLowerCase()));
|
|
11593
|
+
const verified = !!astraId && (!hop || hop.astraId === astraId && Date.now() - hop.checkedAt <= MCP_VERIFIED_HOP_MAX_AGE_MS);
|
|
11594
|
+
const program = headers.get(ISSUER_PROGRAM_HEADER.toLowerCase());
|
|
11595
|
+
return {
|
|
11596
|
+
verified,
|
|
11597
|
+
...verified && program && /^[a-z0-9-]{2,40}$/.test(program) && { issuerProgram: program }
|
|
11598
|
+
};
|
|
11599
|
+
}
|
|
11600
|
+
function minor(value) {
|
|
11601
|
+
const n = typeof value === "number" ? value : Number(String(value ?? "").replace(/[^\d.]/g, ""));
|
|
11602
|
+
return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) : null;
|
|
11603
|
+
}
|
|
11604
|
+
function fromFeed(url, options = {}) {
|
|
11605
|
+
const f = options.fetch ?? fetch;
|
|
11606
|
+
let cache4 = null;
|
|
11607
|
+
return async () => {
|
|
11608
|
+
if (cache4 && Date.now() - cache4.at < 6e4) return cache4.products;
|
|
11609
|
+
const res = await f(url, {
|
|
11610
|
+
headers: { Accept: "application/json, application/xml, text/csv" }
|
|
11611
|
+
});
|
|
11612
|
+
if (!res.ok) throw new Error(`Product feed ${url} answered ${res.status}`);
|
|
11613
|
+
const text = await res.text();
|
|
11614
|
+
const products = parseFeed(text, options.currency ?? "AUD");
|
|
11615
|
+
cache4 = { at: Date.now(), products };
|
|
11616
|
+
return products;
|
|
11617
|
+
};
|
|
11618
|
+
}
|
|
11619
|
+
function parseFeed(text, currency) {
|
|
11620
|
+
const trimmed = text.trimStart();
|
|
11621
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
11622
|
+
const json4 = JSON.parse(trimmed);
|
|
11623
|
+
const list = Array.isArray(json4) ? json4 : json4.products ?? json4.items ?? [];
|
|
11624
|
+
return list.flatMap((p) => {
|
|
11625
|
+
const variant = p.variants?.[0];
|
|
11626
|
+
const price = minor(variant?.price ?? p.price);
|
|
11627
|
+
const sku = String(variant?.sku || p.sku || p.id || "");
|
|
11628
|
+
if (!sku || price === null) return [];
|
|
11629
|
+
const images = Array.isArray(p.images) ? p.images.map(
|
|
11630
|
+
(i) => typeof i === "string" ? i : i.src ?? ""
|
|
11631
|
+
) : [p.image_url, p.image].filter((x) => typeof x === "string");
|
|
11632
|
+
return [
|
|
11633
|
+
{
|
|
11634
|
+
sku,
|
|
11635
|
+
name: String(p.title ?? p.name ?? sku),
|
|
11636
|
+
priceMinor: price,
|
|
11637
|
+
currency: String(p.currency ?? currency),
|
|
11638
|
+
...typeof p.description === "string" && { description: p.description },
|
|
11639
|
+
imageUrls: images.filter((u) => /^https:\/\//.test(u)),
|
|
11640
|
+
inStock: variant ? variant.available !== false : p.in_stock !== false
|
|
11641
|
+
}
|
|
11642
|
+
];
|
|
11643
|
+
});
|
|
11644
|
+
}
|
|
11645
|
+
if (trimmed.startsWith("<")) {
|
|
11646
|
+
const items = trimmed.split(/<item[\s>]|<entry[\s>]/).slice(1);
|
|
11647
|
+
return items.flatMap((chunk) => {
|
|
11648
|
+
const tag = (name) => {
|
|
11649
|
+
const m = new RegExp(`<(?:g:)?${name}[^>]*>([\\s\\S]*?)</(?:g:)?${name}>`).exec(chunk);
|
|
11650
|
+
return m ? m[1].replace(/<!\[CDATA\[|\]\]>/g, "").trim() : void 0;
|
|
11651
|
+
};
|
|
11652
|
+
const sku = tag("id");
|
|
11653
|
+
const price = minor(tag("price"));
|
|
11654
|
+
if (!sku || price === null) return [];
|
|
11655
|
+
const extra = [...chunk.matchAll(/<g:additional_image_link>([^<]+)</g)].map(
|
|
11656
|
+
(m) => m[1].trim()
|
|
11657
|
+
);
|
|
11658
|
+
return [
|
|
11659
|
+
{
|
|
11660
|
+
sku,
|
|
11661
|
+
name: tag("title") ?? sku,
|
|
11662
|
+
priceMinor: price,
|
|
11663
|
+
currency: /[A-Z]{3}/.exec(tag("price") ?? "")?.[0] ?? currency,
|
|
11664
|
+
imageUrls: [tag("image_link"), ...extra].filter(
|
|
11665
|
+
(u) => !!u && /^https:\/\//.test(u)
|
|
11666
|
+
),
|
|
11667
|
+
inStock: (tag("availability") ?? "in stock").replace(/_/g, " ") !== "out of stock"
|
|
11668
|
+
}
|
|
11669
|
+
];
|
|
11670
|
+
});
|
|
11671
|
+
}
|
|
11672
|
+
const [head, ...rows] = trimmed.split(/\r?\n/).filter(Boolean);
|
|
11673
|
+
const cols = head.split(",").map((c) => c.trim().toLowerCase());
|
|
11674
|
+
const at = (r, ...names) => {
|
|
11675
|
+
const i = cols.findIndex((c) => names.includes(c));
|
|
11676
|
+
return i >= 0 ? r[i]?.trim() : void 0;
|
|
11677
|
+
};
|
|
11678
|
+
return rows.flatMap((line) => {
|
|
11679
|
+
const r = line.split(",");
|
|
11680
|
+
const sku = at(r, "id", "sku");
|
|
11681
|
+
const price = minor(at(r, "price"));
|
|
11682
|
+
if (!sku || price === null) return [];
|
|
11683
|
+
const image = at(r, "image_link", "image_url", "image");
|
|
11684
|
+
return [
|
|
11685
|
+
{
|
|
11686
|
+
sku,
|
|
11687
|
+
name: at(r, "title", "name") ?? sku,
|
|
11688
|
+
priceMinor: price,
|
|
11689
|
+
currency,
|
|
11690
|
+
imageUrls: image && /^https:\/\//.test(image) ? [image] : [],
|
|
11691
|
+
inStock: (at(r, "availability", "in_stock") ?? "in stock") !== "out of stock"
|
|
11692
|
+
}
|
|
11693
|
+
];
|
|
11694
|
+
});
|
|
11695
|
+
}
|
|
11696
|
+
function serveAgentCatalog(opts) {
|
|
11697
|
+
return async (req, res) => {
|
|
11698
|
+
const ctx = pricingContextFrom({
|
|
11699
|
+
get: (name) => {
|
|
11700
|
+
const v = req.headers[name.toLowerCase()];
|
|
11701
|
+
return Array.isArray(v) ? v[0] ?? null : v ?? null;
|
|
11702
|
+
}
|
|
11703
|
+
});
|
|
11704
|
+
let products;
|
|
11705
|
+
try {
|
|
11706
|
+
products = await opts.products();
|
|
11707
|
+
} catch {
|
|
11708
|
+
res.status(503).json({ error: "catalog_unavailable" });
|
|
11709
|
+
return;
|
|
11710
|
+
}
|
|
11711
|
+
const rules = opts.commerce.rules();
|
|
11712
|
+
res.setHeader?.("Cache-Control", ctx.verified ? "private, no-store" : "public, max-age=60");
|
|
11713
|
+
res.json({
|
|
11714
|
+
products: products.filter((p) => opts.commerce.visible(p.sku, ctx)).map((p) => {
|
|
11715
|
+
const priced = opts.commerce.price(p.sku, p.priceMinor, ctx);
|
|
11716
|
+
const image = rules?.products?.[p.sku]?.imageUrl ?? p.imageUrls?.[0];
|
|
11717
|
+
return {
|
|
11718
|
+
sku: p.sku,
|
|
11719
|
+
name: p.name,
|
|
11720
|
+
...p.description && { description: p.description },
|
|
11721
|
+
price: priced.priceMinor / 100,
|
|
11722
|
+
currency: p.currency,
|
|
11723
|
+
...image && { image_url: image },
|
|
11724
|
+
in_stock: p.inStock !== false,
|
|
11725
|
+
...p.url && { url: p.url },
|
|
11726
|
+
...priced.issuerProgram && {
|
|
11727
|
+
issuer_offer: {
|
|
11728
|
+
program: priced.issuerProgram,
|
|
11729
|
+
regular_price: (priced.regularMinor ?? p.priceMinor) / 100
|
|
11730
|
+
}
|
|
11731
|
+
}
|
|
11732
|
+
};
|
|
11733
|
+
})
|
|
11734
|
+
});
|
|
11735
|
+
};
|
|
11736
|
+
}
|
|
11737
|
+
|
|
11364
11738
|
// src/ucp/checkout-kit.ts
|
|
11365
11739
|
import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
|
|
11366
11740
|
import { CompactSign as CompactSign2 } from "jose";
|
|
@@ -12102,6 +12476,19 @@ var resumeResult = (outcome, session) => ({
|
|
|
12102
12476
|
});
|
|
12103
12477
|
|
|
12104
12478
|
// src/ucp/checkout-kit.ts
|
|
12479
|
+
function withValidImageUrl(item) {
|
|
12480
|
+
if (item.image_url === void 0) return item;
|
|
12481
|
+
let ok = false;
|
|
12482
|
+
try {
|
|
12483
|
+
const u = new URL(item.image_url);
|
|
12484
|
+
ok = u.protocol === "https:" || u.protocol === "http:";
|
|
12485
|
+
} catch {
|
|
12486
|
+
ok = false;
|
|
12487
|
+
}
|
|
12488
|
+
if (ok) return item;
|
|
12489
|
+
const { image_url: _drop, ...rest } = item;
|
|
12490
|
+
return rest;
|
|
12491
|
+
}
|
|
12105
12492
|
var MemoryCheckoutStore = class {
|
|
12106
12493
|
constructor() {
|
|
12107
12494
|
this.m = /* @__PURE__ */ new Map();
|
|
@@ -12171,6 +12558,7 @@ var clone = (v) => JSON.parse(JSON.stringify(v));
|
|
|
12171
12558
|
var inFlight = (st) => st === "complete_in_progress" || st === "finishing";
|
|
12172
12559
|
function createUcpCheckoutKit(opts) {
|
|
12173
12560
|
const profile = buildUcpProfile(opts.profile);
|
|
12561
|
+
opts.agentCommerce?.report({ checkoutUrl: opts.profile.endpoint, protocols: ["ucp"] });
|
|
12174
12562
|
const store = opts.store ?? new MemoryCheckoutStore();
|
|
12175
12563
|
const idem = {
|
|
12176
12564
|
store: opts.idempotencyStore ?? new MemoryIdempotencyStore(),
|
|
@@ -12344,9 +12732,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12344
12732
|
delete s._approvalId;
|
|
12345
12733
|
if (settled.status === "failed") {
|
|
12346
12734
|
s.status = "ready_for_complete";
|
|
12347
|
-
s.messages = [
|
|
12348
|
-
ucpError("payment_failed", "The payment did not go through.", "recoverable")
|
|
12349
|
-
];
|
|
12735
|
+
s.messages = [ucpError("payment_failed", "The payment did not go through.", "recoverable")];
|
|
12350
12736
|
await save(s);
|
|
12351
12737
|
return;
|
|
12352
12738
|
}
|
|
@@ -12380,12 +12766,14 @@ function createUcpCheckoutKit(opts) {
|
|
|
12380
12766
|
const b = checkUcpAgentBinding(req.headers.get("ucp-agent"), identities);
|
|
12381
12767
|
return b.ok ? null : b;
|
|
12382
12768
|
}
|
|
12383
|
-
async function priceLines(lines) {
|
|
12769
|
+
async function priceLines(lines, pricing = { verified: false }) {
|
|
12384
12770
|
const out = [];
|
|
12385
12771
|
const messages = [];
|
|
12772
|
+
let issuerProgram;
|
|
12386
12773
|
for (const [i, l] of lines.entries()) {
|
|
12387
12774
|
const q = Math.max(1, Math.floor(l.quantity ?? 1));
|
|
12388
|
-
const
|
|
12775
|
+
const found = l.item?.id ? await opts.catalog(l.item.id, q) : null;
|
|
12776
|
+
const item = found && withValidImageUrl(found);
|
|
12389
12777
|
if (!item) {
|
|
12390
12778
|
messages.push(
|
|
12391
12779
|
ucpError(
|
|
@@ -12397,6 +12785,22 @@ function createUcpCheckoutKit(opts) {
|
|
|
12397
12785
|
);
|
|
12398
12786
|
continue;
|
|
12399
12787
|
}
|
|
12788
|
+
if (opts.agentCommerce) {
|
|
12789
|
+
if (!opts.agentCommerce.visible(item.id, pricing)) {
|
|
12790
|
+
messages.push(
|
|
12791
|
+
ucpError(
|
|
12792
|
+
"item_unavailable",
|
|
12793
|
+
`Item ${item.id} is unavailable.`,
|
|
12794
|
+
"recoverable",
|
|
12795
|
+
`$.line_items[${i}]`
|
|
12796
|
+
)
|
|
12797
|
+
);
|
|
12798
|
+
continue;
|
|
12799
|
+
}
|
|
12800
|
+
const priced = opts.agentCommerce.price(item.id, item.price, pricing);
|
|
12801
|
+
item.price = priced.priceMinor;
|
|
12802
|
+
if (priced.issuerProgram) issuerProgram = priced.issuerProgram;
|
|
12803
|
+
}
|
|
12400
12804
|
const amount = item.price * q;
|
|
12401
12805
|
out.push({
|
|
12402
12806
|
id: `li_${i + 1}`,
|
|
@@ -12412,6 +12816,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12412
12816
|
return {
|
|
12413
12817
|
lines: out,
|
|
12414
12818
|
messages,
|
|
12819
|
+
issuerProgram,
|
|
12415
12820
|
totals: [
|
|
12416
12821
|
{ type: "subtotal", amount: sum },
|
|
12417
12822
|
{ type: "total", amount: sum }
|
|
@@ -12474,7 +12879,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12474
12879
|
{ method: req.method, url: req.url, headers, body: body || void 0 },
|
|
12475
12880
|
async (fresh) => {
|
|
12476
12881
|
const pp = fresh ? await fetchProfile(profileUrl, true) : plat;
|
|
12477
|
-
return "
|
|
12882
|
+
return "ucp" in pp ? ucpProfileKeys(pp) : [];
|
|
12478
12883
|
}
|
|
12479
12884
|
);
|
|
12480
12885
|
if (!sig.ok) return { error: json2(sig.status, { code: sig.code, content: sig.content }) };
|
|
@@ -12581,7 +12986,11 @@ function createUcpCheckoutKit(opts) {
|
|
|
12581
12986
|
...checkoutJwt && { checkoutJwt },
|
|
12582
12987
|
valueMajor: total(s) / 100,
|
|
12583
12988
|
agentArtifacts,
|
|
12584
|
-
metadata: {
|
|
12989
|
+
metadata: {
|
|
12990
|
+
kitSessionId: s.id,
|
|
12991
|
+
protocol: "ucp",
|
|
12992
|
+
...s._issuerProgram && { issuerProgram: s._issuerProgram }
|
|
12993
|
+
}
|
|
12585
12994
|
});
|
|
12586
12995
|
const binding2 = await bindAgent(req, result);
|
|
12587
12996
|
if (binding2) {
|
|
@@ -12720,7 +13129,11 @@ function createUcpCheckoutKit(opts) {
|
|
|
12720
13129
|
attemptId: s._attemptId,
|
|
12721
13130
|
...checkoutJwt && { checkoutJwt },
|
|
12722
13131
|
valueMajor: total(s) / 100,
|
|
12723
|
-
metadata: {
|
|
13132
|
+
metadata: {
|
|
13133
|
+
kitSessionId: s.id,
|
|
13134
|
+
protocol: "ucp",
|
|
13135
|
+
...s._issuerProgram && { issuerProgram: s._issuerProgram }
|
|
13136
|
+
},
|
|
12724
13137
|
resumed: { ...s._approvalId && { approvalId: s._approvalId } }
|
|
12725
13138
|
});
|
|
12726
13139
|
if (isHeld(result)) {
|
|
@@ -12770,7 +13183,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12770
13183
|
ucpError(
|
|
12771
13184
|
`approval_${reason}`,
|
|
12772
13185
|
reason === "denied" ? "The buyer declined this purchase." : "The approval for this purchase is no longer valid. Complete the checkout again to request a new one.",
|
|
12773
|
-
"
|
|
13186
|
+
"recoverable"
|
|
12774
13187
|
)
|
|
12775
13188
|
];
|
|
12776
13189
|
await store.put(s);
|
|
@@ -12805,7 +13218,7 @@ function createUcpCheckoutKit(opts) {
|
|
|
12805
13218
|
});
|
|
12806
13219
|
if (status === "approved" || status === "consumed") {
|
|
12807
13220
|
out.push({ sessionId: s.id, outcome: (await resumeApproved(s.id)).outcome });
|
|
12808
|
-
} else if (status === "denied" || status === "expired") {
|
|
13221
|
+
} else if (status === "denied" || status === "expired" || status === "lapsed") {
|
|
12809
13222
|
out.push({ sessionId: s.id, outcome: (await resumeDeclined(s.id, status)).outcome });
|
|
12810
13223
|
} else {
|
|
12811
13224
|
out.push({ sessionId: s.id, outcome: "pending" });
|
|
@@ -12835,7 +13248,10 @@ function createUcpCheckoutKit(opts) {
|
|
|
12835
13248
|
const caller = await resolveCallerScope(req, reqCtx, opts.resolveCallerId) ?? fallbackScope("ucp-profile", ctx.profileUrl);
|
|
12836
13249
|
if (req.method === "POST" && path === "/checkout-sessions") {
|
|
12837
13250
|
return withIdempotency2(req, body, path, caller, async () => {
|
|
12838
|
-
const priced = await priceLines(
|
|
13251
|
+
const priced = await priceLines(
|
|
13252
|
+
parsed.line_items ?? [],
|
|
13253
|
+
pricingContextFrom(req.headers)
|
|
13254
|
+
);
|
|
12839
13255
|
const webhookUrl = ctx.plat.ucp.capabilities?.[UCP_ORDER_CAPABILITY]?.[0]?.config?.webhook_url;
|
|
12840
13256
|
const s2 = {
|
|
12841
13257
|
id: `chk_${randomUUID2().replace(/-/g, "")}`,
|
|
@@ -12848,11 +13264,12 @@ function createUcpCheckoutKit(opts) {
|
|
|
12848
13264
|
_caller: caller,
|
|
12849
13265
|
_platformProfile: ctx.profileUrl,
|
|
12850
13266
|
_ap2: ctx.ap2,
|
|
12851
|
-
_platformKeys: ctx.plat
|
|
13267
|
+
_platformKeys: ucpProfileKeys(ctx.plat),
|
|
12852
13268
|
...typeof webhookUrl === "string" && /^https:\/\//.test(webhookUrl) && { _webhookUrl: webhookUrl },
|
|
12853
13269
|
...req.headers.get("x-astra-checkout-session") && {
|
|
12854
13270
|
_astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
|
|
12855
|
-
}
|
|
13271
|
+
},
|
|
13272
|
+
...priced.issuerProgram && { _issuerProgram: priced.issuerProgram }
|
|
12856
13273
|
};
|
|
12857
13274
|
if (!priced.lines.length) {
|
|
12858
13275
|
return json2(
|
|
@@ -12897,8 +13314,13 @@ function createUcpCheckoutKit(opts) {
|
|
|
12897
13314
|
if (inFlight(s.status)) return busy(s);
|
|
12898
13315
|
if (s.status === "requires_escalation") return heldNoEdit(s);
|
|
12899
13316
|
if (parsed.line_items) {
|
|
12900
|
-
const priced = await priceLines(
|
|
13317
|
+
const priced = await priceLines(
|
|
13318
|
+
parsed.line_items,
|
|
13319
|
+
pricingContextFrom(req.headers)
|
|
13320
|
+
);
|
|
12901
13321
|
s.line_items = priced.lines;
|
|
13322
|
+
if (priced.issuerProgram) s._issuerProgram = priced.issuerProgram;
|
|
13323
|
+
else delete s._issuerProgram;
|
|
12902
13324
|
s.totals = priced.totals;
|
|
12903
13325
|
s.messages = priced.messages;
|
|
12904
13326
|
s.status = priced.lines.length && !priced.messages.length ? "ready_for_complete" : "incomplete";
|
|
@@ -13540,7 +13962,12 @@ var ACP_CODES = /* @__PURE__ */ new Set([
|
|
|
13540
13962
|
"out_of_stock",
|
|
13541
13963
|
"payment_declined",
|
|
13542
13964
|
"requires_sign_in",
|
|
13543
|
-
"requires_3ds"
|
|
13965
|
+
"requires_3ds",
|
|
13966
|
+
// 5.20.0: the owner-approval codes the kit itself emits.
|
|
13967
|
+
"approval_required",
|
|
13968
|
+
"approval_denied",
|
|
13969
|
+
"approval_expired",
|
|
13970
|
+
"approval_lapsed"
|
|
13544
13971
|
]);
|
|
13545
13972
|
function heldBody2(text) {
|
|
13546
13973
|
try {
|
|
@@ -13550,6 +13977,7 @@ function heldBody2(text) {
|
|
|
13550
13977
|
}
|
|
13551
13978
|
}
|
|
13552
13979
|
function createAcpCheckoutKit(opts) {
|
|
13980
|
+
opts.agentCommerce?.report({ protocols: ["acp"] });
|
|
13553
13981
|
const mem = /* @__PURE__ */ new Map();
|
|
13554
13982
|
const store = opts.store ?? {
|
|
13555
13983
|
get: async (id) => mem.get(id) ?? null,
|
|
@@ -13602,12 +14030,14 @@ function createAcpCheckoutKit(opts) {
|
|
|
13602
14030
|
...s.order && { order: s.order }
|
|
13603
14031
|
};
|
|
13604
14032
|
}
|
|
13605
|
-
async function price(items) {
|
|
14033
|
+
async function price(items, pricing = { verified: false }) {
|
|
13606
14034
|
const lines = [];
|
|
13607
14035
|
const messages = [];
|
|
14036
|
+
let issuerProgram;
|
|
13608
14037
|
for (const [i, it] of items.entries()) {
|
|
13609
14038
|
const q = Math.max(1, Math.floor(it.quantity ?? 1));
|
|
13610
|
-
const
|
|
14039
|
+
const found = it.id ? await opts.catalog(it.id, q) : null;
|
|
14040
|
+
const c = found && withValidImageUrl(found);
|
|
13611
14041
|
if (!c) {
|
|
13612
14042
|
messages.push({
|
|
13613
14043
|
type: "error",
|
|
@@ -13618,10 +14048,31 @@ function createAcpCheckoutKit(opts) {
|
|
|
13618
14048
|
});
|
|
13619
14049
|
continue;
|
|
13620
14050
|
}
|
|
14051
|
+
if (opts.agentCommerce) {
|
|
14052
|
+
if (!opts.agentCommerce.visible(c.id, pricing)) {
|
|
14053
|
+
messages.push({
|
|
14054
|
+
type: "error",
|
|
14055
|
+
code: "not_found",
|
|
14056
|
+
content: `Item ${c.id} not found.`,
|
|
14057
|
+
resolution: "recoverable",
|
|
14058
|
+
param: `$.line_items[${i}]`
|
|
14059
|
+
});
|
|
14060
|
+
continue;
|
|
14061
|
+
}
|
|
14062
|
+
const priced = opts.agentCommerce.price(c.id, c.price, pricing);
|
|
14063
|
+
c.price = priced.priceMinor;
|
|
14064
|
+
if (priced.issuerProgram) issuerProgram = priced.issuerProgram;
|
|
14065
|
+
}
|
|
13621
14066
|
const amount = c.price * q;
|
|
13622
14067
|
lines.push({
|
|
13623
14068
|
id: `li_${i + 1}`,
|
|
13624
|
-
item: {
|
|
14069
|
+
item: {
|
|
14070
|
+
id: c.id,
|
|
14071
|
+
name: c.title,
|
|
14072
|
+
unit_amount: c.price,
|
|
14073
|
+
...c.image_url && { image_url: c.image_url },
|
|
14074
|
+
...typeof c.description === "string" && { description: c.description }
|
|
14075
|
+
},
|
|
13625
14076
|
quantity: q,
|
|
13626
14077
|
name: c.title,
|
|
13627
14078
|
unit_amount: c.price,
|
|
@@ -13635,6 +14086,7 @@ function createAcpCheckoutKit(opts) {
|
|
|
13635
14086
|
return {
|
|
13636
14087
|
lines,
|
|
13637
14088
|
messages,
|
|
14089
|
+
issuerProgram,
|
|
13638
14090
|
totals: [
|
|
13639
14091
|
{ type: "subtotal", amount: sum },
|
|
13640
14092
|
{ type: "total", amount: sum }
|
|
@@ -13786,7 +14238,11 @@ function createAcpCheckoutKit(opts) {
|
|
|
13786
14238
|
attemptId,
|
|
13787
14239
|
valueMajor: total2(s) / 100,
|
|
13788
14240
|
agentArtifacts,
|
|
13789
|
-
metadata: {
|
|
14241
|
+
metadata: {
|
|
14242
|
+
kitSessionId: s.id,
|
|
14243
|
+
protocol: "acp",
|
|
14244
|
+
...s._issuerProgram && { issuerProgram: s._issuerProgram }
|
|
14245
|
+
}
|
|
13790
14246
|
});
|
|
13791
14247
|
if (isHeld(result)) {
|
|
13792
14248
|
s.status = "pending_approval";
|
|
@@ -13923,7 +14379,11 @@ function createAcpCheckoutKit(opts) {
|
|
|
13923
14379
|
astraId: s._astraId,
|
|
13924
14380
|
attemptId: s._attemptId,
|
|
13925
14381
|
valueMajor: total2(s) / 100,
|
|
13926
|
-
metadata: {
|
|
14382
|
+
metadata: {
|
|
14383
|
+
kitSessionId: s.id,
|
|
14384
|
+
protocol: "acp",
|
|
14385
|
+
...s._issuerProgram && { issuerProgram: s._issuerProgram }
|
|
14386
|
+
},
|
|
13927
14387
|
resumed: { ...s._approvalId && { approvalId: s._approvalId } }
|
|
13928
14388
|
});
|
|
13929
14389
|
if (isHeld(result)) {
|
|
@@ -13968,7 +14428,7 @@ function createAcpCheckoutKit(opts) {
|
|
|
13968
14428
|
code: `approval_${reason}`,
|
|
13969
14429
|
content: reason === "denied" ? "The buyer declined this purchase." : "The approval for this purchase is no longer valid. Complete the session again to request a new one.",
|
|
13970
14430
|
severity: "medium",
|
|
13971
|
-
resolution: "
|
|
14431
|
+
resolution: "recoverable"
|
|
13972
14432
|
}
|
|
13973
14433
|
];
|
|
13974
14434
|
await store.put(s);
|
|
@@ -14003,7 +14463,7 @@ function createAcpCheckoutKit(opts) {
|
|
|
14003
14463
|
});
|
|
14004
14464
|
if (status === "approved" || status === "consumed") {
|
|
14005
14465
|
out.push({ sessionId: s.id, outcome: (await resumeApproved(s.id)).outcome });
|
|
14006
|
-
} else if (status === "denied" || status === "expired") {
|
|
14466
|
+
} else if (status === "denied" || status === "expired" || status === "lapsed") {
|
|
14007
14467
|
out.push({ sessionId: s.id, outcome: (await resumeDeclined(s.id, status)).outcome });
|
|
14008
14468
|
} else {
|
|
14009
14469
|
out.push({ sessionId: s.id, outcome: "pending" });
|
|
@@ -14041,7 +14501,7 @@ function createAcpCheckoutKit(opts) {
|
|
|
14041
14501
|
const items = parsed.line_items ?? parsed.items;
|
|
14042
14502
|
if (!items?.length)
|
|
14043
14503
|
return apiError(400, "missing", "line_items is required.", "$.line_items");
|
|
14044
|
-
const p = await price(items);
|
|
14504
|
+
const p = await price(items, pricingContextFrom(req.headers));
|
|
14045
14505
|
const s2 = {
|
|
14046
14506
|
id: `cs_${randomUUID3().replace(/-/g, "")}`,
|
|
14047
14507
|
status: p.lines.length && !p.messages.length ? "ready_for_payment" : "not_ready_for_payment",
|
|
@@ -14052,7 +14512,8 @@ function createAcpCheckoutKit(opts) {
|
|
|
14052
14512
|
_caller: caller,
|
|
14053
14513
|
...req.headers.get("x-astra-checkout-session") && {
|
|
14054
14514
|
_astraCheckoutSessionId: req.headers.get("x-astra-checkout-session").slice(0, 64)
|
|
14055
|
-
}
|
|
14515
|
+
},
|
|
14516
|
+
...p.issuerProgram && { _issuerProgram: p.issuerProgram }
|
|
14056
14517
|
};
|
|
14057
14518
|
if (s2.status === "ready_for_payment") {
|
|
14058
14519
|
const v = await validate(s2);
|
|
@@ -14081,7 +14542,9 @@ function createAcpCheckoutKit(opts) {
|
|
|
14081
14542
|
);
|
|
14082
14543
|
const items = parsed.line_items ?? parsed.items;
|
|
14083
14544
|
if (items) {
|
|
14084
|
-
const p = await price(items);
|
|
14545
|
+
const p = await price(items, pricingContextFrom(req.headers));
|
|
14546
|
+
if (p.issuerProgram) s._issuerProgram = p.issuerProgram;
|
|
14547
|
+
else delete s._issuerProgram;
|
|
14085
14548
|
Object.assign(s, {
|
|
14086
14549
|
line_items: p.lines,
|
|
14087
14550
|
totals: p.totals,
|
|
@@ -14231,6 +14694,7 @@ export {
|
|
|
14231
14694
|
DECLARED_SOURCE_MAX,
|
|
14232
14695
|
DEFAULT_EDGE_CONFIG,
|
|
14233
14696
|
DEFAULT_IDEMPOTENCY_TTL_MS,
|
|
14697
|
+
ISSUER_PROGRAM_HEADER,
|
|
14234
14698
|
KYDRequiredError,
|
|
14235
14699
|
MAX_HEADERS,
|
|
14236
14700
|
MAX_HEADER_VALUE_BYTES,
|
|
@@ -14289,6 +14753,7 @@ export {
|
|
|
14289
14753
|
closedPaymentMandate,
|
|
14290
14754
|
contentDigest,
|
|
14291
14755
|
createAcpCheckoutKit,
|
|
14756
|
+
createAgentCommerce,
|
|
14292
14757
|
createKbHop,
|
|
14293
14758
|
createMcpMiddleware,
|
|
14294
14759
|
createUcpCheckoutKit,
|
|
@@ -14310,6 +14775,7 @@ export {
|
|
|
14310
14775
|
fetchRoutes,
|
|
14311
14776
|
fetchUcpProfile,
|
|
14312
14777
|
finishIdempotent,
|
|
14778
|
+
fromFeed,
|
|
14313
14779
|
getCachedWellKnownUrls,
|
|
14314
14780
|
getEdgeConfig,
|
|
14315
14781
|
getTrustLevel,
|
|
@@ -14320,10 +14786,12 @@ export {
|
|
|
14320
14786
|
isEdgeConfig,
|
|
14321
14787
|
isRejectedIdentityClaim,
|
|
14322
14788
|
isRetryableSettlementError,
|
|
14789
|
+
isSharedVerdict,
|
|
14323
14790
|
isSpecialUseIp,
|
|
14324
14791
|
isVerificationUnavailable,
|
|
14325
14792
|
isVerifiedHopValidFor,
|
|
14326
14793
|
issueDelegateSdJwt,
|
|
14794
|
+
issuerOfferPrice,
|
|
14327
14795
|
matchEdgePathRule,
|
|
14328
14796
|
matchPlatformSignature,
|
|
14329
14797
|
matchRoutePattern,
|
|
@@ -14331,8 +14799,10 @@ export {
|
|
|
14331
14799
|
negotiateUcp,
|
|
14332
14800
|
nextjs_exports as nextjs,
|
|
14333
14801
|
normalizeDeclaredSource,
|
|
14802
|
+
onVerificationResult,
|
|
14334
14803
|
openCheckoutMandate,
|
|
14335
14804
|
openPaymentMandate,
|
|
14805
|
+
parseFeed,
|
|
14336
14806
|
parseYaml as parseLocalPolicyYaml,
|
|
14337
14807
|
parseMppCredential,
|
|
14338
14808
|
parseUcpAgentHeader,
|
|
@@ -14341,6 +14811,7 @@ export {
|
|
|
14341
14811
|
parseX402Payment,
|
|
14342
14812
|
pollStepUpStatus,
|
|
14343
14813
|
prefetchWellKnown,
|
|
14814
|
+
priceForAgent,
|
|
14344
14815
|
quickVerify,
|
|
14345
14816
|
recordDecision2 as recordDecision,
|
|
14346
14817
|
reportAttempt,
|
|
@@ -14354,6 +14825,7 @@ export {
|
|
|
14354
14825
|
sdkFetch,
|
|
14355
14826
|
sendUcpWebhook,
|
|
14356
14827
|
serializeVerifiedHop,
|
|
14828
|
+
serveAgentCatalog,
|
|
14357
14829
|
setMcpMeta,
|
|
14358
14830
|
signHttpRequest,
|
|
14359
14831
|
signRootMandate,
|
|
@@ -14364,6 +14836,7 @@ export {
|
|
|
14364
14836
|
ucpEscalation,
|
|
14365
14837
|
ucpFailure,
|
|
14366
14838
|
ucpProfileHandler,
|
|
14839
|
+
ucpProfileKeys,
|
|
14367
14840
|
ucpRequiredComponents,
|
|
14368
14841
|
unavailableRetryAfterSeconds,
|
|
14369
14842
|
validatePolicy as validateLocalPolicy,
|