@knightcodeai/cli 0.19.0 → 0.21.1

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.
Files changed (88) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/dist/bundle/chunks/anthropic-messages-EY2CGNFR.js +6 -0
  3. package/dist/bundle/chunks/azure-openai-responses-KGI5F27P.js +2 -0
  4. package/dist/bundle/chunks/bedrock-converse-stream.js +8 -5
  5. package/dist/bundle/chunks/chunk-424HQSAU.js +10 -0
  6. package/dist/bundle/chunks/{chunk-3QKQRXCZ.js → chunk-5O4LFJS7.js} +4 -4
  7. package/dist/bundle/chunks/chunk-5ZES34QN.js +2 -0
  8. package/dist/bundle/chunks/chunk-BLHRVYHG.js +9 -0
  9. package/dist/bundle/chunks/{chunk-RZDCZFG7.js → chunk-EJS73Z3L.js} +1 -1
  10. package/dist/bundle/chunks/chunk-F22TYE3W.js +7 -0
  11. package/dist/bundle/chunks/{chunk-C62RVFZT.js → chunk-FSAHEI7P.js} +6 -6
  12. package/dist/bundle/chunks/chunk-K6AEXKBA.js +30 -0
  13. package/dist/bundle/chunks/chunk-O2HKRIDR.js +16 -0
  14. package/dist/bundle/chunks/{chunk-KRDGPCZM.js → chunk-OLW2IS5G.js} +2 -2
  15. package/dist/bundle/chunks/chunk-QEVQ7EIS.js +2 -0
  16. package/dist/bundle/chunks/chunk-RRNUW65U.js +4 -0
  17. package/dist/bundle/chunks/{chunk-AA6MLWWY.js → chunk-TOEFTX4C.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-IIS4JDFO.js → chunk-TOGJI36I.js} +147 -134
  19. package/dist/bundle/chunks/chunk-VLP4ZJF6.js +2 -0
  20. package/dist/bundle/chunks/{chunk-2QXPCKUQ.js → chunk-VQBEVSZH.js} +1 -1
  21. package/dist/bundle/chunks/chunk-XDEOVQPJ.js +108 -0
  22. package/dist/bundle/chunks/chunk-YDO6TXKD.js +2 -0
  23. package/dist/bundle/chunks/{cli-SIPOI5P3.js → cli-CIRIEG66.js} +1 -1
  24. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-H3D4RYQQ.js +2 -0
  25. package/dist/bundle/chunks/{easter-egg-3d-BVWLABZN.js → easter-egg-3d-YLN6ACWS.js} +1 -1
  26. package/dist/bundle/chunks/{execute-6UIHV2DR.js → execute-P6HIB6ET.js} +1 -1
  27. package/dist/bundle/chunks/{google-generative-ai-RFCCCXK6.js → google-generative-ai-C3FLXY47.js} +1 -1
  28. package/dist/bundle/chunks/{google-vertex-WN3JAZCD.js → google-vertex-AXIPR47C.js} +1 -1
  29. package/dist/bundle/chunks/knightcode-messages-LTLKDRGW.js +8 -0
  30. package/dist/bundle/chunks/{llama-cpp-classify-FEWC5ZIH.js → llama-cpp-classify-J2EJVDIZ.js} +1 -1
  31. package/dist/bundle/chunks/mistral-conversations-N2UBIKKR.js +5 -0
  32. package/dist/bundle/chunks/{openai-codex-responses-3Y7R752C.js → openai-codex-responses-REOHLPAA.js} +1 -1
  33. package/dist/bundle/chunks/openai-completions-XONAGXPC.js +7 -0
  34. package/dist/bundle/chunks/{openai-decisions-MLI3SMFN.js → openai-decisions-ACB7F6SS.js} +1 -1
  35. package/dist/bundle/chunks/{openai-responses-BNRA5YTD.js → openai-responses-OHJS7C73.js} +1 -1
  36. package/dist/bundle/chunks/{openrouter-images-JWVDFQJ4.js → openrouter-images-SMNBEOPV.js} +1 -1
  37. package/dist/bundle/chunks/{runtime-AROIXIAV.js → runtime-WNZGA67H.js} +1 -1
  38. package/dist/bundle/chunks/typesafe-system-one-KB2F2YGV.js +2 -0
  39. package/dist/bundle/chunks/{virtual-modules-IYKQM2DL.js → virtual-modules-EU3BUTGN.js} +1 -1
  40. package/dist/bundle/cli-runtime.js +1 -1
  41. package/dist/core/export-html/template.css +9 -0
  42. package/dist/core/export-html/template.js +165 -45
  43. package/docs/app-server.md +168 -14
  44. package/docs/app-server.schema.json +7505 -1860
  45. package/docs/cli.md +3 -3
  46. package/docs/containerization.md +2 -2
  47. package/docs/custom-provider.md +2 -0
  48. package/docs/declarative-views.md +118 -0
  49. package/docs/desktop.md +109 -4
  50. package/docs/docs.json +4 -0
  51. package/docs/environment-variables.md +1 -0
  52. package/docs/extensions.md +7 -3
  53. package/docs/keybindings.md +1 -0
  54. package/docs/mcp.md +16 -2
  55. package/docs/message-types.md +1 -1
  56. package/docs/models-configuration.md +9 -0
  57. package/docs/models.md +8 -4
  58. package/docs/packages.md +2 -0
  59. package/docs/plan-mode.md +4 -0
  60. package/docs/settings.md +3 -3
  61. package/docs/slash-commands.md +2 -0
  62. package/docs/usage.md +23 -0
  63. package/examples/extensions/README.md +1 -0
  64. package/examples/extensions/custom-provider-anthropic/index.ts +27 -31
  65. package/examples/extensions/desktop-view.ts +72 -0
  66. package/examples/extensions/gondolin/index.ts +8 -10
  67. package/examples/extensions/gondolin/package-lock.json +20 -30
  68. package/examples/extensions/gondolin/package.json +1 -1
  69. package/examples/extensions/todo.ts +3 -0
  70. package/examples/mcp-app/server.mjs +177 -0
  71. package/package.json +1 -1
  72. package/dist/bundle/chunks/anthropic-messages-34POCEPV.js +0 -6
  73. package/dist/bundle/chunks/azure-openai-responses-ZZQJJY7B.js +0 -2
  74. package/dist/bundle/chunks/chunk-2QXUEOVY.js +0 -2
  75. package/dist/bundle/chunks/chunk-4CRI7BMY.js +0 -2
  76. package/dist/bundle/chunks/chunk-BTRAS32C.js +0 -7
  77. package/dist/bundle/chunks/chunk-FA32X2UJ.js +0 -7
  78. package/dist/bundle/chunks/chunk-GPHSXYPT.js +0 -2
  79. package/dist/bundle/chunks/chunk-JSLSFYBX.js +0 -108
  80. package/dist/bundle/chunks/chunk-OQHO44L6.js +0 -16
  81. package/dist/bundle/chunks/chunk-R7X2RAGR.js +0 -30
  82. package/dist/bundle/chunks/chunk-T47GKPON.js +0 -11
  83. package/dist/bundle/chunks/chunk-UU4A4NV5.js +0 -2
  84. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-QHP5M5HC.js +0 -2
  85. package/dist/bundle/chunks/knightcode-messages-57CV36UV.js +0 -8
  86. package/dist/bundle/chunks/mistral-conversations-GIGX6BW6.js +0 -5
  87. package/dist/bundle/chunks/openai-completions-HBXENZUJ.js +0 -7
  88. package/dist/bundle/chunks/typesafe-system-one-5LELZY2F.js +0 -2
@@ -42,7 +42,7 @@ import {
42
42
  type TextContent,
43
43
  type ThinkingContent,
44
44
  type Tool,
45
- type ToolCall,
45
+ ToolCallArgumentsStream,
46
46
  type ToolResultMessage,
47
47
  type TranscriptContext,
48
48
  } from "@knightcode/ai";
