@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.
- package/dist/.nodetool-build-stamp +1 -1
- package/dist/canvas-codec.d.ts +3 -0
- package/dist/canvas-codec.d.ts.map +1 -0
- package/dist/canvas-codec.js +12 -0
- package/dist/canvas-codec.js.map +1 -0
- package/dist/context-packer.d.ts +1 -1
- package/dist/context-packer.js +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +16 -5
- package/dist/context.js.map +1 -1
- package/dist/host-binaries.d.ts.map +1 -1
- package/dist/host-binaries.js +4 -3
- package/dist/host-binaries.js.map +1 -1
- package/dist/http-client-spans.d.ts +9 -0
- package/dist/http-client-spans.d.ts.map +1 -0
- package/dist/http-client-spans.js +88 -0
- package/dist/http-client-spans.js.map +1 -0
- package/dist/image-codec.d.ts +6 -3
- package/dist/image-codec.d.ts.map +1 -1
- package/dist/image-codec.js +17 -17
- package/dist/image-codec.js.map +1 -1
- package/dist/image-model-tasks.d.ts +20 -0
- package/dist/image-model-tasks.d.ts.map +1 -1
- package/dist/image-model-tasks.js +44 -1
- package/dist/image-model-tasks.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/media-ref-bytes.d.ts.map +1 -1
- package/dist/media-ref-bytes.js +31 -6
- package/dist/media-ref-bytes.js.map +1 -1
- package/dist/otlp-json-trace-exporter.d.ts +89 -0
- package/dist/otlp-json-trace-exporter.d.ts.map +1 -0
- package/dist/otlp-json-trace-exporter.js +155 -0
- package/dist/otlp-json-trace-exporter.js.map +1 -0
- package/dist/prompt-asset-refs.d.ts +12 -1
- package/dist/prompt-asset-refs.d.ts.map +1 -1
- package/dist/prompt-asset-refs.js +9 -4
- package/dist/prompt-asset-refs.js.map +1 -1
- package/dist/providers/antigravity-provider.d.ts +113 -0
- package/dist/providers/antigravity-provider.d.ts.map +1 -0
- package/dist/providers/antigravity-provider.js +527 -0
- package/dist/providers/antigravity-provider.js.map +1 -0
- package/dist/providers/api-route-provider.d.ts +22 -0
- package/dist/providers/api-route-provider.d.ts.map +1 -0
- package/dist/providers/api-route-provider.js +56 -0
- package/dist/providers/api-route-provider.js.map +1 -0
- package/dist/providers/azure-openai-provider.d.ts +69 -0
- package/dist/providers/azure-openai-provider.d.ts.map +1 -0
- package/dist/providers/azure-openai-provider.js +233 -0
- package/dist/providers/azure-openai-provider.js.map +1 -0
- package/dist/providers/base-provider.d.ts.map +1 -1
- package/dist/providers/base-provider.js +41 -4
- package/dist/providers/base-provider.js.map +1 -1
- package/dist/providers/bedrock-provider.d.ts +52 -0
- package/dist/providers/bedrock-provider.d.ts.map +1 -0
- package/dist/providers/bedrock-provider.js +648 -0
- package/dist/providers/bedrock-provider.js.map +1 -0
- package/dist/providers/bedrock-transport.d.ts +37 -0
- package/dist/providers/bedrock-transport.d.ts.map +1 -0
- package/dist/providers/bedrock-transport.js +186 -0
- package/dist/providers/bedrock-transport.js.map +1 -0
- package/dist/providers/chatgpt-provider.d.ts +48 -0
- package/dist/providers/chatgpt-provider.d.ts.map +1 -0
- package/dist/providers/chatgpt-provider.js +551 -0
- package/dist/providers/chatgpt-provider.js.map +1 -0
- package/dist/providers/claude-agent-provider.d.ts +29 -51
- package/dist/providers/claude-agent-provider.d.ts.map +1 -1
- package/dist/providers/claude-agent-provider.js +144 -202
- package/dist/providers/claude-agent-provider.js.map +1 -1
- package/dist/providers/claude-cli.d.ts +265 -0
- package/dist/providers/claude-cli.d.ts.map +1 -0
- package/dist/providers/claude-cli.js +488 -0
- package/dist/providers/claude-cli.js.map +1 -0
- package/dist/providers/codex-provider.d.ts +9 -2
- package/dist/providers/codex-provider.d.ts.map +1 -1
- package/dist/providers/codex-provider.js +25 -11
- package/dist/providers/codex-provider.js.map +1 -1
- package/dist/providers/context-window.d.ts.map +1 -1
- package/dist/providers/context-window.js +1 -0
- package/dist/providers/context-window.js.map +1 -1
- package/dist/providers/core-tools.d.ts +4 -1
- package/dist/providers/core-tools.d.ts.map +1 -1
- package/dist/providers/core-tools.js +5 -3
- package/dist/providers/core-tools.js.map +1 -1
- package/dist/providers/cost-calculator.d.ts.map +1 -1
- package/dist/providers/cost-calculator.js +9 -0
- package/dist/providers/cost-calculator.js.map +1 -1
- package/dist/providers/credential-check.d.ts +8 -0
- package/dist/providers/credential-check.d.ts.map +1 -1
- package/dist/providers/credential-check.js +22 -0
- package/dist/providers/credential-check.js.map +1 -1
- package/dist/providers/custom-openai-provider.d.ts.map +1 -1
- package/dist/providers/custom-openai-provider.js +19 -1
- package/dist/providers/custom-openai-provider.js.map +1 -1
- package/dist/providers/dreamina-pending.d.ts +22 -0
- package/dist/providers/dreamina-pending.d.ts.map +1 -0
- package/dist/providers/dreamina-pending.js +86 -0
- package/dist/providers/dreamina-pending.js.map +1 -0
- package/dist/providers/dreamina-provider.d.ts +10 -3
- package/dist/providers/dreamina-provider.d.ts.map +1 -1
- package/dist/providers/dreamina-provider.js +325 -45
- package/dist/providers/dreamina-provider.js.map +1 -1
- package/dist/providers/extension-tab.d.ts +42 -0
- package/dist/providers/extension-tab.d.ts.map +1 -0
- package/dist/providers/extension-tab.js +200 -0
- package/dist/providers/extension-tab.js.map +1 -0
- package/dist/providers/fake-provider.d.ts +3 -1
- package/dist/providers/fake-provider.d.ts.map +1 -1
- package/dist/providers/fake-provider.js +63 -9
- package/dist/providers/fake-provider.js.map +1 -1
- package/dist/providers/fake-schema-answer.d.ts +10 -0
- package/dist/providers/fake-schema-answer.d.ts.map +1 -0
- package/dist/providers/fake-schema-answer.js +64 -0
- package/dist/providers/fake-schema-answer.js.map +1 -0
- package/dist/providers/fal-provider.d.ts.map +1 -1
- package/dist/providers/fal-provider.js +5 -2
- package/dist/providers/fal-provider.js.map +1 -1
- package/dist/providers/gemini-web-provider.d.ts +76 -0
- package/dist/providers/gemini-web-provider.d.ts.map +1 -0
- package/dist/providers/gemini-web-provider.js +827 -0
- package/dist/providers/gemini-web-provider.js.map +1 -0
- package/dist/providers/http-transport.d.ts.map +1 -1
- package/dist/providers/http-transport.js +2 -0
- package/dist/providers/http-transport.js.map +1 -1
- package/dist/providers/huggingface-provider.d.ts.map +1 -1
- package/dist/providers/huggingface-provider.js +17 -1
- package/dist/providers/huggingface-provider.js.map +1 -1
- package/dist/providers/index.d.ts +18 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +66 -15
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/llama-provider.d.ts +8 -0
- package/dist/providers/llama-provider.d.ts.map +1 -1
- package/dist/providers/llama-provider.js +23 -5
- package/dist/providers/llama-provider.js.map +1 -1
- package/dist/providers/lmstudio-provider.js +2 -2
- package/dist/providers/lmstudio-provider.js.map +1 -1
- package/dist/providers/manifest-models.d.ts +2 -12
- package/dist/providers/manifest-models.d.ts.map +1 -1
- package/dist/providers/manifest-models.js +45 -3
- package/dist/providers/manifest-models.js.map +1 -1
- package/dist/providers/meta-provider.d.ts +53 -6
- package/dist/providers/meta-provider.d.ts.map +1 -1
- package/dist/providers/meta-provider.js +289 -6
- package/dist/providers/meta-provider.js.map +1 -1
- package/dist/providers/meta-segmentation.d.ts +27 -0
- package/dist/providers/meta-segmentation.d.ts.map +1 -0
- package/dist/providers/meta-segmentation.js +141 -0
- package/dist/providers/meta-segmentation.js.map +1 -0
- package/dist/providers/meta-transcription.d.ts +31 -0
- package/dist/providers/meta-transcription.d.ts.map +1 -0
- package/dist/providers/meta-transcription.js +187 -0
- package/dist/providers/meta-transcription.js.map +1 -0
- package/dist/providers/node-llama-cpp-provider.d.ts +6 -3
- package/dist/providers/node-llama-cpp-provider.d.ts.map +1 -1
- package/dist/providers/node-llama-cpp-provider.js +77 -62
- package/dist/providers/node-llama-cpp-provider.js.map +1 -1
- package/dist/providers/ollama-provider.d.ts +23 -0
- package/dist/providers/ollama-provider.d.ts.map +1 -1
- package/dist/providers/ollama-provider.js +140 -27
- package/dist/providers/ollama-provider.js.map +1 -1
- package/dist/providers/openai-compat/client.d.ts +10 -1
- package/dist/providers/openai-compat/client.d.ts.map +1 -1
- package/dist/providers/openai-compat/client.js +21 -3
- package/dist/providers/openai-compat/client.js.map +1 -1
- package/dist/providers/openai-compat/index.d.ts +1 -1
- package/dist/providers/openai-compat/index.d.ts.map +1 -1
- package/dist/providers/openai-compat/index.js +1 -1
- package/dist/providers/openai-compat/index.js.map +1 -1
- package/dist/providers/openai-compat/types.d.ts +4 -0
- package/dist/providers/openai-compat/types.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.d.ts +11 -0
- package/dist/providers/openai-compat-provider.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.js +47 -2
- package/dist/providers/openai-compat-provider.js.map +1 -1
- package/dist/providers/openai-provider.d.ts +15 -0
- package/dist/providers/openai-provider.d.ts.map +1 -1
- package/dist/providers/openai-provider.js +50 -12
- package/dist/providers/openai-provider.js.map +1 -1
- package/dist/providers/opper-provider.d.ts +31 -0
- package/dist/providers/opper-provider.d.ts.map +1 -0
- package/dist/providers/opper-provider.js +91 -0
- package/dist/providers/opper-provider.js.map +1 -0
- package/dist/providers/provider-request-log.d.ts.map +1 -1
- package/dist/providers/provider-request-log.js +3 -2
- package/dist/providers/provider-request-log.js.map +1 -1
- package/dist/providers/python-provider.d.ts.map +1 -1
- package/dist/providers/python-provider.js +5 -3
- package/dist/providers/python-provider.js.map +1 -1
- package/dist/providers/responses-api.d.ts +12 -2
- package/dist/providers/responses-api.d.ts.map +1 -1
- package/dist/providers/responses-api.js +54 -3
- package/dist/providers/responses-api.js.map +1 -1
- package/dist/providers/safe-url.d.ts.map +1 -1
- package/dist/providers/safe-url.js +8 -0
- package/dist/providers/safe-url.js.map +1 -1
- package/dist/providers/scripted-provider.d.ts.map +1 -1
- package/dist/providers/scripted-provider.js +6 -1
- package/dist/providers/scripted-provider.js.map +1 -1
- package/dist/providers/types.d.ts +16 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/vgenv-provider.d.ts +33 -0
- package/dist/providers/vgenv-provider.d.ts.map +1 -0
- package/dist/providers/vgenv-provider.js +261 -0
- package/dist/providers/vgenv-provider.js.map +1 -0
- package/dist/providers/vllm-provider.d.ts +7 -0
- package/dist/providers/vllm-provider.d.ts.map +1 -1
- package/dist/providers/vllm-provider.js +15 -3
- package/dist/providers/vllm-provider.js.map +1 -1
- package/dist/python-bridge-base.d.ts +16 -6
- package/dist/python-bridge-base.d.ts.map +1 -1
- package/dist/python-bridge-base.js +117 -40
- package/dist/python-bridge-base.js.map +1 -1
- package/dist/python-bridge-codec.d.ts +16 -1
- package/dist/python-bridge-codec.d.ts.map +1 -1
- package/dist/python-bridge-codec.js +60 -2
- package/dist/python-bridge-codec.js.map +1 -1
- package/dist/python-bridge-framing.d.ts +10 -1
- package/dist/python-bridge-framing.d.ts.map +1 -1
- package/dist/python-bridge-framing.js +33 -8
- package/dist/python-bridge-framing.js.map +1 -1
- package/dist/python-bridge-types.d.ts +26 -5
- package/dist/python-bridge-types.d.ts.map +1 -1
- package/dist/python-bridge-types.js.map +1 -1
- package/dist/python-graph-resolver.d.ts.map +1 -1
- package/dist/python-graph-resolver.js +12 -2
- package/dist/python-graph-resolver.js.map +1 -1
- package/dist/python-node-executor.d.ts +14 -3
- package/dist/python-node-executor.d.ts.map +1 -1
- package/dist/python-node-executor.js +185 -3
- package/dist/python-node-executor.js.map +1 -1
- package/dist/python-stdio-bridge.d.ts +6 -0
- package/dist/python-stdio-bridge.d.ts.map +1 -1
- package/dist/python-stdio-bridge.js +58 -12
- package/dist/python-stdio-bridge.js.map +1 -1
- package/dist/run-trace-context.d.ts +2 -27
- package/dist/run-trace-context.d.ts.map +1 -1
- package/dist/run-trace-context.js +4 -51
- package/dist/run-trace-context.js.map +1 -1
- package/dist/run-trace-processor.d.ts +3 -0
- package/dist/run-trace-processor.d.ts.map +1 -1
- package/dist/run-trace-processor.js +63 -46
- package/dist/run-trace-processor.js.map +1 -1
- package/dist/run-trace-scope.d.ts +28 -0
- package/dist/run-trace-scope.d.ts.map +1 -0
- package/dist/run-trace-scope.js +51 -0
- package/dist/run-trace-scope.js.map +1 -0
- package/dist/run-trace-serialization.d.ts.map +1 -1
- package/dist/run-trace-serialization.js +4 -3
- package/dist/run-trace-serialization.js.map +1 -1
- package/dist/span-record.d.ts +4 -0
- package/dist/span-record.d.ts.map +1 -0
- package/dist/span-record.js +48 -0
- package/dist/span-record.js.map +1 -0
- package/dist/swappable-python-bridge.d.ts +6 -6
- package/dist/swappable-python-bridge.d.ts.map +1 -1
- package/dist/swappable-python-bridge.js +10 -10
- package/dist/swappable-python-bridge.js.map +1 -1
- package/dist/telemetry.d.ts +2 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +33 -34
- package/dist/telemetry.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +55 -3
- package/dist/testing.js.map +1 -1
- package/dist/token-counter.d.ts +4 -13
- package/dist/token-counter.d.ts.map +1 -1
- package/dist/token-counter.js +68 -18
- package/dist/token-counter.js.map +1 -1
- package/dist/trace-context-manager.d.ts +12 -0
- package/dist/trace-context-manager.d.ts.map +1 -0
- package/dist/trace-context-manager.js +30 -0
- package/dist/trace-context-manager.js.map +1 -0
- package/dist/trace-exporters.d.ts +2 -3
- package/dist/trace-exporters.d.ts.map +1 -1
- package/dist/trace-exporters.js +5 -52
- package/dist/trace-exporters.js.map +1 -1
- package/dist/trace-semantic-conventions.d.ts +2 -0
- package/dist/trace-semantic-conventions.d.ts.map +1 -0
- package/dist/trace-semantic-conventions.js +2 -0
- package/dist/trace-semantic-conventions.js.map +1 -0
- package/dist/traced-storage.d.ts +17 -0
- package/dist/traced-storage.d.ts.map +1 -0
- package/dist/traced-storage.js +41 -0
- package/dist/traced-storage.js.map +1 -0
- package/dist/tracing-module.d.ts +3 -0
- package/dist/tracing-module.d.ts.map +1 -0
- package/dist/tracing-module.js +12 -0
- package/dist/tracing-module.js.map +1 -0
- 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
|
|
5
|
-
*
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* with the machine's logged-in Claude subscription (the
|
|
11
|
-
* under `~/.claude`). The
|
|
12
|
-
*
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
|
38
|
-
import {
|
|
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
|
-
*
|
|
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
|
|
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
|
|
66
|
+
const WEB_TOOLS = ["WebSearch", "WebFetch"];
|
|
70
67
|
/**
|
|
71
|
-
* Env vars a nested Claude subscription session leaks into its children.
|
|
72
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
114
|
+
constructor(secrets = {}, options = {}) {
|
|
137
115
|
super(PROVIDER_IDS.CLAUDE_AGENT_SDK);
|
|
138
116
|
this.injectedQueryFn = options.queryFn ?? null;
|
|
139
|
-
this.
|
|
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
|
|
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
|
|
147
|
-
* tools are exposed as
|
|
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
|
|
156
|
-
* avoid pinning a version that ages out; the concrete model id the
|
|
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
|
-
/**
|
|
169
|
-
|
|
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
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
186
|
-
* {@link generateMessages}; with `tools` we expose them as
|
|
187
|
-
* server whose handlers call `executeTool`, let the
|
|
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.
|
|
180
|
+
return yield* tracedProviderLoop(this.provider, args.model, () => this.generateCliLoop(args));
|
|
193
181
|
}
|
|
194
|
-
async *
|
|
195
|
-
// The
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
249
|
-
// no flattening to text
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
450
|
+
/** Drive one CLI session and translate its messages into stream items. */
|
|
464
451
|
async *runTurn(args, plan) {
|
|
465
|
-
const queryFn =
|
|
466
|
-
// Cancellation: the
|
|
467
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
? [...
|
|
498
|
+
? [...BUILTIN_TOOLS, "Skill", "ToolSearch"]
|
|
512
499
|
: plan.config.toolsOffered
|
|
513
500
|
? ["ToolSearch"]
|
|
514
|
-
: [...
|
|
501
|
+
: [...BUILTIN_TOOLS, "ToolSearch"],
|
|
515
502
|
tools: plan.config.builtinTools,
|
|
516
|
-
// Only the
|
|
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
|
|
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
|
|
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
|
|
568
|
-
//
|
|
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
|
|
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
|
|
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:
|
|
642
|
-
if (msg
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
813
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
956
|
+
if (delta?.type === "text_delta" && isString(delta.text)) {
|
|
970
957
|
return { text: delta.text };
|
|
971
|
-
|
|
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
|
|
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
|
|
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
|
|
1073
|
-
* {@link ProviderTool} to the harness's tool executor. The JSON-Schema input
|
|
1074
|
-
*
|
|
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:
|
|
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
|
|
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
|
|
1224
|
-
if (
|
|
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 (
|
|
1166
|
+
else if (isNonEmptyString(msg.result)) {
|
|
1228
1167
|
parts.push(msg.result);
|
|
1229
1168
|
}
|
|
1230
|
-
const 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
|
|
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
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
return
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|