@nodetool-ai/llm-nodes 0.8.1
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 +52 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/agent-control-tools.d.ts +22 -0
- package/dist/nodes/agent-control-tools.d.ts.map +1 -0
- package/dist/nodes/agent-control-tools.js +123 -0
- package/dist/nodes/agent-control-tools.js.map +1 -0
- package/dist/nodes/agent-loop.d.ts +73 -0
- package/dist/nodes/agent-loop.d.ts.map +1 -0
- package/dist/nodes/agent-loop.js +168 -0
- package/dist/nodes/agent-loop.js.map +1 -0
- package/dist/nodes/agent-recommended-models.d.ts +152 -0
- package/dist/nodes/agent-recommended-models.d.ts.map +1 -0
- package/dist/nodes/agent-recommended-models.js +656 -0
- package/dist/nodes/agent-recommended-models.js.map +1 -0
- package/dist/nodes/agent-thinking.d.ts +29 -0
- package/dist/nodes/agent-thinking.d.ts.map +1 -0
- package/dist/nodes/agent-thinking.js +139 -0
- package/dist/nodes/agent-thinking.js.map +1 -0
- package/dist/nodes/agent-threads.d.ts +17 -0
- package/dist/nodes/agent-threads.d.ts.map +1 -0
- package/dist/nodes/agent-threads.js +93 -0
- package/dist/nodes/agent-threads.js.map +1 -0
- package/dist/nodes/agent-tool-hydration.d.ts +50 -0
- package/dist/nodes/agent-tool-hydration.d.ts.map +1 -0
- package/dist/nodes/agent-tool-hydration.js +106 -0
- package/dist/nodes/agent-tool-hydration.js.map +1 -0
- package/dist/nodes/agent-utils.d.ts +174 -0
- package/dist/nodes/agent-utils.d.ts.map +1 -0
- package/dist/nodes/agent-utils.js +553 -0
- package/dist/nodes/agent-utils.js.map +1 -0
- package/dist/nodes/agents.d.ts +402 -0
- package/dist/nodes/agents.d.ts.map +1 -0
- package/dist/nodes/agents.js +1491 -0
- package/dist/nodes/agents.js.map +1 -0
- package/dist/nodes/director.d.ts +126 -0
- package/dist/nodes/director.d.ts.map +1 -0
- package/dist/nodes/director.js +337 -0
- package/dist/nodes/director.js.map +1 -0
- package/dist/nodes/gemini.d.ts +171 -0
- package/dist/nodes/gemini.d.ts.map +1 -0
- package/dist/nodes/gemini.js +985 -0
- package/dist/nodes/gemini.js.map +1 -0
- package/dist/nodes/generators.d.ts +162 -0
- package/dist/nodes/generators.d.ts.map +1 -0
- package/dist/nodes/generators.js +966 -0
- package/dist/nodes/generators.js.map +1 -0
- package/dist/nodes/mistral.d.ts +109 -0
- package/dist/nodes/mistral.d.ts.map +1 -0
- package/dist/nodes/mistral.js +447 -0
- package/dist/nodes/mistral.js.map +1 -0
- package/dist/nodes/openai.d.ts +292 -0
- package/dist/nodes/openai.d.ts.map +1 -0
- package/dist/nodes/openai.js +1806 -0
- package/dist/nodes/openai.js.map +1 -0
- package/dist/nodes/provider-spend.d.ts +45 -0
- package/dist/nodes/provider-spend.d.ts.map +1 -0
- package/dist/nodes/provider-spend.js +69 -0
- package/dist/nodes/provider-spend.js.map +1 -0
- package/dist/nodes/shots.d.ts +108 -0
- package/dist/nodes/shots.d.ts.map +1 -0
- package/dist/nodes/shots.js +378 -0
- package/dist/nodes/shots.js.map +1 -0
- package/dist/nodes/xai.d.ts +99 -0
- package/dist/nodes/xai.d.ts.map +1 -0
- package/dist/nodes/xai.js +360 -0
- package/dist/nodes/xai.js.map +1 -0
- package/package.json +111 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Chunk } from "@nodetool-ai/protocol";
|
|
2
|
+
export declare function extractThinkTags(text: string): {
|
|
3
|
+
thinking: string;
|
|
4
|
+
text: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Splits streamed text so `<think>…` never appears on the text channel:
|
|
8
|
+
* reasoning goes to `thinking`, user-visible text to `text` chunks only.
|
|
9
|
+
*/
|
|
10
|
+
export declare class RedactedThinkingStreamSplitter {
|
|
11
|
+
private buf;
|
|
12
|
+
private inThink;
|
|
13
|
+
feed(incoming: string): Generator<{
|
|
14
|
+
kind: "text";
|
|
15
|
+
content: string;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "thinking";
|
|
18
|
+
content: string;
|
|
19
|
+
}>;
|
|
20
|
+
flush(): Generator<{
|
|
21
|
+
kind: "text";
|
|
22
|
+
content: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "thinking";
|
|
25
|
+
content: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
export declare function yieldSplitThinkChunks(item: Chunk, rawPiece: string, splitter: RedactedThinkingStreamSplitter): Generator<Record<string, unknown>>;
|
|
29
|
+
//# sourceMappingURL=agent-thinking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-thinking.d.ts","sourceRoot":"","sources":["../../src/nodes/agent-thinking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAwCnD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM;IAazC,QAAQ;IACR,IAAI;EAEP;AAED;;;GAGG;AACH,qBAAa,8BAA8B;IACzC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,OAAO,CAAS;IAEvB,IAAI,CACH,QAAQ,EAAE,MAAM,GACf,SAAS,CACV;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAC1E,CA+CA;IAEA,KAAK,IAAI,SAAS,CACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAC1E,CASA;CACF;AAED,wBAAiB,qBAAqB,CACpC,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,8BAA8B,GACvC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsBpC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opening tag plus the close tags models may emit. The canonical close is
|
|
3
|
+
* `</redacted_thinking>`; `</think>` is the legacy/typo variant. Both must be
|
|
4
|
+
* recognized so the streaming splitter matches what `extractThinkTags` does.
|
|
5
|
+
*/
|
|
6
|
+
const REDACTED_THINKING_OPEN = "<think>";
|
|
7
|
+
const REDACTED_THINKING_CLOSES = ["</redacted_thinking>", "</think>"];
|
|
8
|
+
function findEarliestThinkClose(buf) {
|
|
9
|
+
let best = null;
|
|
10
|
+
for (const c of REDACTED_THINKING_CLOSES) {
|
|
11
|
+
const i = buf.indexOf(c);
|
|
12
|
+
if (i !== -1 && (best === null || i < best.idx)) {
|
|
13
|
+
best = { idx: i, len: c.length };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return best;
|
|
17
|
+
}
|
|
18
|
+
/** Longest suffix of buf that is a proper prefix of one of the candidate strings (for streaming). */
|
|
19
|
+
function holdSuffixForPartialTag(buf, candidates) {
|
|
20
|
+
const maxCheck = Math.max(0, ...candidates.map((c) => Math.max(0, c.length - 1)));
|
|
21
|
+
const limit = Math.min(buf.length, maxCheck);
|
|
22
|
+
for (let len = limit; len >= 1; len--) {
|
|
23
|
+
const suf = buf.slice(-len);
|
|
24
|
+
if (candidates.some((c) => c.startsWith(suf)))
|
|
25
|
+
return len;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
export function extractThinkTags(text) {
|
|
30
|
+
const parts = [];
|
|
31
|
+
const re = /<think>([\s\S]*?)<\/(?:redacted_thinking|think)>/g;
|
|
32
|
+
let cleaned = text.replace(re, (_, content) => {
|
|
33
|
+
parts.push(content.trim());
|
|
34
|
+
return "";
|
|
35
|
+
});
|
|
36
|
+
const orphan = cleaned.match(/<think>([\s\S]*)$/);
|
|
37
|
+
if (orphan && orphan.index !== undefined) {
|
|
38
|
+
parts.push(orphan[1].trim());
|
|
39
|
+
cleaned = cleaned.slice(0, orphan.index);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
thinking: parts.filter((p) => p.length > 0).join("\n\n"),
|
|
43
|
+
text: cleaned.trim()
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Splits streamed text so `<think>…` never appears on the text channel:
|
|
48
|
+
* reasoning goes to `thinking`, user-visible text to `text` chunks only.
|
|
49
|
+
*/
|
|
50
|
+
export class RedactedThinkingStreamSplitter {
|
|
51
|
+
buf = "";
|
|
52
|
+
inThink = false;
|
|
53
|
+
*feed(incoming) {
|
|
54
|
+
if (!incoming)
|
|
55
|
+
return;
|
|
56
|
+
this.buf += incoming;
|
|
57
|
+
while (true) {
|
|
58
|
+
if (!this.inThink) {
|
|
59
|
+
const openIdx = this.buf.indexOf(REDACTED_THINKING_OPEN);
|
|
60
|
+
if (openIdx === -1) {
|
|
61
|
+
const keep = holdSuffixForPartialTag(this.buf, [
|
|
62
|
+
REDACTED_THINKING_OPEN
|
|
63
|
+
]);
|
|
64
|
+
if (this.buf.length > keep) {
|
|
65
|
+
const emitEnd = this.buf.length - keep;
|
|
66
|
+
const out = this.buf.slice(0, emitEnd);
|
|
67
|
+
this.buf = this.buf.slice(emitEnd);
|
|
68
|
+
if (out)
|
|
69
|
+
yield { kind: "text", content: out };
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (openIdx > 0) {
|
|
74
|
+
const out = this.buf.slice(0, openIdx);
|
|
75
|
+
if (out)
|
|
76
|
+
yield { kind: "text", content: out };
|
|
77
|
+
}
|
|
78
|
+
this.buf = this.buf.slice(openIdx + REDACTED_THINKING_OPEN.length);
|
|
79
|
+
this.inThink = true;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const close = findEarliestThinkClose(this.buf);
|
|
83
|
+
if (!close) {
|
|
84
|
+
const keep = holdSuffixForPartialTag(this.buf, REDACTED_THINKING_CLOSES);
|
|
85
|
+
if (this.buf.length > keep) {
|
|
86
|
+
const emitEnd = this.buf.length - keep;
|
|
87
|
+
const out = this.buf.slice(0, emitEnd);
|
|
88
|
+
this.buf = this.buf.slice(emitEnd);
|
|
89
|
+
if (out)
|
|
90
|
+
yield { kind: "thinking", content: out };
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const thinkBody = this.buf.slice(0, close.idx);
|
|
95
|
+
this.buf = this.buf.slice(close.idx + close.len);
|
|
96
|
+
this.inThink = false;
|
|
97
|
+
if (thinkBody)
|
|
98
|
+
yield { kind: "thinking", content: thinkBody };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
*flush() {
|
|
102
|
+
if (!this.buf && !this.inThink)
|
|
103
|
+
return;
|
|
104
|
+
if (this.inThink) {
|
|
105
|
+
if (this.buf)
|
|
106
|
+
yield { kind: "thinking", content: this.buf };
|
|
107
|
+
}
|
|
108
|
+
else if (this.buf) {
|
|
109
|
+
yield { kind: "text", content: this.buf };
|
|
110
|
+
}
|
|
111
|
+
this.buf = "";
|
|
112
|
+
this.inThink = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export function* yieldSplitThinkChunks(item, rawPiece, splitter) {
|
|
116
|
+
for (const part of splitter.feed(rawPiece)) {
|
|
117
|
+
if (part.kind === "text" && part.content.length > 0) {
|
|
118
|
+
yield {
|
|
119
|
+
chunk: { ...item, content: part.content },
|
|
120
|
+
thinking: null,
|
|
121
|
+
text: null,
|
|
122
|
+
audio: null
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
else if (part.kind === "thinking" && part.content.length > 0) {
|
|
126
|
+
yield {
|
|
127
|
+
chunk: null,
|
|
128
|
+
thinking: {
|
|
129
|
+
type: "chunk",
|
|
130
|
+
content: part.content,
|
|
131
|
+
thinking: true
|
|
132
|
+
},
|
|
133
|
+
text: null,
|
|
134
|
+
audio: null
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=agent-thinking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-thinking.js","sourceRoot":"","sources":["../../src/nodes/agent-thinking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Message, ProcessingContext } from "@nodetool-ai/runtime";
|
|
2
|
+
type ThreadLike = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
messages: Message[];
|
|
6
|
+
};
|
|
7
|
+
export declare const THREAD_STORE: Map<string, ThreadLike>;
|
|
8
|
+
/**
|
|
9
|
+
* Get-or-create a fallback thread and mark it most-recently-used (Map keeps
|
|
10
|
+
* insertion order, so delete+set moves it to the end). Evicts the oldest
|
|
11
|
+
* threads once the cap is exceeded.
|
|
12
|
+
*/
|
|
13
|
+
export declare function seedFallbackThread(threadId: string, title: string): ThreadLike;
|
|
14
|
+
export declare function loadThreadMessages(context: ProcessingContext | undefined, threadId: string): Promise<Message[]>;
|
|
15
|
+
export declare function saveThreadMessage(context: ProcessingContext | undefined, threadId: string, message: Message): Promise<void>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=agent-threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-threads.d.ts","sourceRoot":"","sources":["../../src/nodes/agent-threads.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAKvE,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC;AAYrE,eAAO,MAAM,YAAY,yBAAgC,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAW9E;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,EAAE,CAAC,CA4BpB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CAmCf"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createLogger } from "@nodetool-ai/config";
|
|
2
|
+
import { messageContentText, normalizeMessage } from "./agent-utils.js";
|
|
3
|
+
const log = createLogger("nodetool.base-nodes.agents");
|
|
4
|
+
// In-memory thread store used ONLY as a fallback for runs where the context
|
|
5
|
+
// has no message-store model interface wired — hermetic CLI runs
|
|
6
|
+
// (`node run --no-secrets`) and unit tests. When the store IS wired, agent
|
|
7
|
+
// nodes read/write the DB-backed context directly and DB errors propagate; the
|
|
8
|
+
// map below is never touched. It is bounded so a long-running provider-less
|
|
9
|
+
// process can't leak: at most MAX_THREADS threads (least-recently-used evicted)
|
|
10
|
+
// and MAX_MESSAGES_PER_THREAD messages per thread (oldest dropped).
|
|
11
|
+
const MAX_THREADS = 100;
|
|
12
|
+
const MAX_MESSAGES_PER_THREAD = 1000;
|
|
13
|
+
export const THREAD_STORE = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Get-or-create a fallback thread and mark it most-recently-used (Map keeps
|
|
16
|
+
* insertion order, so delete+set moves it to the end). Evicts the oldest
|
|
17
|
+
* threads once the cap is exceeded.
|
|
18
|
+
*/
|
|
19
|
+
export function seedFallbackThread(threadId, title) {
|
|
20
|
+
const existing = THREAD_STORE.get(threadId);
|
|
21
|
+
const thread = existing ?? { id: threadId, title, messages: [] };
|
|
22
|
+
if (existing)
|
|
23
|
+
THREAD_STORE.delete(threadId);
|
|
24
|
+
THREAD_STORE.set(threadId, thread);
|
|
25
|
+
while (THREAD_STORE.size > MAX_THREADS) {
|
|
26
|
+
const oldest = THREAD_STORE.keys().next().value;
|
|
27
|
+
if (oldest === undefined || oldest === threadId)
|
|
28
|
+
break;
|
|
29
|
+
THREAD_STORE.delete(oldest);
|
|
30
|
+
}
|
|
31
|
+
return thread;
|
|
32
|
+
}
|
|
33
|
+
export async function loadThreadMessages(context, threadId) {
|
|
34
|
+
if (!threadId)
|
|
35
|
+
return [];
|
|
36
|
+
// DB-backed path: the getMessages interface is wired, so read through the
|
|
37
|
+
// typed context method and let any error propagate (no silent fallback).
|
|
38
|
+
if (context?.hasModelInterface?.("getMessages")) {
|
|
39
|
+
const result = await context.getThreadMessages(threadId, 1000, null, false);
|
|
40
|
+
const messages = (result.messages ?? [])
|
|
41
|
+
.map((item) => normalizeMessage(item))
|
|
42
|
+
.filter((message) => message !== null && message.role !== "system");
|
|
43
|
+
log.info("Agent thread history loaded from context", {
|
|
44
|
+
threadId,
|
|
45
|
+
messageCount: messages.length
|
|
46
|
+
});
|
|
47
|
+
return messages;
|
|
48
|
+
}
|
|
49
|
+
const fallbackMessages = (THREAD_STORE.get(threadId)?.messages ?? [])
|
|
50
|
+
.filter((message) => message.role !== "system")
|
|
51
|
+
.map((message) => ({ ...message }));
|
|
52
|
+
log.info("Agent thread history loaded from fallback store", {
|
|
53
|
+
threadId,
|
|
54
|
+
messageCount: fallbackMessages.length
|
|
55
|
+
});
|
|
56
|
+
return fallbackMessages;
|
|
57
|
+
}
|
|
58
|
+
export async function saveThreadMessage(context, threadId, message) {
|
|
59
|
+
if (!threadId)
|
|
60
|
+
return;
|
|
61
|
+
// DB-backed path: the createMessage interface is wired, so write through the
|
|
62
|
+
// typed context method and let any error propagate (no silent fallback that
|
|
63
|
+
// would disguise data loss as success).
|
|
64
|
+
if (context?.hasModelInterface?.("createMessage")) {
|
|
65
|
+
await context.createMessage({
|
|
66
|
+
thread_id: threadId,
|
|
67
|
+
role: message.role,
|
|
68
|
+
content: message.content ?? null,
|
|
69
|
+
tool_calls: message.toolCalls ?? null,
|
|
70
|
+
tool_call_id: message.toolCallId ?? null
|
|
71
|
+
});
|
|
72
|
+
log.info("Agent thread message saved via context", {
|
|
73
|
+
threadId,
|
|
74
|
+
role: message.role,
|
|
75
|
+
hasToolCalls: (message.toolCalls?.length ?? 0) > 0,
|
|
76
|
+
textLength: messageContentText(message.content).length
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const thread = seedFallbackThread(threadId, "Agent Conversation");
|
|
81
|
+
thread.messages.push({ ...message, threadId });
|
|
82
|
+
while (thread.messages.length > MAX_MESSAGES_PER_THREAD) {
|
|
83
|
+
thread.messages.shift();
|
|
84
|
+
}
|
|
85
|
+
log.info("Agent thread message saved via fallback store", {
|
|
86
|
+
threadId,
|
|
87
|
+
role: message.role,
|
|
88
|
+
threadSize: thread.messages.length,
|
|
89
|
+
hasToolCalls: (message.toolCalls?.length ?? 0) > 0,
|
|
90
|
+
textLength: messageContentText(message.content).length
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=agent-threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-threads.js","sourceRoot":"","sources":["../../src/nodes/agent-threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builtin agent-tool registry + hydration.
|
|
3
|
+
*
|
|
4
|
+
* Tools can be referenced by name as bare stubs (`{ name }`) and hydrated into
|
|
5
|
+
* real `Tool` instances on demand. The registry is the default agent toolbelt
|
|
6
|
+
* (`getBuiltinTools()` — no hand-maintained name list) plus anything other
|
|
7
|
+
* modules append via {@link registerBuiltinAgentToolClasses} at load time
|
|
8
|
+
* (e.g. `sandbox.ts` registers the `browser_*` CDP tools).
|
|
9
|
+
*
|
|
10
|
+
* CONTRACT: a name-stub is NOT executable until hydrated. `runAgentLoop` and
|
|
11
|
+
* the AgentNode (`normalizeTools`) both hydrate their `tools` before use, so a
|
|
12
|
+
* stub or a real Tool reaches the loop equivalently. Any other caller that
|
|
13
|
+
* builds tools by name and runs them itself MUST call
|
|
14
|
+
* {@link resolveBuiltinAgentTool} / {@link hydrateBuiltinAgentTool} first — an
|
|
15
|
+
* unhydrated stub has no `process`/`inputSchema` and silently can't be called.
|
|
16
|
+
*/
|
|
17
|
+
import { Tool } from "@nodetool-ai/agents";
|
|
18
|
+
type ToolCtor = new () => Tool;
|
|
19
|
+
type MaybeTool = {
|
|
20
|
+
name: string;
|
|
21
|
+
process?: unknown;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Append additional Tool classes to the built-in registry. Called from
|
|
25
|
+
* other modules (e.g. `sandbox.ts`) at module-load time so they don't have
|
|
26
|
+
* to be imported here, which would re-introduce a circular dependency.
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerBuiltinAgentToolClasses(classes: ToolCtor[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Register a factory that produces Tool classes on demand. Unlike
|
|
31
|
+
* `registerBuiltinAgentToolClasses`, the factory is called lazily on the
|
|
32
|
+
* first `resolveBuiltinAgentTool` call, after all modules are initialized.
|
|
33
|
+
* Use this when the tool classes depend on module-level constants that may
|
|
34
|
+
* not be initialized at the time the registering module is evaluated (e.g.
|
|
35
|
+
* `BROWSER_ACTION_SPECS` in esbuild bundles where `__esm` lazy init can
|
|
36
|
+
* cause the constant to be `undefined` at module load time).
|
|
37
|
+
*/
|
|
38
|
+
export declare function registerBuiltinAgentToolFactory(factory: () => ToolCtor[]): void;
|
|
39
|
+
/** Resolve a registered builtin tool by name to a runnable `Tool`, or null. */
|
|
40
|
+
export declare function resolveBuiltinAgentTool(name: string): Tool | null;
|
|
41
|
+
/**
|
|
42
|
+
* Hydrate one tool: a real tool (has `process`) passes through unchanged; a
|
|
43
|
+
* bare name-stub is resolved from the registry (or returned as-is if unknown,
|
|
44
|
+
* so the caller can detect the still-unrunnable stub).
|
|
45
|
+
*/
|
|
46
|
+
export declare function hydrateBuiltinAgentTool<T extends MaybeTool>(tool: T): T | Tool;
|
|
47
|
+
/** Hydrate a list of tools — see {@link hydrateBuiltinAgentTool}. */
|
|
48
|
+
export declare function hydrateBuiltinAgentTools<T extends MaybeTool>(tools: T[]): Array<T | Tool>;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=agent-tool-hydration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tool-hydration.d.ts","sourceRoot":"","sources":["../../src/nodes/agent-tool-hydration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAkC,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAG3E,KAAK,QAAQ,GAAG,UAAU,IAAI,CAAC;AAE/B,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAmCF;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,CAGzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,QAAQ,EAAE,GAAG,IAAI,CAG/E;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAgBjE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,CAG9E;AAED,qEAAqE;AACrE,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAEzF"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builtin agent-tool registry + hydration.
|
|
3
|
+
*
|
|
4
|
+
* Tools can be referenced by name as bare stubs (`{ name }`) and hydrated into
|
|
5
|
+
* real `Tool` instances on demand. The registry is the default agent toolbelt
|
|
6
|
+
* (`getBuiltinTools()` — no hand-maintained name list) plus anything other
|
|
7
|
+
* modules append via {@link registerBuiltinAgentToolClasses} at load time
|
|
8
|
+
* (e.g. `sandbox.ts` registers the `browser_*` CDP tools).
|
|
9
|
+
*
|
|
10
|
+
* CONTRACT: a name-stub is NOT executable until hydrated. `runAgentLoop` and
|
|
11
|
+
* the AgentNode (`normalizeTools`) both hydrate their `tools` before use, so a
|
|
12
|
+
* stub or a real Tool reaches the loop equivalently. Any other caller that
|
|
13
|
+
* builds tools by name and runs them itself MUST call
|
|
14
|
+
* {@link resolveBuiltinAgentTool} / {@link hydrateBuiltinAgentTool} first — an
|
|
15
|
+
* unhydrated stub has no `process`/`inputSchema` and silently can't be called.
|
|
16
|
+
*/
|
|
17
|
+
import { getBuiltinTools, getMediaTools } from "@nodetool-ai/agents";
|
|
18
|
+
import { isCallable } from "@nodetool-ai/node-sdk";
|
|
19
|
+
/**
|
|
20
|
+
* Names a saved workflow may still carry, mapped to what replaced them.
|
|
21
|
+
*
|
|
22
|
+
* The provider-specific search and media tools were retired: one `web_search`
|
|
23
|
+
* chooses a backend host-side and takes a `search_type` for news, image
|
|
24
|
+
* search is the dedicated `image_search`, and `generate_image` /
|
|
25
|
+
* `generate_speech` are provider-agnostic. An AgentNode saved before that
|
|
26
|
+
* stores the old name as a bare stub, and a stub that resolves to nothing is
|
|
27
|
+
* silently uncallable — so resolve it to the tool that took over instead.
|
|
28
|
+
*/
|
|
29
|
+
const RETIRED_TOOL_NAMES = {
|
|
30
|
+
openai_web_search: "web_search",
|
|
31
|
+
google_grounded_search: "web_search",
|
|
32
|
+
dataforseo_search: "web_search",
|
|
33
|
+
// The news search folded into `web_search`'s `search_type`; image search
|
|
34
|
+
// split out into its own `image_search` tool. A hydrated stub carries the
|
|
35
|
+
// name only, so it lands on a real tool — one that exists and answers,
|
|
36
|
+
// rather than resolving to nothing and being silently uncallable, which is
|
|
37
|
+
// what this map is for.
|
|
38
|
+
dataforseo_news: "web_search",
|
|
39
|
+
google_news: "web_search",
|
|
40
|
+
dataforseo_images: "image_search",
|
|
41
|
+
google_images: "image_search",
|
|
42
|
+
image_generation: "generate_image",
|
|
43
|
+
openai_image_generation: "generate_image",
|
|
44
|
+
google_image_generation: "generate_image",
|
|
45
|
+
openai_text_to_speech: "generate_speech"
|
|
46
|
+
};
|
|
47
|
+
const extraToolClasses = [];
|
|
48
|
+
const toolFactories = [];
|
|
49
|
+
let builtinAgentTools = null;
|
|
50
|
+
/**
|
|
51
|
+
* Append additional Tool classes to the built-in registry. Called from
|
|
52
|
+
* other modules (e.g. `sandbox.ts`) at module-load time so they don't have
|
|
53
|
+
* to be imported here, which would re-introduce a circular dependency.
|
|
54
|
+
*/
|
|
55
|
+
export function registerBuiltinAgentToolClasses(classes) {
|
|
56
|
+
for (const c of classes)
|
|
57
|
+
extraToolClasses.push(c);
|
|
58
|
+
builtinAgentTools = null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Register a factory that produces Tool classes on demand. Unlike
|
|
62
|
+
* `registerBuiltinAgentToolClasses`, the factory is called lazily on the
|
|
63
|
+
* first `resolveBuiltinAgentTool` call, after all modules are initialized.
|
|
64
|
+
* Use this when the tool classes depend on module-level constants that may
|
|
65
|
+
* not be initialized at the time the registering module is evaluated (e.g.
|
|
66
|
+
* `BROWSER_ACTION_SPECS` in esbuild bundles where `__esm` lazy init can
|
|
67
|
+
* cause the constant to be `undefined` at module load time).
|
|
68
|
+
*/
|
|
69
|
+
export function registerBuiltinAgentToolFactory(factory) {
|
|
70
|
+
toolFactories.push(factory);
|
|
71
|
+
builtinAgentTools = null;
|
|
72
|
+
}
|
|
73
|
+
/** Resolve a registered builtin tool by name to a runnable `Tool`, or null. */
|
|
74
|
+
export function resolveBuiltinAgentTool(name) {
|
|
75
|
+
if (!builtinAgentTools) {
|
|
76
|
+
builtinAgentTools = new Map();
|
|
77
|
+
for (const tool of [...getBuiltinTools(), ...getMediaTools()]) {
|
|
78
|
+
builtinAgentTools.set(tool.name, tool);
|
|
79
|
+
}
|
|
80
|
+
const dynamicClasses = toolFactories.flatMap((f) => f());
|
|
81
|
+
for (const ToolClass of [...extraToolClasses, ...dynamicClasses]) {
|
|
82
|
+
const tool = new ToolClass();
|
|
83
|
+
builtinAgentTools.set(tool.name, tool);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const direct = builtinAgentTools.get(name);
|
|
87
|
+
if (direct)
|
|
88
|
+
return direct;
|
|
89
|
+
const replacement = RETIRED_TOOL_NAMES[name];
|
|
90
|
+
return replacement ? (builtinAgentTools.get(replacement) ?? null) : null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Hydrate one tool: a real tool (has `process`) passes through unchanged; a
|
|
94
|
+
* bare name-stub is resolved from the registry (or returned as-is if unknown,
|
|
95
|
+
* so the caller can detect the still-unrunnable stub).
|
|
96
|
+
*/
|
|
97
|
+
export function hydrateBuiltinAgentTool(tool) {
|
|
98
|
+
if (isCallable(tool.process))
|
|
99
|
+
return tool;
|
|
100
|
+
return resolveBuiltinAgentTool(tool.name) ?? tool;
|
|
101
|
+
}
|
|
102
|
+
/** Hydrate a list of tools — see {@link hydrateBuiltinAgentTool}. */
|
|
103
|
+
export function hydrateBuiltinAgentTools(tools) {
|
|
104
|
+
return tools.map(hydrateBuiltinAgentTool);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=agent-tool-hydration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tool-hydration.js","sourceRoot":"","sources":["../../src/nodes/agent-tool-hydration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAQ,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AASnD;;;;;;;;;GASG;AACH,MAAM,kBAAkB,GAAqC;IAC3D,iBAAiB,EAAE,YAAY;IAC/B,sBAAsB,EAAE,YAAY;IACpC,iBAAiB,EAAE,YAAY;IAC/B,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,2EAA2E;IAC3E,wBAAwB;IACxB,eAAe,EAAE,YAAY;IAC7B,WAAW,EAAE,YAAY;IACzB,iBAAiB,EAAE,cAAc;IACjC,aAAa,EAAE,cAAc;IAC7B,gBAAgB,EAAE,gBAAgB;IAClC,uBAAuB,EAAE,gBAAgB;IACzC,uBAAuB,EAAE,gBAAgB;IACzC,qBAAqB,EAAE,iBAAiB;CACzC,CAAC;AAEF,MAAM,gBAAgB,GAAe,EAAE,CAAC;AACxC,MAAM,aAAa,GAAyB,EAAE,CAAC;AAC/C,IAAI,iBAAiB,GAA6B,IAAI,CAAC;AAEvD;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAAC,OAAmB;IACjE,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClD,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,+BAA+B,CAAC,OAAyB;IACvE,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,iBAAiB,GAAG,IAAI,GAAG,EAAgB,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,eAAe,EAAE,EAAE,GAAG,aAAa,EAAE,CAAC,EAAE,CAAC;YAC9D,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC;QACD,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACzD,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,gBAAgB,EAAE,GAAG,cAAc,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;YAC7B,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAsB,IAAO;IAClE,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACpD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,wBAAwB,CAAsB,KAAU;IACtE,OAAO,KAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { BaseNode } from "@nodetool-ai/node-sdk";
|
|
2
|
+
import { type PermissionGateOptions } from "@nodetool-ai/agents";
|
|
3
|
+
import type { BaseProvider, Message, MessageContent, ProcessingContext, ProviderStop, ProviderStreamItem, ToolCall } from "@nodetool-ai/runtime";
|
|
4
|
+
import { extractJson, generateStructured } from "@nodetool-ai/runtime";
|
|
5
|
+
import type { Chunk } from "@nodetool-ai/protocol";
|
|
6
|
+
export { extractJson, generateStructured };
|
|
7
|
+
/**
|
|
8
|
+
* What a tool hands back: a primitive, or any object — a record, an array, a
|
|
9
|
+
* byte buffer. `serializeToolResult` renders it to JSON before it reaches the
|
|
10
|
+
* model.
|
|
11
|
+
*/
|
|
12
|
+
export type ToolResult = string | number | boolean | null | undefined | object;
|
|
13
|
+
export type ToolLike = {
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
inputSchema?: Record<string, unknown>;
|
|
17
|
+
process?: (context: ProcessingContext, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Preferred entry point when present (agent-system tools have one): strips
|
|
20
|
+
* reserved fields like `_message` and coerces params against the tool's
|
|
21
|
+
* schema before dispatching to `process`.
|
|
22
|
+
*/
|
|
23
|
+
execute?: (context: ProcessingContext, params: Record<string, unknown>, options?: {
|
|
24
|
+
toolCallId?: string;
|
|
25
|
+
}) => Promise<ToolResult>;
|
|
26
|
+
toProviderTool?: () => {
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
inputSchema?: Record<string, unknown>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare function asText(value: unknown): string;
|
|
33
|
+
export declare function makeThreadId(): string;
|
|
34
|
+
export declare function getCategories(value: unknown): string[];
|
|
35
|
+
export declare function getModelConfig(props: Record<string, unknown>): {
|
|
36
|
+
providerId: string;
|
|
37
|
+
modelId: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Whether this context can hand out a provider. False for the bare context a
|
|
41
|
+
* hermetic single-node run supplies, which carries no provider access.
|
|
42
|
+
*/
|
|
43
|
+
export declare function hasProviderAccess(context: ProcessingContext | undefined): context is ProcessingContext & {
|
|
44
|
+
getProvider(providerId: string): Promise<BaseProvider>;
|
|
45
|
+
};
|
|
46
|
+
export declare function hasProviderSupport(context: ProcessingContext | undefined, providerId: string, modelId: string): context is ProcessingContext & {
|
|
47
|
+
getProvider(providerId: string): Promise<BaseProvider>;
|
|
48
|
+
};
|
|
49
|
+
export declare function normalizeProviderStreamItem(item: ProviderStreamItem): ProviderStreamItem;
|
|
50
|
+
/**
|
|
51
|
+
* Render a tool-call streaming item as a Chunk so callers see tool dispatches
|
|
52
|
+
* inline with the assistant's text/thinking stream. The structured payload
|
|
53
|
+
* lives in `content_metadata`; `content` is a human-readable summary.
|
|
54
|
+
*/
|
|
55
|
+
export declare function toolCallChunk(toolCall: ToolCall): Chunk;
|
|
56
|
+
export declare function streamProviderMessages(provider: BaseProvider, args: Parameters<BaseProvider["generateMessages"]>[0]): AsyncGenerator<ProviderStreamItem>;
|
|
57
|
+
/**
|
|
58
|
+
* Map a model's raw classification output to one of the allowed categories.
|
|
59
|
+
* Tries the parsed JSON `category` field (case-insensitive exact match) first,
|
|
60
|
+
* then a substring-containment scan of the raw text. Throws if neither matches
|
|
61
|
+
* — never invents an answer by defaulting to the first category.
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseCategory(raw: string, categories: string[]): string;
|
|
64
|
+
export declare function messageContentText(content: Message["content"] | unknown): string;
|
|
65
|
+
export declare function normalizeRole(role: unknown): Message["role"] | null;
|
|
66
|
+
export declare function normalizeBinaryRef(value: unknown): {
|
|
67
|
+
uri?: string;
|
|
68
|
+
data?: Uint8Array | string;
|
|
69
|
+
mimeType?: string;
|
|
70
|
+
} | null;
|
|
71
|
+
export declare function normalizeMessageContent(value: unknown): Message["content"];
|
|
72
|
+
export declare function normalizeToolCalls(value: unknown): ToolCall[] | null;
|
|
73
|
+
export declare function normalizeMessage(value: unknown): Message | null;
|
|
74
|
+
/**
|
|
75
|
+
* Normalize a list-typed media input to an array. Accepts an array (the
|
|
76
|
+
* declared `list[image]`/`list[audio]` shape), a lone ref (defensive, in case
|
|
77
|
+
* coercion didn't run), or null/undefined.
|
|
78
|
+
*/
|
|
79
|
+
export declare function toRefArray(value: unknown): unknown[];
|
|
80
|
+
export declare function buildUserMessage(prompt: string, images: unknown, audios: unknown): Message;
|
|
81
|
+
/**
|
|
82
|
+
* A user message that keeps `content` a plain string unless a media ref is
|
|
83
|
+
* actually attached. The Summarizer/Extractor/Classifier nodes declare `image`
|
|
84
|
+
* and `audio` props; wiring them through {@link buildUserMessage} unconditionally
|
|
85
|
+
* would also turn every text-only request into a parts array and run the prompt
|
|
86
|
+
* through `expandAssetReferences`, changing what existing graphs send. This
|
|
87
|
+
* keeps the old string shape whenever no ref resolves.
|
|
88
|
+
*/
|
|
89
|
+
export declare function userMessageWithMedia(text: string, images: unknown, audios: unknown): Message;
|
|
90
|
+
/**
|
|
91
|
+
* A single classified item from a provider's {@link BaseProvider.generateLoop}
|
|
92
|
+
* stream. Both {@link runAgentLoop} and the AgentNode's genProcess drive the
|
|
93
|
+
* same normalize → classify dance off generateLoop; keeping that classification
|
|
94
|
+
* in one place stops the two from drifting (they did once). Presentation and
|
|
95
|
+
* per-turn accumulation stay with each consumer — this only untangles the raw
|
|
96
|
+
* stream into typed events.
|
|
97
|
+
*/
|
|
98
|
+
export type ProviderStreamEvent = {
|
|
99
|
+
kind: "tool_call";
|
|
100
|
+
toolCall: ToolCall;
|
|
101
|
+
} | {
|
|
102
|
+
kind: "thinking";
|
|
103
|
+
chunk: Chunk;
|
|
104
|
+
} | {
|
|
105
|
+
kind: "audio";
|
|
106
|
+
chunk: Chunk;
|
|
107
|
+
} | {
|
|
108
|
+
kind: "text";
|
|
109
|
+
chunk: Chunk;
|
|
110
|
+
delta: string;
|
|
111
|
+
} | {
|
|
112
|
+
kind: "assistant_message";
|
|
113
|
+
message: Message;
|
|
114
|
+
} | {
|
|
115
|
+
kind: "tool_message";
|
|
116
|
+
message: Message;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The loop ended for a reason other than the model ending its turn — a
|
|
120
|
+
* budget, the deadline, the iteration cap, or an abort. Dropping it would
|
|
121
|
+
* make a refused run read exactly like a finished one (invariant I-3), so
|
|
122
|
+
* every consumer sees it and decides what to do.
|
|
123
|
+
*/
|
|
124
|
+
| {
|
|
125
|
+
kind: "stop";
|
|
126
|
+
stop: ProviderStop;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Normalize and classify a provider stream into {@link ProviderStreamEvent}s.
|
|
130
|
+
* Mirrors the branch structure both consumers previously inlined: tool-call
|
|
131
|
+
* announcements, thinking chunks, audio chunks, any other content chunk (as
|
|
132
|
+
* `text`, carrying the original chunk so a consumer can inspect content_type),
|
|
133
|
+
* finalized `{ type: "message" }` events split by role, and the loop's `stop`
|
|
134
|
+
* item. The `text` event's `delta` is the chunk's string content ("" for
|
|
135
|
+
* non-string payloads).
|
|
136
|
+
*/
|
|
137
|
+
export declare function classifyProviderStream(stream: AsyncIterable<ProviderStreamItem>): AsyncGenerator<ProviderStreamEvent>;
|
|
138
|
+
/**
|
|
139
|
+
* Resolve selected tool names to executable tools.
|
|
140
|
+
*
|
|
141
|
+
* A tool the caller injected into the context wins over builtin hydration:
|
|
142
|
+
* only the caller can wire MCP, workspace, skill, and security-gated tools,
|
|
143
|
+
* and a builtin stub of the same name would be the weaker of the two.
|
|
144
|
+
*/
|
|
145
|
+
export declare function normalizeTools(value: unknown, context?: ProcessingContext): ToolLike[];
|
|
146
|
+
/**
|
|
147
|
+
* Wrap an agent node's tools in the run's permission gate.
|
|
148
|
+
*
|
|
149
|
+
* One ladder covers every host (invariant I-1): the node's tools go through
|
|
150
|
+
* the same `decidePermission` a chat turn's belt does, with the gate the host
|
|
151
|
+
* put on the context. Approving `run_node` is then approval to run the node,
|
|
152
|
+
* not to run whatever the node decides to call — its tools used to resolve
|
|
153
|
+
* ungated, so every call inside it ran unasked.
|
|
154
|
+
*
|
|
155
|
+
* A name-stub that never hydrated is left alone: it has nothing to run, and
|
|
156
|
+
* the node's dispatch already answers for it by name.
|
|
157
|
+
*/
|
|
158
|
+
export declare function gateAgentTools(tools: ToolLike[], gate: PermissionGateOptions): ToolLike[];
|
|
159
|
+
export declare function uniqueToolName(baseName: string, existingNames: string[]): string;
|
|
160
|
+
export declare function toProviderTools(tools: ToolLike[]): Array<{
|
|
161
|
+
name: string;
|
|
162
|
+
description?: string;
|
|
163
|
+
inputSchema?: Record<string, unknown>;
|
|
164
|
+
}>;
|
|
165
|
+
/**
|
|
166
|
+
* A tool result rendered as JSON: every byte buffer has become a base64 string.
|
|
167
|
+
*/
|
|
168
|
+
export type SerializedToolResult = string | number | boolean | null | undefined | SerializedToolResult[] | {
|
|
169
|
+
[key: string]: SerializedToolResult;
|
|
170
|
+
};
|
|
171
|
+
export declare function serializeToolResult(value: unknown): SerializedToolResult;
|
|
172
|
+
export declare function getStructuredOutputSchema(node: BaseNode): Record<string, unknown> | null;
|
|
173
|
+
export declare function hasContentType(message: Message | undefined, type: MessageContent["type"]): boolean;
|
|
174
|
+
//# sourceMappingURL=agent-utils.d.ts.map
|