anyclaude-sdk 0.1.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.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/agent.d.ts +110 -0
  4. package/dist/agent.js +897 -0
  5. package/dist/background/index.d.ts +3 -0
  6. package/dist/background/index.js +9 -0
  7. package/dist/background/manager.d.ts +32 -0
  8. package/dist/background/manager.js +108 -0
  9. package/dist/background/tools.d.ts +5 -0
  10. package/dist/background/tools.js +98 -0
  11. package/dist/background/worker.d.ts +19 -0
  12. package/dist/background/worker.js +30 -0
  13. package/dist/commands/builtins.d.ts +2 -0
  14. package/dist/commands/builtins.js +306 -0
  15. package/dist/commands/index.d.ts +21 -0
  16. package/dist/commands/index.js +56 -0
  17. package/dist/commands/types.d.ts +110 -0
  18. package/dist/commands/types.js +5 -0
  19. package/dist/compact.d.ts +22 -0
  20. package/dist/compact.js +67 -0
  21. package/dist/fs/dexie.d.ts +57 -0
  22. package/dist/fs/dexie.js +243 -0
  23. package/dist/fs/index.d.ts +4 -0
  24. package/dist/fs/index.js +13 -0
  25. package/dist/fs/linuxTree.d.ts +11 -0
  26. package/dist/fs/linuxTree.js +43 -0
  27. package/dist/fs/opfs.d.ts +23 -0
  28. package/dist/fs/opfs.js +112 -0
  29. package/dist/index.d.ts +26 -0
  30. package/dist/index.js +29 -0
  31. package/dist/llm/anthropic.d.ts +24 -0
  32. package/dist/llm/anthropic.js +280 -0
  33. package/dist/llm/index.d.ts +3 -0
  34. package/dist/llm/index.js +3 -0
  35. package/dist/llm/inlineTools.d.ts +11 -0
  36. package/dist/llm/inlineTools.js +72 -0
  37. package/dist/llm/openai.d.ts +29 -0
  38. package/dist/llm/openai.js +224 -0
  39. package/dist/llm/responses.d.ts +18 -0
  40. package/dist/llm/responses.js +256 -0
  41. package/dist/mcp/client.d.ts +20 -0
  42. package/dist/mcp/client.js +156 -0
  43. package/dist/mcp/index.d.ts +24 -0
  44. package/dist/mcp/index.js +157 -0
  45. package/dist/mcp/proxy.d.ts +3 -0
  46. package/dist/mcp/proxy.js +25 -0
  47. package/dist/mcp/sdkServer.d.ts +21 -0
  48. package/dist/mcp/sdkServer.js +28 -0
  49. package/dist/mcp/types.d.ts +92 -0
  50. package/dist/mcp/types.js +5 -0
  51. package/dist/memory/index.d.ts +4 -0
  52. package/dist/memory/index.js +5 -0
  53. package/dist/memory/render.d.ts +7 -0
  54. package/dist/memory/render.js +46 -0
  55. package/dist/memory/store.d.ts +20 -0
  56. package/dist/memory/store.js +79 -0
  57. package/dist/memory/tools.d.ts +5 -0
  58. package/dist/memory/tools.js +95 -0
  59. package/dist/memory/types.d.ts +15 -0
  60. package/dist/memory/types.js +4 -0
  61. package/dist/permissions/dangerous.d.ts +4 -0
  62. package/dist/permissions/dangerous.js +24 -0
  63. package/dist/permissions/gate.d.ts +21 -0
  64. package/dist/permissions/gate.js +66 -0
  65. package/dist/permissions/index.d.ts +5 -0
  66. package/dist/permissions/index.js +6 -0
  67. package/dist/permissions/match.d.ts +19 -0
  68. package/dist/permissions/match.js +104 -0
  69. package/dist/permissions/planMode.d.ts +3 -0
  70. package/dist/permissions/planMode.js +33 -0
  71. package/dist/permissions/types.d.ts +19 -0
  72. package/dist/permissions/types.js +2 -0
  73. package/dist/persist.d.ts +15 -0
  74. package/dist/persist.js +58 -0
  75. package/dist/prompt.d.ts +6 -0
  76. package/dist/prompt.js +34 -0
  77. package/dist/query.d.ts +105 -0
  78. package/dist/query.js +115 -0
  79. package/dist/queue.d.ts +23 -0
  80. package/dist/queue.js +43 -0
  81. package/dist/sandbox/cloudflare.d.ts +48 -0
  82. package/dist/sandbox/cloudflare.js +124 -0
  83. package/dist/sandbox/daytona.d.ts +48 -0
  84. package/dist/sandbox/daytona.js +79 -0
  85. package/dist/sandbox/e2b.d.ts +54 -0
  86. package/dist/sandbox/e2b.js +87 -0
  87. package/dist/sandbox/index.d.ts +8 -0
  88. package/dist/sandbox/index.js +19 -0
  89. package/dist/sandbox/local.d.ts +51 -0
  90. package/dist/sandbox/local.js +155 -0
  91. package/dist/sandbox/types.d.ts +18 -0
  92. package/dist/sandbox/types.js +27 -0
  93. package/dist/sandbox/util.d.ts +15 -0
  94. package/dist/sandbox/util.js +100 -0
  95. package/dist/sandbox/vercel.d.ts +48 -0
  96. package/dist/sandbox/vercel.js +130 -0
  97. package/dist/session/index.d.ts +2 -0
  98. package/dist/session/index.js +6 -0
  99. package/dist/session/store.d.ts +28 -0
  100. package/dist/session/store.js +122 -0
  101. package/dist/session/types.d.ts +22 -0
  102. package/dist/session/types.js +2 -0
  103. package/dist/settings/index.d.ts +3 -0
  104. package/dist/settings/index.js +3 -0
  105. package/dist/settings/load.d.ts +20 -0
  106. package/dist/settings/load.js +36 -0
  107. package/dist/settings/merge.d.ts +13 -0
  108. package/dist/settings/merge.js +65 -0
  109. package/dist/settings/types.d.ts +17 -0
  110. package/dist/settings/types.js +3 -0
  111. package/dist/skills/index.d.ts +4 -0
  112. package/dist/skills/index.js +5 -0
  113. package/dist/skills/load.d.ts +23 -0
  114. package/dist/skills/load.js +54 -0
  115. package/dist/skills/parse.d.ts +7 -0
  116. package/dist/skills/parse.js +40 -0
  117. package/dist/skills/tool.d.ts +2 -0
  118. package/dist/skills/tool.js +39 -0
  119. package/dist/skills/types.d.ts +10 -0
  120. package/dist/skills/types.js +4 -0
  121. package/dist/team/dispatch.d.ts +2 -0
  122. package/dist/team/dispatch.js +41 -0
  123. package/dist/team/index.d.ts +9 -0
  124. package/dist/team/index.js +11 -0
  125. package/dist/team/mailbox.d.ts +24 -0
  126. package/dist/team/mailbox.js +33 -0
  127. package/dist/team/prompt.d.ts +1 -0
  128. package/dist/team/prompt.js +12 -0
  129. package/dist/team/runner.d.ts +20 -0
  130. package/dist/team/runner.js +45 -0
  131. package/dist/team/taskBoard.d.ts +41 -0
  132. package/dist/team/taskBoard.js +73 -0
  133. package/dist/team/tools.d.ts +7 -0
  134. package/dist/team/tools.js +190 -0
  135. package/dist/tools/bash.d.ts +2 -0
  136. package/dist/tools/bash.js +45 -0
  137. package/dist/tools/config.d.ts +2 -0
  138. package/dist/tools/config.js +44 -0
  139. package/dist/tools/define.d.ts +18 -0
  140. package/dist/tools/define.js +21 -0
  141. package/dist/tools/delete_file.d.ts +2 -0
  142. package/dist/tools/delete_file.js +33 -0
  143. package/dist/tools/edit_file.d.ts +2 -0
  144. package/dist/tools/edit_file.js +93 -0
  145. package/dist/tools/fileTypes.d.ts +32 -0
  146. package/dist/tools/fileTypes.js +166 -0
  147. package/dist/tools/glob.d.ts +2 -0
  148. package/dist/tools/glob.js +53 -0
  149. package/dist/tools/grep.d.ts +2 -0
  150. package/dist/tools/grep.js +110 -0
  151. package/dist/tools/imageProcessor.d.ts +15 -0
  152. package/dist/tools/imageProcessor.js +83 -0
  153. package/dist/tools/index.d.ts +28 -0
  154. package/dist/tools/index.js +45 -0
  155. package/dist/tools/list_files.d.ts +2 -0
  156. package/dist/tools/list_files.js +42 -0
  157. package/dist/tools/multi_edit.d.ts +2 -0
  158. package/dist/tools/multi_edit.js +112 -0
  159. package/dist/tools/notebook_edit.d.ts +2 -0
  160. package/dist/tools/notebook_edit.js +118 -0
  161. package/dist/tools/plan_mode.d.ts +4 -0
  162. package/dist/tools/plan_mode.js +44 -0
  163. package/dist/tools/read_file.d.ts +2 -0
  164. package/dist/tools/read_file.js +193 -0
  165. package/dist/tools/task.d.ts +2 -0
  166. package/dist/tools/task.js +77 -0
  167. package/dist/tools/todo_write.d.ts +2 -0
  168. package/dist/tools/todo_write.js +104 -0
  169. package/dist/tools/tool_search.d.ts +2 -0
  170. package/dist/tools/tool_search.js +49 -0
  171. package/dist/tools/types.d.ts +82 -0
  172. package/dist/tools/types.js +1 -0
  173. package/dist/tools/walk.d.ts +29 -0
  174. package/dist/tools/walk.js +82 -0
  175. package/dist/tools/web_fetch.d.ts +2 -0
  176. package/dist/tools/web_fetch.js +76 -0
  177. package/dist/tools/web_search.d.ts +22 -0
  178. package/dist/tools/web_search.js +195 -0
  179. package/dist/tools/write_file.d.ts +2 -0
  180. package/dist/tools/write_file.js +39 -0
  181. package/dist/types/index.d.ts +363 -0
  182. package/dist/types/index.js +9 -0
  183. package/dist/util/ids.d.ts +3 -0
  184. package/dist/util/ids.js +22 -0
  185. package/dist/util/paths.d.ts +16 -0
  186. package/dist/util/paths.js +72 -0
  187. package/dist/util/pricing.d.ts +15 -0
  188. package/dist/util/pricing.js +81 -0
  189. package/dist/workspace/index.d.ts +2 -0
  190. package/dist/workspace/index.js +2 -0
  191. package/dist/workspace/memory.d.ts +28 -0
  192. package/dist/workspace/memory.js +97 -0
  193. package/dist/workspace/webcontainer.d.ts +65 -0
  194. package/dist/workspace/webcontainer.js +156 -0
  195. package/package.json +78 -0
