@j-o-r/hello-dave 0.1.0 → 0.1.4
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/CHANGELOG.md +42 -25
- package/README.md +81 -221
- package/TODO.md +173 -35
- package/agents/agent_creator.js +105 -0
- package/agents/agent_creator.prompt.md +371 -0
- package/agents/ask_agent.js +64 -127
- package/agents/claude_agent.js +68 -0
- package/agents/code_agent.js +55 -135
- package/agents/code_agent.prompt.md +50 -0
- package/agents/echo_agent.js +76 -0
- package/agents/financial_expert.js +75 -0
- package/agents/gpt_agent.js +52 -103
- package/agents/gpt_code.js +81 -0
- package/agents/grok_agent.js +58 -114
- package/agents/minimax_agent.js +92 -0
- package/agents/mureka_agent.js +77 -0
- package/agents/planner_agent.js +172 -0
- package/agents/stability_agent.js +87 -0
- package/agents/test_agent.js +75 -157
- package/agents/weather_agent.js +73 -0
- package/agents/workflow_agent.js +189 -0
- package/bin/dave.js +436 -184
- package/docs/bin-dave.md +85 -35
- package/docs/cdn-ssh.md +100 -0
- package/docs/creating-agents.md +301 -0
- package/docs/creating-toolsets.md +336 -0
- package/docs/docs-organization.md +48 -0
- package/docs/project-overview.md +86 -51
- package/lib/API/elevenlabs.io/music.compose.md +441 -0
- package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
- package/lib/API/elevenlabs.io/music.stream.md +425 -0
- package/lib/API/lalal.ai/lalal.js +445 -0
- package/lib/API/lalal.ai/openapi.json +2614 -0
- package/lib/API/minimax/ImageToolset.js +82 -37
- package/lib/API/minimax/MusicToolset.js +125 -79
- package/lib/API/minimax/VideoToolset.js +170 -167
- package/lib/API/minimax/image.js +5 -1
- package/lib/API/minimax/music.js +210 -23
- package/lib/API/minimax/video.js +242 -53
- package/lib/API/mureka/MusicToolset.js +646 -0
- package/lib/API/mureka/README.md +41 -0
- package/lib/API/mureka/index.js +7 -0
- package/lib/API/mureka/music.js +658 -0
- package/lib/API/openai.com/index.js +7 -0
- package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
- package/lib/API/openai.com/video.create.character.md +40 -0
- package/lib/API/openai.com/video.create.md +219 -0
- package/lib/API/openai.com/video.delete.md +44 -0
- package/lib/API/openai.com/video.download.md +31 -0
- package/lib/API/openai.com/video.edit.md +155 -0
- package/lib/API/openai.com/video.extend.md +166 -0
- package/lib/API/openai.com/video.fetch.character.md +43 -0
- package/lib/API/openai.com/video.js +784 -0
- package/lib/API/openai.com/video.list.md +201 -0
- package/lib/API/openai.com/video.remix.md +175 -0
- package/lib/API/openai.com/video.retrieve.md +139 -0
- package/lib/API/openai.com/videoToolset.js +616 -0
- package/lib/API/stability.ai/ImageToolset.js +131 -40
- package/lib/API/stability.ai/MusicToolset.js +79 -47
- package/lib/API/stability.ai/audio.js +63 -131
- package/lib/API/x.ai/chat.responses.md +1040 -0
- package/lib/API/x.ai/image.js +229 -59
- package/lib/API/x.ai/imageToolset.js +376 -0
- package/lib/API/x.ai/index.js +1 -3
- package/lib/API/x.ai/responses.js +9 -18
- package/lib/Agent.js +271 -0
- package/lib/Agent.js.old +284 -0
- package/lib/AgentLauncher.js +562 -0
- package/lib/Cli.js +87 -13
- package/lib/Prompt.js +23 -1
- package/lib/Session.js +5 -4
- package/lib/ToolSet.js +102 -6
- package/lib/agentLoader.js +369 -0
- package/lib/cdn.js +67 -231
- package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
- package/lib/defaultToolsets.js +43 -0
- package/lib/fafs.js +1 -1
- package/lib/genericToolset.js +442 -119
- package/lib/handOffToolset.js +179 -0
- package/lib/index.js +34 -27
- package/lib/toolsetLoader.js +248 -0
- package/package.json +11 -5
- package/types/API/lalal.ai/lalal.d.ts +116 -0
- package/types/API/minimax/image.d.ts +2 -1
- package/types/API/minimax/music.d.ts +189 -26
- package/types/API/minimax/video.d.ts +100 -31
- package/types/API/mureka/index.d.ts +7 -0
- package/types/API/mureka/music.d.ts +472 -0
- package/types/API/openai.com/index.d.ts +7 -0
- package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
- package/types/API/openai.com/video.d.ts +409 -0
- package/types/API/openai.com/videoToolset.d.ts +24 -0
- package/types/API/stability.ai/audio.d.ts +14 -103
- package/types/API/stability.ai/image.d.ts +2 -2
- package/types/API/x.ai/image.d.ts +138 -26
- package/types/API/x.ai/imageToolset.d.ts +3 -0
- package/types/API/x.ai/index.d.ts +1 -3
- package/types/API/x.ai/responses.d.ts +4 -4
- package/types/Agent.d.ts +123 -0
- package/types/AgentLauncher.d.ts +222 -0
- package/types/Cli.d.ts +28 -8
- package/types/Prompt.d.ts +23 -5
- package/types/Session.d.ts +1 -1
- package/types/ToolSet.d.ts +10 -0
- package/types/agentLoader.d.ts +78 -0
- package/types/cdn.d.ts +15 -90
- package/types/defaultToolsets.d.ts +9 -0
- package/types/fafs.d.ts +1 -1
- package/types/genericToolset.d.ts +1 -1
- package/types/handOffToolset.d.ts +28 -0
- package/types/index.d.ts +19 -16
- package/types/toolsetLoader.d.ts +114 -0
- package/utils/format_log.js +101 -23
- package/utils/launch_agent.js +18 -0
- package/utils/list_sessions.sh +13 -5
- package/utils/search_sessions.sh +65 -29
- package/utils/toolsets.js +33 -0
- package/README.md.bak.1779452127 +0 -240
- package/agents/codeserver.sh +0 -47
- package/agents/daisy_agent.js +0 -173
- package/agents/docs_agent.js +0 -148
- package/agents/memory_agent.js +0 -263
- package/agents/minimax.js +0 -173
- package/agents/npm_agent.js +0 -202
- package/agents/prompt_agent.js +0 -133
- package/agents/readme_agent.js +0 -148
- package/agents/spawn_agent.js +0 -160
- package/agents/stability.js +0 -173
- package/agents/todo_agent.js +0 -175
- package/bin/codeDave +0 -58
- package/docs/agent-dave-websocket-protocol.md +0 -180
- package/docs/agent-manager.md +0 -244
- package/docs/codeserver-pattern.md +0 -191
- package/docs/generic-toolset.md +0 -326
- package/docs/howtos/agent-networking.md +0 -253
- package/docs/howtos/spawn-agents.md.bak +0 -200
- package/docs/howtos/spawn-agents.md.bak_new +0 -200
- package/docs/multi-agent-clusters.md +0 -265
- package/docs/music-toolsets.md +0 -137
- package/docs/path-resolution-best-practices.md +0 -104
- package/docs/plans/minimax-music-generation.md +0 -80
- package/docs/plans/unified-agent-architecture.md +0 -146
- package/docs/plans/websocket-streaming-plan.md.bak +0 -317
- package/docs/prompt/spawn_agent.md +0 -175
- package/docs/prompt/spawn_agent.md.bak +0 -201
- package/docs/prompt/task_clarification_and_documentation.md +0 -35
- package/docs/prompt-class.md +0 -141
- package/docs/todo-archive-infra-2026-04-21.md +0 -15
- package/docs/todo-archive-v0.0.8.md +0 -1
- package/docs/todo-archive-v0.1.0.md +0 -32
- package/docs/todo-archive.md +0 -44
- package/docs/tools-syntax-validation.md +0 -121
- package/docs/toolset.md +0 -164
- package/docs/xai-responses.md +0 -111
- package/docs/xai_collections.md +0 -106
- package/lib/API/x.ai/ImageToolset.js +0 -165
- package/lib/API/x.ai/text.js +0 -415
- package/lib/AgentClient.js +0 -248
- package/lib/AgentManager.js +0 -245
- package/lib/AgentServer.js +0 -404
- package/lib/wsCli.js +0 -287
- package/lib/wsIO.js +0 -90
- package/types/API/x.ai/text.d.ts +0 -286
- package/types/AgentClient.d.ts +0 -109
- package/types/AgentManager.d.ts +0 -100
- package/types/AgentServer.d.ts +0 -89
- package/types/wsCli.d.ts +0 -17
- package/types/wsIO.d.ts +0 -30
- package/utils/test.sh +0 -46
- /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
- /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
- /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
- /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
package/lib/Cli.js
CHANGED
|
@@ -71,6 +71,8 @@ e.g. >edit< , >paste< , >#!bash [command]<
|
|
|
71
71
|
* - Event-driven updates from Prompt (messages, truncated, finished).
|
|
72
72
|
* - Built-in help, reset, load session, copy last message (Alt+m).
|
|
73
73
|
* - Customizable intro/help via options.
|
|
74
|
+
* - Supports rebinding to a new Prompt/Session (for in-process agent handoff) without creating a new CLI instance.
|
|
75
|
+
* Use rebind() then start(initialContext) for clean handoff.
|
|
74
76
|
*
|
|
75
77
|
* @example
|
|
76
78
|
* const cli = new Cli({ prompt: myPrompt, session: mySession });
|
|
@@ -89,6 +91,7 @@ class Cli {
|
|
|
89
91
|
#prompt;
|
|
90
92
|
#description = '';
|
|
91
93
|
#lastMessage = '';
|
|
94
|
+
#globalInitialized = false;
|
|
92
95
|
|
|
93
96
|
/**
|
|
94
97
|
* Initializes the Cli instance with required Prompt and Session.
|
|
@@ -104,17 +107,27 @@ class Cli {
|
|
|
104
107
|
this.#session = options.session;
|
|
105
108
|
this.#description = options.description ? options.description + '\n\n' + INTRO : INTRO;
|
|
106
109
|
this.#HELP = options.help || HELP;
|
|
107
|
-
|
|
110
|
+
|
|
111
|
+
// One-time global setup (input handler, key mappings, base roles)
|
|
112
|
+
this.#initializeGlobal();
|
|
113
|
+
|
|
114
|
+
// Attach prompt-specific listeners (can be re-done on rebind)
|
|
115
|
+
this.#attachPromptListeners();
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
/** @private */
|
|
111
|
-
#
|
|
118
|
+
/** @private One-time global CLI setup (safe to call only once) */
|
|
119
|
+
#initializeGlobal() {
|
|
120
|
+
if (this.#globalInitialized) return;
|
|
121
|
+
|
|
112
122
|
cli.setRole('assistant', `${this.#session.name}: > `, 'brightGreen');
|
|
113
123
|
cli.setRole('util', ``, 'brightYellow');
|
|
114
124
|
cli.setRole('error', ``, 'red');
|
|
115
|
-
|
|
125
|
+
cli.setRole('log', '', 'brightBlue');
|
|
126
|
+
cli.setRole('reasoning', 'reasoning: > ', 'brightBlue');
|
|
127
|
+
|
|
116
128
|
// @ts-ignore
|
|
117
129
|
cli.inputHandler = async (s, role) => await this.#handleInput(s, role);
|
|
130
|
+
|
|
118
131
|
cli.registerKeyMappings([
|
|
119
132
|
{ // reset the session, empty the context from the prompt
|
|
120
133
|
name: 'r', ctrl: false, meta: true, shift: false, handler: async () => {
|
|
@@ -146,13 +159,22 @@ class Cli {
|
|
|
146
159
|
}
|
|
147
160
|
}
|
|
148
161
|
]);
|
|
162
|
+
|
|
163
|
+
this.#globalInitialized = true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** @private Attach (or re-attach) listeners to the current prompt */
|
|
167
|
+
#attachPromptListeners() {
|
|
168
|
+
// Remove any previous listeners from this prompt (defensive)
|
|
169
|
+
// NO: the privious prompt should be destructed outside
|
|
170
|
+
|
|
149
171
|
// Event listeners for Prompt updates
|
|
150
172
|
this.#prompt.on('message', (msg) => {
|
|
151
173
|
let write = false;
|
|
152
174
|
if (msg.role === 'reasoning') {
|
|
153
175
|
const content = this.#prompt.contentToString(msg.content);
|
|
154
|
-
cli.focus('
|
|
155
|
-
cli.write(
|
|
176
|
+
cli.focus('reasoning');
|
|
177
|
+
cli.write(`${content}\n`);
|
|
156
178
|
write = true;
|
|
157
179
|
} else if (msg.role === 'log') {
|
|
158
180
|
const content = this.#prompt.contentToString(msg.content);
|
|
@@ -173,15 +195,53 @@ class Cli {
|
|
|
173
195
|
cli.startSpinner();
|
|
174
196
|
}
|
|
175
197
|
});
|
|
198
|
+
|
|
176
199
|
this.#prompt.on('truncated', () => {
|
|
177
200
|
cli.focus('log');
|
|
178
201
|
cli.write('--- prompt truncated ---');
|
|
179
202
|
});
|
|
203
|
+
|
|
180
204
|
this.#prompt.on('finished', () => {
|
|
181
205
|
cli.focus('user', true);
|
|
182
206
|
});
|
|
183
207
|
}
|
|
184
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Rebind this CLI instance to a new Prompt and Session (used for in-process agent handoff).
|
|
211
|
+
*
|
|
212
|
+
* - Unbinds listeners from the old prompt.
|
|
213
|
+
* - Updates internal prompt/session references.
|
|
214
|
+
* - Updates the assistant role prefix to the new agent's name.
|
|
215
|
+
* - Re-attaches prompt event listeners to the *new* prompt.
|
|
216
|
+
* - Optionally updates the description shown on next start().
|
|
217
|
+
*
|
|
218
|
+
* Does **not** re-register global key mappings or inputHandler (prevents duplication and double-typing).
|
|
219
|
+
* Does **not** call start(). Caller should call start() (optionally with initial context) afterwards.
|
|
220
|
+
*
|
|
221
|
+
* @param {Prompt} newPrompt
|
|
222
|
+
* @param {Session} newSession
|
|
223
|
+
* @param {string} [newDescription] - Optional new description/intro (banner + agent info). If omitted, keeps previous.
|
|
224
|
+
*/
|
|
225
|
+
rebind(newPrompt, newSession, newDescription = null) {
|
|
226
|
+
if (!newPrompt || !newSession) {
|
|
227
|
+
throw new Error('rebind() requires a valid prompt and session');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.#prompt = newPrompt;
|
|
231
|
+
this.#session = newSession;
|
|
232
|
+
|
|
233
|
+
if (newDescription && typeof newDescription === 'string' && newDescription.trim()) {
|
|
234
|
+
this.#description = newDescription + '\n\n' + INTRO;
|
|
235
|
+
}
|
|
236
|
+
// else keep the existing description
|
|
237
|
+
|
|
238
|
+
// Update the assistant role prefix for the new agent name
|
|
239
|
+
cli.setRole('assistant', `${this.#session.name}: > `, 'brightGreen');
|
|
240
|
+
|
|
241
|
+
// Re-attach listeners to the new prompt only
|
|
242
|
+
this.#attachPromptListeners();
|
|
243
|
+
}
|
|
244
|
+
|
|
185
245
|
/**
|
|
186
246
|
* Handles user input, processes via Prompt.call(), captures last message, and manages errors.
|
|
187
247
|
* Only processes 'user' role inputs.
|
|
@@ -190,7 +250,11 @@ class Cli {
|
|
|
190
250
|
* @param {string} role - Input role (e.g., 'user').
|
|
191
251
|
*/
|
|
192
252
|
async #handleInput(s, role) {
|
|
193
|
-
if (!s || s.trim() === '')
|
|
253
|
+
if (!s || s.trim() === '') {
|
|
254
|
+
cli.focus('util');
|
|
255
|
+
cli.focus('user', true);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
194
258
|
if (role !== 'user') return;
|
|
195
259
|
cli.startSpinner();
|
|
196
260
|
try {
|
|
@@ -226,18 +290,28 @@ class Cli {
|
|
|
226
290
|
|
|
227
291
|
/**
|
|
228
292
|
* Starts the CLI: Shows intro/description, focuses user input, optionally processes initial message.
|
|
229
|
-
*
|
|
293
|
+
*
|
|
294
|
+
* When called with a string `s` (e.g. handoff context), the context is shown as a user message
|
|
295
|
+
* and then processed. This makes the injected handoff context visible in the terminal.
|
|
296
|
+
*
|
|
297
|
+
* @param {string} [s] - Optional initial user message / handoff context to process.
|
|
230
298
|
* @returns {Promise<void>}
|
|
231
|
-
* @example
|
|
232
|
-
* await cli.start(); // Interactive mode
|
|
233
|
-
* await cli.start('What is the weather?'); // Start with query
|
|
234
299
|
*/
|
|
235
300
|
async start(s) {
|
|
236
301
|
cli.focus('util');
|
|
237
302
|
cli.write(this.#description);
|
|
238
|
-
|
|
303
|
+
|
|
239
304
|
if (s && s.trim() !== '') {
|
|
305
|
+
// Explicitly render the injected context as a user message
|
|
306
|
+
// (normal typed input is echoed by the underlying cli library;
|
|
307
|
+
// programmatically injected messages need to be written manually)
|
|
308
|
+
cli.focus('user');
|
|
309
|
+
cli.write(s);
|
|
310
|
+
cli.focus('user', true);
|
|
311
|
+
|
|
240
312
|
await this.#handleInput(s, 'user');
|
|
313
|
+
} else {
|
|
314
|
+
cli.focus('user', true);
|
|
241
315
|
}
|
|
242
316
|
}
|
|
243
317
|
|
|
@@ -293,4 +367,4 @@ const copyToClipboard = async (text) => {
|
|
|
293
367
|
};
|
|
294
368
|
|
|
295
369
|
export default Cli;
|
|
296
|
-
export { copyToClipboard };
|
|
370
|
+
export { copyToClipboard };
|
package/lib/Prompt.js
CHANGED
|
@@ -538,6 +538,27 @@ class Prompt extends EventEmitter {
|
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Prune resolved function-call I/O from the prompt history, keeping only the
|
|
543
|
+
* most recent complete function request/response pair.
|
|
544
|
+
*
|
|
545
|
+
* A resolved function call is an assistant `function_request` that has a
|
|
546
|
+
* matching tool `function_response` by `call_id` (or `id` fallback). Older
|
|
547
|
+
* resolved pairs are removed from their messages; empty assistant/tool
|
|
548
|
+
* messages are removed entirely. Unresolved function calls and normal
|
|
549
|
+
* user/assistant text messages are preserved.
|
|
550
|
+
*
|
|
551
|
+
* This is useful before serializing history for providers such as the OpenAI
|
|
552
|
+
* Responses API, where repeatedly sending all historical tool transcripts can
|
|
553
|
+
* pollute context and waste tokens.
|
|
554
|
+
*
|
|
555
|
+
* @returns {boolean} True when the prompt history was changed.
|
|
556
|
+
*/
|
|
557
|
+
pruneResolvedFunctionCallsExceptLast() {
|
|
558
|
+
return pruneResolvedToolIOByCallIdExceptLastHelper(this.#messages, 'lastCall');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
|
|
541
562
|
/**
|
|
542
563
|
* reduce the prompt length to fit in the contextWindow
|
|
543
564
|
* or simply reset when there is no contextWindow
|
|
@@ -756,7 +777,8 @@ class Prompt extends EventEmitter {
|
|
|
756
777
|
const tools = this.toolsetFunctions().join(', ');
|
|
757
778
|
const context = this.contextLength;
|
|
758
779
|
const tokens = this.countTokens();
|
|
759
|
-
|
|
780
|
+
const options = JSON.stringify(this.#adapter.options, null, ' ')
|
|
781
|
+
return `Cwd: ${cwd}\nFunction_calls: ${tools}\nMax context: ${context}\nUsed Tokens: ${tokens}\nOptions:\n${options}\n`
|
|
760
782
|
}
|
|
761
783
|
|
|
762
784
|
}
|
package/lib/Session.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session manager for recording and retrieving chat sessions tied to a Prompt instance.
|
|
3
|
-
* Handles caching of messages, records, and logs in NDJSON files under `.cache/hello-dave/<name>/`.
|
|
3
|
+
* Handles caching of messages, records, and logs in NDJSON files under `.cache/@j-o-r/hello-dave/<name>/`.
|
|
4
4
|
* Automatically creates session names from first non-sticky user message.
|
|
5
5
|
* Listens to Prompt events (message, record, reset, truncated, others) for persistence.
|
|
6
6
|
*
|
|
@@ -74,12 +74,12 @@ class Session {
|
|
|
74
74
|
* Initializes cache folders and event listeners.
|
|
75
75
|
* @param {string} promptName - The name of the prompt.
|
|
76
76
|
* @param {Prompt} prompt - The Prompt instance.
|
|
77
|
-
* @param {string} [baseFolder] - Base storage folder; defaults to `.cache/hello-dave`.
|
|
77
|
+
* @param {string} [baseFolder] - Base storage folder; defaults to `.cache/@j-o-r/hello-dave`.
|
|
78
78
|
* @private
|
|
79
79
|
*/
|
|
80
80
|
#initializeCache = (promptName, prompt, baseFolder) => {
|
|
81
81
|
if (!baseFolder) {
|
|
82
|
-
baseFolder = path.resolve('.cache', 'hello-dave');
|
|
82
|
+
baseFolder = path.resolve('.cache', '@j-o-r/hello-dave');
|
|
83
83
|
}
|
|
84
84
|
const root = new CacheSync(baseFolder, true, 'any');
|
|
85
85
|
this.name = root.sanitizeKey(promptName);
|
|
@@ -121,7 +121,7 @@ class Session {
|
|
|
121
121
|
*
|
|
122
122
|
* @param {string} name - The name of the prompt.
|
|
123
123
|
* @param {Prompt} prompt - The Prompt instance.
|
|
124
|
-
* @param {string} [storage] - The base storage folder; defaults to process.cwd()/.cache/hello-dave.
|
|
124
|
+
* @param {string} [storage] - The base storage folder; defaults to process.cwd()/.cache/@j-o-r/hello-dave.
|
|
125
125
|
*/
|
|
126
126
|
constructor(name, prompt, storage) {
|
|
127
127
|
this.#initializeCache(name, prompt, storage);
|
|
@@ -164,6 +164,7 @@ class Session {
|
|
|
164
164
|
} else if (this.#sessionName !== '') {
|
|
165
165
|
sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
|
|
166
166
|
}
|
|
167
|
+
// console.log({sessKey});
|
|
167
168
|
if (sessKey) {
|
|
168
169
|
if (this.#queue.length > 0) {
|
|
169
170
|
let i = 0;
|
package/lib/ToolSet.js
CHANGED
|
@@ -39,6 +39,43 @@ const CHOICES = {
|
|
|
39
39
|
*/
|
|
40
40
|
const isValidName = (s) => /^[#!a-z_0-9]{2,}$/.test(s);
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Parse function-call parameters from provider output.
|
|
44
|
+
*
|
|
45
|
+
* Providers normally return parameters as a JSON object string, but some model
|
|
46
|
+
* outputs/API layers double-encode that string. This parser accepts objects,
|
|
47
|
+
* normal JSON strings, and double-encoded JSON strings without touching nested
|
|
48
|
+
* string literal values.
|
|
49
|
+
*
|
|
50
|
+
* @param {*} parameters - Raw function-call parameters.
|
|
51
|
+
* @returns {Record<string, *>} Parsed parameter object.
|
|
52
|
+
* @throws {Error} If parameters cannot be parsed as an object.
|
|
53
|
+
*/
|
|
54
|
+
const parseFunctionParameters = (parameters) => {
|
|
55
|
+
if (parameters == null || parameters === '') return {};
|
|
56
|
+
if (typeof parameters === 'object' && !Array.isArray(parameters)) return parameters;
|
|
57
|
+
if (typeof parameters !== 'string') {
|
|
58
|
+
throw new Error('Function parameters must be a JSON object or JSON object string');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let current = parameters;
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
for (let i = 0; i < 4 && !seen.has(current); i++) {
|
|
64
|
+
seen.add(current);
|
|
65
|
+
const parsed = JSON.parse(current);
|
|
66
|
+
if (typeof parsed === 'string') {
|
|
67
|
+
current = parsed;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
71
|
+
return parsed;
|
|
72
|
+
}
|
|
73
|
+
throw new Error('Function parameters must decode to a JSON object');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error('Function parameters are repeatedly encoded or invalid');
|
|
77
|
+
};
|
|
78
|
+
|
|
42
79
|
/**
|
|
43
80
|
* @classdesc ToolSet manages a collection of tools (functions) that can be registered and executed, typically for AI agent function calling.
|
|
44
81
|
* Supports schema definition compatible with JSON Schema for parameters.
|
|
@@ -150,6 +187,25 @@ class ToolSet {
|
|
|
150
187
|
return this;
|
|
151
188
|
}
|
|
152
189
|
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Copies ONE tool from another ToolSet into this one.
|
|
193
|
+
* Optionally renames it to avoid name conflicts.
|
|
194
|
+
*
|
|
195
|
+
* @param {ToolSet} sourceToolSet - The source ToolSet
|
|
196
|
+
* @param {string} sourceName - Name of the tool in the source ToolSet
|
|
197
|
+
* @param {string} [newName] - Optional new name for the tool in this ToolSet
|
|
198
|
+
* @returns {ToolSet} Returns this instance for chaining
|
|
199
|
+
*/
|
|
200
|
+
addFrom(sourceToolSet, sourceName, newName = null) {
|
|
201
|
+
if (!(sourceToolSet instanceof ToolSet)) {
|
|
202
|
+
throw new Error("addFrom() expects a ToolSet instance");
|
|
203
|
+
}
|
|
204
|
+
const tool = sourceToolSet.get(sourceName);
|
|
205
|
+
const finalName = newName || sourceName;
|
|
206
|
+
this.add(finalName, tool.description, tool.parameters, tool.method);
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
153
209
|
/**
|
|
154
210
|
* Getter for the current tool choice setting.
|
|
155
211
|
* @returns {string} The tool choice: 'auto', 'none', or 'required'
|
|
@@ -196,11 +252,17 @@ class ToolSet {
|
|
|
196
252
|
const startTime = new Date().getTime();
|
|
197
253
|
let response = '';
|
|
198
254
|
prompt.emit(prompt.EVENTS.tool_request, { name: call.function_request.name, call_id: call.function_request.call_id });
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
response =
|
|
203
|
-
|
|
255
|
+
if (call.function_request.name === 'load_agent' || call.function_request.name === 'hand_over') {
|
|
256
|
+
// Special handoff tool (Stage 1 prototype). Do not attempt normal execution.
|
|
257
|
+
// A clean response is provided here; the handoff sequence (assistant message + emit) is handled below.
|
|
258
|
+
response = 'Handoff initiated.';
|
|
259
|
+
} else {
|
|
260
|
+
try {
|
|
261
|
+
response = await this.call(call.function_request.name, parseFunctionParameters(call.function_request.parameters));
|
|
262
|
+
} catch (error) {
|
|
263
|
+
response = `Error: ${error.name} - ${error.message}`;
|
|
264
|
+
prompt.emit(prompt.EVENTS.tool_error, { name: call.function_request.name, call_id: call.function_request.call_id, error: response });
|
|
265
|
+
}
|
|
204
266
|
}
|
|
205
267
|
const duration = new Date().getTime() - startTime;
|
|
206
268
|
prompt.emit(prompt.EVENTS.tool_response, { name: call.function_request.name, call_id: call.function_request.call_id, duration });
|
|
@@ -221,7 +283,41 @@ class ToolSet {
|
|
|
221
283
|
functionResponses.push(responseObj);
|
|
222
284
|
}
|
|
223
285
|
prompt.addMultiModal('tool', functionResponses);
|
|
286
|
+
|
|
287
|
+
// === Clean in-process handoff support (matches lib/handOffToolset.js schema) ===
|
|
288
|
+
// After all normal function calls (including their function_responses) have been processed:
|
|
289
|
+
// 1. Normal function_response for load_agent / hand_over is already added above (as "Handoff initiated.").
|
|
290
|
+
// 2. Add assistant message: "agent hand-over to <target>..."
|
|
291
|
+
// 3. Emit 'agent:handoff' with clean payload { agent, context } so AgentLauncher can perform fresh-state replacement.
|
|
292
|
+
// This block activates for calls named exactly 'load_agent' or 'hand_over'.
|
|
293
|
+
// All other behavior (existing tools, error handling, events, etc.) remains 100% unchanged.
|
|
294
|
+
for (let i = 0; i < len; i++) {
|
|
295
|
+
const call = calls[i];
|
|
296
|
+
if (call.function_request.name === 'load_agent' || call.function_request.name === 'hand_over') {
|
|
297
|
+
let params = {};
|
|
298
|
+
try {
|
|
299
|
+
params = parseFunctionParameters(call.function_request.parameters || '{}');
|
|
300
|
+
} catch (_) {
|
|
301
|
+
params = {};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Clean schema from handOffToolset.js: { agent: string (required), context: string (required) }
|
|
305
|
+
const target = params.agent || params.target || params.name || params.agentName;
|
|
306
|
+
const context = params.context || '';
|
|
307
|
+
|
|
308
|
+
if (!target) {
|
|
309
|
+
// Fallback: do not emit if we cannot determine a target
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 2. Add the specified assistant message (non-sticky)
|
|
314
|
+
prompt.add('assistant', `agent hand-over to ${target}...`, false);
|
|
315
|
+
|
|
316
|
+
// 3. Emit the event with clean payload (AgentLauncher.#handleHandoff expects this)
|
|
317
|
+
prompt.emit('agent:handoff', { agent: target, context });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
224
320
|
}
|
|
225
321
|
}
|
|
226
322
|
|
|
227
|
-
export default ToolSet;
|
|
323
|
+
export default ToolSet;
|