@oneshot-agent/sdk 0.38.1 → 0.42.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/README.md +17 -0
- package/dist/approvals.d.ts +107 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +103 -0
- package/dist/approvals.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +25 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +77 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +177 -11
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.OneShot = exports.executeSwap = exports.getSwapQuote = exports.ReadOnlyWalletProvider = exports.CdpWalletProvider = exports.EthersWalletProvider = void 0;
|
|
40
40
|
const physical_mail_1 = require("./physical-mail");
|
|
41
41
|
__exportStar(require("./physical-mail"), exports);
|
|
42
|
+
__exportStar(require("./approvals"), exports);
|
|
42
43
|
const deadline_1 = require("./deadline");
|
|
43
44
|
const errors_1 = require("./errors");
|
|
44
45
|
const read_only_1 = require("./providers/read-only");
|
|
@@ -55,6 +56,7 @@ var swap_1 = require("./swap");
|
|
|
55
56
|
Object.defineProperty(exports, "getSwapQuote", { enumerable: true, get: function () { return swap_1.getSwapQuote; } });
|
|
56
57
|
Object.defineProperty(exports, "executeSwap", { enumerable: true, get: function () { return swap_1.executeSwap; } });
|
|
57
58
|
__exportStar(require("./errors"), exports);
|
|
59
|
+
const approvals_1 = require("./approvals");
|
|
58
60
|
// Receipt verification (`canonicalizeReceipt`, `verifyReceipt`,
|
|
59
61
|
// `receiptFromWire`, and their supporting types `SignableReceipt`,
|
|
60
62
|
// `ReceiptSignatureFields`, `ReceiptJwk`, `ReceiptJwks`,
|
|
@@ -80,7 +82,7 @@ __exportStar(require("./errors"), exports);
|
|
|
80
82
|
// not removing the field. Import `SignableReceipt` from the `./receipt`
|
|
81
83
|
// subpath directly if you need it.
|
|
82
84
|
// Keep in sync with package.json `version`. Guarded by version.test.ts.
|
|
83
|
-
const SDK_VERSION = '0.
|
|
85
|
+
const SDK_VERSION = '0.42.0';
|
|
84
86
|
/** HTTP poll cadence while push is unconfirmed: fast first checks, settling at 2s. */
|
|
85
87
|
const HTTP_POLL_BACKOFF_MS = [300, 600, 1000, 2000];
|
|
86
88
|
/** HTTP poll cadence once the WebSocket has delivered for this request. */
|
|
@@ -226,6 +228,11 @@ class OneShot {
|
|
|
226
228
|
await this.failFromResponse('Physical mail request failed', response);
|
|
227
229
|
return await response.json();
|
|
228
230
|
}, input => this.executeToolRequest('/v1/tools/physical-mail/send', { ...input, wait: false }));
|
|
231
|
+
/** Ask a named human to decide, and read decisions. See ./approvals.ts. */
|
|
232
|
+
this.approvals = new approvals_1.Approvals((path, method, body, scope) => this.agentJson(path, method, body, scope));
|
|
233
|
+
/** Which paid calls run, are denied, or wait for approval. Wallet sessions write it. */
|
|
234
|
+
this.policy = new approvals_1.ActionPolicies((path, method, body, scope) => this.agentJson(path, method, body, scope));
|
|
235
|
+
this.readProofWarned = false;
|
|
229
236
|
/** ETH mode: signed payments not yet observed as settled, keyed by reservation id. */
|
|
230
237
|
this._usdcPending = new Map();
|
|
231
238
|
this._usdcReservationSeq = 0;
|
|
@@ -392,6 +399,10 @@ class OneShot {
|
|
|
392
399
|
// Only the send call carries the key — the quote call has no side effects.
|
|
393
400
|
payload.idempotencyKey = options.idempotencyKey;
|
|
394
401
|
}
|
|
402
|
+
if (options.approvalId) {
|
|
403
|
+
// Releases a send the action policy held (see ApprovalRequiredError).
|
|
404
|
+
payload.approvalId = options.approvalId;
|
|
405
|
+
}
|
|
395
406
|
return this.executeToolRequest('/v1/tools/email/send', payload, quote.quote_id);
|
|
396
407
|
}
|
|
397
408
|
/** List the caller's domain pool with warmup and rotation metadata. */
|
|
@@ -581,6 +592,7 @@ class OneShot {
|
|
|
581
592
|
payload,
|
|
582
593
|
signal: options.signal,
|
|
583
594
|
maxCost: options.maxCost,
|
|
595
|
+
approvalId: options.approvalId,
|
|
584
596
|
quoteTimeoutMs: 120000,
|
|
585
597
|
execTimeoutMs: 60000,
|
|
586
598
|
expectMsg: 'Expected 402 for quote',
|
|
@@ -649,6 +661,7 @@ class OneShot {
|
|
|
649
661
|
payload,
|
|
650
662
|
signal: options.signal,
|
|
651
663
|
maxCost: options.maxCost,
|
|
664
|
+
approvalId: options.approvalId,
|
|
652
665
|
expectMsg: 'Expected 402 for quote',
|
|
653
666
|
totalOf: (ctx) => ctx.total,
|
|
654
667
|
onQuote: (ctx) => this.log(`Voice quote: $${ctx.total} for ${ctx.estimated_duration_minutes}min call`),
|
|
@@ -711,6 +724,7 @@ class OneShot {
|
|
|
711
724
|
payload,
|
|
712
725
|
signal: options.signal,
|
|
713
726
|
maxCost: options.maxCost,
|
|
727
|
+
approvalId: options.approvalId,
|
|
714
728
|
expectMsg: 'Expected 402 for quote',
|
|
715
729
|
totalOf: (ctx) => ctx.total,
|
|
716
730
|
onQuote: (ctx) => this.log(`SMS quote: $${ctx.total} for ${ctx.segment_count} segment(s) to ${recipientCount} recipient(s)`),
|
|
@@ -1133,6 +1147,105 @@ class OneShot {
|
|
|
1133
1147
|
}
|
|
1134
1148
|
return response.json();
|
|
1135
1149
|
}
|
|
1150
|
+
// ---------------------------------------------------------------------------
|
|
1151
|
+
// Inbound voice — the agent's number answers its own calls
|
|
1152
|
+
// ---------------------------------------------------------------------------
|
|
1153
|
+
/**
|
|
1154
|
+
* Get this agent a voice number without placing a call. Returns the existing
|
|
1155
|
+
* active number for free; otherwise buys one and charges the one-time phone
|
|
1156
|
+
* registration fee from prepaid credits (see `topUpCredits`). Too little
|
|
1157
|
+
* credit throws a 402 and buys nothing. Then configure it with
|
|
1158
|
+
* `setInboundVoice`.
|
|
1159
|
+
*
|
|
1160
|
+
* @example
|
|
1161
|
+
* ```typescript
|
|
1162
|
+
* const number = await agent.provisionVoiceNumber();
|
|
1163
|
+
* await agent.setInboundVoice(number.id, { prompt: 'You answer for Acme Dental.' });
|
|
1164
|
+
* ```
|
|
1165
|
+
*/
|
|
1166
|
+
async provisionVoiceNumber() {
|
|
1167
|
+
return this.signedJson('POST', '/v1/tools/voice/numbers', {}, 'write', 'Failed to provision a phone number');
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* List this agent's phone numbers. A number comes from `provisionVoiceNumber`
|
|
1171
|
+
* or the agent's first outbound voice call.
|
|
1172
|
+
*
|
|
1173
|
+
* @example
|
|
1174
|
+
* ```typescript
|
|
1175
|
+
* const { numbers } = await agent.voiceNumbers();
|
|
1176
|
+
* ```
|
|
1177
|
+
*/
|
|
1178
|
+
async voiceNumbers() {
|
|
1179
|
+
return this.signedJson('GET', '/v1/tools/voice/numbers', undefined, 'read', 'Failed to list phone numbers');
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Answer calls to one of the agent's numbers. Replaces the whole config.
|
|
1183
|
+
* Inbound calls are paid from prepaid credits (see `topUpCredits`), per
|
|
1184
|
+
* started minute; a call is declined when credits cannot cover the minimum
|
|
1185
|
+
* fee. Keep the returned `webhook_secret` if you set `webhook_url`.
|
|
1186
|
+
*
|
|
1187
|
+
* @example
|
|
1188
|
+
* ```typescript
|
|
1189
|
+
* await agent.setInboundVoice(numberId, {
|
|
1190
|
+
* prompt: 'You answer for Acme Dental. Book cleanings; take a message for anything else.',
|
|
1191
|
+
* business_hours: { timezone: 'America/New_York', windows: [{ days: ['mon','tue','wed','thu','fri'], start: '09:00', end: '17:00' }] },
|
|
1192
|
+
* after_hours: 'take_message',
|
|
1193
|
+
* });
|
|
1194
|
+
* ```
|
|
1195
|
+
*/
|
|
1196
|
+
async setInboundVoice(phoneNumberId, config) {
|
|
1197
|
+
this.validate(phoneNumberId, 'phoneNumberId');
|
|
1198
|
+
return this.signedJson('PUT', `/v1/tools/voice/numbers/${encodeURIComponent(phoneNumberId)}/inbound`, config, 'write', 'Failed to set inbound voice config');
|
|
1199
|
+
}
|
|
1200
|
+
/** Current inbound config for a number (throws 404 when none is set). */
|
|
1201
|
+
async getInboundVoice(phoneNumberId) {
|
|
1202
|
+
this.validate(phoneNumberId, 'phoneNumberId');
|
|
1203
|
+
return this.signedJson('GET', `/v1/tools/voice/numbers/${encodeURIComponent(phoneNumberId)}/inbound`, undefined, 'read', 'Failed to get inbound voice config');
|
|
1204
|
+
}
|
|
1205
|
+
/** Stop answering calls to a number. The settings are kept, disabled. */
|
|
1206
|
+
async disableInboundVoice(phoneNumberId) {
|
|
1207
|
+
this.validate(phoneNumberId, 'phoneNumberId');
|
|
1208
|
+
return this.signedJson('DELETE', `/v1/tools/voice/numbers/${encodeURIComponent(phoneNumberId)}/inbound`, undefined, 'write', 'Failed to disable inbound voice');
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* List inbound calls, newest first.
|
|
1212
|
+
*
|
|
1213
|
+
* @example
|
|
1214
|
+
* ```typescript
|
|
1215
|
+
* const { calls } = await agent.inboundCalls({ limit: 20 });
|
|
1216
|
+
* for (const c of calls) console.log(c.from, c.summary);
|
|
1217
|
+
* ```
|
|
1218
|
+
*/
|
|
1219
|
+
async inboundCalls(options = {}) {
|
|
1220
|
+
const qs = this.buildQuery({
|
|
1221
|
+
limit: options.limit || undefined,
|
|
1222
|
+
before: options.before || undefined,
|
|
1223
|
+
phone_number_id: options.phone_number_id || undefined,
|
|
1224
|
+
include_transcript: options.include_transcript ? 'true' : undefined,
|
|
1225
|
+
});
|
|
1226
|
+
return this.signedJson('GET', `/v1/tools/voice/inbound${qs ? `?${qs}` : ''}`, undefined, 'read', 'Failed to list inbound calls');
|
|
1227
|
+
}
|
|
1228
|
+
/** One inbound call, with transcript. */
|
|
1229
|
+
async inboundCall(callId) {
|
|
1230
|
+
this.validate(callId, 'callId');
|
|
1231
|
+
return this.signedJson('GET', `/v1/tools/voice/inbound/${encodeURIComponent(callId)}`, undefined, 'read', 'Failed to get inbound call');
|
|
1232
|
+
}
|
|
1233
|
+
/** Signed free request returning the `data` field of `{ success, data }`. */
|
|
1234
|
+
async signedJson(method, path, body, scope, failure) {
|
|
1235
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
1236
|
+
method,
|
|
1237
|
+
headers: {
|
|
1238
|
+
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
1239
|
+
...(await this.signedReadHeaders(scope)),
|
|
1240
|
+
},
|
|
1241
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
1242
|
+
});
|
|
1243
|
+
if (!response.ok) {
|
|
1244
|
+
throw new errors_2.ToolError(failure, response.status, await response.text());
|
|
1245
|
+
}
|
|
1246
|
+
const json = await response.json();
|
|
1247
|
+
return (json.data ?? json);
|
|
1248
|
+
}
|
|
1136
1249
|
/**
|
|
1137
1250
|
* Get a specific inbound SMS message
|
|
1138
1251
|
*
|
|
@@ -1192,7 +1305,7 @@ class OneShot {
|
|
|
1192
1305
|
this.validate(notificationId, 'notificationId');
|
|
1193
1306
|
const response = await fetch(`${this.baseUrl}/v1/tools/notifications/${notificationId}/read`, {
|
|
1194
1307
|
method: 'PATCH',
|
|
1195
|
-
headers: await this.signedReadHeaders()
|
|
1308
|
+
headers: await this.signedReadHeaders('write')
|
|
1196
1309
|
});
|
|
1197
1310
|
if (response.status === 404) {
|
|
1198
1311
|
throw new errors_2.ToolError('Notification not found', 404, 'Notification not found');
|
|
@@ -1781,7 +1894,7 @@ class OneShot {
|
|
|
1781
1894
|
* read another agent's data by supplying its address. The proof binds this
|
|
1782
1895
|
* request to the wallet the SDK controls; the server verifies the signature
|
|
1783
1896
|
* locally. A fresh nonce per call prevents replay. Signing failure falls back
|
|
1784
|
-
* to plain headers
|
|
1897
|
+
* to plain headers, with a one-time warning.
|
|
1785
1898
|
*/
|
|
1786
1899
|
async signedReadHeaders(scope = 'read') {
|
|
1787
1900
|
const base = this.headers();
|
|
@@ -1806,7 +1919,16 @@ class OneShot {
|
|
|
1806
1919
|
return { ...base, 'x-agent-proof': proof };
|
|
1807
1920
|
}
|
|
1808
1921
|
catch (err) {
|
|
1809
|
-
|
|
1922
|
+
// Once per client, even without debug: when the API enforces proofs,
|
|
1923
|
+
// these requests will be rejected, and a silent fallback would leave
|
|
1924
|
+
// the caller with a 401 and no idea why.
|
|
1925
|
+
if (!this.readProofWarned) {
|
|
1926
|
+
this.readProofWarned = true;
|
|
1927
|
+
this.logger(`[OneShot] Warning: could not sign the x-agent-proof read proof, so requests go without it and will be rejected where the API enforces proofs. Pass a signer that supports signTypedData, or use an access token. Cause: ${err}`);
|
|
1928
|
+
}
|
|
1929
|
+
else {
|
|
1930
|
+
this.log(`Failed to sign read proof (continuing without): ${err}`);
|
|
1931
|
+
}
|
|
1810
1932
|
return base;
|
|
1811
1933
|
}
|
|
1812
1934
|
}
|
|
@@ -1904,6 +2026,19 @@ class OneShot {
|
|
|
1904
2026
|
* console.log(`${b.spent_today_usdc} of ${b.daily_usdc} spent, resets ${b.resets_at}`);
|
|
1905
2027
|
* ```
|
|
1906
2028
|
*/
|
|
2029
|
+
/** Signed-proof JSON request to a free agent route; returns `data`. Errors map through failFromResponse. */
|
|
2030
|
+
async agentJson(path, method, body, scope = 'read') {
|
|
2031
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
2032
|
+
method,
|
|
2033
|
+
headers: { 'Content-Type': 'application/json', ...(await this.signedReadHeaders(scope)) },
|
|
2034
|
+
...(body === undefined || method === 'GET' ? {} : { body: JSON.stringify(body) }),
|
|
2035
|
+
signal: AbortSignal.timeout(30000),
|
|
2036
|
+
});
|
|
2037
|
+
if (!response.ok)
|
|
2038
|
+
await this.failFromResponse(`${method} ${path.split('?')[0]} failed`, response);
|
|
2039
|
+
const json = await response.json();
|
|
2040
|
+
return (json && typeof json === 'object' && 'data' in json ? json.data : json);
|
|
2041
|
+
}
|
|
1907
2042
|
async budgets() {
|
|
1908
2043
|
const response = await fetch(`${this.baseUrl}/v1/agents/me/budgets`, {
|
|
1909
2044
|
headers: await this.signedReadHeaders(),
|
|
@@ -2018,6 +2153,9 @@ class OneShot {
|
|
|
2018
2153
|
const rejection = [402, 500].includes(response.status) ? this.parsePaymentRejection(text) : undefined;
|
|
2019
2154
|
if (rejection)
|
|
2020
2155
|
throw rejection;
|
|
2156
|
+
const policy = response.status === 403 ? this.parsePolicyRejection(text) : undefined;
|
|
2157
|
+
if (policy)
|
|
2158
|
+
throw policy;
|
|
2021
2159
|
const budget = response.status === 403 ? this.parseBudgetRejection(text) : undefined;
|
|
2022
2160
|
if (budget)
|
|
2023
2161
|
throw budget;
|
|
@@ -2098,6 +2236,23 @@ class OneShot {
|
|
|
2098
2236
|
* "my own budget stopped this" separately from an auth failure or a payment
|
|
2099
2237
|
* rejection. Any other 403 falls through to ToolError.
|
|
2100
2238
|
*/
|
|
2239
|
+
/** 403 from the action-policy gate: held for approval, or denied. */
|
|
2240
|
+
parsePolicyRejection(text) {
|
|
2241
|
+
let body;
|
|
2242
|
+
try {
|
|
2243
|
+
body = JSON.parse(text);
|
|
2244
|
+
}
|
|
2245
|
+
catch {
|
|
2246
|
+
return undefined;
|
|
2247
|
+
}
|
|
2248
|
+
if (body?.error === 'approval_required' && typeof body.approval_id === 'string') {
|
|
2249
|
+
return new errors_2.ApprovalRequiredError(body.message ?? 'This call needs a human approval', body.approval_id, body.approval, body.approval?.policy_rule ?? undefined);
|
|
2250
|
+
}
|
|
2251
|
+
if (body?.error === 'action_denied_by_policy') {
|
|
2252
|
+
return new errors_2.ActionDeniedError(body.message ?? 'Denied by the action policy', body.rule);
|
|
2253
|
+
}
|
|
2254
|
+
return undefined;
|
|
2255
|
+
}
|
|
2101
2256
|
parseBudgetRejection(text) {
|
|
2102
2257
|
try {
|
|
2103
2258
|
const body = JSON.parse(text);
|
|
@@ -2163,6 +2318,12 @@ class OneShot {
|
|
|
2163
2318
|
return undefined;
|
|
2164
2319
|
return { 'Idempotency-Key': idempotencyKey };
|
|
2165
2320
|
}
|
|
2321
|
+
/** X-Approval-Id, sent on both legs; the server reads it only on the paid leg. */
|
|
2322
|
+
approvalHeader(approvalId) {
|
|
2323
|
+
if (!approvalId)
|
|
2324
|
+
return undefined;
|
|
2325
|
+
return { 'X-Approval-Id': approvalId };
|
|
2326
|
+
}
|
|
2166
2327
|
async readReliabilityJson(path, signed, allowDegraded = false) {
|
|
2167
2328
|
const scope = (0, deadline_1.deadlineScope)(undefined, signed ? 10000 : 5000);
|
|
2168
2329
|
try {
|
|
@@ -2216,11 +2377,13 @@ class OneShot {
|
|
|
2216
2377
|
}
|
|
2217
2378
|
}
|
|
2218
2379
|
async executeToolRequestImpl(endpoint, options, quoteId, context = { phase: "initialization" }) {
|
|
2219
|
-
const { totalTimeoutMs, onRequestCreated, onAccepted, signal, onStatusUpdate, wait = true, waitForPhones, phoneTimeoutSec, idempotencyKey, maxCost, ...payload } = options;
|
|
2380
|
+
const { totalTimeoutMs, onRequestCreated, onAccepted, signal, onStatusUpdate, wait = true, waitForPhones, phoneTimeoutSec, idempotencyKey, approvalId, maxCost, ...payload } = options;
|
|
2220
2381
|
const extraHeaders = {
|
|
2221
2382
|
...this.maxCostHeader(maxCost),
|
|
2222
2383
|
...this.idempotencyHeader(idempotencyKey),
|
|
2223
2384
|
};
|
|
2385
|
+
// Releases a call the action policy held (ToolOptions.approvalId).
|
|
2386
|
+
Object.assign(extraHeaders, this.approvalHeader(approvalId));
|
|
2224
2387
|
if (payload.memo !== undefined) {
|
|
2225
2388
|
if (typeof payload.memo !== 'string' || payload.memo.trim().length === 0) {
|
|
2226
2389
|
delete payload.memo; // Drop invalid memo silently
|
|
@@ -2329,7 +2492,8 @@ class OneShot {
|
|
|
2329
2492
|
*/
|
|
2330
2493
|
async runQuoteToPay(cfg) {
|
|
2331
2494
|
await this.ensureBudgetsSynced();
|
|
2332
|
-
const
|
|
2495
|
+
const approval = this.approvalHeader(cfg.approvalId);
|
|
2496
|
+
const quoteResp = await this.makeRequest(cfg.endpoint, cfg.payload, undefined, undefined, cfg.signal, cfg.quoteTimeoutMs, { ...this.maxCostHeader(cfg.maxCost), ...approval });
|
|
2333
2497
|
if (quoteResp.status === 400 && cfg.on400) {
|
|
2334
2498
|
await cfg.on400(quoteResp);
|
|
2335
2499
|
}
|
|
@@ -2344,7 +2508,7 @@ class OneShot {
|
|
|
2344
2508
|
this.assertWithinBudget(cfg.totalOf(quoteData.context));
|
|
2345
2509
|
if (this._accessToken) {
|
|
2346
2510
|
this.checkAbortBeforePayment(cfg.signal);
|
|
2347
|
-
const execResp = await this.retryAsCreditsSession(quoteData, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal, cfg.execTimeoutMs);
|
|
2511
|
+
const execResp = await this.retryAsCreditsSession(quoteData, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal, cfg.execTimeoutMs, approval);
|
|
2348
2512
|
return { context: quoteData.context, execResp };
|
|
2349
2513
|
}
|
|
2350
2514
|
const paymentInfo = {
|
|
@@ -2357,10 +2521,10 @@ class OneShot {
|
|
|
2357
2521
|
token: { address: quoteData.payment_request.token_address, symbol: 'USDC', decimals: 6 }
|
|
2358
2522
|
};
|
|
2359
2523
|
this.checkAbortBeforePayment(cfg.signal);
|
|
2360
|
-
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal);
|
|
2524
|
+
const { accepted, resource, extensions } = await this.getAcceptedRequirements(quoteResp, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal, approval);
|
|
2361
2525
|
paymentInfo.amount = this.chargeAmount(accepted, quoteData.payment_request.amount);
|
|
2362
2526
|
const signed = await this.signPaymentAuthorization(paymentInfo, accepted, resource, extensions);
|
|
2363
|
-
const execResp = await this.makePaidRequest(signed, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal, cfg.execTimeoutMs);
|
|
2527
|
+
const execResp = await this.makePaidRequest(signed, cfg.endpoint, cfg.payload, quoteData.context.quote_id, cfg.signal, cfg.execTimeoutMs, approval);
|
|
2364
2528
|
return { context: quoteData.context, execResp };
|
|
2365
2529
|
}
|
|
2366
2530
|
/**
|
|
@@ -2937,13 +3101,15 @@ class OneShot {
|
|
|
2937
3101
|
* Quote-based routes don't include payment-required header on the initial 402.
|
|
2938
3102
|
* If missing, probe with quote ID (no payment) to trigger the x402 middleware's 402.
|
|
2939
3103
|
*/
|
|
2940
|
-
async getAcceptedRequirements(initialResp, endpoint, payload, quoteId, signal) {
|
|
3104
|
+
async getAcceptedRequirements(initialResp, endpoint, payload, quoteId, signal, extraHeaders) {
|
|
2941
3105
|
const header = initialResp.headers.get('payment-required');
|
|
2942
3106
|
if (header) {
|
|
2943
3107
|
return this.parsePaymentRequired(header);
|
|
2944
3108
|
}
|
|
2945
3109
|
// Probe: send quote ID without payment to get x402 middleware's 402
|
|
2946
|
-
|
|
3110
|
+
// Carries X-Approval-Id: a policy-held call's probe must reach the 402,
|
|
3111
|
+
// not be held again.
|
|
3112
|
+
const probeResp = await this.makeRequest(endpoint, payload, undefined, quoteId, signal, undefined, extraHeaders);
|
|
2947
3113
|
return this.parsePaymentRequired(probeResp.headers.get('payment-required'));
|
|
2948
3114
|
}
|
|
2949
3115
|
async signPaymentAuthorization(paymentInfo, accepted, resource, extensions) {
|