@ctxprotocol/sdk 0.8.4 → 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/dist/index.cjs CHANGED
@@ -342,7 +342,7 @@ var Query = class {
342
342
  * Run an agentic query and wait for the full response.
343
343
  *
344
344
  * The server discovers relevant tools (or uses the ones you specify),
345
- * executes the full agentic pipeline (up to 100 MCP calls per tool),
345
+ * executes the discovery-first pipeline (up to 100 MCP calls per tool),
346
346
  * and returns an AI-synthesized answer. Payment is settled after
347
347
  * successful execution via deferred settlement.
348
348
  *
@@ -371,48 +371,25 @@ var Query = class {
371
371
  */
372
372
  async run(options) {
373
373
  const opts = typeof options === "string" ? { query: options } : options;
374
- const headers = opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0;
375
- const response = await this.client._fetch(
376
- "/api/v1/query",
377
- {
378
- method: "POST",
379
- headers,
380
- body: JSON.stringify({
381
- query: opts.query,
382
- tools: opts.tools,
383
- modelId: opts.modelId,
384
- includeData: opts.includeData,
385
- includeDataUrl: opts.includeDataUrl,
386
- includeDeveloperTrace: opts.includeDeveloperTrace,
387
- queryDepth: opts.queryDepth,
388
- stream: false
389
- })
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;
390
387
  }
391
- );
392
- if ("error" in response) {
393
- throw new ContextError(
394
- response.error,
395
- response.code,
396
- void 0,
397
- response.helpUrl
398
- );
399
388
  }
400
- if (response.success) {
401
- const developerTrace = response.developerTrace ?? (opts.includeDeveloperTrace ? this.buildSyntheticTraceFromRunResult({
402
- toolsUsed: response.toolsUsed,
403
- durationMs: response.durationMs
404
- }) : void 0);
405
- return {
406
- response: response.response,
407
- toolsUsed: response.toolsUsed,
408
- cost: response.cost,
409
- durationMs: response.durationMs,
410
- data: response.data,
411
- dataUrl: response.dataUrl,
412
- developerTrace
413
- };
389
+ if (terminalError) {
390
+ throw new ContextError(terminalError.error, terminalError.code);
414
391
  }
415
- throw new ContextError("Unexpected response format from query API");
392
+ throw new ContextError("Streaming query ended before done event");
416
393
  }
417
394
  /**
418
395
  * Run an agentic query with streaming. Returns an async iterable that
@@ -422,6 +399,7 @@ var Query = class {
422
399
  * - `tool-status` — A tool started executing or changed status
423
400
  * - `text-delta` — A chunk of the AI response text
424
401
  * - `developer-trace` — Runtime trace metadata (when includeDeveloperTrace=true)
402
+ * - `error` — A structured query/runtime error emitted before stream completion
425
403
  * - `done` — The full response is complete (includes final `QueryResult`)
426
404
  *
427
405
  * @param options - Query options or a plain string question
@@ -443,6 +421,9 @@ var Query = class {
443
421
  * case "done":
444
422
  * console.log("\nCost:", event.result.cost.totalCostUsd);
445
423
  * break;
424
+ * case "error":
425
+ * console.error("Stream error:", event.error);
426
+ * break;
446
427
  * }
447
428
  * }
448
429
  * ```
@@ -461,6 +442,7 @@ var Query = class {
461
442
  includeDataUrl: opts.includeDataUrl,
462
443
  includeDeveloperTrace: opts.includeDeveloperTrace,
463
444
  queryDepth: opts.queryDepth,
445
+ debugScoutDeepMode: opts.debugScoutDeepMode,
464
446
  stream: true
465
447
  })
466
448
  });
@@ -498,10 +480,13 @@ var Query = class {
498
480
  event.result.developerTrace
499
481
  );
500
482
  if (!mergedTrace && opts.includeDeveloperTrace) {
501
- mergedTrace = this.buildSyntheticTraceFromStreamStatus({
483
+ mergedTrace = statusTimeline.length > 0 ? this.buildSyntheticTraceFromStreamStatus({
502
484
  statusTimeline,
503
485
  toolsUsed: event.result.toolsUsed,
504
486
  durationMs: event.result.durationMs
487
+ }) : this.buildSyntheticTraceFromRunResult({
488
+ toolsUsed: event.result.toolsUsed,
489
+ durationMs: event.result.durationMs
505
490
  });
506
491
  }
507
492
  if (mergedTrace) {
@@ -618,30 +603,34 @@ var ContextClient = class {
618
603
  *
619
604
  * @internal
620
605
  */
621
- async _fetch(endpoint, options = {}) {
606
+ async _fetch(endpoint, options = {}, fetchOptions) {
622
607
  if (this._closed) {
623
608
  throw new ContextError("Client has been closed");
624
609
  }
625
610
  const url = `${this.baseUrl}${endpoint}`;
626
611
  const maxRetries = 3;
627
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");
628
616
  let lastError;
629
617
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
630
618
  const controller = new AbortController();
631
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}`);
632
625
  try {
633
626
  const response = await fetch(url, {
634
627
  ...options,
635
628
  signal: controller.signal,
636
- headers: {
637
- "Content-Type": "application/json",
638
- Authorization: `Bearer ${this.apiKey}`,
639
- ...options.headers
640
- }
629
+ headers: mergedHeaders
641
630
  });
642
631
  clearTimeout(timeout);
643
632
  if (!response.ok) {
644
- if (response.status >= 500 && attempt < maxRetries) {
633
+ if (response.status >= 500 && canRetryRequest && attempt < maxRetries) {
645
634
  const delay = Math.min(1e3 * 2 ** attempt, 1e4);
646
635
  await new Promise((resolve) => setTimeout(resolve, delay));
647
636
  continue;
@@ -660,7 +649,16 @@ var ContextClient = class {
660
649
  }
661
650
  throw new ContextError(errorMessage, errorCode, response.status, helpUrl);
662
651
  }
663
- return response.json();
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
+ }
664
662
  } catch (error) {
665
663
  clearTimeout(timeout);
666
664
  if (error instanceof ContextError) {
@@ -668,7 +666,7 @@ var ContextClient = class {
668
666
  }
669
667
  lastError = error instanceof Error ? error : new Error(String(error));
670
668
  const isRetryable = lastError.name === "AbortError" || lastError.message.includes("fetch failed") || lastError.message.includes("ECONNRESET") || lastError.message.includes("ETIMEDOUT");
671
- if (isRetryable && attempt < maxRetries) {
669
+ if (isRetryable && canRetryRequest && attempt < maxRetries) {
672
670
  const delay = Math.min(1e3 * 2 ** attempt, 1e4);
673
671
  await new Promise((resolve) => setTimeout(resolve, delay));
674
672
  continue;