@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.
@@ -299,12 +299,14 @@ interface ExecutionResult<T = unknown> {
299
299
  }
300
300
  /** Supported orchestration depth modes for query execution. */
301
301
  type QueryDepth = "fast" | "auto" | "deep";
302
+ type QueryDeepMode = "deep-light" | "deep-heavy";
302
303
  /**
303
304
  * Options for the agentic query endpoint (pay-per-response).
304
305
  *
305
306
  * Unlike `execute()` which calls a single tool once, `query()` sends a
306
- * natural-language question and lets the server handle tool discovery,
307
- * multi-tool orchestration, self-healing retries, and AI synthesis.
307
+ * natural-language question and lets the server handle discovery-first
308
+ * orchestration (`discover/probe -> plan-from-evidence -> execute ->
309
+ * bounded fallback`) plus synthesis.
308
310
  * One flat fee covers up to 100 MCP skill calls per tool.
309
311
  */
310
312
  interface QueryOptions {
@@ -331,6 +333,12 @@ interface QueryOptions {
331
333
  * Useful for large payload workflows where inline JSON is not ideal.
332
334
  */
333
335
  includeDataUrl?: boolean;
336
+ /**
337
+ * Include machine-readable developer trace output for this query response.
338
+ * When enabled, the server may return summary counters plus diagnostics
339
+ * for lane selection, scout probe adequacy, and bounded fallback behavior.
340
+ */
341
+ includeDeveloperTrace?: boolean;
334
342
  /**
335
343
  * Query orchestration depth mode:
336
344
  * - `fast`: lower-latency path
@@ -338,12 +346,168 @@ interface QueryOptions {
338
346
  * - `deep`: full completeness-oriented path
339
347
  */
340
348
  queryDepth?: QueryDepth;
349
+ /**
350
+ * Development/testing only: force the server's internal deep lane.
351
+ * Ignored by normal production usage and invalid when `queryDepth` is `fast`.
352
+ */
353
+ debugScoutDeepMode?: QueryDeepMode;
341
354
  /**
342
355
  * Optional idempotency key (UUID recommended).
343
356
  * Reuse the same key when retrying the same logical request.
344
357
  */
345
358
  idempotencyKey?: string;
346
359
  }
360
+ /**
361
+ * Tool reference attached to developer trace timeline steps.
362
+ */
363
+ interface QueryDeveloperTraceToolRef {
364
+ id?: string;
365
+ name?: string;
366
+ method?: string;
367
+ [key: string]: unknown;
368
+ }
369
+ /**
370
+ * Loop metadata attached to developer trace timeline steps.
371
+ */
372
+ interface QueryDeveloperTraceLoopInfo {
373
+ name?: string;
374
+ iteration?: number;
375
+ maxIterations?: number;
376
+ [key: string]: unknown;
377
+ }
378
+ /**
379
+ * Tool selection metadata attached to discovery/planning diagnostics.
380
+ */
381
+ interface QueryDeveloperTraceToolSelection {
382
+ toolId: string;
383
+ toolName: string;
384
+ selectedMethodCount: number;
385
+ selectedMethods: string[];
386
+ omittedSelectedMethodCount: number;
387
+ priceUsd?: string;
388
+ }
389
+ /**
390
+ * Initial planner diagnostic details.
391
+ */
392
+ interface QueryPlanningTraceDiagnostic {
393
+ plannerQuery: string;
394
+ scoutEvidenceAttached: boolean;
395
+ scoutEvidencePromptBlock: string | null;
396
+ allowedModules: string[];
397
+ }
398
+ /**
399
+ * Rediscovery/fallback diagnostic details.
400
+ */
401
+ interface QueryRediscoveryTraceDiagnostic {
402
+ considered: boolean;
403
+ executed: boolean;
404
+ skipReason: string | null;
405
+ missingCapability: string | null;
406
+ rediscoveryQuery: string | null;
407
+ capabilityLooksLikeSearchNeed: boolean;
408
+ allowSearchFallbackOnElapsedCap: boolean;
409
+ searchFallbackUsed: boolean;
410
+ preRediscoveryBudgetReasonCode: string | null;
411
+ candidateSearchResults: QueryDeveloperTraceToolSelection[];
412
+ selectedAlternatives: QueryDeveloperTraceToolSelection[];
413
+ mergedTools: QueryDeveloperTraceToolSelection[];
414
+ usingPaidFallback: boolean;
415
+ branchPlan: QueryPlanningTraceDiagnostic | null;
416
+ }
417
+ /**
418
+ * Rich developer-trace diagnostics for discovery-first orchestration internals.
419
+ */
420
+ interface QueryDeveloperTraceDiagnostics {
421
+ selection: {
422
+ selectedDepth: string;
423
+ deepMode: string | null;
424
+ debugScoutDeepMode: string | null;
425
+ plannerReasoningStage: string;
426
+ scoutEnabled: boolean;
427
+ preserveFastOneShot: boolean;
428
+ candidateMethodCount: number;
429
+ scoutProbeStatus: string;
430
+ scoutProbeAdequacy: string;
431
+ scoutProbeConfidence: number;
432
+ scoutMetadataConfidence: number;
433
+ scoutProbeShortlistedMethodCount: number;
434
+ scoutProbeMissingCapability: string | null;
435
+ scoutPrePlanProbeCalls: number;
436
+ scoutPrePlanProbeBudgetReasonCode: string | null;
437
+ scoutChangedInitialPlan: boolean;
438
+ scoutChangedPlannerReasoningStage: boolean;
439
+ scoutInitialSelectedDepth: string;
440
+ scoutInitialDeepMode: string | null;
441
+ scoutInitialPlannerReasoningStage: string;
442
+ scoutInitialReasonCode: string;
443
+ scoutFinalReasonCode: string;
444
+ scoutEvidenceAttachedToPlanning: boolean;
445
+ scoutLlmSelectionUsed: boolean;
446
+ scoutLlmSelectionFallback: boolean;
447
+ scoutLlmSelectionLatencyMs: number | null;
448
+ selectedTools: QueryDeveloperTraceToolSelection[];
449
+ };
450
+ planning: {
451
+ initial: QueryPlanningTraceDiagnostic;
452
+ };
453
+ cost?: {
454
+ planningCostUsd: number;
455
+ initialExecutionCostUsd: number;
456
+ rediscoveryAdditionalCostUsd: number;
457
+ synthesisCostUsd: number;
458
+ totalModelCostUsd: number;
459
+ toolCostUsd: number;
460
+ totalChargedUsd: number;
461
+ };
462
+ completeness: {
463
+ evaluations: unknown[];
464
+ triggerNeedsDifferentTools: boolean;
465
+ triggerMissingCapability: string | null;
466
+ };
467
+ rediscovery: QueryRediscoveryTraceDiagnostic | null;
468
+ [key: string]: unknown;
469
+ }
470
+ /**
471
+ * A single developer-trace timeline step.
472
+ */
473
+ interface QueryDeveloperTraceStep {
474
+ stepType?: string;
475
+ event?: string;
476
+ status?: string;
477
+ message?: string;
478
+ timestampMs?: number;
479
+ tool?: QueryDeveloperTraceToolRef;
480
+ attempt?: number;
481
+ loop?: QueryDeveloperTraceLoopInfo;
482
+ metadata?: Record<string, unknown>;
483
+ [key: string]: unknown;
484
+ }
485
+ /**
486
+ * Aggregate counters that summarize developer-trace behavior.
487
+ */
488
+ interface QueryDeveloperTraceSummary {
489
+ toolCalls?: number;
490
+ retryCount?: number;
491
+ selfHealCount?: number;
492
+ fallbackCount?: number;
493
+ failureCount?: number;
494
+ recoveryCount?: number;
495
+ completionChecks?: number;
496
+ loopCount?: number;
497
+ [key: string]: unknown;
498
+ }
499
+ /**
500
+ * Developer Mode trace payload returned per query response (opt-in).
501
+ */
502
+ interface QueryDeveloperTrace {
503
+ summary?: QueryDeveloperTraceSummary;
504
+ timeline?: QueryDeveloperTraceStep[];
505
+ requestId?: string;
506
+ query?: string;
507
+ source?: string;
508
+ diagnostics?: QueryDeveloperTraceDiagnostics;
509
+ [key: string]: unknown;
510
+ }
347
511
  /**
348
512
  * Information about a tool that was used during a query response
349
513
  */
@@ -367,6 +531,19 @@ interface QueryCost {
367
531
  /** Total cost (model + tools) */
368
532
  totalCostUsd: string;
369
533
  }
534
+ /**
535
+ * High-level orchestration outcome metrics returned by the query API.
536
+ */
537
+ interface QueryOrchestrationMetrics {
538
+ parityStage: string;
539
+ orchestrationMode: string;
540
+ /** Whether the first plan path succeeded without fallback. */
541
+ firstPassSuccess: boolean;
542
+ /** Whether execution signaled a missing capability on first pass. */
543
+ capabilityMissSignaled: boolean;
544
+ /** Whether bounded rediscovery/fallback executed. */
545
+ rediscoveryExecuted: boolean;
546
+ }
370
547
  /**
371
548
  * The resolved result of a pay-per-response query
372
549
  */
@@ -383,6 +560,10 @@ interface QueryResult {
383
560
  data?: unknown;
384
561
  /** Optional blob URL for persisted execution data (when includeDataUrl=true) */
385
562
  dataUrl?: string;
563
+ /** Optional machine-readable Developer Mode trace payload */
564
+ developerTrace?: QueryDeveloperTrace;
565
+ /** Optional orchestration outcome metrics for benchmarking and rollout analysis */
566
+ orchestrationMetrics?: QueryOrchestrationMetrics;
386
567
  }
387
568
  /**
388
569
  * Successful response from the /api/v1/query endpoint
@@ -395,6 +576,8 @@ interface QueryApiSuccessResponse {
395
576
  durationMs: number;
396
577
  data?: unknown;
397
578
  dataUrl?: string;
579
+ developerTrace?: QueryDeveloperTrace;
580
+ orchestrationMetrics?: QueryOrchestrationMetrics;
398
581
  }
399
582
  /**
400
583
  * Raw API response from the query endpoint
@@ -414,15 +597,28 @@ interface QueryStreamTextDeltaEvent {
414
597
  type: "text-delta";
415
598
  delta: string;
416
599
  }
600
+ /** Emitted when the server streams developer trace updates/chunks */
601
+ interface QueryStreamDeveloperTraceEvent {
602
+ type: "developer-trace";
603
+ trace: QueryDeveloperTrace;
604
+ }
417
605
  /** Emitted when the full response is complete */
418
606
  interface QueryStreamDoneEvent {
419
607
  type: "done";
420
608
  result: QueryResult;
421
609
  }
610
+ /** Emitted when the server reports a recoverable or terminal query error */
611
+ interface QueryStreamErrorEvent {
612
+ type: "error";
613
+ error: string;
614
+ code?: ContextErrorCode | string;
615
+ scope?: string;
616
+ reasonCode?: string;
617
+ }
422
618
  /**
423
619
  * Union of all events emitted during a streaming query
424
620
  */
425
- type QueryStreamEvent = QueryStreamToolStatusEvent | QueryStreamTextDeltaEvent | QueryStreamDoneEvent;
621
+ type QueryStreamEvent = QueryStreamToolStatusEvent | QueryStreamTextDeltaEvent | QueryStreamDeveloperTraceEvent | QueryStreamDoneEvent | QueryStreamErrorEvent;
426
622
  /**
427
623
  * Specific error codes returned by the Context Protocol API
428
624
  */
@@ -524,8 +720,8 @@ declare class Tools {
524
720
  *
525
721
  * Unlike `tools.execute()` which calls a single tool once (pay-per-request),
526
722
  * the Query resource sends a natural-language question and lets the server
527
- * handle tool discovery, multi-tool orchestration, self-healing retries,
528
- * completeness checks, and AI synthesis — all for one flat fee.
723
+ * handle discovery-first orchestration (`discover/probe -> plan-from-evidence ->
724
+ * execute -> bounded fallback`) plus AI synthesis — all for one flat fee.
529
725
  *
530
726
  * This is the "prepared meal" vs "raw ingredients" distinction:
531
727
  * - `tools.execute()` = raw data, full control, predictable cost
@@ -534,11 +730,15 @@ declare class Tools {
534
730
  declare class Query {
535
731
  private client;
536
732
  constructor(client: ContextClient);
733
+ private buildSyntheticTraceFromRunResult;
734
+ private buildSyntheticTraceFromStreamStatus;
735
+ private mergeDeveloperTrace;
736
+ private parseStreamEvent;
537
737
  /**
538
738
  * Run an agentic query and wait for the full response.
539
739
  *
540
740
  * The server discovers relevant tools (or uses the ones you specify),
541
- * executes the full agentic pipeline (up to 100 MCP calls per tool),
741
+ * executes the discovery-first pipeline (up to 100 MCP calls per tool),
542
742
  * and returns an AI-synthesized answer. Payment is settled after
543
743
  * successful execution via deferred settlement.
544
744
  *
@@ -573,6 +773,8 @@ declare class Query {
573
773
  * Event types:
574
774
  * - `tool-status` — A tool started executing or changed status
575
775
  * - `text-delta` — A chunk of the AI response text
776
+ * - `developer-trace` — Runtime trace metadata (when includeDeveloperTrace=true)
777
+ * - `error` — A structured query/runtime error emitted before stream completion
576
778
  * - `done` — The full response is complete (includes final `QueryResult`)
577
779
  *
578
780
  * @param options - Query options or a plain string question
@@ -588,9 +790,15 @@ declare class Query {
588
790
  * case "text-delta":
589
791
  * process.stdout.write(event.delta);
590
792
  * break;
793
+ * case "developer-trace":
794
+ * console.log("Trace summary:", event.trace.summary);
795
+ * break;
591
796
  * case "done":
592
797
  * console.log("\nCost:", event.result.cost.totalCostUsd);
593
798
  * break;
799
+ * case "error":
800
+ * console.error("Stream error:", event.error);
801
+ * break;
594
802
  * }
595
803
  * }
596
804
  * ```
@@ -666,7 +874,9 @@ declare class ContextClient {
666
874
  *
667
875
  * @internal
668
876
  */
669
- _fetch<T>(endpoint: string, options?: RequestInit): Promise<T>;
877
+ _fetch<T>(endpoint: string, options?: RequestInit, fetchOptions?: {
878
+ retry?: boolean;
879
+ }): Promise<T>;
670
880
  /**
671
881
  * Internal method for making authenticated HTTP requests that returns
672
882
  * the raw Response object. Used for streaming endpoints (SSE).
@@ -677,4 +887,4 @@ declare class ContextClient {
677
887
  _fetchRaw(endpoint: string, options?: RequestInit): Promise<Response>;
678
888
  }
679
889
 
680
- export { ContextClient, type ContextClientOptions, ContextError, type ContextErrorCode, Discovery, type ExecuteApiErrorResponse, type ExecuteApiResponse, type ExecuteApiSuccessResponse, type ExecuteOptions, type ExecuteSessionApiResponse, type ExecuteSessionApiSuccessResponse, type ExecuteSessionResult, type ExecuteSessionSpend, type ExecuteSessionStartOptions, type ExecuteSessionStatus, type ExecutionResult, type McpTool, type McpToolMeta, type McpToolRateLimitHints, Query, type QueryApiResponse, type QueryApiSuccessResponse, type QueryCost, type QueryOptions, type QueryResult, type QueryStreamDoneEvent, type QueryStreamEvent, type QueryStreamTextDeltaEvent, type QueryStreamToolStatusEvent, type QueryToolUsage, type SearchOptions, type SearchResponse, type Tool, Tools };
890
+ export { ContextClient, type ContextClientOptions, ContextError, type ContextErrorCode, Discovery, type ExecuteApiErrorResponse, type ExecuteApiResponse, type ExecuteApiSuccessResponse, type ExecuteOptions, type ExecuteSessionApiResponse, type ExecuteSessionApiSuccessResponse, type ExecuteSessionResult, type ExecuteSessionSpend, type ExecuteSessionStartOptions, type ExecuteSessionStatus, type ExecutionResult, type McpTool, type McpToolMeta, type McpToolRateLimitHints, Query, type QueryApiResponse, type QueryApiSuccessResponse, type QueryCost, type QueryDeepMode, type QueryDeveloperTrace, type QueryDeveloperTraceLoopInfo, type QueryDeveloperTraceStep, type QueryDeveloperTraceSummary, type QueryDeveloperTraceToolRef, type QueryOptions, type QueryResult, type QueryStreamDeveloperTraceEvent, type QueryStreamDoneEvent, type QueryStreamErrorEvent, type QueryStreamEvent, type QueryStreamTextDeltaEvent, type QueryStreamToolStatusEvent, type QueryToolUsage, type SearchOptions, type SearchResponse, type Tool, Tools };
@@ -226,11 +226,119 @@ var Query = class {
226
226
  constructor(client) {
227
227
  this.client = client;
228
228
  }
229
+ buildSyntheticTraceFromRunResult(params) {
230
+ const timeline = params.toolsUsed.map((tool, index) => ({
231
+ stepType: "tool-call",
232
+ event: "tool-call",
233
+ status: "success",
234
+ timestampMs: index,
235
+ tool: {
236
+ id: tool.id,
237
+ name: tool.name
238
+ },
239
+ metadata: {
240
+ skillCalls: tool.skillCalls,
241
+ synthetic: true
242
+ }
243
+ }));
244
+ const toolCalls = params.toolsUsed.reduce(
245
+ (sum, tool) => sum + Math.max(tool.skillCalls, 0),
246
+ 0
247
+ );
248
+ return {
249
+ summary: {
250
+ toolCalls,
251
+ retryCount: 0,
252
+ selfHealCount: 0,
253
+ fallbackCount: 0,
254
+ failureCount: 0,
255
+ recoveryCount: 0,
256
+ completionChecks: 0,
257
+ loopCount: 0
258
+ },
259
+ timeline,
260
+ source: "sdk-fallback",
261
+ synthetic: true,
262
+ reason: "backend_trace_missing",
263
+ durationMs: params.durationMs
264
+ };
265
+ }
266
+ buildSyntheticTraceFromStreamStatus(params) {
267
+ const timeline = params.statusTimeline.map((entry, index) => ({
268
+ stepType: "tool-status",
269
+ event: "tool-status",
270
+ status: entry.status,
271
+ timestampMs: index,
272
+ tool: entry.tool.name || entry.tool.id ? {
273
+ id: entry.tool.id || void 0,
274
+ name: entry.tool.name || void 0
275
+ } : void 0,
276
+ metadata: { synthetic: true }
277
+ }));
278
+ const toolCallsFromUsage = params.toolsUsed.reduce(
279
+ (sum, tool) => sum + Math.max(tool.skillCalls, 0),
280
+ 0
281
+ );
282
+ const toolCallsFromStatus = params.statusTimeline.filter(
283
+ (entry) => entry.status === "tool-complete"
284
+ ).length;
285
+ const toolCalls = toolCallsFromUsage > 0 ? toolCallsFromUsage : toolCallsFromStatus;
286
+ const retryCount = params.statusTimeline.filter(
287
+ (entry) => /(retry|fix|reflect|recover)/i.test(entry.status)
288
+ ).length;
289
+ const completionChecks = params.statusTimeline.filter(
290
+ (entry) => /complet/i.test(entry.status)
291
+ ).length;
292
+ return {
293
+ summary: {
294
+ toolCalls,
295
+ retryCount,
296
+ selfHealCount: retryCount,
297
+ fallbackCount: 0,
298
+ failureCount: 0,
299
+ recoveryCount: 0,
300
+ completionChecks,
301
+ loopCount: retryCount
302
+ },
303
+ timeline,
304
+ source: "sdk-fallback",
305
+ synthetic: true,
306
+ reason: "backend_trace_missing",
307
+ durationMs: params.durationMs
308
+ };
309
+ }
310
+ mergeDeveloperTrace(first, second) {
311
+ if (!first) return second;
312
+ if (!second) return first;
313
+ const firstTimeline = Array.isArray(first.timeline) ? first.timeline : [];
314
+ const secondTimeline = Array.isArray(second.timeline) ? second.timeline : [];
315
+ const mergedTimeline = [...firstTimeline, ...secondTimeline];
316
+ return {
317
+ ...first,
318
+ ...second,
319
+ summary: {
320
+ ...typeof first.summary === "object" && first.summary ? first.summary : {},
321
+ ...typeof second.summary === "object" && second.summary ? second.summary : {}
322
+ },
323
+ ...mergedTimeline.length > 0 ? { timeline: mergedTimeline } : {}
324
+ };
325
+ }
326
+ parseStreamEvent(rawData) {
327
+ const parsed = JSON.parse(rawData);
328
+ if (!parsed || typeof parsed !== "object") {
329
+ return void 0;
330
+ }
331
+ const event = parsed;
332
+ if (typeof event.type !== "string") {
333
+ return void 0;
334
+ }
335
+ return event;
336
+ }
229
337
  /**
230
338
  * Run an agentic query and wait for the full response.
231
339
  *
232
340
  * The server discovers relevant tools (or uses the ones you specify),
233
- * executes the full agentic pipeline (up to 100 MCP calls per tool),
341
+ * executes the discovery-first pipeline (up to 100 MCP calls per tool),
234
342
  * and returns an AI-synthesized answer. Payment is settled after
235
343
  * successful execution via deferred settlement.
236
344
  *
@@ -259,42 +367,25 @@ var Query = class {
259
367
  */
260
368
  async run(options) {
261
369
  const opts = typeof options === "string" ? { query: options } : options;
262
- const headers = opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0;
263
- const response = await this.client._fetch(
264
- "/api/v1/query",
265
- {
266
- method: "POST",
267
- headers,
268
- body: JSON.stringify({
269
- query: opts.query,
270
- tools: opts.tools,
271
- modelId: opts.modelId,
272
- includeData: opts.includeData,
273
- includeDataUrl: opts.includeDataUrl,
274
- queryDepth: opts.queryDepth,
275
- stream: false
276
- })
370
+ let terminalError;
371
+ for await (const event of this.stream(opts)) {
372
+ if (event.type === "error") {
373
+ terminalError = {
374
+ error: event.error,
375
+ ...event.code ? { code: event.code } : {},
376
+ ...event.scope ? { scope: event.scope } : {},
377
+ ...event.reasonCode ? { reasonCode: event.reasonCode } : {}
378
+ };
379
+ continue;
380
+ }
381
+ if (event.type === "done") {
382
+ return event.result;
277
383
  }
278
- );
279
- if ("error" in response) {
280
- throw new ContextError(
281
- response.error,
282
- response.code,
283
- void 0,
284
- response.helpUrl
285
- );
286
384
  }
287
- if (response.success) {
288
- return {
289
- response: response.response,
290
- toolsUsed: response.toolsUsed,
291
- cost: response.cost,
292
- durationMs: response.durationMs,
293
- data: response.data,
294
- dataUrl: response.dataUrl
295
- };
385
+ if (terminalError) {
386
+ throw new ContextError(terminalError.error, terminalError.code);
296
387
  }
297
- throw new ContextError("Unexpected response format from query API");
388
+ throw new ContextError("Streaming query ended before done event");
298
389
  }
299
390
  /**
300
391
  * Run an agentic query with streaming. Returns an async iterable that
@@ -303,6 +394,8 @@ var Query = class {
303
394
  * Event types:
304
395
  * - `tool-status` — A tool started executing or changed status
305
396
  * - `text-delta` — A chunk of the AI response text
397
+ * - `developer-trace` — Runtime trace metadata (when includeDeveloperTrace=true)
398
+ * - `error` — A structured query/runtime error emitted before stream completion
306
399
  * - `done` — The full response is complete (includes final `QueryResult`)
307
400
  *
308
401
  * @param options - Query options or a plain string question
@@ -318,9 +411,15 @@ var Query = class {
318
411
  * case "text-delta":
319
412
  * process.stdout.write(event.delta);
320
413
  * break;
414
+ * case "developer-trace":
415
+ * console.log("Trace summary:", event.trace.summary);
416
+ * break;
321
417
  * case "done":
322
418
  * console.log("\nCost:", event.result.cost.totalCostUsd);
323
419
  * break;
420
+ * case "error":
421
+ * console.error("Stream error:", event.error);
422
+ * break;
324
423
  * }
325
424
  * }
326
425
  * ```
@@ -337,7 +436,9 @@ var Query = class {
337
436
  modelId: opts.modelId,
338
437
  includeData: opts.includeData,
339
438
  includeDataUrl: opts.includeDataUrl,
439
+ includeDeveloperTrace: opts.includeDeveloperTrace,
340
440
  queryDepth: opts.queryDepth,
441
+ debugScoutDeepMode: opts.debugScoutDeepMode,
341
442
  stream: true
342
443
  })
343
444
  });
@@ -348,6 +449,48 @@ var Query = class {
348
449
  const reader = body.getReader();
349
450
  const decoder = new TextDecoder();
350
451
  let buffer = "";
452
+ let aggregatedTrace;
453
+ const statusTimeline = [];
454
+ const parseAndHydrateEvent = (rawData) => {
455
+ const event = this.parseStreamEvent(rawData);
456
+ if (!event) {
457
+ return void 0;
458
+ }
459
+ if (event.type === "developer-trace") {
460
+ aggregatedTrace = this.mergeDeveloperTrace(aggregatedTrace, event.trace);
461
+ return event;
462
+ }
463
+ if (event.type === "tool-status") {
464
+ statusTimeline.push({
465
+ status: event.status,
466
+ tool: {
467
+ id: event.tool.id,
468
+ name: event.tool.name
469
+ }
470
+ });
471
+ return event;
472
+ }
473
+ if (event.type === "done") {
474
+ let mergedTrace = this.mergeDeveloperTrace(
475
+ aggregatedTrace,
476
+ event.result.developerTrace
477
+ );
478
+ if (!mergedTrace && opts.includeDeveloperTrace) {
479
+ mergedTrace = statusTimeline.length > 0 ? this.buildSyntheticTraceFromStreamStatus({
480
+ statusTimeline,
481
+ toolsUsed: event.result.toolsUsed,
482
+ durationMs: event.result.durationMs
483
+ }) : this.buildSyntheticTraceFromRunResult({
484
+ toolsUsed: event.result.toolsUsed,
485
+ durationMs: event.result.durationMs
486
+ });
487
+ }
488
+ if (mergedTrace) {
489
+ event.result.developerTrace = mergedTrace;
490
+ }
491
+ }
492
+ return event;
493
+ };
351
494
  try {
352
495
  while (true) {
353
496
  const { done, value } = await reader.read();
@@ -361,7 +504,10 @@ var Query = class {
361
504
  const data = trimmed.slice(6);
362
505
  if (data === "[DONE]") return;
363
506
  try {
364
- yield JSON.parse(data);
507
+ const event = parseAndHydrateEvent(data);
508
+ if (event) {
509
+ yield event;
510
+ }
365
511
  } catch {
366
512
  }
367
513
  }
@@ -371,7 +517,10 @@ var Query = class {
371
517
  const data = buffer.trim().slice(6);
372
518
  if (data !== "[DONE]") {
373
519
  try {
374
- yield JSON.parse(data);
520
+ const event = parseAndHydrateEvent(data);
521
+ if (event) {
522
+ yield event;
523
+ }
375
524
  } catch {
376
525
  }
377
526
  }
@@ -450,30 +599,34 @@ var ContextClient = class {
450
599
  *
451
600
  * @internal
452
601
  */
453
- async _fetch(endpoint, options = {}) {
602
+ async _fetch(endpoint, options = {}, fetchOptions) {
454
603
  if (this._closed) {
455
604
  throw new ContextError("Client has been closed");
456
605
  }
457
606
  const url = `${this.baseUrl}${endpoint}`;
458
607
  const maxRetries = 3;
459
608
  const timeoutMs = this.requestTimeoutMs;
609
+ const method = (options.method ?? "GET").toUpperCase();
610
+ const requestHeaders = new Headers(options.headers);
611
+ const canRetryRequest = fetchOptions?.retry === false ? false : method === "GET" || method === "HEAD" || method === "OPTIONS" || requestHeaders.has("Idempotency-Key");
460
612
  let lastError;
461
613
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
462
614
  const controller = new AbortController();
463
615
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
616
+ const mergedHeaders = new Headers(requestHeaders);
617
+ if (!mergedHeaders.has("Content-Type")) {
618
+ mergedHeaders.set("Content-Type", "application/json");
619
+ }
620
+ mergedHeaders.set("Authorization", `Bearer ${this.apiKey}`);
464
621
  try {
465
622
  const response = await fetch(url, {
466
623
  ...options,
467
624
  signal: controller.signal,
468
- headers: {
469
- "Content-Type": "application/json",
470
- Authorization: `Bearer ${this.apiKey}`,
471
- ...options.headers
472
- }
625
+ headers: mergedHeaders
473
626
  });
474
627
  clearTimeout(timeout);
475
628
  if (!response.ok) {
476
- if (response.status >= 500 && attempt < maxRetries) {
629
+ if (response.status >= 500 && canRetryRequest && attempt < maxRetries) {
477
630
  const delay = Math.min(1e3 * 2 ** attempt, 1e4);
478
631
  await new Promise((resolve) => setTimeout(resolve, delay));
479
632
  continue;
@@ -492,7 +645,16 @@ var ContextClient = class {
492
645
  }
493
646
  throw new ContextError(errorMessage, errorCode, response.status, helpUrl);
494
647
  }
495
- return response.json();
648
+ try {
649
+ return await response.json();
650
+ } catch (error) {
651
+ const parseError = error instanceof Error ? error : new Error(String(error));
652
+ throw new ContextError(
653
+ `Failed to parse JSON response: ${parseError.message}`,
654
+ void 0,
655
+ response.status
656
+ );
657
+ }
496
658
  } catch (error) {
497
659
  clearTimeout(timeout);
498
660
  if (error instanceof ContextError) {
@@ -500,7 +662,7 @@ var ContextClient = class {
500
662
  }
501
663
  lastError = error instanceof Error ? error : new Error(String(error));
502
664
  const isRetryable = lastError.name === "AbortError" || lastError.message.includes("fetch failed") || lastError.message.includes("ECONNRESET") || lastError.message.includes("ETIMEDOUT");
503
- if (isRetryable && attempt < maxRetries) {
665
+ if (isRetryable && canRetryRequest && attempt < maxRetries) {
504
666
  const delay = Math.min(1e3 * 2 ** attempt, 1e4);
505
667
  await new Promise((resolve) => setTimeout(resolve, delay));
506
668
  continue;