@ctxprotocol/sdk 0.9.0 → 0.11.0

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 CHANGED
@@ -96,16 +96,19 @@ const result = await client.tools.execute({
96
96
  console.log(result.session); // methodPrice, spent, remaining, maxSpend, ...
97
97
  ```
98
98
 
99
- **Query mode** gives you curated answers — the server runs a discovery-first planner contract (`discover/probe -> plan-from-evidence -> execute -> bounded fallback`) with model-aware context budgeting and AI synthesis for one flat fee:
99
+ **Query mode** gives you a managed librarian contract — the server runs a discovery-first planner contract (`discover/probe -> plan-from-evidence -> execute -> bounded fallback`) with model-aware context budgeting and can return plain answers or structured evidence packages for one flat fee:
100
100
  ```typescript
101
101
  const answer = await client.query.run({
102
102
  query: "What are the top whale movements on Base?",
103
- modelId: "glm-model", // optional: choose a supported model
103
+ answerModelId: "glm-model", // optional: choose the final synthesis model
104
+ responseShape: "answer_with_evidence", // optional: answer | answer_with_evidence | evidence_only
104
105
  queryDepth: "deep", // optional: fast | auto | deep
105
106
  includeDataUrl: true, // optional: persist full execution data to blob
106
107
  includeDeveloperTrace: true, // optional: include machine-readable runtime trace
107
108
  });
108
- console.log(answer.response); // AI-synthesized answer
109
+ console.log(answer.response); // response text or summary
110
+ console.log(answer.summary); // short machine-friendly summary
111
+ console.log(answer.evidence); // structured evidence package
109
112
  console.log(answer.toolsUsed); // Which tools were used
110
113
  console.log(answer.cost); // Cost breakdown
111
114
  console.log(answer.dataUrl); // Optional blob URL with full data
@@ -114,6 +117,16 @@ console.log(answer.developerTrace?.diagnostics?.selection); // lane + scout prob
114
117
  console.log(answer.orchestrationMetrics); // high-level first-pass / rediscovery metrics
115
118
  ```
116
119
 
120
+ `responseShape` options:
121
+
122
+ - `answer`: backward-compatible prose answer
123
+ - `answer_with_evidence`: prose plus `summary`, `evidence`, `artifacts`, `freshness`, `confidence`, `usage`, `outcome`, and `controller`
124
+ - `evidence_only`: machine-friendly summary plus the same evidence package for downstream agents
125
+
126
+ Premium wedge answers can also expose `evidence.marketIntelligence`, `view.rows`, `view.columns`, and the top-level controller fields `stopReason`, `issueClass`, and `actionsTaken`.
127
+
128
+ The first-party chat app uses the same Query contract and defaults to `answer_with_evidence`.
129
+
117
130
  > Mixed listings are first-class: one listing can expose methods to both surfaces. Methods without `_meta.pricing.executeUsd` remain query-only until priced.
118
131
  >
119
132
  > Compatibility: SDK/API payload fields such as `price` and `pricePerQuery` are retained for backward compatibility. In Query mode, they represent listing-level **price per response turn**.
@@ -128,8 +141,11 @@ const client = new ContextClient({
128
141
  apiKey: "sk_live_...",
129
142
  });
130
143
 
131
- // Pay-per-response: Ask a question, get a curated answer
132
- const answer = await client.query.run("What are the top whale movements on Base?");
144
+ // Pay-per-response: Ask a question, get a managed answer package
145
+ const answer = await client.query.run({
146
+ query: "What are the top whale movements on Base?",
147
+ responseShape: "answer_with_evidence",
148
+ });
133
149
  console.log(answer.response);
134
150
 
135
151
  // Execute surface: require explicit execute pricing
@@ -402,7 +418,7 @@ const closed = await client.tools.closeSession("sess_123");
402
418
 
403
419
  #### `client.query.run(options)`
404
420
 
405
- Run an agentic query. The server applies discovery-first orchestration (`discover/probe -> plan-from-evidence -> execute -> bounded fallback`) with up to 100 MCP calls per response turn, then returns an AI-synthesized answer.
421
+ Run an agentic query. The server applies discovery-first orchestration (`discover/probe -> plan-from-evidence -> execute -> bounded fallback`) with up to 100 MCP calls per response turn, then returns the selected Query response contract (`answer`, `answer_with_evidence`, or `evidence_only`).
406
422
 
407
423
  `queryDepth` controls orchestration depth:
408
424
  - `fast`: lower-latency path for simple lookups.
@@ -422,14 +438,14 @@ const answer = await client.query.run("What are the top whale movements on Base?
422
438
  const answer = await client.query.run({
423
439
  query: "Analyze whale activity on Base",
424
440
  tools: ["tool-uuid-1", "tool-uuid-2"], // optional — auto-discover if omitted
425
- modelId: "kimi-model-thinking", // optional
441
+ answerModelId: "kimi-model-thinking", // optional final synthesis model
426
442
  queryDepth: "auto", // optional: fast | auto | deep
427
443
  includeData: true, // optional: include execution data inline
428
444
  includeDataUrl: true, // optional: include blob URL for full data
429
445
  includeDeveloperTrace: true, // optional: include Developer Mode trace
430
446
  });
431
447
 
432
- console.log(answer.response); // AI-synthesized text
448
+ console.log(answer.response); // response text or summary
433
449
  console.log(answer.toolsUsed); // [{ id, name, skillCalls }]
434
450
  console.log(answer.cost); // { modelCostUsd, toolCostUsd, totalCostUsd }
435
451
  console.log(answer.durationMs); // Total time
@@ -564,7 +580,7 @@ interface ExecutionResult<T = unknown> {
564
580
 
565
581
  ```typescript
566
582
  interface QueryResult {
567
- response: string; // AI-synthesized answer
583
+ response: string; // response text or summary
568
584
  toolsUsed: QueryToolUsage[]; // Tools used: { id, name, skillCalls }
569
585
  cost: QueryCost; // { modelCostUsd, toolCostUsd, totalCostUsd }
570
586
  durationMs: number;
@@ -1,6 +1,18 @@
1
1
  'use strict';
2
2
 
3
3
  // src/client/types.ts
4
+ var ALLOWED_TOOL_CATEGORIES = [
5
+ "Crypto & DeFi",
6
+ "Financial Markets",
7
+ "Business & Sales",
8
+ "Marketing & SEO",
9
+ "Legal & Regulatory",
10
+ "Real World",
11
+ "Developer Tools",
12
+ "Research & Academia",
13
+ "Utility",
14
+ "Other"
15
+ ];
4
16
  var ContextError = class _ContextError extends Error {
5
17
  constructor(message, code, statusCode, helpUrl) {
6
18
  super(message);
@@ -12,6 +24,57 @@ var ContextError = class _ContextError extends Error {
12
24
  }
13
25
  };
14
26
 
27
+ // src/client/resources/developer.ts
28
+ var Developer = class {
29
+ constructor(client) {
30
+ this.client = client;
31
+ }
32
+ /**
33
+ * Update a tool listing's metadata (name, description, category).
34
+ *
35
+ * Requires an API key belonging to the tool's owner.
36
+ *
37
+ * @param toolId - The UUID of the tool to update
38
+ * @param updates - Fields to update (at least one required)
39
+ * @returns The updated tool metadata
40
+ *
41
+ * @throws {ContextError} If authentication fails or the caller does not own the tool
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const updated = await client.developer.updateTool("tool-uuid", {
46
+ * description: "Updated description with better showcase prompts",
47
+ * category: "crypto",
48
+ * });
49
+ * console.log(updated.updatedAt);
50
+ * ```
51
+ */
52
+ async updateTool(toolId, updates) {
53
+ if (!toolId) {
54
+ throw new ContextError("toolId is required");
55
+ }
56
+ if (updates.name === void 0 && updates.description === void 0 && updates.category === void 0) {
57
+ throw new ContextError(
58
+ "At least one field required: name, description, or category"
59
+ );
60
+ }
61
+ if (updates.category !== void 0 && updates.category !== null && !ALLOWED_TOOL_CATEGORIES.includes(updates.category)) {
62
+ throw new ContextError(
63
+ `category must be one of: ${ALLOWED_TOOL_CATEGORIES.join(", ")}`
64
+ );
65
+ }
66
+ const encodedToolId = encodeURIComponent(toolId);
67
+ return this.client._fetch(
68
+ `/api/v1/tools/${encodedToolId}`,
69
+ {
70
+ method: "PATCH",
71
+ body: JSON.stringify(updates)
72
+ },
73
+ { retry: false }
74
+ );
75
+ }
76
+ };
77
+
15
78
  // src/client/resources/discovery.ts
16
79
  var Discovery = class {
17
80
  constructor(client) {
@@ -236,6 +299,47 @@ var Query = class {
236
299
  constructor(client) {
237
300
  this.client = client;
238
301
  }
302
+ normalizeResult(result) {
303
+ const candidate = result;
304
+ if (candidate.outcomeType === "clarification_required" && "clarification" in candidate && candidate.clarification) {
305
+ return candidate;
306
+ }
307
+ if (candidate.outcomeType === "capability_miss" && "capabilityMiss" in candidate && candidate.capabilityMiss) {
308
+ return candidate;
309
+ }
310
+ return {
311
+ ...candidate,
312
+ outcomeType: "answer"
313
+ };
314
+ }
315
+ buildPolicyErrorEvent(params) {
316
+ if (params.clarificationPolicy !== "error") {
317
+ return;
318
+ }
319
+ if (params.result.outcomeType === "clarification_required") {
320
+ return {
321
+ type: "error",
322
+ error: params.result.response,
323
+ code: "clarification_required",
324
+ reasonCode: "clarification_required",
325
+ outcomeType: "clarification_required",
326
+ clarification: params.result.clarification,
327
+ querySession: params.result.querySession
328
+ };
329
+ }
330
+ if (params.result.outcomeType === "capability_miss") {
331
+ return {
332
+ type: "error",
333
+ error: params.result.response,
334
+ code: "capability_miss",
335
+ reasonCode: "capability_miss",
336
+ outcomeType: "capability_miss",
337
+ capabilityMiss: params.result.capabilityMiss,
338
+ querySession: params.result.querySession
339
+ };
340
+ }
341
+ return void 0;
342
+ }
239
343
  buildSyntheticTraceFromRunResult(params) {
240
344
  const timeline = params.toolsUsed.map((tool, index) => ({
241
345
  stepType: "tool-call",
@@ -378,6 +482,7 @@ var Query = class {
378
482
  async run(options) {
379
483
  const opts = typeof options === "string" ? { query: options } : options;
380
484
  let terminalError;
485
+ let finalResult;
381
486
  for await (const event of this.stream(opts)) {
382
487
  if (event.type === "error") {
383
488
  terminalError = {
@@ -389,9 +494,12 @@ var Query = class {
389
494
  continue;
390
495
  }
391
496
  if (event.type === "done") {
392
- return event.result;
497
+ finalResult = event.result;
393
498
  }
394
499
  }
500
+ if (finalResult) {
501
+ return finalResult;
502
+ }
395
503
  if (terminalError) {
396
504
  throw new ContextError(terminalError.error, terminalError.code);
397
505
  }
@@ -443,7 +551,11 @@ var Query = class {
443
551
  body: JSON.stringify({
444
552
  query: opts.query,
445
553
  tools: opts.tools,
446
- modelId: opts.modelId,
554
+ resumeFrom: opts.resumeFrom,
555
+ forkFrom: opts.forkFrom,
556
+ clarificationPolicy: opts.clarificationPolicy,
557
+ answerModelId: opts.answerModelId,
558
+ responseShape: opts.responseShape,
447
559
  includeData: opts.includeData,
448
560
  includeDataUrl: opts.includeDataUrl,
449
561
  includeDeveloperTrace: opts.includeDeveloperTrace,
@@ -481,22 +593,31 @@ var Query = class {
481
593
  return event;
482
594
  }
483
595
  if (event.type === "done") {
596
+ const normalizedResult = this.normalizeResult(event.result);
484
597
  let mergedTrace = this.mergeDeveloperTrace(
485
598
  aggregatedTrace,
486
- event.result.developerTrace
599
+ normalizedResult.developerTrace
487
600
  );
488
601
  if (!mergedTrace && opts.includeDeveloperTrace) {
489
602
  mergedTrace = statusTimeline.length > 0 ? this.buildSyntheticTraceFromStreamStatus({
490
603
  statusTimeline,
491
- toolsUsed: event.result.toolsUsed,
492
- durationMs: event.result.durationMs
604
+ toolsUsed: normalizedResult.toolsUsed,
605
+ durationMs: normalizedResult.durationMs
493
606
  }) : this.buildSyntheticTraceFromRunResult({
494
- toolsUsed: event.result.toolsUsed,
495
- durationMs: event.result.durationMs
607
+ toolsUsed: normalizedResult.toolsUsed,
608
+ durationMs: normalizedResult.durationMs
496
609
  });
497
610
  }
498
611
  if (mergedTrace) {
499
- event.result.developerTrace = mergedTrace;
612
+ normalizedResult.developerTrace = mergedTrace;
613
+ }
614
+ event.result = normalizedResult;
615
+ const policyErrorEvent = this.buildPolicyErrorEvent({
616
+ result: normalizedResult,
617
+ clarificationPolicy: opts.clarificationPolicy
618
+ });
619
+ if (policyErrorEvent) {
620
+ return policyErrorEvent;
500
621
  }
501
622
  }
502
623
  return event;
@@ -551,6 +672,10 @@ var ContextClient = class {
551
672
  requestTimeoutMs;
552
673
  streamTimeoutMs;
553
674
  _closed = false;
675
+ /**
676
+ * Developer resource for managing tool listings (contributor/developer concerns).
677
+ */
678
+ developer;
554
679
  /**
555
680
  * Discovery resource for searching tools
556
681
  */
@@ -592,6 +717,7 @@ var ContextClient = class {
592
717
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
593
718
  this.requestTimeoutMs = requestTimeoutMs;
594
719
  this.streamTimeoutMs = streamTimeoutMs;
720
+ this.developer = new Developer(this);
595
721
  this.discovery = new Discovery(this);
596
722
  this.tools = new Tools(this);
597
723
  this.query = new Query(this);
@@ -750,8 +876,10 @@ var ContextClient = class {
750
876
  }
751
877
  };
752
878
 
879
+ exports.ALLOWED_TOOL_CATEGORIES = ALLOWED_TOOL_CATEGORIES;
753
880
  exports.ContextClient = ContextClient;
754
881
  exports.ContextError = ContextError;
882
+ exports.Developer = Developer;
755
883
  exports.Discovery = Discovery;
756
884
  exports.Query = Query;
757
885
  exports.Tools = Tools;