@nodetool-ai/runtime 0.8.2 → 0.8.4-a13.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 (295) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/canvas-codec.d.ts +3 -0
  3. package/dist/canvas-codec.d.ts.map +1 -0
  4. package/dist/canvas-codec.js +12 -0
  5. package/dist/canvas-codec.js.map +1 -0
  6. package/dist/context-packer.d.ts +1 -1
  7. package/dist/context-packer.js +1 -1
  8. package/dist/context.d.ts +3 -0
  9. package/dist/context.d.ts.map +1 -1
  10. package/dist/context.js +16 -5
  11. package/dist/context.js.map +1 -1
  12. package/dist/host-binaries.d.ts.map +1 -1
  13. package/dist/host-binaries.js +4 -3
  14. package/dist/host-binaries.js.map +1 -1
  15. package/dist/http-client-spans.d.ts +9 -0
  16. package/dist/http-client-spans.d.ts.map +1 -0
  17. package/dist/http-client-spans.js +88 -0
  18. package/dist/http-client-spans.js.map +1 -0
  19. package/dist/image-codec.d.ts +6 -3
  20. package/dist/image-codec.d.ts.map +1 -1
  21. package/dist/image-codec.js +17 -17
  22. package/dist/image-codec.js.map +1 -1
  23. package/dist/image-model-tasks.d.ts +20 -0
  24. package/dist/image-model-tasks.d.ts.map +1 -1
  25. package/dist/image-model-tasks.js +44 -1
  26. package/dist/image-model-tasks.js.map +1 -1
  27. package/dist/index.d.ts +4 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +4 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/media-ref-bytes.d.ts.map +1 -1
  32. package/dist/media-ref-bytes.js +31 -6
  33. package/dist/media-ref-bytes.js.map +1 -1
  34. package/dist/otlp-json-trace-exporter.d.ts +89 -0
  35. package/dist/otlp-json-trace-exporter.d.ts.map +1 -0
  36. package/dist/otlp-json-trace-exporter.js +155 -0
  37. package/dist/otlp-json-trace-exporter.js.map +1 -0
  38. package/dist/prompt-asset-refs.d.ts +12 -1
  39. package/dist/prompt-asset-refs.d.ts.map +1 -1
  40. package/dist/prompt-asset-refs.js +9 -4
  41. package/dist/prompt-asset-refs.js.map +1 -1
  42. package/dist/providers/antigravity-provider.d.ts +113 -0
  43. package/dist/providers/antigravity-provider.d.ts.map +1 -0
  44. package/dist/providers/antigravity-provider.js +527 -0
  45. package/dist/providers/antigravity-provider.js.map +1 -0
  46. package/dist/providers/api-route-provider.d.ts +22 -0
  47. package/dist/providers/api-route-provider.d.ts.map +1 -0
  48. package/dist/providers/api-route-provider.js +56 -0
  49. package/dist/providers/api-route-provider.js.map +1 -0
  50. package/dist/providers/azure-openai-provider.d.ts +69 -0
  51. package/dist/providers/azure-openai-provider.d.ts.map +1 -0
  52. package/dist/providers/azure-openai-provider.js +233 -0
  53. package/dist/providers/azure-openai-provider.js.map +1 -0
  54. package/dist/providers/base-provider.d.ts.map +1 -1
  55. package/dist/providers/base-provider.js +41 -4
  56. package/dist/providers/base-provider.js.map +1 -1
  57. package/dist/providers/bedrock-provider.d.ts +52 -0
  58. package/dist/providers/bedrock-provider.d.ts.map +1 -0
  59. package/dist/providers/bedrock-provider.js +648 -0
  60. package/dist/providers/bedrock-provider.js.map +1 -0
  61. package/dist/providers/bedrock-transport.d.ts +37 -0
  62. package/dist/providers/bedrock-transport.d.ts.map +1 -0
  63. package/dist/providers/bedrock-transport.js +186 -0
  64. package/dist/providers/bedrock-transport.js.map +1 -0
  65. package/dist/providers/chatgpt-provider.d.ts +48 -0
  66. package/dist/providers/chatgpt-provider.d.ts.map +1 -0
  67. package/dist/providers/chatgpt-provider.js +551 -0
  68. package/dist/providers/chatgpt-provider.js.map +1 -0
  69. package/dist/providers/claude-agent-provider.d.ts +29 -51
  70. package/dist/providers/claude-agent-provider.d.ts.map +1 -1
  71. package/dist/providers/claude-agent-provider.js +144 -202
  72. package/dist/providers/claude-agent-provider.js.map +1 -1
  73. package/dist/providers/claude-cli.d.ts +265 -0
  74. package/dist/providers/claude-cli.d.ts.map +1 -0
  75. package/dist/providers/claude-cli.js +488 -0
  76. package/dist/providers/claude-cli.js.map +1 -0
  77. package/dist/providers/codex-provider.d.ts +9 -2
  78. package/dist/providers/codex-provider.d.ts.map +1 -1
  79. package/dist/providers/codex-provider.js +25 -11
  80. package/dist/providers/codex-provider.js.map +1 -1
  81. package/dist/providers/context-window.d.ts.map +1 -1
  82. package/dist/providers/context-window.js +1 -0
  83. package/dist/providers/context-window.js.map +1 -1
  84. package/dist/providers/core-tools.d.ts +4 -1
  85. package/dist/providers/core-tools.d.ts.map +1 -1
  86. package/dist/providers/core-tools.js +5 -3
  87. package/dist/providers/core-tools.js.map +1 -1
  88. package/dist/providers/cost-calculator.d.ts.map +1 -1
  89. package/dist/providers/cost-calculator.js +9 -0
  90. package/dist/providers/cost-calculator.js.map +1 -1
  91. package/dist/providers/credential-check.d.ts +8 -0
  92. package/dist/providers/credential-check.d.ts.map +1 -1
  93. package/dist/providers/credential-check.js +22 -0
  94. package/dist/providers/credential-check.js.map +1 -1
  95. package/dist/providers/custom-openai-provider.d.ts.map +1 -1
  96. package/dist/providers/custom-openai-provider.js +19 -1
  97. package/dist/providers/custom-openai-provider.js.map +1 -1
  98. package/dist/providers/dreamina-pending.d.ts +22 -0
  99. package/dist/providers/dreamina-pending.d.ts.map +1 -0
  100. package/dist/providers/dreamina-pending.js +86 -0
  101. package/dist/providers/dreamina-pending.js.map +1 -0
  102. package/dist/providers/dreamina-provider.d.ts +10 -3
  103. package/dist/providers/dreamina-provider.d.ts.map +1 -1
  104. package/dist/providers/dreamina-provider.js +325 -45
  105. package/dist/providers/dreamina-provider.js.map +1 -1
  106. package/dist/providers/extension-tab.d.ts +42 -0
  107. package/dist/providers/extension-tab.d.ts.map +1 -0
  108. package/dist/providers/extension-tab.js +200 -0
  109. package/dist/providers/extension-tab.js.map +1 -0
  110. package/dist/providers/fake-provider.d.ts +3 -1
  111. package/dist/providers/fake-provider.d.ts.map +1 -1
  112. package/dist/providers/fake-provider.js +63 -9
  113. package/dist/providers/fake-provider.js.map +1 -1
  114. package/dist/providers/fake-schema-answer.d.ts +10 -0
  115. package/dist/providers/fake-schema-answer.d.ts.map +1 -0
  116. package/dist/providers/fake-schema-answer.js +64 -0
  117. package/dist/providers/fake-schema-answer.js.map +1 -0
  118. package/dist/providers/fal-provider.d.ts.map +1 -1
  119. package/dist/providers/fal-provider.js +5 -2
  120. package/dist/providers/fal-provider.js.map +1 -1
  121. package/dist/providers/gemini-web-provider.d.ts +76 -0
  122. package/dist/providers/gemini-web-provider.d.ts.map +1 -0
  123. package/dist/providers/gemini-web-provider.js +827 -0
  124. package/dist/providers/gemini-web-provider.js.map +1 -0
  125. package/dist/providers/http-transport.d.ts.map +1 -1
  126. package/dist/providers/http-transport.js +2 -0
  127. package/dist/providers/http-transport.js.map +1 -1
  128. package/dist/providers/huggingface-provider.d.ts.map +1 -1
  129. package/dist/providers/huggingface-provider.js +17 -1
  130. package/dist/providers/huggingface-provider.js.map +1 -1
  131. package/dist/providers/index.d.ts +18 -2
  132. package/dist/providers/index.d.ts.map +1 -1
  133. package/dist/providers/index.js +66 -15
  134. package/dist/providers/index.js.map +1 -1
  135. package/dist/providers/llama-provider.d.ts +8 -0
  136. package/dist/providers/llama-provider.d.ts.map +1 -1
  137. package/dist/providers/llama-provider.js +23 -5
  138. package/dist/providers/llama-provider.js.map +1 -1
  139. package/dist/providers/lmstudio-provider.js +2 -2
  140. package/dist/providers/lmstudio-provider.js.map +1 -1
  141. package/dist/providers/manifest-models.d.ts +2 -12
  142. package/dist/providers/manifest-models.d.ts.map +1 -1
  143. package/dist/providers/manifest-models.js +45 -3
  144. package/dist/providers/manifest-models.js.map +1 -1
  145. package/dist/providers/meta-provider.d.ts +53 -6
  146. package/dist/providers/meta-provider.d.ts.map +1 -1
  147. package/dist/providers/meta-provider.js +289 -6
  148. package/dist/providers/meta-provider.js.map +1 -1
  149. package/dist/providers/meta-segmentation.d.ts +27 -0
  150. package/dist/providers/meta-segmentation.d.ts.map +1 -0
  151. package/dist/providers/meta-segmentation.js +141 -0
  152. package/dist/providers/meta-segmentation.js.map +1 -0
  153. package/dist/providers/meta-transcription.d.ts +31 -0
  154. package/dist/providers/meta-transcription.d.ts.map +1 -0
  155. package/dist/providers/meta-transcription.js +187 -0
  156. package/dist/providers/meta-transcription.js.map +1 -0
  157. package/dist/providers/node-llama-cpp-provider.d.ts +6 -3
  158. package/dist/providers/node-llama-cpp-provider.d.ts.map +1 -1
  159. package/dist/providers/node-llama-cpp-provider.js +77 -62
  160. package/dist/providers/node-llama-cpp-provider.js.map +1 -1
  161. package/dist/providers/ollama-provider.d.ts +23 -0
  162. package/dist/providers/ollama-provider.d.ts.map +1 -1
  163. package/dist/providers/ollama-provider.js +140 -27
  164. package/dist/providers/ollama-provider.js.map +1 -1
  165. package/dist/providers/openai-compat/client.d.ts +10 -1
  166. package/dist/providers/openai-compat/client.d.ts.map +1 -1
  167. package/dist/providers/openai-compat/client.js +21 -3
  168. package/dist/providers/openai-compat/client.js.map +1 -1
  169. package/dist/providers/openai-compat/index.d.ts +1 -1
  170. package/dist/providers/openai-compat/index.d.ts.map +1 -1
  171. package/dist/providers/openai-compat/index.js +1 -1
  172. package/dist/providers/openai-compat/index.js.map +1 -1
  173. package/dist/providers/openai-compat/types.d.ts +4 -0
  174. package/dist/providers/openai-compat/types.d.ts.map +1 -1
  175. package/dist/providers/openai-compat-provider.d.ts +11 -0
  176. package/dist/providers/openai-compat-provider.d.ts.map +1 -1
  177. package/dist/providers/openai-compat-provider.js +47 -2
  178. package/dist/providers/openai-compat-provider.js.map +1 -1
  179. package/dist/providers/openai-provider.d.ts +15 -0
  180. package/dist/providers/openai-provider.d.ts.map +1 -1
  181. package/dist/providers/openai-provider.js +50 -12
  182. package/dist/providers/openai-provider.js.map +1 -1
  183. package/dist/providers/opper-provider.d.ts +31 -0
  184. package/dist/providers/opper-provider.d.ts.map +1 -0
  185. package/dist/providers/opper-provider.js +91 -0
  186. package/dist/providers/opper-provider.js.map +1 -0
  187. package/dist/providers/provider-request-log.d.ts.map +1 -1
  188. package/dist/providers/provider-request-log.js +3 -2
  189. package/dist/providers/provider-request-log.js.map +1 -1
  190. package/dist/providers/python-provider.d.ts.map +1 -1
  191. package/dist/providers/python-provider.js +5 -3
  192. package/dist/providers/python-provider.js.map +1 -1
  193. package/dist/providers/responses-api.d.ts +12 -2
  194. package/dist/providers/responses-api.d.ts.map +1 -1
  195. package/dist/providers/responses-api.js +54 -3
  196. package/dist/providers/responses-api.js.map +1 -1
  197. package/dist/providers/safe-url.d.ts.map +1 -1
  198. package/dist/providers/safe-url.js +8 -0
  199. package/dist/providers/safe-url.js.map +1 -1
  200. package/dist/providers/scripted-provider.d.ts.map +1 -1
  201. package/dist/providers/scripted-provider.js +6 -1
  202. package/dist/providers/scripted-provider.js.map +1 -1
  203. package/dist/providers/types.d.ts +16 -0
  204. package/dist/providers/types.d.ts.map +1 -1
  205. package/dist/providers/types.js.map +1 -1
  206. package/dist/providers/vgenv-provider.d.ts +33 -0
  207. package/dist/providers/vgenv-provider.d.ts.map +1 -0
  208. package/dist/providers/vgenv-provider.js +261 -0
  209. package/dist/providers/vgenv-provider.js.map +1 -0
  210. package/dist/providers/vllm-provider.d.ts +7 -0
  211. package/dist/providers/vllm-provider.d.ts.map +1 -1
  212. package/dist/providers/vllm-provider.js +15 -3
  213. package/dist/providers/vllm-provider.js.map +1 -1
  214. package/dist/python-bridge-base.d.ts +16 -6
  215. package/dist/python-bridge-base.d.ts.map +1 -1
  216. package/dist/python-bridge-base.js +117 -40
  217. package/dist/python-bridge-base.js.map +1 -1
  218. package/dist/python-bridge-codec.d.ts +16 -1
  219. package/dist/python-bridge-codec.d.ts.map +1 -1
  220. package/dist/python-bridge-codec.js +60 -2
  221. package/dist/python-bridge-codec.js.map +1 -1
  222. package/dist/python-bridge-framing.d.ts +10 -1
  223. package/dist/python-bridge-framing.d.ts.map +1 -1
  224. package/dist/python-bridge-framing.js +33 -8
  225. package/dist/python-bridge-framing.js.map +1 -1
  226. package/dist/python-bridge-types.d.ts +26 -5
  227. package/dist/python-bridge-types.d.ts.map +1 -1
  228. package/dist/python-bridge-types.js.map +1 -1
  229. package/dist/python-graph-resolver.d.ts.map +1 -1
  230. package/dist/python-graph-resolver.js +12 -2
  231. package/dist/python-graph-resolver.js.map +1 -1
  232. package/dist/python-node-executor.d.ts +14 -3
  233. package/dist/python-node-executor.d.ts.map +1 -1
  234. package/dist/python-node-executor.js +185 -3
  235. package/dist/python-node-executor.js.map +1 -1
  236. package/dist/python-stdio-bridge.d.ts +6 -0
  237. package/dist/python-stdio-bridge.d.ts.map +1 -1
  238. package/dist/python-stdio-bridge.js +58 -12
  239. package/dist/python-stdio-bridge.js.map +1 -1
  240. package/dist/run-trace-context.d.ts +2 -27
  241. package/dist/run-trace-context.d.ts.map +1 -1
  242. package/dist/run-trace-context.js +4 -51
  243. package/dist/run-trace-context.js.map +1 -1
  244. package/dist/run-trace-processor.d.ts +3 -0
  245. package/dist/run-trace-processor.d.ts.map +1 -1
  246. package/dist/run-trace-processor.js +63 -46
  247. package/dist/run-trace-processor.js.map +1 -1
  248. package/dist/run-trace-scope.d.ts +28 -0
  249. package/dist/run-trace-scope.d.ts.map +1 -0
  250. package/dist/run-trace-scope.js +51 -0
  251. package/dist/run-trace-scope.js.map +1 -0
  252. package/dist/run-trace-serialization.d.ts.map +1 -1
  253. package/dist/run-trace-serialization.js +4 -3
  254. package/dist/run-trace-serialization.js.map +1 -1
  255. package/dist/span-record.d.ts +4 -0
  256. package/dist/span-record.d.ts.map +1 -0
  257. package/dist/span-record.js +48 -0
  258. package/dist/span-record.js.map +1 -0
  259. package/dist/swappable-python-bridge.d.ts +6 -6
  260. package/dist/swappable-python-bridge.d.ts.map +1 -1
  261. package/dist/swappable-python-bridge.js +10 -10
  262. package/dist/swappable-python-bridge.js.map +1 -1
  263. package/dist/telemetry.d.ts +2 -2
  264. package/dist/telemetry.d.ts.map +1 -1
  265. package/dist/telemetry.js +33 -34
  266. package/dist/telemetry.js.map +1 -1
  267. package/dist/testing.d.ts +1 -1
  268. package/dist/testing.d.ts.map +1 -1
  269. package/dist/testing.js +55 -3
  270. package/dist/testing.js.map +1 -1
  271. package/dist/token-counter.d.ts +4 -13
  272. package/dist/token-counter.d.ts.map +1 -1
  273. package/dist/token-counter.js +68 -18
  274. package/dist/token-counter.js.map +1 -1
  275. package/dist/trace-context-manager.d.ts +12 -0
  276. package/dist/trace-context-manager.d.ts.map +1 -0
  277. package/dist/trace-context-manager.js +30 -0
  278. package/dist/trace-context-manager.js.map +1 -0
  279. package/dist/trace-exporters.d.ts +2 -3
  280. package/dist/trace-exporters.d.ts.map +1 -1
  281. package/dist/trace-exporters.js +5 -52
  282. package/dist/trace-exporters.js.map +1 -1
  283. package/dist/trace-semantic-conventions.d.ts +2 -0
  284. package/dist/trace-semantic-conventions.d.ts.map +1 -0
  285. package/dist/trace-semantic-conventions.js +2 -0
  286. package/dist/trace-semantic-conventions.js.map +1 -0
  287. package/dist/traced-storage.d.ts +17 -0
  288. package/dist/traced-storage.d.ts.map +1 -0
  289. package/dist/traced-storage.js +41 -0
  290. package/dist/traced-storage.js.map +1 -0
  291. package/dist/tracing-module.d.ts +3 -0
  292. package/dist/tracing-module.d.ts.map +1 -0
  293. package/dist/tracing-module.js +12 -0
  294. package/dist/tracing-module.js.map +1 -0
  295. package/package.json +37 -18
