@ctxprotocol/sdk 0.8.3 → 0.8.4

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
@@ -228,6 +228,114 @@ 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
  *
@@ -273,6 +381,7 @@ var Query = class {
273
381
  modelId: opts.modelId,
274
382
  includeData: opts.includeData,
275
383
  includeDataUrl: opts.includeDataUrl,
384
+ includeDeveloperTrace: opts.includeDeveloperTrace,
276
385
  queryDepth: opts.queryDepth,
277
386
  stream: false
278
387
  })
@@ -287,13 +396,18 @@ var Query = class {
287
396
  );
288
397
  }
289
398
  if (response.success) {
399
+ const developerTrace = response.developerTrace ?? (opts.includeDeveloperTrace ? this.buildSyntheticTraceFromRunResult({
400
+ toolsUsed: response.toolsUsed,
401
+ durationMs: response.durationMs
402
+ }) : void 0);
290
403
  return {
291
404
  response: response.response,
292
405
  toolsUsed: response.toolsUsed,
293
406
  cost: response.cost,
294
407
  durationMs: response.durationMs,
295
408
  data: response.data,
296
- dataUrl: response.dataUrl
409
+ dataUrl: response.dataUrl,
410
+ developerTrace
297
411
  };
298
412
  }
299
413
  throw new ContextError("Unexpected response format from query API");
@@ -305,6 +419,7 @@ var Query = class {
305
419
  * Event types:
306
420
  * - `tool-status` — A tool started executing or changed status
307
421
  * - `text-delta` — A chunk of the AI response text
422
+ * - `developer-trace` — Runtime trace metadata (when includeDeveloperTrace=true)
308
423
  * - `done` — The full response is complete (includes final `QueryResult`)
309
424
  *
310
425
  * @param options - Query options or a plain string question
@@ -320,6 +435,9 @@ var Query = class {
320
435
  * case "text-delta":
321
436
  * process.stdout.write(event.delta);
322
437
  * break;
438
+ * case "developer-trace":
439
+ * console.log("Trace summary:", event.trace.summary);
440
+ * break;
323
441
  * case "done":
324
442
  * console.log("\nCost:", event.result.cost.totalCostUsd);
325
443
  * break;
@@ -339,6 +457,7 @@ var Query = class {
339
457
  modelId: opts.modelId,
340
458
  includeData: opts.includeData,
341
459
  includeDataUrl: opts.includeDataUrl,
460
+ includeDeveloperTrace: opts.includeDeveloperTrace,
342
461
  queryDepth: opts.queryDepth,
343
462
  stream: true
344
463
  })
@@ -350,6 +469,45 @@ var Query = class {
350
469
  const reader = body.getReader();
351
470
  const decoder = new TextDecoder();
352
471
  let buffer = "";
472
+ let aggregatedTrace;
473
+ const statusTimeline = [];
474
+ const parseAndHydrateEvent = (rawData) => {
475
+ const event = this.parseStreamEvent(rawData);
476
+ if (!event) {
477
+ return void 0;
478
+ }
479
+ if (event.type === "developer-trace") {
480
+ aggregatedTrace = this.mergeDeveloperTrace(aggregatedTrace, event.trace);
481
+ return event;
482
+ }
483
+ if (event.type === "tool-status") {
484
+ statusTimeline.push({
485
+ status: event.status,
486
+ tool: {
487
+ id: event.tool.id,
488
+ name: event.tool.name
489
+ }
490
+ });
491
+ return event;
492
+ }
493
+ if (event.type === "done") {
494
+ let mergedTrace = this.mergeDeveloperTrace(
495
+ aggregatedTrace,
496
+ event.result.developerTrace
497
+ );
498
+ if (!mergedTrace && opts.includeDeveloperTrace) {
499
+ mergedTrace = this.buildSyntheticTraceFromStreamStatus({
500
+ statusTimeline,
501
+ toolsUsed: event.result.toolsUsed,
502
+ durationMs: event.result.durationMs
503
+ });
504
+ }
505
+ if (mergedTrace) {
506
+ event.result.developerTrace = mergedTrace;
507
+ }
508
+ }
509
+ return event;
510
+ };
353
511
  try {
354
512
  while (true) {
355
513
  const { done, value } = await reader.read();
@@ -363,7 +521,10 @@ var Query = class {
363
521
  const data = trimmed.slice(6);
364
522
  if (data === "[DONE]") return;
365
523
  try {
366
- yield JSON.parse(data);
524
+ const event = parseAndHydrateEvent(data);
525
+ if (event) {
526
+ yield event;
527
+ }
367
528
  } catch {
368
529
  }
369
530
  }
@@ -373,7 +534,10 @@ var Query = class {
373
534
  const data = buffer.trim().slice(6);
374
535
  if (data !== "[DONE]") {
375
536
  try {
376
- yield JSON.parse(data);
537
+ const event = parseAndHydrateEvent(data);
538
+ if (event) {
539
+ yield event;
540
+ }
377
541
  } catch {
378
542
  }
379
543
  }