toolnexus 0.21.0 → 0.23.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/README.md +50 -4
- package/dist/agents/agent.d.ts +12 -1
- package/dist/agents/agent.js +43 -7
- package/dist/agents/compaction.d.ts +19 -0
- package/dist/agents/compaction.js +26 -4
- package/dist/agents/runtime.d.ts +5 -0
- package/dist/agents/runtime.js +13 -2
- package/dist/builtin.d.ts +16 -0
- package/dist/builtin.js +315 -17
- package/dist/builtin_extras.d.ts +80 -0
- package/dist/builtin_extras.js +248 -0
- package/dist/client.d.ts +77 -2
- package/dist/client.js +304 -61
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/mcp.d.ts +32 -1
- package/dist/mcp.js +184 -6
- package/dist/models.d.ts +20 -0
- package/dist/models.js +46 -0
- package/dist/promptcache.d.ts +21 -0
- package/dist/promptcache.js +59 -0
- package/dist/store.d.ts +32 -0
- package/dist/store.js +131 -0
- package/dist/toolkit.d.ts +2 -2
- package/dist/toolkit.js +2 -2
- package/dist/tooltiers.d.ts +49 -0
- package/dist/tooltiers.js +197 -0
- package/dist/types.d.ts +19 -0
- package/package.json +4 -3
- package/src/agents/agent.ts +39 -8
- package/src/agents/compaction.ts +45 -4
- package/src/agents/runtime.ts +16 -2
- package/src/builtin.ts +333 -18
- package/src/builtin_extras.ts +285 -0
- package/src/client.ts +330 -66
- package/src/index.ts +5 -0
- package/src/mcp.ts +204 -7
- package/src/models.ts +56 -0
- package/src/promptcache.ts +63 -0
- package/src/store.ts +142 -0
- package/src/toolkit.ts +2 -2
- package/src/tooltiers.ts +221 -0
- package/src/types.ts +20 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ unified as one tool set, driving any LLM.
|
|
|
12
12
|
> loop, hooks, streaming, retries, memory — and nothing it doesn't.
|
|
13
13
|
|
|
14
14
|
The JS/TypeScript port of [toolnexus](https://github.com/muthuishere/toolnexus) — the same
|
|
15
|
-
library, byte-identical, also in **Python, Go, Java, C#, Elixir and
|
|
15
|
+
library, byte-identical, also in **Python, Go, Java, C#, Elixir, Clojure and Rust**. Built on
|
|
16
16
|
`@modelcontextprotocol/sdk` (the MCP SDK opencode uses).
|
|
17
17
|
|
|
18
18
|
## Install
|
|
@@ -28,11 +28,11 @@ Built-in tools are on by default, so an empty toolkit can already act:
|
|
|
28
28
|
```ts
|
|
29
29
|
import { createToolkit, createClient } from "toolnexus"
|
|
30
30
|
|
|
31
|
-
const tk = await createToolkit()
|
|
31
|
+
const tk = await createToolkit({}) // 10 built-in tools, on by default
|
|
32
32
|
const agent = createClient({
|
|
33
33
|
baseUrl: "https://openrouter.ai/api/v1", // any OpenAI- or Anthropic-style endpoint
|
|
34
34
|
style: "openai", // or "anthropic"
|
|
35
|
-
model: "openai/gpt-4o-mini",
|
|
35
|
+
model: "openai/gpt-4o-mini", // key: OPENROUTER_API_KEY (matches the host)
|
|
36
36
|
})
|
|
37
37
|
|
|
38
38
|
const { text } = await agent.run("What files are in this folder?", { toolkit: tk })
|
|
@@ -49,6 +49,52 @@ const tk = await createToolkit({ mcp: "mcp.json", skills: ["skills"] })
|
|
|
49
49
|
- `skills/` is a folder of `**/SKILL.md` files, loaded on demand through one `skill` tool.
|
|
50
50
|
- Remote MCP `headers` values expand `${ENV_VAR}` at call time and are never logged.
|
|
51
51
|
|
|
52
|
+
## API key by host (BREAKING in 0.22)
|
|
53
|
+
|
|
54
|
+
A key is only ever sent to the host it belongs to. `apiKey` wins; else `apiKeyEnv` (the NAME of
|
|
55
|
+
an env var); else only the env var of the `baseUrl` host, over https — `api.openai.com` →
|
|
56
|
+
`OPENAI_API_KEY`, `api.anthropic.com` → `ANTHROPIC_API_KEY`, `openrouter.ai` →
|
|
57
|
+
`OPENROUTER_API_KEY`. **A local server, a proxy or any other host needs `apiKey` or `apiKeyEnv`**
|
|
58
|
+
(before 0.22 the first provider key in the environment was sent to any `baseUrl`):
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
createClient({ baseUrl: "http://localhost:11434/v1", style: "openai", model: "llama3", apiKey: "YOUR_KEY_HERE" })
|
|
62
|
+
createClient({ baseUrl: "https://my-proxy.example/v1", style: "openai", model: "m", apiKeyEnv: "MY_PROXY_KEY" })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Prompt cache and tool tiers
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const agent = createClient({
|
|
69
|
+
baseUrl, style, model,
|
|
70
|
+
promptCache: { key: "session-42" }, // anthropic cache breakpoints / openai prompt_cache_key
|
|
71
|
+
toolTiers: { core: ["read", "grep"] }, // the rest load on demand via tool_search / call_tool
|
|
72
|
+
})
|
|
73
|
+
const res = await agent.run(prompt, { toolkit: tk })
|
|
74
|
+
res.usage.cacheReadTokens // cache hits; anthropic context = prompt + cacheRead + cacheWrite
|
|
75
|
+
agent.toolTierPlan(tk) // { read: "core", bash: "listed", … }, frozen per toolkit
|
|
76
|
+
agent.resetToolTiers(tk) // rebuild at session start
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`toolTiers` keeps the tools array identical on every request, so the cached prefix survives a
|
|
80
|
+
large or growing toolkit. `call_tool` is unwrapped before hooks, so permission gates see the real
|
|
81
|
+
tool. Runnable: `npm run example:prompt-cache` ([`examples/prompt_cache.ts`](examples/prompt_cache.ts)).
|
|
82
|
+
|
|
83
|
+
## Host additions (0.22)
|
|
84
|
+
|
|
85
|
+
- `loadMcp(cfg, { onServer })` — each MCP server reported the moment it connects or fails;
|
|
86
|
+
`mcpSource.resourceTools()` adds `list_mcp_resources` / `read_mcp_resource`.
|
|
87
|
+
- `waitFor(request, { signal })` — a pending question ends with its turn.
|
|
88
|
+
- Tool `hints` (`readOnly` / `destructive` / `openWorld`) on every builtin and MCP tool, passed to
|
|
89
|
+
`hooks.beforeTool` — allow, deny with an error result, or ask via `pending`.
|
|
90
|
+
- Opt-in builtins: `builtins: { background: new BackgroundShells(), webSearch, notebookEdit: true }`
|
|
91
|
+
(`bash_output` / `kill_shell`, `websearch`, `notebook_edit`); `grep` takes `context` / `before` /
|
|
92
|
+
`after`; `read` / `grep` report truncation in metadata.
|
|
93
|
+
- `agents.compactor({ onCompact })`, `agent.asToolWith(ctx => ({ hooks, waitFor }))`, runtime
|
|
94
|
+
`waitFor`, `ctx.callId` on tools, `listModels(opts)`.
|
|
95
|
+
|
|
96
|
+
ACP host hooks (permission choice, update stream, cancel, streaming) are Go-only for now.
|
|
97
|
+
|
|
52
98
|
## Simple judgments
|
|
53
99
|
|
|
54
100
|
A thin layer over any `Classifier` (SPEC.md §8B); the wire request is byte-identical to
|
|
@@ -102,4 +148,4 @@ Everything else — the full surface, with runnable examples — lives on the do
|
|
|
102
148
|
| **API reference** | [JavaScript](https://muthuishere.github.io/toolnexus/api/javascript/) |
|
|
103
149
|
| **Cookbook** | [Zero to agent](https://muthuishere.github.io/toolnexus/cookbook/zero-to-agent/) · [MCP servers](https://muthuishere.github.io/toolnexus/cookbook/mcp-servers/) · [Agent skills](https://muthuishere.github.io/toolnexus/cookbook/agent-skills/) · [Judge](https://muthuishere.github.io/toolnexus/cookbook/judge/) |
|
|
104
150
|
|
|
105
|
-
Contract across all
|
|
151
|
+
Contract across all eight ports: [`SPEC.md`](https://github.com/muthuishere/toolnexus/blob/main/SPEC.md).
|
package/dist/agents/agent.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Everything compiles down to the six host verbs in runtime.ts.
|
|
9
9
|
*/
|
|
10
10
|
import type { PromptInput } from "../content.js";
|
|
11
|
-
import type { Answer, Request, Tool } from "../types.js";
|
|
11
|
+
import type { Answer, Request, Tool, ToolContext } from "../types.js";
|
|
12
12
|
import { AgentRuntime, type AgentDef, type Budget, type RuntimeOptions, type TaskResult } from "./runtime.js";
|
|
13
13
|
import { Loop, type Completion, type Guardrail } from "./loop.js";
|
|
14
14
|
import type { ClientOptions } from "../client.js";
|
|
@@ -93,6 +93,8 @@ export declare class Agent {
|
|
|
93
93
|
* case the tree stays parked and `result.runtime.resume(answer)` continues it.
|
|
94
94
|
*/
|
|
95
95
|
run(prompt: PromptInput, opts?: AgentRunOptions): Promise<AgentRunResult>;
|
|
96
|
+
/** `run` that interrupts the child when `signal` aborts. */
|
|
97
|
+
private runSignal;
|
|
96
98
|
/**
|
|
97
99
|
* The bridge: an Agent IS a Tool. Drop the result into the classic API's
|
|
98
100
|
* `extraTools`; the agent's own loop runs in isolation and the classic run
|
|
@@ -100,6 +102,15 @@ export declare class Agent {
|
|
|
100
102
|
* `{agent, turns, totalTokens}`.
|
|
101
103
|
*/
|
|
102
104
|
asTool(opts?: AgentRunOptions): Tool;
|
|
105
|
+
/**
|
|
106
|
+
* `asTool` with options built per call from the calling tool's context (SPEC §4C), so the
|
|
107
|
+
* child runs under the parent turn's gates: set `hooks` (the same beforeTool permission gate —
|
|
108
|
+
* it also sees the child's tool calls), `waitFor` (the same ask bridge) and `onMetric`. A
|
|
109
|
+
* `hooks` on the agent's own spec replaces `opts.hooks` for that agent, so leave it unset when
|
|
110
|
+
* the gate must hold. When the calling turn is cancelled (`ctx.signal`), the child is
|
|
111
|
+
* interrupted and the tool returns a `cancelled:` error result.
|
|
112
|
+
*/
|
|
113
|
+
asToolWith(per: (ctx?: ToolContext) => AgentRunOptions): Tool;
|
|
103
114
|
}
|
|
104
115
|
/** The one new noun: define an agent. */
|
|
105
116
|
export declare function agent(name: string, spec: AgentSpec): Agent;
|
package/dist/agents/agent.js
CHANGED
|
@@ -73,6 +73,32 @@ export class Agent {
|
|
|
73
73
|
await runtime.close(runtime.root);
|
|
74
74
|
return { ...r, runtime };
|
|
75
75
|
}
|
|
76
|
+
/** `run` that interrupts the child when `signal` aborts. */
|
|
77
|
+
async runSignal(prompt, opts, signal) {
|
|
78
|
+
if (!signal)
|
|
79
|
+
return this.run(prompt, opts);
|
|
80
|
+
const reason = () => (signal.reason instanceof Error ? signal.reason.message : String(signal.reason ?? "aborted"));
|
|
81
|
+
if (signal.aborted)
|
|
82
|
+
return { text: reason(), isError: true, turns: 0, totalTokens: 0, cancelled: true };
|
|
83
|
+
const runtime = this.createRuntime(opts);
|
|
84
|
+
const h = runtime.spawn(runtime.root, this.name);
|
|
85
|
+
if (isVerbError(h))
|
|
86
|
+
return { text: h.error, isError: true, turns: 0, totalTokens: 0 };
|
|
87
|
+
const onAbort = () => void runtime.close(runtime.root, { force: true }).catch(() => { });
|
|
88
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
89
|
+
try {
|
|
90
|
+
const woke = runtime.wake(h, prompt);
|
|
91
|
+
const r = await runtime.wait(h);
|
|
92
|
+
if (signal.aborted)
|
|
93
|
+
return { text: reason(), isError: true, turns: r.turns, totalTokens: r.totalTokens, cancelled: true };
|
|
94
|
+
if (woke.ok && r.status !== "pending")
|
|
95
|
+
await runtime.close(runtime.root);
|
|
96
|
+
return r;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
signal.removeEventListener("abort", onAbort);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
76
102
|
/**
|
|
77
103
|
* The bridge: an Agent IS a Tool. Drop the result into the classic API's
|
|
78
104
|
* `extraTools`; the agent's own loop runs in isolation and the classic run
|
|
@@ -80,6 +106,17 @@ export class Agent {
|
|
|
80
106
|
* `{agent, turns, totalTokens}`.
|
|
81
107
|
*/
|
|
82
108
|
asTool(opts = {}) {
|
|
109
|
+
return this.asToolWith(() => opts);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `asTool` with options built per call from the calling tool's context (SPEC §4C), so the
|
|
113
|
+
* child runs under the parent turn's gates: set `hooks` (the same beforeTool permission gate —
|
|
114
|
+
* it also sees the child's tool calls), `waitFor` (the same ask bridge) and `onMetric`. A
|
|
115
|
+
* `hooks` on the agent's own spec replaces `opts.hooks` for that agent, so leave it unset when
|
|
116
|
+
* the gate must hold. When the calling turn is cancelled (`ctx.signal`), the child is
|
|
117
|
+
* interrupted and the tool returns a `cancelled:` error result.
|
|
118
|
+
*/
|
|
119
|
+
asToolWith(per) {
|
|
83
120
|
return defineTool({
|
|
84
121
|
name: this.name,
|
|
85
122
|
description: this.spec.does,
|
|
@@ -88,13 +125,12 @@ export class Agent {
|
|
|
88
125
|
properties: { prompt: { type: "string", description: "The task for this agent" } },
|
|
89
126
|
required: ["prompt"],
|
|
90
127
|
},
|
|
91
|
-
run: async (args) => {
|
|
92
|
-
const r = await this.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
};
|
|
128
|
+
run: async (args, ctx) => {
|
|
129
|
+
const r = await this.runSignal(String(args.prompt), per(ctx), ctx?.signal);
|
|
130
|
+
const metadata = { agent: this.name, turns: r.turns, totalTokens: r.totalTokens };
|
|
131
|
+
if (r.cancelled)
|
|
132
|
+
return { output: `cancelled: ${r.text}`, isError: true, metadata };
|
|
133
|
+
return { output: r.text, isError: r.isError, metadata };
|
|
98
134
|
},
|
|
99
135
|
});
|
|
100
136
|
}
|
|
@@ -32,6 +32,25 @@ export interface CompactorOptions {
|
|
|
32
32
|
* default. Composes with the memory builtin.
|
|
33
33
|
*/
|
|
34
34
|
flushToMemory?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Called after each compaction with what changed, so a host can tell its UI or log (SPEC §4C).
|
|
37
|
+
* Never called on a no-op. Awaited before the next model call.
|
|
38
|
+
*/
|
|
39
|
+
onCompact?: (info: CompactInfo) => void | Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/** One compaction, for {@link CompactorOptions.onCompact}. Token counts come from `countTokens`. */
|
|
42
|
+
export interface CompactInfo {
|
|
43
|
+
tokensBefore: number;
|
|
44
|
+
tokensAfter: number;
|
|
45
|
+
messagesBefore: number;
|
|
46
|
+
messagesAfter: number;
|
|
47
|
+
/** How many messages were folded into the summary. */
|
|
48
|
+
summarized: number;
|
|
49
|
+
/** The text `summarize` returned (without the header line). */
|
|
50
|
+
summary: string;
|
|
51
|
+
/** The summarized messages, verbatim, so a host can persist them (the loop saves only the
|
|
52
|
+
* compacted transcript). */
|
|
53
|
+
folded: Message[];
|
|
35
54
|
}
|
|
36
55
|
/** The event a `beforeLLM` hook receives (§8). */
|
|
37
56
|
export interface BeforeLLMEvent {
|
|
@@ -89,7 +89,8 @@ export function compactor(opts) {
|
|
|
89
89
|
const keepTail = opts.keepTail ?? Math.floor(opts.maxTokens / 2);
|
|
90
90
|
return async (ev) => {
|
|
91
91
|
const msgs = ev.messages;
|
|
92
|
-
|
|
92
|
+
const before = count(msgs);
|
|
93
|
+
if (before <= opts.maxTokens)
|
|
93
94
|
return undefined; // under budget → no-op
|
|
94
95
|
// Preserve a leading system prompt verbatim (identity / soul / skills — never summarized).
|
|
95
96
|
const headEnd = msgs[0]?.role === "system" ? 1 : 0;
|
|
@@ -97,16 +98,37 @@ export function compactor(opts) {
|
|
|
97
98
|
const split = findTailStart(msgs, headEnd, keepTail, count);
|
|
98
99
|
if (split <= headEnd)
|
|
99
100
|
return undefined; // nothing safely compactible
|
|
100
|
-
|
|
101
|
+
let older = msgs.slice(headEnd, split);
|
|
102
|
+
const tail = msgs.slice(split);
|
|
103
|
+
// The latest user prompt is never summarized away. When the only clean boundary is the first
|
|
104
|
+
// message after the system prompt (one prompt followed by a long agentic run), keep that
|
|
105
|
+
// prompt verbatim and summarize the work after it: [system, prompt, summary].
|
|
106
|
+
let pinned = [];
|
|
107
|
+
if (split === msgs.length && headEnd < msgs.length && isBoundary(msgs[headEnd])) {
|
|
108
|
+
pinned = msgs.slice(headEnd, headEnd + 1);
|
|
109
|
+
older = msgs.slice(headEnd + 1);
|
|
110
|
+
}
|
|
101
111
|
if (older.length === 0)
|
|
102
112
|
return undefined;
|
|
103
|
-
const tail = msgs.slice(split);
|
|
104
113
|
const flushNote = opts.flushToMemory
|
|
105
114
|
? [{ role: "system", content: FLUSH_TO_MEMORY_REMINDER }]
|
|
106
115
|
: [];
|
|
107
116
|
const summary = await opts.summarize(older);
|
|
108
117
|
const summaryMsg = { role: "system", content: `${SUMMARY_PREFIX}${summary}` };
|
|
109
|
-
|
|
118
|
+
const out = [...system, ...pinned, summaryMsg, ...flushNote, ...tail];
|
|
119
|
+
const after = count(out);
|
|
120
|
+
if (after >= before)
|
|
121
|
+
return undefined; // compaction would not shrink the transcript → no-op
|
|
122
|
+
await opts.onCompact?.({
|
|
123
|
+
tokensBefore: before,
|
|
124
|
+
tokensAfter: after,
|
|
125
|
+
messagesBefore: msgs.length,
|
|
126
|
+
messagesAfter: out.length,
|
|
127
|
+
summarized: older.length,
|
|
128
|
+
summary,
|
|
129
|
+
folded: older,
|
|
130
|
+
});
|
|
131
|
+
return { messages: out };
|
|
110
132
|
};
|
|
111
133
|
}
|
|
112
134
|
/**
|
package/dist/agents/runtime.d.ts
CHANGED
|
@@ -293,6 +293,11 @@ export interface RuntimeOptions {
|
|
|
293
293
|
/** §8 observability sink applied to EVERY agent's client, unless that agent's def
|
|
294
294
|
* sets its own `onMetric`. Resolves independently of `hooks`. Forwarded verbatim. */
|
|
295
295
|
onMetric?: (ev: MetricEvent) => void;
|
|
296
|
+
/** The runtime-level §10 interpreter (SPEC §4C): answers a suspension when no agent on the
|
|
297
|
+
* handle's chain has its own `waitFor`. Absent ⇒ unchanged (the suspension goes durable). A
|
|
298
|
+
* host running an agent as a tool (`asToolWith`) sets it so the child's asks reach the same
|
|
299
|
+
* bridge as the parent turn. */
|
|
300
|
+
waitFor?: (request: Request) => Promise<Answer>;
|
|
296
301
|
}
|
|
297
302
|
/**
|
|
298
303
|
* The §7D agent runtime: six host verbs over a tree of Handles, three loud
|
package/dist/agents/runtime.js
CHANGED
|
@@ -595,12 +595,23 @@ export class AgentRuntime {
|
|
|
595
595
|
* escalation traced (nearest interpreter wins; the Request's `data.path` names
|
|
596
596
|
* the suspended subtree that is asking). */
|
|
597
597
|
wrapWaitFor(h) {
|
|
598
|
-
const
|
|
598
|
+
const own = h.def.waitFor;
|
|
599
|
+
let waitFor = own;
|
|
600
|
+
if (!waitFor && this.opts.waitFor) {
|
|
601
|
+
// The runtime-level bridge answers only when NO agent on the chain has its own waitFor;
|
|
602
|
+
// otherwise the ask escalates to that nearest interpreter, as before.
|
|
603
|
+
let a = h.parent;
|
|
604
|
+
while (a && !a.def.waitFor)
|
|
605
|
+
a = a.parent;
|
|
606
|
+
if (!a)
|
|
607
|
+
waitFor = this.opts.waitFor;
|
|
608
|
+
}
|
|
599
609
|
if (!waitFor)
|
|
600
610
|
return undefined;
|
|
611
|
+
const answerer = own ? h.id : this.root.id;
|
|
601
612
|
return async (req) => {
|
|
602
613
|
const path = Array.isArray(req.data?.path) ? req.data.path.join("/") : h.id;
|
|
603
|
-
this.t(`${path}: escalate → ${
|
|
614
|
+
this.t(`${path}: escalate → ${answerer} answers ("nearest interpreter")`);
|
|
604
615
|
return waitFor(req);
|
|
605
616
|
};
|
|
606
617
|
}
|
package/dist/builtin.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Tool } from "./types.js";
|
|
2
|
+
import { BackgroundShells, type WebSearchFunc } from "./builtin_extras.js";
|
|
2
3
|
/**
|
|
3
4
|
* Config for the single global builtin toggle (mirrors MCP isEnabled precedence),
|
|
4
5
|
* plus the host boundary: which interpreter `bash` runs, what a relative path
|
|
@@ -28,6 +29,21 @@ export type BuiltinsConfig = boolean | {
|
|
|
28
29
|
* builtins, NOT a sandbox — `bash` still reaches the whole filesystem.
|
|
29
30
|
*/
|
|
30
31
|
confineToBaseDir?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* POSIX wait in ms between SIGTERM and SIGKILL of a timed-out or cancelled
|
|
34
|
+
* `bash` job's process group. Absent or 0 ⇒ 2000. No effect on Windows,
|
|
35
|
+
* which has no graceful stop for a console process (SPEC §4A).
|
|
36
|
+
*/
|
|
37
|
+
killGraceMs?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Opt-in (SPEC §4C): `bash` takes `run_in_background` and the `bash_output` / `kill_shell`
|
|
40
|
+
* tools exist. The host owns the registry: call `closeAll()` on shutdown.
|
|
41
|
+
*/
|
|
42
|
+
background?: BackgroundShells;
|
|
43
|
+
/** Opt-in (SPEC §4C): adds a `websearch` tool backed by this provider. The library ships none. */
|
|
44
|
+
webSearch?: WebSearchFunc;
|
|
45
|
+
/** Opt-in (SPEC §4C): adds the `notebook_edit` tool for .ipynb cells. */
|
|
46
|
+
notebookEdit?: boolean;
|
|
31
47
|
};
|
|
32
48
|
/**
|
|
33
49
|
* Whether the builtin source is on. Default ON. Same precedence as MCP:
|