@@ -1,28 +1,25 @@
1
1
  import { getTracer } from "../telemetry.js";
2
2
  import { tracedProviderLoop, tracedToolCall, beginProviderRound, providerRoundContext, traceContentAllowed, messageTraceContent, traceTextContent } from "./loop-tracing.js";
3
3
  /**
4
- * ClaudeAgentProvider — Claude reached through the official
5
- * `@anthropic-ai/claude-agent-sdk` instead of an API key.
4
+ * ClaudeAgentProvider — Claude reached through the headless Claude Code CLI
5
+ * instead of an API key.
6
6
  *
7
7
  * Unlike {@link AnthropicProvider}, this provider sends no `ANTHROPIC_API_KEY`:
8
- * the SDK drives the bundled `claude` binary (shipped as an optional platform
9
- * dependency, so there is nothing to install on PATH) and lets it authenticate
10
- * with the machine's logged-in Claude subscription (the credentials stored
11
- * under `~/.claude`). The SDK streams `SDKMessage`s, which we translate into the
12
- * cross-provider {@link ProviderStreamItem} stream.
8
+ * it runs the user's installed `claude` executable in print mode with
9
+ * stream-json input and output (see {@link runClaudeCli}) and lets it
10
+ * authenticate with the machine's logged-in Claude subscription (the
11
+ * credentials stored under `~/.claude`). The executable is `CLAUDE_CLI_PATH`
12
+ * when set, else found on PATH or in the usual install locations. The CLI
13
+ * streams JSON messages, which we translate into the cross-provider
14
+ * {@link ProviderStreamItem} stream.
13
15
  *
14
- * `@anthropic-ai/claude-agent-sdk` is a *soft* (optional peer) dependency: it is
15
- * not installed by default and must be added with the package manager before
16
- * this provider can run. It is loaded lazily via {@link loadSdk}, so a missing
17
- * package surfaces as a clear install hint only when the provider is actually
18
- * used — the rest of the runtime, and the browser worker bundle, never need it.
19
- *
20
- * It is a *pure LLM* provider: the agentic tool loop is collapsed to a single
21
- * turn with every built-in tool disabled (`allowedTools: []`), filesystem
22
- * settings/skills disabled (`settingSources: []`), permission prompts neutered
23
- * (`permissionMode: "dontAsk"`), and the default agent system prompt fully
24
- * replaced (`systemPrompt`), so the model behaves like a plain chat completion —
25
- * text in, text (and thinking) out.
16
+ * The single-turn primitive is a *pure LLM* call: one turn with every built-in
17
+ * tool disabled (`tools: []`), filesystem settings disabled
18
+ * (`settingSources: []`), and the default agent system prompt fully replaced
19
+ * (`systemPrompt`), so the model behaves like a plain chat completion — text
20
+ * in, text (and thinking) out. {@link generateLoop} lets the CLI run its own
21
+ * agent loop over NodeTool's tools, which this process serves as an MCP server
22
+ * over the CLI's control channel.
26
23
  *
27
24
  * Session continuity: when a `threadId` is supplied the provider routes the
28
25
  * thread through a single upstream session and, on subsequent turns, resumes it
@@ -34,8 +31,8 @@ import { tracedProviderLoop, tracedToolCall, beginProviderRound, providerRoundCo
34
31
  import * as fs from "node:fs/promises";
35
32
  import * as os from "node:os";
36
33
  import * as path from "node:path";
37
- import { createLogger, importOptionalModule } from "@nodetool-ai/config";
38
- import { z } from "zod";
34
+ import { createLogger } from "@nodetool-ai/config";
35
+ import { CLAUDE_CLI_PATH_SETTING, resolveClaudeCliPath, runClaudeCli, systemLookupEnv } from "./claude-cli.js";
39
36
  import { BaseProvider, budgetStopItem, resolveTurnBudget } from "./base-provider.js";
40
37
  import { SDK_NATIVE_TOOL_REPLACEMENTS, sdkNativeReplacements } from "./core-tools.js";
41
38
  import { isProviderMessageEvent, isProviderSessionUpdate, isProviderStop } from "./types.js";
@@ -45,11 +42,11 @@ const log = createLogger("nodetool.runtime.providers.claude-agent");
45
42
  /** Replacement system prompt used when the caller supplies none. */
