@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.
Files changed (173) hide show
  1. package/CHANGELOG.md +42 -25
  2. package/README.md +81 -221
  3. package/TODO.md +173 -35
  4. package/agents/agent_creator.js +105 -0
  5. package/agents/agent_creator.prompt.md +371 -0
  6. package/agents/ask_agent.js +64 -127
  7. package/agents/claude_agent.js +68 -0
  8. package/agents/code_agent.js +55 -135
  9. package/agents/code_agent.prompt.md +50 -0
  10. package/agents/echo_agent.js +76 -0
  11. package/agents/financial_expert.js +75 -0
  12. package/agents/gpt_agent.js +52 -103
  13. package/agents/gpt_code.js +81 -0
  14. package/agents/grok_agent.js +58 -114
  15. package/agents/minimax_agent.js +92 -0
  16. package/agents/mureka_agent.js +77 -0
  17. package/agents/planner_agent.js +172 -0
  18. package/agents/stability_agent.js +87 -0
  19. package/agents/test_agent.js +75 -157
  20. package/agents/weather_agent.js +73 -0
  21. package/agents/workflow_agent.js +189 -0
  22. package/bin/dave.js +436 -184
  23. package/docs/bin-dave.md +85 -35
  24. package/docs/cdn-ssh.md +100 -0
  25. package/docs/creating-agents.md +301 -0
  26. package/docs/creating-toolsets.md +336 -0
  27. package/docs/docs-organization.md +48 -0
  28. package/docs/project-overview.md +86 -51
  29. package/lib/API/elevenlabs.io/music.compose.md +441 -0
  30. package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
  31. package/lib/API/elevenlabs.io/music.stream.md +425 -0
  32. package/lib/API/lalal.ai/lalal.js +445 -0
  33. package/lib/API/lalal.ai/openapi.json +2614 -0
  34. package/lib/API/minimax/ImageToolset.js +82 -37
  35. package/lib/API/minimax/MusicToolset.js +125 -79
  36. package/lib/API/minimax/VideoToolset.js +170 -167
  37. package/lib/API/minimax/image.js +5 -1
  38. package/lib/API/minimax/music.js +210 -23
  39. package/lib/API/minimax/video.js +242 -53
  40. package/lib/API/mureka/MusicToolset.js +646 -0
  41. package/lib/API/mureka/README.md +41 -0
  42. package/lib/API/mureka/index.js +7 -0
  43. package/lib/API/mureka/music.js +658 -0
  44. package/lib/API/openai.com/index.js +7 -0
  45. package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
  46. package/lib/API/openai.com/video.create.character.md +40 -0
  47. package/lib/API/openai.com/video.create.md +219 -0
  48. package/lib/API/openai.com/video.delete.md +44 -0
  49. package/lib/API/openai.com/video.download.md +31 -0
  50. package/lib/API/openai.com/video.edit.md +155 -0
  51. package/lib/API/openai.com/video.extend.md +166 -0
  52. package/lib/API/openai.com/video.fetch.character.md +43 -0
  53. package/lib/API/openai.com/video.js +784 -0
  54. package/lib/API/openai.com/video.list.md +201 -0
  55. package/lib/API/openai.com/video.remix.md +175 -0
  56. package/lib/API/openai.com/video.retrieve.md +139 -0
  57. package/lib/API/openai.com/videoToolset.js +616 -0
  58. package/lib/API/stability.ai/ImageToolset.js +131 -40
  59. package/lib/API/stability.ai/MusicToolset.js +79 -47
  60. package/lib/API/stability.ai/audio.js +63 -131
  61. package/lib/API/x.ai/chat.responses.md +1040 -0
  62. package/lib/API/x.ai/image.js +229 -59
  63. package/lib/API/x.ai/imageToolset.js +376 -0
  64. package/lib/API/x.ai/index.js +1 -3
  65. package/lib/API/x.ai/responses.js +9 -18
  66. package/lib/Agent.js +271 -0
  67. package/lib/Agent.js.old +284 -0
  68. package/lib/AgentLauncher.js +562 -0
  69. package/lib/Cli.js +87 -13
  70. package/lib/Prompt.js +23 -1
  71. package/lib/Session.js +5 -4
  72. package/lib/ToolSet.js +102 -6
  73. package/lib/agentLoader.js +369 -0
  74. package/lib/cdn.js +67 -231
  75. package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
  76. package/lib/defaultToolsets.js +43 -0
  77. package/lib/fafs.js +1 -1
  78. package/lib/genericToolset.js +442 -119
  79. package/lib/handOffToolset.js +179 -0
  80. package/lib/index.js +34 -27
  81. package/lib/toolsetLoader.js +248 -0
  82. package/package.json +11 -5
  83. package/types/API/lalal.ai/lalal.d.ts +116 -0
  84. package/types/API/minimax/image.d.ts +2 -1
  85. package/types/API/minimax/music.d.ts +189 -26
  86. package/types/API/minimax/video.d.ts +100 -31
  87. package/types/API/mureka/index.d.ts +7 -0
  88. package/types/API/mureka/music.d.ts +472 -0
  89. package/types/API/openai.com/index.d.ts +7 -0
  90. package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
  91. package/types/API/openai.com/video.d.ts +409 -0
  92. package/types/API/openai.com/videoToolset.d.ts +24 -0
  93. package/types/API/stability.ai/audio.d.ts +14 -103
  94. package/types/API/stability.ai/image.d.ts +2 -2
  95. package/types/API/x.ai/image.d.ts +138 -26
  96. package/types/API/x.ai/imageToolset.d.ts +3 -0
  97. package/types/API/x.ai/index.d.ts +1 -3
  98. package/types/API/x.ai/responses.d.ts +4 -4
  99. package/types/Agent.d.ts +123 -0
  100. package/types/AgentLauncher.d.ts +222 -0
  101. package/types/Cli.d.ts +28 -8
  102. package/types/Prompt.d.ts +23 -5
  103. package/types/Session.d.ts +1 -1
  104. package/types/ToolSet.d.ts +10 -0
  105. package/types/agentLoader.d.ts +78 -0
  106. package/types/cdn.d.ts +15 -90
  107. package/types/defaultToolsets.d.ts +9 -0
  108. package/types/fafs.d.ts +1 -1
  109. package/types/genericToolset.d.ts +1 -1
  110. package/types/handOffToolset.d.ts +28 -0
  111. package/types/index.d.ts +19 -16
  112. package/types/toolsetLoader.d.ts +114 -0
  113. package/utils/format_log.js +101 -23
  114. package/utils/launch_agent.js +18 -0
  115. package/utils/list_sessions.sh +13 -5
  116. package/utils/search_sessions.sh +65 -29
  117. package/utils/toolsets.js +33 -0
  118. package/README.md.bak.1779452127 +0 -240
  119. package/agents/codeserver.sh +0 -47
  120. package/agents/daisy_agent.js +0 -173
  121. package/agents/docs_agent.js +0 -148
  122. package/agents/memory_agent.js +0 -263
  123. package/agents/minimax.js +0 -173
  124. package/agents/npm_agent.js +0 -202
  125. package/agents/prompt_agent.js +0 -133
  126. package/agents/readme_agent.js +0 -148
  127. package/agents/spawn_agent.js +0 -160
  128. package/agents/stability.js +0 -173
  129. package/agents/todo_agent.js +0 -175
  130. package/bin/codeDave +0 -58
  131. package/docs/agent-dave-websocket-protocol.md +0 -180
  132. package/docs/agent-manager.md +0 -244
  133. package/docs/codeserver-pattern.md +0 -191
  134. package/docs/generic-toolset.md +0 -326
  135. package/docs/howtos/agent-networking.md +0 -253
  136. package/docs/howtos/spawn-agents.md.bak +0 -200
  137. package/docs/howtos/spawn-agents.md.bak_new +0 -200
  138. package/docs/multi-agent-clusters.md +0 -265
  139. package/docs/music-toolsets.md +0 -137
  140. package/docs/path-resolution-best-practices.md +0 -104
  141. package/docs/plans/minimax-music-generation.md +0 -80
  142. package/docs/plans/unified-agent-architecture.md +0 -146
  143. package/docs/plans/websocket-streaming-plan.md.bak +0 -317
  144. package/docs/prompt/spawn_agent.md +0 -175
  145. package/docs/prompt/spawn_agent.md.bak +0 -201
  146. package/docs/prompt/task_clarification_and_documentation.md +0 -35
  147. package/docs/prompt-class.md +0 -141
  148. package/docs/todo-archive-infra-2026-04-21.md +0 -15
  149. package/docs/todo-archive-v0.0.8.md +0 -1
  150. package/docs/todo-archive-v0.1.0.md +0 -32
  151. package/docs/todo-archive.md +0 -44
  152. package/docs/tools-syntax-validation.md +0 -121
  153. package/docs/toolset.md +0 -164
  154. package/docs/xai-responses.md +0 -111
  155. package/docs/xai_collections.md +0 -106
  156. package/lib/API/x.ai/ImageToolset.js +0 -165
  157. package/lib/API/x.ai/text.js +0 -415
  158. package/lib/AgentClient.js +0 -248
  159. package/lib/AgentManager.js +0 -245
  160. package/lib/AgentServer.js +0 -404
  161. package/lib/wsCli.js +0 -287
  162. package/lib/wsIO.js +0 -90
  163. package/types/API/x.ai/text.d.ts +0 -286
  164. package/types/AgentClient.d.ts +0 -109
  165. package/types/AgentManager.d.ts +0 -100
  166. package/types/AgentServer.d.ts +0 -89
  167. package/types/wsCli.d.ts +0 -17
  168. package/types/wsIO.d.ts +0 -30
  169. package/utils/test.sh +0 -46
  170. /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
  171. /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
  172. /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
  173. /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
@@ -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;
@@ -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&lt;void&gt;}
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;