@j-o-r/hello-dave 0.1.1 → 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 -1
- 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 -1
- 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 -17
- 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/AgentManager.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { Prompt, Cli, AgentClient, AgentServer, API, ToolSet, Session, env } from './index.js';
|
|
2
|
-
import toolsPool from './genericToolset.js'
|
|
3
|
-
/**
|
|
4
|
-
* @module lib/AgentManager
|
|
5
|
-
* @exports default AgentManager
|
|
6
|
-
* High-level orchestrator for AI agent lifecycle: configures Prompt/Session/ToolSet, launches CLI/Server/Client modes.
|
|
7
|
-
* Supports chaining setup() → start(); adds generic tools; direct calls.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @typedef {import('./API/x.ai/text.js').XOptions} XOptions
|
|
12
|
-
* @typedef {import('./API/x.ai/responses.js').XAIOptions} XAIOptions
|
|
13
|
-
* @typedef {import('./API/openai.com/reponses/text.js').OAOptions} OAOptions
|
|
14
|
-
* @typedef {import('./API/anthropic.com/text.js').ANTHOptions} ANTHOptions
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {Object} Setup
|
|
18
|
-
* @property {string} prompt - Initial system prompt.
|
|
19
|
-
* @property {XAIOptions|OAOptions|XOptions|ANTHOptions} options - API model options.
|
|
20
|
-
* @property {'gpt'|'xai'|'claude'} api - AI provider.
|
|
21
|
-
* @property {number} [contextWindow=300000] - Token limit for context.
|
|
22
|
-
* @property {'auto'|'required'|null} [toolsetMode] - Toolset activation mode.
|
|
23
|
-
* @property {boolean} [debug] - Enable verbose output.
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {Object} Options
|
|
27
|
-
* @property {string} name - Agent name (validated /^[a-z_0-9]{2,}$/).
|
|
28
|
-
* @property {string} secret - WS auth secret (base64-encoded).
|
|
29
|
-
* @property {string} [cachePath='.cache/hello-dave'] - Session cache directory.
|
|
30
|
-
* @example { name: 'code-agent', secret: 'mysecret' }
|
|
31
|
-
*/
|
|
32
|
-
/** @const {RegExp} Validates agent/tool names (lowercase a-z0-9_, min 2 chars). */
|
|
33
|
-
const validNameRegex = /^[a-z_0-9]{2,}$/;
|
|
34
|
-
|
|
35
|
-
class AgentManager {
|
|
36
|
-
/** @private @type {Prompt} */
|
|
37
|
-
#prompt;
|
|
38
|
-
/** @private @type {Session} */
|
|
39
|
-
#sessionStorage;
|
|
40
|
-
/** @private @type {string} */
|
|
41
|
-
#name = 'agent';
|
|
42
|
-
/** @private @type {string} */
|
|
43
|
-
#secret = '';
|
|
44
|
-
/** @private @type {string} */
|
|
45
|
-
#cachePath = '.cache/hello-dave';
|
|
46
|
-
/** @private @type {boolean} */
|
|
47
|
-
#debug = false;
|
|
48
|
-
/** @private @type {string} */
|
|
49
|
-
#model = '';
|
|
50
|
-
/** @private @type {number} */
|
|
51
|
-
#contextWindow = 0;
|
|
52
|
-
/** @private @type {string} */
|
|
53
|
-
#sysPrompt = '';
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Initializes AgentManager with base options.
|
|
57
|
-
* Validates name regex; encodes secret.
|
|
58
|
-
* @param {Options} options - Base configuration.
|
|
59
|
-
* @throws {Error} Invalid name (regex) or options.
|
|
60
|
-
* @chainable
|
|
61
|
-
*/
|
|
62
|
-
constructor(options) {
|
|
63
|
-
this.#name = options.name || this.#name;
|
|
64
|
-
if (options.secret && typeof options.secret === 'string') {
|
|
65
|
-
this.#secret = Buffer.from(options.secret).toString('base64');
|
|
66
|
-
}
|
|
67
|
-
if (!validNameRegex.test(this.#name)) {
|
|
68
|
-
throw new Error(`Invalid agent 'name': '${this.#name}'. Must match /^[a-z_0-9]{2,}$/`);
|
|
69
|
-
}
|
|
70
|
-
this.#cachePath = options.cachePath || this.#cachePath;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Configures the Prompt/Session/ToolSet with API adaptor.
|
|
75
|
-
* Adds system prompt; chains to start().
|
|
76
|
-
* @param {Setup} setup - AI setup details.
|
|
77
|
-
* @returns {AgentManager} This instance (chainable).
|
|
78
|
-
* @throws {Error} Invalid setup.
|
|
79
|
-
* @example mgr.setup({ prompt: 'You are a coder...', api: 'gpt', options: { model: 'gpt-4o' } });
|
|
80
|
-
*/
|
|
81
|
-
setup(setup) {
|
|
82
|
-
const {
|
|
83
|
-
prompt,
|
|
84
|
-
api = 'gpt',
|
|
85
|
-
contextWindow = 300000,
|
|
86
|
-
options = null,
|
|
87
|
-
toolsetMode = null,
|
|
88
|
-
debug = false
|
|
89
|
-
} = setup;
|
|
90
|
-
this.#sysPrompt = prompt;
|
|
91
|
-
this.#contextWindow = contextWindow;
|
|
92
|
-
if (options?.model) this.#model = options.model;
|
|
93
|
-
let toolset;
|
|
94
|
-
if (toolsetMode) {
|
|
95
|
-
toolset = new ToolSet(toolsetMode);
|
|
96
|
-
}
|
|
97
|
-
if (debug) {
|
|
98
|
-
this.#debug = true;
|
|
99
|
-
}
|
|
100
|
-
this.#prompt = new Prompt(contextWindow);
|
|
101
|
-
this.#sessionStorage = new Session(this.#name, this.#prompt, this.#cachePath);
|
|
102
|
-
this.#prompt.add('system', prompt, true);
|
|
103
|
-
this.#prompt.setAdaptor(API.text[api], toolset, options);
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @private
|
|
109
|
-
* Launches interactive CLI mode.
|
|
110
|
-
* @param {string} description - CLI intro message.
|
|
111
|
-
* @returns {void}
|
|
112
|
-
* @throws {Error} No setup() called.
|
|
113
|
-
*/
|
|
114
|
-
#startCli(description) {
|
|
115
|
-
if (!this.#prompt) throw new Error('Run setup first');
|
|
116
|
-
const cli = new Cli({ prompt: this.#prompt, session: this.#sessionStorage, description });
|
|
117
|
-
setTimeout(() => {
|
|
118
|
-
cli.start();
|
|
119
|
-
}, 1000);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* @private
|
|
124
|
-
* Attaches this agent as a WS client to another server (for chaining agents).
|
|
125
|
-
* @param {string} name - Tool name.
|
|
126
|
-
* @param {string} description - Tool desc.
|
|
127
|
-
* @param {string} [url='ws://127.0.0.1:8000/ws'] - Target WS URL.
|
|
128
|
-
* @returns {AgentClient} New client instance.
|
|
129
|
-
* @throws {Error} No setup().
|
|
130
|
-
*/
|
|
131
|
-
#attach(name, description, url = 'ws://127.0.0.1:8000/ws') {
|
|
132
|
-
if (!this.#prompt) throw new Error('Run setup first');
|
|
133
|
-
return new AgentClient({ prompt: this.#prompt, name, description, url, secret: this.#secret });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* @private
|
|
138
|
-
* Enables server mode: Registers this agent for remote tool calls.
|
|
139
|
-
* Optionally attaches as client too.
|
|
140
|
-
* @param {string} name - Server/tool name.
|
|
141
|
-
* @param {string} [description=''] - Desc (auto from sysPrompt).
|
|
142
|
-
* @param {number} [port=8000] - Port.
|
|
143
|
-
* @returns {void}
|
|
144
|
-
* @throws {Error} No setup/toolset.
|
|
145
|
-
*/
|
|
146
|
-
#enableServer(name, description = '', port = 8000) {
|
|
147
|
-
if (!this.#prompt) throw new Error('Run setup first');
|
|
148
|
-
if (!this.#prompt.toolset) throw new Error('No toolset defined');
|
|
149
|
-
const server = new AgentServer({
|
|
150
|
-
port,
|
|
151
|
-
name,
|
|
152
|
-
secret: this.#secret,
|
|
153
|
-
description,
|
|
154
|
-
prompt: this.#prompt,
|
|
155
|
-
session: this.#sessionStorage,
|
|
156
|
-
debug: this.#debug
|
|
157
|
-
});
|
|
158
|
-
this.#prompt.on('reset', () => {
|
|
159
|
-
server.resetAll();
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Direct synchronous call to the underlying Prompt.
|
|
165
|
-
* @param {string} input - User input.
|
|
166
|
-
* @returns {Promise<string>} Response.
|
|
167
|
-
* @throws {Error} No setup.
|
|
168
|
-
*/
|
|
169
|
-
async directCall(input) {
|
|
170
|
-
const res = await this.#prompt.call(input);
|
|
171
|
-
return res;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** @returns {Prompt} Underlying Prompt instance. */
|
|
175
|
-
getPrompt() { return this.#prompt; }
|
|
176
|
-
|
|
177
|
-
/** @returns {ToolSet|null} Toolset if configured. */
|
|
178
|
-
getToolset() { return this.#prompt.toolset; }
|
|
179
|
-
|
|
180
|
-
/** @returns {Promise<import('./fafs.js').EnvironmentInfo>} System env info. */
|
|
181
|
-
async environment() {
|
|
182
|
-
return env();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Adds a pre-defined generic tool from toolsPool (genericToolset) to this agent's ToolSet.
|
|
187
|
-
* @param {'execute_bash_script'|'execute_remote_script'|'get_user_env'|'history_search'|'javascript_interpreter'|'memory_recall'|'memory_write'|'open_link'|'read_file'|'send_email'|'syntax_check'|'write_file'} name - Tool name.
|
|
188
|
-
* @throws {Error} Invalid name, no toolset, or tool missing.
|
|
189
|
-
* @example mgr.addGenericToolcall('read_file');
|
|
190
|
-
*/
|
|
191
|
-
addGenericToolcall(name) {
|
|
192
|
-
if (!validNameRegex.test(name)) {
|
|
193
|
-
throw new Error(`Invalid 'name': '${name}'. Must match /^[a-z_0-9]{2,}$/`);
|
|
194
|
-
}
|
|
195
|
-
if (!this.#prompt.toolset) throw new Error('No ToolSet defined');
|
|
196
|
-
let tool = toolsPool.get(name);
|
|
197
|
-
if (!tool) throw new Error(`Tool '${name}' not found in pool`);
|
|
198
|
-
this.#prompt.toolset.add(name, tool.description, tool.parameters, tool.method);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Smart launcher: CLI (default), server (servePort), or client (connectUrl).
|
|
203
|
-
* Auto-generates intros/descriptions if empty.
|
|
204
|
-
* @param {number} [servePort] - Launch server on port.
|
|
205
|
-
* @param {string} [connectUrl] - Connect as client to WS.
|
|
206
|
-
* @param {string} [cliIntro=""] - CLI intro text.
|
|
207
|
-
* @param {string} [toolName=""] - Tool/server name (falls back to #name).
|
|
208
|
-
* @param {string} [toolDescription=""] - Tool desc.
|
|
209
|
-
* @returns {Promise<void>}
|
|
210
|
-
* @throws {Error} No setup; invalid names/ports.
|
|
211
|
-
* @example mgr.start(8000); // Server\nmgr.start(undefined, 'ws://other:8001/ws'); // Client\nmgr.start(); // CLI
|
|
212
|
-
*/
|
|
213
|
-
async start(servePort, connectUrl, cliIntro = "", toolName = "", toolDescription = "") {
|
|
214
|
-
if (!this.#prompt) throw new Error("Run setup first");
|
|
215
|
-
|
|
216
|
-
toolName = toolName || this.#name;
|
|
217
|
-
if (!validNameRegex.test(toolName)) {
|
|
218
|
-
throw new Error(`Invalid 'toolName': '${toolName}'. Must match /^[a-z_0-9]{2,}$/`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (!toolDescription && this.#sysPrompt) {
|
|
222
|
-
toolDescription = this.#sysPrompt.slice(0, 100).trim() + "...";
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (!cliIntro) {
|
|
226
|
-
const introStr = `${this.#name} ${this.#model || "unknown-model"} - context: ${this.#contextWindow}`;
|
|
227
|
-
cliIntro = introStr.trim();
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (servePort) {
|
|
231
|
-
console.log(toolName);
|
|
232
|
-
console.log(toolDescription);
|
|
233
|
-
this.#enableServer(toolName, toolDescription, servePort);
|
|
234
|
-
if (connectUrl) {
|
|
235
|
-
this.#attach(toolName, toolDescription, connectUrl);
|
|
236
|
-
}
|
|
237
|
-
} else if (connectUrl) {
|
|
238
|
-
this.#attach(toolName, toolDescription, connectUrl);
|
|
239
|
-
} else {
|
|
240
|
-
this.#startCli(cliIntro);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export default AgentManager;
|
package/lib/AgentServer.js
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import { WebSocketServer, server } from '@j-o-r/apiserver';
|
|
2
|
-
|
|
3
|
-
const TIMEOUT = 30000;
|
|
4
|
-
const MAX_INTERVALS = 20; // 10 minutes (30 sec * 20)
|
|
5
|
-
|
|
6
|
-
let ws;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @module lib/AgentServer
|
|
10
|
-
* @exports default AgentServer
|
|
11
|
-
* Dynamic WebSocket server for AI agents. Registers connected clients as dynamic tool calls in the Prompt's ToolSet.
|
|
12
|
-
* Handles authentication, user/agent messages, sessions, resets, and broadcasts.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {import('@j-o-r/apiserver/types/ClientWrapper.d.ts').ClientWrapper} Client
|
|
17
|
-
* @typedef {import('./Prompt.js').default} Prompt
|
|
18
|
-
* @typedef {import('./ToolSet.js').default} ToolSet
|
|
19
|
-
* @typedef {import('./Session.js').default} Session
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* @typedef {string|object} MessageContent
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {Object} Message
|
|
26
|
-
* @property {string} action - Action type (must be in ACTIONS).
|
|
27
|
-
* @property {MessageContent} [content]
|
|
28
|
-
* @property {string} [id]
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* @typedef {(conn: import('@j-o-r/apiserver/types/WebSocketServer.js').WebSocketConnection, req: any) => Promise<boolean>} AuthFunction
|
|
32
|
-
*/
|
|
33
|
-
/**
|
|
34
|
-
* @typedef {Object} AgentServerOptions
|
|
35
|
-
* @property {string} name - Server/agent name.
|
|
36
|
-
* @property {string} secret - Access secret (matches client secrets).
|
|
37
|
-
* @property {string} description - Server description.
|
|
38
|
-
* @property {Prompt} prompt - Prompt instance.
|
|
39
|
-
* @property {Session} session - Session manager.
|
|
40
|
-
* @property {AuthFunction} [auth] - Custom auth function.
|
|
41
|
-
* @property {number} [port=8000] - Listen port (>=1024).
|
|
42
|
-
* @property {boolean} [debug=false] - Verbose logging.
|
|
43
|
-
* @example { name: 'main-agent', prompt: myPrompt, session: mySession, secret: 'abc', port: 8001 }
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
/** @const @type {readonly string[]} Supported WebSocket message actions. Use .includes() for validation. */
|
|
47
|
-
const ACTIONS = [
|
|
48
|
-
// Agent / Toolset function calls
|
|
49
|
-
'agent_introduction', // Client introduces as tool
|
|
50
|
-
'agent_error', // Agent error response
|
|
51
|
-
'agent_query', // Query to agent
|
|
52
|
-
'agent_response', // Agent response
|
|
53
|
-
// User interactions
|
|
54
|
-
'user_introduction', // Client connect
|
|
55
|
-
'user_request', // User query
|
|
56
|
-
'server_response', // Server response to user
|
|
57
|
-
'server_error', // Server error to user
|
|
58
|
-
'user_reset', // Reset session
|
|
59
|
-
'user_sessionlist', // List sessions
|
|
60
|
-
'user_loadsession', // Load session
|
|
61
|
-
'user_info', // Server info
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
// WS clients map: conn_id → agent name
|
|
65
|
-
const clients = new Map();
|
|
66
|
-
// Pending responses: `${conn_id}:${msg_id}` → {resolve, reject, timer}
|
|
67
|
-
const pendingResponses = new Map();
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @function deleteClient
|
|
71
|
-
* @private
|
|
72
|
-
* Removes a client from the toolset and clients map.
|
|
73
|
-
* @param {ToolSet|null} toolset - Prompt's toolset.
|
|
74
|
-
* @param {string} conn_id - Connection ID.
|
|
75
|
-
* @returns {void}
|
|
76
|
-
*/
|
|
77
|
-
const deleteClient = (toolset, conn_id) => {
|
|
78
|
-
if (!toolset) return;
|
|
79
|
-
const name = clients.get(conn_id);
|
|
80
|
-
if (name) {
|
|
81
|
-
clients.delete(conn_id);
|
|
82
|
-
if (toolset.has(name)) {
|
|
83
|
-
toolset.delete(name);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @function addClient
|
|
90
|
-
* @private
|
|
91
|
-
* Registers a WS client as a dynamic tool in the toolset.
|
|
92
|
-
* Sends query via WS, awaits response with timeout.
|
|
93
|
-
* @param {ToolSet|null} toolset - Prompt's toolset.
|
|
94
|
-
* @param {string} conn_id - Connection ID.
|
|
95
|
-
* @param {string} name - Tool name.
|
|
96
|
-
* @param {string} description - Tool description.
|
|
97
|
-
* @returns {void}
|
|
98
|
-
*/
|
|
99
|
-
const addClient = (toolset, conn_id, name, description) => {
|
|
100
|
-
if (!toolset) return;
|
|
101
|
-
toolset.add(
|
|
102
|
-
name,
|
|
103
|
-
description,
|
|
104
|
-
{
|
|
105
|
-
type: 'object',
|
|
106
|
-
properties: {
|
|
107
|
-
query: {
|
|
108
|
-
type: 'string',
|
|
109
|
-
description: `Command, question or query for this particular expert well formed in natural language`
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
required: ['query']
|
|
113
|
-
},
|
|
114
|
-
// @ts-ignore
|
|
115
|
-
(params) => {
|
|
116
|
-
return new Promise((resolve, reject) => {
|
|
117
|
-
const msg_id = Date.now().toString();
|
|
118
|
-
const key = `${conn_id}:${msg_id}`;
|
|
119
|
-
clients.set(conn_id, name);
|
|
120
|
-
|
|
121
|
-
let intervals = 0;
|
|
122
|
-
const timer = setInterval(() => {
|
|
123
|
-
intervals++;
|
|
124
|
-
if (intervals >= MAX_INTERVALS) {
|
|
125
|
-
clearInterval(timer);
|
|
126
|
-
if (pendingResponses.has(key)) {
|
|
127
|
-
reject('Timeout after max intervals');
|
|
128
|
-
pendingResponses.delete(key);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}, TIMEOUT);
|
|
132
|
-
|
|
133
|
-
pendingResponses.set(key, { resolve, reject, timer });
|
|
134
|
-
|
|
135
|
-
ws.sendToConnection(conn_id, JSON.stringify({
|
|
136
|
-
action: 'agent_query',
|
|
137
|
-
id: msg_id,
|
|
138
|
-
content: params.query
|
|
139
|
-
}));
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
);
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* AgentServer: Core WebSocket server class.
|
|
147
|
-
* Exposes connected agents as tools; handles user CLI/WS interactions, auth, sessions.
|
|
148
|
-
* Starts on localhost; broadcasts resets.
|
|
149
|
-
* Logs events via console; emits via Prompt.
|
|
150
|
-
* @emits tool_request - Tool calls (via #prompt).
|
|
151
|
-
* @emits tool_error - Tool failures (via #prompt).
|
|
152
|
-
* @example
|
|
153
|
-
* const server = new AgentServer({
|
|
154
|
-
* name: 'main',
|
|
155
|
-
* prompt: myPrompt,
|
|
156
|
-
* session: mySession,
|
|
157
|
-
* secret: 'abc',
|
|
158
|
-
* port: 8000,
|
|
159
|
-
* debug: true
|
|
160
|
-
* });
|
|
161
|
-
*/
|
|
162
|
-
class AgentServer {
|
|
163
|
-
/** @private @type {boolean} */
|
|
164
|
-
#debug = false;
|
|
165
|
-
/** @private @type {number} */
|
|
166
|
-
#port = 8000;
|
|
167
|
-
/** @private @type {string} */
|
|
168
|
-
#name = '';
|
|
169
|
-
/** @private @type {string} */
|
|
170
|
-
#secret = '';
|
|
171
|
-
/** @private @type {string} */
|
|
172
|
-
#description = '';
|
|
173
|
-
/** @private @type {Prompt} */
|
|
174
|
-
#prompt;
|
|
175
|
-
/** @private @type {Session} */
|
|
176
|
-
#session;
|
|
177
|
-
/**
|
|
178
|
-
* @private
|
|
179
|
-
* Default auth: Checks 'wssrc_id' query param against #secret.
|
|
180
|
-
* @param {Object} conn - Connection object.
|
|
181
|
-
* @returns {Promise<boolean>}
|
|
182
|
-
*/
|
|
183
|
-
#auth = async (conn) => {
|
|
184
|
-
const q = conn.client.query;
|
|
185
|
-
const key = q.get('wssrc_id');
|
|
186
|
-
return key === this.#secret;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Initializes and starts the AgentServer.
|
|
191
|
-
* Validates port, sets up Prompt event logging, launches WS server.
|
|
192
|
-
* @param {AgentServerOptions} options - Server configuration.
|
|
193
|
-
* @throws {Error} If port < 1024.
|
|
194
|
-
* @example See AgentServerOptions.
|
|
195
|
-
*/
|
|
196
|
-
constructor(options) {
|
|
197
|
-
if (options.port) {
|
|
198
|
-
if (options.port < 1024) {
|
|
199
|
-
throw new Error('Port must be higher than 1024');
|
|
200
|
-
}
|
|
201
|
-
this.#port = options.port;
|
|
202
|
-
}
|
|
203
|
-
if (options.auth) {
|
|
204
|
-
this.#auth = options.auth;
|
|
205
|
-
}
|
|
206
|
-
this.#name = options.name;
|
|
207
|
-
this.#secret = options.secret;
|
|
208
|
-
this.#description = options.description;
|
|
209
|
-
this.#session = options.session;
|
|
210
|
-
this.#prompt = options.prompt;
|
|
211
|
-
this.#debug = options.debug ?? false;
|
|
212
|
-
|
|
213
|
-
// Log Prompt events
|
|
214
|
-
const events = Object.keys(this.#prompt.EVENTS);
|
|
215
|
-
events.forEach((evt) => {
|
|
216
|
-
this.#prompt.on(evt, (_msg) => {
|
|
217
|
-
console.log(`** ${this.#name} e:${evt}**`);
|
|
218
|
-
if (evt === 'tool_request') {
|
|
219
|
-
console.log(`tool execute: ${this.#name} ${_msg.name} ${_msg.call_id}`);
|
|
220
|
-
} else if (evt === 'tool_error') {
|
|
221
|
-
console.log(`tool error:`);
|
|
222
|
-
console.log(JSON.stringify(_msg, null, ' '));
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// Async start
|
|
228
|
-
(async () => {
|
|
229
|
-
await this._start();
|
|
230
|
-
})();
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* @private
|
|
235
|
-
* Launches the WS server with handlers for messages, connections, closes.
|
|
236
|
-
* Defines HTTP / API index endpoint.
|
|
237
|
-
* @returns {Promise<void>}
|
|
238
|
-
*/
|
|
239
|
-
async _start() {
|
|
240
|
-
const name = this.#name;
|
|
241
|
-
const description = this.#description;
|
|
242
|
-
const port = this.#port;
|
|
243
|
-
const DEBUG = this.#debug;
|
|
244
|
-
const API = {
|
|
245
|
-
/**
|
|
246
|
-
* @param {Client} client
|
|
247
|
-
*/
|
|
248
|
-
index: async (client) => {
|
|
249
|
-
await client.serve(200, { name, description });
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
ws = new WebSocketServer({
|
|
253
|
-
verbose: this.#debug,
|
|
254
|
-
onMessage: async (conn, msg) => {
|
|
255
|
-
if (!msg.action || !ACTIONS.includes(msg.action)) {
|
|
256
|
-
console.log('Unknown action: ' + msg.action);
|
|
257
|
-
deleteClient(this.#prompt.toolset, conn.id);
|
|
258
|
-
ws.closeConnection(conn.id);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (msg.action === 'agent_introduction') {
|
|
262
|
-
const name = msg.name;
|
|
263
|
-
const content = (typeof msg.content === 'string') ? msg.content : JSON.stringify(msg.content);
|
|
264
|
-
if (!content) {
|
|
265
|
-
deleteClient(this.#prompt.toolset, conn.id);
|
|
266
|
-
ws.closeConnection(conn.id);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
addClient(this.#prompt.toolset, conn.id, name, content);
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
if (msg.action === 'agent_response') {
|
|
273
|
-
const key = `${conn.id}:${msg.id}`;
|
|
274
|
-
const pending = pendingResponses.get(key);
|
|
275
|
-
if (pending) {
|
|
276
|
-
try { clearInterval(pending.timer); } catch { }
|
|
277
|
-
pending.resolve(msg.content);
|
|
278
|
-
pendingResponses.delete(key);
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
if (msg.action === 'agent_error') {
|
|
283
|
-
const key = `${conn.id}:${msg.id}`;
|
|
284
|
-
const pending = pendingResponses.get(key);
|
|
285
|
-
if (pending) {
|
|
286
|
-
try { clearInterval(pending.timer); } catch { }
|
|
287
|
-
pending.reject(msg.content);
|
|
288
|
-
pendingResponses.delete(key);
|
|
289
|
-
}
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
// User interactions
|
|
293
|
-
if (msg.action === 'user_request') {
|
|
294
|
-
const id = msg.id;
|
|
295
|
-
if (!msg.content || msg.content.trim() === '') {
|
|
296
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
297
|
-
action: 'server_response',
|
|
298
|
-
id,
|
|
299
|
-
content: ''
|
|
300
|
-
}));
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
console.log(`user_request:>>\n${msg.content}\n`);
|
|
304
|
-
try {
|
|
305
|
-
const content = await this.#prompt.call(msg.content);
|
|
306
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
307
|
-
action: 'server_response',
|
|
308
|
-
id,
|
|
309
|
-
content
|
|
310
|
-
}));
|
|
311
|
-
console.log(`server_response:>>\n${content}\n`);
|
|
312
|
-
} catch (e) {
|
|
313
|
-
console.log(`server_error:>>\n`);
|
|
314
|
-
console.error(e);
|
|
315
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
316
|
-
action: 'server_error',
|
|
317
|
-
id,
|
|
318
|
-
e: String(e)
|
|
319
|
-
}));
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (msg.action === 'user_reset') {
|
|
323
|
-
const id = msg.id;
|
|
324
|
-
this.#prompt.reset();
|
|
325
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
326
|
-
action: 'server_reset',
|
|
327
|
-
id,
|
|
328
|
-
content: 'ok'
|
|
329
|
-
}));
|
|
330
|
-
}
|
|
331
|
-
if (msg.action === 'user_sessionlist') {
|
|
332
|
-
const id = msg.id;
|
|
333
|
-
const sess = this.#session.sessionList().reverse();
|
|
334
|
-
sess.push('NONE');
|
|
335
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
336
|
-
action: 'server_sessionlist',
|
|
337
|
-
id,
|
|
338
|
-
content: sess
|
|
339
|
-
}));
|
|
340
|
-
}
|
|
341
|
-
if (msg.action === 'user_loadsession') {
|
|
342
|
-
const id = msg.id;
|
|
343
|
-
const selected = msg.content;
|
|
344
|
-
const messages = [];
|
|
345
|
-
const list = this.#session.set(selected);
|
|
346
|
-
this.#prompt.messages = list;
|
|
347
|
-
let i = 0;
|
|
348
|
-
const length = list.length;
|
|
349
|
-
for (; i < length; i++) {
|
|
350
|
-
const msg = list[i];
|
|
351
|
-
if (!msg.sticky) {
|
|
352
|
-
if (['user', 'assistant', 'reasoning', 'log'].includes(msg.role)) {
|
|
353
|
-
if (msg.content[0].type !== 'function_request') {
|
|
354
|
-
const content = this.#prompt.contentToString(msg.content);
|
|
355
|
-
messages.push({ role: msg.role, content });
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
await this.#prompt.triggerRequest();
|
|
361
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
362
|
-
action: 'server_sessionloaded',
|
|
363
|
-
id,
|
|
364
|
-
content: messages
|
|
365
|
-
}));
|
|
366
|
-
}
|
|
367
|
-
if (msg.action === 'user_info') {
|
|
368
|
-
const id = msg.id;
|
|
369
|
-
const sessionInfo = this.#session.info();
|
|
370
|
-
const promptInfo = this.#prompt.info();
|
|
371
|
-
const info = `\nSESSION\n${sessionInfo}\nPROMPT\n${promptInfo}\nSERVER\nName: ${this.#name}\nDescription: ${this.#description}`;
|
|
372
|
-
ws.sendToConnection(conn.id, JSON.stringify({
|
|
373
|
-
action: 'server_info',
|
|
374
|
-
id,
|
|
375
|
-
content: info
|
|
376
|
-
}));
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
onConnection: async (conn) => {
|
|
380
|
-
return this.#auth(conn);
|
|
381
|
-
},
|
|
382
|
-
onClose: async (conn, reason) => {
|
|
383
|
-
deleteClient(this.#prompt.toolset, conn.id);
|
|
384
|
-
},
|
|
385
|
-
onError: async (conn, error) => {
|
|
386
|
-
deleteClient(this.#prompt.toolset, conn.id);
|
|
387
|
-
ws.closeConnection(conn.id);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
await server.create('v1', { port, host: '127.0.0.1', ws, strict: DEBUG, verbose: DEBUG }, API);
|
|
391
|
-
console.log(`Websocket server: ws://127.0.0.1:${port}/ws`);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Broadcasts a reset message to all connected clients (starts new sessions).
|
|
396
|
-
* @returns {void}
|
|
397
|
-
* @fires reset - Via underlying prompt/clients.
|
|
398
|
-
*/
|
|
399
|
-
resetAll() {
|
|
400
|
-
ws.broadcast({ action: 'reset' });
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
export default AgentServer;
|