46
43
  const DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant.";
47
44
  /**
48
- * The Claude Agent SDK's built-in tools. Passed as `disallowedTools` on the
45
+ * Claude Code's built-in tools. Passed as `disallowedTools` on the
49
46
  * tool-free path so a "pure LLM" completion cannot execute host tools (Bash,
50
47
  * file, web) under bypassPermissions.
51
48
  */
52
- const SDK_BUILTIN_TOOLS = [
49
+ const BUILTIN_TOOLS = [
53
50
  "Bash",
54
51
  "BashOutput",
55
52
  "KillShell",
@@ -66,18 +63,16 @@ const SDK_BUILTIN_TOOLS = [
66
63
  "TodoWrite"
67
64
  ];
68
65
  /** The built-ins a tool-using turn keeps even when it offered no counterpart. */
69
- const SDK_WEB_TOOLS = ["WebSearch", "WebFetch"];
66
+ const WEB_TOOLS = ["WebSearch", "WebFetch"];
70
67
  /**
71
- * Env vars a nested Claude subscription session leaks into its children. The
72
- * SDK spawns the bundled CLI as a subprocess; left in place these would make it
73
- * believe it is itself nested. We strip them via `options.env` rather than any
74
- * raw `child_process` plumbing.
68
+ * Env vars a nested Claude subscription session leaks into its children. Left
69
+ * in place these would make the spawned CLI believe it is itself nested.
75
70
  */
76
71
  const NESTED_SESSION_ENV = /^(CLAUDECODE|CLAUDE_(CODE|SESSION|ENABLE|AFTER|AUTO)_[A-Za-z0-9_]*)$/;
77
- /** MCP server name under which NodeTool's tools are exposed to the SDK. */
72
+ /** MCP server name under which NodeTool's tools are exposed to the CLI. */
78
73
  const TOOL_SERVER_NAME = "nodetool_tools";
79
74
  /**
80
- * Turns a structured-output call may take. The SDK validates the answer
75
+ * Turns a structured-output call may take. The CLI validates the answer
81
76
  * against the schema and, when it does not match, hands the model the errors
82
77
  * and asks again — each retry is a turn. With one turn, the first answer that
83
78
  * misses the schema fails the call as `error_max_turns`.
@@ -94,57 +89,46 @@ const GUEST_TOOL_PREFIX = "tools.";
94
89
  const DEFAULT_TOOL_TURNS = 16;
95
90
  /** Name of the local plugin the user's DB skills are materialized into. */
96
91
  const SKILLS_PLUGIN_NAME = "nodetool-user-skills";
97
- /**
98
- * Lazily import the optional `@anthropic-ai/claude-agent-sdk`. The SDK is a soft
99
- * (peer) dependency — it is not bundled with the app and must be installed at
100
- * runtime from the Package Manager (Software → Claude Agent SDK), which drops it
101
- * into the user-managed optional-node `node_modules`. {@link importOptionalModule}
102
- * hides the specifier from bundlers (so the browser/worker bundle never pulls in
103
- * the Node-only SDK) and falls back to resolving it from that optional root via
104
- * `NODETOOL_OPTIONAL_NODE_MODULES`, so a plain ESM `import` (which ignores
105
- * NODE_PATH) still finds a Package-Manager install. A missing package surfaces
106
- * as a clear, actionable error at call time.
107
- */
108
- async function loadSdk() {
109
- try {
110
- return await importOptionalModule("@anthropic-ai/claude-agent-sdk");
111
- }
112
- catch (err) {
113
- throw new Error("The Claude Agent provider requires the optional " +
114
- "'@anthropic-ai/claude-agent-sdk' package, which is not installed. " +
115
- "Install it from the Package Manager (Software → Claude Agent SDK).", { cause: err });
116
- }
117
- }
118
92
  export class ClaudeAgentProvider extends BaseProvider {
119
- /** The SDK runs with bypassPermissions, so its built-in `WebSearch` is live. */
93
+ /** The CLI runs with bypassPermissions, so its built-in `WebSearch` is live. */
120
94
  get supportsNativeWebSearch() {
121
95
  return true;
122
96
  }
123
97
  static requiredSecrets() {
124
- // Auth lives in the SDK's own credential store (~/.claude), not in an env
98
+ // Auth lives in the CLI's own credential store (~/.claude), not in an env
125
99
  // secret — there is nothing for the registry to resolve.
126
100
  return [];
127
101
  }
128
102
  injectedQueryFn;
129
- injectedCreateMcpServerFn;
103
+ spawnFn;
104
+ /** `CLAUDE_CLI_PATH` from settings or the environment, when set. */
105
+ configuredCliPath;
106
+ /** The resolved executable. Cleared on failure so a later install is found. */
107
+ cliPath = null;
130
108
  /**
131
109
  * Within-process cache of the active session per thread. The persisted
132
110
  * `provider_session` column is the source of truth; this only spares a DB
133
111
  * round-trip for back-to-back turns in the same process.
134
112
  */
135
113
  sessions = new Map();
136
- constructor(_secrets = {}, options = {}) {
114
+ constructor(secrets = {}, options = {}) {
137
115
  super(PROVIDER_IDS.CLAUDE_AGENT_SDK);
138
116
  this.injectedQueryFn = options.queryFn ?? null;
139
- this.injectedCreateMcpServerFn = options.createMcpServerFn ?? null;
117
+ this.spawnFn = options.spawn;
118
+ const configured = secrets[CLAUDE_CLI_PATH_SETTING];
119
+ this.configuredCliPath = isNonEmptyString(configured)
120
+ ? configured
121
+ : (typeof process !== "undefined"
122
+ ? process.env[CLAUDE_CLI_PATH_SETTING]
123
+ : undefined) || undefined;
140
124
  }
141
- /** The subscription token is the SDK's business — never hand it to a sandbox. */
125
+ /** The subscription token is the CLI's business — never hand it to a sandbox. */
142
126
  getContainerEnv() {
143
127
  return {};
144
128
  }
145
129
  /**
146
- * Tools run inside the SDK's own agent loop ({@link generateLoop}): NodeTool's
147
- * tools are exposed as an in-process MCP server whose handlers call back into
130
+ * Tools run inside the CLI's own agent loop ({@link generateLoop}): NodeTool's
131
+ * tools are exposed as a host-served MCP server whose handlers call back into
148
132
  * the harness's tool executor. The single-turn {@link generateMessages} stays
149
133
  * tool-free.
150
134
  */
@@ -152,8 +136,8 @@ export class ClaudeAgentProvider extends BaseProvider {
152
136
  return true;
153
137
  }
154
138
  /**
155
- * Stable model aliases the SDK resolves to the latest dated model. Aliases
156
- * avoid pinning a version that ages out; the concrete model id the SDK
139
+ * Stable model aliases the CLI resolves to the latest dated model. Aliases
140
+ * avoid pinning a version that ages out; the concrete model id the CLI
157
141
  * selects is captured from the stream for accurate cost attribution.
158
142
  */
159
143
  async getAvailableLanguageModels() {
@@ -165,36 +149,40 @@ export class ClaudeAgentProvider extends BaseProvider {
165
149
  { id: "haiku", name: "Claude Haiku (subscription)", provider }
166
150
  ];
167
151
  }
168
- /** Resolve the SDK `query`, lazily importing it off the Node-only package. */
169
- async loadQuery() {
152
+ /** The CLI executable, located once per provider. */
153
+ resolveCliPath() {
154
+ this.cliPath ??= (async () => resolveClaudeCliPath(this.configuredCliPath, await systemLookupEnv()))();
155
+ const pending = this.cliPath;
156
+ pending.catch(() => {
157
+ if (this.cliPath === pending)
158
+ this.cliPath = null;
159
+ });
160
+ return pending;
161
+ }
162
+ /** The session runner: the injected one, or the real CLI. */
163
+ loadQuery() {
170
164
  if (this.injectedQueryFn)
171
165
  return this.injectedQueryFn;
172
- const mod = await loadSdk();
173
- return mod.query;
174
- }
175
- /** Resolve the SDK `createSdkMcpServer`, lazily importing the Node-only pkg. */
176
- async loadCreateMcpServer() {
177
- if (this.injectedCreateMcpServerFn)
178
- return this.injectedCreateMcpServerFn;
179
- const mod = await loadSdk();
180
- // SAFETY: the SDK's factory is generic over each tool's Zod shape; this
181
- // provider always hands it the default `AnyZodRawShape` definitions.
182
- return mod.createSdkMcpServer;
166
+ const spawn = this.spawnFn;
167
+ return (params) => (async function* (provider) {
168
+ const cliPath = await provider.resolveCliPath();
169
+ yield* runClaudeCli({ cliPath, spawn, ...params });
170
+ })(this);
183
171
  }
184
172
  /**
185
- * Run the SDK's own agent loop. Tool-free turns behave exactly like
186
- * {@link generateMessages}; with `tools` we expose them as an in-process MCP
187
- * server whose handlers call `executeTool`, let the SDK loop (`maxTurns > 1`),
173
+ * Run the CLI's own agent loop. Tool-free turns behave exactly like
174
+ * {@link generateMessages}; with `tools` we expose them as a host-served MCP
175
+ * server whose handlers call `executeTool`, let the CLI loop (`maxTurns > 1`),
188
176
  * and translate its `tool_use`/`tool_result` stream into ToolCall items and
189
177
  * persistable {@link ProviderMessageEvent}s.
190
178
  */
191
179
  async *generateLoop(args) {
192
- return yield* tracedProviderLoop(this.provider, args.model, () => this.generateSdkLoop(args));
180
+ return yield* tracedProviderLoop(this.provider, args.model, () => this.generateCliLoop(args));
193
181
  }
194
- async *generateSdkLoop(args) {
195
- // The SDK owns the loop, so honoring the budget is this override's job:
182
+ async *generateCliLoop(args) {
183
+ // The CLI owns the loop, so honoring the budget is this override's job:
196
184
  // reserve before the first turn, and again after every assistant turn that
197
- // requested tools — that is the point where the SDK will make another call.
185
+ // requested tools — that is the point where the CLI will make another call.
198
186
  // A refusal aborts the session instead of letting it spend past the cap.
199
187
  const { runBudget, turnBudget } = resolveTurnBudget(args.turnBudget);
200
188
  if (runBudget?.deadline.expired()) {
@@ -202,7 +190,7 @@ export class ClaudeAgentProvider extends BaseProvider {
202
190
  return;
203
191
  }
204
192
  // Every turn this session makes holds its own reservation, and the whole
205
- // set is settled in the `finally` below — the SDK reports one number for
193
+ // set is settled in the `finally` below — the CLI reports one number for
206
194
  // the session, not one per turn.
207
195
  const reservations = [];
208
196
  if (turnBudget) {
@@ -221,7 +209,7 @@ export class ClaudeAgentProvider extends BaseProvider {
221
209
  : sdkNativeReplacements(offered.map((t) => t.name), args.workspaceDir);
222
210
  const tools = offered.filter((t) => !replaced.has(t.name));
223
211
  if (replaced.size > 0) {
224
- log.debug("Using SDK built-ins in place of NodeTool tools", {
212
+ log.debug("Using CLI built-ins in place of NodeTool tools", {
225
213
  replaced: [...replaced]
226
214
  });
227
215
  }
@@ -230,7 +218,7 @@ export class ClaudeAgentProvider extends BaseProvider {
230
218
  // A tool dispatches either through its own `execute` or the harness
231
219
  // `executeTool`; build the MCP server when at least one route exists.
232
220
  const hasToolExecute = tools.some((t) => t.execute);
233
- // The SDK loop is stopped by aborting this controller — a terminal tool
221
+ // The CLI loop is stopped by aborting this controller — a terminal tool
234
222
  // fires it after running. Bridge the caller's signal into it too, and pass
235
223
  // its signal (overriding args.signal) down so runTurn cancels on either.
236
224
  const abortController = new AbortController();
@@ -243,12 +231,11 @@ export class ClaudeAgentProvider extends BaseProvider {
243
231
  }
244
232
  let mcp = null;
245
233
  if (tools.length > 0 && (executeTool || hasToolExecute)) {
246
- const createServer = await this.loadCreateMcpServer();
247
234
  // MCP tool results carry typed content blocks, so image-bearing results
248
- // (view_image) are returned as real image blocks the SDK hands to Claude —
249
- // no flattening to text on the agent-SDK path.
235
+ // (view_image) are returned as real image blocks the CLI hands to Claude —
236
+ // no flattening to text.
250
237
  const defs = tools.map((t) => toolDefinition(t, async (name, toolArgs) => {
251
- // The SDK runs parallel calls of one tool in the same millisecond,
238
+ // The CLI runs parallel calls of one tool in the same millisecond,
252
239
  // so a clock alone would hand them one id.
253
240
  const toolCallId = `call_${name}_${crypto.randomUUID()}`;
254
241
  const result = t.execute
@@ -258,22 +245,22 @@ export class ClaudeAgentProvider extends BaseProvider {
258
245
  : executeTool
259
246
  ? await tracedToolCall({ id: toolCallId, name, args: toolArgs }, () => executeTool({ id: toolCallId, name, args: toolArgs }))
260
247
  : `Tool "${name}" is not available`;
261
- // A terminal tool ends the SDK loop after its result is delivered.
248
+ // A terminal tool ends the CLI loop after its result is delivered.
262
249
  if (t.terminal)
263
250
  abortController.abort();
264
251
  return result;
265
252
  }, args.resolveMedia));
266
- const server = createServer({
253
+ const server = {
267
254
  name: TOOL_SERVER_NAME,
268
255
  version: "1.0.0",
269
256
  tools: defs
270
- });
257
+ };
271
258
  mcp = {
272
259
  mcpServers: { [TOOL_SERVER_NAME]: server },
273
260
  allowedTools: tools.map((t) => `${TOOL_PREFIX}${t.name}`)
274
261
  };
275
262
  }
276
- // Materialize the user's skills into a throwaway local plugin the SDK's
263
+ // Materialize the user's skills into a throwaway local plugin the CLI's
277
264
  // native skill loader discovers. Best-effort: a disk failure drops skills
278
265
  // for the turn, it does not sink it. Cleaned up in the `finally` below.
279
266
  let skillsPlugin = null;
@@ -282,13 +269,13 @@ export class ClaudeAgentProvider extends BaseProvider {
282
269
  skillsPlugin = await materializeSkillsPlugin(args.skills ?? []);
283
270
  }
284
271
  catch (err) {
285
- log.warn("Failed to materialize user skills for the SDK", {
272
+ log.warn("Failed to materialize user skills for the CLI", {
286
273
  error: err instanceof Error ? err.message : String(err)
287
274
  });
288
275
  }
289
276
  }
290
277
  const costBefore = turnBudget ? this.getTotalCost() : 0;
291
- // Set when the budget refuses a further turn: the SDK session is aborted
278
+ // Set when the budget refuses a further turn: the CLI session is aborted
292
279
  // and the stop is yielded once the stream has drained, so it stays the
293
280
  // loop's final item.
294
281
  let budgetStop = null;
@@ -307,7 +294,7 @@ export class ClaudeAgentProvider extends BaseProvider {
307
294
  cwd: args.workspaceDir,
308
295
  skillsPlugin
309
296
  });
310
- // The SDK grows the conversation internally, so reserving against the
297
+ // The CLI grows the conversation internally, so reserving against the
311
298
  // opening prompt every time would under-count every later turn — badly,
312
299
  // on a tool-heavy loop. The messages it emits are the same ones it is
313
300
  // accumulating, so the transcript is rebuilt here to reserve against.
@@ -333,7 +320,7 @@ export class ClaudeAgentProvider extends BaseProvider {
333
320
  }
334
321
  }
335
322
  finally {
336
- // The SDK reports usage once, on the terminal `result` message, so a
323
+ // The CLI reports usage once, on the terminal `result` message, so a
337
324
  // per-turn actual doesn't exist here. Reservations accumulate untouched
338
325
  // for the session's duration — conservative by construction — and the
339
326
  // real number lands when the session ends.
@@ -358,7 +345,7 @@ export class ClaudeAgentProvider extends BaseProvider {
358
345
  }
359
346
  if (args.signal)
360
347
  args.signal.removeEventListener("abort", onExternalAbort);
361
- // Remove the throwaway skills plugin. The SDK's subprocess has finished
348
+ // Remove the throwaway skills plugin. The CLI process has finished
362
349
  // reading it by now (the stream is drained), so best-effort cleanup is
363
350
  // safe; a leftover temp dir is not worth failing the turn over.
364
351
  if (skillsPlugin) {
@@ -394,7 +381,7 @@ export class ClaudeAgentProvider extends BaseProvider {
394
381
  /**
395
382
  * Shared session machinery for {@link generateMessages} and
396
383
  * {@link generateLoop}: decide resume vs fresh (best-effort, with a
397
- * full-history priming fallback), then drive one SDK turn via {@link runTurn}.
384
+ * full-history priming fallback), then drive one CLI turn via {@link runTurn}.
398
385
  * `config` selects single-turn streaming vs the tool/message-emitting loop.
399
386
  */
400
387
  async *runWithSession(args, config) {
@@ -460,11 +447,11 @@ export class ClaudeAgentProvider extends BaseProvider {
460
447
  config
461
448
  });
462
449
  }
463
- /** Drive one SDK `query` turn and translate its messages into stream items. */
450
+ /** Drive one CLI session and translate its messages into stream items. */
464
451
  async *runTurn(args, plan) {
465
- const queryFn = await this.loadQuery();
466
- // Cancellation: the SDK stops the underlying query when its AbortController
467
- // fires. Bridge the caller's signal onto a fresh controller per turn.
452
+ const queryFn = this.loadQuery();
453
+ // Cancellation: the CLI process is killed when its AbortController fires.
454
+ // Bridge the caller's signal onto a fresh controller per turn.
468
455
  const abortController = new AbortController();
469
456
  const onAbort = () => abortController.abort();
470
457
  if (args.signal) {
@@ -475,7 +462,7 @@ export class ClaudeAgentProvider extends BaseProvider {
475
462
  }
476
463
  const options = {
477
464
  systemPrompt: plan.systemPrompt,
478
- // Pass aliases ("sonnet"/"opus"/"haiku") straight through to the SDK.
465
+ // Pass aliases ("sonnet"/"opus"/"haiku") straight through to the CLI.
479
466
  model: args.model || undefined,
480
467
  // 1 for a single turn; higher when tools may drive multiple rounds.
481
468
  maxTurns: plan.config.maxTurns,
@@ -485,13 +472,13 @@ export class ClaudeAgentProvider extends BaseProvider {
485
472
  // Do NOT load repo .claude / CLAUDE.md / skills.
486
473
  settingSources: [],
487
474
  // Run without asking for permissions: never prompt AND never deny. This
488
- // lets the SDK agent use the built-ins `tools` keeps alongside
475
+ // lets the CLI agent use the built-ins `tools` keeps alongside
489
476
  // NodeTool's MCP tools. bypassPermissions requires the explicit
490
477
  // safety flag below.
491
478
  permissionMode: "bypassPermissions",
492
479
  allowDangerouslySkipPermissions: true,
493
480
  // On the tool-free path (the documented "pure LLM" primitive used by
494
- // generateMessage/generateMessages), explicitly disable the SDK's
481
+ // generateMessage/generateMessages), explicitly disable the CLI's
495
482
  // built-in tools. Under bypassPermissions they are otherwise live and
496
483
  // auto-approved, so a prompt-injected "call Bash …" in untrusted text
497
484
  // being summarized/classified would execute on the host. allowedTools is
@@ -501,23 +488,23 @@ export class ClaudeAgentProvider extends BaseProvider {
501
488
  // replaced by a built-in offered tools and has no MCP server, and
502
489
  // disabling the built-ins there would leave it with nothing.
503
490
  //
504
- // `ToolSearch` is disabled on BOTH paths. It searches tools the SDK has
491
+ // `ToolSearch` is disabled on BOTH paths. It searches tools the CLI has
505
492
  // deferred, and NodeTool defers none — it registers its handful of tools
506
493
  // in-process — so the search always comes back empty. A model that fell
507
494
  // back to it after a mis-named tool call got nothing and stalled the turn.
508
495
  // Supplied-only loops also disable Skill and keep all supplied dispatch
509
496
  // inside the caller's MCP boundary.
510
497
  disallowedTools: plan.config.providedToolsOnly
511
- ? [...SDK_BUILTIN_TOOLS, "Skill", "ToolSearch"]
498
+ ? [...BUILTIN_TOOLS, "Skill", "ToolSearch"]
512
499
  : plan.config.toolsOffered
513
500
  ? ["ToolSearch"]
514
- : [...SDK_BUILTIN_TOOLS, "ToolSearch"],
501
+ : [...BUILTIN_TOOLS, "ToolSearch"],
515
502
  tools: plan.config.builtinTools,
516
- // Only the in-process NodeTool server. Without this the child also loads
503
+ // Only the host-served NodeTool server. Without this the child also loads
517
504
  // the account's claude.ai connectors and the user's MCP servers, and
518
505
  // their tool schemas grow every request of the loop.
519
506
  strictMcpConfig: true,
520
- // Hide the skills bundled with the SDK. The plugin below names its own.
507
+ // Hide the skills bundled with the CLI. The plugin below names its own.
521
508
  skills: [],
522
509
  includePartialMessages: true,
523
510
  // Setting env REPLACES the child env, so spread process.env minus the
@@ -545,7 +532,7 @@ export class ClaudeAgentProvider extends BaseProvider {
545
532
  if (plan.config.mcp) {
546
533
  options.mcpServers = plan.config.mcp.mcpServers;
547
534
  }
548
- // Enable the SDK's native skill loop over the user's DB skills, which were
535
+ // Enable the CLI's native skill loop over the user's DB skills, which were
549
536
  // materialized into an isolated local plugin. This rides `plugins`, NOT
550
537
  // `settingSources` (kept `[]`), so no `~/.claude` / `.claude` settings or
551
538
  // `CLAUDE.md` are read; `skipMcpDiscovery` blocks any `.mcp.json` the dir
@@ -564,8 +551,8 @@ export class ClaudeAgentProvider extends BaseProvider {
564
551
  if (plan.resume) {
565
552
  options.resume = plan.resume;
566
553
  }
567
- // Log the exact wire body (sans the non-serializable AbortController, MCP
568
- // server instances, and the full env, which can carry secrets) so the
554
+ // Log the request (sans the non-serializable AbortController, the MCP
555
+ // tool handlers, and the full env, which can carry secrets) so the
569
556
  // request-log UI shows what was sent without leaking the environment.
570
557
  this.recordRequestPayload({
571
558
  prompt: plan.prompt,
@@ -579,7 +566,7 @@ export class ClaudeAgentProvider extends BaseProvider {
579
566
  : undefined
580
567
  }
581
568
  });
582
- log.debug("Claude Agent SDK request", {
569
+ log.debug("Claude CLI request", {
583
570
  model: args.model,
584
571
  resume: Boolean(plan.resume)
585
572
  });
@@ -612,7 +599,7 @@ export class ClaudeAgentProvider extends BaseProvider {
612
599
  // a "success" result flagged `is_error`. Hold its text back from the stream
613
600
  // and fail the call with it instead.
614
601
  let authFailure = null;
615
- // The SDK splits one API assistant turn into one frame per content block —
602
+ // The CLI splits one API assistant turn into one frame per content block —
616
603
  // thinking, then text, then each tool_use. Emitting a message per frame
617
604
  // would hand the consumer a text-only assistant message in the middle of a
618
605
  // tool round, which every chat surface reads as "the turn ended" (the chat
@@ -638,8 +625,8 @@ export class ClaudeAgentProvider extends BaseProvider {
638
625
  };
639
626
  };
640
627
  try {
641
- for await (const msg of queryFn({ prompt: sdkPrompt(plan.prompt), options })) {
642
- if (msg.type === "system" && msg.subtype === "init") {
628
+ for await (const msg of queryFn({ prompt: plan.prompt, options })) {
629
+ if (isSystemInit(msg)) {
643
630
  // Capture the session and surface it immediately so a streaming
644
631
  // consumer can persist it onto the assistant message it creates.
645
632
  if (isNonEmptyString(msg.model))
@@ -658,7 +645,7 @@ export class ClaudeAgentProvider extends BaseProvider {
658
645
  }
659
646
  continue;
660
647
  }
661
- if (msg.type === "stream_event") {
648
+ if (isStreamEvent(msg)) {
662
649
  if (msg.event.type === "message_start") {
663
650
  if (observedTurn) {
664
651
  closeLlm();
@@ -694,7 +681,7 @@ export class ClaudeAgentProvider extends BaseProvider {
694
681
  }
695
682
  continue;
696
683
  }
697
- if (msg.type === "assistant") {
684
+ if (isAssistant(msg)) {
698
685
  if (msg.error === "authentication_failed") {
699
686
  authFailure = finalBlocks(msg)
700
687
  .filter((block) => !block.thinking)
@@ -747,7 +734,7 @@ export class ClaudeAgentProvider extends BaseProvider {
747
734
  // Tool results come back as a user message; surface them as tool
748
735
  // messages so the harness can persist them. The assistant turn that
749
736
  // asked for them is complete by now, so it goes out first.
750
- if (msg.type === "user") {
737
+ if (isUser(msg)) {
751
738
  if (plan.config.emitMessages) {
752
739
  const flushed = flushPending();
753
740
  if (flushed)
@@ -765,7 +752,7 @@ export class ClaudeAgentProvider extends BaseProvider {
765
752
  }
766
753
  continue;
767
754
  }
768
- if (msg.type === "result") {
755
+ if (isResult(msg)) {
769
756
  // Bill every terminal result, not just the successful ones: an
770
757
  // errored/max-turns run still consumed (and was charged for) tokens.
771
758
  this.trackResultUsage(msg, resolvedModel);
@@ -801,7 +788,7 @@ export class ClaudeAgentProvider extends BaseProvider {
801
788
  yield trailing;
802
789
  }
803
790
  catch (err) {
804
- // The query can reject (auth failure, missing session, spawn error). Never
791
+ // The session can fail (auth failure, missing session, spawn error). Never
805
792
  // collapse to a generic string — surface the real message.
806
793
  if (args.signal?.aborted)
807
794
  return;
@@ -809,8 +796,8 @@ export class ClaudeAgentProvider extends BaseProvider {
809
796
  }
810
797
  finally {
811
798
  closeLlm();
812
- // Terminate the SDK query on every exit path. On normal completion the
813
- // query has already finished and this is a no-op; on an early `break`
799
+ // Terminate the CLI session on every exit path. On normal completion it
800
+ // has already finished and this is a no-op; on an early `break`
814
801
  // (consumer cancelled) or a throw it is the only thing that stops the
815
802
  // subprocess, which otherwise runs its whole agentic loop to completion.
816
803
  abortController.abort();
@@ -868,7 +855,7 @@ function num(value) {
868
855
  const CHILD_ENV_ALLOWLIST = /^CLAUDE_CODE_OAUTH_TOKEN$/;
869
856
  /**
870
857
  * A copy of the current environment with nested-session leakage stripped, for
871
- * the SDK's `options.env`. `ANTHROPIC_BASE_URL` and the `HTTP(S)_PROXY` vars are
858
+ * the CLI process. `ANTHROPIC_BASE_URL` and the `HTTP(S)_PROXY` vars are
872
859
  * preserved (they are not matched by {@link NESTED_SESSION_ENV}) so API routing
873
860
  * keeps working; `CLAUDE_CODE_OAUTH_TOKEN` is explicitly allowlisted so
874
861
  * token-based auth survives on headless hosts.
@@ -901,7 +888,7 @@ function turnBuiltins(replaced, toolsOffered, hasSkillsPlugin) {
901
888
  builtins.add(builtin);
902
889
  }
903
890
  if (toolsOffered)
904
- for (const name of SDK_WEB_TOOLS)
891
+ for (const name of WEB_TOOLS)
905
892
  builtins.add(name);
906
893
  if (hasSkillsPlugin)
907
894
  builtins.add("Skill");
@@ -917,7 +904,7 @@ function isFilesystemSafeSkillName(name) {
917
904
  return /^[a-z0-9][a-z0-9-]{0,63}$/.test(name);
918
905
  }
919
906
  /**
920
- * Write the user's DB skills to a throwaway local plugin the SDK's native skill
907
+ * Write the user's DB skills to a throwaway local plugin the CLI's native skill
921
908
  * loader can discover, and return `{ dir, names }` (or null when nothing was
922
909
  * written). The layout is the Claude Code plugin format:
923
910
  *
@@ -966,10 +953,12 @@ function partialDelta(msg) {
966
953
  if (event.type !== "content_block_delta")
967
954
  return null;
968
955
  const delta = event.delta;
969
- if (delta.type === "text_delta")
956
+ if (delta?.type === "text_delta" && isString(delta.text)) {
970
957
  return { text: delta.text };
971
- if (delta.type === "thinking_delta")
958
+ }
959
+ if (delta?.type === "thinking_delta" && isString(delta.thinking)) {
972
960
  return { thinking: delta.thinking };
961
+ }
973
962
  return null;
974
963
  }
975
964
  /** The concrete dated model id, captured from a partial `message_start`. */
@@ -1000,7 +989,7 @@ function finalBlocks(msg) {
1000
989
  * Recover the plain tool name from what the model emitted.
1001
990
  *
1002
991
  * Two prefixes get stripped, in order:
1003
- * - `mcp__<server>__`, which the SDK adds to in-process MCP tool names.
992
+ * - `mcp__<server>__`, which the CLI adds to MCP tool names.
1004
993
  * - `tools.`, the retired guest toolbelt's call form, which models still
1005
994
  * turn into a top-level tool name. Without this the call reaches no tool
1006
995
  * at all.
@@ -1037,7 +1026,7 @@ function assistantParts(msg) {
1037
1026
  }
1038
1027
  return { text, toolCalls };
1039
1028
  }
1040
- /** Extract `tool_result` blocks from a user message the SDK emitted. */
1029
+ /** Extract `tool_result` blocks from a user message the CLI emitted. */
1041
1030
  function toolResultsFromUser(msg) {
1042
1031
  const content = msg.message?.content;
1043
1032
  const out = [];
@@ -1069,15 +1058,15 @@ function toolResultText(content) {
1069
1058
  return "";
1070
1059
  }
1071
1060
  /**
1072
- * Build an in-process MCP tool definition that bridges a NodeTool
1073
- * {@link ProviderTool} to the harness's tool executor. The JSON-Schema input is
1074
- * shimmed to the Zod raw shape the SDK expects.
1061
+ * Build a host-served MCP tool definition that bridges a NodeTool
1062
+ * {@link ProviderTool} to the harness's tool executor. The JSON-Schema input
1063
+ * goes to the CLI unchanged.
1075
1064
  */
1076
1065
  function toolDefinition(tool, run, resolveMedia) {
1077
1066
  return {
1078
1067
  name: tool.name,
1079
1068
  description: tool.description ?? "",
1080
- inputSchema: jsonSchemaToZodShape(tool.inputSchema),
1069
+ inputSchema: tool.inputSchema ?? { type: "object", properties: {} },
1081
1070
  handler: async (toolArgs) => {
1082
1071
  const result = await run(tool.name, toolArgs ?? {});
1083
1072
  const resolved = Array.isArray(result) &&
@@ -1127,7 +1116,7 @@ function toMcpImageBlock(image) {
1127
1116
  }
1128
1117
  /**
1129
1118
  * Convert a tool result into MCP content blocks. Text passes through; image
1130
- * parts become MCP image blocks (the SDK forwards them to Claude as real
1119
+ * parts become MCP image blocks (the CLI forwards them to Claude as real
1131
1120
  * images). An image that can't be inlined becomes a failure note.
1132
1121
  */
1133
1122
  export function toolResultToMcpContent(result) {
@@ -1158,56 +1147,6 @@ export function toolResultToMcpContent(result) {
1158
1147
  }
1159
1148
  return blocks;
1160
1149
  }
1161
- /** Convert a JSON-Schema object's properties to a Zod raw shape. */
1162
- function jsonSchemaToZodShape(schema) {
1163
- const props = schema?.properties ?? {};
1164
- const required = new Set(schema?.required ?? []);
1165
- const shape = {};
1166
- for (const [key, raw] of Object.entries(props)) {
1167
- let zt = jsonPropToZod(raw);
1168
- if (!required.has(key))
1169
- zt = zt.optional();
1170
- shape[key] = zt;
1171
- }
1172
- return shape;
1173
- }
1174
- /** Convert one JSON-Schema property to a Zod type (the subset NodeTool uses). */
1175
- function jsonPropToZod(prop) {
1176
- const desc = isString(prop?.description) ? prop.description : undefined;
1177
- let zt;
1178
- switch (prop?.type) {
1179
- case "string":
1180
- zt = z.string();
1181
- break;
1182
- case "number":
1183
- case "integer":
1184
- zt = z.number();
1185
- break;
1186
- case "boolean":
1187
- zt = z.boolean();
1188
- break;
1189
- case "array":
1190
- zt = z.array(prop.items
1191
- ? jsonPropToZod(prop.items)
1192
- : z.unknown());
1193
- break;
1194
- case "object": {
1195
- const shape = jsonSchemaToZodShape(prop);
1196
- // Free-form objects (no declared sub-properties — e.g. add_node's
1197
- // `node_properties`) must keep arbitrary keys. `z.object({})` strips
1198
- // every key, silently dropping all node configuration on the SDK tool
1199
- // bridge; passthrough preserves the nested values.
1200
- zt =
1201
- Object.keys(shape).length > 0
1202
- ? z.object(shape)
1203
- : z.object({}).passthrough();
1204
- break;
1205
- }
1206
- default:
1207
- zt = z.unknown();
1208
- }
1209
- return desc ? zt.describe(desc) : zt;
1210
- }
1211
1150
  /**
1212
1151
  * A refused Claude login, carrying status 401 so the provider layer classifies
1213
1152
  * it as a credential failure and the UI offers to sign in again.
@@ -1220,14 +1159,14 @@ function claudeAuthError(detail) {
1220
1159
  }
1221
1160
  /** Build a descriptive Error from a non-success `result` message. */
1222
1161
  function resultError(msg) {
1223
- const parts = [`Claude Agent SDK query failed (${msg.subtype})`];
1224
- if ("errors" in msg && Array.isArray(msg.errors) && msg.errors.length) {
1162
+ const parts = [`Claude Code session failed (${msg.subtype})`];
1163
+ if (Array.isArray(msg.errors) && msg.errors.length) {
1225
1164
  parts.push(msg.errors.join("; "));
1226
1165
  }
1227
- else if ("result" in msg && isNonEmptyString(msg.result)) {
1166
+ else if (isNonEmptyString(msg.result)) {
1228
1167
  parts.push(msg.result);
1229
1168
  }
1230
- const denials = (msg).permission_denials;
1169
+ const denials = msg.permission_denials;
1231
1170
  if (Array.isArray(denials) && denials.length) {
1232
1171
  parts.push(`permission denied: ${denials.map((d) => d.tool_name ?? "?").join(", ")}`);
1233
1172
  }
@@ -1277,7 +1216,7 @@ function buildResumeDelta(messages, checkpoint) {
1277
1216
  * The prompt for a fresh session. A lone user turn is sent verbatim. When there
1278
1217
  * is pre-existing FOREIGN history (a cold thread, a model switch, or an
1279
1218
  * edited/branched conversation) we prime context ONCE with a single delimited
1280
- * user message instead of rebuilding a `Human:/Assistant:` transcript — the SDK
1219
+ * user message instead of rebuilding a `Human:/Assistant:` transcript — the CLI
1281
1220
  * cannot import external assistant turns, so this is deliberate context priming,
1282
1221
  * not a faithful reconstruction. Text and inline images are retained, while
1283
1222
  * thinking is stripped.
@@ -1348,16 +1287,19 @@ function promptBlocks(content) {
1348
1287
  }
1349
1288
  return blocks;
1350
1289
  }
1351
- function sdkPrompt(content) {
1352
- if (isString(content)) {
1353
- return content;
1354
- }
1355
- return (async function* () {
1356
- yield {
1357
- type: "user",
1358
- parent_tool_use_id: null,
1359
- message: { role: "user", content }
1360
- };
1361
- })();
1290
+ function isSystemInit(msg) {
1291
+ return msg.type === "system" && msg.subtype === "init";
1292
+ }
1293
+ function isStreamEvent(msg) {
1294
+ return msg.type === "stream_event";
1295
+ }
1296
+ function isAssistant(msg) {
1297
+ return msg.type === "assistant";
1298
+ }
1299
+ function isUser(msg) {
1300
+ return msg.type === "user";
1301
+ }
1302
+ function isResult(msg) {
1303
+ return msg.type === "result";
1362
1304
  }
1363
1305
  //# sourceMappingURL=claude-agent-provider.js.map