@probemesh/sdk 0.1.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 (83) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +160 -0
  3. package/dist/chunk-257Y7LN2.js +827 -0
  4. package/dist/chunk-257Y7LN2.js.map +1 -0
  5. package/dist/chunk-2ASMVLG4.js +56 -0
  6. package/dist/chunk-2ASMVLG4.js.map +1 -0
  7. package/dist/chunk-2KWGHJYP.js +285 -0
  8. package/dist/chunk-2KWGHJYP.js.map +1 -0
  9. package/dist/chunk-3H7UGVI6.js +1117 -0
  10. package/dist/chunk-3H7UGVI6.js.map +1 -0
  11. package/dist/chunk-5Q3PDYIA.js +657 -0
  12. package/dist/chunk-5Q3PDYIA.js.map +1 -0
  13. package/dist/chunk-CXZOO3U4.js +550 -0
  14. package/dist/chunk-CXZOO3U4.js.map +1 -0
  15. package/dist/chunk-FRFBK4SY.js +270 -0
  16. package/dist/chunk-FRFBK4SY.js.map +1 -0
  17. package/dist/chunk-HJK52QJF.js +994 -0
  18. package/dist/chunk-HJK52QJF.js.map +1 -0
  19. package/dist/chunk-HNBDX7IU.js +705 -0
  20. package/dist/chunk-HNBDX7IU.js.map +1 -0
  21. package/dist/chunk-NYTV263W.js +116 -0
  22. package/dist/chunk-NYTV263W.js.map +1 -0
  23. package/dist/chunk-PXG54XOG.js +595 -0
  24. package/dist/chunk-PXG54XOG.js.map +1 -0
  25. package/dist/chunk-TDOBAMYM.js +607 -0
  26. package/dist/chunk-TDOBAMYM.js.map +1 -0
  27. package/dist/chunk-TV42EZSI.js +2157 -0
  28. package/dist/chunk-TV42EZSI.js.map +1 -0
  29. package/dist/chunk-UU2ZG7P7.js +408 -0
  30. package/dist/chunk-UU2ZG7P7.js.map +1 -0
  31. package/dist/chunk-WKN7QOCA.js +977 -0
  32. package/dist/chunk-WKN7QOCA.js.map +1 -0
  33. package/dist/chunk-ZJOLPBJJ.js +1091 -0
  34. package/dist/chunk-ZJOLPBJJ.js.map +1 -0
  35. package/dist/cli/audit-trail-export.cjs +1193 -0
  36. package/dist/cli/audit-trail-export.cjs.map +1 -0
  37. package/dist/cli/audit-trail-export.d.cts +1 -0
  38. package/dist/cli/audit-trail-export.d.ts +1 -0
  39. package/dist/cli/audit-trail-export.js +24 -0
  40. package/dist/cli/audit-trail-export.js.map +1 -0
  41. package/dist/cli/catalog-check.cjs +2687 -0
  42. package/dist/cli/catalog-check.cjs.map +1 -0
  43. package/dist/cli/catalog-check.d.cts +1 -0
  44. package/dist/cli/catalog-check.d.ts +1 -0
  45. package/dist/cli/catalog-check.js +26 -0
  46. package/dist/cli/catalog-check.js.map +1 -0
  47. package/dist/cli/probemesh-init.cjs +1049 -0
  48. package/dist/cli/probemesh-init.cjs.map +1 -0
  49. package/dist/cli/probemesh-init.d.cts +1 -0
  50. package/dist/cli/probemesh-init.d.ts +1 -0
  51. package/dist/cli/probemesh-init.js +22 -0
  52. package/dist/cli/probemesh-init.js.map +1 -0
  53. package/dist/cli/provider-conformance.cjs +6180 -0
  54. package/dist/cli/provider-conformance.cjs.map +1 -0
  55. package/dist/cli/provider-conformance.d.cts +1 -0
  56. package/dist/cli/provider-conformance.d.ts +1 -0
  57. package/dist/cli/provider-conformance.js +29 -0
  58. package/dist/cli/provider-conformance.js.map +1 -0
  59. package/dist/cli/provider-dossier-check.cjs +2978 -0
  60. package/dist/cli/provider-dossier-check.cjs.map +1 -0
  61. package/dist/cli/provider-dossier-check.d.cts +1 -0
  62. package/dist/cli/provider-dossier-check.d.ts +1 -0
  63. package/dist/cli/provider-dossier-check.js +27 -0
  64. package/dist/cli/provider-dossier-check.js.map +1 -0
  65. package/dist/cli/provider-dossier.cjs +1753 -0
  66. package/dist/cli/provider-dossier.cjs.map +1 -0
  67. package/dist/cli/provider-dossier.d.cts +1 -0
  68. package/dist/cli/provider-dossier.d.ts +1 -0
  69. package/dist/cli/provider-dossier.js +26 -0
  70. package/dist/cli/provider-dossier.js.map +1 -0
  71. package/dist/cli/x402-accept.cjs +6009 -0
  72. package/dist/cli/x402-accept.cjs.map +1 -0
  73. package/dist/cli/x402-accept.d.cts +1 -0
  74. package/dist/cli/x402-accept.d.ts +1 -0
  75. package/dist/cli/x402-accept.js +28 -0
  76. package/dist/cli/x402-accept.js.map +1 -0
  77. package/dist/index.cjs +13671 -0
  78. package/dist/index.cjs.map +1 -0
  79. package/dist/index.d.cts +2026 -0
  80. package/dist/index.d.ts +2026 -0
  81. package/dist/index.js +2560 -0
  82. package/dist/index.js.map +1 -0
  83. package/package.json +111 -0
