@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
package/lib/Agent.js ADDED
@@ -0,0 +1,271 @@
1
+ import Prompt from './Prompt.js';
2
+ import Session from './Session.js';
3
+ import ToolSet from './ToolSet.js';
4
+
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ /**
10
+ * @typedef {import("./API/x.ai/responses.js").XAIOptions} XAIOptions
11
+ * @typedef {import("./API/openai.com/reponses/text.js").OAOptions} OAOptions
12
+ * @typedef {import("./API/anthropic.com/text.js").ANTHOptions} ANTHOptions
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} AgentConfig
17
+ * @property {string} prompt - Initial system prompt (can be loaded via Agent.loadPrompt for extended agents)
18
+ * @property {Function} api - Chat adaptor function from API.chat.* (e.g. API.chat.xai)
19
+ * @property {XAIOptions|OAOptions|Object} [options] - Model options
20
+ * @property {number} [contextWindow=300000]
21
+ * @property {"auto"|"required"|null} [toolsetMode]
22
+ * @property {boolean} [debug=false]
23
+ * @property {string} [secret] - Plain text secret (will be base64 encoded internally)
24
+ * @property {string} [cachePath=".cache/@j-o-r/hello-dave"]
25
+ * @property {string} call_name - Agent/tool name (required). Must match /^[a-z0-9_]{2,}$/. Recommended: derive via Agent.deriveCallName(import.meta.url)
26
+ * @property {string} call_description - Description for the tool (required, non-empty string)
27
+ * @property {string} [cliIntro] - CLI intro message (stored for future use)
28
+ * @property {boolean} [debug=true] - show debug information
29
+ */
30
+
31
+ /** @const {RegExp} Validates agent names (lowercase a-z0-9_, min 2 chars). No uppercase, no hyphens. */
32
+ const validNameRegex = /^[a-z0-9_]{2,}$/;
33
+
34
+ class Agent {
35
+ /** @private @type {Prompt} */
36
+ #prompt;
37
+ /** @private @type {Session} */
38
+ #sessionStorage;
39
+ /** @private @type {string} */
40
+ #name = 'agent';
41
+ /** @private @type {string} */
42
+ #secret = '';
43
+ /** @private @type {string} */
44
+ #cachePath = '.cache/hello-dave';
45
+ /** @private @type {boolean} */
46
+ #debug = true;
47
+ /** @private @type {string} */
48
+ #model = '';
49
+ /** @private @type {number} */
50
+ #contextWindow = 0;
51
+ /** @private @type {string} */
52
+ #sysPrompt = '';
53
+ /** @private @type {string} */
54
+ #description = '';
55
+ /** @private @type {string} */
56
+ #cliIntro = '';
57
+
58
+ /**
59
+ * Creates a new Agent instance.
60
+ * Minimal core skeleton (no run(), no CLI parsing, no server/client logic).
61
+ * All configuration is passed via the constructor.
62
+ * Designed to be used by AgentLauncher and for clean handoff support.
63
+ *
64
+ * @param {AgentConfig} config
65
+ */
66
+ constructor(config = {}) {
67
+ const {
68
+ prompt,
69
+ api,
70
+ options = null,
71
+ contextWindow = 300000,
72
+ toolsetMode = null,
73
+ debug = true,
74
+ secret = "",
75
+ cachePath,
76
+ call_name,
77
+ call_description,
78
+ cliIntro
79
+ } = config;
80
+
81
+ if (secret && typeof secret === "string") {
82
+ this.#secret = Buffer.from(secret).toString("base64");
83
+ }
84
+
85
+ this.#cachePath = cachePath || this.#cachePath;
86
+ this.#sysPrompt = prompt;
87
+ this.#contextWindow = contextWindow;
88
+ if (options?.model) this.#model = options.model;
89
+
90
+ // call_name is required and becomes the agent name
91
+ if (!call_name || typeof call_name !== "string" || !validNameRegex.test(call_name)) {
92
+ throw new Error(`"call_name" is required and must match /^[a-z0-9_]{2,}$/ (lowercase letters, digits, underscore only; min 2 chars). File must be <name>.js.`);
93
+ }
94
+ this.#name = call_name; // name === call_name
95
+
96
+ // call_description is required and must be non-empty
97
+ if (!call_description || typeof call_description !== "string" || call_description.trim() === "") {
98
+ throw new Error(`"call_description" is required and must be a non-empty string`);
99
+ }
100
+ this.#description = call_description;
101
+
102
+ if (cliIntro !== undefined) {
103
+ this.#cliIntro = cliIntro;
104
+ }
105
+
106
+ let toolset;
107
+ if (toolsetMode) {
108
+ toolset = new ToolSet(toolsetMode);
109
+ }
110
+ if (debug) {
111
+ this.#debug = true;
112
+ }
113
+
114
+ this.#prompt = new Prompt(contextWindow);
115
+ this.#sessionStorage = new Session(this.#name, this.#prompt, this.#cachePath);
116
+ this.#prompt.add("system", prompt, true);
117
+ this.#prompt.setAdaptor(api, toolset, options);
118
+ return this;
119
+ }
120
+
121
+ // ──────────────────────────────────────────────────────────────────────────
122
+ // Static helpers for consistent agent authoring
123
+ // ──────────────────────────────────────────────────────────────────────────
124
+
125
+ /**
126
+ * Derives the canonical `call_name` from the agent's own filename.
127
+ * This makes the **filename the single source of truth** for the agent name.
128
+ *
129
+ * Recommended usage in every agent:
130
+ * const call_name = Agent.deriveCallName(import.meta.url);
131
+ *
132
+ * @param {string} importMetaUrl - Always pass `import.meta.url` from the agent module.
133
+ * @returns {string} The derived call_name (basename without extension)
134
+ * @throws {Error} If the derived name is invalid.
135
+ */
136
+ static deriveCallName(importMetaUrl) {
137
+ if (!importMetaUrl || typeof importMetaUrl !== 'string') {
138
+ throw new Error('Agent.deriveCallName(import.meta.url) is required. Pass import.meta.url from your agent file.');
139
+ }
140
+
141
+ const filePath = fileURLToPath(importMetaUrl);
142
+ const parsed = path.parse(filePath);
143
+ const name = parsed.name; // e.g. "code_agent" from "code_agent.js"
144
+
145
+ if (!validNameRegex.test(name)) {
146
+ throw new Error(
147
+ `Derived call_name "${name}" (from filename "${parsed.base}") is invalid.\n` +
148
+ `Agent filenames must match /^[a-z0-9_]{2,}$/ (lowercase a-z, 0-9, underscore only, min 2 chars).`
149
+ );
150
+ }
151
+
152
+ return name;
153
+ }
154
+
155
+ /**
156
+ * Loads the external system prompt for "extended" agents.
157
+ *
158
+ * Looks for a pure Markdown sibling prompt file in the same directory as the
159
+ * calling agent. The filename is always `<call_name>.prompt.md`, where
160
+ * `call_name` is derived from the calling agent filename.
161
+ *
162
+ * Two common patterns:
163
+ *
164
+ * 1. Simple one-file agent (internal prompt):
165
+ * const prompt = `You are ...`; // define inline
166
+ *
167
+ * 2. Extended agent (external Markdown prompt):
168
+ * const prompt = await Agent.loadPrompt(import.meta.url);
169
+ * // Automatically loads ./<call_name>.prompt.md as UTF-8 text.
170
+ *
171
+ * @param {string} importMetaUrl - Pass `import.meta.url` from the agent module.
172
+ * @param {Object} [options]
173
+ * @param {boolean} [options.required=true] - If true, throws when no external prompt file is found.
174
+ * @param {string|null} [options.fallback=null] - Prompt to return if no external file is found and required=false.
175
+ * @returns {Promise<string>} The Markdown prompt content (trimmed).
176
+ * @throws {Error} When required=true and no prompt file can be found, or when the prompt file cannot be read.
177
+ */
178
+ static async loadPrompt(importMetaUrl, options = {}) {
179
+ const { required = true, fallback = null } = options;
180
+
181
+ const call_name = this.deriveCallName(importMetaUrl);
182
+
183
+ const filePath = fileURLToPath(importMetaUrl);
184
+ const dir = path.dirname(filePath);
185
+ const promptPath = path.join(dir, `${call_name}.prompt.md`);
186
+
187
+ if (fs.existsSync(promptPath)) {
188
+ try {
189
+ const content = fs.readFileSync(promptPath, 'utf8');
190
+ return content.trim();
191
+ } catch (err) {
192
+ throw new Error(
193
+ `Failed to load prompt for "${call_name}" from ${promptPath}: ${err.message}`
194
+ );
195
+ }
196
+ }
197
+
198
+ if (required) {
199
+ throw new Error(
200
+ `External prompt file not found for agent "${call_name}".\n` +
201
+ `Expected: ${promptPath}\n\n` +
202
+ `For "simple" one-file agents, define the prompt as a template literal inside the .js file instead.\n` +
203
+ `For "extended" agents, create a pure Markdown sibling file named ${call_name}.prompt.md.`
204
+ );
205
+ }
206
+
207
+ return fallback;
208
+ }
209
+
210
+ // ──────────────────────────────────────────────────────────────────────────
211
+
212
+ /**
213
+ * @returns {string}
214
+ */
215
+ info() {
216
+ const sys = this.#sysPrompt;
217
+ const sessionInfo = this.#sessionStorage.info();
218
+ const promptInfo = this.#prompt.info();
219
+ const info = `\nSESSION:\n${sessionInfo}\n\nPROMPT:\n\----\n${sys}\n\----\n${promptInfo}\n\nDESCRIPTION:\n${this.#description}`;
220
+ return info;
221
+ }
222
+
223
+ /**
224
+ * Direct synchronous call to the underlying Prompt.
225
+ * @param {string} input - User input.
226
+ * @returns {Promise<string>} Response.
227
+ */
228
+ async directCall(input) {
229
+ const res = await this.#prompt.call(input);
230
+ return res;
231
+ }
232
+
233
+ /** @returns {Prompt} Underlying Prompt instance. */
234
+ getPrompt() { return this.#prompt; }
235
+
236
+ /** @returns {Session} Underlying Session instance (for CLI and handoff support). */
237
+ getSession() { return this.#sessionStorage; }
238
+
239
+ /** @returns {ToolSet|null} Toolset if configured. */
240
+ getToolset() { return this.#prompt.toolset; }
241
+
242
+ /** @returns {string} Agent name (from call_name). */
243
+ get name() { return this.#name; }
244
+
245
+ /** @returns {string} Agent description. */
246
+ get description() { return this.#description; }
247
+
248
+ /** @returns {string} CLI intro (if provided). */
249
+ get cliIntro() { return this.#cliIntro; }
250
+
251
+ /** @returns {string} Base64-encoded secret (if provided). */
252
+ get secret() { return this.#secret; }
253
+
254
+ /** @returns {string} Cache path. */
255
+ get cachePath() { return this.#cachePath; }
256
+
257
+ /** @returns {number} Context window size. */
258
+ get contextWindow() { return this.#contextWindow; }
259
+
260
+ /** @returns {string} Model name (if set via options). */
261
+ get model() { return this.#model; }
262
+
263
+ destructor() {
264
+ try {
265
+ this.getPrompt().removeAllListeners();
266
+ } catch (e) {};
267
+ }
268
+
269
+ }
270
+
271
+ export default Agent;
@@ -0,0 +1,284 @@
1
+ import { Prompt, Cli, AgentClient, AgentServer, API, ToolSet, Session, env } from './index.js';
2
+
3
+ /**
4
+ * @typedef {import("./API/x.ai/responses.js").XAIOptions} XAIOptions
5
+ * @typedef {import("./API/openai.com/reponses/text.js").OAOptions} OAOptions
6
+ * @typedef {import("./API/anthropic.com/text.js").ANTHOptions} ANTHOptions
7
+ */
8
+
9
+ /**
10
+ * @typedef {Object} AgentConfig
11
+ * @property {string} prompt - Initial system prompt
12
+ * @property {Function} api - Chat adaptor function from API.chat.* (e.g. API.chat.xai)
13
+ * @property {XAIOptions|OAOptions|Object} [options] - Model options
14
+ * @property {number} [contextWindow=300000]
15
+ * @property {"auto"|"required"|null} [toolsetMode]
16
+ * @property {boolean} [debug=false]
17
+ * @property {string} [secret] - Plain text secret (will be base64 encoded internally)
18
+ * @property {string} [cachePath=".cache/hello-dave"]
19
+ * @property {string} call_name - Tool name (required, validated /^[a-z_0-9]{2,}$/)
20
+ * @property {string} call_description - Description for the tool (required, non-empty string)
21
+ * @property {string} [cliIntro] - CLI intro message
22
+ * @property {boolean} [debug=true] - show debug information
23
+ */
24
+
25
+ /** @const {RegExp} Validates agent/tool names (lowercase a-z0-9_, min 2 chars). */
26
+ const validNameRegex = /^[a-z_0-9]{2,}$/;
27
+
28
+ class Agent {
29
+ /** @private @type {Prompt} */
30
+ #prompt;
31
+ /** @private @type {Session} */
32
+ #sessionStorage;
33
+ /** @private @type {string} */
34
+ #name = 'agent';
35
+ /** @private @type {string} */
36
+ #secret = '';
37
+ /** @private @type {string} */
38
+ #cachePath = '.cache/hello-dave';
39
+ /** @private @type {boolean} */
40
+ #debug = true;
41
+ /** @private @type {string} */
42
+ #model = '';
43
+ /** @private @type {number} */
44
+ #contextWindow = 0;
45
+ /** @private @type {string} */
46
+ #sysPrompt = '';
47
+ /** @private @type {string} */
48
+ #description = '';
49
+ /** @private @type {string} */
50
+ #cliIntro = '';
51
+
52
+ /**
53
+ * Creates a new Agent instance.
54
+ * All configuration is passed via the constructor.
55
+ *
56
+ * @param {AgentConfig} config
57
+ */
58
+ constructor(config = {}) {
59
+ const {
60
+ prompt,
61
+ api,
62
+ options = null,
63
+ contextWindow = 300000,
64
+ toolsetMode = null,
65
+ debug = true,
66
+ secret = "",
67
+ cachePath,
68
+ call_name,
69
+ call_description,
70
+ cliIntro
71
+ } = config;
72
+
73
+
74
+ if (secret && typeof secret === "string") {
75
+ this.#secret = Buffer.from(secret).toString("base64");
76
+ }
77
+
78
+ this.#cachePath = cachePath || this.#cachePath;
79
+ this.#sysPrompt = prompt;
80
+ this.#contextWindow = contextWindow;
81
+ if (options?.model) this.#model = options.model;
82
+
83
+ // call_name is required and becomes the agent name
84
+ if (!call_name || typeof call_name !== "string" || !validNameRegex.test(call_name)) {
85
+ throw new Error(`"call_name" is required and must match /^[a-z_0-9]{2,}$/`);
86
+ }
87
+ this.#name = call_name; // name === call_name
88
+
89
+ // call_description is required and must be non-empty
90
+ if (!call_description || typeof call_description !== "string" || call_description.trim() === "") {
91
+ throw new Error(`"call_description" is required and must be a non-empty string`);
92
+ }
93
+ this.#description = call_description;
94
+
95
+ if (cliIntro !== undefined) {
96
+ this.#cliIntro = cliIntro;
97
+ }
98
+
99
+ let toolset;
100
+ if (toolsetMode) {
101
+ toolset = new ToolSet(toolsetMode);
102
+ }
103
+ if (debug) {
104
+ this.#debug = true;
105
+ }
106
+
107
+ this.#prompt = new Prompt(contextWindow);
108
+ this.#sessionStorage = new Session(this.#name, this.#prompt, this.#cachePath);
109
+ this.#prompt.add("system", prompt, true);
110
+ this.#prompt.setAdaptor(api, toolset, options);
111
+ return this;
112
+ }
113
+
114
+ /**
115
+ * @private
116
+ * Launches interactive CLI mode.
117
+ * @param {string} description - CLI intro message.
118
+ * @returns {void}
119
+ * @throws {Error} No setup() called.
120
+ */
121
+ #startCli(description) {
122
+ if (!this.#prompt) throw new Error('Run setup first');
123
+ const cli = new Cli({ prompt: this.#prompt, session: this.#sessionStorage, description });
124
+ setTimeout(() => {
125
+ cli.start();
126
+ }, 1000);
127
+ }
128
+ /**
129
+ * @returns {string}
130
+ */
131
+ info() {
132
+ const sys = this.#sysPrompt;
133
+ const sessionInfo = this.#sessionStorage.info();
134
+ const promptInfo = this.#prompt.info();
135
+ const info = `\nSESSION:\n${sessionInfo}\n\nPROMPT:\n\----\n${sys}\n\----\n${promptInfo}\n\nDESCRIPTION:\n${this.#description}`;
136
+ return info;
137
+ }
138
+
139
+ /**
140
+ * @private
141
+ * Attaches this agent as a WS client to another server (for chaining agents).
142
+ * @param {string} name - Tool name.
143
+ * @param {string} description - Tool desc.
144
+ * @param {string} [url='ws://127.0.0.1:8000/ws'] - Target WS URL.
145
+ * @returns {AgentClient} New client instance.
146
+ * @throws {Error} No setup().
147
+ */
148
+ #attach(name, description, url = 'ws://127.0.0.1:8000/ws') {
149
+ if (!this.#prompt) throw new Error('Run setup first');
150
+ return new AgentClient({ prompt: this.#prompt, name, description, url, secret: this.#secret });
151
+ }
152
+
153
+ /**
154
+ * @private
155
+ * Enables server mode: Registers this agent for remote tool calls.
156
+ * Optionally attaches as client too.
157
+ * @param {string} name - Server/tool name.
158
+ * @param {string} [description=''] - Desc (auto from sysPrompt).
159
+ * @param {number} [port=8000] - Port.
160
+ * @returns {void}
161
+ * @throws {Error} No setup/toolset.
162
+ */
163
+ #enableServer(name, description = '', port = 8000) {
164
+ if (!this.#prompt) throw new Error('Run setup first');
165
+ if (!this.#prompt.toolset) throw new Error('No toolset defined');
166
+ const server = new AgentServer({
167
+ port,
168
+ name,
169
+ secret: this.#secret,
170
+ description,
171
+ prompt: this.#prompt,
172
+ session: this.#sessionStorage,
173
+ debug: this.#debug
174
+ });
175
+ this.#prompt.on('reset', () => {
176
+ server.resetAll();
177
+ });
178
+ }
179
+
180
+ /**
181
+ * Direct synchronous call to the underlying Prompt.
182
+ * @param {string} input - User input.
183
+ * @returns {Promise<string>} Response.
184
+ * @throws {Error} No setup.
185
+ */
186
+ async directCall(input) {
187
+ const res = await this.#prompt.call(input);
188
+ return res;
189
+ }
190
+
191
+ /** @returns {Prompt} Underlying Prompt instance. */
192
+ getPrompt() { return this.#prompt; }
193
+
194
+ /** @returns {ToolSet|null} Toolset if configured. */
195
+ getToolset() { return this.#prompt.toolset; }
196
+
197
+ /**
198
+ * High-level entry point.
199
+ * Parses CLI args (--serve, --connect, --secret, positional, --help) and decides between directCall or start() modes.
200
+ * Uses the call_name / call_description / cliIntro values set in the constructor.
201
+ *
202
+ * For direct-call (positional input), the result is automatically printed to stdout.
203
+ * This allows every agent script to simply end with `await agent.run();`
204
+ * without any extra boilerplate for capturing output.
205
+ *
206
+ * @returns {Promise<any>}
207
+ */
208
+ async run() {
209
+ const call_name = this.#name || "";
210
+ const call_description = this.#description || "";
211
+ const cliIntro = this.#cliIntro || "";
212
+
213
+ const args = process.argv.slice(2);
214
+ const hasHelp = args.includes("--help");
215
+ if (hasHelp) {
216
+ console.log("Usage: node agent.js [--serve PORT] [--connect URL] [--secret SECRET] [positional input] [--help] [--info]");
217
+ return;
218
+ }
219
+ const info = args.includes("--info");
220
+ if (info) {
221
+ console.log(this.info());
222
+ return;
223
+ }
224
+ const serveIndex = args.indexOf("--serve");
225
+ const connectIndex = args.indexOf("--connect");
226
+ const secretIndex = args.indexOf("--secret");
227
+
228
+ let servePort = null;
229
+ let connectUrl = null;
230
+ let cliSecret = null;
231
+
232
+ if (serveIndex !== -1 && args[serveIndex + 1]) servePort = parseInt(args[serveIndex + 1], 10);
233
+ if (connectIndex !== -1 && args[connectIndex + 1]) connectUrl = args[connectIndex + 1];
234
+ if (secretIndex !== -1 && args[secretIndex + 1]) cliSecret = args[secretIndex + 1];
235
+
236
+ // Handle --secret in run() (overrides constructor secret if provided)
237
+ if (cliSecret && typeof cliSecret === "string") {
238
+ this.#secret = Buffer.from(cliSecret).toString("base64");
239
+ }
240
+
241
+ // Positional argument → directCall (one-shot mode)
242
+ const positional = args.filter(a => !a.startsWith("--"));
243
+ const input = positional.length > 0 ? positional[0] : null;
244
+ if (input && !servePort && !connectUrl) {
245
+ const result = await this.#prompt.call(input);
246
+ if (result != null) {
247
+ console.log(result);
248
+ }
249
+ return result;
250
+ }
251
+
252
+ // === Combined start() logic ===
253
+ const toolName = call_name || this.#name;
254
+ if (!validNameRegex.test(toolName)) {
255
+ throw new Error(`Invalid "toolName": "${toolName}". Must match /^[a-z_0-9]{2,}$/`);
256
+ }
257
+
258
+ let finalDescription = call_description;
259
+ if (!finalDescription && this.#sysPrompt) {
260
+ finalDescription = this.#sysPrompt.slice(0, 100).trim() + "...";
261
+ }
262
+
263
+ let finalCliIntro = cliIntro;
264
+ if (!finalCliIntro) {
265
+ const introStr = `${this.#name} ${this.#model || "unknown-model"} - context: ${this.#contextWindow}`;
266
+ finalCliIntro = introStr.trim();
267
+ }
268
+
269
+ if (servePort) {
270
+ console.log(toolName);
271
+ console.log(finalDescription);
272
+ this.#enableServer(toolName, finalDescription, servePort);
273
+ if (connectUrl) {
274
+ this.#attach(toolName, finalDescription, connectUrl);
275
+ }
276
+ } else if (connectUrl) {
277
+ this.#attach(toolName, finalDescription, connectUrl);
278
+ } else {
279
+ this.#startCli(finalCliIntro);
280
+ }
281
+ }
282
+ }
283
+
284
+ export default Agent;