anyclaude-sdk 0.7.3 → 0.8.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/agent.d.ts +5 -0
- package/dist/agent.js +41 -7
- package/dist/llm/dialects.d.ts +16 -0
- package/dist/llm/dialects.js +73 -2
- package/dist/llm/openai.js +3 -5
- package/dist/loop.js +24 -3
- package/dist/query.d.ts +3 -0
- package/dist/query.js +1 -0
- package/dist/tools/define.d.ts +3 -0
- package/dist/tools/define.js +2 -0
- package/dist/tools/tool_search.js +4 -1
- package/dist/tools/types.d.ts +11 -0
- package/package.json +1 -1
package/dist/agent.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export interface AgentOptions {
|
|
|
26
26
|
allowedTools?: string[];
|
|
27
27
|
/** Denylist of tool names, applied after allowedTools. */
|
|
28
28
|
disallowedTools?: string[];
|
|
29
|
+
/** Tool names to DEFER out of the per-turn payload — still discoverable via
|
|
30
|
+
* `tool_search` and executable, but their schema isn't sent (saving tokens)
|
|
31
|
+
* until the model searches and the loop arms them. For large pools of
|
|
32
|
+
* rarely-used integration tools. (Per-tool `defer: true` works too.) */
|
|
33
|
+
deferredTools?: string[];
|
|
29
34
|
maxTurns?: number;
|
|
30
35
|
/** Wall-clock budget (ms). At a turn boundary past this, the loop pauses: it
|
|
31
36
|
* persists to sessionStore and emits a `paused` system message instead of
|
package/dist/agent.js
CHANGED
|
@@ -25,6 +25,7 @@ import { DEFAULT_MAX_RESULT_CHARS, maybePersistLargeResult } from './persist.js'
|
|
|
25
25
|
import { computeCostUSD, contextWindowFor } from './util/pricing.js';
|
|
26
26
|
import { estimateTokens, summarizeHistory } from './compact.js';
|
|
27
27
|
import { validateToolArguments } from './llm/repair.js';
|
|
28
|
+
import { parseToolCalls } from './llm/dialects.js';
|
|
28
29
|
import { uuid } from './util/ids.js';
|
|
29
30
|
/** Wrap a single text prompt into the async-iterable form runAgent expects. */
|
|
30
31
|
async function* singleUserPrompt(text) {
|
|
@@ -262,8 +263,25 @@ export async function* runAgent(options) {
|
|
|
262
263
|
for (const t of tools)
|
|
263
264
|
if (!t.run)
|
|
264
265
|
clientTools.add(t.def.function.name);
|
|
265
|
-
const defs = toolDefs(tools);
|
|
266
|
+
const defs = toolDefs(tools); // FULL set — for the search index, suppression, and call recovery
|
|
266
267
|
const byName = toolByName(tools);
|
|
268
|
+
// Deferred tools: kept OUT of the per-turn payload (token savings) but still
|
|
269
|
+
// discoverable via tool_search and executable. `tool_search` surfaces them and
|
|
270
|
+
// arms them (adds their schema to subsequent turns). tool_search itself is
|
|
271
|
+
// never deferred, or discovery breaks.
|
|
272
|
+
const deferredSet = new Set([...(options.deferredTools ?? []), ...tools.filter((t) => t.defer).map((t) => t.def.function.name)].filter((n) => n !== 'tool_search'));
|
|
273
|
+
const armed = new Set();
|
|
274
|
+
const sentDefs = () => deferredSet.size
|
|
275
|
+
? toolDefs(tools.filter((t) => !deferredSet.has(t.def.function.name) || armed.has(t.def.function.name)))
|
|
276
|
+
: defs;
|
|
277
|
+
// Stop streaming visible deltas once tool-call / reasoning markup begins (native
|
|
278
|
+
// dialects, <thinking>, or named-tag tools like <finish>); final text is cleaned.
|
|
279
|
+
const streamSuppressRe = (() => {
|
|
280
|
+
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
281
|
+
const names = defs.map((d) => d.function.name).filter(Boolean).map(esc);
|
|
282
|
+
const named = names.length ? `|<(?:${names.join('|')})[\\s/>]` : '';
|
|
283
|
+
return new RegExp(`<tool_call|<function\\s*=|<thinking${named}`, 'i');
|
|
284
|
+
})();
|
|
267
285
|
let system = options.systemPrompt != null ? options.systemPrompt : defaultSystemPrompt(cwd);
|
|
268
286
|
if (teamEnabled)
|
|
269
287
|
system += '\n\n' + coordinatorPrompt();
|
|
@@ -293,6 +311,13 @@ export async function* runAgent(options) {
|
|
|
293
311
|
memory,
|
|
294
312
|
skills,
|
|
295
313
|
planMode,
|
|
314
|
+
armTools: deferredSet.size
|
|
315
|
+
? (names) => {
|
|
316
|
+
for (const n of names)
|
|
317
|
+
if (deferredSet.has(n))
|
|
318
|
+
armed.add(n);
|
|
319
|
+
}
|
|
320
|
+
: undefined,
|
|
296
321
|
};
|
|
297
322
|
const skillCommands = skillsToCommands(skills);
|
|
298
323
|
const allCommands = [...(options.commands ?? []), ...skillCommands];
|
|
@@ -403,7 +428,7 @@ export async function* runAgent(options) {
|
|
|
403
428
|
subtype: 'init',
|
|
404
429
|
apiKeySource: 'none',
|
|
405
430
|
cwd,
|
|
406
|
-
tools:
|
|
431
|
+
tools: sentDefs().map((d) => d.function.name),
|
|
407
432
|
mcp_servers: mcpStatuses,
|
|
408
433
|
model: model ?? 'unknown',
|
|
409
434
|
permissionMode,
|
|
@@ -632,14 +657,14 @@ export async function* runAgent(options) {
|
|
|
632
657
|
let inToolMarkup = false;
|
|
633
658
|
const sp = llm.streamChat(history, {
|
|
634
659
|
model,
|
|
635
|
-
tools:
|
|
660
|
+
tools: sentDefs(),
|
|
636
661
|
signal,
|
|
637
662
|
onToken: (delta) => {
|
|
638
663
|
streamedText += delta;
|
|
639
664
|
// Stop streaming once inline tool-call markup begins; it would
|
|
640
665
|
// otherwise flood the UI with raw XML / file contents. The cleaned
|
|
641
666
|
// text arrives with the final assistant message.
|
|
642
|
-
if (!inToolMarkup &&
|
|
667
|
+
if (!inToolMarkup && streamSuppressRe.test(streamedText)) {
|
|
643
668
|
inToolMarkup = true;
|
|
644
669
|
}
|
|
645
670
|
if (inToolMarkup)
|
|
@@ -664,7 +689,7 @@ export async function* runAgent(options) {
|
|
|
664
689
|
else {
|
|
665
690
|
result = await llm.streamChat(history, {
|
|
666
691
|
model,
|
|
667
|
-
tools:
|
|
692
|
+
tools: sentDefs(),
|
|
668
693
|
signal,
|
|
669
694
|
onToken: (delta) => {
|
|
670
695
|
streamedText += delta;
|
|
@@ -680,8 +705,17 @@ export async function* runAgent(options) {
|
|
|
680
705
|
break;
|
|
681
706
|
}
|
|
682
707
|
apiMs += Date.now() - apiStart;
|
|
683
|
-
|
|
684
|
-
|
|
708
|
+
let text = result.text || streamedText;
|
|
709
|
+
let calls = result.toolCalls.length ? result.toolCalls : captured;
|
|
710
|
+
// Loop-level safety net: recover inline tool calls (native dialects +
|
|
711
|
+
// named-tag tools like <finish>) a custom LLMClient left as text, and scrub
|
|
712
|
+
// leaked tool/reasoning markup so raw tags never reach the user.
|
|
713
|
+
if (!calls.length) {
|
|
714
|
+
const recovered = parseToolCalls(text, { toolNames: defs.map((d) => d.function.name) });
|
|
715
|
+
if (recovered.calls.length)
|
|
716
|
+
calls = recovered.calls;
|
|
717
|
+
text = recovered.cleanedText;
|
|
718
|
+
}
|
|
685
719
|
lastText = text || lastText;
|
|
686
720
|
resultModel = result.model || resultModel;
|
|
687
721
|
addUsageInto(usageTotal, result.usage);
|
package/dist/llm/dialects.d.ts
CHANGED
|
@@ -16,6 +16,21 @@ export interface ToolDialect {
|
|
|
16
16
|
export declare const xmlFunctionDialect: ToolDialect;
|
|
17
17
|
export declare const hermesDialect: ToolDialect;
|
|
18
18
|
export declare const jsonFenceDialect: ToolDialect;
|
|
19
|
+
/**
|
|
20
|
+
* Named-tag tool calls (the Cline/Roo/Aider convention): a tool invoked as
|
|
21
|
+
* `<tool_name><param>value</param></tool_name>` (or `<tool_name/>`). Scoped to
|
|
22
|
+
* the KNOWN tool names so ordinary markup the model writes isn't misread. This
|
|
23
|
+
* is what leaks as raw `<finish>…</finish>` text when a model emulates a custom
|
|
24
|
+
* tool format and the SDK doesn't recognize it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseNamedTagToolCalls(text: string, toolNames: string[], idBase?: number): ParsedToolCalls;
|
|
27
|
+
/**
|
|
28
|
+
* Remove leaked reasoning / tool-wrapper markup from user-visible text:
|
|
29
|
+
* `<thinking>…</thinking>` blocks and orphan `<tool_call>` / `<function…>` /
|
|
30
|
+
* `<parameter…>` tags that a model emitted as prose. Conservative — only these
|
|
31
|
+
* well-known control tags, which essentially never appear in legitimate output.
|
|
32
|
+
*/
|
|
33
|
+
export declare function stripControlTags(text: string): string;
|
|
19
34
|
/** All built-in dialects, keyed by name. */
|
|
20
35
|
export declare const dialects: Record<string, ToolDialect>;
|
|
21
36
|
/** Default attempt order — xml-function first preserves original behavior. */
|
|
@@ -27,6 +42,7 @@ export declare const DEFAULT_DIALECTS: string[];
|
|
|
27
42
|
export declare function parseToolCalls(text: string, opts?: {
|
|
28
43
|
dialects?: string[];
|
|
29
44
|
idBase?: number;
|
|
45
|
+
toolNames?: string[];
|
|
30
46
|
}): ParsedToolCalls;
|
|
31
47
|
/** True if ANY of the given dialects (default: all) detects tool-call markup. */
|
|
32
48
|
export declare function hasToolCalls(text: string, order?: string[]): boolean;
|
package/dist/llm/dialects.js
CHANGED
|
@@ -117,6 +117,69 @@ export const jsonFenceDialect = {
|
|
|
117
117
|
return { calls, cleanedText };
|
|
118
118
|
},
|
|
119
119
|
};
|
|
120
|
+
function escapeRe(s) {
|
|
121
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
122
|
+
}
|
|
123
|
+
/** Extract params from a tool-tag body: both `<parameter=key>v</parameter>` and direct `<key>v</key>` children. */
|
|
124
|
+
function parseTagParams(body) {
|
|
125
|
+
const args = {};
|
|
126
|
+
const pRe = /<parameter\s*=\s*([^>\s]+)\s*>([\s\S]*?)(?:<\/parameter>|<parameter\s*=|$)/gi;
|
|
127
|
+
let m;
|
|
128
|
+
while ((m = pRe.exec(body)) !== null)
|
|
129
|
+
args[m[1].trim()] = trimEdges(m[2]);
|
|
130
|
+
const tRe = /<([a-zA-Z_][\w-]*)\s*>([\s\S]*?)<\/\1>/g;
|
|
131
|
+
while ((m = tRe.exec(body)) !== null) {
|
|
132
|
+
const k = m[1];
|
|
133
|
+
if (k === 'parameter' || k in args)
|
|
134
|
+
continue;
|
|
135
|
+
args[k] = trimEdges(m[2]);
|
|
136
|
+
}
|
|
137
|
+
return args;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Named-tag tool calls (the Cline/Roo/Aider convention): a tool invoked as
|
|
141
|
+
* `<tool_name><param>value</param></tool_name>` (or `<tool_name/>`). Scoped to
|
|
142
|
+
* the KNOWN tool names so ordinary markup the model writes isn't misread. This
|
|
143
|
+
* is what leaks as raw `<finish>…</finish>` text when a model emulates a custom
|
|
144
|
+
* tool format and the SDK doesn't recognize it.
|
|
145
|
+
*/
|
|
146
|
+
export function parseNamedTagToolCalls(text, toolNames, idBase = 0) {
|
|
147
|
+
if (!text || !toolNames?.length)
|
|
148
|
+
return { calls: [], cleanedText: text };
|
|
149
|
+
let best = { idx: -1, name: '', after: -1 };
|
|
150
|
+
for (const name of toolNames) {
|
|
151
|
+
const re = new RegExp('<' + escapeRe(name) + '(?:\\s[^>]*)?/?>', 'i');
|
|
152
|
+
const m = re.exec(text);
|
|
153
|
+
if (m && (best.idx < 0 || m.index < best.idx))
|
|
154
|
+
best = { idx: m.index, name, after: m.index + m[0].length };
|
|
155
|
+
}
|
|
156
|
+
if (best.idx < 0)
|
|
157
|
+
return { calls: [], cleanedText: text };
|
|
158
|
+
const closer = new RegExp('</' + escapeRe(best.name) + '>', 'i');
|
|
159
|
+
const rest = text.slice(best.after);
|
|
160
|
+
const cm = closer.exec(rest);
|
|
161
|
+
const body = cm ? rest.slice(0, cm.index) : rest;
|
|
162
|
+
const args = parseTagParams(body);
|
|
163
|
+
return {
|
|
164
|
+
calls: [{ id: `call_inline_${idBase}`, type: 'function', function: { name: best.name, arguments: JSON.stringify(args) } }],
|
|
165
|
+
cleanedText: text.slice(0, best.idx).trim(),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Remove leaked reasoning / tool-wrapper markup from user-visible text:
|
|
170
|
+
* `<thinking>…</thinking>` blocks and orphan `<tool_call>` / `<function…>` /
|
|
171
|
+
* `<parameter…>` tags that a model emitted as prose. Conservative — only these
|
|
172
|
+
* well-known control tags, which essentially never appear in legitimate output.
|
|
173
|
+
*/
|
|
174
|
+
export function stripControlTags(text) {
|
|
175
|
+
if (!text || text.indexOf('<') < 0)
|
|
176
|
+
return text;
|
|
177
|
+
return text
|
|
178
|
+
.replace(/<thinking\s*>[\s\S]*?<\/thinking\s*>/gi, '')
|
|
179
|
+
.replace(/<\/?(?:thinking|tool_call|function|parameter|antml:[a-z_]+)(?:\s[^>]*|=[^>]*)?\/?>/gi, '')
|
|
180
|
+
.replace(/[ \t]+(\r?\n)/g, '$1')
|
|
181
|
+
.trim();
|
|
182
|
+
}
|
|
120
183
|
/** All built-in dialects, keyed by name. */
|
|
121
184
|
export const dialects = {
|
|
122
185
|
'xml-function': xmlFunctionDialect,
|
|
@@ -139,9 +202,17 @@ export function parseToolCalls(text, opts = {}) {
|
|
|
139
202
|
continue;
|
|
140
203
|
const parsed = d.parse(text, opts.idBase ?? 0);
|
|
141
204
|
if (parsed.calls.length)
|
|
142
|
-
return parsed;
|
|
205
|
+
return { calls: parsed.calls, cleanedText: stripControlTags(parsed.cleanedText) };
|
|
206
|
+
}
|
|
207
|
+
// Named-tag fallback (e.g. `<finish>…</finish>`) — scoped to known tool names.
|
|
208
|
+
if (opts.toolNames?.length) {
|
|
209
|
+
const named = parseNamedTagToolCalls(text, opts.toolNames, opts.idBase ?? 0);
|
|
210
|
+
if (named.calls.length)
|
|
211
|
+
return { calls: named.calls, cleanedText: stripControlTags(named.cleanedText) };
|
|
143
212
|
}
|
|
144
|
-
|
|
213
|
+
// No tool call recognized — still scrub any leaked control/reasoning markup so
|
|
214
|
+
// raw tags never render to the user.
|
|
215
|
+
return { calls: [], cleanedText: stripControlTags(text) };
|
|
145
216
|
}
|
|
146
217
|
/** True if ANY of the given dialects (default: all) detects tool-call markup. */
|
|
147
218
|
export function hasToolCalls(text, order = DEFAULT_DIALECTS) {
|
package/dist/llm/openai.js
CHANGED
|
@@ -113,11 +113,9 @@ export function createOpenAIClient(options = {}) {
|
|
|
113
113
|
// visible text. (Empty `dialects` — e.g. for native GPT/Claude — skips this.)
|
|
114
114
|
let finalText = text;
|
|
115
115
|
if (!toolCalls.length && (!dialects || dialects.length)) {
|
|
116
|
-
const inline = parseToolCalls(text, { dialects });
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
finalText = inline.cleanedText;
|
|
120
|
-
}
|
|
116
|
+
const inline = parseToolCalls(text, { dialects, toolNames: opts.tools?.map((t) => t.function.name) });
|
|
117
|
+
toolCalls.push(...inline.calls);
|
|
118
|
+
finalText = inline.cleanedText; // also scrubs leaked control tags even when no call is found
|
|
121
119
|
}
|
|
122
120
|
if (toolCalls.length && opts.onTool)
|
|
123
121
|
opts.onTool(toolCalls);
|
package/dist/loop.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { toolByName, toolDefs } from './tools/index.js';
|
|
2
2
|
import { validateToolArguments } from './llm/repair.js';
|
|
3
|
+
import { parseToolCalls } from './llm/dialects.js';
|
|
3
4
|
import { uuid } from './util/ids.js';
|
|
5
|
+
/** Regex that matches the onset of tool-call / reasoning markup in streamed text. */
|
|
6
|
+
function buildSuppressRe(toolNames) {
|
|
7
|
+
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
8
|
+
const names = toolNames.filter(Boolean).map(esc);
|
|
9
|
+
const named = names.length ? `|<(?:${names.join('|')})[\\s/>]` : '';
|
|
10
|
+
return new RegExp(`<tool_call|<function\\s*=|<thinking${named}`, 'i');
|
|
11
|
+
}
|
|
4
12
|
const emptyUsage = () => ({ input_tokens: 0, output_tokens: 0 });
|
|
5
13
|
function addUsage(t, b) {
|
|
6
14
|
if (!b)
|
|
@@ -85,6 +93,10 @@ export async function* runToolLoop(opts) {
|
|
|
85
93
|
const emitPartial = !!opts.includePartialMessages;
|
|
86
94
|
const byName = toolByName(tools);
|
|
87
95
|
const defs = toolDefs(tools);
|
|
96
|
+
// Stop streaming visible deltas once tool-call / reasoning markup begins — the
|
|
97
|
+
// final cleaned text comes from the parsed result. Covers native dialects,
|
|
98
|
+
// <thinking>, and named-tag tools (e.g. <finish>) so they never flicker to the UI.
|
|
99
|
+
const suppressRe = buildSuppressRe(defs.map((d) => d.function.name));
|
|
88
100
|
const startedAt = Date.now();
|
|
89
101
|
let apiMs = 0;
|
|
90
102
|
let turns = 0;
|
|
@@ -115,7 +127,7 @@ export async function* runToolLoop(opts) {
|
|
|
115
127
|
signal,
|
|
116
128
|
onToken: (delta) => {
|
|
117
129
|
streamedText += delta;
|
|
118
|
-
if (!inToolMarkup &&
|
|
130
|
+
if (!inToolMarkup && suppressRe.test(streamedText))
|
|
119
131
|
inToolMarkup = true;
|
|
120
132
|
if (inToolMarkup)
|
|
121
133
|
return;
|
|
@@ -155,8 +167,17 @@ export async function* runToolLoop(opts) {
|
|
|
155
167
|
break;
|
|
156
168
|
}
|
|
157
169
|
apiMs += Date.now() - apiStart;
|
|
158
|
-
|
|
159
|
-
|
|
170
|
+
let text = result.text || streamedText;
|
|
171
|
+
let calls = result.toolCalls.length ? result.toolCalls : captured;
|
|
172
|
+
// Loop-level safety net: recover tool calls a (possibly custom) LLMClient left
|
|
173
|
+
// as inline text — native dialects + named-tag tools — and scrub leaked
|
|
174
|
+
// tool/reasoning markup so it never renders. Runs for ANY client, not just ours.
|
|
175
|
+
if (!calls.length) {
|
|
176
|
+
const recovered = parseToolCalls(text, { toolNames: defs.map((d) => d.function.name) });
|
|
177
|
+
if (recovered.calls.length)
|
|
178
|
+
calls = recovered.calls;
|
|
179
|
+
text = recovered.cleanedText;
|
|
180
|
+
}
|
|
160
181
|
lastText = text || lastText;
|
|
161
182
|
resultModel = result.model || resultModel;
|
|
162
183
|
addUsage(usageTotal, result.usage);
|
package/dist/query.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export interface QueryOptions {
|
|
|
22
22
|
appendSystemPrompt?: string;
|
|
23
23
|
allowedTools?: string[];
|
|
24
24
|
disallowedTools?: string[];
|
|
25
|
+
/** Tool names to defer out of the per-turn payload — discoverable via `tool_search`
|
|
26
|
+
* and armed on demand. Saves tokens on large tool pools (also per-tool `defer: true`). */
|
|
27
|
+
deferredTools?: string[];
|
|
25
28
|
maxTurns?: number;
|
|
26
29
|
/** Wall-clock budget (ms): pause at a turn boundary past this + emit `paused` (survivor). */
|
|
27
30
|
maxDurationMs?: number;
|
package/dist/query.js
CHANGED
|
@@ -21,6 +21,7 @@ export function query(options) {
|
|
|
21
21
|
appendSystemPrompt: options.appendSystemPrompt,
|
|
22
22
|
allowedTools: options.allowedTools,
|
|
23
23
|
disallowedTools: options.disallowedTools,
|
|
24
|
+
deferredTools: options.deferredTools,
|
|
24
25
|
maxTurns: options.maxTurns,
|
|
25
26
|
maxDurationMs: options.maxDurationMs,
|
|
26
27
|
continueRun: options.continueRun,
|
package/dist/tools/define.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export interface DefineToolSpec {
|
|
|
15
15
|
run?: (input: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult> | ToolResult;
|
|
16
16
|
/** Optional: spill threshold for large outputs (see Tool.maxResultChars). */
|
|
17
17
|
maxResultChars?: number;
|
|
18
|
+
/** Defer out of the per-turn payload — discoverable via `tool_search`, armed on
|
|
19
|
+
* demand. For large pools of rarely-used tools (see Tool.defer). */
|
|
20
|
+
defer?: boolean;
|
|
18
21
|
}
|
|
19
22
|
/** Build a `Tool` from a friendly spec. */
|
|
20
23
|
export declare function defineTool(spec: DefineToolSpec): Tool;
|
package/dist/tools/define.js
CHANGED
|
@@ -41,8 +41,11 @@ export const toolSearch = {
|
|
|
41
41
|
.slice(0, limit);
|
|
42
42
|
if (!scored.length)
|
|
43
43
|
return { content: `No tools matched "${q}".` };
|
|
44
|
+
// Arm any deferred tools we surfaced so their full schema is sent next turn
|
|
45
|
+
// and the model can call them directly.
|
|
46
|
+
ctx.armTools?.(scored.map(({ t }) => t.name));
|
|
44
47
|
return {
|
|
45
|
-
content: `Matching tools for "${q}":\n` +
|
|
48
|
+
content: `Matching tools for "${q}" (now available to call):\n` +
|
|
46
49
|
scored.map(({ t }) => ` ${t.name} — ${t.description.split('\n')[0]}`).join('\n'),
|
|
47
50
|
};
|
|
48
51
|
},
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -72,6 +72,9 @@ export interface ToolContext {
|
|
|
72
72
|
planMode?: {
|
|
73
73
|
active: boolean;
|
|
74
74
|
};
|
|
75
|
+
/** Arm deferred tools by name so their full schema is sent on subsequent turns.
|
|
76
|
+
* Provided by the loop; `tool_search` calls it for the deferred tools it surfaces. */
|
|
77
|
+
armTools?: (names: string[]) => void;
|
|
75
78
|
}
|
|
76
79
|
/** Result returned by a tool run. */
|
|
77
80
|
export interface ToolResult {
|
|
@@ -92,4 +95,12 @@ export interface Tool {
|
|
|
92
95
|
* When omitted, the loop uses its global default threshold.
|
|
93
96
|
*/
|
|
94
97
|
maxResultChars?: number;
|
|
98
|
+
/**
|
|
99
|
+
* DEFER this tool out of the per-turn payload sent to the LLM. It stays
|
|
100
|
+
* discoverable via `tool_search` and executable when called, but its schema
|
|
101
|
+
* isn't sent (saving tokens every turn) until `tool_search` surfaces it — at
|
|
102
|
+
* which point the loop "arms" it for subsequent turns. Use for large pools of
|
|
103
|
+
* rarely-used integration tools. (Also settable via `query({ deferredTools })`.)
|
|
104
|
+
*/
|
|
105
|
+
defer?: boolean;
|
|
95
106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anyclaude-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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",
|