zidane 5.13.26 → 5.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-CjAoILwj.js → acp-DgKEJH3P.js} +7 -7
- package/dist/{acp-CjAoILwj.js.map → acp-DgKEJH3P.js.map} +1 -1
- package/dist/acp-cli.js +11 -6
- package/dist/acp-cli.js.map +1 -1
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{tools-B32lZXLE.js → agent-B-cKanSC.js} +8035 -9459
- package/dist/agent-B-cKanSC.js.map +1 -0
- package/dist/{agent-DQIL5BFk.d.ts → agent-BAgOgS37.d.ts} +367 -208
- package/dist/agent-BAgOgS37.d.ts.map +1 -0
- package/dist/agent.d.ts +3 -0
- package/dist/agent.js +5 -0
- package/dist/anthropic-RGqsJlC8.js +807 -0
- package/dist/anthropic-RGqsJlC8.js.map +1 -0
- package/dist/{atomic-write-Bgtr5JPu.js → atomic-write-B7zr1ddF.js} +2 -2
- package/dist/{atomic-write-Bgtr5JPu.js.map → atomic-write-B7zr1ddF.js.map} +1 -1
- package/dist/{auth-DPrSP1YV.js → auth-C-ms5N2x.js} +33 -5
- package/dist/{auth-DPrSP1YV.js.map → auth-C-ms5N2x.js.map} +1 -1
- package/dist/cache-telemetry-BKAwsNxm.js +169 -0
- package/dist/cache-telemetry-BKAwsNxm.js.map +1 -0
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat/pure.js +1 -1
- package/dist/chat.d.ts +21 -7
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +30 -5
- package/dist/chat.js.map +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/docker.js +0 -1
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/errors-CkTvg97v.d.ts +203 -0
- package/dist/errors-CkTvg97v.d.ts.map +1 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +3 -0
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +2 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +406 -1
- package/dist/headless.js.map +1 -0
- package/dist/{index-YY3vp2F2.d.ts → index-CLuLFFLa.d.ts} +88 -418
- package/dist/index-CLuLFFLa.d.ts.map +1 -0
- package/dist/{index-C5qw3pj7.d.ts → index-CgBIl2mX.d.ts} +2 -2
- package/dist/{index-C5qw3pj7.d.ts.map → index-CgBIl2mX.d.ts.map} +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +113 -167
- package/dist/index.js.map +1 -1
- package/dist/{logger-C2ngE8CY.d.ts → logger-CJ-lpKPy.d.ts} +2 -2
- package/dist/{logger-C2ngE8CY.d.ts.map → logger-CJ-lpKPy.d.ts.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/messages-YZQLKaz2.js +1157 -0
- package/dist/messages-YZQLKaz2.js.map +1 -0
- package/dist/oauth-8LqbeI_Q.js +292 -0
- package/dist/oauth-8LqbeI_Q.js.map +1 -0
- package/dist/{messages-Iy_vEycX.js → openai-compat-0Y7jcncn.js} +27 -1379
- package/dist/openai-compat-0Y7jcncn.js.map +1 -0
- package/dist/output/stream-json.d.ts +2 -2
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +2 -2
- package/dist/{presets-B9nDwmaz.js → presets-Pr02ZNcy.js} +3 -2
- package/dist/{presets-B9nDwmaz.js.map → presets-Pr02ZNcy.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.js +210 -0
- package/dist/prompt.js.map +1 -0
- package/dist/providers/anthropic.d.ts +2 -0
- package/dist/providers/anthropic.js +2 -0
- package/dist/providers/arcee.d.ts +2 -0
- package/dist/providers/arcee.js +42 -0
- package/dist/providers/arcee.js.map +1 -0
- package/dist/providers/baseten.d.ts +2 -0
- package/dist/providers/baseten.js +66 -0
- package/dist/providers/baseten.js.map +1 -0
- package/dist/providers/cerebras.d.ts +2 -0
- package/dist/providers/cerebras.js +31 -0
- package/dist/providers/cerebras.js.map +1 -0
- package/dist/providers/local.d.ts +2 -0
- package/dist/providers/local.js +44 -0
- package/dist/providers/local.js.map +1 -0
- package/dist/providers/openai-compat.d.ts +2 -0
- package/dist/providers/openai-compat.js +2 -0
- package/dist/providers/openai.d.ts +2 -0
- package/dist/providers/openai.js +504 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +2 -0
- package/dist/providers/openrouter.js +52 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/xai.d.ts +2 -0
- package/dist/providers/xai.js +2 -0
- package/dist/providers-DgmdYGKm.js +168 -0
- package/dist/providers-DgmdYGKm.js.map +1 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +10 -2
- package/dist/restate.d.ts +1 -1
- package/dist/run-summary-DAnBZnrt.d.ts +249 -0
- package/dist/run-summary-DAnBZnrt.d.ts.map +1 -0
- package/dist/run-summary-nD3IQ4ZQ.js +253 -0
- package/dist/run-summary-nD3IQ4ZQ.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-CiA1_8SB.js → session-CqrAFAKJ.js} +2 -2
- package/dist/{session-CiA1_8SB.js.map → session-CqrAFAKJ.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/system-prompt-BlsdjcUi.d.ts +139 -0
- package/dist/system-prompt-BlsdjcUi.d.ts.map +1 -0
- package/dist/timeout-CpFm0jJ6.d.ts +10 -0
- package/dist/timeout-CpFm0jJ6.d.ts.map +1 -0
- package/dist/timeout-kGGSXagK.js +84 -0
- package/dist/timeout-kGGSXagK.js.map +1 -0
- package/dist/{tool-formatters-w5oaoCS4.d.ts → tool-formatters-Ch8oRpYR.d.ts} +2 -2
- package/dist/{tool-formatters-w5oaoCS4.d.ts.map → tool-formatters-Ch8oRpYR.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools-00_Nqazv.js +1689 -0
- package/dist/tools-00_Nqazv.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +2 -1
- package/dist/{transcript-anchors-BUtOd-qa.js → transcript-anchors-BUmvPnis.js} +10 -9
- package/dist/{transcript-anchors-BUtOd-qa.js.map → transcript-anchors-BUmvPnis.js.map} +1 -1
- package/dist/{transcript-anchors-BJ-ESWnm.d.ts → transcript-anchors-CL9UM_M4.d.ts} +4 -4
- package/dist/{transcript-anchors-BJ-ESWnm.d.ts.map → transcript-anchors-CL9UM_M4.d.ts.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.js +38 -13
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-BSSQHNq1.d.ts → turn-operations-Cj6v_ypW.d.ts} +3 -3
- package/dist/{turn-operations-BSSQHNq1.d.ts.map → turn-operations-Cj6v_ypW.d.ts.map} +1 -1
- package/dist/{turn-operations-B_XeuNp-.js → turn-operations-tG0vuBNc.js} +2 -2
- package/dist/{turn-operations-B_XeuNp-.js.map → turn-operations-tG0vuBNc.js.map} +1 -1
- package/dist/types-CyVGdbia.js.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/xai-tPdbAGkq.js +379 -0
- package/dist/xai-tPdbAGkq.js.map +1 -0
- package/docs/ARCHITECTURE.md +4 -2
- package/docs/CHAT.md +4 -0
- package/docs/SKILL.md +31 -5
- package/package.json +63 -1
- package/dist/agent-DQIL5BFk.d.ts.map +0 -1
- package/dist/headless-2SH7dvoW.js +0 -656
- package/dist/headless-2SH7dvoW.js.map +0 -1
- package/dist/index-YY3vp2F2.d.ts.map +0 -1
- package/dist/messages-Iy_vEycX.js.map +0 -1
- package/dist/providers-BMw1D_lj.js +0 -2332
- package/dist/providers-BMw1D_lj.js.map +0 -1
- package/dist/tools-B32lZXLE.js.map +0 -1
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
import { t as lazyAsync } from "./lazy-DLOurOC_.js";
|
|
2
|
+
import { c as classifyErrorPrelude, d as isRetryableHttpStatus, f as matchesContextExceeded, p as matchesToolPairingError } from "./errors-DJUxZg9b.js";
|
|
3
|
+
import { b as fillEstimatedCost, w as unsupportedMediaError, y as sanitizeToolSpecs } from "./openai-compat-0Y7jcncn.js";
|
|
4
|
+
import { renderSystemForWire, splitSystemPrompt } from "./prompt.js";
|
|
5
|
+
import { l as fromAnthropic, p as toAnthropic } from "./messages-YZQLKaz2.js";
|
|
6
|
+
import { i as resolveOAuthApiKey, r as readOAuthCredentials, t as extractRuntimeCredentials } from "./oauth-8LqbeI_Q.js";
|
|
7
|
+
//#region src/chat/anthropic-models.ts
|
|
8
|
+
/**
|
|
9
|
+
* Anthropic "fast mode" — `speed: "fast"` on the Messages API. ~2.5× output
|
|
10
|
+
* tokens/sec at a per-model price multiplier. Supported on Opus 4.6 / 4.7 / 4.8
|
|
11
|
+
* only; the API errors if sent on any other model, so it's attached just to
|
|
12
|
+
* those entries (see {@link FAST_MODE_OPTIONS}).
|
|
13
|
+
*
|
|
14
|
+
* The multiplier scales ALL rate components (input, output, and both cache
|
|
15
|
+
* rates) uniformly: per Anthropic, "prompt caching multipliers apply on top of
|
|
16
|
+
* fast mode pricing" across the full context window. Opus 4.8 is 2× ($5/$25 →
|
|
17
|
+
* $10/$50); Opus 4.6 / 4.7 are a steeper 6× ($5/$25 → $30/$150).
|
|
18
|
+
* See https://platform.claude.com/docs/en/build-with-claude/fast-mode.
|
|
19
|
+
*/
|
|
20
|
+
const FAST_MODE_OPTION = {
|
|
21
|
+
id: "fast",
|
|
22
|
+
label: "Fast mode",
|
|
23
|
+
description: "~2.5× faster output at premium pricing",
|
|
24
|
+
costMultiplier: {
|
|
25
|
+
input: 2,
|
|
26
|
+
output: 2,
|
|
27
|
+
cacheRead: 2,
|
|
28
|
+
cacheWrite: 2
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const FAST_MODE_OPTION_LEGACY = {
|
|
32
|
+
...FAST_MODE_OPTION,
|
|
33
|
+
description: "~2.5× faster output at 6× pricing",
|
|
34
|
+
costMultiplier: {
|
|
35
|
+
input: 6,
|
|
36
|
+
output: 6,
|
|
37
|
+
cacheRead: 6,
|
|
38
|
+
cacheWrite: 6
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/** Model ids that accept `speed: "fast"`, mapped to their fast-mode option. */
|
|
42
|
+
const FAST_MODE_OPTIONS = {
|
|
43
|
+
"claude-opus-4-8": FAST_MODE_OPTION,
|
|
44
|
+
"claude-opus-4-7": FAST_MODE_OPTION_LEGACY,
|
|
45
|
+
"claude-opus-4-6": FAST_MODE_OPTION_LEGACY
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The price multiplier to apply to a model's base rates when fast mode is
|
|
49
|
+
* active, or `undefined` when the model has no fast-mode pricing (i.e. doesn't
|
|
50
|
+
* support it). Mirrors {@link FAST_MODE_OPTIONS} — kept as a separate accessor
|
|
51
|
+
* so the cost layer can stay decoupled from the picker-facing option objects.
|
|
52
|
+
*/
|
|
53
|
+
function fastModeCostMultiplier(modelId) {
|
|
54
|
+
return FAST_MODE_OPTIONS[modelId]?.costMultiplier;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Claude Fable 5 — GA 2026-06-09. Anthropic's most capable widely released
|
|
58
|
+
* model: $10/$50 per MTok, 1M context, 128k output, adaptive thinking
|
|
59
|
+
* (always on), vision. No fast-mode support (Opus 4.6/4.7/4.8 only).
|
|
60
|
+
*
|
|
61
|
+
* Claude Opus 4.8 — shipped 2026-05-28. Same price/context/output as Opus 4.7
|
|
62
|
+
* ($5/$25 per MTok, 1M context, 128k output), adaptive thinking, vision.
|
|
63
|
+
* See https://www.anthropic.com/news/claude-opus-4-8.
|
|
64
|
+
*
|
|
65
|
+
* Claude Mythos 5 is deliberately absent: it's invitation-only (Project
|
|
66
|
+
* Glasswing), so it would be a dead picker entry for virtually all users.
|
|
67
|
+
*
|
|
68
|
+
* See https://platform.claude.com/docs/en/about-claude/models/overview.
|
|
69
|
+
*/
|
|
70
|
+
const ANTHROPIC_EXTRA_MODELS = [{
|
|
71
|
+
id: "claude-fable-5",
|
|
72
|
+
name: "Claude Fable 5",
|
|
73
|
+
reasoning: true,
|
|
74
|
+
input: ["text", "image"],
|
|
75
|
+
cost: {
|
|
76
|
+
input: 10,
|
|
77
|
+
output: 50,
|
|
78
|
+
cacheRead: 1,
|
|
79
|
+
cacheWrite: 12.5
|
|
80
|
+
},
|
|
81
|
+
contextWindow: 1e6,
|
|
82
|
+
maxTokens: 128e3,
|
|
83
|
+
provider: "anthropic"
|
|
84
|
+
}, {
|
|
85
|
+
id: "claude-opus-4-8",
|
|
86
|
+
name: "Claude Opus 4.8",
|
|
87
|
+
reasoning: true,
|
|
88
|
+
input: ["text", "image"],
|
|
89
|
+
cost: {
|
|
90
|
+
input: 5,
|
|
91
|
+
output: 25,
|
|
92
|
+
cacheRead: .5,
|
|
93
|
+
cacheWrite: 6.25
|
|
94
|
+
},
|
|
95
|
+
contextWindow: 1e6,
|
|
96
|
+
maxTokens: 128e3,
|
|
97
|
+
provider: "anthropic",
|
|
98
|
+
options: [FAST_MODE_OPTION]
|
|
99
|
+
}];
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/providers/anthropic.ts
|
|
102
|
+
const loadAnthropicSdk = lazyAsync(async () => {
|
|
103
|
+
try {
|
|
104
|
+
return (await import("@anthropic-ai/sdk")).default;
|
|
105
|
+
} catch (err) {
|
|
106
|
+
throw new Error("The `anthropic` provider requires the `@anthropic-ai/sdk` package, which is an optional peer dependency. Install it with your package manager (e.g. `bun add @anthropic-ai/sdk`).", err instanceof Error ? { cause: err } : void 0);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
/** Beta flags sent unconditionally on the OAuth path (Claude Code parity). */
|
|
110
|
+
const OAUTH_DEFAULT_BETAS = ["claude-code-20250219", "oauth-2025-04-20"];
|
|
111
|
+
/**
|
|
112
|
+
* Beta flag that enables the `speed: "fast"` request field (fast mode — up to
|
|
113
|
+
* 2.5× output tokens/sec on Opus 4.6/4.7/4.8 at premium pricing). Sent as an
|
|
114
|
+
* `anthropic-beta` header only on requests that opt into fast mode; absent
|
|
115
|
+
* otherwise so standard requests keep a byte-stable header for prompt caching.
|
|
116
|
+
*
|
|
117
|
+
* See: https://platform.claude.com/docs/en/build-with-claude/fast-mode
|
|
118
|
+
*/
|
|
119
|
+
const FAST_MODE_BETA = "fast-mode-2026-02-01";
|
|
120
|
+
const API_KEY_RESERVED_EXTRA_HEADER_NAMES = new Set(["anthropic-beta"]);
|
|
121
|
+
const OAUTH_RESERVED_EXTRA_HEADER_NAMES = new Set([
|
|
122
|
+
"anthropic-beta",
|
|
123
|
+
"anthropic-dangerous-direct-browser-access",
|
|
124
|
+
"user-agent",
|
|
125
|
+
"x-app"
|
|
126
|
+
]);
|
|
127
|
+
function filterReservedExtraHeaders(extraHeaders, reservedNames) {
|
|
128
|
+
const out = {};
|
|
129
|
+
for (const [name, value] of Object.entries(extraHeaders ?? {})) if (!reservedNames.has(name.toLowerCase())) out[name] = value;
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Build the `anthropic-beta` header value — OAuth defaults plus caller-supplied
|
|
134
|
+
* extras, de-duped while preserving order. Returns `undefined` when no betas
|
|
135
|
+
* apply (non-OAuth, no extras).
|
|
136
|
+
*/
|
|
137
|
+
function resolveAnthropicBetas(isOAuth, extraBetas) {
|
|
138
|
+
const seen = /* @__PURE__ */ new Set();
|
|
139
|
+
const out = [];
|
|
140
|
+
if (isOAuth) {
|
|
141
|
+
for (const b of OAUTH_DEFAULT_BETAS) if (!seen.has(b)) {
|
|
142
|
+
seen.add(b);
|
|
143
|
+
out.push(b);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (extraBetas) {
|
|
147
|
+
for (const b of extraBetas) if (typeof b === "string" && b.length > 0 && !seen.has(b)) {
|
|
148
|
+
seen.add(b);
|
|
149
|
+
out.push(b);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return out.length > 0 ? out.join(",") : void 0;
|
|
153
|
+
}
|
|
154
|
+
function getConfiguredApiKey(anthropicParams) {
|
|
155
|
+
if (anthropicParams?.apiKey) return anthropicParams.apiKey;
|
|
156
|
+
if (anthropicParams?.access) return anthropicParams.access;
|
|
157
|
+
if (process.env.ANTHROPIC_API_KEY) return process.env.ANTHROPIC_API_KEY;
|
|
158
|
+
const access = readOAuthCredentials().anthropic?.access;
|
|
159
|
+
if (typeof access === "string" && access.length > 0) return access;
|
|
160
|
+
throw new Error("No API key found. Run `bun run auth` first.");
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Resolve {@link AnthropicParams.extraHeaders} to a concrete map. A function
|
|
164
|
+
* form is invoked (and awaited) per call so gateway auth tokens can refresh
|
|
165
|
+
* each turn; a static map passes through.
|
|
166
|
+
*/
|
|
167
|
+
async function resolveExtraHeaders(value) {
|
|
168
|
+
return typeof value === "function" ? value() : value;
|
|
169
|
+
}
|
|
170
|
+
function createClient(SDK, apiKey, isOAuth, baseURL, extraBetas, extraHeaders) {
|
|
171
|
+
const base = baseURL ? { baseURL } : {};
|
|
172
|
+
const betaHeader = resolveAnthropicBetas(isOAuth, extraBetas);
|
|
173
|
+
if (isOAuth) {
|
|
174
|
+
const defaultHeaders = {
|
|
175
|
+
...filterReservedExtraHeaders(extraHeaders, OAUTH_RESERVED_EXTRA_HEADER_NAMES),
|
|
176
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
177
|
+
"user-agent": "zidane/2.0.0",
|
|
178
|
+
"x-app": "cli"
|
|
179
|
+
};
|
|
180
|
+
if (betaHeader) defaultHeaders["anthropic-beta"] = betaHeader;
|
|
181
|
+
return new SDK({
|
|
182
|
+
apiKey: null,
|
|
183
|
+
authToken: apiKey,
|
|
184
|
+
dangerouslyAllowBrowser: true,
|
|
185
|
+
defaultHeaders,
|
|
186
|
+
...base
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const defaultHeaders = filterReservedExtraHeaders(extraHeaders, API_KEY_RESERVED_EXTRA_HEADER_NAMES);
|
|
190
|
+
if (betaHeader) defaultHeaders["anthropic-beta"] = betaHeader;
|
|
191
|
+
return new SDK({
|
|
192
|
+
apiKey,
|
|
193
|
+
...Object.keys(defaultHeaders).length > 0 ? { defaultHeaders } : {},
|
|
194
|
+
...base
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Map `ThinkingLevel` budgeted tiers to Anthropic's `output_config.effort`
|
|
199
|
+
* enum. `minimal` collapses to `low` (the closest equivalent — Anthropic does
|
|
200
|
+
* not have a sub-`low` tier).
|
|
201
|
+
*/
|
|
202
|
+
const EFFORT_FOR_LEVEL = {
|
|
203
|
+
minimal: "low",
|
|
204
|
+
low: "low",
|
|
205
|
+
medium: "medium",
|
|
206
|
+
high: "high",
|
|
207
|
+
xhigh: "xhigh",
|
|
208
|
+
max: "max"
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Decide how to translate a `ThinkingLevel` into Anthropic's request shape.
|
|
212
|
+
* Pure / synchronous — exported so tests can assert routing without standing
|
|
213
|
+
* up the SDK.
|
|
214
|
+
*
|
|
215
|
+
* Routing rules:
|
|
216
|
+
* - `'off'` → `null` (no thinking field, no effort hint).
|
|
217
|
+
* - `'adaptive'` → adaptive thinking with no effort hint (model decides
|
|
218
|
+
* everything). When `customBudget` is set, it is carried as `maxTokensCap`
|
|
219
|
+
* so the request builder caps `max_tokens` accordingly — adaptive has no
|
|
220
|
+
* native budget knob, but capping the response envelope soft-bounds the
|
|
221
|
+
* thinking that lives inside it.
|
|
222
|
+
* - Budgeted levels → adaptive thinking with an `effort` hint, unless
|
|
223
|
+
* `customBudget` is provided.
|
|
224
|
+
* - Any level + `customBudget` → explicit-budget `enabled` path. The caller
|
|
225
|
+
* has opted into precise budget control and accepts the Anthropic
|
|
226
|
+
* deprecation warning that comes with it on opus 4.6+. `'adaptive'` is the
|
|
227
|
+
* sole exception: it never falls back to enabled, since adaptive is the
|
|
228
|
+
* current Anthropic API surface for self-budgeted thinking.
|
|
229
|
+
*/
|
|
230
|
+
function planAnthropicThinking(level, customBudget) {
|
|
231
|
+
if (level === "off") return null;
|
|
232
|
+
if (level === "adaptive") {
|
|
233
|
+
if (typeof customBudget === "number" && customBudget > 0) return {
|
|
234
|
+
kind: "adaptive",
|
|
235
|
+
maxTokensCap: customBudget
|
|
236
|
+
};
|
|
237
|
+
return { kind: "adaptive" };
|
|
238
|
+
}
|
|
239
|
+
if (customBudget !== void 0) return {
|
|
240
|
+
kind: "enabled",
|
|
241
|
+
budgetTokens: customBudget,
|
|
242
|
+
maxTokensBump: customBudget
|
|
243
|
+
};
|
|
244
|
+
return {
|
|
245
|
+
kind: "adaptive",
|
|
246
|
+
effort: EFFORT_FOR_LEVEL[level]
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Map Anthropic's native `stop_reason` to the zidane `TurnFinishReason` union.
|
|
251
|
+
*
|
|
252
|
+
* `pause_turn` and `model_context_window_exceeded` are 4.6+ stop reasons that
|
|
253
|
+
* pre-Z21 collapsed to `'other'` and silently terminated the run. They now
|
|
254
|
+
* map to `'pause'` and `'length'` respectively, and the surrounding caller
|
|
255
|
+
* adjusts the `done` flag so the loop can recover.
|
|
256
|
+
*/
|
|
257
|
+
function mapStopReason(stopReason) {
|
|
258
|
+
if (!stopReason) return void 0;
|
|
259
|
+
switch (stopReason) {
|
|
260
|
+
case "end_turn":
|
|
261
|
+
case "stop_sequence": return "stop";
|
|
262
|
+
case "tool_use": return "tool-calls";
|
|
263
|
+
case "max_tokens":
|
|
264
|
+
case "model_context_window_exceeded": return "length";
|
|
265
|
+
case "refusal": return "content-filter";
|
|
266
|
+
case "pause_turn": return "pause";
|
|
267
|
+
default: return "other";
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const EPHEMERAL = { type: "ephemeral" };
|
|
271
|
+
/**
|
|
272
|
+
* Mutate an Anthropic request in place to add cache breakpoints on the three
|
|
273
|
+
* stable prefixes:
|
|
274
|
+
* 1. System prompt — `cache_control` on the static prefix only (see below).
|
|
275
|
+
* 2. Tool definitions — last tool.
|
|
276
|
+
* 3. Conversation — last content block of the last message.
|
|
277
|
+
*
|
|
278
|
+
* Each breakpoint tells Anthropic to cache the prefix ending at that block;
|
|
279
|
+
* subsequent turns reuse the cached prefix and pay only for the delta. Safe
|
|
280
|
+
* no-op when any prefix is empty (no tools, empty system, etc.).
|
|
281
|
+
*
|
|
282
|
+
* System-prompt boundary handling (`SYSTEM_PROMPT_BOUNDARY`):
|
|
283
|
+
*
|
|
284
|
+
* - When `originalSystem` is provided AND carries the marker, the system
|
|
285
|
+
* block is rewritten as a 2-block array: static (cached) then dynamic
|
|
286
|
+
* (uncached). The caller is expected to have already stripped the marker
|
|
287
|
+
* from `params.system` via {@link renderSystemForWire} — this helper
|
|
288
|
+
* only re-splits to apply `cache_control` cleanly.
|
|
289
|
+
* - When `originalSystem` is omitted or marker-free, the existing single-
|
|
290
|
+
* block treatment applies: `cache_control` on the whole system string.
|
|
291
|
+
* - Array system prompts: existing semantics — `cache_control` lands on the
|
|
292
|
+
* last block. Callers building their own multi-block layout own the
|
|
293
|
+
* breakpoint placement.
|
|
294
|
+
*/
|
|
295
|
+
function applyAnthropicCacheBreakpoints(params, originalSystem) {
|
|
296
|
+
if (typeof params.system === "string") {
|
|
297
|
+
if (params.system.length > 0) {
|
|
298
|
+
const parts = splitSystemPrompt(originalSystem && originalSystem.length > 0 ? originalSystem : params.system);
|
|
299
|
+
if (parts.dynamic.length > 0) {
|
|
300
|
+
const blocks = [];
|
|
301
|
+
if (parts.static.length > 0) blocks.push({
|
|
302
|
+
type: "text",
|
|
303
|
+
text: parts.static,
|
|
304
|
+
cache_control: EPHEMERAL
|
|
305
|
+
});
|
|
306
|
+
blocks.push({
|
|
307
|
+
type: "text",
|
|
308
|
+
text: parts.dynamic
|
|
309
|
+
});
|
|
310
|
+
params.system = blocks;
|
|
311
|
+
} else params.system = [{
|
|
312
|
+
type: "text",
|
|
313
|
+
text: parts.static,
|
|
314
|
+
cache_control: EPHEMERAL
|
|
315
|
+
}];
|
|
316
|
+
}
|
|
317
|
+
} else if (Array.isArray(params.system) && params.system.length > 0) {
|
|
318
|
+
const lastIdx = params.system.length - 1;
|
|
319
|
+
params.system = params.system.map((block, i) => i === lastIdx ? {
|
|
320
|
+
...block,
|
|
321
|
+
cache_control: EPHEMERAL
|
|
322
|
+
} : block);
|
|
323
|
+
}
|
|
324
|
+
if (params.tools && params.tools.length > 0) {
|
|
325
|
+
const lastIdx = params.tools.length - 1;
|
|
326
|
+
params.tools = params.tools.map((tool, i) => i === lastIdx ? {
|
|
327
|
+
...tool,
|
|
328
|
+
cache_control: EPHEMERAL
|
|
329
|
+
} : tool);
|
|
330
|
+
}
|
|
331
|
+
if (params.messages.length === 0) return;
|
|
332
|
+
const lastMsgIdx = params.messages.length - 1;
|
|
333
|
+
const lastMsg = params.messages[lastMsgIdx];
|
|
334
|
+
if (typeof lastMsg.content === "string") {
|
|
335
|
+
if (lastMsg.content.length === 0) return;
|
|
336
|
+
params.messages[lastMsgIdx] = {
|
|
337
|
+
...lastMsg,
|
|
338
|
+
content: [{
|
|
339
|
+
type: "text",
|
|
340
|
+
text: lastMsg.content,
|
|
341
|
+
cache_control: EPHEMERAL
|
|
342
|
+
}]
|
|
343
|
+
};
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (!Array.isArray(lastMsg.content) || lastMsg.content.length === 0) return;
|
|
347
|
+
const blocks = lastMsg.content;
|
|
348
|
+
let targetIdx = blocks.length - 1;
|
|
349
|
+
while (targetIdx >= 0 && isThinkingBlock(blocks[targetIdx])) targetIdx -= 1;
|
|
350
|
+
if (targetIdx < 0) return;
|
|
351
|
+
const nextBlocks = blocks.slice();
|
|
352
|
+
nextBlocks[targetIdx] = {
|
|
353
|
+
...nextBlocks[targetIdx],
|
|
354
|
+
cache_control: EPHEMERAL
|
|
355
|
+
};
|
|
356
|
+
params.messages[lastMsgIdx] = {
|
|
357
|
+
...lastMsg,
|
|
358
|
+
content: nextBlocks
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function isThinkingBlock(block) {
|
|
362
|
+
return block.type === "thinking" || block.type === "redacted_thinking";
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Duck-type check for an Anthropic SDK `APIError` — avoids a runtime dependency
|
|
366
|
+
* on `@anthropic-ai/sdk` so `classifyError` stays usable even when the optional
|
|
367
|
+
* peer dep isn't loaded (e.g. host code calling it on an unrelated provider's
|
|
368
|
+
* error).
|
|
369
|
+
*
|
|
370
|
+
* Anthropic's APIError shape: `.status: number` + `.error` (parsed body, object
|
|
371
|
+
* or null). Plain `Error`s don't have `.status` + `.error`.
|
|
372
|
+
*/
|
|
373
|
+
function looksLikeAnthropicApiError(err) {
|
|
374
|
+
if (!err || typeof err !== "object") return false;
|
|
375
|
+
const e = err;
|
|
376
|
+
return typeof e.status === "number" && "error" in e;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Anthropic SSE `error` event types that warrant a retry. `overloaded_error`
|
|
380
|
+
* is the headline case (capacity event mid-stream); `api_error` and
|
|
381
|
+
* `timeout_error` are both server-side transient failures. `invalid_request_error`,
|
|
382
|
+
* `authentication_error`, `permission_error`, `not_found_error`, `billing_error`
|
|
383
|
+
* are terminal — retrying them would just hammer a bad request.
|
|
384
|
+
*
|
|
385
|
+
* `rate_limit_error` here would be redundant: the SDK's pre-stream retry
|
|
386
|
+
* handles 429, and a mid-stream `rate_limit_error` is exceedingly rare. We
|
|
387
|
+
* include it anyway for symmetry with the HTTP-level retry policy.
|
|
388
|
+
*/
|
|
389
|
+
const SSE_RETRYABLE_ERROR_TYPES = new Set([
|
|
390
|
+
"overloaded_error",
|
|
391
|
+
"api_error",
|
|
392
|
+
"timeout_error",
|
|
393
|
+
"rate_limit_error"
|
|
394
|
+
]);
|
|
395
|
+
/**
|
|
396
|
+
* Try to parse an Anthropic SSE `error` event payload out of a thrown error's
|
|
397
|
+
* message. The SDK's `MessageStream` surfaces mid-stream error events by
|
|
398
|
+
* throwing an `AnthropicError` whose `.message` contains the raw JSON shape:
|
|
399
|
+
*
|
|
400
|
+
* {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"},"request_id":"req_..."}
|
|
401
|
+
*
|
|
402
|
+
* Returns the inner `{ type, message }` or `null` when the message isn't
|
|
403
|
+
* the canonical SSE error shape. Tolerant: silently returns `null` for
|
|
404
|
+
* non-string input, non-JSON content, or any shape mismatch.
|
|
405
|
+
*/
|
|
406
|
+
function matchSseErrorEvent(message) {
|
|
407
|
+
if (typeof message !== "string" || message.length === 0) return null;
|
|
408
|
+
if (!message.includes("\"type\":\"error\"") && !message.includes("\"type\": \"error\"")) return null;
|
|
409
|
+
const start = message.indexOf("{");
|
|
410
|
+
if (start < 0) return null;
|
|
411
|
+
try {
|
|
412
|
+
const parsed = JSON.parse(message.slice(start));
|
|
413
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
414
|
+
const outer = parsed;
|
|
415
|
+
if (outer.type !== "error" || !outer.error || typeof outer.error !== "object") return null;
|
|
416
|
+
const inner = outer.error;
|
|
417
|
+
if (typeof inner.type !== "string") return null;
|
|
418
|
+
return {
|
|
419
|
+
type: inner.type,
|
|
420
|
+
message: typeof inner.message === "string" ? inner.message : inner.type
|
|
421
|
+
};
|
|
422
|
+
} catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Classify an Anthropic SDK / HTTP error for typed-error wrapping.
|
|
428
|
+
*
|
|
429
|
+
* - `prompt is too long` (400 invalid_request_error) → `context_exceeded`.
|
|
430
|
+
* - `'tool_use' ids were found without 'tool_result' blocks` /
|
|
431
|
+
* `'tool_result must be preceded by a tool_call'` (400) →
|
|
432
|
+
* `tool_pairing_corruption`. The local repair pass should have caught
|
|
433
|
+
* this — the typed error tells consumers their pre-send pipeline is
|
|
434
|
+
* shipping orphaned blocks somehow (custom `context:transform` hook,
|
|
435
|
+
* bypassed loop, mid-stream session edit) so they can patch the root
|
|
436
|
+
* cause instead of chasing 400s.
|
|
437
|
+
* - Any other Anthropic `APIError`-shaped value → `provider_error` with the
|
|
438
|
+
* native status/type code.
|
|
439
|
+
* - Unknown errors → `null` (loop wraps in `AgentProviderError` generically).
|
|
440
|
+
*
|
|
441
|
+
* Anthropic's wire error shape is `{ type: 'error', error: { type, message } }` — the
|
|
442
|
+
* SDK stores the parsed body on `err.error`. We walk both levels so callers see the
|
|
443
|
+
* most specific `providerCode` we can find.
|
|
444
|
+
*/
|
|
445
|
+
function classifyAnthropicError(err) {
|
|
446
|
+
const prelude = classifyErrorPrelude(err);
|
|
447
|
+
if (prelude === "not-object") return null;
|
|
448
|
+
if (prelude === "aborted") return { kind: "aborted" };
|
|
449
|
+
const anyErr = err;
|
|
450
|
+
if (!looksLikeAnthropicApiError(err)) {
|
|
451
|
+
const sseError = matchSseErrorEvent(anyErr.message);
|
|
452
|
+
if (sseError) return {
|
|
453
|
+
kind: "provider_error",
|
|
454
|
+
providerCode: sseError.type,
|
|
455
|
+
message: sseError.message,
|
|
456
|
+
retryable: SSE_RETRYABLE_ERROR_TYPES.has(sseError.type)
|
|
457
|
+
};
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
const innerType = anyErr.error?.error?.type;
|
|
461
|
+
const outerType = anyErr.error?.type;
|
|
462
|
+
const nativeType = innerType && innerType !== "error" ? innerType : outerType;
|
|
463
|
+
const message = anyErr.error?.error?.message ?? anyErr.error?.message ?? anyErr.message ?? "";
|
|
464
|
+
if (matchesContextExceeded(message)) return {
|
|
465
|
+
kind: "context_exceeded",
|
|
466
|
+
providerCode: nativeType ?? "invalid_request_error",
|
|
467
|
+
message
|
|
468
|
+
};
|
|
469
|
+
if (matchesToolPairingError(message)) return {
|
|
470
|
+
kind: "tool_pairing_corruption",
|
|
471
|
+
providerCode: nativeType ?? "invalid_request_error",
|
|
472
|
+
message
|
|
473
|
+
};
|
|
474
|
+
const status = anyErr.status;
|
|
475
|
+
const retryable = typeof status === "number" ? isRetryableHttpStatus(status) : void 0;
|
|
476
|
+
return {
|
|
477
|
+
kind: "provider_error",
|
|
478
|
+
providerCode: nativeType ?? (status ? String(status) : void 0),
|
|
479
|
+
message,
|
|
480
|
+
...retryable !== void 0 ? { retryable } : {}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function shouldFallbackFromFastMode(err) {
|
|
484
|
+
const classified = classifyAnthropicError(err);
|
|
485
|
+
if (classified?.kind === "provider_error" && classified.retryable === true) return true;
|
|
486
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
487
|
+
return /\b(?:fast|speed)\b/i.test(message) && /capacity|overload|rate|unavailable|unsupported|not supported|not permitted|extra inputs|invalid/i.test(message);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Build a user `SessionMessage` from multimodal prompt parts.
|
|
491
|
+
*
|
|
492
|
+
* - Text parts → text blocks.
|
|
493
|
+
* - Image parts → base64 image blocks.
|
|
494
|
+
* - Document parts → canonical document blocks. `toAnthropic` emits native
|
|
495
|
+
* document blocks only for base64 PDFs; text/non-PDF documents fall back to
|
|
496
|
+
* attachment-tagged text to avoid invalid Anthropic source media types.
|
|
497
|
+
*
|
|
498
|
+
* The format mirrors `defaultPromptMessage`'s output on shape — Anthropic-specific
|
|
499
|
+
* handling differs only in that `promptMessage` being present tells the agent loop
|
|
500
|
+
* to route PromptPart[] through this function rather than throwing on base64 docs.
|
|
501
|
+
*/
|
|
502
|
+
function anthropicPromptMessage(parts) {
|
|
503
|
+
const content = [];
|
|
504
|
+
for (const part of parts) {
|
|
505
|
+
if (part.type === "text") {
|
|
506
|
+
if (part.text.length > 0) content.push({
|
|
507
|
+
type: "text",
|
|
508
|
+
text: part.text
|
|
509
|
+
});
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (part.type === "image") {
|
|
513
|
+
content.push({
|
|
514
|
+
type: "image",
|
|
515
|
+
mediaType: part.mediaType,
|
|
516
|
+
data: part.data
|
|
517
|
+
});
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (part.type === "audio") throw unsupportedMediaError("audio", "anthropic");
|
|
521
|
+
if (part.type === "video") throw unsupportedMediaError("video", "anthropic");
|
|
522
|
+
content.push({
|
|
523
|
+
type: "document",
|
|
524
|
+
mediaType: part.mediaType,
|
|
525
|
+
data: part.data,
|
|
526
|
+
encoding: part.encoding,
|
|
527
|
+
...part.name ? { name: part.name } : {}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
role: "user",
|
|
532
|
+
content
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function anthropic(anthropicParams) {
|
|
536
|
+
const isOAuth = getConfiguredApiKey(anthropicParams).includes("sk-ant-oat");
|
|
537
|
+
const defaultModel = anthropicParams?.defaultModel || "claude-opus-4-8";
|
|
538
|
+
let runtimeCredentials = extractRuntimeCredentials(anthropicParams);
|
|
539
|
+
return {
|
|
540
|
+
name: "anthropic",
|
|
541
|
+
meta: {
|
|
542
|
+
defaultModel,
|
|
543
|
+
isOAuth,
|
|
544
|
+
clearsContextServerSide: Boolean(anthropicParams?.contextManagement),
|
|
545
|
+
capabilities: {
|
|
546
|
+
vision: true,
|
|
547
|
+
imageInToolResult: true,
|
|
548
|
+
documents: true,
|
|
549
|
+
audio: false,
|
|
550
|
+
video: false,
|
|
551
|
+
nativeWebSearch: { maxUses: 5 }
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
formatTools(tools) {
|
|
555
|
+
const nativeWebSearch = this.meta.capabilities?.nativeWebSearch;
|
|
556
|
+
const hasWebSearch = !!nativeWebSearch && tools.some((t) => t.name === "web_search");
|
|
557
|
+
const out = sanitizeToolSpecs(hasWebSearch ? tools.filter((t) => t.name !== "web_search") : tools, { profile: "anthropic" }).map((t) => ({
|
|
558
|
+
name: t.name,
|
|
559
|
+
description: t.description,
|
|
560
|
+
input_schema: t.inputSchema
|
|
561
|
+
}));
|
|
562
|
+
if (hasWebSearch && nativeWebSearch) {
|
|
563
|
+
const block = {
|
|
564
|
+
type: "web_search_20250305",
|
|
565
|
+
name: "web_search"
|
|
566
|
+
};
|
|
567
|
+
if (typeof nativeWebSearch.maxUses === "number") block.max_uses = nativeWebSearch.maxUses;
|
|
568
|
+
out.push(block);
|
|
569
|
+
}
|
|
570
|
+
return out;
|
|
571
|
+
},
|
|
572
|
+
userMessage(content) {
|
|
573
|
+
return {
|
|
574
|
+
role: "user",
|
|
575
|
+
content: [{
|
|
576
|
+
type: "text",
|
|
577
|
+
text: content
|
|
578
|
+
}]
|
|
579
|
+
};
|
|
580
|
+
},
|
|
581
|
+
assistantMessage(content) {
|
|
582
|
+
return {
|
|
583
|
+
role: "assistant",
|
|
584
|
+
content: [{
|
|
585
|
+
type: "text",
|
|
586
|
+
text: content
|
|
587
|
+
}]
|
|
588
|
+
};
|
|
589
|
+
},
|
|
590
|
+
toolResultsMessage(results) {
|
|
591
|
+
return {
|
|
592
|
+
role: "user",
|
|
593
|
+
content: results.map((r) => ({
|
|
594
|
+
type: "tool_result",
|
|
595
|
+
callId: r.id,
|
|
596
|
+
output: r.content,
|
|
597
|
+
...r.isError ? { isError: true } : {}
|
|
598
|
+
}))
|
|
599
|
+
};
|
|
600
|
+
},
|
|
601
|
+
promptMessage: anthropicPromptMessage,
|
|
602
|
+
classifyError: classifyAnthropicError,
|
|
603
|
+
async countTokens(payload, signal) {
|
|
604
|
+
try {
|
|
605
|
+
const SDK = await loadAnthropicSdk();
|
|
606
|
+
const apiKey = await resolveOAuthApiKey({
|
|
607
|
+
provider: "anthropic",
|
|
608
|
+
providerId: "anthropic",
|
|
609
|
+
params: runtimeCredentials ? {
|
|
610
|
+
...anthropicParams,
|
|
611
|
+
...runtimeCredentials
|
|
612
|
+
} : anthropicParams,
|
|
613
|
+
envKey: "ANTHROPIC_API_KEY",
|
|
614
|
+
missingError: "No API key found. Run `bun run auth` first.",
|
|
615
|
+
refreshError: (reason) => `Anthropic OAuth token refresh failed. ${reason}`
|
|
616
|
+
});
|
|
617
|
+
const callIsOAuth = apiKey.includes("sk-ant-oat");
|
|
618
|
+
const client = createClient(SDK, apiKey, callIsOAuth, anthropicParams?.baseURL, anthropicParams?.extraBetas, await resolveExtraHeaders(anthropicParams?.extraHeaders));
|
|
619
|
+
const wireSystem = renderSystemForWire(payload.system);
|
|
620
|
+
const system = callIsOAuth ? `You are Claude Code, Anthropic's official CLI for Claude.` : wireSystem;
|
|
621
|
+
const messages = callIsOAuth && payload.system ? [
|
|
622
|
+
{
|
|
623
|
+
role: "user",
|
|
624
|
+
content: [{
|
|
625
|
+
type: "text",
|
|
626
|
+
text: wireSystem
|
|
627
|
+
}]
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
role: "assistant",
|
|
631
|
+
content: [{
|
|
632
|
+
type: "text",
|
|
633
|
+
text: "Understood. I will proceed with these instructions above the rest of my system prompt."
|
|
634
|
+
}]
|
|
635
|
+
},
|
|
636
|
+
...payload.messages
|
|
637
|
+
] : [...payload.messages];
|
|
638
|
+
return (await client.messages.countTokens({
|
|
639
|
+
model: payload.model,
|
|
640
|
+
system,
|
|
641
|
+
tools: payload.tools,
|
|
642
|
+
messages: messages.map((m) => toAnthropic(m))
|
|
643
|
+
}, signal ? { signal } : void 0)).input_tokens;
|
|
644
|
+
} catch (err) {
|
|
645
|
+
if (process.env.ZIDANE_DEBUG_COUNT) console.error("[anthropic.countTokens] failed:", err);
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
async stream(options, callbacks) {
|
|
650
|
+
const SDK = await loadAnthropicSdk();
|
|
651
|
+
const apiKey = await resolveOAuthApiKey({
|
|
652
|
+
provider: "anthropic",
|
|
653
|
+
providerId: "anthropic",
|
|
654
|
+
params: runtimeCredentials ? {
|
|
655
|
+
...anthropicParams,
|
|
656
|
+
...runtimeCredentials
|
|
657
|
+
} : anthropicParams,
|
|
658
|
+
envKey: "ANTHROPIC_API_KEY",
|
|
659
|
+
missingError: "No API key found. Run `bun run auth` first.",
|
|
660
|
+
refreshError: (reason) => `Anthropic OAuth token refresh failed. Run \`bun run auth --anthropic\` again. ${reason}`
|
|
661
|
+
}, {
|
|
662
|
+
...callbacks,
|
|
663
|
+
async onOAuthRefresh(ctx) {
|
|
664
|
+
if (ctx.source === "params") runtimeCredentials = {
|
|
665
|
+
access: ctx.credentials.access,
|
|
666
|
+
refresh: ctx.credentials.refresh,
|
|
667
|
+
expires: ctx.credentials.expires
|
|
668
|
+
};
|
|
669
|
+
await callbacks.onOAuthRefresh?.(ctx);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
const requestBetas = options.modelOptions?.fast ? [...anthropicParams?.extraBetas ?? [], FAST_MODE_BETA] : anthropicParams?.extraBetas;
|
|
673
|
+
const callIsOAuth = apiKey.includes("sk-ant-oat");
|
|
674
|
+
const client = createClient(SDK, apiKey, callIsOAuth, anthropicParams?.baseURL, requestBetas, await resolveExtraHeaders(anthropicParams?.extraHeaders));
|
|
675
|
+
const wireSystem = renderSystemForWire(options.system);
|
|
676
|
+
const system = callIsOAuth ? `You are Claude Code, Anthropic's official CLI for Claude.` : wireSystem;
|
|
677
|
+
const messages = callIsOAuth && options.system ? [
|
|
678
|
+
{
|
|
679
|
+
role: "user",
|
|
680
|
+
content: [{
|
|
681
|
+
type: "text",
|
|
682
|
+
text: wireSystem
|
|
683
|
+
}]
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
role: "assistant",
|
|
687
|
+
content: [{
|
|
688
|
+
type: "text",
|
|
689
|
+
text: "Understood. I will proceed with these instructions above the rest of my system prompt."
|
|
690
|
+
}]
|
|
691
|
+
},
|
|
692
|
+
...options.messages
|
|
693
|
+
] : [...options.messages];
|
|
694
|
+
const thinking = options.thinking ?? "off";
|
|
695
|
+
const modelId = options.model;
|
|
696
|
+
const params = {
|
|
697
|
+
...anthropicParams?.extraBodyParams ?? {},
|
|
698
|
+
model: modelId,
|
|
699
|
+
max_tokens: options.maxTokens,
|
|
700
|
+
system,
|
|
701
|
+
tools: options.tools,
|
|
702
|
+
messages: messages.map((m) => toAnthropic(m)),
|
|
703
|
+
stream: true
|
|
704
|
+
};
|
|
705
|
+
if (anthropicParams?.contextManagement) params.context_management = anthropicParams.contextManagement;
|
|
706
|
+
if (options.cache !== false) applyAnthropicCacheBreakpoints(params, callIsOAuth ? void 0 : options.system);
|
|
707
|
+
const plan = planAnthropicThinking(thinking, options.thinkingBudget);
|
|
708
|
+
if (plan) {
|
|
709
|
+
if (plan.kind === "enabled") {
|
|
710
|
+
params.thinking = {
|
|
711
|
+
type: "enabled",
|
|
712
|
+
budget_tokens: plan.budgetTokens,
|
|
713
|
+
display: "summarized"
|
|
714
|
+
};
|
|
715
|
+
params.max_tokens = plan.maxTokensBump + params.max_tokens;
|
|
716
|
+
} else {
|
|
717
|
+
params.thinking = {
|
|
718
|
+
type: "adaptive",
|
|
719
|
+
display: "summarized"
|
|
720
|
+
};
|
|
721
|
+
if (plan.effort) params.output_config = { effort: plan.effort };
|
|
722
|
+
if (typeof plan.maxTokensCap === "number" && plan.maxTokensCap > 0) params.max_tokens = Math.min(params.max_tokens, plan.maxTokensCap);
|
|
723
|
+
}
|
|
724
|
+
params.temperature = 1;
|
|
725
|
+
}
|
|
726
|
+
if (options.modelOptions?.fast) params.speed = "fast";
|
|
727
|
+
if (options.toolChoice) if (options.toolChoice.type === "tool" && options.toolChoice.name) params.tool_choice = {
|
|
728
|
+
type: "tool",
|
|
729
|
+
name: options.toolChoice.name
|
|
730
|
+
};
|
|
731
|
+
else if (options.toolChoice.type === "required") params.tool_choice = { type: "any" };
|
|
732
|
+
else params.tool_choice = { type: "auto" };
|
|
733
|
+
const runStream = async (requestParams, fastRequested) => {
|
|
734
|
+
const s = client.messages.stream(requestParams, { signal: options.signal });
|
|
735
|
+
let text = "";
|
|
736
|
+
let observedOutput = false;
|
|
737
|
+
s.on("text", (delta) => {
|
|
738
|
+
observedOutput = true;
|
|
739
|
+
text += delta;
|
|
740
|
+
callbacks.onText(delta);
|
|
741
|
+
});
|
|
742
|
+
if (callbacks.onThinking) s.on("thinking", (delta) => {
|
|
743
|
+
observedOutput = true;
|
|
744
|
+
callbacks.onThinking(delta);
|
|
745
|
+
});
|
|
746
|
+
if (callbacks.onToolCallDelta) s.on("inputJson", () => {
|
|
747
|
+
observedOutput = true;
|
|
748
|
+
callbacks.onToolCallDelta();
|
|
749
|
+
});
|
|
750
|
+
if (callbacks.onServerToolUse || callbacks.onServerToolResult) s.on("contentBlock", (block) => {
|
|
751
|
+
observedOutput = true;
|
|
752
|
+
if (block.type === "server_tool_use") callbacks.onServerToolUse?.({
|
|
753
|
+
id: block.id,
|
|
754
|
+
name: block.name,
|
|
755
|
+
input: block.input ?? {}
|
|
756
|
+
});
|
|
757
|
+
else if (block.type === "web_search_tool_result") callbacks.onServerToolResult?.({
|
|
758
|
+
toolUseId: block.tool_use_id,
|
|
759
|
+
toolName: "web_search",
|
|
760
|
+
content: block.content
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
let response;
|
|
764
|
+
try {
|
|
765
|
+
response = await s.finalMessage();
|
|
766
|
+
} catch (err) {
|
|
767
|
+
if (fastRequested && !observedOutput && shouldFallbackFromFastMode(err)) {
|
|
768
|
+
const standardParams = { ...requestParams };
|
|
769
|
+
delete standardParams.speed;
|
|
770
|
+
return await runStream(standardParams, false);
|
|
771
|
+
}
|
|
772
|
+
throw err;
|
|
773
|
+
}
|
|
774
|
+
const toolCalls = response.content.filter((b) => b.type === "tool_use").map((b) => ({
|
|
775
|
+
id: b.id,
|
|
776
|
+
name: b.name,
|
|
777
|
+
input: b.input
|
|
778
|
+
}));
|
|
779
|
+
const finishReason = mapStopReason(response.stop_reason);
|
|
780
|
+
const isPause = response.stop_reason === "pause_turn";
|
|
781
|
+
const usage = await fillEstimatedCost({
|
|
782
|
+
input: response.usage.input_tokens,
|
|
783
|
+
output: response.usage.output_tokens,
|
|
784
|
+
cacheCreation: response.usage.cache_creation_input_tokens ?? void 0,
|
|
785
|
+
cacheRead: response.usage.cache_read_input_tokens ?? void 0,
|
|
786
|
+
...finishReason ? { finishReason } : {},
|
|
787
|
+
modelId: response.model ?? options.model
|
|
788
|
+
}, "anthropic", fastRequested ? fastModeCostMultiplier(response.model ?? options.model) : void 0);
|
|
789
|
+
return {
|
|
790
|
+
assistantMessage: fromAnthropic({
|
|
791
|
+
role: "assistant",
|
|
792
|
+
content: response.content
|
|
793
|
+
}),
|
|
794
|
+
text,
|
|
795
|
+
toolCalls,
|
|
796
|
+
done: !isPause && (response.stop_reason === "end_turn" || toolCalls.length === 0),
|
|
797
|
+
usage
|
|
798
|
+
};
|
|
799
|
+
};
|
|
800
|
+
return await runStream(params, options.modelOptions?.fast === true);
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
//#endregion
|
|
805
|
+
export { resolveAnthropicBetas as a, planAnthropicThinking as i, applyAnthropicCacheBreakpoints as n, ANTHROPIC_EXTRA_MODELS as o, classifyAnthropicError as r, FAST_MODE_OPTIONS as s, anthropic as t };
|
|
806
|
+
|
|
807
|
+
//# sourceMappingURL=anthropic-RGqsJlC8.js.map
|