@oneshot-agent/sdk 0.50.0 → 0.52.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 +24 -22
- package/dist/approvals.d.ts +1 -1
- package/dist/approvals.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +47 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +139 -39
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +130 -83
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ const approvals_1 = require("./approvals");
|
|
|
65
65
|
// stay off this public surface (see tests/unit/sdk-provider-cost-removed.test.ts).
|
|
66
66
|
// Import from the `@oneshot-agent/sdk/receipt` subpath instead.
|
|
67
67
|
// Keep in sync with package.json `version`. Guarded by version.test.ts.
|
|
68
|
-
const SDK_VERSION = '0.
|
|
68
|
+
const SDK_VERSION = '0.52.0';
|
|
69
69
|
/** HTTP poll cadence while push is unconfirmed: fast first checks, settling at 2s. */
|
|
70
70
|
const HTTP_POLL_BACKOFF_MS = [300, 600, 1000, 2000];
|
|
71
71
|
/** HTTP poll cadence once the WebSocket has delivered for this request. */
|
|
@@ -145,6 +145,8 @@ function validateBudgetConfig(budgets) {
|
|
|
145
145
|
fraction(budgets.pauseAt, 'pauseAt');
|
|
146
146
|
return budgets;
|
|
147
147
|
}
|
|
148
|
+
/** The keys executeToolRequestImpl takes out of a request as SDK options, so they never reach the API body. */
|
|
149
|
+
const PAID_ROUTE_OPTION_KEYS = ['timeout', 'totalTimeoutMs', 'onRequestCreated', 'onAccepted', 'signal', 'onStatusUpdate', 'wait', 'waitForPhones', 'phoneTimeoutSec', 'idempotencyKey', 'approvalId', 'maxCost'];
|
|
148
150
|
class OneShot {
|
|
149
151
|
/**
|
|
150
152
|
* Async factory — required for CDP wallets (account creation is async).
|
|
@@ -455,48 +457,48 @@ class OneShot {
|
|
|
455
457
|
this.validate(options.topic, 'topic');
|
|
456
458
|
return this.tool('research', { ...options });
|
|
457
459
|
}
|
|
458
|
-
async
|
|
459
|
-
return this.tool('
|
|
460
|
+
async personFind(options) {
|
|
461
|
+
return this.tool('find/person', { ...options, limit: options.limit ?? 100 });
|
|
460
462
|
}
|
|
461
|
-
async
|
|
463
|
+
async personProfile(options) {
|
|
462
464
|
if (!options.linkedin_url && !options.email && !options.name) {
|
|
463
465
|
throw new errors_2.ValidationError('At least one of linkedin_url, email, or name is required', 'identifier');
|
|
464
466
|
}
|
|
465
|
-
return this.tool('
|
|
467
|
+
return this.tool('lookup/person-profile', { ...options });
|
|
466
468
|
}
|
|
467
|
-
async
|
|
468
|
-
return this.tool('
|
|
469
|
+
async companyFind(options = {}) {
|
|
470
|
+
return this.tool('find/company', { ...options, limit: options.limit ?? 10 });
|
|
469
471
|
}
|
|
470
|
-
async
|
|
472
|
+
async companyProfile(options) {
|
|
471
473
|
if (!options.domain && !options.name && !options.linkedin_url && !options.ticker) {
|
|
472
474
|
throw new errors_2.ValidationError('At least one of domain, name, linkedin_url, or ticker is required', 'identifier');
|
|
473
475
|
}
|
|
474
|
-
return this.tool('
|
|
476
|
+
return this.tool('lookup/company-profile', { ...options });
|
|
475
477
|
}
|
|
476
478
|
/**
|
|
477
479
|
* Discover local businesses (restaurants, contractors, practices) by
|
|
478
480
|
* category/keywords × location. Flat price per search, not per row.
|
|
479
481
|
*/
|
|
480
|
-
async
|
|
482
|
+
async businessFind(options) {
|
|
481
483
|
if (!options.location || options.location.length === 0) {
|
|
482
484
|
throw new errors_2.ValidationError('location is required (e.g. ["Austin, TX"])', 'location');
|
|
483
485
|
}
|
|
484
486
|
if (!(options.category && options.category.length) && !(options.keywords && options.keywords.length)) {
|
|
485
487
|
throw new errors_2.ValidationError('At least one of category or keywords is required', 'category');
|
|
486
488
|
}
|
|
487
|
-
return this.tool('
|
|
489
|
+
return this.tool('find/business', { ...options, limit: options.limit ?? 100 });
|
|
488
490
|
}
|
|
489
491
|
/**
|
|
490
492
|
* Resolve a business name + one locating field to its domain, phone,
|
|
491
493
|
* category and operating status. A miss resolves with `found: false`
|
|
492
494
|
* (a completed job), never a rejection.
|
|
493
495
|
*/
|
|
494
|
-
async
|
|
496
|
+
async businessResolve(options) {
|
|
495
497
|
this.validate(options.name, 'name');
|
|
496
498
|
if (!options.address && !options.city && !options.postal_code && !options.phone) {
|
|
497
499
|
throw new errors_2.ValidationError('name plus at least one of address, city, postal_code, or phone is required', 'address');
|
|
498
500
|
}
|
|
499
|
-
return this.tool('
|
|
501
|
+
return this.tool('resolve/business', { ...options });
|
|
500
502
|
}
|
|
501
503
|
/**
|
|
502
504
|
* Federal contract opportunities (SAM.gov) by NAICS code — Sources Sought
|
|
@@ -505,7 +507,7 @@ class OneShot {
|
|
|
505
507
|
* `data_as_of` on the result is the snapshot behind the rows (SAM.gov's
|
|
506
508
|
* daily extract publish time, or "now" when the live API served).
|
|
507
509
|
*/
|
|
508
|
-
async
|
|
510
|
+
async recordSolicitationsFind(options) {
|
|
509
511
|
if (!options.naics || options.naics.length === 0) {
|
|
510
512
|
throw new errors_2.ValidationError('naics is required (one or more 6-digit codes, e.g. ["541511"])', 'naics');
|
|
511
513
|
}
|
|
@@ -513,53 +515,53 @@ class OneShot {
|
|
|
513
515
|
if (bad !== undefined) {
|
|
514
516
|
throw new errors_2.ValidationError(`NAICS codes are 6 digits (got ${JSON.stringify(bad)})`, 'naics');
|
|
515
517
|
}
|
|
516
|
-
return this.tool('
|
|
518
|
+
return this.tool('find/record-solicitations', {
|
|
517
519
|
...options,
|
|
518
520
|
notice_types: options.notice_types ?? ['r', 'p'],
|
|
519
521
|
limit: options.limit ?? 100,
|
|
520
522
|
});
|
|
521
523
|
}
|
|
522
|
-
async
|
|
524
|
+
async personEmail(options) {
|
|
523
525
|
this.validate(options.company_domain, 'company_domain');
|
|
524
526
|
if (!options.full_name && !(options.first_name && options.last_name)) {
|
|
525
527
|
throw new errors_2.ValidationError('Either full_name or both first_name and last_name required', 'name');
|
|
526
528
|
}
|
|
527
|
-
return this.tool('
|
|
529
|
+
return this.tool('lookup/person-email', { ...options });
|
|
528
530
|
}
|
|
529
|
-
async
|
|
531
|
+
async personEmailStatus(options) {
|
|
530
532
|
this.validate(options.email, 'email');
|
|
531
|
-
return this.tool('
|
|
533
|
+
return this.tool('lookup/person-email-status', { ...options });
|
|
532
534
|
}
|
|
533
|
-
async
|
|
535
|
+
async personDossier(options) {
|
|
534
536
|
if (!options.email && !options.social_media_url && !options.name) {
|
|
535
537
|
throw new errors_2.ValidationError('At least one of email, social_media_url, or name is required', 'identifier');
|
|
536
538
|
}
|
|
537
|
-
return this.tool('
|
|
539
|
+
return this.tool('lookup/person-dossier', { ...options });
|
|
538
540
|
}
|
|
539
|
-
async
|
|
541
|
+
async personSocialAccounts(options) {
|
|
540
542
|
if (!options.email && !options.social_media_url) {
|
|
541
543
|
throw new errors_2.ValidationError('At least one of email or social_media_url is required', 'identifier');
|
|
542
544
|
}
|
|
543
|
-
return this.tool('
|
|
545
|
+
return this.tool('lookup/person-social-accounts', { ...options });
|
|
544
546
|
}
|
|
545
|
-
async
|
|
547
|
+
async personArticles(options) {
|
|
546
548
|
this.validate(options.name, 'name');
|
|
547
549
|
this.validate(options.company, 'company');
|
|
548
|
-
return this.tool('
|
|
550
|
+
return this.tool('lookup/person-articles', { ...options });
|
|
549
551
|
}
|
|
550
|
-
async
|
|
552
|
+
async personPosts(options) {
|
|
551
553
|
this.validate(options.social_media_url, 'social_media_url');
|
|
552
|
-
return this.tool('
|
|
554
|
+
return this.tool('lookup/person-posts', { ...options });
|
|
553
555
|
}
|
|
554
556
|
async personInterests(options) {
|
|
555
557
|
if (!options.email && !options.phone && !options.social_media_url) {
|
|
556
558
|
throw new errors_2.ValidationError('At least one of email, phone, or social_media_url is required', 'identifier');
|
|
557
559
|
}
|
|
558
|
-
return this.tool('
|
|
560
|
+
return this.tool('lookup/person-interests', { ...options });
|
|
559
561
|
}
|
|
560
|
-
async
|
|
562
|
+
async personFollowers(options) {
|
|
561
563
|
this.validate(options.social_media_url, 'social_media_url');
|
|
562
|
-
return this.tool('
|
|
564
|
+
return this.tool('lookup/person-followers', { ...options });
|
|
563
565
|
}
|
|
564
566
|
async inboxList(options = {}) {
|
|
565
567
|
const qs = this.buildQuery({
|
|
@@ -1295,6 +1297,95 @@ class OneShot {
|
|
|
1295
1297
|
this.validate(callId, 'callId');
|
|
1296
1298
|
return this.signedJson('GET', `/v1/tools/voice/inbound/${encodeURIComponent(callId)}`, undefined, 'read', 'Failed to get inbound call');
|
|
1297
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Call a declared route of the API by method and path (#1580), the one generic call for a route that has no
|
|
1302
|
+
* typed method. Follows the Python client's `call_free_get/post/patch/put/delete`:
|
|
1303
|
+
*
|
|
1304
|
+
* - `path` is the route, with `{name}` (or `:name`) for a path parameter, filled from `params` and URL-encoded
|
|
1305
|
+
* (`/v1/compute/{goal_id}/budget`). It must start with `/v1/` and hold no `..` or `?`.
|
|
1306
|
+
* - `query` is the query string (null/undefined left out; booleans `true`/`false`; objects and arrays as JSON).
|
|
1307
|
+
* - `body` is the JSON body of a `POST`, `PUT` or `PATCH` (an object field is sent as it is); a `GET` or
|
|
1308
|
+
* `DELETE` sends none.
|
|
1309
|
+
* - A free route is authorised like the other free calls: a signed proof (read scope for `GET`, write scope for
|
|
1310
|
+
* any other method) or, in an access-token session, the bearer token. Returns the `data` field of
|
|
1311
|
+
* `{ success, data }`, or the whole body when there is none; an empty answer is `{ success: true }`.
|
|
1312
|
+
* - `paid: true` sends the route through the quote-to-pay flow of {@link OneShot.tool} (a `POST` only), with the
|
|
1313
|
+
* {@link ToolOptions} (`maxCost`, `idempotencyKey`, `wait`, `signal`, ...) given in `options`.
|
|
1314
|
+
*
|
|
1315
|
+
* @example
|
|
1316
|
+
* ```typescript
|
|
1317
|
+
* const budget = await agent.route('GET', '/v1/compute/{goal_id}/budget', { params: { goal_id: 'goal_01HX' } });
|
|
1318
|
+
* const changes = await agent.route('POST', '/v1/tools/changes', { paid: true, body: { attribute: 'vehicle_identification', key: '1HGCM82633A004352', since: '2026-01-01' } });
|
|
1319
|
+
* ```
|
|
1320
|
+
*/
|
|
1321
|
+
async route(method, path, options = {}) {
|
|
1322
|
+
const { params = {}, query = {}, body, paid = false, ...toolOptions } = options;
|
|
1323
|
+
if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method))
|
|
1324
|
+
throw new errors_2.ValidationError(`Not an HTTP method of the API: ${method}`, 'method');
|
|
1325
|
+
if (!path.startsWith('/v1/') || path.includes('..') || path.includes('?') || path.includes('#')) {
|
|
1326
|
+
throw new errors_2.ValidationError(`Not a route of the API: ${path}`, 'path');
|
|
1327
|
+
}
|
|
1328
|
+
const filled = path.replace(/\{([A-Za-z0-9_]+)\}|:([A-Za-z0-9_]+)/g, (_match, braced, colon) => {
|
|
1329
|
+
const name = braced ?? colon;
|
|
1330
|
+
const value = params[name];
|
|
1331
|
+
if ((typeof value !== 'string' && typeof value !== 'number') || String(value) === '') {
|
|
1332
|
+
throw new errors_2.ValidationError(`${name} is required to call ${method} ${path}`, name);
|
|
1333
|
+
}
|
|
1334
|
+
return encodeURIComponent(String(value));
|
|
1335
|
+
});
|
|
1336
|
+
const qs = new URLSearchParams();
|
|
1337
|
+
for (const [key, value] of Object.entries(query)) {
|
|
1338
|
+
if (value === undefined || value === null)
|
|
1339
|
+
continue;
|
|
1340
|
+
qs.set(key, typeof value === 'boolean' ? (value ? 'true' : 'false') : typeof value === 'object' ? JSON.stringify(value) : String(value));
|
|
1341
|
+
}
|
|
1342
|
+
const target = qs.size ? `${filled}?${qs.toString()}` : filled;
|
|
1343
|
+
if (paid) {
|
|
1344
|
+
if (method !== 'POST')
|
|
1345
|
+
throw new errors_2.ValidationError(`A paid route is called with POST, not ${method}`, 'method');
|
|
1346
|
+
// executeToolRequestImpl strips these keys out of the payload as SDK options, so a body field of the same
|
|
1347
|
+
// name would be dropped silently: refuse it, and tell the caller to send it as an option or rename it.
|
|
1348
|
+
for (const name of PAID_ROUTE_OPTION_KEYS) {
|
|
1349
|
+
if (body && name in body)
|
|
1350
|
+
throw new errors_2.ValidationError(`The body field "${name}" of ${path} is an option of the SDK call and would not reach the API`, name);
|
|
1351
|
+
}
|
|
1352
|
+
return this.executeToolRequest(target, { ...(body ?? {}), ...toolOptions });
|
|
1353
|
+
}
|
|
1354
|
+
const sendsBody = method !== 'GET' && method !== 'DELETE';
|
|
1355
|
+
const scope = (0, deadline_1.deadlineScope)(toolOptions.signal, filled.startsWith('/v1/tools/browser/profiles') ? 150000 : 30000);
|
|
1356
|
+
try {
|
|
1357
|
+
const request = (async () => {
|
|
1358
|
+
const response = await fetch(`${this.baseUrl}${target}`, {
|
|
1359
|
+
method,
|
|
1360
|
+
headers: { ...(sendsBody ? { 'Content-Type': 'application/json' } : {}), ...(await this.signedReadHeaders(method === 'GET' ? 'read' : 'write')) },
|
|
1361
|
+
...(sendsBody ? { body: JSON.stringify(body ?? {}) } : {}),
|
|
1362
|
+
signal: scope.signal,
|
|
1363
|
+
});
|
|
1364
|
+
if (!response.ok) {
|
|
1365
|
+
if (response.status === 409 && filled.startsWith('/v1/tools/linkedin/')) {
|
|
1366
|
+
const text = await response.clone().text();
|
|
1367
|
+
let code;
|
|
1368
|
+
try {
|
|
1369
|
+
code = JSON.parse(text)?.error;
|
|
1370
|
+
}
|
|
1371
|
+
catch { /* Not JSON. */ }
|
|
1372
|
+
if (code === 'connect_required')
|
|
1373
|
+
throw new errors_1.LinkedInConnectRequiredError(text);
|
|
1374
|
+
}
|
|
1375
|
+
await this.failFromResponse(`${method} ${filled} failed`, response);
|
|
1376
|
+
}
|
|
1377
|
+
const text = await response.text();
|
|
1378
|
+
if (response.status === 204 || !text)
|
|
1379
|
+
return { success: true };
|
|
1380
|
+
const json = JSON.parse(text);
|
|
1381
|
+
return (json && typeof json === 'object' && 'data' in json ? json.data : json);
|
|
1382
|
+
})();
|
|
1383
|
+
return await (0, deadline_1.abortable)(request, scope.signal);
|
|
1384
|
+
}
|
|
1385
|
+
finally {
|
|
1386
|
+
scope.close();
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1298
1389
|
/** Signed free request returning the `data` field of `{ success, data }`. */
|
|
1299
1390
|
async signedJson(method, path, body, scope, failure) {
|
|
1300
1391
|
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
@@ -1701,11 +1792,17 @@ class OneShot {
|
|
|
1701
1792
|
return json.data;
|
|
1702
1793
|
}
|
|
1703
1794
|
/**
|
|
1704
|
-
* Top up budget for a recurring compute goal
|
|
1795
|
+
* Top up budget for a recurring compute goal, or for a goal (one-off or recurring) that
|
|
1796
|
+
* ended because it ran out of budget (`end_reason: 'budget_exhausted'`).
|
|
1705
1797
|
*
|
|
1706
1798
|
* Uses the quote-then-pay flow: the first call gets a 402 with the top-up price,
|
|
1707
1799
|
* the second call (with payment) credits the budget. The amount IS the price.
|
|
1708
1800
|
*
|
|
1801
|
+
* Funding a goal that ran out of budget sets it back to `active` and wakes it; it keeps its
|
|
1802
|
+
* id, tasks, workspace, outputs, done-check and deadline. A goal that ended any other way, or
|
|
1803
|
+
* whose deadline has passed, is refused (`invalid_state` / `deadline_passed`). While the first
|
|
1804
|
+
* ending's refund is still settling the call fails with a retryable `refund_pending` (409).
|
|
1805
|
+
*
|
|
1709
1806
|
* @example
|
|
1710
1807
|
* ```typescript
|
|
1711
1808
|
* const result = await agent.fundComputeGoal('goal_01HX...', 10.00);
|
|
@@ -1900,7 +1997,7 @@ class OneShot {
|
|
|
1900
1997
|
* await agent.tagReceiptValue('rcpt_01HX...', { type: 'revenue', amount: 5.00, label: 'Sale from lead' });
|
|
1901
1998
|
*
|
|
1902
1999
|
* // By the request_id you already kept from the call:
|
|
1903
|
-
* const r = await agent.
|
|
2000
|
+
* const r = await agent.personEmailStatus({ email: 'x@y.com' });
|
|
1904
2001
|
* await agent.tagReceiptValue({ requestId: r.request_id }, { type: 'lead', amount: 1 });
|
|
1905
2002
|
*
|
|
1906
2003
|
* // By cadence correlation key — one call attributes a closed deal to the whole sequence:
|
|
@@ -2484,7 +2581,7 @@ class OneShot {
|
|
|
2484
2581
|
}
|
|
2485
2582
|
async executeToolRequest(endpoint, options, quoteId) {
|
|
2486
2583
|
// LinkedIn writes act through a human's account and cannot be recalled: always keyed.
|
|
2487
|
-
const reliable = /(?:^|\/)(
|
|
2584
|
+
const reliable = /(?:^|\/)(lookup\/person-(profile|email|email-status)|lookup\/place-permits|linkedin\/(reply|message|profile-view|react|invite(?:\/withdraw)?))$/.test(endpoint);
|
|
2488
2585
|
const key = options.idempotencyKey ?? (reliable ? ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(16)) : undefined);
|
|
2489
2586
|
if (options.totalTimeoutMs !== undefined && (!Number.isFinite(options.totalTimeoutMs) || options.totalTimeoutMs <= 0)) {
|
|
2490
2587
|
throw new errors_2.ValidationError('totalTimeoutMs must be positive', 'totalTimeoutMs');
|
|
@@ -2511,7 +2608,7 @@ class OneShot {
|
|
|
2511
2608
|
}
|
|
2512
2609
|
}
|
|
2513
2610
|
async executeToolRequestImpl(endpoint, options, quoteId, context = { phase: "initialization" }) {
|
|
2514
|
-
const { totalTimeoutMs, onRequestCreated, onAccepted, signal, onStatusUpdate, wait = true, waitForPhones, phoneTimeoutSec, idempotencyKey, approvalId, maxCost, ...payload } = options;
|
|
2611
|
+
const { timeout, totalTimeoutMs, onRequestCreated, onAccepted, signal, onStatusUpdate, wait = true, waitForPhones, phoneTimeoutSec, idempotencyKey, approvalId, maxCost, ...payload } = options;
|
|
2515
2612
|
const extraHeaders = {
|
|
2516
2613
|
...this.maxCostHeader(maxCost),
|
|
2517
2614
|
...this.idempotencyHeader(idempotencyKey),
|
|
@@ -2562,6 +2659,7 @@ class OneShot {
|
|
|
2562
2659
|
const { accepted, resource, extensions } = this.parsePaymentRequired(paymentRequiredHeader);
|
|
2563
2660
|
// Fallback: parse legacy body format for amount display
|
|
2564
2661
|
const data = await response.json();
|
|
2662
|
+
quoteId ?? (quoteId = data.context?.quote_id);
|
|
2565
2663
|
if (this._accessToken) {
|
|
2566
2664
|
// Credits-only session: never sign. Let the server settle it.
|
|
2567
2665
|
this.checkAbortBeforePayment(signal);
|
|
@@ -2609,7 +2707,7 @@ class OneShot {
|
|
|
2609
2707
|
if (!wait) {
|
|
2610
2708
|
return { ...result, idempotencyKey };
|
|
2611
2709
|
}
|
|
2612
|
-
const completed = await this.pollJob(result.request_id,
|
|
2710
|
+
const completed = await this.pollJob(result.request_id, timeout, signal, onStatusUpdate, waitForPhones ? { waitForPhones, phoneTimeoutSec } : undefined);
|
|
2613
2711
|
if (completed && typeof completed === 'object' && !Array.isArray(completed)) {
|
|
2614
2712
|
return { ...completed, request_id: context.requestId, receipt_id: completed.receipt_id ?? context.receiptId, idempotencyKey };
|
|
2615
2713
|
}
|
|
@@ -2738,8 +2836,7 @@ class OneShot {
|
|
|
2738
2836
|
* has a pending async webhook and the webhook URL is configured; the webhook
|
|
2739
2837
|
* handler flips it to false (or removes it) once phones arrive.
|
|
2740
2838
|
*
|
|
2741
|
-
*
|
|
2742
|
-
* deep_research_person wrapper `{result: {phones_pending}}`.
|
|
2839
|
+
* Accepts legacy flat results and person_dossier's result.profile.data.
|
|
2743
2840
|
*/
|
|
2744
2841
|
_isPhonesPending(result) {
|
|
2745
2842
|
if (!result || typeof result !== 'object')
|
|
@@ -2750,6 +2847,9 @@ class OneShot {
|
|
|
2750
2847
|
const inner = r.result;
|
|
2751
2848
|
if (inner && typeof inner === 'object' && inner.phones_pending === true)
|
|
2752
2849
|
return true;
|
|
2850
|
+
const profile = inner?.profile;
|
|
2851
|
+
if (profile?.data?.phones_pending === true)
|
|
2852
|
+
return true;
|
|
2753
2853
|
return false;
|
|
2754
2854
|
}
|
|
2755
2855
|
/**
|
|
@@ -3075,7 +3175,7 @@ class OneShot {
|
|
|
3075
3175
|
// alone (401 proof_required).
|
|
3076
3176
|
const unsignedStub = payment?.payload?.signature === '0x';
|
|
3077
3177
|
const askedForProof = !!extraHeaders?.['x-agent-proof'];
|
|
3078
|
-
const proofScope = /(
|
|
3178
|
+
const proofScope = /(lookup\/person-(profile|email|email-status)|lookup\/place-permits|physical-mail\/send)$/.test(endpoint)
|
|
3079
3179
|
? 'submit'
|
|
3080
3180
|
: askedForProof ? 'read' : 'write';
|
|
3081
3181
|
const headers = {
|
|
@@ -3260,7 +3360,7 @@ class OneShot {
|
|
|
3260
3360
|
}
|
|
3261
3361
|
catch (err) {
|
|
3262
3362
|
// Keyed writes may already be queued server-side after a transport failure: keep the reservation.
|
|
3263
|
-
if (!/(
|
|
3363
|
+
if (!/(lookup\/person-(profile|email|email-status)|lookup\/place-permits|physical-mail\/send|linkedin\/(reply|message|profile-view|react|invite(?:\/withdraw)?))$/.test(endpoint))
|
|
3264
3364
|
this.releaseUsdcReservation(signed.reservation);
|
|
3265
3365
|
throw err;
|
|
3266
3366
|
}
|