@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.
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 -1
  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 -1
  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 -17
  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,415 +0,0 @@
1
- /**
2
- * [api documentation](https://docs.x.ai/docs/overview)
3
- */
4
- import { GLOBAL } from '../../fafs.js'
5
- import { request as doRequest } from '@j-o-r/apiserver';
6
- import { sleep } from '@j-o-r/sh';
7
- /**
8
- * @typedef {import('../../Prompt.js').default} Prompt
9
- * @typedef {import('../../ToolSet.js').default} ToolSet
10
- */
11
- /**
12
- * @typedef {"grok-4"|"grok-3"|"grok-3-mini"|"grok-3-fast"|"grok-3-mini-fast"} grokModels
13
- * @typedef {"low"|"high"} grokReason
14
- */
15
- /**
16
- * @typedef {Object} SearchParameters
17
- * @property {"on"|"auto"} mode
18
- * @property {string} [from_date] ISO-8601 date (YYYY-MM-DD)
19
- * @property {string} [to_date] ISO-8601 date (YYYY-MM-DD)
20
- * @property {(RssSource|WebSource|NewsSource|XSource)[]} [sources]
21
- *
22
- * @typedef {Object} RssSource
23
- * @property {"rss"} type
24
- * @property {string[]} links
25
- *
26
- * @typedef {Object} WebSource
27
- * @property {"web"} type
28
- * @property {string} [country] ISO-3166-1 alpha-2
29
- * @property {boolean} [safe_search=true]
30
- * @property {string[]} [allowed_websites] // cannot coexist with excluded_websites
31
- * @property {string[]} [excluded_websites]
32
- *
33
- * @typedef {Object} NewsSource
34
- * @property {"news"} type
35
- * @property {string} [country]
36
- * @property {boolean} [safe_search=true]
37
- * @property {string[]} [allowed_websites]
38
- * @property {string[]} [excluded_websites]
39
- *
40
- * @typedef {Object} XSource
41
- * @property {"x"} type
42
- */
43
- /**
44
- * @typedef {object} XRequest
45
- * @property {XOptions} body
46
- * @property {Headers} headers
47
- * @property {string} url -
48
- */
49
- /**
50
- * @typedef {Object} XFunctionCall
51
- * @property {string} name - The name of the function to call.
52
- * @property {string} arguments - The arguments for the function call in JSON string format.
53
- * @typedef {Object} XToolCall
54
- * @property {string} id - The unique identifier for the tool call.
55
- * @property {string} type - The type of the tool call, e.g., "function".
56
- * @property {XFunctionCall} function - The function call details.
57
- */
58
- /**
59
- * @typedef {Object} XFunctionResponse
60
- * @property {string} role - must be 'tool'
61
- * @property {string} content - The arguments for the function call in JSON string format.
62
- * @property {string} tool_call_id - The unique identifier for the tool call.
63
- */
64
- /**
65
- * @typedef {Object} XMessageResponse
66
- * @property {number} index - The index of the message.
67
- * @property {Object} message - The message details.
68
- * @property {string} message.role - The role of the sender (e.g., "assistant").
69
- * @property {string} message.content - The content of the message.
70
- * @property {string} [message.reasoning_content] - The reasoning behind the response.
71
- * @property {?string} message.refusal - Any refusal content, if applicable.
72
- * @property {string} finish_reason - The reason for finishing (e.g., "stop").
73
- * @property {XToolCall[]} tools_calls - The reason for finishing (e.g., "stop").
74
- */
75
-
76
- /**
77
- * https://docs.x.ai/api/endpoints#chat-completions
78
- * @typedef {object} XOptions
79
- * @property {grokModels} [model] - What model to use
80
- * @property {Array<*>} [messages] - the actual prompt
81
- * @property {XToolCall[]} [tools], callable functions
82
- * @property {string} [tool_choice] - Default 'none' when no function, 'auto' when present, or required
83
- * @property {Object} [response_format] - Response format
84
- * @property {string} [response_format.type] - The role of the sender (e.g., "assistant").
85
- * @property {number} [temperature] - What sampling temperature to use, between 0 and 2.
86
- * @property {number} [max_completion_tokens] - The maximum number of tokens allowed for the generated answer.
87
- * @property {number} [presence_penalty] - Number between -2.0 and 2.0.
88
- * @property {number} [frequency_penalty] - Number between -2.0 and 2.0.
89
- * @property {number} [top_p] - Number between -2.0 and 2.0.
90
- * @property {number} [n] - How many chat completion choices to generate for each input message. default 1
91
- * @property {boolean} [stream] - Chunk/stream request default null
92
- * @property {boolean} [parallel_tool_calls] - Multiple calls at once
93
- * @property {grokReason} [reasoning_effort] - Who i am
94
- * @property {string} [user] - Who i am
95
- * @property {number} [seed] - see options
96
- * @property {SearchParameters} [search_parameters] - live search
97
- * @property {string[]} [stop] - Up to 4 sequences where the API will stop generating further tokens.
98
- */
99
- const API_URL = 'https://api.x.ai/v1/chat/completions';
100
- /** @type {XOptions} */
101
- const defaultSettings = {
102
- model: 'grok-4',
103
- messages: [
104
- {
105
- role: 'system',
106
- content: 'Be precise and concise'
107
- }
108
- ],
109
- temperature: 0.2,
110
- // response_format: { type: 'text' },
111
- parallel_tool_calls: true,
112
- // reasoning_effort: null,
113
- // max_completion_tokens: 2000,
114
- // presence_penalty: 0,
115
- // frequency_penalty: 0,
116
- // top_p: 1,
117
- // stop: []
118
- };
119
-
120
-
121
- /**
122
- * Get the default headers
123
- * @returns {object}
124
- */
125
- const getHeaders = () => {
126
- if (!process.env['XAIKEY']) {
127
- throw new Error('Missing XAIKEY! export XAIKEY=<XAIKEY>')
128
- }
129
- const KEY = process.env['XAIKEY'];
130
- return {
131
- 'Content-Type': 'application/json',
132
- 'Authorization': `Bearer ${KEY}`
133
- }
134
- }
135
- /**
136
- * Get the text from content
137
- * @param {import('../../Prompt.js').Content[]} content
138
- * @returns {string}
139
- */
140
- function getTextFromContent(content) {
141
- let res = '';
142
- let i = 0;
143
- const len = content.length;
144
- for (; i < len; i++) {
145
- if (content[i].type === 'text') {
146
- // @ts-ignore
147
- res = `${res}\n${content[i].text} `
148
- }
149
- }
150
- return res.trim();
151
- }
152
- /**
153
- * Convert messages to XAI
154
- * @param {Prompt} prompt
155
- * @returns {Array<*>}
156
- */
157
- const generateHistory = (prompt) => {
158
- const messages = prompt.messages
159
- const result = [];
160
- let i = 0;
161
- const len = messages.length;
162
- for (; i < len; i++) {
163
- const msg = messages[i];
164
- const role = msg.role;
165
- if (['system', 'assistant', 'user'].includes(role)) {
166
- // Get the text
167
- const text = getTextFromContent(msg.content);
168
- const newMesg = {
169
- role,
170
- content: text,
171
- refusal: null
172
- };
173
- // Add initial toolcalls
174
- const toolCalls = msg.content
175
- .filter(item => item.type === 'function_request')
176
- .map(req => ({
177
- // @ts-ignore
178
- id: req.function_request.id,
179
- type: "function",
180
- function: {
181
- // @ts-ignore
182
- arguments: req.function_request.parameters,
183
- // @ts-ignore
184
- name: req.function_request.name,
185
- }
186
- }));
187
- if (toolCalls.length > 0) {
188
- newMesg.tool_calls = toolCalls;
189
- }
190
- result.push(newMesg);
191
- } else if (role === 'tool') {
192
- const content = msg.content;
193
- let it = 0;
194
- const lent = content.length;
195
- for (; it < lent; it++) {
196
- /** @type {import('../..//Prompt.js').FunctionResponse} */
197
- // @ts-ignore
198
- const item = content[it];
199
- if (item.type === 'function_response') {
200
- // Add tool function response
201
- result.push({
202
- role: 'tool',
203
- name: item.function_response.name,
204
- call_id: item.function_response.call_id,
205
- content: item.function_response.response
206
- });
207
- }
208
- }
209
- }
210
- }
211
- return result;
212
- }
213
-
214
- /**
215
- * Convert a toolset to something openai understands
216
- * @param {ToolSet} toolset
217
- * @returns {XToolCall[]}
218
- */
219
- const generateXAIToolCalls = (toolset) => {
220
- const list = toolset.list();
221
- /** @type {XToolCall[]} */
222
- const result = [];
223
- list.forEach((item) => {
224
- result.push({
225
- type: 'function', function: {
226
- name: item.name,
227
- // @ts-ignore
228
- description: item.description,
229
- parameters: item.parameters
230
- }
231
- })
232
- });
233
- return result;
234
- }
235
-
236
- /**
237
- * Create an anthropic request
238
- * @param {Prompt} prompt
239
- * @param {ToolSet|void} [tools]
240
- * @param {XOptions} [opts] overwrite default request settings
241
- * @param {object} [hdrs] - optional headers to pass
242
- * @returns {XRequest}
243
- * @throws {Error}
244
- */
245
- const generateRequest = (prompt, tools, opts = {}, hdrs = {}) => {
246
- /** @type {XOptions} */
247
- const body = { ...defaultSettings, ...opts };
248
- const headers = { ...getHeaders(), ...hdrs };
249
- // Sanity check
250
- // basePromptCheck(prompt);
251
- // body.messages = generateXAIMessages(prompt) || [];
252
- body.messages = generateHistory(prompt);
253
- if (body.messages.length == 0) {
254
- throw new Error('No messages found');
255
- }
256
- body.tools = (tools) ? generateXAIToolCalls(tools) : [];
257
- body.tool_choice = (tools) ? tools.toolChoice : '';
258
- if (body.tools.length === 0) {
259
- delete body.tools;
260
- delete body.tool_choice;
261
- }
262
- return { body, headers, url: API_URL };
263
- };
264
-
265
- // RESPONSE PARSER
266
-
267
- /**
268
- * @typedef {Object} XResponse
269
- * @property {string} id - The unique identifier for the response.
270
- * @property {string} object - The type of object returned, typically "chat.completion".
271
- * @property {number} created - The timestamp of when the response was created.
272
- * @property {string} model - The model used to generate the response.
273
- * @property {Array<XChoice>} choices - An array of choice objects containing the response details.
274
- * @property {XUsage} usage - An object containing token usage information.
275
- * @property {string} system_fingerprint - The system fingerprint for the response.
276
- * @property {Object} http_headers - HTTP headers associated with the response.
277
- */
278
- /**
279
- * @typedef {Object} XChoice
280
- * @property {number} index - The index of the choice in the response.
281
- * @property {XMessageResponse} message - The message object containing the role and content.
282
- * @property {null} logprobs - Log probabilities, typically null.
283
- * @property {string} finish_reason - The reason why the response finished.
284
- */
285
- /**
286
- * @typedef {Object} XUsage
287
- * @property {number} prompt_tokens - The number of tokens in the prompt.
288
- * @property {number} completion_tokens - The number of tokens in the completion.
289
- * @property {number} total_tokens - The total number of tokens used.
290
- */
291
-
292
- const parseMessages = (response, prompt) => {
293
- /** @type {import('../../Prompt.js').FunctionRequest[]} */
294
- const function_requests = [];
295
- const messages = response.choices;
296
- if (response.citations && response.citations.length > 0) {
297
- const cits = response.citations.map((item, index) => `${index + 1}. ${item}`).join('\n');
298
- prompt.add('log', cits);
299
- }
300
- let i = 0;
301
- const len = messages.length;
302
- for (; i < len; i++) {
303
- const msg = messages[i];
304
- if (msg.message.reasoning_content) {
305
- // Add reasoning content
306
- // Handy for debugging prompts
307
- prompt.add('reasoning', msg.message.reasoning_content);
308
- }
309
- if (msg.refusal) {
310
- // Wrong question in relation to policies
311
- prompt.add('assistant', msg.refusal);
312
- // console.log(msg);
313
- continue;
314
- }
315
- // Add a text message
316
- if (
317
- msg.message.role === 'assistant' &&
318
- typeof msg.message.content === 'string') {
319
- prompt.add('assistant', msg.message.content);
320
- }
321
- // Get function request
322
- if (msg.message.tool_calls) {
323
- for (const toolCall of msg.message.tool_calls) {
324
- function_requests.push(
325
- {
326
- type: 'function_request',
327
- function_request: {
328
- name: toolCall.function.name,
329
- id: toolCall.id,
330
- call_id: toolCall.id,
331
- parameters: toolCall.function.arguments
332
- }
333
- }
334
- );
335
- }
336
- }
337
- }
338
- if (function_requests.length > 0) {
339
- prompt.addMultiModal('assistant', function_requests);
340
- }
341
- }
342
-
343
- /**
344
- * Process an openai response
345
- * @param {number} duration - the time is MS before and after the request
346
- * @param {import('@j-o-r/apiserver/types/request.js').FetchResponseObject} res
347
- * @param {Prompt} prompt
348
- * @param {ToolSet} [toolset]
349
- * @returns {Promise<void>}
350
- * @throws {Error}
351
- */
352
- const parseResponse = async (duration, prompt, res, toolset) => {
353
- // @todo - Put some effort in the parsing, this is quick and dirty
354
- if (res.status !== 200) {
355
- new Error(`${res.status}: res.response`)
356
- }
357
- const record = prompt.templateRecord();
358
- record.model = res.response.model;
359
- record.id = res.response.id;
360
- record.environment = 'openai';
361
- record.isoDate = new Date(res.response.created * 1000).toISOString();
362
- record.tokensIn = res.response.usage.prompt_tokens;
363
- record.tokensOut = res.response.usage.completion_tokens;
364
- record.duration = duration;
365
- prompt.addRecord(record);
366
- parseMessages(res.response, prompt);
367
- if (toolset) {
368
- // Inspect for function_request and execute if so
369
- await toolset.execute(prompt);
370
- }
371
- }
372
-
373
- /**
374
- * Do a request
375
- * @param {Prompt} prompt
376
- * @param {ToolSet|null} toolset
377
- * @param {XOptions} [options]:
378
- * @param {number} [counter] - leave empty this counts the number of requests when doing recursive request calls
379
- * @return {Promise<import('../../Session.js').Message>}
380
- */
381
- async function request(prompt, toolset = null, options, counter = 0) {
382
- // Max number of recurusive calls
383
- const counterMax = GLOBAL.max_recursive_requests;
384
- const start = new Date().getTime();
385
- // Generate the request
386
- const { url, headers, body } = generateRequest(prompt, toolset, options);
387
- prompt.emit(prompt.EVENTS.http_request, {url, counter, body});
388
- const res = await doRequest(url, 'POST', headers, body);
389
- prompt.emit(prompt.EVENTS.http_response, res);
390
- if (res.status !== 200) {
391
- throw new Error(`${res.status}: ${JSON.stringify(res.response, null, ' ')}`)
392
- }
393
- const duration = new Date().getTime() - start;
394
- await parseResponse(duration, prompt, res, toolset);
395
- counter = 1 + counter;
396
- if (counter >= counterMax) {
397
- throw new Error('Max number of recursive calls reached');
398
- }
399
- const lastMessage = prompt.getLastMessage();
400
- if (!lastMessage) throw new Error('No message found');
401
- const role = lastMessage.role;
402
- if (role === 'tool') {
403
- // need to do another roundtrip to include the function_responses
404
- // Go easy on the speed, do not hit limits to soon
405
- await sleep('30s');
406
- return request(prompt, toolset, options, counter)
407
- } else {
408
- return lastMessage
409
- }
410
- }
411
- export {
412
- generateRequest,
413
- parseResponse,
414
- request
415
- }
@@ -1,248 +0,0 @@
1
- import { WebSocketClient } from '@j-o-r/apiserver';
2
- /**
3
- * @module lib/AgentClient
4
- * @exports default AgentClient
5
- * Websocket client for AI agent sessions, wrapping a Prompt/ToolSet instance with robust queue-based message handling, auto-reconnect, and epoch-based resets.
6
- */
7
-
8
- /**
9
- * @typedef {import('./API/openai.com/reponses/text.js').request} OARequest
10
- * @typedef {import('./API/x.ai/text.js').request} XRequest
11
- * @typedef {import('./API/anthropic.com/text.js').request} ANTHRequest
12
- *
13
- * @typedef {import('./API/x.ai/text.js').XOptions} XOptions
14
- * @typedef {import('./API/openai.com/reponses/text.js').OAOptions} OAOptions
15
- * @typedef {import('./API/anthropic.com/text.js').ANTHOptions} ANTHOptions
16
- *
17
- * @typedef {import('./Prompt.js').default} Prompt
18
- * @typedef {import('./ToolSet.js').default} ToolSet
19
- */
20
- /**
21
- * @typedef {Object} WSOptions
22
- * @property {Prompt} prompt - The prompt session instance.
23
- * @property {ToolSet} [toolset] - Optional toolset for the prompt.
24
- * @property {string} description - Custom introduction message for the agent.
25
- * @property {string} name - Logical name (e.g., 'search', 'code', 'os').
26
- * @property {string} secret - Websocket server access secret (must match server).
27
- * @property {string} [url='ws://127.0.0.1:8000/ws'] - WebSocket URL.
28
- * @property {number} [intervalMs=2000] - Polling interval for queue processing (ms).
29
- * @example { prompt, name: 'code-agent', secret: 'abc123', url: 'ws://localhost:8001/ws' }
30
- */
31
-
32
- /**
33
- * AgentClient: WebSocket client wrapper for AI agent sessions.
34
- * Combines Prompt/ToolSet with WS communication for bidirectional queries, responses, tool calls, and resets.
35
- * Features: Sequential message queue (one-at-a-time processing), auto-reconnect on close, epoch invalidation for resets.
36
- * Logs events via console; emits via underlying Prompt (tool_request, tool_error, etc.).
37
- * @emits tool_request - When tools are requested (via #prompt).
38
- * @emits tool_error - When tools fail (via #prompt).
39
- * @example
40
- * const client = new AgentClient({
41
- * prompt: myPrompt,
42
- * name: 'code',
43
- * description: 'Code execution agent',
44
- * secret: 'mysecret',
45
- * intervalMs: 1000
46
- * });
47
- */
48
- class AgentClient {
49
- /** @private @type {Prompt} */
50
- #prompt;
51
- /** @private @type {string} */
52
- #description = '';
53
- /** @private @type {string} */
54
- #name;
55
- /** @private @type {string} */
56
- #secret = '';
57
- /** @private @type {import('@j-o-r/apiserver').WebSocketClient} */
58
- #ws;
59
- /** @private @type {string} */
60
- #url = 'ws://127.0.0.1:8000/ws';
61
-
62
- /** @private @type {Array&lt;Object&gt;} Message queue for sequential processing. */
63
- #queue = [];
64
- /** @private @type {boolean} Flag to prevent reentrant processing. */
65
- #processing = false;
66
- /** @private @type {number} Epoch counter; increments on reset to invalidate in-flight work. */
67
- #epoch = 0;
68
-
69
- /** @private @type {NodeJS.Timeout|null} Interval ID for scheduler. */
70
- #intervalId = null;
71
- /** @private @type {number} Queue polling interval (ms). */
72
- #intervalMs = 2000;
73
-
74
- /**
75
- * Initializes the AgentClient with configuration options.
76
- * Registers Prompt events for logging and starts the WebSocket connection.
77
- * @param {WSOptions} options - Configuration object.
78
- * @throws {TypeError} If required options (e.g., prompt, secret) are invalid/missing.
79
- * @example See WSOptions.
80
- */
81
- constructor(options) {
82
- if (options.url) this.#url = options.url;
83
- this.#prompt = options.prompt;
84
- this.#description = options.description;
85
- this.#name = options.name;
86
- this.#secret = options.secret;
87
- if (typeof options.intervalMs === 'number') this.#intervalMs = Math.max(1, options.intervalMs);
88
- this.#registerPromptEvents();
89
- this._start();
90
- }
91
-
92
- /**
93
- * @private
94
- * Registers Prompt events for logging tool requests/errors.
95
- * @returns {void}
96
- */
97
- #registerPromptEvents() {
98
- const events = Object.keys(this.#prompt.EVENTS);
99
- events.forEach((evt) => {
100
- this.#prompt.on(evt, (_msg) => {
101
- console.log(`** ${this.#name} e:${evt}**`);
102
- if (evt === 'tool_request') {
103
- console.log(`tool execute: ${_msg.name} ${_msg.call_id}`);
104
- } else if (evt === 'tool_error') {
105
- console.log(`tool error:`);
106
- console.log(JSON.stringify(_msg, null, ' '));
107
- }
108
- });
109
- });
110
- }
111
-
112
- /**
113
- * @private
114
- * Starts the WebSocket connection, sends introduction, sets up handlers.
115
- * Auto-retries on close after 5s.
116
- * @returns {void}
117
- */
118
- _start() {
119
- const url = this.#url + '?wssrc_id=' + this.#secret;
120
- this.#ws = new WebSocketClient(url);
121
- this.#ws.onopen = () => {
122
- this.#ws.send(JSON.stringify({
123
- action: 'agent_introduction',
124
- name: this.#name,
125
- content: this.#description,
126
- id: new Date().getTime()
127
- }));
128
- console.log('introduction_send:>>');
129
- console.log(this.#name);
130
- console.log(this.#description);
131
- };
132
- this.#ws.onmessage = (m) => {
133
- this.incoming(m);
134
- };
135
- this.#ws.onclose = () => {
136
- console.error('CLOSE: Lost Connection, retry in 5 seconds');
137
- this.#stopInterval();
138
- setTimeout(() => {
139
- this._start();
140
- }, 5000);
141
- };
142
- this.#ws.onerror = (e) => {};
143
- }
144
-
145
- /**
146
- * Enqueues an incoming WebSocket message and starts interval processing if needed.
147
- * Immediately handles 'reset' actions (clears queue, resets prompt, increments epoch).
148
- * @param {MessageEvent} e - Raw WebSocket message event.
149
- * @returns {void}
150
- * @throws {SyntaxError} If JSON parse fails.
151
- */
152
- incoming(e) {
153
- let message;
154
- try {
155
- message = JSON.parse(e.data);
156
- } catch (err) {
157
- console.error('Invalid JSON in incoming message');
158
- return;
159
- }
160
- if (message?.action === "reset") {
161
- console.log("<RESET:incoming>");
162
- this.#epoch++;
163
- this.#queue = [];
164
- this.#stopInterval();
165
- this.#prompt.reset();
166
- return;
167
- }
168
- this.#queue.push(message);
169
- this.#startInterval();
170
- }
171
-
172
- /**
173
- * @private
174
- * Starts the interval ticker to process one queued message per tick.
175
- * Idempotent; skips if already running.
176
- * @returns {void}
177
- */
178
- #startInterval() {
179
- if (this.#intervalId) return;
180
- this.#intervalId = setInterval(() => {
181
- void this.#processOne();
182
- }, this.#intervalMs);
183
- }
184
-
185
- /**
186
- * @private
187
- * Stops the processing interval.
188
- * @returns {void}
189
- */
190
- #stopInterval() {
191
- if (this.#intervalId) {
192
- clearInterval(this.#intervalId);
193
- this.#intervalId = null;
194
- }
195
- }
196
-
197
- /**
198
- * @private
199
- * Processes exactly one queued message (guards reentrancy).
200
- * Stops interval if queue empty post-process.
201
- * @returns {Promise&lt;void&gt;}
202
- */
203
- async #processOne() {
204
- if (this.#processing) return;
205
- const message = this.#queue.shift();
206
- if (!message) {
207
- this.#stopInterval();
208
- return;
209
- }
210
- this.#processing = true;
211
- try {
212
- const epochAtStart = this.#epoch;
213
- await this.#handleMessage(message, epochAtStart);
214
- } finally {
215
- this.#processing = false;
216
- if (this.#queue.length === 0) this.#stopInterval();
217
- }
218
- }
219
-
220
- /**
221
- * @private
222
- * Handles a single enqueued message (query/response/reset).
223
- * Sends responses/errors back via WS.
224
- * @param {Object} message - Parsed message.
225
- * @param {number} epochAtStart - Epoch at start; aborts if changed.
226
- * @returns {Promise&lt;void&gt;}
227
- */
228
- async #handleMessage(message, epochAtStart) {
229
- if (message.action === 'agent_query') {
230
- const id = message.id;
231
- try {
232
- console.log('IN:>>\n' + message.content);
233
- const content = await this.#prompt.call(message.content);
234
- console.log('OUT:>>\n' + content);
235
- this.#ws.send(JSON.stringify({ action: 'agent_response', content, id }));
236
- } catch (err) {
237
- this.#ws.send(JSON.stringify({ action: 'agent_error', content: String(err?.message || err), id }));
238
- }
239
- } else if (message.action === 'reset') {
240
- console.log('<RESET>');
241
- this.#queue = [];
242
- this.#epoch++;
243
- this.#prompt.reset();
244
- }
245
- }
246
- }
247
-
248
- export default AgentClient;