@@ -454,8 +454,9 @@ function streamCustomAnthropic(
454
454
  const anthropicStream = client.messages.stream({ ...params }, { signal: options?.signal });
455
455
  stream.push({ type: "start", partial: output });
456
456
 
457
- type Block = (ThinkingContent | TextContent | (ToolCall & { partialJson: string })) & { index: number };
458
- const blocks = output.content as Block[];
457
+ // Stream state per Anthropic block index, kept off the content blocks so it never reaches partial messages.
458
+ // ToolCallArgumentsStream reads each argument delta once and shows the arguments parsed so far.
459
+ const openBlocks = new Map<number, { contentIndex: number; arguments?: ToolCallArgumentsStream }>();
459
460
 
460
461
  for await (const event of anthropicStream) {
461
462
  if (event.type === "message_start") {
@@ -468,15 +469,12 @@ function streamCustomAnthropic(
468
469
  calculateCost(model, output.usage);
469
470
  } else if (event.type === "content_block_start") {
470
471
  if (event.content_block.type === "text") {
471
- output.content.push({ type: "text", text: "", index: event.index } as any);
472
+ output.content.push({ type: "text", text: "" });
473
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
472
474
  stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
473
475
  } else if (event.content_block.type === "thinking") {
474
- output.content.push({
475
- type: "thinking",
476
- thinking: "",
477
- thinkingSignature: "",
478
- index: event.index,
479
- } as any);
476
+ output.content.push({ type: "thinking", thinking: "", thinkingSignature: "" });
477
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
480
478
  stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
481
479
  } else if (event.content_block.type === "tool_use") {
482
480
  output.content.push({
@@ -484,15 +482,18 @@ function streamCustomAnthropic(
484
482
  id: event.content_block.id,
485
483
  name: isOAuth ? fromClaudeCodeName(event.content_block.name, tools) : event.content_block.name,
486
484
  arguments: {},
487
- partialJson: "",
488
- index: event.index,
489
- } as any);
485
+ });
486
+ openBlocks.set(event.index, {
487
+ contentIndex: output.content.length - 1,
488
+ arguments: new ToolCallArgumentsStream(),
489
+ });
490
490
  stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
491
491
  }
492
492
  } else if (event.type === "content_block_delta") {
493
- const index = blocks.findIndex((b) => b.index === event.index);
494
- const block = blocks[index];
495
- if (!block) continue;
493
+ const open = openBlocks.get(event.index);
494
+ if (!open) continue;
495
+ const index = open.contentIndex;
496
+ const block = output.content[index]!;
496
497
 
497
498
  if (event.delta.type === "text_delta" && block.type === "text") {
498
499
  block.text += event.delta.text;
@@ -505,11 +506,8 @@ function streamCustomAnthropic(
505
506
  delta: event.delta.thinking,
506
507
  partial: output,
507
508
  });
508
- } else if (event.delta.type === "input_json_delta" && block.type === "toolCall") {
509
- (block as any).partialJson += event.delta.partial_json;
510
- try {
511
- block.arguments = JSON.parse((block as any).partialJson);
512
- } catch {}
509
+ } else if (event.delta.type === "input_json_delta" && block.type === "toolCall" && open.arguments) {
510
+ block.arguments = open.arguments.append(event.delta.partial_json);
513
511
  stream.push({
514
512
  type: "toolcall_delta",
515
513
  contentIndex: index,
@@ -520,20 +518,19 @@ function streamCustomAnthropic(
520
518
  block.thinkingSignature = (block.thinkingSignature || "") + (event.delta as any).signature;
521
519
  }
522
520
  } else if (event.type === "content_block_stop") {
523
- const index = blocks.findIndex((b) => b.index === event.index);
524
- const block = blocks[index];
525
- if (!block) continue;
521
+ const open = openBlocks.get(event.index);
522
+ if (!open) continue;
523
+ openBlocks.delete(event.index);
524
+ const index = open.contentIndex;
525
+ const block = output.content[index]!;
526
526
 
527
- delete (block as any).index;
528
527
  if (block.type === "text") {
529
528
  stream.push({ type: "text_end", contentIndex: index, content: block.text, partial: output });
530
529
  } else if (block.type === "thinking") {
531
530
  stream.push({ type: "thinking_end", contentIndex: index, content: block.thinking, partial: output });
532
- } else if (block.type === "toolCall") {
533
- try {
534
- block.arguments = JSON.parse((block as any).partialJson);
535
- } catch {}
536
- delete (block as any).partialJson;
531
+ } else if (block.type === "toolCall" && open.arguments) {
532
+ // One full parse of the complete text; malformed arguments are reported in `argumentsError`.
533
+ Object.assign(block, open.arguments.finish());
537
534
  stream.push({ type: "toolcall_end", contentIndex: index, toolCall: block, partial: output });
538
535
  }
539
536
  } else if (event.type === "message_delta") {
@@ -563,7 +560,6 @@ function streamCustomAnthropic(
563
560
  stream.push({ type: "done", reason: output.stopReason, message: output });
564
561
  stream.end();
565
562
  } catch (error) {
566
- for (const block of output.content) delete (block as any).index;
567
563
  output.stopReason = options?.signal?.aborted ? "aborted" : "error";
568
564
  output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
569
565
  stream.push({ type: "error", reason: output.stopReason, error: output });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Desktop View - declarative views in a widget, answering presses, and in a tool result
3
+ *
4
+ * Shows a widget with a toggle and a button, drawn by the terminal and by the desktop app from the same data
5
+ * (docs/declarative-views.md). In the desktop app the controls are live: a press reaches `ctx.ui.onViewEvent`, and
6
+ * the extension answers by setting the widget again. The `check_summary` tool returns a table view, which both
7
+ * clients draw in place of the tool's text output.
8
+ *
9
+ * Usage:
10
+ * knightcode --extension examples/extensions/desktop-view.ts
11
+ */
12
+
13
+ import { Type } from "@knightcode/ai";
14
+ import { defineTool, type ExtensionAPI, type ExtensionContext, type View } from "@knightcodeai/cli";
15
+
16
+ const WIDGET = "desktop-view:checks";
17
+
18
+ const checkSummary = defineTool({
19
+ name: "check_summary",
20
+ label: "Check summary",
21
+ description: "Summarize the project's checks as a table",
22
+ parameters: Type.Object({}),
23
+ async execute() {
24
+ const view: View = {
25
+ type: "table",
26
+ columns: ["check", "result"],
27
+ rows: [
28
+ ["types", "passed"],
29
+ ["tests", "2 failed"],
30
+ ],
31
+ };
32
+ return { content: [{ type: "text", text: "types: passed\ntests: 2 failed" }], details: { view } };
33
+ },
34
+ });
35
+
36
+ export default function (knightcode: ExtensionAPI) {
37
+ // Per session: the factory runs again for every thread that loads the extension.
38
+ let verbose = false;
39
+ let resets = 0;
40
+
41
+ const show = (ctx: ExtensionContext) => {
42
+ const view: View = {
43
+ type: "stack",
44
+ children: [
45
+ { type: "text", text: `Checks reset ${resets} time${resets === 1 ? "" : "s"}`, tone: "muted" },
46
+ {
47
+ type: "stack",
48
+ direction: "horizontal",
49
+ children: [
50
+ { type: "field", id: "verbose", kind: "toggle", label: "Verbose", value: verbose },
51
+ { type: "button", id: "reset", label: "Reset" },
52
+ ],
53
+ },
54
+ ],
55
+ };
56
+ ctx.ui.setWidget(WIDGET, view);
57
+ };
58
+
59
+ knightcode.registerTool(checkSummary);
60
+
61
+ knightcode.on("session_start", (_event, ctx) => {
62
+ if (!ctx.hasUI) return;
63
+ show(ctx);
64
+ // Only where widgets are interactive (the desktop app); the terminal draws the controls as text.
65
+ ctx.ui.onViewEvent?.((event) => {
66
+ if (event.widgetKey !== WIDGET) return;
67
+ if (event.id === "verbose" && typeof event.value === "boolean") verbose = event.value;
68
+ if (event.id === "reset") resets++;
69
+ show(ctx);
70
+ });
71
+ });
72
+ }
@@ -7,20 +7,20 @@
7
7
  * isolated to the VM.
8
8
  *
9
9
  * Setup:
10
- * cd packages/coding-agent/examples/extensions/gondolin
10
+ * cd packages/cli/examples/extensions/gondolin
11
11
  * npm install --ignore-scripts
12
12
  *
13
13
  * Usage:
14
14
  * cd /path/to/project
15
- * knightcode -e /path/to/knightcode/packages/coding-agent/examples/extensions/gondolin
15
+ * knightcode -e /path/to/knightcode/packages/cli/examples/extensions/gondolin
16
16
  *
17
17
  * Requirements:
18
- * - Node.js >= 23.6.0 for @KnightCodeAI/gondolin
18
+ * - Node.js >= 22.19.0 for @earendil-works/gondolin
19
19
  * - QEMU installed (for example, `brew install qemu` on macOS)
20
20
  */
21
21
 
22
22
  import path from "node:path";
23
- import { RealFSProvider, VM } from "@KnightCodeAI/gondolin";
23
+ import { RealFSProvider, VM } from "@earendil-works/gondolin";
24
24
  import type { ExtensionAPI, ExtensionContext } from "@knightcodeai/cli";
25
25
  import {
26
26
  type BashOperations,
@@ -249,7 +249,6 @@ async function executeGondolinGrep(
249
249
  const contextLines = params.context && params.context > 0 ? params.context : 0;
250
250
  const effectiveLimit = Math.max(1, params.limit ?? DEFAULT_GREP_LIMIT);
251
251
  const outputLines: string[] = [];
252
- const details: GrepToolDetails = {};
253
252
  let matchCount = 0;
254
253
  let matchLimitReached = false;
255
254
  let linesTruncated = false;
@@ -285,12 +284,14 @@ async function executeGondolinGrep(
285
284
  signal,
286
285
  );
287
286
 
288
- if (matchCount === 0) return { content: [{ type: "text", text: "No matches found" }], details: undefined };
287
+ if (matchCount === 0) return { content: [{ type: "text", text: "No matches found" }], details: { matchCount: 0 } };
289
288
 
290
289
  const rawOutput = outputLines.join("\n");
291
290
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
292
291
  const notices: string[] = [];
293
292
  let output = truncation.content;
293
+ // The renderer counts output lines when this is missing, and context lines are not matches.
294
+ const details: GrepToolDetails = { matchCount };
294
295
 
295
296
  if (matchLimitReached) {
296
297
  details.matchLimitReached = effectiveLimit;
@@ -306,10 +307,7 @@ async function executeGondolinGrep(
306
307
  }
307
308
  if (notices.length > 0) output += `\n\n[${notices.join(". ")}]`;
308
309
 
309
- return {
310
- content: [{ type: "text", text: output }],
311
- details: Object.keys(details).length > 0 ? details : undefined,
312
- };
310
+ return { content: [{ type: "text", text: output }], details };
313
311
  }
314
312
 
315
313
  function sanitizeEnv(env: NodeJS.ProcessEnv | undefined): Record<string, string> | undefined {
@@ -8,7 +8,7 @@
8
8
  "name": "knightcode-extension-gondolin",
9
9
  "version": "0.84.3",
10
10
  "dependencies": {
11
- "@KnightCodeAI/gondolin": "0.12.0"
11
+ "@earendil-works/gondolin": "0.14.0"
12
12
  }
13
13
  },
14
14
  "node_modules/@cto.af/wtf8": {
@@ -20,32 +20,31 @@
20
20
  "node": ">=20"
21
21
  }
22
22
  },
23
- "node_modules/@KnightCodeAI/gondolin": {
24
- "version": "0.12.0",
25
- "resolved": "https://registry.npmjs.org/@KnightCodeAI/gondolin/-/gondolin-0.12.0.tgz",
26
- "integrity": "sha512-BXbvzQKb5QmxY5NtthRDONJTu7+IDKbzqWGrJyyNXMP7N681Tx0Q9TK8pK1ba8nUvYQTipNJyGZOsJfYiZll1A==",
23
+ "node_modules/@earendil-works/gondolin": {
24
+ "version": "0.14.0",
25
+ "resolved": "https://registry.npmjs.org/@earendil-works/gondolin/-/gondolin-0.14.0.tgz",
26
+ "integrity": "sha512-MSw+EGcqFJeyKHdXEfm+nMRr74ytT4x8DGIohav0oPCHeVQAB9AajquM94cv2xqvIU3VDAaZZVC07IXsOY6OVQ==",
27
27
  "license": "Apache-2.0",
28
28
  "dependencies": {
29
29
  "cbor2": "^2.3.0",
30
- "node-forge": "^1.3.3",
31
30
  "ssh2": "^1.17.0",
32
- "undici": "^6.21.0"
31
+ "undici": "^6.29.0"
33
32
  },
34
33
  "bin": {
35
34
  "gondolin": "dist/bin/gondolin.js"
36
35
  },
37
36
  "engines": {
38
- "node": ">=23.6.0"
37
+ "node": ">=22.19.0"
39
38
  },
40
39
  "optionalDependencies": {
41
- "@KnightCodeAI/gondolin-krun-runner-darwin-arm64": "0.12.0",
42
- "@KnightCodeAI/gondolin-krun-runner-linux-x64": "0.12.0"
40
+ "@earendil-works/gondolin-krun-runner-darwin-arm64": "0.14.0",
41
+ "@earendil-works/gondolin-krun-runner-linux-x64": "0.14.0"
43
42
  }
44
43
  },
45
- "node_modules/@KnightCodeAI/gondolin-krun-runner-darwin-arm64": {
46
- "version": "0.12.0",
47
- "resolved": "https://registry.npmjs.org/@KnightCodeAI/gondolin-krun-runner-darwin-arm64/-/gondolin-krun-runner-darwin-arm64-0.12.0.tgz",
48
- "integrity": "sha512-ftDlusht4PcT7Y3TuPrZIKrCXy3isiBTVMvlXYK0pcud2uXY6uwFTGeunYgP+8ND/60ddb+MImqbfmkcK8B84A==",
44
+ "node_modules/@earendil-works/gondolin-krun-runner-darwin-arm64": {
45
+ "version": "0.14.0",
46
+ "resolved": "https://registry.npmjs.org/@earendil-works/gondolin-krun-runner-darwin-arm64/-/gondolin-krun-runner-darwin-arm64-0.14.0.tgz",
47
+ "integrity": "sha512-fvuhz1CZPaEbmXC0luZoXd1obAb0bjaaGJz33Jo63bhMlosYunAahItakh7cU0dnTlNFzufOSnpGOPqtuYjnpQ==",
49
48
  "cpu": [
50
49
  "arm64"
51
50
  ],
@@ -58,10 +57,10 @@
58
57
  "gondolin-krun-runner": "bin/gondolin-krun-runner"
59
58
  }
60
59
  },
61
- "node_modules/@KnightCodeAI/gondolin-krun-runner-linux-x64": {
62
- "version": "0.12.0",
63
- "resolved": "https://registry.npmjs.org/@KnightCodeAI/gondolin-krun-runner-linux-x64/-/gondolin-krun-runner-linux-x64-0.12.0.tgz",
64
- "integrity": "sha512-RRYsgwe2r5ApKmFNy469QgwnyjAHpAs9XANdWpTd9ol4iUYOY3sX7e0xIooAKxd+ktxGI4N/xRWicwGen3D/Ow==",
60
+ "node_modules/@earendil-works/gondolin-krun-runner-linux-x64": {
61
+ "version": "0.14.0",
62
+ "resolved": "https://registry.npmjs.org/@earendil-works/gondolin-krun-runner-linux-x64/-/gondolin-krun-runner-linux-x64-0.14.0.tgz",
63
+ "integrity": "sha512-91iw4smoOH/yOoAuQS6N2UZOxqSFwRhtk8rTyrokXBoWiWcveAzg4kHvnLcm40ZlNrWwzIDFrere4bkZeGDQcA==",
65
64
  "cpu": [
66
65
  "x64"
67
66
  ],
@@ -134,15 +133,6 @@
134
133
  "license": "MIT",
135
134
  "optional": true
136
135
  },
137
- "node_modules/node-forge": {
138
- "version": "1.4.0",
139
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
140
- "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
141
- "license": "(BSD-3-Clause OR GPL-2.0)",
142
- "engines": {
143
- "node": ">= 6.13.0"
144
- }
145
- },
146
136
  "node_modules/safer-buffer": {
147
137
  "version": "2.1.2",
148
138
  "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -173,9 +163,9 @@
173
163
  "license": "Unlicense"
174
164
  },
175
165
  "node_modules/undici": {
176
- "version": "6.26.0",
177
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz",
178
- "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==",
166
+ "version": "6.29.1",
167
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz",
168
+ "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==",
179
169
  "license": "MIT",
180
170
  "engines": {
181
171
  "node": ">=18.17"
@@ -14,6 +14,6 @@
14
14
  ]
15
15
  },
16
16
  "dependencies": {
17
- "@KnightCodeAI/gondolin": "0.12.0"
17
+ "@earendil-works/gondolin": "0.14.0"
18
18
  }
19
19
  }
@@ -273,6 +273,9 @@ export default function (knightcode: ExtensionAPI) {
273
273
 
274
274
  case "clear":
275
275
  return new Text(theme.fg("success", "✓ ") + theme.fg("muted", "Cleared all todos"), 0, 0);
276
+
277
+ default:
278
+ return new Text(result.content[0]?.type === "text" ? theme.fg("error", result.content[0].text) : "", 0, 0);
276
279
  }
277
280
  },
278
281
  });
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * A stdio MCP server with one MCP App: a counter. It needs no dependencies (newline-delimited JSON-RPC over stdio).
4
+ *
5
+ * - `show_counter` (model and app): shows the counter; its `_meta.ui.resourceUri` names the app, `ui://counter/app.html`.
6
+ * - `increment` (app only, `_meta.ui.visibility: ["app"]`): changes the count, so outside Full access the desktop
7
+ * asks the user before it runs.
8
+ * - `read_count` (read-only): what the count is; an app may call it without asking.
9
+ *
10
+ * Add it to `~/.knightcode/agent/mcp.json`:
11
+ *
12
+ * { "mcpServers": { "counter": { "command": "node", "args": ["/path/to/server.mjs"], "exposure": "direct" } } }
13
+ *
14
+ * In the desktop app, ask KnightCode to show the counter: the tool's row draws the app. In the terminal, which draws
15
+ * no apps, the tool answers with its text.
16
+ */
17
+
18
+ import { createInterface } from "node:readline";
19
+
20
+ const APP_URI = "ui://counter/app.html";
21
+ const APP_MIME = "text/html;profile=mcp-app";
22
+ let count = 0;
23
+
24
+ const APP_HTML = `<!doctype html>
25
+ <meta charset="utf-8">
26
+ <style>
27
+ body { margin: 0; padding: 12px 14px; font: 14px var(--font-sans, system-ui); color: var(--color-text-primary, #111);
28
+ background: var(--color-background-primary, transparent); }
29
+ .row { display: flex; align-items: center; gap: 10px; }
30
+ output { font: 600 22px var(--font-mono, monospace); min-width: 3ch; }
31
+ button { font: inherit; padding: 4px 12px; border-radius: var(--border-radius-md, 8px);
32
+ border: 1px solid var(--color-border-primary, #ccc); background: var(--color-background-secondary, #f4f4f4);
33
+ color: inherit; cursor: pointer; }
34
+ #status { margin-top: 8px; color: var(--color-text-secondary, #666); font-size: 12px; }
35
+ </style>
36
+ <div class="row">
37
+ <span>Count</span><output id="count">…</output>
38
+ <button id="add">Add one</button>
39
+ <button id="tell">Tell KnightCode</button>
40
+ </div>
41
+ <div id="status"></div>
42
+ <script>
43
+ const pending = new Map();
44
+ let next = 0;
45
+ const request = (method, params) =>
46
+ new Promise((resolve, reject) => {
47
+ const id = ++next;
48
+ pending.set(id, { resolve, reject });
49
+ parent.postMessage({ jsonrpc: "2.0", id, method, params }, "*");
50
+ });
51
+ const notify = (method, params) => parent.postMessage({ jsonrpc: "2.0", method, params }, "*");
52
+ const show = (n) => (document.getElementById("count").textContent = String(n));
53
+ const status = (text) => (document.getElementById("status").textContent = text);
54
+ const theme = (context) => {
55
+ for (const [name, value] of Object.entries(context?.styles?.variables ?? {}))
56
+ if (value) document.documentElement.style.setProperty(name, value);
57
+ };
58
+ window.addEventListener("message", (event) => {
59
+ const message = event.data;
60
+ if (!message || message.jsonrpc !== "2.0") return;
61
+ if (message.id !== undefined && !message.method) {
62
+ const waiter = pending.get(message.id);
63
+ pending.delete(message.id);
64
+ if (message.error) waiter?.reject(new Error(message.error.message));
65
+ else waiter?.resolve(message.result);
66
+ return;
67
+ }
68
+ if (message.method === "ui/notifications/tool-result") show(message.params?.structuredContent?.count ?? "?");
69
+ else if (message.method === "ui/notifications/tool-cancelled") status("The call was cancelled");
70
+ else if (message.method === "ui/notifications/host-context-changed") theme(message.params);
71
+ else if (message.method === "ui/resource-teardown") parent.postMessage({ jsonrpc: "2.0", id: message.id, result: {} }, "*");
72
+ else if (message.method === "ping") parent.postMessage({ jsonrpc: "2.0", id: message.id, result: {} }, "*");
73
+ });
74
+ document.getElementById("add").onclick = async () => {
75
+ try {
76
+ const result = await request("tools/call", { name: "increment", arguments: { by: 1 } });
77
+ show(result.structuredContent.count);
78
+ status("");
79
+ } catch (error) {
80
+ status(error.message);
81
+ }
82
+ };
83
+ document.getElementById("tell").onclick = async () => {
84
+ const now = document.getElementById("count").textContent;
85
+ try {
86
+ await request("ui/message", { role: "user", content: [{ type: "text", text: "The counter app shows " + now + "." }] });
87
+ status("Sent");
88
+ } catch (error) {
89
+ status(error.message);
90
+ }
91
+ };
92
+ (async () => {
93
+ const init = await request("ui/initialize", {
94
+ protocolVersion: "2026-01-26",
95
+ appInfo: { name: "counter", version: "1.0.0" },
96
+ appCapabilities: { availableDisplayModes: ["inline"] },
97
+ });
98
+ theme(init.hostContext);
99
+ notify("ui/notifications/initialized", {});
100
+ notify("ui/notifications/size-changed", { height: document.documentElement.scrollHeight });
101
+ })();
102
+ </script>`;
103
+
104
+ const tools = [
105
+ {
106
+ name: "show_counter",
107
+ description: "Show the counter app with the current count.",
108
+ inputSchema: { type: "object", properties: {} },
109
+ annotations: { readOnlyHint: true },
110
+ _meta: { ui: { resourceUri: APP_URI } },
111
+ },
112
+ {
113
+ name: "increment",
114
+ description: "Add to the counter.",
115
+ inputSchema: { type: "object", properties: { by: { type: "number" } } },
116
+ _meta: { ui: { visibility: ["app"] } },
117
+ },
118
+ {
119
+ name: "read_count",
120
+ description: "Read the counter.",
121
+ inputSchema: { type: "object", properties: {} },
122
+ annotations: { readOnlyHint: true },
123
+ },
124
+ ];
125
+
126
+ const countResult = (text) => ({ content: [{ type: "text", text }], structuredContent: { count } });
127
+
128
+ function answer(method, params) {
129
+ switch (method) {
130
+ case "initialize":
131
+ return {
132
+ protocolVersion: params?.protocolVersion ?? "2025-11-25",
133
+ capabilities: { tools: {}, resources: {} },
134
+ serverInfo: { name: "counter-app", version: "1.0.0" },
135
+ };
136
+ case "ping":
137
+ return {};
138
+ case "tools/list":
139
+ return { tools };
140
+ case "tools/call":
141
+ if (params?.name === "show_counter") return countResult(`The counter is at ${count}.`);
142
+ if (params?.name === "increment") {
143
+ count += Number(params.arguments?.by ?? 1);
144
+ return countResult(`The counter is now ${count}.`);
145
+ }
146
+ if (params?.name === "read_count") return countResult(String(count));
147
+ throw Object.assign(new Error(`no tool ${params?.name}`), { code: -32602 });
148
+ case "resources/list":
149
+ return { resources: [{ uri: APP_URI, name: "Counter app", mimeType: APP_MIME }] };
150
+ case "resources/read":
151
+ if (params?.uri !== APP_URI) throw Object.assign(new Error(`no resource ${params?.uri}`), { code: -32002 });
152
+ return {
153
+ contents: [
154
+ { uri: APP_URI, mimeType: APP_MIME, text: APP_HTML, _meta: { ui: { prefersBorder: true, csp: {} } } },
155
+ ],
156
+ };
157
+ default:
158
+ throw Object.assign(new Error(`method not found: ${method}`), { code: -32601 });
159
+ }
160
+ }
161
+
162
+ const write = (message) => process.stdout.write(`${JSON.stringify(message)}\n`);
163
+ createInterface({ input: process.stdin }).on("line", (line) => {
164
+ let message;
165
+ try {
166
+ message = JSON.parse(line);
167
+ } catch {
168
+ return;
169
+ }
170
+ // Notifications (no id) need no answer.
171
+ if (message.id === undefined || message.id === null) return;
172
+ try {
173
+ write({ jsonrpc: "2.0", id: message.id, result: answer(message.method, message.params) });
174
+ } catch (error) {
175
+ write({ jsonrpc: "2.0", id: message.id, error: { code: error.code ?? -32603, message: error.message } });
176
+ }
177
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knightcodeai/cli",
3
- "version": "0.19.0",
3
+ "version": "0.21.1",
4
4
  "description": "KnightCode — a local, BYOK terminal coding agent powered by OpenRouter.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,6 +0,0 @@
1
- import { createRequire as __kcCreateRequire } from "node:module"; const require = __kcCreateRequire(import.meta.url);
2
- import{buildCopilotDynamicHeaders,hasCopilotVisionInput}from"./chunk-HWXGL4YB.js";import{ANTHROPIC_FEDERATION_RULE_ID_ENV,ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,ANTHROPIC_ORGANIZATION_ID_ENV,ANTHROPIC_SERVICE_ACCOUNT_ID_ENV,ANTHROPIC_WORKSPACE_ID_ENV}from"./chunk-HSL72VZI.js";import{Anthropic}from"./chunk-VJJ6J35N.js";import{retryProviderRequest}from"./chunk-57BPR4HF.js";import{MIN_ANSWER_TOKENS,adjustMaxTokensForThinking,buildBaseOptions,clampMaxTokensToContext,clampThinkingBudgetToAnswerRoom,getJsonSchemaToolParameters,resolveJsonSchemaStrictSampling,splitErrorToolResultImages,transformMessages}from"./chunk-GPHSXYPT.js";import{sanitizeSurrogates}from"./chunk-B2FT7RH5.js";import{headersToRecord}from"./chunk-DVFCXHT4.js";import{calculateCost,getCurrentTools,getInitialSystemMessage,getSystemMessageText,renderSystemMessageUpdate,resolveTranscript}from"./chunk-T47GKPON.js";import{getProviderEnvValue}from"./chunk-BRKUE2MD.js";import{parseJsonWithRepair,parseStreamingJson,parseToolCallArguments}from"./chunk-FA32X2UJ.js";import{AssistantMessageEventStream,appendAssistantMessageDiagnostic}from"./chunk-ETOMYYZC.js";import{getIdentityHeaders}from"./chunk-UU4A4NV5.js";import"./chunk-AHB7JV5B.js";function resolveCacheRetention(cacheRetention,env){return cacheRetention||(getProviderEnvValue("KNIGHTCODE_CACHE_RETENTION",env)==="long"?"long":"short")}function getCacheControl(model,cacheRetention,env){let retention=resolveCacheRetention(cacheRetention,env);if(retention==="none")return{retention};let ttl=retention==="long"&&getAnthropicCompat(model).supportsLongCacheRetention?"1h":void 0;return{retention,cacheControl:{type:"ephemeral",...ttl&&{ttl}}}}var claudeCodeVersion="2.1.280",claudeCodeTools=["Read","Write","Edit","Bash","Grep","Glob","AskUserQuestion","EnterPlanMode","ExitPlanMode","KillShell","NotebookEdit","Skill","Task","TaskOutput","TodoWrite","WebFetch","WebSearch"],ccToolLookup=new Map(claudeCodeTools.map(t=>[t.toLowerCase(),t])),toClaudeCodeName=name=>ccToolLookup.get(name.toLowerCase())??name,fromClaudeCodeName=(name,tools)=>{if(tools&&tools.length>0){let lowerName=name.toLowerCase(),matchedTool=tools.find(tool=>tool.name.toLowerCase()===lowerName);if(matchedTool)return matchedTool.name}return name};function convertImageBlock(image){return{type:"image",source:{type:"base64",media_type:image.mimeType,data:image.data}}}function convertContentBlocks(content){if(!content.some(c=>c.type==="image"))return sanitizeSurrogates(content.map(c=>c.text).join(`
3
- `));let blocks=content.map(block=>block.type==="text"?{type:"text",text:sanitizeSurrogates(block.text)}:convertImageBlock(block));return blocks.some(b=>b.type==="text")||blocks.unshift({type:"text",text:"(see attached image)"}),blocks}var FINE_GRAINED_TOOL_STREAMING_BETA="fine-grained-tool-streaming-2025-05-14",INTERLEAVED_THINKING_BETA="interleaved-thinking-2025-05-14",SERVER_SIDE_FALLBACK_BETA="server-side-fallback-2026-07-01",MID_CONVERSATION_OUTPUT_CONFIG_BETA="mid-conversation-output-config-2026-07-01",THINKING_BINDING_CONTROLS_BETA="thinking-binding-controls-2026-08-01",INLINE_TOOLS_BETA="inline-tools-2026-09-15",DEFERRED_TOOL_PLACEHOLDER={name:"__knightcode_deferred_placeholder__",description:"Reserved placeholder. Never available. Never call this.",input_schema:{type:"object",properties:{},required:[]},defer_loading:!0};function shouldUseServerSideFallbackBeta(model){return(model.compat?.allowedFallbackModels?.length??0)>0}function getAnthropicCompat(model){let isOpenRouter=model.provider==="openrouter"||model.baseUrl.includes("openrouter.ai");return{supportsEagerToolInputStreaming:model.compat?.supportsEagerToolInputStreaming??!0,supportsLongCacheRetention:model.compat?.supportsLongCacheRetention??!0,sendSessionAffinityHeaders:model.compat?.sendSessionAffinityHeaders??isOpenRouter,sessionAffinityFormat:model.compat?.sessionAffinityFormat??(isOpenRouter?"openrouter":void 0),supportsCacheControlOnTools:model.compat?.supportsCacheControlOnTools??!0,supportsTemperature:model.compat?.supportsTemperature??!0,allowEmptySignature:model.compat?.allowEmptySignature??!1,supportsStrictTools:model.compat?.supportsStrictTools??!1,supportsMidConvoSystemMessages:model.compat?.supportsMidConvoSystemMessages??!1,supportsMidConvoToolChanges:model.compat?.supportsMidConvoToolChanges??!1}}function mergeHeaders(...headerSources){let merged={};for(let headers of headerSources)headers&&Object.assign(merged,headers);return merged}function hasHeader(headers,name){if(!headers)return!1;let expected=name.toLowerCase();for(let[key,value]of Object.entries(headers))if(key.toLowerCase()===expected&&value!==null&&value.trim().length>0)return!0;return!1}function hasRequestAuth(apiKey,headers){return!!apiKey||hasHeader(headers,"authorization")||hasHeader(headers,"x-api-key")||hasHeader(headers,"cf-aig-authorization")}function assertRequestAuth(provider,apiKey,headers){if(!hasRequestAuth(apiKey,headers))throw new Error(`No API key for provider: ${provider}`)}var KnightCodeAnthropic=class extends Anthropic{_shouldResolveDefaultCredentials(){return!1}};function getAnthropicFederation(model,apiKey,headers,env){if(model.provider!=="anthropic"||hasRequestAuth(apiKey,headers))return;let federationRuleId=getProviderEnvValue(ANTHROPIC_FEDERATION_RULE_ID_ENV,env),organizationId=getProviderEnvValue(ANTHROPIC_ORGANIZATION_ID_ENV,env),identityTokenFile=getProviderEnvValue(ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,env);if(!(!federationRuleId||!organizationId||!identityTokenFile))return{organization_id:organizationId,workspace_id:getProviderEnvValue(ANTHROPIC_WORKSPACE_ID_ENV,env),authentication:{type:"oidc_federation",federation_rule_id:federationRuleId,service_account_id:getProviderEnvValue(ANTHROPIC_SERVICE_ACCOUNT_ID_ENV,env),identity_token:{source:"file",path:identityTokenFile}}}}var defaultFetchKey={},federationClients=new WeakMap,ANTHROPIC_MESSAGE_EVENTS=new Set(["message_start","message_delta","message_stop","content_block_start","content_block_delta","content_block_stop"]);function flushSseEvent(state){if(!state.event&&state.data.length===0)return null;let event={event:state.event,data:state.data.join(`
4
- `),raw:[...state.raw]};return state.event=null,state.data=[],state.raw=[],event}function decodeSseLine(line,state){if(line==="")return flushSseEvent(state);if(state.raw.push(line),line.startsWith(":"))return null;let delimiterIndex=line.indexOf(":"),fieldName=delimiterIndex===-1?line:line.slice(0,delimiterIndex),value=delimiterIndex===-1?"":line.slice(delimiterIndex+1);return value.startsWith(" ")&&(value=value.slice(1)),fieldName==="event"?state.event=value:fieldName==="data"&&state.data.push(value),null}function nextLineBreakIndex(text){let carriageReturnIndex=text.indexOf("\r"),newlineIndex=text.indexOf(`
5
- `);return carriageReturnIndex===-1?newlineIndex:newlineIndex===-1?carriageReturnIndex:Math.min(carriageReturnIndex,newlineIndex)}function consumeLine(text){let lineBreakIndex=nextLineBreakIndex(text);if(lineBreakIndex===-1)return null;let nextIndex=lineBreakIndex+1;return text[lineBreakIndex]==="\r"&&text[nextIndex]===`
6
- `&&(nextIndex+=1),{line:text.slice(0,lineBreakIndex),rest:text.slice(nextIndex)}}async function*iterateSseMessages(body,signal){let reader=body.getReader(),decoder=new TextDecoder,state={event:null,data:[],raw:[]},buffer="";try{for(;;){if(signal?.aborted)throw new Error("Request was aborted");let{value,done}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});let consumed2=consumeLine(buffer);for(;consumed2;){buffer=consumed2.rest;let event=decodeSseLine(consumed2.line,state);event&&(yield event),consumed2=consumeLine(buffer)}}buffer+=decoder.decode();let consumed=consumeLine(buffer);for(;consumed;){buffer=consumed.rest;let event=decodeSseLine(consumed.line,state);event&&(yield event),consumed=consumeLine(buffer)}if(buffer.length>0){let event=decodeSseLine(buffer,state);event&&(yield event)}let trailingEvent=flushSseEvent(state);trailingEvent&&(yield trailingEvent)}finally{reader.releaseLock()}}async function*iterateAnthropicEvents(response,signal){if(!response.body)throw new Error("Attempted to iterate over an Anthropic response with no body");let sawMessageStart=!1,sawMessageEnd=!1;for await(let sse of iterateSseMessages(response.body,signal)){if(sse.event==="error")throw new Error(sse.data);if(ANTHROPIC_MESSAGE_EVENTS.has(sse.event??""))try{let event=parseJsonWithRepair(sse.data);event.type==="message_start"?sawMessageStart=!0:event.type==="message_stop"&&(sawMessageEnd=!0),yield event}catch(error){let message=error instanceof Error?error.message:String(error);throw new Error(`Could not parse Anthropic SSE event ${sse.event}: ${message}; data=${sse.data}; raw=${sse.raw.join("\\n")}`)}}if(sawMessageStart&&!sawMessageEnd)throw new Error("Anthropic stream ended before message_stop")}var stream=(model,context,options)=>{let stream2=new AssistantMessageEventStream,normalizedContext=resolveTranscript(context,getAnthropicCompat(model).supportsMidConvoSystemMessages),currentTools=getCurrentTools(normalizedContext.messages);return(async()=>{let output={role:"assistant",content:[],api:model.api,provider:model.provider,model:model.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"pending",timestamp:Date.now()};try{let client,isOAuth,usageModel=model,inputTransformations;if(options?.client)client=options.client,isOAuth=!1;else{let apiKey=options?.apiKey,federation=getAnthropicFederation(model,apiKey,options?.headers,options?.env);federation||assertRequestAuth(model.provider,apiKey,options?.headers);let copilotDynamicHeaders;if(model.provider==="github-copilot"){let hasImages=hasCopilotVisionInput(normalizedContext.messages);copilotDynamicHeaders=buildCopilotDynamicHeaders({messages:normalizedContext.messages,hasImages})}let cacheSessionId=resolveCacheRetention(options?.cacheRetention,options?.env)==="none"?void 0:options?.sessionId,created=createClient(model,apiKey,options?.headers,options?.fetch,copilotDynamicHeaders,cacheSessionId,federation,options?.identity);client=created.client,isOAuth=created.isOAuthToken}let requestOptions={...options?.signal?{signal:options.signal}:{},...options?.timeoutMs!==void 0?{timeout:options.timeoutMs}:{},maxRetries:0},disablePreferredStrictTools=!1,response;for(;;){options?.signal?.throwIfAborted();let built=buildParams(model,normalizedContext,isOAuth,options,disablePreferredStrictTools);built.providerThinkingLevel&&(output.providerThinkingLevel=built.providerThinkingLevel);let params=built.params,nextParams=await options?.onPayload?.(params,model);nextParams!==void 0&&(params={...nextParams,stream:!0});try{response=await retryProviderRequest(()=>client.beta.messages.create(params,requestOptions).asResponse(),{maxRetries:options?.maxRetries,maxRetryDelayMs:options?.maxRetryDelayMs,signal:options?.signal});break}catch(error){if(options?.signal?.aborted||disablePreferredStrictTools||!built.hasPreferredStrictTools||!(error instanceof Anthropic.APIError)||error.status!==400||error.type!=="invalid_request_error"||!["The compiled grammar is too large","Too many strict tools (","Schemas contains too many parameters with union types ("].some(message=>error.message.includes(message)))throw error;disablePreferredStrictTools=!0,appendAssistantMessageDiagnostic(output,{type:"anthropic_strict_tools_fallback",timestamp:Date.now(),error:{message:error.message},details:{reason:"strict_tool_limits_exceeded"}})}}await options?.onResponse?.({status:response.status,headers:headersToRecord(response.headers)},model),stream2.push({type:"start",partial:output});let blocks=output.content;for await(let event of iterateAnthropicEvents(response,options?.signal))if(await options?.onProviderStreamEvent?.(event,model),event.type==="message_start"){output.responseId=event.message.id;let transformations=event.message.input_transformations;Array.isArray(transformations)&&(inputTransformations=transformations);let responseModel=event.message.model;responseModel!==model.id&&(output.responseModel=responseModel);let fallbackCost=responseModel===model.id?void 0:model.compat?.allowedFallbackModels?.find(fallback=>fallback.provider===model.provider&&fallback.model===responseModel)?.cost;usageModel=fallbackCost?{...model,id:responseModel,cost:fallbackCost}:model,output.usage.input=event.message.usage.input_tokens||0,output.usage.output=event.message.usage.output_tokens||0,output.usage.cacheRead=event.message.usage.cache_read_input_tokens||0,output.usage.cacheWrite=event.message.usage.cache_creation_input_tokens||0,output.usage.cacheWrite1h=event.message.usage.cache_creation?.ephemeral_1h_input_tokens||0,output.usage.totalTokens=output.usage.input+output.usage.output+output.usage.cacheRead+output.usage.cacheWrite,calculateCost(usageModel,output.usage)}else if(event.type==="content_block_start"){if(event.content_block.type==="fallback"){if(output.content.length>0)throw new Error("Anthropic performed an unsupported mid-output model fallback");continue}if(event.content_block.type==="text"){let block={type:"text",text:event.content_block.text??"",index:event.index};output.content.push(block),stream2.push({type:"text_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="thinking"){let block={type:"thinking",thinking:event.content_block.thinking??"",thinkingSignature:event.content_block.signature??"",index:event.index};output.content.push(block),stream2.push({type:"thinking_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="redacted_thinking"){let block={type:"thinking",thinking:"[Reasoning redacted]",thinkingSignature:event.content_block.data,redacted:!0,index:event.index};output.content.push(block),stream2.push({type:"thinking_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="tool_use"){let block={type:"toolCall",id:event.content_block.id,name:isOAuth?fromClaudeCodeName(event.content_block.name,currentTools):event.content_block.name,arguments:event.content_block.input??{},partialJson:"",index:event.index};output.content.push(block),stream2.push({type:"toolcall_start",contentIndex:output.content.length-1,partial:output})}}else if(event.type==="content_block_delta"){if(event.delta.type==="text_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="text"&&(block.text+=event.delta.text,stream2.push({type:"text_delta",contentIndex:index,delta:event.delta.text,partial:output}))}else if(event.delta.type==="thinking_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="thinking"&&(block.thinking+=event.delta.thinking,stream2.push({type:"thinking_delta",contentIndex:index,delta:event.delta.thinking,partial:output}))}else if(event.delta.type==="input_json_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="toolCall"&&(block.partialJson+=event.delta.partial_json,block.arguments=parseStreamingJson(block.partialJson),stream2.push({type:"toolcall_delta",contentIndex:index,delta:event.delta.partial_json,partial:output}))}else if(event.delta.type==="signature_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="thinking"&&(block.thinkingSignature=block.thinkingSignature||"",block.thinkingSignature+=event.delta.signature)}}else if(event.type==="content_block_stop"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&(delete block.index,block.type==="text"?stream2.push({type:"text_end",contentIndex:index,content:block.text,partial:output}):block.type==="thinking"?stream2.push({type:"thinking_end",contentIndex:index,content:block.thinking,partial:output}):block.type==="toolCall"&&(Object.assign(block,parseToolCallArguments(block.partialJson)),delete block.partialJson,stream2.push({type:"toolcall_end",contentIndex:index,toolCall:block,partial:output})))}else if(event.type==="message_delta"){let transformations=event.input_transformations;if(Array.isArray(transformations)&&(inputTransformations=transformations),event.delta.stop_reason){output.rawStopReason=event.delta.stop_reason;let stopReasonResult=mapStopReason(event.delta.stop_reason,event.delta.stop_details);output.stopReason=stopReasonResult.stopReason,stopReasonResult.errorMessage&&(output.errorMessage=stopReasonResult.errorMessage)}if(event.usage){event.usage.input_tokens!=null&&(output.usage.input=event.usage.input_tokens),event.usage.output_tokens!=null&&(output.usage.output=event.usage.output_tokens),event.usage.cache_read_input_tokens!=null&&(output.usage.cacheRead=event.usage.cache_read_input_tokens),event.usage.cache_creation_input_tokens!=null&&(output.usage.cacheWrite=event.usage.cache_creation_input_tokens);let cacheCreation=event.usage.cache_creation;cacheCreation?.ephemeral_1h_input_tokens!=null&&(output.usage.cacheWrite1h=cacheCreation.ephemeral_1h_input_tokens);let thinkingTokens=event.usage.output_tokens_details?.thinking_tokens;thinkingTokens!=null&&(output.usage.reasoning=thinkingTokens)}output.usage.totalTokens=output.usage.input+output.usage.output+output.usage.cacheRead+output.usage.cacheWrite,calculateCost(usageModel,output.usage)}if(options?.signal?.aborted)throw new Error("Request was aborted");if(output.stopReason==="pending")throw new Error("Anthropic stream ended without a stop reason");if(output.stopReason==="aborted"||output.stopReason==="error")throw new Error(output.errorMessage||"An unknown error occurred");inputTransformations&&inputTransformations.length>0&&appendAssistantMessageDiagnostic(output,{type:"anthropic_input_transformations",timestamp:Date.now(),details:{transformations:inputTransformations.map(transformation=>({type:transformation.type??void 0,path:transformation.path??void 0,reason:transformation.reason??void 0}))}}),stream2.push({type:"done",reason:output.stopReason,message:output}),stream2.end()}catch(error){for(let block of output.content)delete block.index,delete block.partialJson;output.stopReason=options?.signal?.aborted?"aborted":"error",output.errorMessage=error instanceof Error?error.message:JSON.stringify(error),stream2.push({type:"error",reason:output.stopReason,error:output}),stream2.end()}})(),stream2};function mapThinkingLevelToEffort(model,level){let mapped=level?model.thinkingLevelMap?.[level]:void 0;if(typeof mapped=="string")return mapped;switch(level){case"minimal":case"low":return"low";case"medium":return"medium";case"high":return"high";default:return"high"}}var streamSimple=(model,context,options)=>{getAnthropicFederation(model,options?.apiKey,options?.headers,options?.env)||assertRequestAuth(model.provider,options?.apiKey,options?.headers);let base={...buildBaseOptions(model,context,options,options?.apiKey),toolChoice:options?.toolChoice};if(!options?.reasoning)return stream(model,context,{...base,thinkingEnabled:!1});if(model.compat?.forceAdaptiveThinking===!0){let effort=mapThinkingLevelToEffort(model,options.reasoning);return stream(model,context,{...base,thinkingEnabled:!0,effort})}let adjusted=adjustMaxTokensForThinking(base.maxTokens,model.maxTokens,options.reasoning,options.thinkingBudgets),maxTokens=clampMaxTokensToContext(model,context,adjusted.maxTokens),thinkingBudgetTokens=clampThinkingBudgetToAnswerRoom(adjusted.thinkingBudget,maxTokens);return thinkingBudgetTokens<MIN_ANSWER_TOKENS?stream(model,context,{...base,maxTokens,thinkingEnabled:!1}):stream(model,context,{...base,maxTokens,thinkingEnabled:!0,thinkingBudgetTokens})};function isOAuthToken(apiKey){return apiKey.includes("sk-ant-oat")}function createClient(model,apiKey,optionsHeaders,fetch,dynamicHeaders,sessionId,federation,identity){if(model.provider==="github-copilot")return{client:new KnightCodeAnthropic({apiKey:null,authToken:apiKey??null,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders:mergeHeaders(getIdentityHeaders(model,identity),{accept:"application/json","anthropic-dangerous-direct-browser-access":"true"},model.headers,dynamicHeaders,optionsHeaders)}),isOAuthToken:!1};if(apiKey&&isOAuthToken(apiKey))return{client:new KnightCodeAnthropic({apiKey:null,authToken:apiKey,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders:mergeHeaders({accept:"application/json","anthropic-dangerous-direct-browser-access":"true","user-agent":`claude-cli/${claudeCodeVersion}`,"x-app":"cli"},model.headers,optionsHeaders)}),isOAuthToken:!0};let compat=getAnthropicCompat(model),sessionAffinityHeaders={};if(sessionId&&compat.sendSessionAffinityHeaders){let header=compat.sessionAffinityFormat==="openrouter"?"x-session-id":"x-session-affinity";sessionAffinityHeaders[header]=sessionId}let defaultHeaders=mergeHeaders(getIdentityHeaders(model,identity),{accept:"application/json","anthropic-dangerous-direct-browser-access":"true"},sessionAffinityHeaders,model.headers,optionsHeaders);if(federation){let fetchKey=fetch??defaultFetchKey,clients=federationClients.get(fetchKey);clients||(clients=new Map,federationClients.set(fetchKey,clients));let key=JSON.stringify([model.baseUrl,federation]),client2=clients.get(key);return client2||(client2=new KnightCodeAnthropic({apiKey:null,authToken:null,config:federation,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch}),clients.set(key,client2)),{client:client2.withOptions({defaultHeaders}),isOAuthToken:!1}}return{client:new KnightCodeAnthropic({apiKey:apiKey??null,authToken:null,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders}),isOAuthToken:!1}}function getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options){let configuredFeatures;for(let headers of[model.headers,options?.headers])for(let[name,value]of Object.entries(headers??{}))name.toLowerCase()==="anthropic-beta"&&(configuredFeatures=value);if(configuredFeatures===null)return[];if(configuredFeatures!==void 0)return[...new Set(configuredFeatures.split(",").map(feature=>feature.trim()).filter(feature=>feature.length>0))];let features=[];return isOAuthToken2&&features.push("claude-code-20250219","oauth-2025-04-20"),shouldUseFineGrainedToolStreamingBeta(model,context)&&features.push(FINE_GRAINED_TOOL_STREAMING_BETA),model.reasoning&&options?.thinkingEnabled===!0&&(options.interleavedThinking??!0)&&model.compat?.forceAdaptiveThinking!==!0&&features.push(INTERLEAVED_THINKING_BETA),shouldUseServerSideFallbackBeta(model)&&features.push(SERVER_SIDE_FALLBACK_BETA),model.compat?.supportsMidConvoEffort===!0&&features.push(MID_CONVERSATION_OUTPUT_CONFIG_BETA,THINKING_BINDING_CONTROLS_BETA),nativeToolChanges&&features.push(INLINE_TOOLS_BETA),[...new Set(features)]}function buildParams(model,context,isOAuthToken2,options,disablePreferredStrictTools=!1){let{cacheControl}=getCacheControl(model,options?.cacheRetention,options?.env),compat=getAnthropicCompat(model),initialSystemMessage=getInitialSystemMessage(context.messages),initialSystemText=initialSystemMessage?getSystemMessageText(initialSystemMessage):"",transformedMessages=transformMessages(context.messages,model,normalizeToolCallId),conversationMessages=initialSystemMessage?transformedMessages.slice(1):transformedMessages,hasPreferredStrictTools=!1,convertToolDefinitions=(tools,toolCacheControl2)=>{let definitions=convertTools(tools,isOAuthToken2,compat.supportsEagerToolInputStreaming,compat.supportsStrictTools,toolCacheControl2,disablePreferredStrictTools);return definitions.some((definition,index)=>{let config=tools[index].constrainedSampling;return definition.strict===!0&&config&&config.type==="json_schema"&&config.strict==="prefer"})&&(hasPreferredStrictTools=!0),definitions},initialTools=initialSystemMessage?.toolsAdded??[],nativeToolChanges=compat.supportsMidConvoSystemMessages&&compat.supportsMidConvoToolChanges&&initialTools.length>0,thinkingDisabled=model.reasoning&&options?.thinkingEnabled===!1&&model.thinkingLevelMap?.off!==null,effortMessages=model.compat?.supportsMidConvoEffort===!0&&!thinkingDisabled,converted=convertMessages(conversationMessages,isOAuthToken2,cacheControl,compat.allowEmptySignature,effortMessages?model.provider:void 0,nativeToolChanges?convertToolDefinitions:void 0),activeEffort=options?.effort??"high",betaFeatures=getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options),params={model:model.id,messages:effortMessages?insertThinkingLevelMessages(converted,activeEffort):converted.messages,max_tokens:options?.maxTokens??model.maxTokens,stream:!0,...betaFeatures.length>0?{betas:betaFeatures}:{}};isOAuthToken2?(params.system=[{type:"text",text:"You are Claude Code, Anthropic's official CLI for Claude.",...cacheControl?{cache_control:cacheControl}:{}}],initialSystemText&&params.system.push({type:"text",text:sanitizeSurrogates(initialSystemText),...cacheControl?{cache_control:cacheControl}:{}})):initialSystemText&&(params.system=[{type:"text",text:sanitizeSurrogates(initialSystemText),...cacheControl?{cache_control:cacheControl}:{}}]),options?.temperature!==void 0&&!options?.thinkingEnabled&&model.compat?.supportsMidConvoEffort!==!0&&compat.supportsTemperature&&(params.temperature=options.temperature);let toolCacheControl=compat.supportsCacheControlOnTools?cacheControl:void 0;if(nativeToolChanges)params.tools=[...convertToolDefinitions(initialTools,toolCacheControl),DEFERRED_TOOL_PLACEHOLDER];else{let tools=getCurrentTools(context.messages);tools.length>0&&(params.tools=convertToolDefinitions(tools,toolCacheControl))}if(thinkingDisabled)params.thinking={type:"disabled"};else if(effortMessages)params.thinking={type:"adaptive",display:options?.thinkingDisplay??"summarized",block_binding:{prefix_mismatch_behavior:"drop_block"}},params.output_config={effort:"high"};else if(model.reasoning&&options?.thinkingEnabled){let display=options.thinkingDisplay??"summarized";model.compat?.forceAdaptiveThinking===!0?(params.thinking={type:"adaptive",display},options.effort&&(params.output_config={effort:options.effort})):params.thinking={type:"enabled",budget_tokens:options.thinkingBudgetTokens||1024,display}}if(options?.metadata){let userId=options.metadata.user_id;typeof userId=="string"&&(params.metadata={user_id:userId})}options?.toolChoice&&(typeof options.toolChoice=="string"?params.tool_choice={type:options.toolChoice}:params.tool_choice=options.toolChoice);let allowedFallbackModels=model.compat?.allowedFallbackModels;return allowedFallbackModels&&allowedFallbackModels.length>0&&(params.fallbacks=allowedFallbackModels.map(fallback=>({model:fallback.model}))),{params,hasPreferredStrictTools,providerThinkingLevel:effortMessages?activeEffort:void 0}}function normalizeToolCallId(id){return id.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,64)}function convertToolResult(msg,content=msg.content){return{type:"tool_result",tool_use_id:msg.toolCallId,content:convertContentBlocks(content),is_error:msg.isError}}function convertMessages(transformedMessages,isOAuthToken2,cacheControl,allowEmptySignature=!1,managedProvider,convertToolDefinitions){let params=[],assistantLevels=new Map,pendingSystemMessages=[],flushPendingSystemMessages=()=>{params.push(...pendingSystemMessages),pendingSystemMessages.length=0};for(let i=0;i<transformedMessages.length;i++){let msg=transformedMessages[i];if(msg.role==="system"){let text=renderSystemMessageUpdate(msg),blocks=[];if(text.length>0&&blocks.push({type:"text",text:sanitizeSurrogates(text)}),convertToolDefinitions){let added=msg.toolsAdded??[],redefined=new Set(added.map(tool=>tool.name));for(let tool of msg.toolsRemoved??[])redefined.has(tool.name)||blocks.push({type:"tool_removal",tool:{type:"tool_reference",name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name}});for(let definition of convertToolDefinitions(added))blocks.push({type:"tool_addition",tool:{type:"tool_definition",definition}})}blocks.length>0&&pendingSystemMessages.push({role:"system",content:blocks})}else if(msg.role==="user")if(typeof msg.content=="string")msg.content.trim().length>0&&params.push({role:"user",content:sanitizeSurrogates(msg.content)});else{let filteredBlocks=msg.content.map(item=>item.type==="text"?{type:"text",text:sanitizeSurrogates(item.text)}:convertImageBlock(item)).filter(b=>b.type==="text"?b.text.trim().length>0:!0);if(filteredBlocks.length===0)continue;params.push({role:"user",content:filteredBlocks})}else if(msg.role==="assistant"){flushPendingSystemMessages();let blocks=[];for(let block of msg.content)if(block.type==="text"){if(block.text.trim().length===0)continue;blocks.push({type:"text",text:sanitizeSurrogates(block.text)})}else if(block.type==="thinking"){if(block.redacted){blocks.push({type:"redacted_thinking",data:block.thinkingSignature});continue}let thinkingSignature=block.thinkingSignature,hasThinkingSignature=!!thinkingSignature&&thinkingSignature.trim().length>0;if(block.thinking.trim().length===0&&!hasThinkingSignature)continue;hasThinkingSignature?blocks.push({type:"thinking",thinking:sanitizeSurrogates(block.thinking),signature:thinkingSignature}):blocks.push(allowEmptySignature?{type:"thinking",thinking:sanitizeSurrogates(block.thinking),signature:""}:{type:"text",text:sanitizeSurrogates(block.thinking)})}else block.type==="toolCall"&&blocks.push({type:"tool_use",id:block.id,name:isOAuthToken2?toClaudeCodeName(block.name):block.name,input:block.arguments??{}});if(blocks.length===0)continue;let messageIndex=params.length;params.push({role:"assistant",content:blocks}),managedProvider!==void 0&&msg.api==="anthropic-messages"&&msg.provider===managedProvider&&isAnthropicEffort(msg.providerThinkingLevel)&&assistantLevels.set(messageIndex,msg.providerThinkingLevel)}else if(msg.role==="toolResult"){let toolResults=[],errorImages=[],j=i;for(;j<transformedMessages.length&&transformedMessages[j].role==="toolResult";){let toolMsg=transformedMessages[j],split=splitErrorToolResultImages(toolMsg);toolResults.push(convertToolResult(toolMsg,split?.content)),split&&errorImages.push({type:"text",text:split.caption},...split.images.map(convertImageBlock)),j++}i=j-1,params.push({role:"user",content:[...toolResults,...errorImages]})}}if(flushPendingSystemMessages(),cacheControl&&params.length>0){let lastMessage=params[params.length-1];if(lastMessage.role==="user"||lastMessage.role==="system")if(Array.isArray(lastMessage.content)){let lastBlock=lastMessage.content[lastMessage.content.length-1];lastBlock&&(lastBlock.type==="text"||lastBlock.type==="image"||lastBlock.type==="tool_result"||lastBlock.type==="tool_addition"||lastBlock.type==="tool_removal")&&(lastBlock.cache_control=cacheControl)}else typeof lastMessage.content=="string"&&(lastMessage.content=[{type:"text",text:lastMessage.content,cache_control:cacheControl}])}return{messages:params,assistantLevels}}function isAnthropicEffort(value){return value==="low"||value==="medium"||value==="high"||value==="xhigh"||value==="max"}function insertThinkingLevelMessages(converted,activeEffort){let messages=[];for(let index=0;index<converted.messages.length;index++){let historicalEffort=converted.assistantLevels.get(index);historicalEffort!==void 0&&messages.push({role:"system",content:[],output_config:{effort:historicalEffort}}),messages.push(converted.messages[index])}return messages.push({role:"system",content:[],output_config:{effort:activeEffort}}),messages}function shouldUseFineGrainedToolStreamingBeta(model,context){return getCurrentTools(context.messages).length>0&&!getAnthropicCompat(model).supportsEagerToolInputStreaming}var ANTHROPIC_STRICT_UNSUPPORTED_KEYWORDS=new Set(["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf","maxItems","uniqueItems","minContains","maxContains","minProperties","maxProperties"]),ANTHROPIC_STRICT_STRING_FORMATS=new Set(["date-time","time","date","duration","email","hostname","uri","ipv4","ipv6","uuid"]),isAnthropicStrictUnsupportedKeyword=(key,value)=>ANTHROPIC_STRICT_UNSUPPORTED_KEYWORDS.has(key)?!0:key==="minItems"?value!==0&&value!==1:key==="format"?typeof value!="string"||!ANTHROPIC_STRICT_STRING_FORMATS.has(value):!1;function convertTools(tools,isOAuthToken2,supportsEagerToolInputStreaming,supportsStrictTools,cacheControl,disablePreferredStrictTools=!1){return tools?tools.map((tool,index)=>{let config=tool.constrainedSampling,strict=disablePreferredStrictTools&&config&&config.type==="json_schema"&&config.strict==="prefer"?void 0:resolveJsonSchemaStrictSampling(tool,supportsStrictTools,isAnthropicStrictUnsupportedKeyword),parameters=getJsonSchemaToolParameters(tool,strict),schema=parameters,legacyInputSchema={type:"object",properties:schema.properties??{},required:schema.required??[]},inputSchema=strict===!0?{...parameters,...legacyInputSchema}:legacyInputSchema;return{name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name,description:tool.description,...supportsEagerToolInputStreaming?{eager_input_streaming:!0}:{},...strict===!0?{strict:!0}:{},input_schema:inputSchema,...cacheControl&&index===tools.length-1?{cache_control:cacheControl}:{}}}):[]}function mapStopReason(reason,stopDetails){switch(reason){case"end_turn":return{stopReason:"stop"};case"max_tokens":return{stopReason:"length"};case"tool_use":return{stopReason:"toolUse"};case"refusal":return{stopReason:"error",errorMessage:stopDetails?.explanation||"The model refused to complete the request"};case"pause_turn":return{stopReason:"stop"};case"stop_sequence":return{stopReason:"stop"};case"sensitive":return{stopReason:"error",errorMessage:"Provider stopped with: sensitive"};default:throw new Error(`Unhandled stop reason: ${reason}`)}}export{stream,streamSimple};