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