@swifty.js/swifty 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/{agent-ZCMBUWLZ.js → agent-RABW3Z3R.js} +1 -1
  2. package/dist/anthropic-V22T6E6E.js +4 -0
  3. package/dist/{checker-OJFS2MZY.js → checker-3RWAYF2U.js} +1 -1
  4. package/dist/{chunk-Y5WGBAGP.js → chunk-5KXBVL2A.js} +1 -1
  5. package/dist/{chunk-MUPVOATV.js → chunk-CV7OKXYC.js} +1 -1
  6. package/dist/chunk-MJR2C7S7.js +130 -0
  7. package/dist/{chunk-GUXBLUNR.js → chunk-NR62AA5K.js} +1 -1
  8. package/dist/{chunk-S6ZADC7C.js → chunk-OCQ6TYLT.js} +21 -20
  9. package/dist/chunk-QN27QEFS.js +4 -0
  10. package/dist/{chunk-LX2LK3TF.js → chunk-SIWLMWHH.js} +12 -11
  11. package/dist/chunk-Y43POSIW.js +389 -0
  12. package/dist/lib/agent-GHXYOARN.js +9 -0
  13. package/dist/lib/anthropic-7YEN2QTU.js +22 -0
  14. package/dist/lib/bwrap-QRGPUP4J.js +6 -0
  15. package/dist/lib/checker-6BW4222R.js +19 -0
  16. package/dist/lib/chunk-7IZEK7X5.js +545 -0
  17. package/dist/lib/chunk-BN4Q54G2.js +1096 -0
  18. package/dist/lib/chunk-BO76JDFZ.js +617 -0
  19. package/dist/lib/chunk-CYDNMEBC.js +339 -0
  20. package/dist/lib/chunk-EY7HE52Q.js +384 -0
  21. package/dist/lib/chunk-GHF2PSEW.js +8 -0
  22. package/dist/lib/chunk-GNI7YX6F.js +426 -0
  23. package/dist/lib/chunk-HK2Z6WP4.js +223 -0
  24. package/dist/lib/chunk-MF3YLQDS.js +1277 -0
  25. package/dist/lib/chunk-OO2CLOEE.js +88 -0
  26. package/dist/lib/chunk-ORSYNBMM.js +38 -0
  27. package/dist/lib/chunk-PZ42NAFA.js +242 -0
  28. package/dist/lib/chunk-VUD72RTY.js +39 -0
  29. package/dist/lib/glob.wasm +0 -0
  30. package/dist/lib/index.d.ts +5347 -0
  31. package/dist/lib/index.js +11990 -0
  32. package/dist/lib/openai-RDHZFJUT.js +15 -0
  33. package/dist/lib/seatbelt-FT5IY73W.js +6 -0
  34. package/dist/lib/tool-filter-VF7TZRE5.js +19 -0
  35. package/dist/main.js +204 -466
  36. package/dist/{openai-HXD52MZB.js → openai-6NWVJ5LI.js} +15 -15
  37. package/dist/{server-VMHWEO2Y.js → server-KQTLAXC6.js} +17 -17
  38. package/package.json +18 -6
  39. package/dist/anthropic-4ZVG7AMA.js +0 -4
  40. package/dist/chunk-6E6UA7MT.js +0 -126
  41. package/dist/chunk-DDBH5AEN.js +0 -386
  42. package/dist/chunk-ZZQE743W.js +0 -4
