@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/dist/index.js
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;
|