toolnexus 0.21.0 → 0.22.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/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 +74 -2
- package/dist/client.js +292 -55
- 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/tooltiers.d.ts +49 -0
- package/dist/tooltiers.js +197 -0
- package/dist/types.d.ts +19 -0
- package/package.json +2 -2
- 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 +317 -60
- 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/tooltiers.ts +221 -0
- package/src/types.ts +20 -0
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:
|