@@ -0,0 +1,112 @@
1
+ // OpfsFileSystem — backed by the Origin Private File System (OPFS). Gives a
2
+ // true hierarchical, persistent filesystem with native binary storage; best for
3
+ // large files. Implements the SDK's FileSystem interface.
4
+ //
5
+ // OPFS has no metadata (mode/mtime) or symlinks — use DexieFileSystem when you
6
+ // need those. Reads return null on NotFound rather than throwing.
7
+ import { resolvePath, segments } from '../util/paths.js';
8
+ const encoder = new TextEncoder();
9
+ const decoder = new TextDecoder();
10
+ export class OpfsFileSystem {
11
+ constructor(options = {}) {
12
+ this.cwd = options.cwd ?? '/';
13
+ }
14
+ static isSupported() {
15
+ return typeof navigator !== 'undefined' && !!navigator.storage?.getDirectory;
16
+ }
17
+ async root() {
18
+ const nav = navigator;
19
+ return nav.storage.getDirectory();
20
+ }
21
+ /** Walk to the directory handle for `dirSegments`, optionally creating it. */
22
+ async dirHandle(dirSegments, create) {
23
+ let handle = await this.root();
24
+ for (const seg of dirSegments) {
25
+ try {
26
+ handle = await handle.getDirectoryHandle(seg, { create });
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ return handle;
33
+ }
34
+ split(path) {
35
+ const segs = segments(resolvePath(this.cwd, path));
36
+ if (segs.length === 0)
37
+ return { dir: [], name: null }; // root
38
+ return { dir: segs.slice(0, -1), name: segs[segs.length - 1] };
39
+ }
40
+ // ---- FileSystem interface ----
41
+ async readBinary(path) {
42
+ try {
43
+ const { dir, name } = this.split(path);
44
+ if (name == null)
45
+ return null;
46
+ const parent = await this.dirHandle(dir, false);
47
+ if (!parent)
48
+ return null;
49
+ const fh = await parent.getFileHandle(name, { create: false });
50
+ const file = await fh.getFile();
51
+ return new Uint8Array(await file.arrayBuffer());
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ async readFile(path) {
58
+ const bytes = await this.readBinary(path);
59
+ return bytes ? decoder.decode(bytes) : null;
60
+ }
61
+ async writeBinary(path, data) {
62
+ const { dir, name } = this.split(path);
63
+ if (name == null)
64
+ throw new Error('EISDIR: cannot write to root');
65
+ const parent = await this.dirHandle(dir, true);
66
+ if (!parent)
67
+ throw new Error(`ENOENT: cannot create '${path}'`);
68
+ const fh = await parent.getFileHandle(name, { create: true });
69
+ const w = await fh.createWritable();
70
+ await w.write(data);
71
+ await w.close();
72
+ }
73
+ async writeFile(path, contents) {
74
+ await this.writeBinary(path, encoder.encode(contents));
75
+ }
76
+ async deleteFile(path) {
77
+ const { dir, name } = this.split(path);
78
+ if (name == null)
79
+ return;
80
+ const parent = await this.dirHandle(dir, false);
81
+ if (!parent)
82
+ return;
83
+ try {
84
+ await parent.removeEntry(name, { recursive: true });
85
+ }
86
+ catch {
87
+ // already gone
88
+ }
89
+ }
90
+ async readdir(path) {
91
+ try {
92
+ const segs = segments(resolvePath(this.cwd, path));
93
+ const handle = await this.dirHandle(segs, false);
94
+ if (!handle)
95
+ return null;
96
+ const out = [];
97
+ for await (const [entryName, entry] of handle.entries()) {
98
+ out.push({ name: entryName, isDir: entry.kind === 'directory' });
99
+ }
100
+ return out;
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ async mkdir(path) {
107
+ const segs = segments(resolvePath(this.cwd, path));
108
+ const handle = await this.dirHandle(segs, true);
109
+ if (!handle)
110
+ throw new Error(`ENOENT: cannot create directory '${path}'`);
111
+ }
112
+ }
@@ -0,0 +1,26 @@
1
+ export * from './types/index.js';
2
+ export * from './query.js';
3
+ export { runAgent, type Workspace, type AgentOptions } from './agent.js';
4
+ export { defaultSystemPrompt } from './prompt.js';
5
+ export { maybePersistLargeResult, DEFAULT_MAX_RESULT_CHARS, PREVIEW_CHARS, TOOL_RESULTS_DIR, } from './persist.js';
6
+ export * from './workspace/index.js';
7
+ export * from './sandbox/index.js';
8
+ export * from './fs/index.js';
9
+ export * from './llm/index.js';
10
+ export * from './tools/index.js';
11
+ export { task } from './tools/task.js';
12
+ export * from './mcp/index.js';
13
+ export * from './commands/index.js';
14
+ export * from './background/index.js';
15
+ export * from './queue.js';
16
+ export * from './team/index.js';
17
+ export * from './session/index.js';
18
+ export * from './memory/index.js';
19
+ export * from './permissions/index.js';
20
+ export * from './settings/index.js';
21
+ export * from './skills/index.js';
22
+ export { enterPlanMode, exitPlanMode, PLAN_MODE_TOOLS } from './tools/plan_mode.js';
23
+ export { uuid } from './util/ids.js';
24
+ export * as paths from './util/paths.js';
25
+ export { priceFor, computeCostUSD, contextWindowFor, type Pricing } from './util/pricing.js';
26
+ export { estimateTokens, summarizeHistory } from './compact.js';
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ // browser-claude-sdk — Claude Code agent capabilities in the browser, against
2
+ // any OpenAI/Anthropic-compatible LLM endpoint. No backend required.
3
+ export * from './types/index.js';
4
+ export * from './query.js';
5
+ export { runAgent } from './agent.js';
6
+ export { defaultSystemPrompt } from './prompt.js';
7
+ export { maybePersistLargeResult, DEFAULT_MAX_RESULT_CHARS, PREVIEW_CHARS, TOOL_RESULTS_DIR, } from './persist.js';
8
+ export * from './workspace/index.js';
9
+ export * from './sandbox/index.js';
10
+ export * from './fs/index.js';
11
+ export * from './llm/index.js';
12
+ export * from './tools/index.js';
13
+ export { task } from './tools/task.js';
14
+ export * from './mcp/index.js';
15
+ export * from './commands/index.js';
16
+ export * from './background/index.js';
17
+ export * from './queue.js';
18
+ export * from './team/index.js';
19
+ export * from './session/index.js';
20
+ export * from './memory/index.js';
21
+ export * from './permissions/index.js';
22
+ export * from './settings/index.js';
23
+ export * from './skills/index.js';
24
+ export { enterPlanMode, exitPlanMode, PLAN_MODE_TOOLS } from './tools/plan_mode.js';
25
+ export { uuid } from './util/ids.js';
26
+ export * as paths from './util/paths.js';
27
+ export { priceFor, computeCostUSD, contextWindowFor } from './util/pricing.js';
28
+ export { estimateTokens, summarizeHistory } from './compact.js';
29
+ // (createResponsesClient is exported via ./llm/index.js)
@@ -0,0 +1,24 @@
1
+ import type { LLMClient } from '../types/index.js';
2
+ export interface AnthropicClientOptions {
3
+ apiKey?: string;
4
+ /** Base URL of the Anthropic-compatible API. Default: https://api.anthropic.com/v1 */
5
+ baseUrl?: string;
6
+ /** Default model id. Can be overridden per call via streamChat opts. */
7
+ model?: string;
8
+ /** Extra headers merged into every request. */
9
+ headers?: Record<string, string>;
10
+ /** anthropic-version header. Default: '2023-06-01'. */
11
+ anthropicVersion?: string;
12
+ /** Sampling temperature. */
13
+ temperature?: number;
14
+ /** Max output tokens (required by the API). Default: 4096. */
15
+ maxTokens?: number;
16
+ }
17
+ /**
18
+ * Creates an LLMClient backed by the Anthropic Messages API.
19
+ *
20
+ * Streams text deltas through `onToken` and surfaces tool calls (assembled
21
+ * from streamed `input_json_delta` events) via the returned StreamResult and
22
+ * the `onTool` hook.
23
+ */
24
+ export declare function createAnthropicClient(options?: AnthropicClientOptions): LLMClient;
@@ -0,0 +1,280 @@
1
+ // Anthropic-compatible streaming chat client for browser-claude-sdk.
2
+ // Talks to the Anthropic Messages API (or any compatible endpoint). Browser-
3
+ // safe: relies only on the global `fetch`, `TextDecoder`, and `ReadableStream`.
4
+ //
5
+ // Reuses the SSE line reader from the OpenAI client. Anthropic emits both
6
+ // `event:` and `data:` lines per event, but every `data:` payload carries its
7
+ // own `type` field, so parsing the data JSON alone is sufficient.
8
+ import { consumeSSE } from './openai.js';
9
+ import { parseInlineToolCalls } from './inlineTools.js';
10
+ /**
11
+ * Creates an LLMClient backed by the Anthropic Messages API.
12
+ *
13
+ * Streams text deltas through `onToken` and surfaces tool calls (assembled
14
+ * from streamed `input_json_delta` events) via the returned StreamResult and
15
+ * the `onTool` hook.
16
+ */
17
+ export function createAnthropicClient(options = {}) {
18
+ const baseUrl = (options.baseUrl ?? 'https://api.anthropic.com/v1').replace(/\/$/, '');
19
+ const defaultModel = options.model ?? 'claude-sonnet-4-6';
20
+ const version = options.anthropicVersion ?? '2023-06-01';
21
+ return {
22
+ async streamChat(messages, opts) {
23
+ const model = opts.model || defaultModel;
24
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
25
+ const body = {
26
+ model,
27
+ max_tokens: options.maxTokens ?? 4096,
28
+ messages: anthropicMessages,
29
+ stream: true,
30
+ };
31
+ if (system)
32
+ body.system = system;
33
+ if (options.temperature !== undefined)
34
+ body.temperature = options.temperature;
35
+ if (opts.tools?.length)
36
+ body.tools = opts.tools.map(toAnthropicTool);
37
+ const res = await fetch(`${baseUrl}/messages`, {
38
+ method: 'POST',
39
+ signal: opts.signal,
40
+ headers: {
41
+ 'content-type': 'application/json',
42
+ 'anthropic-version': version,
43
+ 'anthropic-dangerous-direct-browser-access': 'true',
44
+ ...(options.apiKey ? { 'x-api-key': options.apiKey } : {}),
45
+ ...options.headers,
46
+ },
47
+ body: JSON.stringify(body),
48
+ });
49
+ if (!res.ok || !res.body) {
50
+ const errText = await res.text().catch(() => res.statusText);
51
+ throw new Error(`Anthropic request failed (${res.status}): ${errText}`);
52
+ }
53
+ let text = '';
54
+ let inputTokens = 0;
55
+ let outputTokens = 0;
56
+ let cacheRead;
57
+ let cacheCreation;
58
+ let stopReason = null;
59
+ // Tool uses arrive as a content_block_start (with id/name) followed by a
60
+ // sequence of input_json_delta partials. Accumulate by block index.
61
+ const toolAcc = new Map();
62
+ await consumeSSE(res.body, (data) => {
63
+ if (data === '[DONE]')
64
+ return;
65
+ let event;
66
+ try {
67
+ event = JSON.parse(data);
68
+ }
69
+ catch {
70
+ return;
71
+ }
72
+ // Usage: input tokens arrive on message_start, output on message_delta.
73
+ if (event.type === 'message_start' && event.message?.usage) {
74
+ inputTokens = event.message.usage.input_tokens ?? 0;
75
+ cacheRead = event.message.usage.cache_read_input_tokens ?? cacheRead;
76
+ cacheCreation = event.message.usage.cache_creation_input_tokens ?? cacheCreation;
77
+ }
78
+ if (event.type === 'message_delta') {
79
+ if (event.usage?.output_tokens != null)
80
+ outputTokens = event.usage.output_tokens;
81
+ if (event.delta?.stop_reason)
82
+ stopReason = mapStop(event.delta.stop_reason);
83
+ }
84
+ switch (event.type) {
85
+ case 'content_block_start': {
86
+ const block = event.content_block;
87
+ if (block?.type === 'tool_use') {
88
+ toolAcc.set(event.index ?? 0, {
89
+ id: block.id ?? '',
90
+ name: block.name ?? '',
91
+ json: '',
92
+ });
93
+ }
94
+ break;
95
+ }
96
+ case 'content_block_delta': {
97
+ const delta = event.delta;
98
+ if (delta?.type === 'text_delta' && delta.text) {
99
+ text += delta.text;
100
+ opts.onToken(delta.text);
101
+ }
102
+ else if (delta?.type === 'input_json_delta' && delta.partial_json) {
103
+ const cur = toolAcc.get(event.index ?? 0);
104
+ if (cur)
105
+ cur.json += delta.partial_json;
106
+ }
107
+ break;
108
+ }
109
+ default:
110
+ // message_start / message_delta / message_stop / content_block_stop
111
+ // / ping — nothing to accumulate.
112
+ break;
113
+ }
114
+ });
115
+ const toolCalls = [...toolAcc.entries()]
116
+ .sort(([a], [b]) => a - b)
117
+ .map(([idx, t]) => ({
118
+ id: t.id || `toolu_${idx}`,
119
+ type: 'function',
120
+ // `json` is already a JSON-encoded object string; default to "{}".
121
+ function: { name: t.name, arguments: t.json || '{}' },
122
+ }));
123
+ // Fallback: some relays/models emit tool calls as inline text rather than
124
+ // native tool_use blocks. Parse them out and clean the visible text.
125
+ let finalText = text;
126
+ if (!toolCalls.length) {
127
+ const inline = parseInlineToolCalls(text);
128
+ if (inline.calls.length) {
129
+ toolCalls.push(...inline.calls);
130
+ finalText = inline.cleanedText;
131
+ }
132
+ }
133
+ if (toolCalls.length && opts.onTool)
134
+ opts.onTool(toolCalls);
135
+ const usage = {
136
+ input_tokens: inputTokens,
137
+ output_tokens: outputTokens,
138
+ cache_read_input_tokens: cacheRead,
139
+ cache_creation_input_tokens: cacheCreation,
140
+ };
141
+ return { text: finalText, toolCalls, model, usage, stopReason };
142
+ },
143
+ };
144
+ }
145
+ /** Map Anthropic stop_reason to our StopReason vocabulary. */
146
+ function mapStop(reason) {
147
+ switch (reason) {
148
+ case 'end_turn':
149
+ case 'max_tokens':
150
+ case 'stop_sequence':
151
+ case 'tool_use':
152
+ case 'pause_turn':
153
+ case 'refusal':
154
+ return reason;
155
+ default:
156
+ return 'end_turn';
157
+ }
158
+ }
159
+ /**
160
+ * Convert our provider-neutral ChatMsg[] into the Anthropic Messages format:
161
+ * leading system messages are hoisted into a top-level `system` string, and the
162
+ * remaining messages are mapped to user/assistant turns with content blocks.
163
+ */
164
+ function toAnthropicMessages(messages) {
165
+ const systemParts = [];
166
+ const out = [];
167
+ for (const msg of messages) {
168
+ if (msg.role === 'system') {
169
+ systemParts.push(typeof msg.content === 'string' ? msg.content : blocksToText(msg.content));
170
+ continue;
171
+ }
172
+ if (msg.role === 'tool') {
173
+ // A tool result is delivered to Anthropic as a user message containing a
174
+ // tool_result block keyed by the originating tool_use id.
175
+ pushUserBlock(out, {
176
+ type: 'tool_result',
177
+ tool_use_id: msg.tool_call_id,
178
+ content: typeof msg.content === 'string' ? msg.content : blocksToText(msg.content),
179
+ });
180
+ continue;
181
+ }
182
+ if (msg.role === 'assistant') {
183
+ const blocks = [];
184
+ const textContent = typeof msg.content === 'string' ? msg.content : blocksToText(msg.content);
185
+ if (textContent)
186
+ blocks.push({ type: 'text', text: textContent });
187
+ if (msg.tool_calls?.length) {
188
+ for (const tc of msg.tool_calls) {
189
+ blocks.push({
190
+ type: 'tool_use',
191
+ id: tc.id,
192
+ name: tc.function.name,
193
+ input: safeParseObject(tc.function.arguments),
194
+ });
195
+ }
196
+ }
197
+ out.push({ role: 'assistant', content: blocks.length ? blocks : [{ type: 'text', text: '' }] });
198
+ continue;
199
+ }
200
+ // user
201
+ out.push({ role: 'user', content: contentToAnthropicBlocks(msg.content) });
202
+ }
203
+ return { system: systemParts.join('\n\n'), messages: out };
204
+ }
205
+ /** Append a single block to a user message, coalescing with a trailing user turn. */
206
+ function pushUserBlock(out, block) {
207
+ const last = out[out.length - 1];
208
+ if (last && last.role === 'user') {
209
+ last.content.push(block);
210
+ }
211
+ else {
212
+ out.push({ role: 'user', content: [block] });
213
+ }
214
+ }
215
+ /** Map our content blocks to Anthropic content blocks. */
216
+ function contentToAnthropicBlocks(content) {
217
+ if (typeof content === 'string')
218
+ return [{ type: 'text', text: content }];
219
+ const blocks = [];
220
+ for (const b of content) {
221
+ if (b.type === 'text') {
222
+ blocks.push({ type: 'text', text: b.text });
223
+ }
224
+ else if (b.type === 'image') {
225
+ blocks.push({
226
+ type: 'image',
227
+ source: { type: 'base64', media_type: b.source.media_type, data: b.source.data },
228
+ });
229
+ }
230
+ else if (b.type === 'document') {
231
+ blocks.push({
232
+ type: 'document',
233
+ source: b.source,
234
+ ...(b.title ? { title: b.title } : {}),
235
+ ...(b.context ? { context: b.context } : {}),
236
+ });
237
+ }
238
+ else if (b.type === 'tool_use') {
239
+ blocks.push({ type: 'tool_use', id: b.id, name: b.name, input: b.input });
240
+ }
241
+ else if (b.type === 'tool_result') {
242
+ blocks.push({
243
+ type: 'tool_result',
244
+ tool_use_id: b.tool_use_id,
245
+ content: typeof b.content === 'string' ? b.content : blocksToText(b.content),
246
+ ...(b.is_error ? { is_error: true } : {}),
247
+ });
248
+ }
249
+ }
250
+ return blocks.length ? blocks : [{ type: 'text', text: '' }];
251
+ }
252
+ /** Convert an OpenAI-shape ToolDef into an Anthropic tool definition. */
253
+ function toAnthropicTool(tool) {
254
+ return {
255
+ name: tool.function.name,
256
+ description: tool.function.description,
257
+ input_schema: tool.function.parameters,
258
+ };
259
+ }
260
+ function blocksToText(blocks) {
261
+ return blocks
262
+ .map((b) => {
263
+ if (b.type === 'text')
264
+ return b.text;
265
+ if (b.type === 'tool_result')
266
+ return typeof b.content === 'string' ? b.content : blocksToText(b.content);
267
+ return '';
268
+ })
269
+ .filter(Boolean)
270
+ .join('\n');
271
+ }
272
+ function safeParseObject(json) {
273
+ try {
274
+ const parsed = JSON.parse(json);
275
+ return parsed && typeof parsed === 'object' ? parsed : {};
276
+ }
277
+ catch {
278
+ return {};
279
+ }
280
+ }
@@ -0,0 +1,3 @@
1
+ export * from './openai.js';
2
+ export * from './anthropic.js';
3
+ export * from './responses.js';
@@ -0,0 +1,3 @@
1
+ export * from './openai.js';
2
+ export * from './anthropic.js';
3
+ export * from './responses.js';
@@ -0,0 +1,11 @@
1
+ import type { ToolCall } from '../types/index.js';
2
+ export declare function hasInlineToolCalls(text: string): boolean;
3
+ /**
4
+ * Extract inline tool calls from assistant text. Returns the parsed calls and
5
+ * the text with the tool-call markup removed. If none are found, returns the
6
+ * original text and an empty array.
7
+ */
8
+ export declare function parseInlineToolCalls(text: string): {
9
+ calls: ToolCall[];
10
+ cleanedText: string;
11
+ };
@@ -0,0 +1,72 @@
1
+ // Fallback parser for models/proxies that emit tool calls as inline TEXT
2
+ // instead of native function-calling blocks. Several relays and open models use
3
+ // an "XML" tool-call format like:
4
+ //
5
+ // <tool_call>
6
+ // <function=write_file>
7
+ // <parameter=path>index.html</parameter>
8
+ // <parameter=content>
9
+ // <!DOCTYPE html> ...
10
+ // </parameter>
11
+ // </function>
12
+ // </tool_call>
13
+ //
14
+ // Parameters may or may not have closing </parameter> tags, and the wrapper
15
+ // <tool_call> may be absent. This parser is tolerant of all those variants and
16
+ // also strips the markup out of the user-visible text.
17
+ const FUNCTION_MARKER = /<function\s*=/;
18
+ export function hasInlineToolCalls(text) {
19
+ return FUNCTION_MARKER.test(text);
20
+ }
21
+ /**
22
+ * Extract inline tool calls from assistant text. Returns the parsed calls and
23
+ * the text with the tool-call markup removed. If none are found, returns the
24
+ * original text and an empty array.
25
+ */
26
+ export function parseInlineToolCalls(text) {
27
+ if (!text || !FUNCTION_MARKER.test(text))
28
+ return { calls: [], cleanedText: text };
29
+ const calls = [];
30
+ const markerRe = /<function\s*=\s*([^>\s]+)\s*>/g;
31
+ const markers = [];
32
+ let m;
33
+ while ((m = markerRe.exec(text)) !== null) {
34
+ markers.push({ name: m[1], bodyStart: markerRe.lastIndex, markerStart: m.index });
35
+ }
36
+ for (let i = 0; i < markers.length; i++) {
37
+ const cur = markers[i];
38
+ const end = i + 1 < markers.length ? markers[i + 1].markerStart : text.length;
39
+ let body = text.slice(cur.bodyStart, end);
40
+ // Trim at the function/tool_call closers if present.
41
+ body = body.replace(/<\/function>[\s\S]*$/, '').replace(/<\/tool_call>[\s\S]*$/, '');
42
+ const args = {};
43
+ const parts = body.split(/<parameter\s*=/).slice(1);
44
+ for (const part of parts) {
45
+ const gt = part.indexOf('>');
46
+ if (gt < 0)
47
+ continue;
48
+ const key = part.slice(0, gt).trim();
49
+ let val = part.slice(gt + 1);
50
+ // Value ends at its own closer (or the function/tool_call closer, or the
51
+ // next parameter — already removed by the split).
52
+ val = val
53
+ .replace(/<\/parameter>[\s\S]*$/, '')
54
+ .replace(/<\/function>[\s\S]*$/, '')
55
+ .replace(/<\/tool_call>[\s\S]*$/, '');
56
+ args[key] = trimEdges(val);
57
+ }
58
+ calls.push({
59
+ id: `call_inline_${i}`,
60
+ type: 'function',
61
+ function: { name: cur.name.trim(), arguments: JSON.stringify(args) },
62
+ });
63
+ }
64
+ // Everything from the first tool-call/function marker onward is markup.
65
+ const cut = text.search(/<tool_call>|<function\s*=/);
66
+ const cleanedText = cut >= 0 ? text.slice(0, cut).trim() : text;
67
+ return { calls, cleanedText };
68
+ }
69
+ /** Strip a single leading newline and trailing whitespace from a param value. */
70
+ function trimEdges(v) {
71
+ return v.replace(/^\r?\n/, '').replace(/\s+$/, '');
72
+ }
@@ -0,0 +1,29 @@
1
+ import type { LLMClient } from '../types/index.js';
2
+ export interface OpenAIClientOptions {
3
+ /** API key, or a function returning one per request (for round-robin key pools). */
4
+ apiKey?: string | (() => string | undefined);
5
+ /** Base URL of the OpenAI-compatible API. Default: https://api.openai.com/v1 */
6
+ baseUrl?: string;
7
+ /** Default model id. Can be overridden per call via streamChat opts. */
8
+ model?: string;
9
+ /** Extra headers (e.g. for Groq, OpenRouter, Together). */
10
+ headers?: Record<string, string>;
11
+ /** Sampling temperature. */
12
+ temperature?: number;
13
+ /** Max output tokens. */
14
+ maxTokens?: number;
15
+ /** Reasoning effort ('none'|'low'|'high'…) → sets `reasoning_effort` (reasoning models). */
16
+ reasoningEffort?: string;
17
+ /** Allow the model to batch multiple tool calls → sets `parallel_tool_calls` (when tools present). */
18
+ parallelToolCalls?: boolean;
19
+ }
20
+ /**
21
+ * Creates an LLMClient backed by any OpenAI-compatible /chat/completions
22
+ * endpoint (OpenAI, Groq, Together, OpenRouter, local llama.cpp, etc.).
23
+ *
24
+ * Streams text deltas through `onToken` and surfaces tool calls (assembled
25
+ * from streamed deltas) via the returned StreamResult and the `onTool` hook.
26
+ */
27
+ export declare function createOpenAIClient(options?: OpenAIClientOptions): LLMClient;
28
+ /** Read an SSE response body, invoking `onData` for each `data:` payload. */
29
+ export declare function consumeSSE(body: ReadableStream<Uint8Array>, onData: (data: string) => void): Promise<void>;