@ctxprotocol/sdk 0.8.3 → 0.8.5
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 +26 -3
- package/dist/client/index.cjs +207 -45
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +218 -8
- package/dist/client/index.d.ts +218 -8
- package/dist/client/index.js +207 -45
- package/dist/client/index.js.map +1 -1
- package/dist/index.cjs +207 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +207 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,18 +96,22 @@ const result = await client.tools.execute({
|
|
|
96
96
|
console.log(result.session); // methodPrice, spent, remaining, maxSpend, ...
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
**Query mode** gives you curated answers — the server
|
|
99
|
+
**Query mode** gives you curated answers — the server runs a discovery-first planner contract (`discover/probe -> plan-from-evidence -> execute -> bounded fallback`) with model-aware context budgeting and AI synthesis for one flat fee:
|
|
100
100
|
```typescript
|
|
101
101
|
const answer = await client.query.run({
|
|
102
102
|
query: "What are the top whale movements on Base?",
|
|
103
103
|
modelId: "glm-model", // optional: choose a supported model
|
|
104
|
-
queryDepth: "
|
|
104
|
+
queryDepth: "deep", // optional: fast | auto | deep
|
|
105
105
|
includeDataUrl: true, // optional: persist full execution data to blob
|
|
106
|
+
includeDeveloperTrace: true, // optional: include machine-readable runtime trace
|
|
106
107
|
});
|
|
107
108
|
console.log(answer.response); // AI-synthesized answer
|
|
108
109
|
console.log(answer.toolsUsed); // Which tools were used
|
|
109
110
|
console.log(answer.cost); // Cost breakdown
|
|
110
111
|
console.log(answer.dataUrl); // Optional blob URL with full data
|
|
112
|
+
console.log(answer.developerTrace?.summary); // retries/fallbacks/loops summary
|
|
113
|
+
console.log(answer.developerTrace?.diagnostics?.selection); // lane + scout probe diagnostics
|
|
114
|
+
console.log(answer.orchestrationMetrics); // high-level first-pass / rediscovery metrics
|
|
111
115
|
```
|
|
112
116
|
|
|
113
117
|
> Mixed listings are first-class: one listing can expose methods to both surfaces. Methods without `_meta.pricing.executeUsd` remain query-only until priced.
|
|
@@ -398,13 +402,18 @@ const closed = await client.tools.closeSession("sess_123");
|
|
|
398
402
|
|
|
399
403
|
#### `client.query.run(options)`
|
|
400
404
|
|
|
401
|
-
Run an agentic query. The server
|
|
405
|
+
Run an agentic query. The server applies discovery-first orchestration (`discover/probe -> plan-from-evidence -> execute -> bounded fallback`) with up to 100 MCP calls per response turn, then returns an AI-synthesized answer.
|
|
402
406
|
|
|
403
407
|
`queryDepth` controls orchestration depth:
|
|
404
408
|
- `fast`: lower-latency path for simple lookups.
|
|
405
409
|
- `auto`: server routes to either `fast` or `deep` from query intent + selected tool complexity.
|
|
406
410
|
- `deep`: completeness-oriented path (default when omitted).
|
|
407
411
|
|
|
412
|
+
`includeDeveloperTrace` and `orchestrationMetrics` expose optional rollout diagnostics.
|
|
413
|
+
`developerTrace.summary` reports aggregate retry/fallback counters, while
|
|
414
|
+
`developerTrace.diagnostics.selection` exposes runtime lane and scout probe signals
|
|
415
|
+
used by discovery-first planning.
|
|
416
|
+
|
|
408
417
|
```typescript
|
|
409
418
|
// Simple string
|
|
410
419
|
const answer = await client.query.run("What are the top whale movements on Base?");
|
|
@@ -417,6 +426,7 @@ const answer = await client.query.run({
|
|
|
417
426
|
queryDepth: "auto", // optional: fast | auto | deep
|
|
418
427
|
includeData: true, // optional: include execution data inline
|
|
419
428
|
includeDataUrl: true, // optional: include blob URL for full data
|
|
429
|
+
includeDeveloperTrace: true, // optional: include Developer Mode trace
|
|
420
430
|
});
|
|
421
431
|
|
|
422
432
|
console.log(answer.response); // AI-synthesized text
|
|
@@ -425,6 +435,9 @@ console.log(answer.cost); // { modelCostUsd, toolCostUsd, totalCostUsd }
|
|
|
425
435
|
console.log(answer.durationMs); // Total time
|
|
426
436
|
console.log(answer.data); // Optional execution data (when includeData=true)
|
|
427
437
|
console.log(answer.dataUrl); // Optional blob URL (when includeDataUrl=true)
|
|
438
|
+
console.log(answer.developerTrace?.summary); // Optional trace summary (retries/fallbacks/loops)
|
|
439
|
+
console.log(answer.developerTrace?.diagnostics?.selection?.deepMode); // Optional deep lane trace
|
|
440
|
+
console.log(answer.orchestrationMetrics); // Optional high-level first-pass metrics
|
|
428
441
|
```
|
|
429
442
|
|
|
430
443
|
When retrieval-first synthesis rollout is enabled server-side, full-data or truncation-sensitive query requests can switch to retrieval-first context assembly using private stage artifacts and canonical execution data slices. `includeData` and `includeDataUrl` continue to reference the same canonical dataset used for synthesis.
|
|
@@ -433,6 +446,12 @@ When retrieval-first synthesis rollout is enabled server-side, full-data or trun
|
|
|
433
446
|
|
|
434
447
|
Same as `run()` but streams events in real-time via SSE.
|
|
435
448
|
|
|
449
|
+
Event types:
|
|
450
|
+
- `tool-status`
|
|
451
|
+
- `text-delta`
|
|
452
|
+
- `developer-trace` (when `includeDeveloperTrace=true`)
|
|
453
|
+
- `done`
|
|
454
|
+
|
|
436
455
|
```typescript
|
|
437
456
|
for await (const event of client.query.stream({
|
|
438
457
|
query: "What are the top whale movements?",
|
|
@@ -472,6 +491,8 @@ import type {
|
|
|
472
491
|
// Query types (pay-per-response)
|
|
473
492
|
QueryOptions,
|
|
474
493
|
QueryResult,
|
|
494
|
+
QueryDeveloperTrace,
|
|
495
|
+
QueryOrchestrationMetrics,
|
|
475
496
|
QueryCost,
|
|
476
497
|
QueryStreamEvent,
|
|
477
498
|
ContextErrorCode,
|
|
@@ -549,6 +570,8 @@ interface QueryResult {
|
|
|
549
570
|
durationMs: number;
|
|
550
571
|
data?: unknown; // Optional execution data (includeData=true)
|
|
551
572
|
dataUrl?: string; // Optional blob URL (includeDataUrl=true)
|
|
573
|
+
developerTrace?: QueryDeveloperTrace; // Optional runtime trace + diagnostics
|
|
574
|
+
orchestrationMetrics?: QueryOrchestrationMetrics; // Optional first-pass outcome metrics
|
|
552
575
|
}
|
|
553
576
|
```
|
|
554
577
|
|
package/dist/client/index.cjs
CHANGED
|
@@ -228,11 +228,119 @@ var Query = class {
|
|
|
228
228
|
constructor(client) {
|
|
229
229
|
this.client = client;
|
|
230
230
|
}
|
|
231
|
+
buildSyntheticTraceFromRunResult(params) {
|
|
232
|
+
const timeline = params.toolsUsed.map((tool, index) => ({
|
|
233
|
+
stepType: "tool-call",
|
|
234
|
+
event: "tool-call",
|
|
235
|
+
status: "success",
|
|
236
|
+
timestampMs: index,
|
|
237
|
+
tool: {
|
|
238
|
+
id: tool.id,
|
|
239
|
+
name: tool.name
|
|
240
|
+
},
|
|
241
|
+
metadata: {
|
|
242
|
+
skillCalls: tool.skillCalls,
|
|
243
|
+
synthetic: true
|
|
244
|
+
}
|
|
245
|
+
}));
|
|
246
|
+
const toolCalls = params.toolsUsed.reduce(
|
|
247
|
+
(sum, tool) => sum + Math.max(tool.skillCalls, 0),
|
|
248
|
+
0
|
|
249
|
+
);
|
|
250
|
+
return {
|
|
251
|
+
summary: {
|
|
252
|
+
toolCalls,
|
|
253
|
+
retryCount: 0,
|
|
254
|
+
selfHealCount: 0,
|
|
255
|
+
fallbackCount: 0,
|
|
256
|
+
failureCount: 0,
|
|
257
|
+
recoveryCount: 0,
|
|
258
|
+
completionChecks: 0,
|
|
259
|
+
loopCount: 0
|
|
260
|
+
},
|
|
261
|
+
timeline,
|
|
262
|
+
source: "sdk-fallback",
|
|
263
|
+
synthetic: true,
|
|
264
|
+
reason: "backend_trace_missing",
|
|
265
|
+
durationMs: params.durationMs
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
buildSyntheticTraceFromStreamStatus(params) {
|
|
269
|
+
const timeline = params.statusTimeline.map((entry, index) => ({
|
|
270
|
+
stepType: "tool-status",
|
|
271
|
+
event: "tool-status",
|
|
272
|
+
status: entry.status,
|
|
273
|
+
timestampMs: index,
|
|
274
|
+
tool: entry.tool.name || entry.tool.id ? {
|
|
275
|
+
id: entry.tool.id || void 0,
|
|
276
|
+
name: entry.tool.name || void 0
|
|
277
|
+
} : void 0,
|
|
278
|
+
metadata: { synthetic: true }
|
|
279
|
+
}));
|
|
280
|
+
const toolCallsFromUsage = params.toolsUsed.reduce(
|
|
281
|
+
(sum, tool) => sum + Math.max(tool.skillCalls, 0),
|
|
282
|
+
0
|
|
283
|
+
);
|
|
284
|
+
const toolCallsFromStatus = params.statusTimeline.filter(
|
|
285
|
+
(entry) => entry.status === "tool-complete"
|
|
286
|
+
).length;
|
|
287
|
+
const toolCalls = toolCallsFromUsage > 0 ? toolCallsFromUsage : toolCallsFromStatus;
|
|
288
|
+
const retryCount = params.statusTimeline.filter(
|
|
289
|
+
(entry) => /(retry|fix|reflect|recover)/i.test(entry.status)
|
|
290
|
+
).length;
|
|
291
|
+
const completionChecks = params.statusTimeline.filter(
|
|
292
|
+
(entry) => /complet/i.test(entry.status)
|
|
293
|
+
).length;
|
|
294
|
+
return {
|
|
295
|
+
summary: {
|
|
296
|
+
toolCalls,
|
|
297
|
+
retryCount,
|
|
298
|
+
selfHealCount: retryCount,
|
|
299
|
+
fallbackCount: 0,
|
|
300
|
+
failureCount: 0,
|
|
301
|
+
recoveryCount: 0,
|
|
302
|
+
completionChecks,
|
|
303
|
+
loopCount: retryCount
|
|
304
|
+
},
|
|
305
|
+
timeline,
|
|
306
|
+
source: "sdk-fallback",
|
|
307
|
+
synthetic: true,
|
|
308
|
+
reason: "backend_trace_missing",
|
|
309
|
+
durationMs: params.durationMs
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
mergeDeveloperTrace(first, second) {
|
|
313
|
+
if (!first) return second;
|
|
314
|
+
if (!second) return first;
|
|
315
|
+
const firstTimeline = Array.isArray(first.timeline) ? first.timeline : [];
|
|
316
|
+
const secondTimeline = Array.isArray(second.timeline) ? second.timeline : [];
|
|
317
|
+
const mergedTimeline = [...firstTimeline, ...secondTimeline];
|
|
318
|
+
return {
|
|
319
|
+
...first,
|
|
320
|
+
...second,
|
|
321
|
+
summary: {
|
|
322
|
+
...typeof first.summary === "object" && first.summary ? first.summary : {},
|
|
323
|
+
...typeof second.summary === "object" && second.summary ? second.summary : {}
|
|
324
|
+
},
|
|
325
|
+
...mergedTimeline.length > 0 ? { timeline: mergedTimeline } : {}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
parseStreamEvent(rawData) {
|
|
329
|
+
const parsed = JSON.parse(rawData);
|
|
330
|
+
if (!parsed || typeof parsed !== "object") {
|
|
331
|
+
return void 0;
|
|
332
|
+
}
|
|
333
|
+
const event = parsed;
|
|
334
|
+
if (typeof event.type !== "string") {
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
return event;
|
|
338
|
+
}
|
|
231
339
|
/**
|
|
232
340
|
* Run an agentic query and wait for the full response.
|
|
233
341
|
*
|
|
234
342
|
* The server discovers relevant tools (or uses the ones you specify),
|
|
235
|
-
* executes the
|
|
343
|
+
* executes the discovery-first pipeline (up to 100 MCP calls per tool),
|
|
236
344
|
* and returns an AI-synthesized answer. Payment is settled after
|
|
237
345
|
* successful execution via deferred settlement.
|
|
238
346
|
*
|
|
@@ -261,42 +369,25 @@ var Query = class {
|
|
|
261
369
|
*/
|
|
262
370
|
async run(options) {
|
|
263
371
|
const opts = typeof options === "string" ? { query: options } : options;
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
"
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
stream: false
|
|
278
|
-
})
|
|
372
|
+
let terminalError;
|
|
373
|
+
for await (const event of this.stream(opts)) {
|
|
374
|
+
if (event.type === "error") {
|
|
375
|
+
terminalError = {
|
|
376
|
+
error: event.error,
|
|
377
|
+
...event.code ? { code: event.code } : {},
|
|
378
|
+
...event.scope ? { scope: event.scope } : {},
|
|
379
|
+
...event.reasonCode ? { reasonCode: event.reasonCode } : {}
|
|
380
|
+
};
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (event.type === "done") {
|
|
384
|
+
return event.result;
|
|
279
385
|
}
|
|
280
|
-
);
|
|
281
|
-
if ("error" in response) {
|
|
282
|
-
throw new ContextError(
|
|
283
|
-
response.error,
|
|
284
|
-
response.code,
|
|
285
|
-
void 0,
|
|
286
|
-
response.helpUrl
|
|
287
|
-
);
|
|
288
386
|
}
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
response: response.response,
|
|
292
|
-
toolsUsed: response.toolsUsed,
|
|
293
|
-
cost: response.cost,
|
|
294
|
-
durationMs: response.durationMs,
|
|
295
|
-
data: response.data,
|
|
296
|
-
dataUrl: response.dataUrl
|
|
297
|
-
};
|
|
387
|
+
if (terminalError) {
|
|
388
|
+
throw new ContextError(terminalError.error, terminalError.code);
|
|
298
389
|
}
|
|
299
|
-
throw new ContextError("
|
|
390
|
+
throw new ContextError("Streaming query ended before done event");
|
|
300
391
|
}
|
|
301
392
|
/**
|
|
302
393
|
* Run an agentic query with streaming. Returns an async iterable that
|
|
@@ -305,6 +396,8 @@ var Query = class {
|
|
|
305
396
|
* Event types:
|
|
306
397
|
* - `tool-status` — A tool started executing or changed status
|
|
307
398
|
* - `text-delta` — A chunk of the AI response text
|
|
399
|
+
* - `developer-trace` — Runtime trace metadata (when includeDeveloperTrace=true)
|
|
400
|
+
* - `error` — A structured query/runtime error emitted before stream completion
|
|
308
401
|
* - `done` — The full response is complete (includes final `QueryResult`)
|
|
309
402
|
*
|
|
310
403
|
* @param options - Query options or a plain string question
|
|
@@ -320,9 +413,15 @@ var Query = class {
|
|
|
320
413
|
* case "text-delta":
|
|
321
414
|
* process.stdout.write(event.delta);
|
|
322
415
|
* break;
|
|
416
|
+
* case "developer-trace":
|
|
417
|
+
* console.log("Trace summary:", event.trace.summary);
|
|
418
|
+
* break;
|
|
323
419
|
* case "done":
|
|
324
420
|
* console.log("\nCost:", event.result.cost.totalCostUsd);
|
|
325
421
|
* break;
|
|
422
|
+
* case "error":
|
|
423
|
+
* console.error("Stream error:", event.error);
|
|
424
|
+
* break;
|
|
326
425
|
* }
|
|
327
426
|
* }
|
|
328
427
|
* ```
|
|
@@ -339,7 +438,9 @@ var Query = class {
|
|
|
339
438
|
modelId: opts.modelId,
|
|
340
439
|
includeData: opts.includeData,
|
|
341
440
|
includeDataUrl: opts.includeDataUrl,
|
|
441
|
+
includeDeveloperTrace: opts.includeDeveloperTrace,
|
|
342
442
|
queryDepth: opts.queryDepth,
|
|
443
|
+
debugScoutDeepMode: opts.debugScoutDeepMode,
|
|
343
444
|
stream: true
|
|
344
445
|
})
|
|
345
446
|
});
|
|
@@ -350,6 +451,48 @@ var Query = class {
|
|
|
350
451
|
const reader = body.getReader();
|
|
351
452
|
const decoder = new TextDecoder();
|
|
352
453
|
let buffer = "";
|
|
454
|
+
let aggregatedTrace;
|
|
455
|
+
const statusTimeline = [];
|
|
456
|
+
const parseAndHydrateEvent = (rawData) => {
|
|
457
|
+
const event = this.parseStreamEvent(rawData);
|
|
458
|
+
if (!event) {
|
|
459
|
+
return void 0;
|
|
460
|
+
}
|
|
461
|
+
if (event.type === "developer-trace") {
|
|
462
|
+
aggregatedTrace = this.mergeDeveloperTrace(aggregatedTrace, event.trace);
|
|
463
|
+
return event;
|
|
464
|
+
}
|
|
465
|
+
if (event.type === "tool-status") {
|
|
466
|
+
statusTimeline.push({
|
|
467
|
+
status: event.status,
|
|
468
|
+
tool: {
|
|
469
|
+
id: event.tool.id,
|
|
470
|
+
name: event.tool.name
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
return event;
|
|
474
|
+
}
|
|
475
|
+
if (event.type === "done") {
|
|
476
|
+
let mergedTrace = this.mergeDeveloperTrace(
|
|
477
|
+
aggregatedTrace,
|
|
478
|
+
event.result.developerTrace
|
|
479
|
+
);
|
|
480
|
+
if (!mergedTrace && opts.includeDeveloperTrace) {
|
|
481
|
+
mergedTrace = statusTimeline.length > 0 ? this.buildSyntheticTraceFromStreamStatus({
|
|
482
|
+
statusTimeline,
|
|
483
|
+
toolsUsed: event.result.toolsUsed,
|
|
484
|
+
durationMs: event.result.durationMs
|
|
485
|
+
}) : this.buildSyntheticTraceFromRunResult({
|
|
486
|
+
toolsUsed: event.result.toolsUsed,
|
|
487
|
+
durationMs: event.result.durationMs
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
if (mergedTrace) {
|
|
491
|
+
event.result.developerTrace = mergedTrace;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return event;
|
|
495
|
+
};
|
|
353
496
|
try {
|
|
354
497
|
while (true) {
|
|
355
498
|
const { done, value } = await reader.read();
|
|
@@ -363,7 +506,10 @@ var Query = class {
|
|
|
363
506
|
const data = trimmed.slice(6);
|
|
364
507
|
if (data === "[DONE]") return;
|
|
365
508
|
try {
|
|
366
|
-
|
|
509
|
+
const event = parseAndHydrateEvent(data);
|
|
510
|
+
if (event) {
|
|
511
|
+
yield event;
|
|
512
|
+
}
|
|
367
513
|
} catch {
|
|
368
514
|
}
|
|
369
515
|
}
|
|
@@ -373,7 +519,10 @@ var Query = class {
|
|
|
373
519
|
const data = buffer.trim().slice(6);
|
|
374
520
|
if (data !== "[DONE]") {
|
|
375
521
|
try {
|
|
376
|
-
|
|
522
|
+
const event = parseAndHydrateEvent(data);
|
|
523
|
+
if (event) {
|
|
524
|
+
yield event;
|
|
525
|
+
}
|
|
377
526
|
} catch {
|
|
378
527
|
}
|
|
379
528
|
}
|
|
@@ -452,30 +601,34 @@ var ContextClient = class {
|
|
|
452
601
|
*
|
|
453
602
|
* @internal
|
|
454
603
|
*/
|
|
455
|
-
async _fetch(endpoint, options = {}) {
|
|
604
|
+
async _fetch(endpoint, options = {}, fetchOptions) {
|
|
456
605
|
if (this._closed) {
|
|
457
606
|
throw new ContextError("Client has been closed");
|
|
458
607
|
}
|
|
459
608
|
const url = `${this.baseUrl}${endpoint}`;
|
|
460
609
|
const maxRetries = 3;
|
|
461
610
|
const timeoutMs = this.requestTimeoutMs;
|
|
611
|
+
const method = (options.method ?? "GET").toUpperCase();
|
|
612
|
+
const requestHeaders = new Headers(options.headers);
|
|
613
|
+
const canRetryRequest = fetchOptions?.retry === false ? false : method === "GET" || method === "HEAD" || method === "OPTIONS" || requestHeaders.has("Idempotency-Key");
|
|
462
614
|
let lastError;
|
|
463
615
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
464
616
|
const controller = new AbortController();
|
|
465
617
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
618
|
+
const mergedHeaders = new Headers(requestHeaders);
|
|
619
|
+
if (!mergedHeaders.has("Content-Type")) {
|
|
620
|
+
mergedHeaders.set("Content-Type", "application/json");
|
|
621
|
+
}
|
|
622
|
+
mergedHeaders.set("Authorization", `Bearer ${this.apiKey}`);
|
|
466
623
|
try {
|
|
467
624
|
const response = await fetch(url, {
|
|
468
625
|
...options,
|
|
469
626
|
signal: controller.signal,
|
|
470
|
-
headers:
|
|
471
|
-
"Content-Type": "application/json",
|
|
472
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
473
|
-
...options.headers
|
|
474
|
-
}
|
|
627
|
+
headers: mergedHeaders
|
|
475
628
|
});
|
|
476
629
|
clearTimeout(timeout);
|
|
477
630
|
if (!response.ok) {
|
|
478
|
-
if (response.status >= 500 && attempt < maxRetries) {
|
|
631
|
+
if (response.status >= 500 && canRetryRequest && attempt < maxRetries) {
|
|
479
632
|
const delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
480
633
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
481
634
|
continue;
|
|
@@ -494,7 +647,16 @@ var ContextClient = class {
|
|
|
494
647
|
}
|
|
495
648
|
throw new ContextError(errorMessage, errorCode, response.status, helpUrl);
|
|
496
649
|
}
|
|
497
|
-
|
|
650
|
+
try {
|
|
651
|
+
return await response.json();
|
|
652
|
+
} catch (error) {
|
|
653
|
+
const parseError = error instanceof Error ? error : new Error(String(error));
|
|
654
|
+
throw new ContextError(
|
|
655
|
+
`Failed to parse JSON response: ${parseError.message}`,
|
|
656
|
+
void 0,
|
|
657
|
+
response.status
|
|
658
|
+
);
|
|
659
|
+
}
|
|
498
660
|
} catch (error) {
|
|
499
661
|
clearTimeout(timeout);
|
|
500
662
|
if (error instanceof ContextError) {
|
|
@@ -502,7 +664,7 @@ var ContextClient = class {
|
|
|
502
664
|
}
|
|
503
665
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
504
666
|
const isRetryable = lastError.name === "AbortError" || lastError.message.includes("fetch failed") || lastError.message.includes("ECONNRESET") || lastError.message.includes("ETIMEDOUT");
|
|
505
|
-
if (isRetryable && attempt < maxRetries) {
|
|
667
|
+
if (isRetryable && canRetryRequest && attempt < maxRetries) {
|
|
506
668
|
const delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
507
669
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
508
670
|
continue;
|