@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.cjs
CHANGED
|
@@ -230,11 +230,119 @@ var Query = class {
|
|
|
230
230
|
constructor(client) {
|
|
231
231
|
this.client = client;
|
|
232
232
|
}
|
|
233
|
+
buildSyntheticTraceFromRunResult(params) {
|
|
234
|
+
const timeline = params.toolsUsed.map((tool, index) => ({
|
|
235
|
+
stepType: "tool-call",
|
|
236
|
+
event: "tool-call",
|
|
237
|
+
status: "success",
|
|
238
|
+
timestampMs: index,
|
|
239
|
+
tool: {
|
|
240
|
+
id: tool.id,
|
|
241
|
+
name: tool.name
|
|
242
|
+
},
|
|
243
|
+
metadata: {
|
|
244
|
+
skillCalls: tool.skillCalls,
|
|
245
|
+
synthetic: true
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
248
|
+
const toolCalls = params.toolsUsed.reduce(
|
|
249
|
+
(sum, tool) => sum + Math.max(tool.skillCalls, 0),
|
|
250
|
+
0
|
|
251
|
+
);
|
|
252
|
+
return {
|
|
253
|
+
summary: {
|
|
254
|
+
toolCalls,
|
|
255
|
+
retryCount: 0,
|
|
256
|
+
selfHealCount: 0,
|
|
257
|
+
fallbackCount: 0,
|
|
258
|
+
failureCount: 0,
|
|
259
|
+
recoveryCount: 0,
|
|
260
|
+
completionChecks: 0,
|
|
261
|
+
loopCount: 0
|
|
262
|
+
},
|
|
263
|
+
timeline,
|
|
264
|
+
source: "sdk-fallback",
|
|
265
|
+
synthetic: true,
|
|
266
|
+
reason: "backend_trace_missing",
|
|
267
|
+
durationMs: params.durationMs
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
buildSyntheticTraceFromStreamStatus(params) {
|
|
271
|
+
const timeline = params.statusTimeline.map((entry, index) => ({
|
|
272
|
+
stepType: "tool-status",
|
|
273
|
+
event: "tool-status",
|
|
274
|
+
status: entry.status,
|
|
275
|
+
timestampMs: index,
|
|
276
|
+
tool: entry.tool.name || entry.tool.id ? {
|
|
277
|
+
id: entry.tool.id || void 0,
|
|
278
|
+
name: entry.tool.name || void 0
|
|
279
|
+
} : void 0,
|
|
280
|
+
metadata: { synthetic: true }
|
|
281
|
+
}));
|
|
282
|
+
const toolCallsFromUsage = params.toolsUsed.reduce(
|
|
283
|
+
(sum, tool) => sum + Math.max(tool.skillCalls, 0),
|
|
284
|
+
0
|
|
285
|
+
);
|
|
286
|
+
const toolCallsFromStatus = params.statusTimeline.filter(
|
|
287
|
+
(entry) => entry.status === "tool-complete"
|
|
288
|
+
).length;
|
|
289
|
+
const toolCalls = toolCallsFromUsage > 0 ? toolCallsFromUsage : toolCallsFromStatus;
|
|
290
|
+
const retryCount = params.statusTimeline.filter(
|
|
291
|
+
(entry) => /(retry|fix|reflect|recover)/i.test(entry.status)
|
|
292
|
+
).length;
|
|
293
|
+
const completionChecks = params.statusTimeline.filter(
|
|
294
|
+
(entry) => /complet/i.test(entry.status)
|
|
295
|
+
).length;
|
|
296
|
+
return {
|
|
297
|
+
summary: {
|
|
298
|
+
toolCalls,
|
|
299
|
+
retryCount,
|
|
300
|
+
selfHealCount: retryCount,
|
|
301
|
+
fallbackCount: 0,
|
|
302
|
+
failureCount: 0,
|
|
303
|
+
recoveryCount: 0,
|
|
304
|
+
completionChecks,
|
|
305
|
+
loopCount: retryCount
|
|
306
|
+
},
|
|
307
|
+
timeline,
|
|
308
|
+
source: "sdk-fallback",
|
|
309
|
+
synthetic: true,
|
|
310
|
+
reason: "backend_trace_missing",
|
|
311
|
+
durationMs: params.durationMs
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
mergeDeveloperTrace(first, second) {
|
|
315
|
+
if (!first) return second;
|
|
316
|
+
if (!second) return first;
|
|
317
|
+
const firstTimeline = Array.isArray(first.timeline) ? first.timeline : [];
|
|
318
|
+
const secondTimeline = Array.isArray(second.timeline) ? second.timeline : [];
|
|
319
|
+
const mergedTimeline = [...firstTimeline, ...secondTimeline];
|
|
320
|
+
return {
|
|
321
|
+
...first,
|
|
322
|
+
...second,
|
|
323
|
+
summary: {
|
|
324
|
+
...typeof first.summary === "object" && first.summary ? first.summary : {},
|
|
325
|
+
...typeof second.summary === "object" && second.summary ? second.summary : {}
|
|
326
|
+
},
|
|
327
|
+
...mergedTimeline.length > 0 ? { timeline: mergedTimeline } : {}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
parseStreamEvent(rawData) {
|
|
331
|
+
const parsed = JSON.parse(rawData);
|
|
332
|
+
if (!parsed || typeof parsed !== "object") {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
const event = parsed;
|
|
336
|
+
if (typeof event.type !== "string") {
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
return event;
|
|
340
|
+
}
|
|
233
341
|
/**
|
|
234
342
|
* Run an agentic query and wait for the full response.
|
|
235
343
|
*
|
|
236
344
|
* The server discovers relevant tools (or uses the ones you specify),
|
|
237
|
-
* executes the
|
|
345
|
+
* executes the discovery-first pipeline (up to 100 MCP calls per tool),
|
|
238
346
|
* and returns an AI-synthesized answer. Payment is settled after
|
|
239
347
|
* successful execution via deferred settlement.
|
|
240
348
|
*
|
|
@@ -263,42 +371,25 @@ var Query = class {
|
|
|
263
371
|
*/
|
|
264
372
|
async run(options) {
|
|
265
373
|
const opts = typeof options === "string" ? { query: options } : options;
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
"
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
stream: false
|
|
280
|
-
})
|
|
374
|
+
let terminalError;
|
|
375
|
+
for await (const event of this.stream(opts)) {
|
|
376
|
+
if (event.type === "error") {
|
|
377
|
+
terminalError = {
|
|
378
|
+
error: event.error,
|
|
379
|
+
...event.code ? { code: event.code } : {},
|
|
380
|
+
...event.scope ? { scope: event.scope } : {},
|
|
381
|
+
...event.reasonCode ? { reasonCode: event.reasonCode } : {}
|
|
382
|
+
};
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (event.type === "done") {
|
|
386
|
+
return event.result;
|
|
281
387
|
}
|
|
282
|
-
);
|
|
283
|
-
if ("error" in response) {
|
|
284
|
-
throw new ContextError(
|
|
285
|
-
response.error,
|
|
286
|
-
response.code,
|
|
287
|
-
void 0,
|
|
288
|
-
response.helpUrl
|
|
289
|
-
);
|
|
290
388
|
}
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
response: response.response,
|
|
294
|
-
toolsUsed: response.toolsUsed,
|
|
295
|
-
cost: response.cost,
|
|
296
|
-
durationMs: response.durationMs,
|
|
297
|
-
data: response.data,
|
|
298
|
-
dataUrl: response.dataUrl
|
|
299
|
-
};
|
|
389
|
+
if (terminalError) {
|
|
390
|
+
throw new ContextError(terminalError.error, terminalError.code);
|
|
300
391
|
}
|
|
301
|
-
throw new ContextError("
|
|
392
|
+
throw new ContextError("Streaming query ended before done event");
|
|
302
393
|
}
|
|
303
394
|
/**
|
|
304
395
|
* Run an agentic query with streaming. Returns an async iterable that
|
|
@@ -307,6 +398,8 @@ var Query = class {
|
|
|
307
398
|
* Event types:
|
|
308
399
|
* - `tool-status` — A tool started executing or changed status
|
|
309
400
|
* - `text-delta` — A chunk of the AI response text
|
|
401
|
+
* - `developer-trace` — Runtime trace metadata (when includeDeveloperTrace=true)
|
|
402
|
+
* - `error` — A structured query/runtime error emitted before stream completion
|
|
310
403
|
* - `done` — The full response is complete (includes final `QueryResult`)
|
|
311
404
|
*
|
|
312
405
|
* @param options - Query options or a plain string question
|
|
@@ -322,9 +415,15 @@ var Query = class {
|
|
|
322
415
|
* case "text-delta":
|
|
323
416
|
* process.stdout.write(event.delta);
|
|
324
417
|
* break;
|
|
418
|
+
* case "developer-trace":
|
|
419
|
+
* console.log("Trace summary:", event.trace.summary);
|
|
420
|
+
* break;
|
|
325
421
|
* case "done":
|
|
326
422
|
* console.log("\nCost:", event.result.cost.totalCostUsd);
|
|
327
423
|
* break;
|
|
424
|
+
* case "error":
|
|
425
|
+
* console.error("Stream error:", event.error);
|
|
426
|
+
* break;
|
|
328
427
|
* }
|
|
329
428
|
* }
|
|
330
429
|
* ```
|
|
@@ -341,7 +440,9 @@ var Query = class {
|
|
|
341
440
|
modelId: opts.modelId,
|
|
342
441
|
includeData: opts.includeData,
|
|
343
442
|
includeDataUrl: opts.includeDataUrl,
|
|
443
|
+
includeDeveloperTrace: opts.includeDeveloperTrace,
|
|
344
444
|
queryDepth: opts.queryDepth,
|
|
445
|
+
debugScoutDeepMode: opts.debugScoutDeepMode,
|
|
345
446
|
stream: true
|
|
346
447
|
})
|
|
347
448
|
});
|
|
@@ -352,6 +453,48 @@ var Query = class {
|
|
|
352
453
|
const reader = body.getReader();
|
|
353
454
|
const decoder = new TextDecoder();
|
|
354
455
|
let buffer = "";
|
|
456
|
+
let aggregatedTrace;
|
|
457
|
+
const statusTimeline = [];
|
|
458
|
+
const parseAndHydrateEvent = (rawData) => {
|
|
459
|
+
const event = this.parseStreamEvent(rawData);
|
|
460
|
+
if (!event) {
|
|
461
|
+
return void 0;
|
|
462
|
+
}
|
|
463
|
+
if (event.type === "developer-trace") {
|
|
464
|
+
aggregatedTrace = this.mergeDeveloperTrace(aggregatedTrace, event.trace);
|
|
465
|
+
return event;
|
|
466
|
+
}
|
|
467
|
+
if (event.type === "tool-status") {
|
|
468
|
+
statusTimeline.push({
|
|
469
|
+
status: event.status,
|
|
470
|
+
tool: {
|
|
471
|
+
id: event.tool.id,
|
|
472
|
+
name: event.tool.name
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
return event;
|
|
476
|
+
}
|
|
477
|
+
if (event.type === "done") {
|
|
478
|
+
let mergedTrace = this.mergeDeveloperTrace(
|
|
479
|
+
aggregatedTrace,
|
|
480
|
+
event.result.developerTrace
|
|
481
|
+
);
|
|
482
|
+
if (!mergedTrace && opts.includeDeveloperTrace) {
|
|
483
|
+
mergedTrace = statusTimeline.length > 0 ? this.buildSyntheticTraceFromStreamStatus({
|
|
484
|
+
statusTimeline,
|
|
485
|
+
toolsUsed: event.result.toolsUsed,
|
|
486
|
+
durationMs: event.result.durationMs
|
|
487
|
+
}) : this.buildSyntheticTraceFromRunResult({
|
|
488
|
+
toolsUsed: event.result.toolsUsed,
|
|
489
|
+
durationMs: event.result.durationMs
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
if (mergedTrace) {
|
|
493
|
+
event.result.developerTrace = mergedTrace;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return event;
|
|
497
|
+
};
|
|
355
498
|
try {
|
|
356
499
|
while (true) {
|
|
357
500
|
const { done, value } = await reader.read();
|
|
@@ -365,7 +508,10 @@ var Query = class {
|
|
|
365
508
|
const data = trimmed.slice(6);
|
|
366
509
|
if (data === "[DONE]") return;
|
|
367
510
|
try {
|
|
368
|
-
|
|
511
|
+
const event = parseAndHydrateEvent(data);
|
|
512
|
+
if (event) {
|
|
513
|
+
yield event;
|
|
514
|
+
}
|
|
369
515
|
} catch {
|
|
370
516
|
}
|
|
371
517
|
}
|
|
@@ -375,7 +521,10 @@ var Query = class {
|
|
|
375
521
|
const data = buffer.trim().slice(6);
|
|
376
522
|
if (data !== "[DONE]") {
|
|
377
523
|
try {
|
|
378
|
-
|
|
524
|
+
const event = parseAndHydrateEvent(data);
|
|
525
|
+
if (event) {
|
|
526
|
+
yield event;
|
|
527
|
+
}
|
|
379
528
|
} catch {
|
|
380
529
|
}
|
|
381
530
|
}
|
|
@@ -454,30 +603,34 @@ var ContextClient = class {
|
|
|
454
603
|
*
|
|
455
604
|
* @internal
|
|
456
605
|
*/
|
|
457
|
-
async _fetch(endpoint, options = {}) {
|
|
606
|
+
async _fetch(endpoint, options = {}, fetchOptions) {
|
|
458
607
|
if (this._closed) {
|
|
459
608
|
throw new ContextError("Client has been closed");
|
|
460
609
|
}
|
|
461
610
|
const url = `${this.baseUrl}${endpoint}`;
|
|
462
611
|
const maxRetries = 3;
|
|
463
612
|
const timeoutMs = this.requestTimeoutMs;
|
|
613
|
+
const method = (options.method ?? "GET").toUpperCase();
|
|
614
|
+
const requestHeaders = new Headers(options.headers);
|
|
615
|
+
const canRetryRequest = fetchOptions?.retry === false ? false : method === "GET" || method === "HEAD" || method === "OPTIONS" || requestHeaders.has("Idempotency-Key");
|
|
464
616
|
let lastError;
|
|
465
617
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
466
618
|
const controller = new AbortController();
|
|
467
619
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
620
|
+
const mergedHeaders = new Headers(requestHeaders);
|
|
621
|
+
if (!mergedHeaders.has("Content-Type")) {
|
|
622
|
+
mergedHeaders.set("Content-Type", "application/json");
|
|
623
|
+
}
|
|
624
|
+
mergedHeaders.set("Authorization", `Bearer ${this.apiKey}`);
|
|
468
625
|
try {
|
|
469
626
|
const response = await fetch(url, {
|
|
470
627
|
...options,
|
|
471
628
|
signal: controller.signal,
|
|
472
|
-
headers:
|
|
473
|
-
"Content-Type": "application/json",
|
|
474
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
475
|
-
...options.headers
|
|
476
|
-
}
|
|
629
|
+
headers: mergedHeaders
|
|
477
630
|
});
|
|
478
631
|
clearTimeout(timeout);
|
|
479
632
|
if (!response.ok) {
|
|
480
|
-
if (response.status >= 500 && attempt < maxRetries) {
|
|
633
|
+
if (response.status >= 500 && canRetryRequest && attempt < maxRetries) {
|
|
481
634
|
const delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
482
635
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
483
636
|
continue;
|
|
@@ -496,7 +649,16 @@ var ContextClient = class {
|
|
|
496
649
|
}
|
|
497
650
|
throw new ContextError(errorMessage, errorCode, response.status, helpUrl);
|
|
498
651
|
}
|
|
499
|
-
|
|
652
|
+
try {
|
|
653
|
+
return await response.json();
|
|
654
|
+
} catch (error) {
|
|
655
|
+
const parseError = error instanceof Error ? error : new Error(String(error));
|
|
656
|
+
throw new ContextError(
|
|
657
|
+
`Failed to parse JSON response: ${parseError.message}`,
|
|
658
|
+
void 0,
|
|
659
|
+
response.status
|
|
660
|
+
);
|
|
661
|
+
}
|
|
500
662
|
} catch (error) {
|
|
501
663
|
clearTimeout(timeout);
|
|
502
664
|
if (error instanceof ContextError) {
|
|
@@ -504,7 +666,7 @@ var ContextClient = class {
|
|
|
504
666
|
}
|
|
505
667
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
506
668
|
const isRetryable = lastError.name === "AbortError" || lastError.message.includes("fetch failed") || lastError.message.includes("ECONNRESET") || lastError.message.includes("ETIMEDOUT");
|
|
507
|
-
if (isRetryable && attempt < maxRetries) {
|
|
669
|
+
if (isRetryable && canRetryRequest && attempt < maxRetries) {
|
|
508
670
|
const delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
509
671
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
510
672
|
continue;
|