anyclaude-sdk 0.4.6 → 0.4.8
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/agent.d.ts +5 -0
- package/dist/agent.js +16 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/loop.d.ts +45 -0
- package/dist/loop.js +305 -0
- package/dist/query.d.ts +4 -0
- package/dist/query.js +1 -0
- package/dist/tools/define.d.ts +4 -2
- package/dist/tools/define.js +3 -1
- package/dist/tools/types.d.ts +4 -1
- package/package.json +5 -1
package/dist/agent.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ export interface AgentOptions {
|
|
|
38
38
|
* one, the loop emits a `client_tool_request` + pauses; the client runs it and
|
|
39
39
|
* resumes (continueRun) with `clientToolResults`. (e.g. bash on a browser WebContainer.) */
|
|
40
40
|
clientTools?: string[];
|
|
41
|
+
/** One switch to delegate ALL built-in workspace tools (bash + file ops) to the
|
|
42
|
+
* host: the server emits client_tool_request for them and NEVER runs them
|
|
43
|
+
* against its own (in-memory) workspace — execution happens client-side
|
|
44
|
+
* (e.g. a browser WebContainer / IndexedDB via createWorkspaceClientTools). */
|
|
45
|
+
clientWorkspaceTools?: boolean;
|
|
41
46
|
/** Results for client-tool calls, injected into the transcript before continuing. */
|
|
42
47
|
clientToolResults?: Array<{
|
|
43
48
|
tool_use_id: string;
|
package/dist/agent.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// 5. Execute each tool against the workspace
|
|
9
9
|
// 6. Run PostToolUse hooks; append results to the message history
|
|
10
10
|
// 7. Repeat until no tool calls or max turns reached
|
|
11
|
-
import { ALL_CLAUDE_CODE_TOOLS, toolByName, toolDefs } from './tools/index.js';
|
|
11
|
+
import { ALL_CLAUDE_CODE_TOOLS, toolByName, toolDefs, WORKSPACE_TOOL_NAMES } from './tools/index.js';
|
|
12
12
|
import { task as taskTool } from './tools/task.js';
|
|
13
13
|
import { askUserQuestion } from './tools/ask_user.js';
|
|
14
14
|
import { loadMcpServers } from './mcp/index.js';
|
|
@@ -250,6 +250,16 @@ export async function* runAgent(options) {
|
|
|
250
250
|
tools = [...localTools, ...loaded.tools];
|
|
251
251
|
mcpStatuses = loaded.statuses.map((s) => ({ name: s.name, status: s.status }));
|
|
252
252
|
}
|
|
253
|
+
// Delegation set: explicit clientTools, plus (one switch) every built-in
|
|
254
|
+
// workspace tool when clientWorkspaceTools is on, plus any tool with no `run`
|
|
255
|
+
// (Vercel-style "no execute = client"). These emit client_tool_request and are
|
|
256
|
+
// never executed on the server.
|
|
257
|
+
if (options.clientWorkspaceTools)
|
|
258
|
+
for (const n of WORKSPACE_TOOL_NAMES)
|
|
259
|
+
clientTools.add(n);
|
|
260
|
+
for (const t of tools)
|
|
261
|
+
if (!t.run)
|
|
262
|
+
clientTools.add(t.def.function.name);
|
|
253
263
|
const defs = toolDefs(tools);
|
|
254
264
|
const byName = toolByName(tools);
|
|
255
265
|
let system = options.systemPrompt != null ? options.systemPrompt : defaultSystemPrompt(cwd);
|
|
@@ -707,8 +717,11 @@ export async function* runAgent(options) {
|
|
|
707
717
|
let content = '';
|
|
708
718
|
let isError = false;
|
|
709
719
|
let extraContext = '';
|
|
710
|
-
if (!tool) {
|
|
711
|
-
|
|
720
|
+
if (!tool || !tool.run) {
|
|
721
|
+
// Unknown, or a run-less (client-delegated) tool that somehow reached
|
|
722
|
+
// server execution — both are errors here (delegated tools are handled
|
|
723
|
+
// above via clientTools).
|
|
724
|
+
content = `Error: ${tool ? `tool "${name}" has no server executor (it is client-delegated)` : `unknown tool "${name}"`}`;
|
|
712
725
|
isError = true;
|
|
713
726
|
}
|
|
714
727
|
else {
|
package/dist/index.d.ts
CHANGED
|
@@ -25,3 +25,4 @@ export { uuid } from './util/ids.js';
|
|
|
25
25
|
export * as paths from './util/paths.js';
|
|
26
26
|
export { priceFor, computeCostUSD, contextWindowFor, type Pricing } from './util/pricing.js';
|
|
27
27
|
export { estimateTokens, summarizeHistory, compactWithWindow } from './compact.js';
|
|
28
|
+
export { runToolLoop, type RunToolLoopOptions } from './loop.js';
|
package/dist/index.js
CHANGED
|
@@ -27,4 +27,5 @@ export { uuid } from './util/ids.js';
|
|
|
27
27
|
export * as paths from './util/paths.js';
|
|
28
28
|
export { priceFor, computeCostUSD, contextWindowFor } from './util/pricing.js';
|
|
29
29
|
export { estimateTokens, summarizeHistory, compactWithWindow } from './compact.js';
|
|
30
|
+
export { runToolLoop } from './loop.js';
|
|
30
31
|
// (createResponsesClient is exported via ./llm/index.js)
|
package/dist/loop.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CanUseTool, ChatMsg, LLMClient, SDKMessage } from './types/index.js';
|
|
2
|
+
import type { Tool, ToolContext } from './tools/types.js';
|
|
3
|
+
export interface RunToolLoopOptions {
|
|
4
|
+
/** Conversation so far: `[systemMsg, ...]`. Mutated in place as the loop appends turns. */
|
|
5
|
+
history: ChatMsg[];
|
|
6
|
+
/** Tools the model may call (executed via `ctx`). */
|
|
7
|
+
tools: Tool[];
|
|
8
|
+
/** Any OpenAI/Anthropic-compatible client. */
|
|
9
|
+
llm: LLMClient;
|
|
10
|
+
model?: string;
|
|
11
|
+
/** Tool-execution context (fs / exec / cwd / readFiles / limits …). */
|
|
12
|
+
ctx: ToolContext;
|
|
13
|
+
/** Max LLM turns before stopping with `error_max_turns`. Default 50. */
|
|
14
|
+
maxTurns?: number;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
/** Optional permission gate; a `deny` result turns into an error tool_result. */
|
|
17
|
+
canUseTool?: CanUseTool;
|
|
18
|
+
/** Tool names to DELEGATE to the host instead of running via `ctx` (inline). A
|
|
19
|
+
* tool with no `run` is auto-delegated too (Vercel-style "no execute = client"). */
|
|
20
|
+
clientTools?: string[];
|
|
21
|
+
/** Executor for delegated tools — runs the call (e.g. on a browser WebContainer)
|
|
22
|
+
* and returns the result inline. Required if any tool is delegated. */
|
|
23
|
+
onClientTool?: (req: {
|
|
24
|
+
tool_use_id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
input: Record<string, unknown>;
|
|
27
|
+
}) => Promise<{
|
|
28
|
+
content: unknown;
|
|
29
|
+
is_error?: boolean;
|
|
30
|
+
}> | {
|
|
31
|
+
content: unknown;
|
|
32
|
+
is_error?: boolean;
|
|
33
|
+
};
|
|
34
|
+
/** Emit `stream_event` text deltas as the assistant streams. */
|
|
35
|
+
includePartialMessages?: boolean;
|
|
36
|
+
/** Correlation id stamped on every emitted SDKMessage. */
|
|
37
|
+
sessionId?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run the bare tool loop, yielding SDKMessages until the model stops or maxTurns.
|
|
41
|
+
*
|
|
42
|
+
* const ctx = { fs, exec, cwd: '/work', readFiles: new Set() } as ToolContext
|
|
43
|
+
* for await (const m of runToolLoop({ history, tools, llm, model, ctx })) render(m)
|
|
44
|
+
*/
|
|
45
|
+
export declare function runToolLoop(opts: RunToolLoopOptions): AsyncGenerator<SDKMessage>;
|
package/dist/loop.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { toolByName, toolDefs } from './tools/index.js';
|
|
2
|
+
import { uuid } from './util/ids.js';
|
|
3
|
+
const emptyUsage = () => ({ input_tokens: 0, output_tokens: 0 });
|
|
4
|
+
function addUsage(t, b) {
|
|
5
|
+
if (!b)
|
|
6
|
+
return;
|
|
7
|
+
t.input_tokens += b.input_tokens || 0;
|
|
8
|
+
t.output_tokens += b.output_tokens || 0;
|
|
9
|
+
t.cache_read_input_tokens = (t.cache_read_input_tokens || 0) + (b.cache_read_input_tokens || 0);
|
|
10
|
+
t.cache_creation_input_tokens = (t.cache_creation_input_tokens || 0) + (b.cache_creation_input_tokens || 0);
|
|
11
|
+
}
|
|
12
|
+
function safeParse(json) {
|
|
13
|
+
if (!json || !json.trim())
|
|
14
|
+
return {};
|
|
15
|
+
try {
|
|
16
|
+
const v = JSON.parse(json);
|
|
17
|
+
return v && typeof v === 'object' ? v : { value: v };
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return { _raw: json };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function toolUseBlocks(calls) {
|
|
24
|
+
return calls.map((c) => ({ type: 'tool_use', id: c.id, name: c.function.name, input: safeParse(c.function.arguments) }));
|
|
25
|
+
}
|
|
26
|
+
function resultToText(content) {
|
|
27
|
+
if (typeof content === 'string')
|
|
28
|
+
return content;
|
|
29
|
+
return content
|
|
30
|
+
.map((b) => (b.type === 'text' ? b.text : b.type === 'image' ? '[image]' : b.type === 'document' ? '[document]' : `[${b.type}]`))
|
|
31
|
+
.join('\n');
|
|
32
|
+
}
|
|
33
|
+
function toToolResultContent(content) {
|
|
34
|
+
if (typeof content === 'string')
|
|
35
|
+
return content;
|
|
36
|
+
return content.filter((b) => b.type === 'text' || b.type === 'image' || b.type === 'document');
|
|
37
|
+
}
|
|
38
|
+
function createPushQueue() {
|
|
39
|
+
const items = [];
|
|
40
|
+
let resolveNext = null;
|
|
41
|
+
let closed = false;
|
|
42
|
+
return {
|
|
43
|
+
push(v) {
|
|
44
|
+
if (resolveNext) {
|
|
45
|
+
resolveNext({ value: v, done: false });
|
|
46
|
+
resolveNext = null;
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
items.push(v);
|
|
50
|
+
},
|
|
51
|
+
close() {
|
|
52
|
+
closed = true;
|
|
53
|
+
if (resolveNext) {
|
|
54
|
+
resolveNext({ value: undefined, done: true });
|
|
55
|
+
resolveNext = null;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
[Symbol.asyncIterator]() {
|
|
59
|
+
return {
|
|
60
|
+
next: () => {
|
|
61
|
+
if (items.length)
|
|
62
|
+
return Promise.resolve({ value: items.shift(), done: false });
|
|
63
|
+
if (closed)
|
|
64
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
65
|
+
return new Promise((res) => (resolveNext = res));
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Run the bare tool loop, yielding SDKMessages until the model stops or maxTurns.
|
|
73
|
+
*
|
|
74
|
+
* const ctx = { fs, exec, cwd: '/work', readFiles: new Set() } as ToolContext
|
|
75
|
+
* for await (const m of runToolLoop({ history, tools, llm, model, ctx })) render(m)
|
|
76
|
+
*/
|
|
77
|
+
export async function* runToolLoop(opts) {
|
|
78
|
+
const { history, llm, model, ctx, signal, canUseTool, onClientTool } = opts;
|
|
79
|
+
const tools = opts.tools;
|
|
80
|
+
const clientTools = new Set(opts.clientTools ?? []);
|
|
81
|
+
const maxTurns = opts.maxTurns ?? 50;
|
|
82
|
+
const sessionId = opts.sessionId ?? uuid();
|
|
83
|
+
const emitPartial = !!opts.includePartialMessages;
|
|
84
|
+
const byName = toolByName(tools);
|
|
85
|
+
const defs = toolDefs(tools);
|
|
86
|
+
const startedAt = Date.now();
|
|
87
|
+
let apiMs = 0;
|
|
88
|
+
let turns = 0;
|
|
89
|
+
let lastText = '';
|
|
90
|
+
let resultModel = model ?? 'unknown';
|
|
91
|
+
let hitMaxTurns = false;
|
|
92
|
+
let errored = null;
|
|
93
|
+
const usageTotal = emptyUsage();
|
|
94
|
+
while (true) {
|
|
95
|
+
if (signal?.aborted)
|
|
96
|
+
break;
|
|
97
|
+
if (turns >= maxTurns) {
|
|
98
|
+
hitMaxTurns = true;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
turns++;
|
|
102
|
+
let streamedText = '';
|
|
103
|
+
let captured = [];
|
|
104
|
+
const apiStart = Date.now();
|
|
105
|
+
let result;
|
|
106
|
+
try {
|
|
107
|
+
if (emitPartial) {
|
|
108
|
+
const q = createPushQueue();
|
|
109
|
+
let inToolMarkup = false;
|
|
110
|
+
const sp = llm.streamChat(history, {
|
|
111
|
+
model,
|
|
112
|
+
tools: defs,
|
|
113
|
+
signal,
|
|
114
|
+
onToken: (delta) => {
|
|
115
|
+
streamedText += delta;
|
|
116
|
+
if (!inToolMarkup && /<tool_call|<function\s*=/.test(streamedText))
|
|
117
|
+
inToolMarkup = true;
|
|
118
|
+
if (inToolMarkup)
|
|
119
|
+
return;
|
|
120
|
+
q.push({
|
|
121
|
+
type: 'stream_event',
|
|
122
|
+
event: { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: delta } },
|
|
123
|
+
parent_tool_use_id: null,
|
|
124
|
+
uuid: uuid(),
|
|
125
|
+
session_id: sessionId,
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
onTool: (calls) => {
|
|
129
|
+
captured = calls;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
sp.then(() => { }, () => { }).finally(() => q.close());
|
|
133
|
+
for await (const ev of q)
|
|
134
|
+
yield ev;
|
|
135
|
+
result = await sp;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
result = await llm.streamChat(history, {
|
|
139
|
+
model,
|
|
140
|
+
tools: defs,
|
|
141
|
+
signal,
|
|
142
|
+
onToken: (delta) => {
|
|
143
|
+
streamedText += delta;
|
|
144
|
+
},
|
|
145
|
+
onTool: (calls) => {
|
|
146
|
+
captured = calls;
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
errored = err instanceof Error ? err.message : String(err);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
apiMs += Date.now() - apiStart;
|
|
156
|
+
const text = result.text || streamedText;
|
|
157
|
+
const calls = result.toolCalls.length ? result.toolCalls : captured;
|
|
158
|
+
lastText = text || lastText;
|
|
159
|
+
resultModel = result.model || resultModel;
|
|
160
|
+
addUsage(usageTotal, result.usage);
|
|
161
|
+
const stopReason = calls.length ? 'tool_use' : result.stopReason ?? 'end_turn';
|
|
162
|
+
const assistantContent = [];
|
|
163
|
+
if (text)
|
|
164
|
+
assistantContent.push({ type: 'text', text });
|
|
165
|
+
assistantContent.push(...toolUseBlocks(calls));
|
|
166
|
+
const apiAssistant = {
|
|
167
|
+
id: 'msg_' + uuid().replace(/-/g, '').slice(0, 24),
|
|
168
|
+
type: 'message',
|
|
169
|
+
role: 'assistant',
|
|
170
|
+
model: resultModel,
|
|
171
|
+
content: assistantContent,
|
|
172
|
+
stop_reason: stopReason,
|
|
173
|
+
stop_sequence: null,
|
|
174
|
+
usage: result.usage ?? emptyUsage(),
|
|
175
|
+
};
|
|
176
|
+
yield { type: 'assistant', message: apiAssistant, parent_tool_use_id: null, uuid: uuid(), session_id: sessionId };
|
|
177
|
+
history.push({ role: 'assistant', content: text, tool_calls: calls.length ? calls : undefined });
|
|
178
|
+
if (!calls.length)
|
|
179
|
+
break;
|
|
180
|
+
const toolResultBlocks = [];
|
|
181
|
+
const turnMedia = [];
|
|
182
|
+
for (const call of calls) {
|
|
183
|
+
if (signal?.aborted)
|
|
184
|
+
break;
|
|
185
|
+
const name = call.function.name;
|
|
186
|
+
let input = safeParse(call.function.arguments);
|
|
187
|
+
const tool = byName.get(name);
|
|
188
|
+
let content = '';
|
|
189
|
+
let isError = false;
|
|
190
|
+
// Delegated tool (listed in clientTools, or has no `run`): execute on the
|
|
191
|
+
// host via onClientTool instead of `ctx` — never touches the server FS.
|
|
192
|
+
const delegated = clientTools.has(name) || (tool != null && !tool.run);
|
|
193
|
+
if (delegated) {
|
|
194
|
+
if (!onClientTool) {
|
|
195
|
+
content = `No client executor for "${name}" (delegated tool; pass onClientTool).`;
|
|
196
|
+
isError = true;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
try {
|
|
200
|
+
const r = await onClientTool({ tool_use_id: call.id, name, input });
|
|
201
|
+
content = (typeof r.content === 'string' ? r.content : JSON.stringify(r.content ?? ''));
|
|
202
|
+
isError = !!r.is_error;
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
content = `Error (client) ${name}: ${err instanceof Error ? err.message : String(err)}`;
|
|
206
|
+
isError = true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else if (!tool) {
|
|
211
|
+
content = `Error: unknown tool "${name}"`;
|
|
212
|
+
isError = true;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const decision = canUseTool
|
|
216
|
+
? await canUseTool(name, input, { signal, toolUseId: call.id })
|
|
217
|
+
: { behavior: 'allow' };
|
|
218
|
+
if (decision.behavior === 'deny') {
|
|
219
|
+
content = `Permission denied: ${decision.message}`;
|
|
220
|
+
isError = true;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
if ('updatedInput' in decision && decision.updatedInput)
|
|
224
|
+
input = decision.updatedInput;
|
|
225
|
+
try {
|
|
226
|
+
const r = await tool.run(input, ctx);
|
|
227
|
+
content = r.content;
|
|
228
|
+
isError = !!r.isError;
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
content = `Error executing ${name}: ${err instanceof Error ? err.message : String(err)}`;
|
|
232
|
+
isError = true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const textOut = resultToText(content);
|
|
237
|
+
history.push({ role: 'tool', tool_call_id: call.id, content: textOut });
|
|
238
|
+
if (Array.isArray(content)) {
|
|
239
|
+
for (const b of content)
|
|
240
|
+
if (b.type === 'image' || b.type === 'document')
|
|
241
|
+
turnMedia.push(b);
|
|
242
|
+
}
|
|
243
|
+
toolResultBlocks.push({
|
|
244
|
+
type: 'tool_result',
|
|
245
|
+
tool_use_id: call.id,
|
|
246
|
+
content: typeof content === 'string' ? textOut : toToolResultContent(content),
|
|
247
|
+
is_error: isError || undefined,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (turnMedia.length) {
|
|
251
|
+
history.push({
|
|
252
|
+
role: 'user',
|
|
253
|
+
content: [{ type: 'text', text: 'Attached file content from the tools above:' }, ...turnMedia],
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
if (toolResultBlocks.length) {
|
|
257
|
+
yield {
|
|
258
|
+
type: 'user',
|
|
259
|
+
message: { role: 'user', content: toolResultBlocks },
|
|
260
|
+
parent_tool_use_id: null,
|
|
261
|
+
isSynthetic: true,
|
|
262
|
+
timestamp: new Date().toISOString(),
|
|
263
|
+
uuid: uuid(),
|
|
264
|
+
session_id: sessionId,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const durationMs = Date.now() - startedAt;
|
|
269
|
+
if (errored || hitMaxTurns) {
|
|
270
|
+
yield {
|
|
271
|
+
type: 'result',
|
|
272
|
+
subtype: hitMaxTurns ? 'error_max_turns' : 'error_during_execution',
|
|
273
|
+
duration_ms: durationMs,
|
|
274
|
+
duration_api_ms: apiMs,
|
|
275
|
+
is_error: true,
|
|
276
|
+
num_turns: turns,
|
|
277
|
+
stop_reason: hitMaxTurns ? 'max_turns' : 'error',
|
|
278
|
+
total_cost_usd: 0,
|
|
279
|
+
usage: usageTotal,
|
|
280
|
+
modelUsage: {},
|
|
281
|
+
permission_denials: [],
|
|
282
|
+
errors: errored ? [errored] : [`Reached max turns (${maxTurns})`],
|
|
283
|
+
uuid: uuid(),
|
|
284
|
+
session_id: sessionId,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
yield {
|
|
289
|
+
type: 'result',
|
|
290
|
+
subtype: 'success',
|
|
291
|
+
duration_ms: durationMs,
|
|
292
|
+
duration_api_ms: apiMs,
|
|
293
|
+
is_error: false,
|
|
294
|
+
num_turns: turns,
|
|
295
|
+
result: lastText,
|
|
296
|
+
stop_reason: 'end_turn',
|
|
297
|
+
total_cost_usd: 0,
|
|
298
|
+
usage: usageTotal,
|
|
299
|
+
modelUsage: {},
|
|
300
|
+
permission_denials: [],
|
|
301
|
+
uuid: uuid(),
|
|
302
|
+
session_id: sessionId,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
package/dist/query.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export interface QueryOptions {
|
|
|
29
29
|
/** Tool names the HOST/client executes (e.g. bash on a browser WebContainer). The agent
|
|
30
30
|
* emits a `client_tool_request` + pauses; the client runs it and resumes with results. */
|
|
31
31
|
clientTools?: string[];
|
|
32
|
+
/** One switch: delegate ALL built-in workspace tools (bash + file ops) to the host
|
|
33
|
+
* so the server never runs them against its in-memory workspace — execution happens
|
|
34
|
+
* client-side (pair with anyclaude-react `createWorkspaceClientTools`). */
|
|
35
|
+
clientWorkspaceTools?: boolean;
|
|
32
36
|
/** Results for client-tool calls, injected before continuing (with continueRun). */
|
|
33
37
|
clientToolResults?: Array<{
|
|
34
38
|
tool_use_id: string;
|
package/dist/query.js
CHANGED
|
@@ -23,6 +23,7 @@ export function query(options) {
|
|
|
23
23
|
maxDurationMs: options.maxDurationMs,
|
|
24
24
|
continueRun: options.continueRun,
|
|
25
25
|
clientTools: options.clientTools,
|
|
26
|
+
clientWorkspaceTools: options.clientWorkspaceTools,
|
|
26
27
|
clientToolResults: options.clientToolResults,
|
|
27
28
|
cwd: options.cwd,
|
|
28
29
|
sessionId: options.sessionId,
|
package/dist/tools/define.d.ts
CHANGED
|
@@ -9,8 +9,10 @@ export interface DefineToolSpec {
|
|
|
9
9
|
properties: Record<string, unknown>;
|
|
10
10
|
required?: string[];
|
|
11
11
|
};
|
|
12
|
-
/** Execution method. Receives the parsed input + the tool context (fs/exec/cwd/signal/…).
|
|
13
|
-
|
|
12
|
+
/** Execution method. Receives the parsed input + the tool context (fs/exec/cwd/signal/…).
|
|
13
|
+
* OMIT it to make this a CLIENT/delegated tool — the agent loop emits a
|
|
14
|
+
* client_tool_request and the host executes it (resume with clientToolResults). */
|
|
15
|
+
run?: (input: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult> | ToolResult;
|
|
14
16
|
/** Optional: spill threshold for large outputs (see Tool.maxResultChars). */
|
|
15
17
|
maxResultChars?: number;
|
|
16
18
|
}
|
package/dist/tools/define.js
CHANGED
|
@@ -13,8 +13,10 @@ export function defineTool(spec) {
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
|
-
run: async (input, ctx) => spec.run(input, ctx),
|
|
17
16
|
};
|
|
17
|
+
// With a run → server-executed. Without → client-delegated (no run on the Tool).
|
|
18
|
+
if (spec.run)
|
|
19
|
+
tool.run = async (input, ctx) => spec.run(input, ctx);
|
|
18
20
|
if (spec.maxResultChars !== undefined)
|
|
19
21
|
tool.maxResultChars = spec.maxResultChars;
|
|
20
22
|
return tool;
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -81,7 +81,10 @@ export interface ToolResult {
|
|
|
81
81
|
/** A tool pairs an OpenAI-shape function definition with its implementation. */
|
|
82
82
|
export interface Tool {
|
|
83
83
|
def: ToolDef;
|
|
84
|
-
|
|
84
|
+
/** Server-side executor. OMIT to make the tool client-delegated (Vercel-style
|
|
85
|
+
* "no execute = client"): the loop emits a client_tool_request instead of
|
|
86
|
+
* running it, and the host supplies the result. */
|
|
87
|
+
run?(input: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult>;
|
|
85
88
|
/**
|
|
86
89
|
* Max result size in characters before the agent loop spills the full output
|
|
87
90
|
* to a file and replaces it with a preview + path (see large-output handling).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anyclaude-sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "Standalone, browser-compatible SDK providing Claude Code agent capabilities (tools, tool loop, multi-turn, MCP, sub-agents, sessions) against any OpenAI/Anthropic-compatible LLM endpoint. Runs in the browser (WebContainer), Node, and Bun — no backend required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
"./prompt": {
|
|
64
64
|
"types": "./dist/prompt.d.ts",
|
|
65
65
|
"import": "./dist/prompt.js"
|
|
66
|
+
},
|
|
67
|
+
"./loop": {
|
|
68
|
+
"types": "./dist/loop.d.ts",
|
|
69
|
+
"import": "./dist/loop.js"
|
|
66
70
|
}
|
|
67
71
|
},
|
|
68
72
|
"sideEffects": false,
|