@@ -0,0 +1,2157 @@
1
+ import {
2
+ createProbeMeshCallAuditArtifact
3
+ } from "./chunk-5Q3PDYIA.js";
4
+ import {
5
+ verifyProviderCatalogPolicyBundleRuntime
6
+ } from "./chunk-CXZOO3U4.js";
7
+ import {
8
+ assertProviderCatalog,
9
+ findProviderCatalogMatches
10
+ } from "./chunk-ZJOLPBJJ.js";
11
+ import {
12
+ mergePaymentPreferences,
13
+ paymentSelectionToCost,
14
+ resolveProviderPaymentOption,
15
+ validatePaymentPreferences,
16
+ validatePaymentStrategy
17
+ } from "./chunk-3H7UGVI6.js";
18
+ import {
19
+ ProbeMeshError,
20
+ isProbeMeshError
21
+ } from "./chunk-2ASMVLG4.js";
22
+
23
+ // src/client.ts
24
+ import { randomUUID } from "crypto";
25
+ function createProbeMesh(options) {
26
+ validateClientOptions(options);
27
+ if (options.hosted) {
28
+ return createHostedProbeMeshClient(options.hosted);
29
+ }
30
+ const adapters = [...options.adapters];
31
+ return {
32
+ async call(request) {
33
+ validateCallRequest(request);
34
+ const callId = request.idempotencyKey ?? generateCallId();
35
+ const startedAt = /* @__PURE__ */ new Date();
36
+ const startTimeMs = Date.now();
37
+ const adapterSelection = selectAdapter(adapters, request);
38
+ const adapter = adapterSelection?.adapter;
39
+ const paymentSelection = adapterSelection?.paymentSelection;
40
+ const abortController = new AbortController();
41
+ if (!adapter) {
42
+ throw new ProbeMeshError({
43
+ code: "provider_unavailable",
44
+ message: `No adapter is available for capability "${request.capability}".`,
45
+ capability: request.capability,
46
+ callId
47
+ });
48
+ }
49
+ enforceBudget(
50
+ request.constraints,
51
+ adapter,
52
+ request.capability,
53
+ callId,
54
+ paymentSelection
55
+ );
56
+ try {
57
+ const result = await withLatencyLimit(
58
+ adapter.call(
59
+ {
60
+ ...request,
61
+ callId
62
+ },
63
+ {
64
+ callId,
65
+ startedAt,
66
+ signal: abortController.signal,
67
+ constraints: request.constraints,
68
+ providerPreferences: request.providerPreferences,
69
+ paymentPreferences: request.paymentPreferences,
70
+ paymentStrategy: request.paymentStrategy,
71
+ paymentSelection
72
+ }
73
+ ),
74
+ request,
75
+ adapter,
76
+ callId,
77
+ abortController
78
+ );
79
+ const normalizedResult = validateAdapterResult(
80
+ result,
81
+ adapter,
82
+ request.capability,
83
+ callId
84
+ );
85
+ const receiptRefs = normalizedResult.receiptRefs ?? [];
86
+ if (request.constraints?.requireReceipt && receiptRefs.length === 0) {
87
+ throw new ProbeMeshError({
88
+ code: "receipt_missing",
89
+ message: `Adapter "${adapter.id}" did not return a receipt for capability "${request.capability}".`,
90
+ capability: request.capability,
91
+ provider: adapter.id,
92
+ callId
93
+ });
94
+ }
95
+ return {
96
+ callId,
97
+ capability: request.capability,
98
+ data: normalizedResult.data,
99
+ provider: {
100
+ id: normalizedResult.provider?.id ?? adapter.id,
101
+ mode: normalizedResult.provider?.mode ?? adapter.mode ?? "local",
102
+ displayName: normalizedResult.provider?.displayName ?? adapter.displayName
103
+ },
104
+ cost: normalizedResult.cost ?? paymentSelectionToCost(paymentSelection) ?? pricingToCost(getPricing(adapter, request.capability)),
105
+ latencyMs: Date.now() - startTimeMs,
106
+ receiptRefs,
107
+ safety: normalizedResult.safety,
108
+ status: "completed"
109
+ };
110
+ } catch (error) {
111
+ if (isProbeMeshError(error)) {
112
+ throw error;
113
+ }
114
+ throw new ProbeMeshError({
115
+ code: "provider_unavailable",
116
+ message: `Adapter "${adapter.id}" failed while handling capability "${request.capability}".`,
117
+ capability: request.capability,
118
+ provider: adapter.id,
119
+ callId,
120
+ cause: error
121
+ });
122
+ }
123
+ },
124
+ async retry() {
125
+ throw new ProbeMeshError({
126
+ code: "invalid_request",
127
+ message: "ProbeMesh retry is only available for hosted clients."
128
+ });
129
+ }
130
+ };
131
+ }
132
+ function createHostedProbeMeshClient(options) {
133
+ const endpoint = new URL("/v1/calls", ensureTrailingSlash(options.baseUrl));
134
+ return {
135
+ async call(request) {
136
+ validateCallRequest(request);
137
+ const response = await fetch(endpoint, {
138
+ method: "POST",
139
+ headers: {
140
+ "content-type": "application/json",
141
+ ...options.apiKey ? {
142
+ authorization: `Bearer ${options.apiKey}`
143
+ } : {}
144
+ },
145
+ body: JSON.stringify(request)
146
+ });
147
+ const body = await readHostedJson(response);
148
+ if (!response.ok) {
149
+ throw hostedErrorToProbeMeshError(body, response.status);
150
+ }
151
+ return validateHostedResponse(body);
152
+ },
153
+ async retry(request) {
154
+ validateRetryRequest(request);
155
+ const retryEndpoint = new URL(
156
+ `/v1/calls/${encodeURIComponent(request.callId)}/retry`,
157
+ ensureTrailingSlash(options.baseUrl)
158
+ );
159
+ const response = await fetch(retryEndpoint, {
160
+ method: "POST",
161
+ headers: {
162
+ "content-type": "application/json",
163
+ ...options.apiKey ? {
164
+ authorization: `Bearer ${options.apiKey}`
165
+ } : {}
166
+ },
167
+ body: JSON.stringify({
168
+ idempotencyKey: request.idempotencyKey
169
+ })
170
+ });
171
+ const body = await readHostedJson(response);
172
+ if (!response.ok) {
173
+ throw hostedErrorToProbeMeshError(body, response.status);
174
+ }
175
+ return validateHostedResponse(body);
176
+ }
177
+ };
178
+ }
179
+ function validateClientOptions(options) {
180
+ if (!options || typeof options !== "object") {
181
+ throw new ProbeMeshError({
182
+ code: "invalid_request",
183
+ message: "ProbeMesh client options must be an object."
184
+ });
185
+ }
186
+ const hasAdapters = options.adapters !== void 0;
187
+ const hasHosted = options.hosted !== void 0;
188
+ if (hasAdapters && hasHosted) {
189
+ throw new ProbeMeshError({
190
+ code: "invalid_request",
191
+ message: "Use either adapters or hosted options, not both."
192
+ });
193
+ }
194
+ if (!hasAdapters && !hasHosted) {
195
+ throw new ProbeMeshError({
196
+ code: "invalid_request",
197
+ message: "ProbeMesh client requires adapters or hosted options."
198
+ });
199
+ }
200
+ if (hasAdapters && !Array.isArray(options.adapters)) {
201
+ throw new ProbeMeshError({
202
+ code: "invalid_request",
203
+ message: "adapters must be an array when provided."
204
+ });
205
+ }
206
+ if (hasHosted) {
207
+ validateHostedOptions(options.hosted);
208
+ }
209
+ }
210
+ function validateHostedOptions(hosted) {
211
+ if (!hosted || typeof hosted !== "object") {
212
+ throw new ProbeMeshError({
213
+ code: "invalid_request",
214
+ message: "hosted options must be an object."
215
+ });
216
+ }
217
+ if (typeof hosted.baseUrl !== "string" || hosted.baseUrl.length === 0) {
218
+ throw new ProbeMeshError({
219
+ code: "invalid_request",
220
+ message: "hosted.baseUrl must be a non-empty string."
221
+ });
222
+ }
223
+ if (hosted.apiKey !== void 0 && typeof hosted.apiKey !== "string") {
224
+ throw new ProbeMeshError({
225
+ code: "invalid_request",
226
+ message: "hosted.apiKey must be a string when provided."
227
+ });
228
+ }
229
+ }
230
+ async function readHostedJson(response) {
231
+ const text = await response.text();
232
+ if (text.length === 0) {
233
+ return void 0;
234
+ }
235
+ try {
236
+ return JSON.parse(text);
237
+ } catch (error) {
238
+ throw new ProbeMeshError({
239
+ code: "invalid_provider_response",
240
+ message: `Hosted router returned invalid JSON with status ${response.status}.`,
241
+ cause: error
242
+ });
243
+ }
244
+ }
245
+ function validateHostedResponse(body) {
246
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
247
+ throw new ProbeMeshError({
248
+ code: "invalid_provider_response",
249
+ message: "Hosted router response must be an object."
250
+ });
251
+ }
252
+ const response = body;
253
+ if (typeof response.callId !== "string" || typeof response.capability !== "string" || !("data" in response) || !response.provider || typeof response.provider !== "object" || typeof response.latencyMs !== "number" || !Array.isArray(response.receiptRefs) || response.status !== "completed") {
254
+ throw new ProbeMeshError({
255
+ code: "invalid_provider_response",
256
+ message: "Hosted router response does not match ProbeMesh call response shape."
257
+ });
258
+ }
259
+ if (response.safety !== void 0) {
260
+ validateHostedSafety(response.safety);
261
+ }
262
+ if (response.attempt !== void 0) {
263
+ validateHostedAttempt(response.attempt);
264
+ }
265
+ if (response.route !== void 0) {
266
+ validateHostedRoute(response.route);
267
+ }
268
+ return response;
269
+ }
270
+ function hostedErrorToProbeMeshError(body, statusCode) {
271
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
272
+ return new ProbeMeshError({
273
+ code: "provider_unavailable",
274
+ message: `Hosted router returned HTTP ${statusCode}.`
275
+ });
276
+ }
277
+ const error = body;
278
+ return new ProbeMeshError({
279
+ code: normalizeHostedErrorCode(error.code),
280
+ message: error.message ?? `Hosted router returned HTTP ${statusCode}.`,
281
+ capability: error.capability,
282
+ provider: error.provider,
283
+ callId: error.callId,
284
+ receiptRefs: validateHostedReceiptRefs(error.receiptRefs),
285
+ safety: validateHostedSafety(error.safety),
286
+ timeline: validateHostedTimeline(error.timeline),
287
+ retrySafety: validateHostedRetrySafety(error.retrySafety),
288
+ attempt: validateHostedAttempt(error.attempt),
289
+ route: validateHostedRoute(error.route)
290
+ });
291
+ }
292
+ function normalizeHostedErrorCode(code) {
293
+ const knownCodes = /* @__PURE__ */ new Set([
294
+ "unauthorized",
295
+ "invalid_request",
296
+ "budget_exceeded",
297
+ "provider_unavailable",
298
+ "retry_not_available",
299
+ "payment_failed",
300
+ "receipt_missing",
301
+ "timeout_after_payment",
302
+ "charged_but_no_result",
303
+ "provider_timeout",
304
+ "invalid_provider_response"
305
+ ]);
306
+ return code && knownCodes.has(code) ? code : "provider_unavailable";
307
+ }
308
+ function ensureTrailingSlash(value) {
309
+ return value.endsWith("/") ? value : `${value}/`;
310
+ }
311
+ function validateAdapterResult(result, adapter, capability, callId) {
312
+ if (!result || typeof result !== "object") {
313
+ throw invalidProviderResponse(
314
+ adapter,
315
+ capability,
316
+ callId,
317
+ "Adapter result must be an object."
318
+ );
319
+ }
320
+ const candidate = result;
321
+ if (!("data" in candidate)) {
322
+ throw invalidProviderResponse(
323
+ adapter,
324
+ capability,
325
+ callId,
326
+ "Adapter result requires data."
327
+ );
328
+ }
329
+ if (candidate.cost !== void 0) {
330
+ validateCost(candidate.cost, adapter, capability, callId);
331
+ }
332
+ if (candidate.receiptRefs !== void 0) {
333
+ validateReceiptRefs(candidate.receiptRefs, adapter, capability, callId);
334
+ }
335
+ if (candidate.safety !== void 0) {
336
+ validateAdapterSafety(candidate.safety, adapter, capability, callId);
337
+ }
338
+ if (candidate.provider !== void 0 && (!candidate.provider || typeof candidate.provider !== "object")) {
339
+ throw invalidProviderResponse(
340
+ adapter,
341
+ capability,
342
+ callId,
343
+ "Adapter provider metadata must be an object when provided."
344
+ );
345
+ }
346
+ return {
347
+ data: candidate.data,
348
+ cost: candidate.cost,
349
+ receiptRefs: candidate.receiptRefs,
350
+ safety: candidate.safety,
351
+ provider: candidate.provider
352
+ };
353
+ }
354
+ function validateCost(cost, adapter, capability, callId) {
355
+ if (!cost || typeof cost !== "object") {
356
+ throw invalidProviderResponse(
357
+ adapter,
358
+ capability,
359
+ callId,
360
+ "Adapter cost must be an object when provided."
361
+ );
362
+ }
363
+ if (!Number.isFinite(cost.amountUsd) || cost.amountUsd < 0) {
364
+ throw invalidProviderResponse(
365
+ adapter,
366
+ capability,
367
+ callId,
368
+ "Adapter cost.amountUsd must be a non-negative number."
369
+ );
370
+ }
371
+ }
372
+ function validateAdapterSafety(safety, adapter, capability, callId) {
373
+ if (!isValidSafety(safety)) {
374
+ throw invalidProviderResponse(
375
+ adapter,
376
+ capability,
377
+ callId,
378
+ "Adapter safety metadata must match ProbeMeshCallSafety."
379
+ );
380
+ }
381
+ }
382
+ function validateHostedSafety(safety) {
383
+ if (safety === void 0) {
384
+ return void 0;
385
+ }
386
+ if (!isValidSafety(safety)) {
387
+ throw new ProbeMeshError({
388
+ code: "invalid_provider_response",
389
+ message: "Hosted router safety metadata does not match ProbeMeshCallSafety."
390
+ });
391
+ }
392
+ return safety;
393
+ }
394
+ function validateHostedReceiptRefs(receiptRefs) {
395
+ if (receiptRefs === void 0) {
396
+ return void 0;
397
+ }
398
+ if (!isValidReceiptRefs(receiptRefs)) {
399
+ throw new ProbeMeshError({
400
+ code: "invalid_provider_response",
401
+ message: "Hosted router error receiptRefs do not match ReceiptRef[]."
402
+ });
403
+ }
404
+ return receiptRefs;
405
+ }
406
+ function validateHostedTimeline(timeline) {
407
+ if (timeline === void 0) {
408
+ return void 0;
409
+ }
410
+ if (!timeline || typeof timeline !== "object" || Array.isArray(timeline) || typeof timeline.routeId !== "string" || !Array.isArray(timeline.events)) {
411
+ throw new ProbeMeshError({
412
+ code: "invalid_provider_response",
413
+ message: "Hosted router timeline does not match ProbeMeshCallTimeline."
414
+ });
415
+ }
416
+ return timeline;
417
+ }
418
+ function validateHostedRetrySafety(retrySafety) {
419
+ if (retrySafety === void 0) {
420
+ return void 0;
421
+ }
422
+ if (!retrySafety || typeof retrySafety !== "object" || Array.isArray(retrySafety) || typeof retrySafety.retryable !== "boolean" || typeof retrySafety.safeToAutoRetry !== "boolean" || !isRetrySafetyReason(
423
+ retrySafety.reason
424
+ )) {
425
+ throw new ProbeMeshError({
426
+ code: "invalid_provider_response",
427
+ message: "Hosted router retrySafety does not match ProbeMeshRetrySafety."
428
+ });
429
+ }
430
+ return retrySafety;
431
+ }
432
+ function validateHostedAttempt(attempt) {
433
+ if (attempt === void 0) {
434
+ return void 0;
435
+ }
436
+ if (!isValidAttempt(attempt)) {
437
+ throw new ProbeMeshError({
438
+ code: "invalid_provider_response",
439
+ message: "Hosted router attempt metadata does not match ProbeMeshAttemptMetadata."
440
+ });
441
+ }
442
+ return attempt;
443
+ }
444
+ function validateHostedRoute(route) {
445
+ if (route === void 0) {
446
+ return void 0;
447
+ }
448
+ if (!route || typeof route !== "object" || Array.isArray(route) || typeof route.routeId !== "string" || typeof route.mode !== "string" || route.selectedProviderId !== void 0 && typeof route.selectedProviderId !== "string" || route.explanation !== void 0 && !isValidRouteExplanation(
449
+ route.explanation
450
+ )) {
451
+ throw new ProbeMeshError({
452
+ code: "invalid_provider_response",
453
+ message: "Hosted router route metadata does not match ProbeMeshRouteMetadata."
454
+ });
455
+ }
456
+ return route;
457
+ }
458
+ function isValidRouteExplanation(explanation) {
459
+ if (!explanation || typeof explanation !== "object" || Array.isArray(explanation)) {
460
+ return false;
461
+ }
462
+ const candidate = explanation;
463
+ return Array.isArray(candidate.consideredProviders) && candidate.consideredProviders.every((provider) => typeof provider === "string") && (candidate.selectedProviderId === void 0 || typeof candidate.selectedProviderId === "string") && typeof candidate.fallbackUsed === "boolean" && typeof candidate.fallbackCount === "number" && typeof candidate.stopReason === "string" && Array.isArray(candidate.attempts) && candidate.attempts.every(
464
+ (attempt) => attempt && typeof attempt === "object" && !Array.isArray(attempt) && typeof attempt.providerId === "string" && typeof attempt.status === "string" && typeof attempt.reason === "string"
465
+ );
466
+ }
467
+ function isValidAttempt(attempt) {
468
+ if (!attempt || typeof attempt !== "object" || Array.isArray(attempt)) {
469
+ return false;
470
+ }
471
+ const candidate = attempt;
472
+ return typeof candidate.originalCallId === "string" && candidate.originalCallId.length > 0 && typeof candidate.attemptId === "string" && candidate.attemptId.length > 0 && typeof candidate.attemptNumber === "number" && Number.isInteger(candidate.attemptNumber) && candidate.attemptNumber > 0 && typeof candidate.isRetry === "boolean" && (candidate.previousAttemptId === void 0 || typeof candidate.previousAttemptId === "string");
473
+ }
474
+ function isValidSafety(safety) {
475
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) {
476
+ return false;
477
+ }
478
+ const candidate = safety;
479
+ return (candidate.paymentAttemptId === void 0 || typeof candidate.paymentAttemptId === "string") && isPaymentStatus(candidate.paymentStatus) && isSettlementStatus(candidate.settlementStatus) && isDeliveryStatus(candidate.deliveryStatus) && typeof candidate.moneyMayHaveMoved === "boolean";
480
+ }
481
+ function isValidReceiptRefs(receiptRefs) {
482
+ return Array.isArray(receiptRefs) && receiptRefs.every(
483
+ (receiptRef) => receiptRef && typeof receiptRef === "object" && !Array.isArray(receiptRef) && typeof receiptRef.type === "string" && typeof receiptRef.provider === "string" && typeof receiptRef.status === "string" && typeof receiptRef.ref === "string" && typeof receiptRef.timestamp === "string"
484
+ );
485
+ }
486
+ function isRetrySafetyReason(value) {
487
+ return value === "completed" || value === "not_retryable" || value === "money_may_have_moved" || value === "payment_failed_no_charge" || value === "no_payment_attempted" || value === "unknown_payment_state";
488
+ }
489
+ function isPaymentStatus(value) {
490
+ return value === "not_attempted" || value === "authorized" || value === "failed";
491
+ }
492
+ function isSettlementStatus(value) {
493
+ return value === "not_attempted" || value === "settled" || value === "failed";
494
+ }
495
+ function isDeliveryStatus(value) {
496
+ return value === "not_attempted" || value === "delivered" || value === "failed" || value === "missing";
497
+ }
498
+ function validateReceiptRefs(receiptRefs, adapter, capability, callId) {
499
+ if (!Array.isArray(receiptRefs)) {
500
+ throw invalidProviderResponse(
501
+ adapter,
502
+ capability,
503
+ callId,
504
+ "Adapter receiptRefs must be an array when provided."
505
+ );
506
+ }
507
+ for (const receiptRef of receiptRefs) {
508
+ if (!receiptRef || typeof receiptRef !== "object") {
509
+ throw invalidProviderResponse(
510
+ adapter,
511
+ capability,
512
+ callId,
513
+ "Adapter receiptRefs entries must be objects."
514
+ );
515
+ }
516
+ if (typeof receiptRef.type !== "string" || typeof receiptRef.provider !== "string" || typeof receiptRef.status !== "string" || typeof receiptRef.ref !== "string" || typeof receiptRef.timestamp !== "string") {
517
+ throw invalidProviderResponse(
518
+ adapter,
519
+ capability,
520
+ callId,
521
+ "Adapter receiptRefs entries require type, provider, status, ref, and timestamp strings."
522
+ );
523
+ }
524
+ }
525
+ }
526
+ function invalidProviderResponse(adapter, capability, callId, message) {
527
+ return new ProbeMeshError({
528
+ code: "invalid_provider_response",
529
+ message,
530
+ capability,
531
+ provider: adapter.id,
532
+ callId
533
+ });
534
+ }
535
+ async function withLatencyLimit(operation, request, adapter, callId, abortController) {
536
+ const maxLatencyMs = request.constraints?.maxLatencyMs;
537
+ if (maxLatencyMs === void 0) {
538
+ return operation;
539
+ }
540
+ let timeout;
541
+ try {
542
+ return await Promise.race([
543
+ operation,
544
+ new Promise((_, reject) => {
545
+ timeout = setTimeout(() => {
546
+ abortController.abort();
547
+ reject(
548
+ new ProbeMeshError({
549
+ code: "provider_timeout",
550
+ message: `Adapter "${adapter.id}" exceeded maxLatencyMs ${maxLatencyMs} for capability "${request.capability}".`,
551
+ capability: request.capability,
552
+ provider: adapter.id,
553
+ callId
554
+ })
555
+ );
556
+ }, maxLatencyMs);
557
+ })
558
+ ]);
559
+ } finally {
560
+ if (timeout) {
561
+ clearTimeout(timeout);
562
+ }
563
+ }
564
+ }
565
+ function validateCallRequest(request) {
566
+ if (!request || typeof request !== "object") {
567
+ throw new ProbeMeshError({
568
+ code: "invalid_request",
569
+ message: "ProbeMesh call request must be an object."
570
+ });
571
+ }
572
+ const candidate = request;
573
+ if (typeof candidate.capability !== "string" || candidate.capability.trim().length === 0) {
574
+ throw new ProbeMeshError({
575
+ code: "invalid_request",
576
+ message: "ProbeMesh call request requires a non-empty capability."
577
+ });
578
+ }
579
+ if (!("input" in candidate)) {
580
+ throw new ProbeMeshError({
581
+ code: "invalid_request",
582
+ message: `ProbeMesh call request for capability "${candidate.capability}" requires input.`,
583
+ capability: candidate.capability
584
+ });
585
+ }
586
+ validateConstraints(candidate.constraints, candidate.capability);
587
+ validateProviderPreferences(
588
+ candidate.providerPreferences,
589
+ candidate.capability
590
+ );
591
+ validatePaymentPreferences(candidate.paymentPreferences, candidate.capability);
592
+ validatePaymentStrategy(candidate.paymentStrategy, candidate.capability);
593
+ }
594
+ function validateRetryRequest(request) {
595
+ if (!request || typeof request !== "object") {
596
+ throw new ProbeMeshError({
597
+ code: "invalid_request",
598
+ message: "ProbeMesh retry request must be an object."
599
+ });
600
+ }
601
+ const candidate = request;
602
+ if (typeof candidate.callId !== "string" || candidate.callId.length === 0) {
603
+ throw new ProbeMeshError({
604
+ code: "invalid_request",
605
+ message: "ProbeMesh retry request requires a non-empty callId."
606
+ });
607
+ }
608
+ if (candidate.idempotencyKey !== void 0 && (typeof candidate.idempotencyKey !== "string" || candidate.idempotencyKey.length === 0)) {
609
+ throw new ProbeMeshError({
610
+ code: "invalid_request",
611
+ message: "ProbeMesh retry idempotencyKey must be a non-empty string when provided.",
612
+ callId: candidate.callId
613
+ });
614
+ }
615
+ }
616
+ function validateConstraints(constraints, capability) {
617
+ if (constraints === void 0) {
618
+ return;
619
+ }
620
+ if (!constraints || typeof constraints !== "object") {
621
+ throw new ProbeMeshError({
622
+ code: "invalid_request",
623
+ message: "constraints must be an object when provided.",
624
+ capability
625
+ });
626
+ }
627
+ validatePositiveNumber(constraints.maxCostUsd, "maxCostUsd", capability);
628
+ validatePositiveNumber(constraints.maxLatencyMs, "maxLatencyMs", capability);
629
+ if (constraints.minConfidence !== void 0 && (!Number.isFinite(constraints.minConfidence) || constraints.minConfidence < 0 || constraints.minConfidence > 1)) {
630
+ throw new ProbeMeshError({
631
+ code: "invalid_request",
632
+ message: "minConfidence must be a number between 0 and 1 when provided.",
633
+ capability
634
+ });
635
+ }
636
+ }
637
+ function validateProviderPreferences(providerPreferences, capability) {
638
+ if (providerPreferences === void 0) {
639
+ return;
640
+ }
641
+ if (!providerPreferences || typeof providerPreferences !== "object") {
642
+ throw new ProbeMeshError({
643
+ code: "invalid_request",
644
+ message: "providerPreferences must be an object when provided.",
645
+ capability
646
+ });
647
+ }
648
+ validateStringArray(
649
+ providerPreferences.allowedProviders,
650
+ "allowedProviders",
651
+ capability
652
+ );
653
+ validateStringArray(
654
+ providerPreferences.blockedProviders,
655
+ "blockedProviders",
656
+ capability
657
+ );
658
+ }
659
+ function validatePositiveNumber(value, field, capability) {
660
+ if (value === void 0) {
661
+ return;
662
+ }
663
+ if (!Number.isFinite(value) || value <= 0) {
664
+ throw new ProbeMeshError({
665
+ code: "invalid_request",
666
+ message: `${field} must be a positive number when provided.`,
667
+ capability
668
+ });
669
+ }
670
+ }
671
+ function validateStringArray(value, field, capability) {
672
+ if (value === void 0) {
673
+ return;
674
+ }
675
+ if (!Array.isArray(value) || value.some((provider) => typeof provider !== "string" || provider.length === 0)) {
676
+ throw new ProbeMeshError({
677
+ code: "invalid_request",
678
+ message: `${field} must be an array of non-empty provider ids when provided.`,
679
+ capability
680
+ });
681
+ }
682
+ }
683
+ function selectAdapter(adapters, request) {
684
+ const allowedProviders = request.providerPreferences?.allowedProviders;
685
+ const blockedProviders = request.providerPreferences?.blockedProviders ?? [];
686
+ for (const adapter of adapters) {
687
+ if (!adapter.capabilities.includes(request.capability)) {
688
+ continue;
689
+ }
690
+ if (allowedProviders && !allowedProviders.includes(adapter.id)) {
691
+ continue;
692
+ }
693
+ if (blockedProviders.includes(adapter.id)) {
694
+ continue;
695
+ }
696
+ const resolution = resolveProviderPaymentOption({
697
+ providerId: adapter.id,
698
+ providerMode: adapter.mode ?? "local",
699
+ capabilities: adapter.capabilities,
700
+ capability: request.capability,
701
+ pricing: adapter.pricing,
702
+ paymentOptions: adapter.paymentOptions,
703
+ paymentPreferences: request.paymentPreferences,
704
+ paymentStrategy: request.paymentStrategy
705
+ });
706
+ if (resolution.selection) {
707
+ return {
708
+ adapter,
709
+ paymentSelection: resolution.selection
710
+ };
711
+ }
712
+ }
713
+ return void 0;
714
+ }
715
+ function enforceBudget(constraints, adapter, capability, callId, paymentSelection) {
716
+ if (constraints?.maxCostUsd === void 0) {
717
+ return;
718
+ }
719
+ const paymentCost = paymentSelectionToCost(paymentSelection);
720
+ if (paymentCost) {
721
+ if (paymentCost.amountUsd <= constraints.maxCostUsd) {
722
+ return;
723
+ }
724
+ throw new ProbeMeshError({
725
+ code: "budget_exceeded",
726
+ message: `Capability "${capability}" payment option "${paymentSelection?.optionId}" costs $${paymentCost.amountUsd.toFixed(
727
+ 2
728
+ )}, which exceeds maxCostUsd $${constraints.maxCostUsd.toFixed(2)}.`,
729
+ capability,
730
+ provider: adapter.id,
731
+ callId
732
+ });
733
+ }
734
+ const pricing = getPricing(adapter, capability);
735
+ if (!pricing || pricing.amountUsd <= constraints.maxCostUsd) {
736
+ return;
737
+ }
738
+ throw new ProbeMeshError({
739
+ code: "budget_exceeded",
740
+ message: `Capability "${capability}" costs $${pricing.amountUsd.toFixed(
741
+ 2
742
+ )}, which exceeds maxCostUsd $${constraints.maxCostUsd.toFixed(2)}.`,
743
+ capability,
744
+ provider: adapter.id,
745
+ callId
746
+ });
747
+ }
748
+ function getPricing(adapter, capability) {
749
+ if (!adapter.pricing) {
750
+ return void 0;
751
+ }
752
+ if (isSinglePricing(adapter.pricing)) {
753
+ return adapter.pricing;
754
+ }
755
+ return adapter.pricing[capability];
756
+ }
757
+ function isSinglePricing(pricing) {
758
+ return typeof pricing?.amountUsd === "number" && typeof pricing?.unit === "string";
759
+ }
760
+ function pricingToCost(pricing) {
761
+ if (!pricing) {
762
+ return void 0;
763
+ }
764
+ return {
765
+ amountUsd: pricing.amountUsd,
766
+ currency: pricing.currency ?? "USD",
767
+ unit: pricing.unit
768
+ };
769
+ }
770
+ function generateCallId() {
771
+ return `call_${randomUUID()}`;
772
+ }
773
+
774
+ // src/hostedRouter.ts
775
+ import { randomUUID as randomUUID2 } from "crypto";
776
+ import { createServer } from "http";
777
+ var DEFAULT_PROVIDER_HEALTH_COOLDOWN_MS = 6e4;
778
+ function createHostedRouter(options) {
779
+ const attemptStore = createAttemptStore();
780
+ const providerHealthStore = createProviderHealthStore();
781
+ const providerHealthOptions = resolveProviderHealthOptions(
782
+ options.providerHealth
783
+ );
784
+ const catalogPolicyOptions = resolveProviderCatalogPolicyOptions(
785
+ options.catalogPolicy,
786
+ options.adapters
787
+ );
788
+ return {
789
+ async handle(request, response) {
790
+ const route = parseHostedRoute(request);
791
+ if (!route) {
792
+ writeJson(response, 404, {
793
+ code: "provider_unavailable",
794
+ message: "Route not found.",
795
+ status: "failed"
796
+ });
797
+ return;
798
+ }
799
+ const routeId = `route_${randomUUID2()}`;
800
+ const timeline = createTimeline(routeId);
801
+ try {
802
+ assertAuthorized(request, options.apiKeys);
803
+ if (route.kind === "call") {
804
+ const body2 = await readJsonBody(request);
805
+ const callRequest = body2;
806
+ const attempt = createInitialAttempt(callRequest);
807
+ await executeHostedCall({
808
+ options,
809
+ response,
810
+ timeline,
811
+ attemptStore,
812
+ providerHealthStore,
813
+ providerHealthOptions,
814
+ catalogPolicyOptions,
815
+ originalRequest: callRequest,
816
+ callRequest,
817
+ attempt
818
+ });
819
+ return;
820
+ }
821
+ const body = await readOptionalJsonBody(request);
822
+ const retryRequest = validateRetryBody(route.callId, body);
823
+ await executeHostedRetry({
824
+ options,
825
+ response,
826
+ timeline,
827
+ attemptStore,
828
+ providerHealthStore,
829
+ providerHealthOptions,
830
+ catalogPolicyOptions,
831
+ retryRequest
832
+ });
833
+ } catch (error) {
834
+ emitPaymentAndReceiptTelemetry(options, timeline, {
835
+ callId: isProbeMeshError(error) ? error.callId : void 0,
836
+ capability: isProbeMeshError(error) ? error.capability : void 0,
837
+ provider: isProbeMeshError(error) ? error.provider : void 0,
838
+ safety: isProbeMeshError(error) ? error.safety : void 0,
839
+ receiptRefs: isProbeMeshError(error) ? error.receiptRefs : void 0,
840
+ attempt: isProbeMeshError(error) ? error.attempt : void 0
841
+ });
842
+ emitFailureTelemetry(options, timeline, error);
843
+ writeHostedError(response, error, timeline, retrySafetyForError(error));
844
+ }
845
+ }
846
+ };
847
+ }
848
+ function createAttemptStore() {
849
+ return {
850
+ attempts: /* @__PURE__ */ new Map(),
851
+ attemptCounts: /* @__PURE__ */ new Map()
852
+ };
853
+ }
854
+ function createProviderHealthStore() {
855
+ return {
856
+ records: /* @__PURE__ */ new Map()
857
+ };
858
+ }
859
+ function resolveProviderHealthOptions(options) {
860
+ const requestedCooldownMs = options?.cooldownMs;
861
+ const cooldownMs = typeof requestedCooldownMs === "number" && Number.isFinite(requestedCooldownMs) && requestedCooldownMs >= 0 ? requestedCooldownMs : DEFAULT_PROVIDER_HEALTH_COOLDOWN_MS;
862
+ return {
863
+ enabled: options?.enabled ?? true,
864
+ cooldownMs
865
+ };
866
+ }
867
+ function resolveProviderCatalogPolicyOptions(options, adapters) {
868
+ if (!options || options.enabled === false) {
869
+ return {
870
+ enabled: false,
871
+ status: options?.status ?? "ready"
872
+ };
873
+ }
874
+ assertProviderCatalog(options.catalog);
875
+ if (options.maxCostUsd !== void 0 && (typeof options.maxCostUsd !== "number" || !Number.isFinite(options.maxCostUsd) || options.maxCostUsd < 0)) {
876
+ throw new ProbeMeshError({
877
+ code: "invalid_request",
878
+ message: "Hosted provider catalog policy maxCostUsd must be a non-negative finite number."
879
+ });
880
+ }
881
+ validateStringArrayOption(
882
+ options.providerIds,
883
+ "Hosted provider catalog policy providerIds"
884
+ );
885
+ validateStringArrayOption(
886
+ options.blockedProviderIds,
887
+ "Hosted provider catalog policy blockedProviderIds"
888
+ );
889
+ if (options.requiredReceiptTypes !== void 0 && (!Array.isArray(options.requiredReceiptTypes) || options.requiredReceiptTypes.some((receiptType) => !isReceiptType(receiptType)))) {
890
+ throw new ProbeMeshError({
891
+ code: "invalid_request",
892
+ message: "Hosted provider catalog policy requiredReceiptTypes must contain known receipt types."
893
+ });
894
+ }
895
+ if (options.status !== void 0 && options.status !== "ready" && options.status !== "rejected") {
896
+ throw new ProbeMeshError({
897
+ code: "invalid_request",
898
+ message: 'Hosted provider catalog policy status must be "ready" or "rejected".'
899
+ });
900
+ }
901
+ if (options.protocolMode !== void 0 && (typeof options.protocolMode !== "string" || options.protocolMode.length === 0)) {
902
+ throw new ProbeMeshError({
903
+ code: "invalid_request",
904
+ message: "Hosted provider catalog policy protocolMode must be a non-empty string."
905
+ });
906
+ }
907
+ validatePaymentPreferences(options.paymentPreferences, "hosted catalog policy");
908
+ validatePaymentStrategy(options.paymentStrategy, "hosted catalog policy");
909
+ const bundlePolicy = createBundlePolicyOptions(options);
910
+ const bundleAdapterInventory = createAdapterInventory(adapters);
911
+ const bundleQueries = options.bundle?.queries;
912
+ const bundleVerification = options.bundle ? verifyProviderCatalogPolicyBundleRuntime({
913
+ bundle: options.bundle,
914
+ catalog: options.catalog,
915
+ catalogPolicy: bundlePolicy,
916
+ adapterInventory: bundleAdapterInventory,
917
+ queries: bundleQueries
918
+ }) : void 0;
919
+ if (bundleVerification && !bundleVerification.valid) {
920
+ throw new ProbeMeshError({
921
+ code: "invalid_request",
922
+ message: `Hosted provider catalog policy bundle does not match runtime inputs: ${formatBundleVerificationErrors(
923
+ bundleVerification.errors
924
+ )}`
925
+ });
926
+ }
927
+ return {
928
+ enabled: true,
929
+ catalog: options.catalog,
930
+ status: options.status ?? "ready",
931
+ protocolMode: options.protocolMode,
932
+ paymentPreferences: options.paymentPreferences,
933
+ paymentStrategy: options.paymentStrategy,
934
+ maxCostUsd: options.maxCostUsd,
935
+ requiredReceiptTypes: options.requiredReceiptTypes,
936
+ providerIds: options.providerIds,
937
+ blockedProviderIds: options.blockedProviderIds,
938
+ bundle: options.bundle,
939
+ bundlePolicy,
940
+ bundleQueries,
941
+ bundleAdapterInventory,
942
+ bundleAttestation: bundleVerification?.attestation
943
+ };
944
+ }
945
+ function createBundlePolicyOptions(options) {
946
+ const policy = {};
947
+ if (options.enabled !== void 0) {
948
+ policy.enabled = options.enabled;
949
+ }
950
+ if (options.status !== void 0) {
951
+ policy.status = options.status;
952
+ }
953
+ if (options.protocolMode !== void 0) {
954
+ policy.protocolMode = options.protocolMode;
955
+ }
956
+ if (options.paymentPreferences !== void 0) {
957
+ policy.paymentPreferences = options.paymentPreferences;
958
+ }
959
+ if (options.paymentStrategy !== void 0) {
960
+ policy.paymentStrategy = options.paymentStrategy;
961
+ }
962
+ if (options.maxCostUsd !== void 0) {
963
+ policy.maxCostUsd = options.maxCostUsd;
964
+ }
965
+ if (options.requiredReceiptTypes !== void 0) {
966
+ policy.requiredReceiptTypes = options.requiredReceiptTypes;
967
+ }
968
+ if (options.providerIds !== void 0) {
969
+ policy.providerIds = options.providerIds;
970
+ }
971
+ if (options.blockedProviderIds !== void 0) {
972
+ policy.blockedProviderIds = options.blockedProviderIds;
973
+ }
974
+ return Object.keys(policy).length > 0 ? policy : void 0;
975
+ }
976
+ function createAdapterInventory(adapters) {
977
+ return adapters.map((adapter) => ({
978
+ id: adapter.id,
979
+ capabilities: [...adapter.capabilities],
980
+ ...adapter.pricing !== void 0 ? { pricing: adapter.pricing } : {},
981
+ ...adapter.paymentOptions !== void 0 ? { paymentOptions: adapter.paymentOptions } : {}
982
+ }));
983
+ }
984
+ function formatBundleVerificationErrors(errors) {
985
+ return errors.map((error) => `${error.path}: ${error.message}`).join("; ");
986
+ }
987
+ function validateStringArrayOption(value, label) {
988
+ if (value !== void 0 && (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0))) {
989
+ throw new ProbeMeshError({
990
+ code: "invalid_request",
991
+ message: `${label} must be an array of non-empty strings.`
992
+ });
993
+ }
994
+ }
995
+ function isReceiptType(value) {
996
+ return value === "payment_proof" || value === "settlement_confirmation" || value === "provider_delivery" || value === "provider_response_evidence" || value === "authorization_decision";
997
+ }
998
+ function parseHostedRoute(request) {
999
+ if (request.method !== "POST" || !request.url) {
1000
+ return void 0;
1001
+ }
1002
+ const url = new URL(request.url, "http://localhost");
1003
+ if (url.pathname === "/v1/calls") {
1004
+ return {
1005
+ kind: "call"
1006
+ };
1007
+ }
1008
+ const retryMatch = url.pathname.match(/^\/v1\/calls\/(.+)\/retry$/);
1009
+ if (retryMatch) {
1010
+ return {
1011
+ kind: "retry",
1012
+ callId: decodeURIComponent(retryMatch[1])
1013
+ };
1014
+ }
1015
+ return void 0;
1016
+ }
1017
+ async function executeHostedCall(options) {
1018
+ const {
1019
+ options: routerOptions,
1020
+ response,
1021
+ timeline,
1022
+ attemptStore,
1023
+ providerHealthStore,
1024
+ providerHealthOptions,
1025
+ catalogPolicyOptions,
1026
+ originalRequest,
1027
+ callRequest,
1028
+ attempt
1029
+ } = options;
1030
+ const executableRequest = {
1031
+ ...callRequest,
1032
+ idempotencyKey: attempt.attemptId
1033
+ };
1034
+ const routePlan = createRoutePlan(
1035
+ routerOptions.adapters,
1036
+ executableRequest,
1037
+ providerHealthStore,
1038
+ providerHealthOptions,
1039
+ catalogPolicyOptions
1040
+ );
1041
+ emitTelemetry(routerOptions, timeline, {
1042
+ type: "call.requested",
1043
+ callId: attempt.attemptId,
1044
+ capability: executableRequest.capability,
1045
+ attempt,
1046
+ attestation: routePlan.attestation
1047
+ });
1048
+ if (routePlan.eligibleAdapters.length === 0) {
1049
+ const error = createNoEligibleProviderError(executableRequest, routePlan);
1050
+ const retrySafety2 = retrySafetyForError(error);
1051
+ emitFailureTelemetry(
1052
+ routerOptions,
1053
+ timeline,
1054
+ error,
1055
+ attempt,
1056
+ routePlan.attestation
1057
+ );
1058
+ await emitCallAuditArtifact(routerOptions, {
1059
+ request: executableRequest,
1060
+ error,
1061
+ timeline,
1062
+ retrySafety: retrySafety2,
1063
+ attempt,
1064
+ route: createRouteMetadata(
1065
+ timeline.routeId,
1066
+ routePlan.explanation,
1067
+ void 0,
1068
+ routePlan.attestation,
1069
+ void 0,
1070
+ routePlan.paymentStrategy
1071
+ )
1072
+ });
1073
+ storeAttempt(attemptStore, {
1074
+ originalRequest,
1075
+ attempt,
1076
+ outcome: "failed",
1077
+ retrySafety: retrySafety2,
1078
+ timeline
1079
+ });
1080
+ writeHostedError(
1081
+ response,
1082
+ error,
1083
+ timeline,
1084
+ retrySafety2,
1085
+ attempt,
1086
+ createRouteMetadata(
1087
+ timeline.routeId,
1088
+ routePlan.explanation,
1089
+ void 0,
1090
+ routePlan.attestation,
1091
+ void 0,
1092
+ routePlan.paymentStrategy
1093
+ )
1094
+ );
1095
+ return;
1096
+ }
1097
+ let lastError;
1098
+ for (const [index, candidate] of routePlan.eligibleAdapters.entries()) {
1099
+ const { adapter, paymentSelection } = candidate;
1100
+ markRouteAttempt(routePlan.explanation, adapter.id, {
1101
+ status: "attempted",
1102
+ reason: "eligible",
1103
+ paymentSelection,
1104
+ paymentStrategy: routePlan.paymentStrategy,
1105
+ paymentOptionScore: paymentSelection.score,
1106
+ paymentOptionScoreReasons: paymentSelection.scoreReasons
1107
+ });
1108
+ try {
1109
+ const client = createProbeMesh({
1110
+ adapters: createTimelineAdapters(
1111
+ {
1112
+ ...routerOptions,
1113
+ adapters: [adapter]
1114
+ },
1115
+ timeline,
1116
+ attempt,
1117
+ routePlan.attestation
1118
+ )
1119
+ });
1120
+ const result = await client.call({
1121
+ ...executableRequest,
1122
+ providerPreferences: {
1123
+ allowedProviders: [adapter.id]
1124
+ }
1125
+ });
1126
+ const selectedPayment = result.route?.paymentSelection ?? paymentSelection;
1127
+ clearProviderHealth(
1128
+ providerHealthStore,
1129
+ providerHealthOptions,
1130
+ result.capability,
1131
+ adapter.id
1132
+ );
1133
+ markRouteAttempt(routePlan.explanation, adapter.id, {
1134
+ status: "succeeded",
1135
+ reason: "selected",
1136
+ paymentEvidence: hasPaymentEvidence(result.safety, result.receiptRefs),
1137
+ healthStatus: providerHealthOptions.enabled ? "healthy" : void 0,
1138
+ paymentSelection: selectedPayment,
1139
+ paymentStrategy: routePlan.paymentStrategy,
1140
+ paymentOptionScore: selectedPayment?.score,
1141
+ paymentOptionScoreReasons: selectedPayment?.scoreReasons
1142
+ });
1143
+ routePlan.explanation.selectedProviderId = result.provider.id;
1144
+ routePlan.explanation.stopReason = "stopped_completed";
1145
+ routePlan.explanation.fallbackUsed = routePlan.explanation.fallbackCount > 0;
1146
+ emitTelemetry(routerOptions, timeline, {
1147
+ type: "provider.selected",
1148
+ callId: result.callId,
1149
+ capability: result.capability,
1150
+ provider: result.provider.id,
1151
+ attempt,
1152
+ attestation: routePlan.attestation
1153
+ });
1154
+ emitPaymentAndReceiptTelemetry(routerOptions, timeline, {
1155
+ callId: result.callId,
1156
+ capability: result.capability,
1157
+ provider: result.provider.id,
1158
+ safety: result.safety,
1159
+ receiptRefs: result.receiptRefs,
1160
+ attempt,
1161
+ attestation: routePlan.attestation
1162
+ });
1163
+ emitTelemetry(routerOptions, timeline, {
1164
+ type: "call.completed",
1165
+ callId: result.callId,
1166
+ capability: result.capability,
1167
+ provider: result.provider.id,
1168
+ status: "completed",
1169
+ safety: result.safety,
1170
+ attempt,
1171
+ attestation: routePlan.attestation
1172
+ });
1173
+ const retrySafety2 = retrySafetyForCompletedCall();
1174
+ const hostedResult = addHostedRouteMetadata(
1175
+ result,
1176
+ timeline.routeId,
1177
+ timeline,
1178
+ retrySafety2,
1179
+ attempt,
1180
+ routePlan.explanation,
1181
+ routePlan.attestation,
1182
+ selectedPayment,
1183
+ routePlan.paymentStrategy
1184
+ );
1185
+ storeAttempt(attemptStore, {
1186
+ originalRequest,
1187
+ attempt,
1188
+ outcome: "completed",
1189
+ safety: hostedResult.safety,
1190
+ receiptRefs: hostedResult.receiptRefs,
1191
+ retrySafety: retrySafety2,
1192
+ timeline
1193
+ });
1194
+ await emitCallAuditArtifact(routerOptions, {
1195
+ request: executableRequest,
1196
+ response: hostedResult,
1197
+ timeline,
1198
+ retrySafety: retrySafety2,
1199
+ attempt,
1200
+ route: hostedResult.route
1201
+ });
1202
+ writeJson(response, 200, hostedResult);
1203
+ return;
1204
+ } catch (error) {
1205
+ const normalizedError2 = toHostedProbeMeshError(error);
1206
+ const paymentEvidence = moneyMayHaveMoved(
1207
+ normalizedError2.safety,
1208
+ normalizedError2.receiptRefs
1209
+ );
1210
+ const retrySafety2 = retrySafetyForError(normalizedError2);
1211
+ const healthRecord = shouldRememberProviderHealthFailure(
1212
+ normalizedError2,
1213
+ paymentEvidence,
1214
+ retrySafety2
1215
+ ) ? markProviderUnhealthy(
1216
+ providerHealthStore,
1217
+ providerHealthOptions,
1218
+ executableRequest.capability,
1219
+ adapter.id
1220
+ ) : void 0;
1221
+ lastError = normalizedError2;
1222
+ markRouteAttempt(routePlan.explanation, adapter.id, {
1223
+ status: "failed",
1224
+ reason: paymentEvidence ? "stopped_payment_evidence" : "fallback_after_failure",
1225
+ errorCode: normalizedError2.code,
1226
+ paymentEvidence,
1227
+ ...healthRecord ?? {},
1228
+ message: normalizedError2.message
1229
+ });
1230
+ const canFallback = !paymentEvidence && retrySafety2.safeToAutoRetry && index < routePlan.eligibleAdapters.length - 1;
1231
+ if (canFallback) {
1232
+ routePlan.explanation.fallbackCount += 1;
1233
+ routePlan.explanation.fallbackUsed = true;
1234
+ continue;
1235
+ }
1236
+ routePlan.explanation.stopReason = paymentEvidence ? "stopped_payment_evidence" : retrySafety2.safeToAutoRetry ? "no_eligible_provider" : "stopped_not_retryable";
1237
+ routePlan.explanation.fallbackUsed = routePlan.explanation.fallbackCount > 0;
1238
+ break;
1239
+ }
1240
+ }
1241
+ const normalizedError = lastError ?? createNoEligibleProviderError(executableRequest, routePlan);
1242
+ const retrySafety = retrySafetyForError(normalizedError);
1243
+ emitPaymentAndReceiptTelemetry(routerOptions, timeline, {
1244
+ callId: normalizedError.callId,
1245
+ capability: normalizedError.capability,
1246
+ provider: normalizedError.provider,
1247
+ safety: normalizedError.safety,
1248
+ receiptRefs: normalizedError.receiptRefs,
1249
+ attempt,
1250
+ attestation: routePlan.attestation
1251
+ });
1252
+ emitFailureTelemetry(
1253
+ routerOptions,
1254
+ timeline,
1255
+ normalizedError,
1256
+ attempt,
1257
+ routePlan.attestation
1258
+ );
1259
+ await emitCallAuditArtifact(routerOptions, {
1260
+ request: executableRequest,
1261
+ error: normalizedError,
1262
+ timeline,
1263
+ retrySafety,
1264
+ attempt,
1265
+ route: createRouteMetadata(
1266
+ timeline.routeId,
1267
+ routePlan.explanation,
1268
+ void 0,
1269
+ routePlan.attestation,
1270
+ void 0,
1271
+ routePlan.paymentStrategy
1272
+ )
1273
+ });
1274
+ storeAttempt(attemptStore, {
1275
+ originalRequest,
1276
+ attempt,
1277
+ outcome: "failed",
1278
+ safety: normalizedError.safety,
1279
+ receiptRefs: normalizedError.receiptRefs,
1280
+ retrySafety,
1281
+ timeline
1282
+ });
1283
+ writeHostedError(
1284
+ response,
1285
+ normalizedError,
1286
+ timeline,
1287
+ retrySafety,
1288
+ attempt,
1289
+ createRouteMetadata(
1290
+ timeline.routeId,
1291
+ routePlan.explanation,
1292
+ void 0,
1293
+ routePlan.attestation,
1294
+ void 0,
1295
+ routePlan.paymentStrategy
1296
+ )
1297
+ );
1298
+ }
1299
+ function createRoutePlan(adapters, request, providerHealthStore, providerHealthOptions, catalogPolicyOptions) {
1300
+ const allowedProviders = request.providerPreferences?.allowedProviders;
1301
+ const blockedProviders = request.providerPreferences?.blockedProviders ?? [];
1302
+ const attempts = [];
1303
+ const eligibleAdapters = [];
1304
+ const plannedAt = /* @__PURE__ */ new Date();
1305
+ const effectiveMaxCostUsd = strictestMaxCostUsd(
1306
+ request.constraints?.maxCostUsd,
1307
+ catalogPolicyOptions.maxCostUsd
1308
+ );
1309
+ const catalogPolicyPlan = createCatalogPolicyPlan(
1310
+ adapters,
1311
+ request,
1312
+ catalogPolicyOptions,
1313
+ effectiveMaxCostUsd
1314
+ );
1315
+ const paymentStrategy = request.paymentStrategy ?? catalogPolicyOptions.paymentStrategy;
1316
+ const bundleVerification = verifyPolicyBundleCapability(
1317
+ catalogPolicyOptions,
1318
+ request.capability
1319
+ );
1320
+ const attestation = bundleVerification?.attestation;
1321
+ const bundleBlocksCapability = attestation !== void 0 && !bundleVerification?.valid;
1322
+ for (const adapter of adapters) {
1323
+ const pricing = getPricing2(adapter, request.capability);
1324
+ const paymentPreferences = mergePaymentPreferences(
1325
+ catalogPolicyOptions.paymentPreferences,
1326
+ request.paymentPreferences
1327
+ );
1328
+ const paymentResolution = resolveProviderPaymentOption({
1329
+ providerId: adapter.id,
1330
+ providerMode: adapter.mode ?? "hosted",
1331
+ capabilities: adapter.capabilities,
1332
+ capability: request.capability,
1333
+ pricing: adapter.pricing,
1334
+ paymentOptions: adapter.paymentOptions,
1335
+ paymentPreferences,
1336
+ paymentStrategy
1337
+ });
1338
+ const paymentSelection = paymentResolution.selection;
1339
+ const cost = paymentSelectionToCost(paymentSelection) ?? pricingToCost2(pricing);
1340
+ const catalogMatch = catalogPolicyPlan.matchesByProviderId.get(adapter.id);
1341
+ const catalogEntry = catalogPolicyPlan.catalogEntryByProviderId.get(adapter.id);
1342
+ const baseAttempt = {
1343
+ providerId: adapter.id,
1344
+ cost,
1345
+ paymentSelection,
1346
+ paymentStrategy,
1347
+ paymentOptionScore: paymentSelection?.score,
1348
+ paymentOptionScoreReasons: paymentSelection?.scoreReasons,
1349
+ ...catalogRouteMetadata(catalogMatch, catalogEntry)
1350
+ };
1351
+ const healthMetadata = getProviderHealthMetadata(
1352
+ providerHealthStore,
1353
+ providerHealthOptions,
1354
+ request.capability,
1355
+ adapter.id,
1356
+ plannedAt
1357
+ );
1358
+ if (!adapter.capabilities.includes(request.capability)) {
1359
+ attempts.push({
1360
+ ...baseAttempt,
1361
+ status: "skipped",
1362
+ reason: "skipped_capability_mismatch"
1363
+ });
1364
+ continue;
1365
+ }
1366
+ if (bundleBlocksCapability) {
1367
+ attempts.push({
1368
+ ...baseAttempt,
1369
+ status: "skipped",
1370
+ reason: "skipped_policy_bundle_uncovered",
1371
+ attestation,
1372
+ catalogPolicyReason: attestation?.reason ?? `Capability "${request.capability}" is not covered by the policy bundle.`
1373
+ });
1374
+ continue;
1375
+ }
1376
+ if (allowedProviders && !allowedProviders.includes(adapter.id)) {
1377
+ attempts.push({
1378
+ ...baseAttempt,
1379
+ status: "skipped",
1380
+ reason: "skipped_allowed_provider"
1381
+ });
1382
+ continue;
1383
+ }
1384
+ if (blockedProviders.includes(adapter.id)) {
1385
+ attempts.push({
1386
+ ...baseAttempt,
1387
+ status: "skipped",
1388
+ reason: "skipped_blocked_provider"
1389
+ });
1390
+ continue;
1391
+ }
1392
+ if (!paymentSelection) {
1393
+ attempts.push({
1394
+ ...baseAttempt,
1395
+ status: "skipped",
1396
+ reason: "skipped_payment_preferences",
1397
+ paymentOptionReason: paymentResolution.reason,
1398
+ message: paymentResolution.reason
1399
+ });
1400
+ continue;
1401
+ }
1402
+ if (effectiveMaxCostUsd !== void 0 && cost && cost.amountUsd > effectiveMaxCostUsd) {
1403
+ attempts.push({
1404
+ ...baseAttempt,
1405
+ status: "skipped",
1406
+ reason: "skipped_budget_exceeded",
1407
+ budgetExceeded: true,
1408
+ message: `Capability "${request.capability}" costs $${cost.amountUsd.toFixed(
1409
+ 2
1410
+ )}, which exceeds maxCostUsd $${effectiveMaxCostUsd.toFixed(
1411
+ 2
1412
+ )}.`
1413
+ });
1414
+ continue;
1415
+ }
1416
+ if (catalogPolicyPlan.enabled && !catalogMatch) {
1417
+ attempts.push({
1418
+ ...baseAttempt,
1419
+ status: "skipped",
1420
+ reason: "skipped_catalog_policy",
1421
+ catalogPolicyReason: `Provider "${adapter.id}" does not match the hosted catalog policy for capability "${request.capability}".`
1422
+ });
1423
+ continue;
1424
+ }
1425
+ if (healthMetadata.healthStatus === "unhealthy") {
1426
+ attempts.push({
1427
+ ...baseAttempt,
1428
+ ...healthMetadata,
1429
+ status: "skipped",
1430
+ reason: "skipped_unhealthy_provider",
1431
+ message: `Provider "${adapter.id}" is cooling down after a recent safe pre-payment failure.`
1432
+ });
1433
+ continue;
1434
+ }
1435
+ attempts.push({
1436
+ ...baseAttempt,
1437
+ ...healthMetadata,
1438
+ status: "considered",
1439
+ reason: "eligible"
1440
+ });
1441
+ eligibleAdapters.push({
1442
+ adapter,
1443
+ paymentSelection
1444
+ });
1445
+ }
1446
+ for (const match of bundleBlocksCapability ? [] : catalogPolicyPlan.missingAdapterMatches) {
1447
+ attempts.push({
1448
+ providerId: match.providerId,
1449
+ cost: match.cost,
1450
+ status: "skipped",
1451
+ reason: "skipped_catalog_adapter_missing",
1452
+ catalogArtifactId: match.entry.artifactId,
1453
+ catalogStatus: match.entry.status,
1454
+ catalogMatchReasons: match.reasons,
1455
+ catalogPolicyReason: `Catalog provider "${match.providerId}" matched policy but has no registered adapter.`
1456
+ });
1457
+ }
1458
+ return {
1459
+ eligibleAdapters,
1460
+ catalogPolicyPlan,
1461
+ paymentStrategy,
1462
+ attestation,
1463
+ explanation: {
1464
+ consideredProviders: attempts.map((attempt) => attempt.providerId),
1465
+ fallbackUsed: false,
1466
+ fallbackCount: 0,
1467
+ stopReason: eligibleAdapters.length > 0 ? "eligible" : "no_eligible_provider",
1468
+ attempts
1469
+ }
1470
+ };
1471
+ }
1472
+ function verifyPolicyBundleCapability(catalogPolicyOptions, capability) {
1473
+ if (!catalogPolicyOptions.bundle || !catalogPolicyOptions.catalog || !catalogPolicyOptions.bundleAdapterInventory) {
1474
+ return void 0;
1475
+ }
1476
+ return verifyProviderCatalogPolicyBundleRuntime({
1477
+ bundle: catalogPolicyOptions.bundle,
1478
+ catalog: catalogPolicyOptions.catalog,
1479
+ catalogPolicy: catalogPolicyOptions.bundlePolicy,
1480
+ adapterInventory: catalogPolicyOptions.bundleAdapterInventory,
1481
+ queries: catalogPolicyOptions.bundleQueries,
1482
+ capability
1483
+ });
1484
+ }
1485
+ function createCatalogPolicyPlan(adapters, request, catalogPolicyOptions, effectiveMaxCostUsd) {
1486
+ const emptyPlan = {
1487
+ enabled: false,
1488
+ effectiveMaxCostUsd,
1489
+ matchesByProviderId: /* @__PURE__ */ new Map(),
1490
+ missingAdapterMatches: [],
1491
+ catalogEntryByProviderId: /* @__PURE__ */ new Map()
1492
+ };
1493
+ if (!catalogPolicyOptions.enabled || !catalogPolicyOptions.catalog) {
1494
+ return emptyPlan;
1495
+ }
1496
+ const matches = findProviderCatalogMatches(catalogPolicyOptions.catalog, {
1497
+ capability: request.capability,
1498
+ protocolMode: catalogPolicyOptions.protocolMode,
1499
+ paymentPreferences: mergePaymentPreferences(
1500
+ catalogPolicyOptions.paymentPreferences,
1501
+ request.paymentPreferences
1502
+ ),
1503
+ paymentStrategy: request.paymentStrategy ?? catalogPolicyOptions.paymentStrategy,
1504
+ maxCostUsd: effectiveMaxCostUsd,
1505
+ requiredReceiptTypes: catalogPolicyOptions.requiredReceiptTypes,
1506
+ providerIds: catalogPolicyOptions.providerIds,
1507
+ blockedProviderIds: catalogPolicyOptions.blockedProviderIds,
1508
+ status: catalogPolicyOptions.status
1509
+ });
1510
+ const registeredAdapterIds = new Set(adapters.map((adapter) => adapter.id));
1511
+ const matchesByProviderId = /* @__PURE__ */ new Map();
1512
+ const missingAdapterMatches = [];
1513
+ for (const match of matches) {
1514
+ if (!matchesByProviderId.has(match.providerId)) {
1515
+ matchesByProviderId.set(match.providerId, match);
1516
+ }
1517
+ if (!registeredAdapterIds.has(match.providerId) && requestAllowsProvider(request, match.providerId)) {
1518
+ missingAdapterMatches.push(match);
1519
+ }
1520
+ }
1521
+ return {
1522
+ enabled: true,
1523
+ effectiveMaxCostUsd,
1524
+ matchesByProviderId,
1525
+ missingAdapterMatches,
1526
+ catalogEntryByProviderId: createCatalogEntryIndex(catalogPolicyOptions.catalog)
1527
+ };
1528
+ }
1529
+ function createCatalogEntryIndex(catalog) {
1530
+ const entries = /* @__PURE__ */ new Map();
1531
+ for (const entry of catalog.entries) {
1532
+ if (!entries.has(entry.provider.id)) {
1533
+ entries.set(entry.provider.id, entry);
1534
+ }
1535
+ }
1536
+ return entries;
1537
+ }
1538
+ function requestAllowsProvider(request, providerId) {
1539
+ const allowedProviders = request.providerPreferences?.allowedProviders;
1540
+ const blockedProviders = request.providerPreferences?.blockedProviders ?? [];
1541
+ return (!allowedProviders || allowedProviders.includes(providerId)) && !blockedProviders.includes(providerId);
1542
+ }
1543
+ function catalogRouteMetadata(match, entry) {
1544
+ if (match) {
1545
+ return {
1546
+ catalogArtifactId: match.entry.artifactId,
1547
+ catalogStatus: match.entry.status,
1548
+ catalogMatchReasons: match.reasons
1549
+ };
1550
+ }
1551
+ if (entry) {
1552
+ return {
1553
+ catalogArtifactId: entry.artifactId,
1554
+ catalogStatus: entry.status
1555
+ };
1556
+ }
1557
+ return {};
1558
+ }
1559
+ function strictestMaxCostUsd(requestMaxCostUsd, policyMaxCostUsd) {
1560
+ if (requestMaxCostUsd === void 0) {
1561
+ return policyMaxCostUsd;
1562
+ }
1563
+ if (policyMaxCostUsd === void 0) {
1564
+ return requestMaxCostUsd;
1565
+ }
1566
+ return Math.min(requestMaxCostUsd, policyMaxCostUsd);
1567
+ }
1568
+ function createNoEligibleProviderError(request, routePlan) {
1569
+ const budgetAttempt = routePlan.explanation.attempts.find(
1570
+ (attempt) => attempt.reason === "skipped_budget_exceeded"
1571
+ );
1572
+ if (budgetAttempt) {
1573
+ return new ProbeMeshError({
1574
+ code: "budget_exceeded",
1575
+ message: budgetAttempt.message ?? `No provider for capability "${request.capability}" fits maxCostUsd.`,
1576
+ capability: request.capability,
1577
+ provider: budgetAttempt.providerId,
1578
+ callId: request.idempotencyKey
1579
+ });
1580
+ }
1581
+ const uncoveredAttempt = routePlan.explanation.attempts.find(
1582
+ (attempt) => attempt.reason === "skipped_policy_bundle_uncovered"
1583
+ );
1584
+ if (uncoveredAttempt) {
1585
+ return new ProbeMeshError({
1586
+ code: "provider_unavailable",
1587
+ message: uncoveredAttempt.catalogPolicyReason ?? `Capability "${request.capability}" is not covered by the policy bundle.`,
1588
+ capability: request.capability,
1589
+ callId: request.idempotencyKey
1590
+ });
1591
+ }
1592
+ const paymentAttempt = routePlan.explanation.attempts.find(
1593
+ (attempt) => attempt.reason === "skipped_payment_preferences"
1594
+ );
1595
+ if (paymentAttempt) {
1596
+ return new ProbeMeshError({
1597
+ code: "provider_unavailable",
1598
+ message: paymentAttempt.message ?? `No provider for capability "${request.capability}" matches paymentPreferences.`,
1599
+ capability: request.capability,
1600
+ provider: paymentAttempt.providerId,
1601
+ callId: request.idempotencyKey
1602
+ });
1603
+ }
1604
+ return new ProbeMeshError({
1605
+ code: "provider_unavailable",
1606
+ message: `No adapter is available for capability "${request.capability}".`,
1607
+ capability: request.capability,
1608
+ callId: request.idempotencyKey
1609
+ });
1610
+ }
1611
+ function providerHealthKey(capability, providerId) {
1612
+ return `${capability}\0${providerId}`;
1613
+ }
1614
+ function getProviderHealthMetadata(store, options, capability, providerId, now) {
1615
+ if (!options.enabled) {
1616
+ return {};
1617
+ }
1618
+ const key = providerHealthKey(capability, providerId);
1619
+ const record = store.records.get(key);
1620
+ if (!record) {
1621
+ return {
1622
+ healthStatus: "healthy"
1623
+ };
1624
+ }
1625
+ if (Date.parse(record.cooldownUntil) <= now.getTime()) {
1626
+ store.records.delete(key);
1627
+ return {
1628
+ healthStatus: "healthy"
1629
+ };
1630
+ }
1631
+ return record;
1632
+ }
1633
+ function markProviderUnhealthy(store, options, capability, providerId) {
1634
+ if (!options.enabled) {
1635
+ return void 0;
1636
+ }
1637
+ const key = providerHealthKey(capability, providerId);
1638
+ const previousRecord = store.records.get(key);
1639
+ const failedAt = /* @__PURE__ */ new Date();
1640
+ const record = {
1641
+ healthStatus: "unhealthy",
1642
+ lastFailureAt: failedAt.toISOString(),
1643
+ cooldownUntil: new Date(
1644
+ failedAt.getTime() + options.cooldownMs
1645
+ ).toISOString(),
1646
+ consecutiveFailures: (previousRecord?.consecutiveFailures ?? 0) + 1
1647
+ };
1648
+ store.records.set(key, record);
1649
+ return record;
1650
+ }
1651
+ function clearProviderHealth(store, options, capability, providerId) {
1652
+ if (!options.enabled) {
1653
+ return;
1654
+ }
1655
+ store.records.delete(providerHealthKey(capability, providerId));
1656
+ }
1657
+ function shouldRememberProviderHealthFailure(error, paymentEvidence, retrySafety) {
1658
+ return !paymentEvidence && retrySafety.safeToAutoRetry && (error.code === "provider_unavailable" || error.code === "provider_timeout");
1659
+ }
1660
+ function markRouteAttempt(explanation, providerId, updates) {
1661
+ const routeAttempt = explanation.attempts.find(
1662
+ (attempt) => attempt.providerId === providerId
1663
+ );
1664
+ if (!routeAttempt) {
1665
+ return;
1666
+ }
1667
+ Object.assign(routeAttempt, updates);
1668
+ }
1669
+ function createRouteMetadata(routeId, explanation, selectedProviderId, attestation, paymentSelection, paymentStrategy) {
1670
+ return {
1671
+ routeId,
1672
+ mode: "hosted",
1673
+ selectedProviderId,
1674
+ paymentStrategy,
1675
+ paymentSelection,
1676
+ explanation,
1677
+ attestation
1678
+ };
1679
+ }
1680
+ async function executeHostedRetry(options) {
1681
+ const {
1682
+ options: routerOptions,
1683
+ response,
1684
+ timeline,
1685
+ attemptStore,
1686
+ providerHealthStore,
1687
+ providerHealthOptions,
1688
+ catalogPolicyOptions,
1689
+ retryRequest
1690
+ } = options;
1691
+ emitTelemetry(routerOptions, timeline, {
1692
+ type: "retry.requested",
1693
+ callId: retryRequest.callId
1694
+ });
1695
+ const sourceAttempt = attemptStore.attempts.get(retryRequest.callId);
1696
+ if (!sourceAttempt) {
1697
+ const retrySafety = notRetryableSafety();
1698
+ const error = new ProbeMeshError({
1699
+ code: "retry_not_available",
1700
+ message: `No hosted attempt is available for callId "${retryRequest.callId}".`,
1701
+ callId: retryRequest.callId,
1702
+ retrySafety
1703
+ });
1704
+ emitBlockedRetry(routerOptions, timeline, error);
1705
+ writeHostedError(response, error, timeline, retrySafety);
1706
+ return;
1707
+ }
1708
+ if (!sourceAttempt.retrySafety.safeToAutoRetry) {
1709
+ const error = new ProbeMeshError({
1710
+ code: "retry_not_available",
1711
+ message: `Hosted attempt "${retryRequest.callId}" is not safe to retry.`,
1712
+ callId: sourceAttempt.attempt.attemptId,
1713
+ receiptRefs: sourceAttempt.receiptRefs,
1714
+ safety: sourceAttempt.safety,
1715
+ retrySafety: sourceAttempt.retrySafety,
1716
+ attempt: sourceAttempt.attempt
1717
+ });
1718
+ emitBlockedRetry(routerOptions, timeline, error);
1719
+ writeHostedError(
1720
+ response,
1721
+ error,
1722
+ timeline,
1723
+ sourceAttempt.retrySafety,
1724
+ sourceAttempt.attempt
1725
+ );
1726
+ return;
1727
+ }
1728
+ const retryAttempt = createRetryAttempt(
1729
+ attemptStore,
1730
+ sourceAttempt,
1731
+ retryRequest.idempotencyKey
1732
+ );
1733
+ const retryCallRequest = {
1734
+ ...sourceAttempt.originalRequest,
1735
+ idempotencyKey: retryAttempt.attemptId
1736
+ };
1737
+ await executeHostedCall({
1738
+ options: routerOptions,
1739
+ response,
1740
+ timeline,
1741
+ attemptStore,
1742
+ providerHealthStore,
1743
+ providerHealthOptions,
1744
+ catalogPolicyOptions,
1745
+ originalRequest: sourceAttempt.originalRequest,
1746
+ callRequest: retryCallRequest,
1747
+ attempt: retryAttempt
1748
+ });
1749
+ }
1750
+ function emitBlockedRetry(options, timeline, error) {
1751
+ emitTelemetry(options, timeline, {
1752
+ type: "retry.blocked",
1753
+ callId: error.callId,
1754
+ errorCode: error.code,
1755
+ safety: error.safety,
1756
+ attempt: error.attempt
1757
+ });
1758
+ emitFailureTelemetry(options, timeline, error, error.attempt);
1759
+ }
1760
+ function createInitialAttempt(callRequest) {
1761
+ const attemptId = callRequest.idempotencyKey ?? generateCallId2();
1762
+ return {
1763
+ originalCallId: attemptId,
1764
+ attemptId,
1765
+ attemptNumber: 1,
1766
+ isRetry: false
1767
+ };
1768
+ }
1769
+ function createRetryAttempt(attemptStore, sourceAttempt, idempotencyKey) {
1770
+ const originalCallId = sourceAttempt.attempt.originalCallId;
1771
+ const attemptNumber = (attemptStore.attemptCounts.get(originalCallId) ?? 1) + 1;
1772
+ const attemptId = idempotencyKey ?? `${originalCallId}:retry:${attemptNumber}`;
1773
+ return {
1774
+ originalCallId,
1775
+ attemptId,
1776
+ attemptNumber,
1777
+ isRetry: true,
1778
+ previousAttemptId: sourceAttempt.attempt.attemptId
1779
+ };
1780
+ }
1781
+ function storeAttempt(attemptStore, record) {
1782
+ attemptStore.attempts.set(record.attempt.attemptId, record);
1783
+ attemptStore.attemptCounts.set(
1784
+ record.attempt.originalCallId,
1785
+ Math.max(
1786
+ attemptStore.attemptCounts.get(record.attempt.originalCallId) ?? 0,
1787
+ record.attempt.attemptNumber
1788
+ )
1789
+ );
1790
+ }
1791
+ function generateCallId2() {
1792
+ return `call_${randomUUID2()}`;
1793
+ }
1794
+ function createTimeline(routeId) {
1795
+ return {
1796
+ routeId,
1797
+ events: []
1798
+ };
1799
+ }
1800
+ function createTimelineAdapters(options, timeline, attempt, attestation) {
1801
+ return options.adapters.map((adapter) => ({
1802
+ ...adapter,
1803
+ call(request, context) {
1804
+ emitTelemetry(options, timeline, {
1805
+ type: "provider.attempted",
1806
+ callId: context.callId,
1807
+ capability: request.capability,
1808
+ provider: adapter.id,
1809
+ attempt,
1810
+ attestation
1811
+ });
1812
+ return adapter.call(request, context);
1813
+ }
1814
+ }));
1815
+ }
1816
+ function assertAuthorized(request, apiKeys) {
1817
+ if (!apiKeys || apiKeys.length === 0) {
1818
+ return;
1819
+ }
1820
+ const authorization = request.headers.authorization;
1821
+ const token = authorization?.startsWith("Bearer ") ? authorization.slice("Bearer ".length) : void 0;
1822
+ if (!token || !apiKeys.includes(token)) {
1823
+ throw new ProbeMeshError({
1824
+ code: "unauthorized",
1825
+ message: "Hosted router request requires a valid bearer token."
1826
+ });
1827
+ }
1828
+ }
1829
+ async function startHostedRouter(options) {
1830
+ const router = createHostedRouter(options);
1831
+ const server = createServer((request, response) => {
1832
+ void router.handle(request, response);
1833
+ });
1834
+ await new Promise((resolve, reject) => {
1835
+ server.once("error", reject);
1836
+ server.listen(options.port, options.host ?? "127.0.0.1", () => {
1837
+ server.off("error", reject);
1838
+ resolve();
1839
+ });
1840
+ });
1841
+ const address = server.address();
1842
+ const port = typeof address === "object" && address ? address.port : options.port;
1843
+ const host = options.host ?? "127.0.0.1";
1844
+ return {
1845
+ server,
1846
+ url: `http://${host}:${port}`,
1847
+ close() {
1848
+ return new Promise((resolve, reject) => {
1849
+ server.close((error) => {
1850
+ if (error) {
1851
+ reject(error);
1852
+ return;
1853
+ }
1854
+ resolve();
1855
+ });
1856
+ });
1857
+ }
1858
+ };
1859
+ }
1860
+ async function readJsonBody(request) {
1861
+ const body = await readRawJsonBody(request);
1862
+ if (body === void 0) {
1863
+ throw new ProbeMeshError({
1864
+ code: "invalid_request",
1865
+ message: "Hosted call request body is required."
1866
+ });
1867
+ }
1868
+ return body;
1869
+ }
1870
+ async function readOptionalJsonBody(request) {
1871
+ return await readRawJsonBody(request) ?? {};
1872
+ }
1873
+ async function readRawJsonBody(request) {
1874
+ const chunks = [];
1875
+ for await (const chunk of request) {
1876
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1877
+ }
1878
+ const rawBody = Buffer.concat(chunks).toString("utf8");
1879
+ if (rawBody.length === 0) {
1880
+ return void 0;
1881
+ }
1882
+ try {
1883
+ return JSON.parse(rawBody);
1884
+ } catch (error) {
1885
+ throw new ProbeMeshError({
1886
+ code: "invalid_request",
1887
+ message: "Hosted call request body must be valid JSON.",
1888
+ cause: error
1889
+ });
1890
+ }
1891
+ }
1892
+ function validateRetryBody(callId, body) {
1893
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
1894
+ throw new ProbeMeshError({
1895
+ code: "invalid_request",
1896
+ message: "Hosted retry request body must be an object when provided.",
1897
+ callId
1898
+ });
1899
+ }
1900
+ const candidate = body;
1901
+ if (candidate.idempotencyKey !== void 0 && (typeof candidate.idempotencyKey !== "string" || candidate.idempotencyKey.length === 0)) {
1902
+ throw new ProbeMeshError({
1903
+ code: "invalid_request",
1904
+ message: "Hosted retry idempotencyKey must be a non-empty string when provided.",
1905
+ callId
1906
+ });
1907
+ }
1908
+ return {
1909
+ callId,
1910
+ idempotencyKey: candidate.idempotencyKey
1911
+ };
1912
+ }
1913
+ function addHostedRouteMetadata(response, routeId, timeline, retrySafety, attempt, explanation, attestation, paymentSelection, paymentStrategy) {
1914
+ return {
1915
+ ...response,
1916
+ timeline,
1917
+ retrySafety,
1918
+ attempt,
1919
+ route: createRouteMetadata(
1920
+ routeId,
1921
+ explanation ?? {
1922
+ consideredProviders: [response.provider.id],
1923
+ selectedProviderId: response.provider.id,
1924
+ fallbackUsed: false,
1925
+ fallbackCount: 0,
1926
+ stopReason: "stopped_completed",
1927
+ attempts: [
1928
+ {
1929
+ providerId: response.provider.id,
1930
+ status: "succeeded",
1931
+ reason: "selected",
1932
+ paymentEvidence: hasPaymentEvidence(
1933
+ response.safety,
1934
+ response.receiptRefs
1935
+ )
1936
+ }
1937
+ ]
1938
+ },
1939
+ response.provider.id,
1940
+ attestation,
1941
+ paymentSelection ?? response.route?.paymentSelection,
1942
+ paymentStrategy
1943
+ )
1944
+ };
1945
+ }
1946
+ function writeHostedError(response, error, timeline, retrySafety, attempt, route) {
1947
+ const normalizedError = toHostedProbeMeshError(error);
1948
+ writeJson(response, statusCodeForError(normalizedError.code), {
1949
+ ...normalizedError.toJSON(),
1950
+ timeline,
1951
+ retrySafety,
1952
+ attempt: normalizedError.attempt ?? attempt,
1953
+ route: normalizedError.route ?? route
1954
+ });
1955
+ }
1956
+ function toHostedProbeMeshError(error) {
1957
+ if (isProbeMeshError(error)) {
1958
+ return error;
1959
+ }
1960
+ return new ProbeMeshError({
1961
+ code: "provider_unavailable",
1962
+ message: "Hosted router failed while handling the call.",
1963
+ cause: error
1964
+ });
1965
+ }
1966
+ function retrySafetyForCompletedCall() {
1967
+ return {
1968
+ retryable: false,
1969
+ safeToAutoRetry: false,
1970
+ reason: "completed"
1971
+ };
1972
+ }
1973
+ function notRetryableSafety() {
1974
+ return {
1975
+ retryable: false,
1976
+ safeToAutoRetry: false,
1977
+ reason: "not_retryable"
1978
+ };
1979
+ }
1980
+ function retrySafetyForError(error) {
1981
+ if (!isProbeMeshError(error)) {
1982
+ return {
1983
+ retryable: true,
1984
+ safeToAutoRetry: true,
1985
+ reason: "no_payment_attempted"
1986
+ };
1987
+ }
1988
+ if (error.code === "budget_exceeded" || error.code === "unauthorized" || error.code === "invalid_request" || error.code === "retry_not_available") {
1989
+ return {
1990
+ retryable: false,
1991
+ safeToAutoRetry: false,
1992
+ reason: "not_retryable"
1993
+ };
1994
+ }
1995
+ if (moneyMayHaveMoved(error.safety, error.receiptRefs)) {
1996
+ return {
1997
+ retryable: false,
1998
+ safeToAutoRetry: false,
1999
+ reason: "money_may_have_moved"
2000
+ };
2001
+ }
2002
+ if (error.code === "payment_failed" && error.safety?.moneyMayHaveMoved === false) {
2003
+ return {
2004
+ retryable: true,
2005
+ safeToAutoRetry: true,
2006
+ reason: "payment_failed_no_charge"
2007
+ };
2008
+ }
2009
+ if ((error.code === "provider_unavailable" || error.code === "provider_timeout") && !hasPaymentEvidence(error.safety, error.receiptRefs)) {
2010
+ return {
2011
+ retryable: true,
2012
+ safeToAutoRetry: true,
2013
+ reason: "no_payment_attempted"
2014
+ };
2015
+ }
2016
+ return {
2017
+ retryable: true,
2018
+ safeToAutoRetry: false,
2019
+ reason: "unknown_payment_state"
2020
+ };
2021
+ }
2022
+ function moneyMayHaveMoved(safety, receiptRefs) {
2023
+ return safety?.moneyMayHaveMoved === true || receiptRefs?.some((receiptRef) => receiptRef.type === "payment_proof") === true;
2024
+ }
2025
+ function hasPaymentEvidence(safety, receiptRefs) {
2026
+ return moneyMayHaveMoved(safety, receiptRefs);
2027
+ }
2028
+ function getPricing2(adapter, capability) {
2029
+ if (!adapter.pricing) {
2030
+ return void 0;
2031
+ }
2032
+ const pricing = adapter.pricing;
2033
+ if (typeof pricing.amountUsd === "number" && typeof pricing.unit === "string") {
2034
+ return pricing;
2035
+ }
2036
+ return adapter.pricing[capability];
2037
+ }
2038
+ function pricingToCost2(pricing) {
2039
+ if (!pricing) {
2040
+ return void 0;
2041
+ }
2042
+ return {
2043
+ amountUsd: pricing.amountUsd,
2044
+ currency: pricing.currency ?? "USD",
2045
+ unit: pricing.unit
2046
+ };
2047
+ }
2048
+ function emitFailureTelemetry(options, timeline, error, attempt, attestation) {
2049
+ if (!isProbeMeshError(error)) {
2050
+ emitTelemetry(options, timeline, {
2051
+ type: "call.failed",
2052
+ status: "failed",
2053
+ errorCode: "provider_unavailable",
2054
+ attempt,
2055
+ attestation
2056
+ });
2057
+ return;
2058
+ }
2059
+ emitTelemetry(options, timeline, {
2060
+ type: "call.failed",
2061
+ callId: error.callId,
2062
+ capability: error.capability,
2063
+ provider: error.provider,
2064
+ status: "failed",
2065
+ errorCode: error.code,
2066
+ safety: error.safety,
2067
+ attempt: error.attempt ?? attempt,
2068
+ attestation
2069
+ });
2070
+ }
2071
+ async function emitCallAuditArtifact(options, details) {
2072
+ if (!options.auditArtifacts || options.auditArtifacts.enabled === false || !options.auditArtifacts.onArtifact) {
2073
+ return;
2074
+ }
2075
+ try {
2076
+ const artifact = createProbeMeshCallAuditArtifact({
2077
+ request: details.request,
2078
+ response: details.response,
2079
+ error: details.error,
2080
+ timeline: details.timeline,
2081
+ retrySafety: details.retrySafety,
2082
+ attempt: details.attempt,
2083
+ route: details.route
2084
+ });
2085
+ await options.auditArtifacts.onArtifact(artifact);
2086
+ } catch {
2087
+ }
2088
+ }
2089
+ function emitPaymentAndReceiptTelemetry(options, timeline, details) {
2090
+ if (shouldEmitPaymentAttempted(details.safety, details.receiptRefs)) {
2091
+ emitTelemetry(options, timeline, {
2092
+ type: "payment.attempted",
2093
+ callId: details.callId,
2094
+ capability: details.capability,
2095
+ provider: details.provider,
2096
+ safety: details.safety,
2097
+ attempt: details.attempt,
2098
+ attestation: details.attestation
2099
+ });
2100
+ }
2101
+ for (const receiptRef of details.receiptRefs ?? []) {
2102
+ emitTelemetry(options, timeline, {
2103
+ type: "receipt.recorded",
2104
+ callId: details.callId,
2105
+ capability: details.capability,
2106
+ provider: receiptRef.provider,
2107
+ receiptRef,
2108
+ safety: details.safety,
2109
+ attempt: details.attempt,
2110
+ attestation: details.attestation
2111
+ });
2112
+ }
2113
+ }
2114
+ function shouldEmitPaymentAttempted(safety, receiptRefs) {
2115
+ return safety?.paymentStatus === "authorized" || safety?.paymentStatus === "failed" || safety?.moneyMayHaveMoved === true || receiptRefs?.some((receiptRef) => receiptRef.type === "payment_proof") === true;
2116
+ }
2117
+ function emitTelemetry(options, timeline, event) {
2118
+ const timelineEvent = {
2119
+ ...event,
2120
+ routeId: timeline.routeId,
2121
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2122
+ };
2123
+ timeline.events.push(timelineEvent);
2124
+ options.onTelemetryEvent?.(timelineEvent);
2125
+ }
2126
+ function statusCodeForError(code) {
2127
+ switch (code) {
2128
+ case "unauthorized":
2129
+ return 401;
2130
+ case "invalid_request":
2131
+ return 400;
2132
+ case "retry_not_available":
2133
+ return 409;
2134
+ case "budget_exceeded":
2135
+ case "receipt_missing":
2136
+ case "invalid_provider_response":
2137
+ return 422;
2138
+ case "provider_timeout":
2139
+ case "timeout_after_payment":
2140
+ return 504;
2141
+ case "provider_unavailable":
2142
+ default:
2143
+ return 503;
2144
+ }
2145
+ }
2146
+ function writeJson(response, statusCode, body) {
2147
+ response.statusCode = statusCode;
2148
+ response.setHeader("content-type", "application/json");
2149
+ response.end(JSON.stringify(body));
2150
+ }
2151
+
2152
+ export {
2153
+ createProbeMesh,
2154
+ createHostedRouter,
2155
+ startHostedRouter
2156
+ };
2157
+ //# sourceMappingURL=chunk-TV42EZSI.js.map