@@ -0,0 +1,9 @@
1
+ import {
2
+ Agent
3
+ } from "./chunk-MF3YLQDS.js";
4
+ import "./chunk-BN4Q54G2.js";
5
+ import "./chunk-OO2CLOEE.js";
6
+ import "./chunk-EY7HE52Q.js";
7
+ export {
8
+ Agent
9
+ };
@@ -0,0 +1,22 @@
1
+ import {
2
+ AnthropicClient,
3
+ buildAnthropicMessages,
4
+ fetchModelContextWindow,
5
+ markLastUserTailForCache,
6
+ markToolsForCache,
7
+ needsToolSearchBeta
8
+ } from "./chunk-7IZEK7X5.js";
9
+ import "./chunk-BN4Q54G2.js";
10
+ import "./chunk-CYDNMEBC.js";
11
+ import "./chunk-OO2CLOEE.js";
12
+ import "./chunk-PZ42NAFA.js";
13
+ import "./chunk-EY7HE52Q.js";
14
+ import "./chunk-GHF2PSEW.js";
15
+ export {
16
+ AnthropicClient,
17
+ buildAnthropicMessages,
18
+ fetchModelContextWindow,
19
+ markLastUserTailForCache,
20
+ markToolsForCache,
21
+ needsToolSearchBeta
22
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ BwrapSandbox
3
+ } from "./chunk-ORSYNBMM.js";
4
+ export {
5
+ BwrapSandbox
6
+ };
@@ -0,0 +1,19 @@
1
+ import {
2
+ PathSandbox,
3
+ PermissionChecker,
4
+ RuleEngine,
5
+ evaluateRules,
6
+ extractContent,
7
+ isSafeCommand
8
+ } from "./chunk-GNI7YX6F.js";
9
+ import "./chunk-PZ42NAFA.js";
10
+ import "./chunk-EY7HE52Q.js";
11
+ import "./chunk-GHF2PSEW.js";
12
+ export {
13
+ PathSandbox,
14
+ PermissionChecker,
15
+ RuleEngine,
16
+ evaluateRules,
17
+ extractContent,
18
+ isSafeCommand
19
+ };
@@ -0,0 +1,545 @@
1
+ import {
2
+ computeCompactThreshold
3
+ } from "./chunk-BN4Q54G2.js";
4
+ import {
5
+ getContextWindow,
6
+ getMaxOutputTokens,
7
+ resolveAPIKey
8
+ } from "./chunk-CYDNMEBC.js";
9
+ import {
10
+ AuthenticationError,
11
+ ContextTooLongError,
12
+ LLMError,
13
+ NetworkError,
14
+ RateLimitError,
15
+ ensureToolPairing
16
+ } from "./chunk-OO2CLOEE.js";
17
+ import {
18
+ MCP_TOOL_PREFIX,
19
+ isMcpToolLike
20
+ } from "./chunk-PZ42NAFA.js";
21
+ import {
22
+ asErrorString,
23
+ asRecord,
24
+ asString,
25
+ contentToText,
26
+ createChildLogger,
27
+ isRecord
28
+ } from "./chunk-EY7HE52Q.js";
29
+
30
+ // src/llm/anthropic.ts
31
+ import Anthropic from "@anthropic-ai/sdk";
32
+ import { safeParseAsync, z } from "zod";
33
+
34
+ // src/mcp/strategy.ts
35
+ var DEFAULT_EAGER_THRESHOLD_PERCENT = 10;
36
+ var CHARS_PER_TOKEN = 2.5;
37
+ var NATIVE_TOOL_USE_BETA = "advanced-tool-use-2025-11-20";
38
+ var OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["api.anthropic.com"]);
39
+ var ENV_OVERRIDE = "SWIFTY_MCP_LOADING";
40
+ function isOfficialAnthropicEndpoint(baseUrl) {
41
+ if (!baseUrl) {
42
+ return true;
43
+ }
44
+ try {
45
+ return OFFICIAL_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ function estimateSchemaTokens(schemaChars) {
51
+ return Math.floor(schemaChars / CHARS_PER_TOKEN);
52
+ }
53
+ function decideMode(baseUrl, contextWindow, mcpSchemaChars, thresholdPercent = DEFAULT_EAGER_THRESHOLD_PERCENT) {
54
+ const override = (process.env[ENV_OVERRIDE] ?? "").trim().toLowerCase();
55
+ if (override === "eager" || override === "native" || override === "dispatch") {
56
+ return override;
57
+ }
58
+ if (mcpSchemaChars <= 0) {
59
+ return "eager";
60
+ }
61
+ const budget = contextWindow * thresholdPercent / 100;
62
+ if (estimateSchemaTokens(mcpSchemaChars) < budget) {
63
+ return "eager";
64
+ }
65
+ return isOfficialAnthropicEndpoint(baseUrl) ? "native" : "dispatch";
66
+ }
67
+ function measureSchemaChars(registry) {
68
+ let total = 0;
69
+ for (const tool of registry.listTools()) {
70
+ if (!tool.name.startsWith(MCP_TOOL_PREFIX)) {
71
+ continue;
72
+ }
73
+ try {
74
+ total += JSON.stringify(tool.schema()).length;
75
+ } catch {
76
+ total += tool.name.length + (tool.description?.length ?? 0);
77
+ }
78
+ }
79
+ return total;
80
+ }
81
+ function applyMode(registry, mode) {
82
+ registry.mcpLoadingMode = mode;
83
+ const eager = mode === "eager";
84
+ for (const tool of registry.listTools()) {
85
+ if (isMcpToolLike(tool) && typeof tool.setDeferLoading === "function") {
86
+ tool.setDeferLoading(!eager);
87
+ }
88
+ }
89
+ registry.exposeToolSearch = !eager;
90
+ registry.exposeMcpCall = mode === "dispatch";
91
+ }
92
+ function decideAndApply(registry, baseUrl, contextWindow) {
93
+ const mode = decideMode(baseUrl, contextWindow, measureSchemaChars(registry));
94
+ applyMode(registry, mode);
95
+ return mode;
96
+ }
97
+
98
+ // src/llm/anthropic.ts
99
+ function markToolsForCache(tools) {
100
+ for (let i = tools.length - 1; i >= 0; i--) {
101
+ const t = tools[i];
102
+ if (t.defer_loading === true) {
103
+ continue;
104
+ }
105
+ t.cache_control = { type: "ephemeral" };
106
+ return;
107
+ }
108
+ }
109
+ function needsToolSearchBeta(toolSchemas) {
110
+ return toolSchemas.some((s) => s.defer_loading);
111
+ }
112
+ var log = createChildLogger({ module: "llm" });
113
+ var MODEL_FETCH_TIMEOUT_MS = 3e3;
114
+ var ModelContextWindowResSchema = z.object({
115
+ max_input_tokens: z.coerce.number()
116
+ });
117
+ async function fetchModelContextWindow(config) {
118
+ if (config.protocol !== "anthropic") {
119
+ return 0;
120
+ }
121
+ const apiKey = resolveAPIKey(config);
122
+ const base = config.base_url.replace(/\/+$/, "");
123
+ const url = `${base}/v1/models/${encodeURIComponent(config.model)}`;
124
+ const controller = new AbortController();
125
+ const timer = setTimeout(() => {
126
+ controller.abort();
127
+ }, MODEL_FETCH_TIMEOUT_MS);
128
+ try {
129
+ const res = await fetch(url, {
130
+ method: "GET",
131
+ headers: {
132
+ "anthropic-version": "2023-06-01",
133
+ ...apiKey ? { "x-api-key": apiKey } : {}
134
+ },
135
+ signal: controller.signal
136
+ });
137
+ if (!res.ok) {
138
+ return 0;
139
+ }
140
+ const body = await res.json();
141
+ const { success, error, data } = await safeParseAsync(ModelContextWindowResSchema, body);
142
+ if (!success) {
143
+ log.warn({ message: error.message }, "model context window schema validation failed");
144
+ return 0;
145
+ }
146
+ const maxInputTokens = data.max_input_tokens;
147
+ return Math.max(maxInputTokens, 0);
148
+ } catch (err) {
149
+ log.error({ err }, "failed to fetch model context window");
150
+ return 0;
151
+ } finally {
152
+ clearTimeout(timer);
153
+ }
154
+ }
155
+ function supportsAdaptiveThinking() {
156
+ return true;
157
+ }
158
+ function userBlocksFor(content) {
159
+ if (typeof content === "string") {
160
+ return [{ type: "text", text: content }];
161
+ }
162
+ return content;
163
+ }
164
+ function buildAnthropicMessages(messages) {
165
+ const result = [];
166
+ for (const m of messages) {
167
+ if (m.role === "assistant") {
168
+ const blocks = [];
169
+ if (m.thinkingBlocks) {
170
+ for (const tb of m.thinkingBlocks) {
171
+ blocks.push({
172
+ type: "thinking",
173
+ thinking: tb.thinking,
174
+ signature: tb.signature
175
+ });
176
+ }
177
+ }
178
+ const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
179
+ if (assistantText) {
180
+ blocks.push({
181
+ type: "text",
182
+ text: assistantText
183
+ });
184
+ }
185
+ if (m.toolUses) {
186
+ for (const tu of m.toolUses) {
187
+ blocks.push({
188
+ type: "tool_use",
189
+ // tool use **request**
190
+ id: tu.toolUseId,
191
+ name: tu.toolName,
192
+ input: tu.arguments
193
+ });
194
+ }
195
+ }
196
+ if (blocks.length === 0) {
197
+ blocks.push({ type: "text", text: "" });
198
+ }
199
+ result.push({ role: "assistant", content: blocks });
200
+ } else if (m.toolResults && m.toolResults.length > 0) {
201
+ const blocks = [];
202
+ for (const tr of m.toolResults) {
203
+ blocks.push({
204
+ type: "tool_result",
205
+ // tool result
206
+ tool_use_id: tr.toolUseId,
207
+ is_error: tr.isError,
208
+ content: tr.contentBlocks?.length ? tr.contentBlocks : tr.content
209
+ });
210
+ }
211
+ result.push({ role: "user", content: blocks });
212
+ } else {
213
+ if (result.length === 0) {
214
+ result.push({
215
+ role: "user",
216
+ content: userBlocksFor(m.content)
217
+ });
218
+ continue;
219
+ }
220
+ let canMerge = false;
221
+ const prev = result[result.length - 1];
222
+ let content = prev.content;
223
+ if (prev.role === "user" && (typeof content === "string" || Array.isArray(content) && content.length > 0 && // content[0].type !== "tool_result"
224
+ (content[0].type === "text" || content[0].type === "image"))) {
225
+ canMerge = true;
226
+ }
227
+ if (canMerge) {
228
+ if (typeof content === "string") {
229
+ content = prev.content = content.trim().length > 0 ? [
230
+ {
231
+ type: "text",
232
+ text: content
233
+ }
234
+ ] : [];
235
+ }
236
+ content.push(...userBlocksFor(m.content));
237
+ } else {
238
+ result.push({
239
+ role: "user",
240
+ content: userBlocksFor(m.content)
241
+ });
242
+ }
243
+ }
244
+ }
245
+ return result;
246
+ }
247
+ var AnthropicClient = class {
248
+ client;
249
+ model;
250
+ /**
251
+ * Whether supports/enable thinking, default false
252
+ */
253
+ thinking;
254
+ systemPrompt;
255
+ maxOutputTokens;
256
+ /** Currently not used */
257
+ contextWindow;
258
+ constructor(config, systemPrompt) {
259
+ const apiKey = resolveAPIKey(config);
260
+ if (!apiKey) {
261
+ throw new AuthenticationError(
262
+ "Anthropic API key not found, set ANTHROPIC_API_KEY in .swifty/config.y(a)ml, or via ANTHROPIC_API_KEY env variable."
263
+ );
264
+ }
265
+ this.client = new Anthropic({
266
+ apiKey,
267
+ baseURL: config.base_url
268
+ });
269
+ this.model = config.model;
270
+ this.thinking = config.thinking ?? true;
271
+ this.systemPrompt = systemPrompt;
272
+ this.maxOutputTokens = getMaxOutputTokens(config);
273
+ this.contextWindow = getContextWindow(config);
274
+ }
275
+ setSystemPrompt(prompt) {
276
+ this.systemPrompt = prompt;
277
+ }
278
+ setMaxOutputTokens(maxTokens) {
279
+ this.maxOutputTokens = maxTokens;
280
+ }
281
+ async *stream(conversation, toolSchemas, abortSignal) {
282
+ const messages = buildAnthropicMessages(ensureToolPairing(conversation.getMessages()));
283
+ const sendToolSearchBeta = needsToolSearchBeta(toolSchemas);
284
+ const antToolSchemas = toolSchemas.map((s) => {
285
+ const inputSchema = s.input_schema;
286
+ const tool = {
287
+ name: s.name,
288
+ description: s.description,
289
+ input_schema: {
290
+ type: "object",
291
+ properties: inputSchema.properties,
292
+ required: inputSchema.required ?? []
293
+ }
294
+ };
295
+ if (s.defer_loading === true) {
296
+ tool.defer_loading = true;
297
+ }
298
+ return tool;
299
+ });
300
+ markToolsForCache(antToolSchemas);
301
+ markLastUserTailForCache(messages);
302
+ const params = {
303
+ model: this.model,
304
+ max_tokens: this.maxOutputTokens,
305
+ stream: true,
306
+ system: [
307
+ {
308
+ type: "text",
309
+ text: this.systemPrompt,
310
+ cache_control: {
311
+ type: "ephemeral"
312
+ // Prompt cache
313
+ }
314
+ }
315
+ ],
316
+ messages,
317
+ ...antToolSchemas.length > 0 ? { tools: antToolSchemas } : {}
318
+ };
319
+ if (this.thinking) {
320
+ if (supportsAdaptiveThinking()) {
321
+ params.thinking = {
322
+ type: "enabled",
323
+ budget_tokens: computeCompactThreshold(this.contextWindow, this.maxOutputTokens)
324
+ };
325
+ }
326
+ } else {
327
+ params.thinking = {
328
+ type: "enabled",
329
+ budget_tokens: computeCompactThreshold(this.contextWindow, this.maxOutputTokens)
330
+ };
331
+ }
332
+ let inputTokens = 0;
333
+ let outputTokens = 0;
334
+ let cacheReadInputTokens = 0;
335
+ let cacheCreationInputTokens = 0;
336
+ let stopReason = "end_turn";
337
+ let thinkingAccumulate = "";
338
+ let thinkingSignature = "";
339
+ let inThinking = false;
340
+ try {
341
+ const response = this.client.messages.stream(params, {
342
+ ...abortSignal ? { signal: abortSignal } : {},
343
+ // If any tool uses defer_loading this beta header is required, otherwise the server does not recognize the field.
344
+ // Only the official endpoint reaches this code path (see mcp/strategy).
345
+ ...sendToolSearchBeta ? { headers: { "anthropic-beta": NATIVE_TOOL_USE_BETA } } : {}
346
+ });
347
+ let currentToolName = "";
348
+ let currentToolId = "";
349
+ let jsonAccumulate = "";
350
+ for await (const event of response) {
351
+ switch (event.type) {
352
+ case "content_block_start": {
353
+ const block = event.content_block;
354
+ if (block.type === "thinking") {
355
+ inThinking = true;
356
+ thinkingAccumulate = "";
357
+ thinkingSignature = "";
358
+ } else if (block.type === "tool_use") {
359
+ currentToolId = block.id;
360
+ currentToolName = block.name;
361
+ jsonAccumulate = "";
362
+ yield {
363
+ type: "tool_call_start",
364
+ toolName: currentToolName,
365
+ toolId: currentToolId
366
+ };
367
+ }
368
+ break;
369
+ }
370
+ // end case "content_block_start"
371
+ case "content_block_delta": {
372
+ const delta = event.delta;
373
+ if (delta.type === "thinking_delta") {
374
+ thinkingAccumulate += delta.thinking;
375
+ yield {
376
+ type: "thinking_delta",
377
+ text: delta.thinking
378
+ };
379
+ } else if (delta.type === "signature_delta") {
380
+ thinkingSignature = delta.signature;
381
+ } else if (delta.type === "text_delta") {
382
+ yield {
383
+ type: "text_delta",
384
+ text: delta.text
385
+ };
386
+ } else if (delta.type === "input_json_delta") {
387
+ jsonAccumulate += delta.partial_json;
388
+ yield {
389
+ type: "tool_call_delta",
390
+ text: delta.partial_json
391
+ };
392
+ }
393
+ break;
394
+ }
395
+ // end case "content_block_delta"
396
+ case "content_block_stop": {
397
+ if (inThinking) {
398
+ yield {
399
+ type: "thinking_complete",
400
+ thinking: thinkingAccumulate,
401
+ signature: thinkingSignature
402
+ };
403
+ inThinking = false;
404
+ }
405
+ if (currentToolName) {
406
+ let args = {};
407
+ if (jsonAccumulate) {
408
+ try {
409
+ const parsed = JSON.parse(jsonAccumulate);
410
+ args = isRecord(parsed) ? asRecord(parsed) : {};
411
+ } catch (err) {
412
+ log.error({ err }, "llm operation failed");
413
+ args = {};
414
+ }
415
+ }
416
+ yield {
417
+ type: "tool_call_complete",
418
+ toolId: currentToolId,
419
+ toolName: currentToolName,
420
+ arguments: args
421
+ };
422
+ currentToolName = "";
423
+ currentToolId = "";
424
+ jsonAccumulate = "";
425
+ }
426
+ break;
427
+ }
428
+ // end case "content_block_stop"
429
+ case "message_delta": {
430
+ if (event.delta.stop_reason) {
431
+ stopReason = event.delta.stop_reason;
432
+ }
433
+ if (event.usage.output_tokens) {
434
+ outputTokens = event.usage.output_tokens;
435
+ if (event.usage.input_tokens) {
436
+ inputTokens = event.usage.input_tokens;
437
+ }
438
+ if (event.usage.cache_read_input_tokens) {
439
+ cacheReadInputTokens = event.usage.cache_read_input_tokens;
440
+ }
441
+ if (event.usage.cache_creation_input_tokens) {
442
+ cacheCreationInputTokens = event.usage.cache_creation_input_tokens;
443
+ }
444
+ }
445
+ break;
446
+ }
447
+ // end case "message_delta"
448
+ case "message_start": {
449
+ inputTokens = event.message.usage.input_tokens;
450
+ outputTokens = event.message.usage.output_tokens;
451
+ cacheReadInputTokens = event.message.usage.cache_read_input_tokens ?? 0;
452
+ cacheCreationInputTokens = event.message.usage.cache_creation_input_tokens ?? 0;
453
+ break;
454
+ }
455
+ }
456
+ }
457
+ yield {
458
+ type: "stream_end",
459
+ stopReason,
460
+ usage: {
461
+ inputTokens,
462
+ outputTokens,
463
+ cacheReadInputTokens,
464
+ cacheCreationInputTokens
465
+ }
466
+ };
467
+ } catch (err) {
468
+ log.error({ err }, "llm operation failed");
469
+ throw classifyAnthropicError(err);
470
+ }
471
+ }
472
+ };
473
+ function markLastUserTailForCache(messages) {
474
+ for (let i = messages.length - 1; i >= 0; i--) {
475
+ if (messages[i].role !== "user") {
476
+ continue;
477
+ }
478
+ let content = messages[i].content;
479
+ if (typeof content === "string" && content.length === 0 || Array.isArray(content) && content.length === 0) {
480
+ return;
481
+ }
482
+ if (typeof content === "string") {
483
+ content = messages[i].content = [
484
+ {
485
+ type: "text",
486
+ text: content
487
+ }
488
+ ];
489
+ }
490
+ let last = content[content.length - 1];
491
+ for (let j = content.length - 1; j >= 0; j--) {
492
+ if (content[j].type !== "image") {
493
+ last = content[j];
494
+ break;
495
+ }
496
+ }
497
+ Reflect.set(last, "cache_control", {
498
+ type: "ephemeral"
499
+ });
500
+ }
501
+ }
502
+ function classifyAnthropicError(err) {
503
+ if (err instanceof Anthropic.APIError) {
504
+ if (err.status === 413 /* PromptTooLong */ || /prompts?\s+too\s+long/i.test(err.message)) {
505
+ return new ContextTooLongError(`Prompt too long: ${err.message}`);
506
+ }
507
+ if (err.status === 401 /* InvalidAPIKey */) {
508
+ return new AuthenticationError(`Invalid API key: ${err.message}`);
509
+ }
510
+ if (err.status === 429 /* RateLimitError */) {
511
+ const retryAfter = asRecord(err.headers)["retry-after"];
512
+ let message = "Rate Limited";
513
+ if (retryAfter) {
514
+ const s = Number.parseInt(asString(retryAfter));
515
+ if (Number.isNaN(s)) {
516
+ message += ", please wait.";
517
+ }
518
+ message += `, retry after ${asString(s)}s.`;
519
+ } else {
520
+ message += ", please wait.";
521
+ }
522
+ return new RateLimitError(message, retryAfter ? asString(retryAfter) : void 0);
523
+ }
524
+ return new LLMError(`Anthropic API error (${asString(err.status)}): ${err.message}`);
525
+ }
526
+ return new NetworkError(`Network error: ${asErrorString(err)}`);
527
+ }
528
+
529
+ export {
530
+ DEFAULT_EAGER_THRESHOLD_PERCENT,
531
+ CHARS_PER_TOKEN,
532
+ NATIVE_TOOL_USE_BETA,
533
+ isOfficialAnthropicEndpoint,
534
+ estimateSchemaTokens,
535
+ decideMode,
536
+ measureSchemaChars,
537
+ applyMode,
538
+ decideAndApply,
539
+ markToolsForCache,
540
+ needsToolSearchBeta,
541
+ fetchModelContextWindow,
542
+ buildAnthropicMessages,
543
+ AnthropicClient,
544
+ markLastUserTailForCache
545
+ };