@j-o-r/hello-dave 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +216 -56
- package/README.md.bak +481 -0
- package/README.md.bak.1774780058 +338 -0
- package/README.md.bak2 +455 -0
- package/docs.bak.1774780058/agent-manager.md +167 -0
- package/docs.bak.1774780058/agent-manager.md.bak +137 -0
- package/docs.bak.1774780058/agent-manager.md.bak2 +157 -0
- package/docs.bak.1774780058/codeserver-pattern.md +191 -0
- package/docs.bak.1774780058/path-resolution-best-practices.md +104 -0
- package/docs.bak.1774780058/project-overview.md +67 -0
- package/docs.bak.1774780058/project-overview.md.bak +67 -0
- package/docs.bak.1774780058/prompt-class.md +141 -0
- package/docs.bak.1774780058/prompt-class.md.bak +142 -0
- package/docs.bak.1774780058/tools-syntax-validation.md +121 -0
- package/docs.bak.1774780058/tools-syntax-validation.md.bak2 +125 -0
- package/docs.bak.1774780058/tools-syntax-validation.md.bak3 +125 -0
- package/docs.bak.1774780058/tools-syntax-validation.md.bak4 +106 -0
- package/docs.bak.1774780058/tools-syntax-validation.md.bak_path +106 -0
- package/docs.bak.1774780058/toolset.md +164 -0
- package/docs.bak.1774780058/toolset.md.bak +94 -0
- package/docs.bak.1774780058/toolset.md.bak3 +161 -0
- package/docs.bak.1774780058/toolset.md.bak4 +161 -0
- package/docs.bak.1774780058/toolset.md.bak5 +161 -0
- package/docs.bak.1774780058/toolset.md.bak6 +163 -0
- package/docs.bak.1774780058/toolset.md.bak_path +163 -0
- package/docs.bak.1774780058/toolset.md.bak_syntax +161 -0
- package/docs.bak.1774780058/xai-responses.md +111 -0
- package/docs.bak.1774780058/xai-responses.md.bak +107 -0
- package/docs.bak.1774780058/xai-responses.md.bak2 +107 -0
- package/docs.bak.1774780058/xai_collections.md +106 -0
- package/examples/ask_agent.js +137 -0
- package/examples/code_agent.js +149 -0
- package/examples/coderev_agent.js +136 -0
- package/examples/codeserver.sh +47 -0
- package/examples/daisy_agent.js +170 -0
- package/examples/docs_agent.js +148 -0
- package/examples/gpt_agent.js +125 -0
- package/examples/grok_agent.js +132 -0
- package/examples/grok_agent.js.bak +98 -0
- package/examples/grok_agent.js.bak.2 +99 -0
- package/examples/grok_agent.js.bak.3 +1 -0
- package/examples/grok_agent.js.bak.4 +124 -0
- package/examples/grok_agent.js.bak.5 +1 -0
- package/examples/grok_agent.js.bak.6 +1 -0
- package/examples/memory_agent.js +152 -0
- package/examples/npm_agent.js +202 -0
- package/examples/npm_agent.js.bak.3 +2 -0
- package/examples/npm_agent.js.bak.4 +205 -0
- package/examples/npm_agent.js.bak.5 +1 -0
- package/examples/npm_agent.js.bak.6 +1 -0
- package/examples/prompt_agent.js +133 -0
- package/examples/readme_agent.js +148 -0
- package/examples/spawn_agent.js +293 -0
- package/examples/test_agent.js +187 -0
- package/examples/todo_agent.js +175 -0
- package/{examples/codeDave.js → examples.bak.1774780058/code_agent.js} +40 -6
- package/{examples/CodeServer → examples.bak.1774780058/codeserver.sh} +9 -5
- package/examples.bak.1774780058/memory_agent.js +112 -0
- package/{examples/spawndave.js → examples.bak.1774780058/spawn_agent.js} +29 -6
- package/examples.bak.1774780058/test_agent.js +162 -0
- package/{examples/todoDave.js → examples.bak.1774780058/todo_agent.js} +8 -2
- package/lib/API/x.ai/responses.js +7 -9
- package/lib/AgentManager.js +3 -2
- package/lib/AgentServer.js +23 -19
- package/lib/genericToolset.js +159 -26
- package/lib/genericToolset.js.bak_syntax +402 -0
- package/lib/wsCli.js +0 -1
- package/package.json +4 -5
- package/scenarios.bak.1774780058/data/eval_node_message.json +9 -0
- package/scenarios.bak.1774780058/data/hist_oa.json +66 -0
- package/scenarios.bak.1774780058/data/o3_response1.json +96 -0
- package/scenarios.bak.1774780058/data/oa_reasoning_parse.json +112 -0
- package/scenarios.bak.1774780058/data/tool_oa.json +96 -0
- package/scenarios.bak.1774780058/data/tool_xai.json +59 -0
- package/scenarios.bak.1774780058/data/tool_xai2.json +40 -0
- package/scenarios.bak.1774780058/data/xai-response-1.json +59 -0
- package/scenarios.bak.1774780058/data/xai-response-2.json +10 -0
- package/scenarios.bak.1774780058/data/xai_reasoning_tools_resp.json +59 -0
- package/scenarios.bak.1774780058/data/xai_search_response.json +58 -0
- package/scenarios.bak.1774780058/environment.js +10 -0
- package/scenarios.bak.1774780058/example.js +17 -0
- package/scenarios.bak.1774780058/genericToolset.test.js +182 -0
- package/scenarios.bak.1774780058/grok.js +113 -0
- package/scenarios.bak.1774780058/memory-tools.js +51 -0
- package/scenarios.bak.1774780058/openai-o3.js +137 -0
- package/scenarios.bak.1774780058/openai-prompt.js +155 -0
- package/scenarios.bak.1774780058/openai-session.js +148 -0
- package/scenarios.bak.1774780058/openai.js +102 -0
- package/scenarios.bak.1774780058/prompt.js +118 -0
- package/scenarios.bak.1774780058/promptFishbowl.js +76 -0
- package/scenarios.bak.1774780058/search.brave.com.js +25 -0
- package/scenarios.bak.1774780058/sh.js +15 -0
- package/scenarios.bak.1774780058/test-wsio.js +26 -0
- package/scenarios.bak.1774780058/testToolset.js +42 -0
- package/scenarios.bak.1774780058/toolset.js +16 -0
- package/scenarios.bak.1774780058/toolset.test.js +141 -0
- package/scenarios.bak.1774780058/write_file_syntax.test.js +145 -0
- package/scenarios.bak.1774780058/write_file_validation/README.md +30 -0
- package/scenarios.bak.1774780058/write_file_validation/bad.js +3 -0
- package/scenarios.bak.1774780058/write_file_validation/good.js +4 -0
- package/scenarios.bak.1774780058/write_file_validation/test.sh +43 -0
- package/scenarios.bak.1774780058/wsClient.js +69 -0
- package/scenarios.bak.1774780058/xai_responses.integration.test.js +57 -0
- package/scenarios.bak.1774780058/xai_responses.test.js +154 -0
- package/scenarios.bak.1774780058/xaicoll.js +50 -0
- package/scenarios.bak.1774780058/xaifiles.js +48 -0
- package/types/AgentManager.d.ts +4 -3
- package/utils/syntax_check.sh +61 -0
- package/utils/test.sh +46 -0
- /package/{examples/askDave.js → examples.bak.1774780058/ask_agent.js} +0 -0
- /package/{examples/coderev.js → examples.bak.1774780058/coderev_agent.js} +0 -0
- /package/{examples/daisy.js → examples.bak.1774780058/daisy_agent.js} +0 -0
- /package/{examples/docsDave.js → examples.bak.1774780058/docs_agent.js} +0 -0
- /package/{examples/gpt.js → examples.bak.1774780058/gpt_agent.js} +0 -0
- /package/{examples/grok.js → examples.bak.1774780058/grok_agent.js} +0 -0
- /package/{examples/npmDave.js → examples.bak.1774780058/npm_agent.js} +0 -0
- /package/{examples/promptDave.js → examples.bak.1774780058/prompt_agent.js} +0 -0
- /package/{examples/readmeDave.js → examples.bak.1774780058/readme_agent.js} +0 -0
package/lib/genericToolset.js
CHANGED
|
@@ -7,6 +7,12 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
9
|
|
|
10
|
+
// STANDARDIZED UTILS PATHS (hoisted constants for performance/consistency)
|
|
11
|
+
const utilsDir = path.resolve(__dirname, '..', 'utils');
|
|
12
|
+
const searchSessionsSh = path.join(utilsDir, 'search_sessions.sh');
|
|
13
|
+
const listSessionsSh = path.join(utilsDir, 'list_sessions.sh');
|
|
14
|
+
const syntaxCheckSh = path.join(utilsDir, 'syntax_check.sh');
|
|
15
|
+
|
|
10
16
|
const user = await env();
|
|
11
17
|
const environment = `
|
|
12
18
|
Name: ${user.name}
|
|
@@ -55,7 +61,7 @@ tools.add(
|
|
|
55
61
|
async (params) => {
|
|
56
62
|
let response = '';
|
|
57
63
|
try {
|
|
58
|
-
const delim = `JS_STDIN_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,6)}`;
|
|
64
|
+
const delim = `JS_STDIN_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
59
65
|
response = await SH`cat <<'${delim}' | node --input-type=module -
|
|
60
66
|
${params.script}
|
|
61
67
|
${delim}
|
|
@@ -170,31 +176,29 @@ ${delim}
|
|
|
170
176
|
}
|
|
171
177
|
);
|
|
172
178
|
tools.add(
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
'history_search',
|
|
180
|
+
`Search previous LLM chat sessions or list them hierarchically.
|
|
175
181
|
Example query: "(todo|task)" or "package.json".
|
|
176
182
|
Searches filenames & content in .cache/[app]/[prompt]/sessions/*.ndjson (case-insensitive regex, with context).
|
|
177
183
|
Omit query (or use empty string) to list sessions (see utils/list_sessions.sh).`,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
}
|
|
184
|
+
{
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
query: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
description: `Search query or regex (quoted for multi-word/regex). Omit or empty for list mode.`
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
required: []
|
|
193
|
+
},
|
|
194
|
+
async (params) => {
|
|
195
|
+
if (typeof params.query === 'string' && params.query.trim() !== '') {
|
|
196
|
+
const escapedQuery = bashEscape(params.query);
|
|
197
|
+
return await SH`${searchSessionsSh} "${escapedQuery}"`.run();
|
|
198
|
+
} else {
|
|
199
|
+
return await SH`${listSessionsSh}`.run();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
198
202
|
);
|
|
199
203
|
tools.add(
|
|
200
204
|
'read_file',
|
|
@@ -232,7 +236,7 @@ tools.add(
|
|
|
232
236
|
|
|
233
237
|
tools.add(
|
|
234
238
|
'write_file',
|
|
235
|
-
'Write raw content to a file strictly within the current working directory (CWD). Paths must be relative (no leading /, no ..). Content written as-is (no escaping).',
|
|
239
|
+
'Write raw content to a file strictly within the current working directory (CWD). Paths must be relative (no leading /, no ..). Content written as-is (no escaping). **AUTO-VALIDATES** JS/Python/Bash/JSON/etc. via `utils/syntax_check.sh`; chmod +x shebangs. Retries syntax errors force LLM fix.',
|
|
236
240
|
{
|
|
237
241
|
type: 'object',
|
|
238
242
|
properties: {
|
|
@@ -262,10 +266,139 @@ tools.add(
|
|
|
262
266
|
}
|
|
263
267
|
try {
|
|
264
268
|
await fs.writeFile(resolvedPath, content, 'utf8');
|
|
265
|
-
|
|
269
|
+
|
|
270
|
+
// AUTO-VALIDATE: Multi-lang syntax check via utils/syntax_check.sh + chmod shebang
|
|
271
|
+
const hasShebang = content.startsWith('#!');
|
|
272
|
+
|
|
273
|
+
let validationMsg = '';
|
|
274
|
+
try {
|
|
275
|
+
await SH`${syntaxCheckSh} ${[resolvedPath]}`.run();
|
|
276
|
+
validationMsg = ' ✓ Multi-lang syntax OK';
|
|
277
|
+
} catch (e) {
|
|
278
|
+
const errPreview = content.slice(0, 1000).split('\n').slice(0, 20).join('\n');
|
|
279
|
+
throw new Error(`❌ SYNTAX ERROR in '${file}' (syntax_check.sh failed):\n${e.message}\n\nPREVIEW:\n${errPreview}\n\n... Fix syntax (quotes/backticks) and retry write_file.`);
|
|
280
|
+
}
|
|
281
|
+
if (hasShebang) {
|
|
282
|
+
await SH`chmod +x ${[resolvedPath]}`.run();
|
|
283
|
+
validationMsg += ' ✓ chmod +x';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return `Successfully wrote to '${file}' (${Buffer.byteLength(content, 'utf8')} bytes).${validationMsg}`;
|
|
266
287
|
} catch (e) {
|
|
267
288
|
throw new Error(`Failed to write '${file}': ${e.message}`);
|
|
268
289
|
}
|
|
269
290
|
}
|
|
270
291
|
);
|
|
271
|
-
|
|
292
|
+
|
|
293
|
+
tools.add(
|
|
294
|
+
'syntax_check',
|
|
295
|
+
'Standalone syntax validation for files (JS/Python/Bash/JSON/etc.) via utils/syntax_check.sh. Detects lang from ext/shebang.',
|
|
296
|
+
{
|
|
297
|
+
type: 'object',
|
|
298
|
+
properties: {
|
|
299
|
+
file: {
|
|
300
|
+
type: 'string',
|
|
301
|
+
description: `Relative path to the file within CWD.`
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
required: ['file']
|
|
305
|
+
},
|
|
306
|
+
async (params) => {
|
|
307
|
+
const file = params.file?.trim();
|
|
308
|
+
if (typeof file !== 'string' || !file) {
|
|
309
|
+
throw new Error('Valid relative file path required.');
|
|
310
|
+
}
|
|
311
|
+
const resolvedPath = path.resolve(process.cwd(), file);
|
|
312
|
+
if (!resolvedPath.startsWith(process.cwd())) {
|
|
313
|
+
throw new Error(`Path '${file}' escapes CWD scope.`);
|
|
314
|
+
}
|
|
315
|
+
return await SH`${syntaxCheckSh} ${[resolvedPath]}`.run();
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
tools.add(
|
|
320
|
+
'memory_write',
|
|
321
|
+
`Persist agent memory for tasks, errors, or user preferences to .cache/memory.ndjson in CWD (${user.cwd}). Use to store decisions, tasks, errors, or prefs for later recall. Agent should use this to avoid repeating work or token burn on loops.`,
|
|
322
|
+
{
|
|
323
|
+
type: 'object',
|
|
324
|
+
properties: {
|
|
325
|
+
category: {
|
|
326
|
+
type: 'string',
|
|
327
|
+
enum: ['tasks', 'errors', 'prefs'],
|
|
328
|
+
description: 'Category: one of "tasks", "errors", "prefs"'
|
|
329
|
+
},
|
|
330
|
+
content: {
|
|
331
|
+
type: 'string',
|
|
332
|
+
description: 'Detailed content (e.g., "Pending task: implement runWithMemory optimization", "Error: loop burning tokens", "Pref: temperature=0.2 for decisions")'
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
required: ['category', 'content']
|
|
336
|
+
},
|
|
337
|
+
async ({ category, content }) => {
|
|
338
|
+
if (!['tasks', 'errors', 'prefs'].includes(category)) {
|
|
339
|
+
throw new Error(`Invalid category '${category}'. Must be 'tasks', 'errors', or 'prefs'.`);
|
|
340
|
+
}
|
|
341
|
+
const memoryPath = path.join(process.cwd(), '.cache', 'memory.ndjson');
|
|
342
|
+
const dir = path.dirname(memoryPath);
|
|
343
|
+
try {
|
|
344
|
+
await fs.mkdir(dir, { recursive: true });
|
|
345
|
+
} catch (e) {
|
|
346
|
+
// Dir likely exists
|
|
347
|
+
}
|
|
348
|
+
const entry = {
|
|
349
|
+
timestamp: new Date().toISOString(),
|
|
350
|
+
category,
|
|
351
|
+
content: content.trim()
|
|
352
|
+
};
|
|
353
|
+
await fs.appendFile(memoryPath, JSON.stringify(entry) + '\n', 'utf8');
|
|
354
|
+
return `✓ Memory stored: [${category}] ${content.length > 50 ? content.slice(0, 47) + '...' : content}`;
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
tools.add(
|
|
359
|
+
'memory_recall',
|
|
360
|
+
`Retrieve stored agent memories (tasks, errors, prefs) from .cache/memory.ndjson in CWD (${user.cwd}). Use before acting to check prior decisions/tasks/errors/prefs. Query by keyword or category; empty lists recent.`,
|
|
361
|
+
{
|
|
362
|
+
type: 'object',
|
|
363
|
+
properties: {
|
|
364
|
+
query: {
|
|
365
|
+
type: 'string',
|
|
366
|
+
description: 'Keyword, category (tasks/errors/prefs), or phrase to filter (case-insensitive). Empty/omit lists last 20.'
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
required: []
|
|
370
|
+
},
|
|
371
|
+
async (params = {}) => {
|
|
372
|
+
const memoryPath = path.join(process.cwd(), '.cache', 'memory.ndjson');
|
|
373
|
+
let content;
|
|
374
|
+
try {
|
|
375
|
+
content = await fs.readFile(memoryPath, 'utf8');
|
|
376
|
+
} catch (e) {
|
|
377
|
+
return 'No memories stored yet. Use memory_write first.';
|
|
378
|
+
}
|
|
379
|
+
const lines = content.trim().split('\n').filter(l => l.trim());
|
|
380
|
+
const memories = [];
|
|
381
|
+
for (const line of lines) {
|
|
382
|
+
try {
|
|
383
|
+
memories.push(JSON.parse(line));
|
|
384
|
+
} catch {
|
|
385
|
+
// Skip invalid lines
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (!params.query || !params.query.toString().trim()) {
|
|
389
|
+
const recent = memories.slice(-20).reverse();
|
|
390
|
+
return recent.length
|
|
391
|
+
? 'Recent memories:\n' + recent.map(m => `• ${m.timestamp.slice(0, 19).replace('T', ' ')} [${m.category}] ${m.content}`).join('\n')
|
|
392
|
+
: 'No memories stored.';
|
|
393
|
+
}
|
|
394
|
+
const q = params.query.toString().toLowerCase();
|
|
395
|
+
const matches = memories.filter(m =>
|
|
396
|
+
m.category.toLowerCase().includes(q) || m.content.toLowerCase().includes(q)
|
|
397
|
+
);
|
|
398
|
+
return matches.length
|
|
399
|
+
? `Matches for "${params.query}":\n` + matches.slice(0, 20).map(m => `• ${m.timestamp.slice(0, 19).replace('T', ' ')} [${m.category}] ${m.content}`).join('\n')
|
|
400
|
+
: `No memories match "${params.query}".`;
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
export default tools
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { SH, bashEscape } from '@j-o-r/sh'
|
|
2
|
+
import { ToolSet, env } from './index.js'
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { promises as fs } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
const user = await env();
|
|
11
|
+
const environment = `
|
|
12
|
+
Name: ${user.name}
|
|
13
|
+
System: ${user.system}
|
|
14
|
+
City: ${user.city}
|
|
15
|
+
Region: ${user.region}
|
|
16
|
+
Country: ${user.country}
|
|
17
|
+
Timezone: ${user.timezone}
|
|
18
|
+
ExternalIp: ${user.external_ip}
|
|
19
|
+
`.trim();
|
|
20
|
+
const tools = new ToolSet('auto');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* reduce the error output to essential info only, if possible
|
|
24
|
+
* @param {string} errorStr
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
const getJSError = (errorStr) => {
|
|
28
|
+
let result = '';
|
|
29
|
+
const linematch = errorStr.match(/\[eval\]:(\d+)/);
|
|
30
|
+
const lineNumber = linematch ? linematch[1] : '';
|
|
31
|
+
const match = errorStr.split(/\" \"\[eval\]:\d+/s);
|
|
32
|
+
if (match.length > 1) {
|
|
33
|
+
// Remove last 10 lines
|
|
34
|
+
const res = match[1].split('\n').slice(0, -10).join('\n');
|
|
35
|
+
result = `Error: line ${lineNumber}\n${res} `
|
|
36
|
+
} else {
|
|
37
|
+
result = errorStr;
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
tools.add(
|
|
43
|
+
'javascript_interpreter',
|
|
44
|
+
`Execute ESM ES6 javascript on \`node\`.`,
|
|
45
|
+
{
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
script: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: `ES6 ESM Javascript eval. 'console.log' to capture the response. cwd: ${user.cwd}`,
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
required: ['script']
|
|
54
|
+
},
|
|
55
|
+
async (params) => {
|
|
56
|
+
let response = '';
|
|
57
|
+
try {
|
|
58
|
+
const delim = `JS_STDIN_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,6)}`;
|
|
59
|
+
response = await SH`cat <<'${delim}' | node --input-type=module -
|
|
60
|
+
${params.script}
|
|
61
|
+
${delim}
|
|
62
|
+
`.run();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
const errorStr = e.toString();
|
|
65
|
+
response = getJSError(errorStr);
|
|
66
|
+
}
|
|
67
|
+
return response;
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
tools.add(
|
|
71
|
+
'get_user_env', // name
|
|
72
|
+
'Get the user location, name and OS environment', // desciption
|
|
73
|
+
{
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async (_params) => {
|
|
79
|
+
return environment;
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
tools.add(
|
|
84
|
+
'execute_bash_script',
|
|
85
|
+
'Execute a bash script or command. (char escaping not needed)',
|
|
86
|
+
{
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
bash_script: { type: 'string', description: `RAW bash script (LITERAL TEXT: NO ESCAPING—output $, |, <, >, &, ", ', \\, \` , newlines, $(()), [[ ]] verbatim. Heredoc delimiter handles safely. EX: echo "$((1+1)) | grep \'<&>\"hi$USER\"\' && ls -la\`. (${user.system})` }
|
|
90
|
+
},
|
|
91
|
+
required: ['bash_script']
|
|
92
|
+
},
|
|
93
|
+
async (params) => {
|
|
94
|
+
const delim = `END_SCRIPT_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
95
|
+
return await SH`bash <<'${delim}'
|
|
96
|
+
${params.bash_script}
|
|
97
|
+
${delim}
|
|
98
|
+
`.run()
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
tools.add(
|
|
103
|
+
'send_email',
|
|
104
|
+
'Send an email.',
|
|
105
|
+
{
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
to: { type: 'string', description: 'Recipient email' },
|
|
109
|
+
subject: { type: 'string', description: 'Subject' },
|
|
110
|
+
body: { type: 'string', description: 'Message body' }
|
|
111
|
+
},
|
|
112
|
+
required: ['to', 'subject', 'body']
|
|
113
|
+
},
|
|
114
|
+
async (params) => {
|
|
115
|
+
const delim = `END_EMAIL_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
116
|
+
return await SH`msmtp ${params.to} <<'${delim}'
|
|
117
|
+
To: ${params.to}
|
|
118
|
+
Subject: ${params.subject}
|
|
119
|
+
|
|
120
|
+
${params.body}
|
|
121
|
+
${delim}
|
|
122
|
+
`.run();
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
tools.add(
|
|
126
|
+
'open_link',
|
|
127
|
+
'Open an url or file in the local user environment. (xdg-open)',
|
|
128
|
+
{
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: {
|
|
131
|
+
url: { type: 'string', description: 'file | URL' }
|
|
132
|
+
},
|
|
133
|
+
required: ['url']
|
|
134
|
+
},
|
|
135
|
+
async (params) => {
|
|
136
|
+
return await SH`xdg-open ${[params.url]}`.run();
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
tools.add(
|
|
140
|
+
'execute_remote_script',
|
|
141
|
+
'Execute bash script on a remote machine via SSH.',
|
|
142
|
+
{
|
|
143
|
+
type: 'object',
|
|
144
|
+
properties: {
|
|
145
|
+
url: { type: 'string', description: 'SSH URL, e.g., ssh://user@host or ssh://user@host:port' },
|
|
146
|
+
script: { type: 'string', description: 'RAW script code to execute remotely (no escaping needed)' }
|
|
147
|
+
},
|
|
148
|
+
required: ['url', 'script']
|
|
149
|
+
},
|
|
150
|
+
async (params) => {
|
|
151
|
+
const { url, script } = params;
|
|
152
|
+
if (!url.startsWith('ssh://')) throw new Error('Invalid SSH URL');
|
|
153
|
+
const withoutProto = url.slice(6);
|
|
154
|
+
const parts = withoutProto.split(':');
|
|
155
|
+
let port = 22;
|
|
156
|
+
let userHost = withoutProto;
|
|
157
|
+
if (parts.length > 1) {
|
|
158
|
+
userHost = parts[0];
|
|
159
|
+
port = parseInt(parts[1]);
|
|
160
|
+
}
|
|
161
|
+
const [user, host] = userHost.split('@');
|
|
162
|
+
if (!user || !host) throw new Error('Invalid SSH URL format: use ssh://user@host[:port]');
|
|
163
|
+
|
|
164
|
+
const delim = `END_SCRIPT_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
165
|
+
return await SH`ssh -p ${port} ${user}@${host} bash <<'${delim}'
|
|
166
|
+
${script}
|
|
167
|
+
${delim}
|
|
168
|
+
`.run();
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
tools.add(
|
|
173
|
+
'history_search',
|
|
174
|
+
`Search previous LLM chat sessions or list them hierarchically.
|
|
175
|
+
Example query: "(todo|task)" or "package.json".
|
|
176
|
+
Searches filenames & content in .cache/[app]/[prompt]/sessions/*.ndjson (case-insensitive regex, with context).
|
|
177
|
+
Omit query (or use empty string) to list sessions (see utils/list_sessions.sh).`,
|
|
178
|
+
{
|
|
179
|
+
type: 'object',
|
|
180
|
+
properties: {
|
|
181
|
+
query: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
description: `Search query or regex (quoted for multi-word/regex). Omit or empty for list mode.`
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
required: []
|
|
187
|
+
},
|
|
188
|
+
async (params) => {
|
|
189
|
+
const history_search = path.resolve(__dirname, '..', 'utils', 'search_sessions.sh');
|
|
190
|
+
const list_sessions = path.resolve(__dirname, '..', 'utils', 'list_sessions.sh');
|
|
191
|
+
if (typeof params.query === 'string' && params.query.trim() !== '') {
|
|
192
|
+
const escapedQuery = bashEscape(params.query);
|
|
193
|
+
return await SH`${history_search} "${escapedQuery}"`.run();
|
|
194
|
+
} else {
|
|
195
|
+
return await SH`${list_sessions}`.run();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
tools.add(
|
|
200
|
+
'read_file',
|
|
201
|
+
'Read the raw content of a file strictly within the current working directory (CWD). Paths must be relative (no leading /, no ..).',
|
|
202
|
+
{
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {
|
|
205
|
+
file: {
|
|
206
|
+
type: 'string',
|
|
207
|
+
description: `Relative path to the file within CWD, e.g., 'path/to/file.txt' (no escaping needed). cwd: ${user.cwd}`
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
required: ['file']
|
|
211
|
+
},
|
|
212
|
+
async (params) => {
|
|
213
|
+
const file = params.file?.trim();
|
|
214
|
+
if (typeof file !== 'string' || !file) {
|
|
215
|
+
throw new Error('Valid relative file path required.');
|
|
216
|
+
}
|
|
217
|
+
if (file.startsWith('/') || file.includes('..') || file.includes('\\\\')) {
|
|
218
|
+
throw new Error('Path must be relative within CWD only (no `/`, `..`, or `\\\\`).');
|
|
219
|
+
}
|
|
220
|
+
const resolvedPath = path.resolve(process.cwd(), file);
|
|
221
|
+
if (!resolvedPath.startsWith(process.cwd())) {
|
|
222
|
+
throw new Error(`Path '${file}' escapes CWD scope.`);
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const content = await fs.readFile(resolvedPath, 'utf8');
|
|
226
|
+
return content;
|
|
227
|
+
} catch (e) {
|
|
228
|
+
throw new Error(`Failed to read '${file}': ${e.message}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
tools.add(
|
|
234
|
+
'write_file',
|
|
235
|
+
'Write raw content to a file strictly within the current working directory (CWD). Paths must be relative (no leading /, no ..). Content written as-is (no escaping). **AUTO-VALIDATES** JS/Python/Bash/JSON/etc. via `utils/syntax_check.sh`; chmod +x shebangs. Retries syntax errors force LLM fix.',
|
|
236
|
+
{
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
file: {
|
|
240
|
+
type: 'string',
|
|
241
|
+
description: `Relative path to the file within CWD, e.g., 'path/to/file.txt' (no escaping needed).`
|
|
242
|
+
},
|
|
243
|
+
content: {
|
|
244
|
+
type: 'string',
|
|
245
|
+
description: `Raw content to write (as-is, no char escaping needed; supports newlines, $, |, <, >, &, ", ', \\, etc.).`
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
required: ['file', 'content']
|
|
249
|
+
},
|
|
250
|
+
async (params) => {
|
|
251
|
+
const file = params.file?.trim();
|
|
252
|
+
const content = params.content ?? '';
|
|
253
|
+
if (typeof file !== 'string' || !file) {
|
|
254
|
+
throw new Error('Valid relative file path required.');
|
|
255
|
+
}
|
|
256
|
+
if (file.startsWith('/') || file.includes('..') || file.includes('\\\\')) {
|
|
257
|
+
throw new Error('Path must be relative within CWD only (no `/`, `..`, or `\\\\`).');
|
|
258
|
+
}
|
|
259
|
+
const resolvedPath = path.resolve(process.cwd(), file);
|
|
260
|
+
if (!resolvedPath.startsWith(process.cwd())) {
|
|
261
|
+
throw new Error(`Path '${file}' escapes CWD scope.`);
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
await fs.writeFile(resolvedPath, content, 'utf8');
|
|
265
|
+
|
|
266
|
+
// AUTO-VALIDATE: Multi-lang syntax check via utils/syntax_check.sh + chmod shebang
|
|
267
|
+
const syntaxChecker = path.resolve(process.cwd(), 'utils/syntax_check.sh');
|
|
268
|
+
const hasShebang = content.startsWith('#!');
|
|
269
|
+
|
|
270
|
+
let validationMsg = '';
|
|
271
|
+
try {
|
|
272
|
+
await SH`${syntaxChecker} ${[resolvedPath]}`.run();
|
|
273
|
+
validationMsg = ' ✓ Multi-lang syntax OK';
|
|
274
|
+
} catch (e) {
|
|
275
|
+
const errPreview = content.slice(0, 1000).split('\n').slice(0, 20).join('\n');
|
|
276
|
+
throw new Error(`❌ SYNTAX ERROR in '${file}' (syntax_check.sh failed):\n${e.message}\n\nPREVIEW:\n${errPreview}\n\n... Fix syntax (quotes/backticks) and retry write_file.`);
|
|
277
|
+
}
|
|
278
|
+
if (hasShebang) {
|
|
279
|
+
await SH`chmod +x ${[resolvedPath]}`.run();
|
|
280
|
+
validationMsg += ' ✓ chmod +x';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return `Successfully wrote to '${file}' (${Buffer.byteLength(content, 'utf8')} bytes).${validationMsg}`;
|
|
284
|
+
} catch (e) {
|
|
285
|
+
throw new Error(`Failed to write '${file}': ${e.message}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
tools.add(
|
|
291
|
+
'syntax_check',
|
|
292
|
+
'Standalone syntax validation for files (JS/Python/Bash/JSON/etc.) via utils/syntax_check.sh. Detects lang from ext/shebang.',
|
|
293
|
+
{
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: {
|
|
296
|
+
file: {
|
|
297
|
+
type: 'string',
|
|
298
|
+
description: `Relative path to the file within CWD.`
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
required: ['file']
|
|
302
|
+
},
|
|
303
|
+
async (params) => {
|
|
304
|
+
const file = params.file?.trim();
|
|
305
|
+
if (typeof file !== 'string' || !file) {
|
|
306
|
+
throw new Error('Valid relative file path required.');
|
|
307
|
+
}
|
|
308
|
+
const resolvedPath = path.resolve(process.cwd(), file);
|
|
309
|
+
if (!resolvedPath.startsWith(process.cwd())) {
|
|
310
|
+
throw new Error(`Path '${file}' escapes CWD scope.`);
|
|
311
|
+
}
|
|
312
|
+
const syntaxChecker = path.resolve(process.cwd(), 'utils/syntax_check.sh');
|
|
313
|
+
return await SH`${syntaxChecker} ${[resolvedPath]}`.run();
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
tools.add(
|
|
318
|
+
'memory_write',
|
|
319
|
+
`Persist agent memory for tasks, errors, or user preferences to .cache/memory.ndjson in CWD (${user.cwd}). Use to store decisions, tasks, errors, or prefs for later recall. Agent should use this to avoid repeating work or token burn on loops.`,
|
|
320
|
+
{
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: {
|
|
323
|
+
category: {
|
|
324
|
+
type: 'string',
|
|
325
|
+
enum: ['tasks', 'errors', 'prefs'],
|
|
326
|
+
description: 'Category: one of "tasks", "errors", "prefs"'
|
|
327
|
+
},
|
|
328
|
+
content: {
|
|
329
|
+
type: 'string',
|
|
330
|
+
description: 'Detailed content (e.g., "Pending task: implement runWithMemory optimization", "Error: loop burning tokens", "Pref: temperature=0.2 for decisions")'
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
required: ['category', 'content']
|
|
334
|
+
},
|
|
335
|
+
async ({category, content}) => {
|
|
336
|
+
if (!['tasks', 'errors', 'prefs'].includes(category)) {
|
|
337
|
+
throw new Error(`Invalid category '${category}'. Must be 'tasks', 'errors', or 'prefs'.`);
|
|
338
|
+
}
|
|
339
|
+
const memoryPath = path.join(process.cwd(), '.cache', 'memory.ndjson');
|
|
340
|
+
const dir = path.dirname(memoryPath);
|
|
341
|
+
try {
|
|
342
|
+
await fs.mkdir(dir, { recursive: true });
|
|
343
|
+
} catch (e) {
|
|
344
|
+
// Dir likely exists
|
|
345
|
+
}
|
|
346
|
+
const entry = {
|
|
347
|
+
timestamp: new Date().toISOString(),
|
|
348
|
+
category,
|
|
349
|
+
content: content.trim()
|
|
350
|
+
};
|
|
351
|
+
await fs.appendFile(memoryPath, JSON.stringify(entry) + '\n', 'utf8');
|
|
352
|
+
return `✓ Memory stored: [${category}] ${content.length > 50 ? content.slice(0, 47) + '...' : content}`;
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
tools.add(
|
|
357
|
+
'memory_recall',
|
|
358
|
+
`Retrieve stored agent memories (tasks, errors, prefs) from .cache/memory.ndjson in CWD (${user.cwd}). Use before acting to check prior decisions/tasks/errors/prefs. Query by keyword or category; empty lists recent.`,
|
|
359
|
+
{
|
|
360
|
+
type: 'object',
|
|
361
|
+
properties: {
|
|
362
|
+
query: {
|
|
363
|
+
type: 'string',
|
|
364
|
+
description: 'Keyword, category (tasks/errors/prefs), or phrase to filter (case-insensitive). Empty/omit lists last 20.'
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
required: []
|
|
368
|
+
},
|
|
369
|
+
async (params = {}) => {
|
|
370
|
+
const memoryPath = path.join(process.cwd(), '.cache', 'memory.ndjson');
|
|
371
|
+
let content;
|
|
372
|
+
try {
|
|
373
|
+
content = await fs.readFile(memoryPath, 'utf8');
|
|
374
|
+
} catch (e) {
|
|
375
|
+
return 'No memories stored yet. Use memory_write first.';
|
|
376
|
+
}
|
|
377
|
+
const lines = content.trim().split('\n').filter(l => l.trim());
|
|
378
|
+
const memories = [];
|
|
379
|
+
for (const line of lines) {
|
|
380
|
+
try {
|
|
381
|
+
memories.push(JSON.parse(line));
|
|
382
|
+
} catch {
|
|
383
|
+
// Skip invalid lines
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (!params.query || !params.query.toString().trim()) {
|
|
387
|
+
const recent = memories.slice(-20).reverse();
|
|
388
|
+
return recent.length
|
|
389
|
+
? 'Recent memories:\n' + recent.map(m => `• ${m.timestamp.slice(0, 19).replace('T', ' ')} [${m.category}] ${m.content}`).join('\n')
|
|
390
|
+
: 'No memories stored.';
|
|
391
|
+
}
|
|
392
|
+
const q = params.query.toString().toLowerCase();
|
|
393
|
+
const matches = memories.filter(m =>
|
|
394
|
+
m.category.toLowerCase().includes(q) || m.content.toLowerCase().includes(q)
|
|
395
|
+
);
|
|
396
|
+
return matches.length
|
|
397
|
+
? `Matches for "${params.query}":\n` + matches.slice(0, 20).map(m => `• ${m.timestamp.slice(0, 19).replace('T', ' ')} [${m.category}] ${m.content}`).join('\n')
|
|
398
|
+
: `No memories match "${params.query}".`;
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
export default tools
|
package/lib/wsCli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j-o-r/hello-dave",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "ESM toolkit for building AI agents with unified access to Grok (XAI), OpenAI, and Anthropic endpoints",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./types/index.d.ts",
|
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
16
|
"dave": "bin/dave.js",
|
|
17
|
-
"createAgent": "examples/
|
|
17
|
+
"createAgent": "examples/spawn_agent.js"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"test": "scenarios/grok.js",
|
|
21
20
|
"release": "npm run types && npm pack --pack-destination=release",
|
|
22
21
|
"types": "rm -fr types/* && tsc -p tsc.json",
|
|
23
22
|
"publish": "npm run release && npm publish --access public",
|
|
24
23
|
"local": "npm -g uninstall '@j-o-r/hello-dave' && npm install -g .",
|
|
25
24
|
"link-self": "mkdir -p node_modules/@j-o-r && ln -sf . node_modules/@j-o-r/hello-dave && npm ls @j-o-r/hello-dave",
|
|
26
|
-
"unlink-self": "rm -rf node_modules/@j-o-r/hello-dave"
|
|
25
|
+
"unlink-self": "rm -rf node_modules/@j-o-r/hello-dave",
|
|
26
|
+
"tests": "utils/test.sh"
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@j-o-r/apiserver": "*",
|
|
34
34
|
"@j-o-r/cli": "*",
|
|
35
|
-
"@j-o-r/sh": "1.1.28",
|
|
36
35
|
"gpt-3-encoder": "*"
|
|
37
36
|
},
|
|
38
37
|
"exports": {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function_request",
|
|
3
|
+
"function_request": {
|
|
4
|
+
"name": "execute_javascript",
|
|
5
|
+
"id": "fc_686284be7fec81a0af404739ec17e29f09dcb726e1aa6681",
|
|
6
|
+
"call_id": "call_w0WnYfQiqFey5UiF4jG0XQcn",
|
|
7
|
+
"parameters": "{\"script\": \"/*\\n * Function: parseMessages\\n * Description: Parses the 'response' object and adds messages to the given Prompt instance.\\n * The function supports two types of response items:\\n * 1. function_call: These are mapped to a function_request message with role 'assistant'.\\n * The function_request content fields are: name, id, call_id, and parameters (from arguments).\\n * 2. message: These are mapped to text messages. In case the content array contains items with type 'output_text',\\n * they are converted into PRTextMessage objects (i.e., with type \\\"text\\\") and added to the prompt.\\n * In the case where there is more than one piece of content, addMultiModal is used.\\n *\\n * Note: This function assumes that the response is already parsed (i.e., a JS object) and that prompt is an instance\\n * of the Prompt class imported from lib/Prompt.js.\\n */\\n\\nexport function parseMessages(response, prompt) {\\n if (!response || !Array.isArray(response.output)) {\\n console.warn('Invalid response format. Expected an object with an output array.');\\n return;\\n }\\n\\n response.output.forEach(item => {\\n if (item.type === 'function_call') {\\n // Convert function_call to a function_request message\\n const functionRequestContent = {\\n type: 'function_request',\\n function_request: {\\n id: item.id,\\n name: item.name,\\n call_id: item.call_id,\\n parameters: item.arguments\\n }\\n };\\n // Add as a multi-modal assistant message\\n try {\\n prompt.addMultiModal('assistant', [functionRequestContent]);\\n } catch (err) {\\n console.error('Error adding function_request message:', err);\\n }\\n } else if (item.type === 'message') {\\n // Determine the role; if not provided, use 'assistant' by default\\n const role = item.role || 'assistant';\\n let prContents = [];\\n\\n if (Array.isArray(item.content)) {\\n item.content.forEach(contentItem => {\\n if (contentItem.type === 'output_text') {\\n prContents.push({\\n type: 'text',\\n text: contentItem.text\\n });\\n } else if (contentItem.type === 'image_url') {\\n prContents.push({\\n type: 'image_url',\\n image_url: contentItem.image_url\\n });\\n } else {\\n // If another type is encountered, you may want to add additional handling here.\\n console.warn('Unhandled content type in message:', contentItem.type);\\n }\\n });\\n }\\n\\n if (prContents.length > 0) {\\n try {\\n // If there is only one text message and no multi-modal requirements, use add\\n if (prContents.length === 1 && prContents[0].type === 'text') {\\n prompt.add(role, prContents[0].text);\\n } else {\\n prompt.addMultiModal(role, prContents);\\n }\\n } catch (err) {\\n console.error('Error adding message:', err);\\n }\\n }\\n } else {\\n console.warn('Unhandled item type:', item.type);\\n }\\n });\\n}\\n\\n// Example usage:\\n// import Prompt from './lib/Prompt.js';\\n// import { parseMessages } from './parseMessages.js';\\n// \\n// const prompt = new Prompt(1000);\\n// const response = {\\n// output: [\\n// {\\n// id: 'fc_68624cb486148191b2078ec2a76e389a068902ce013ce130',\\n// type: 'function_call',\\n// status: 'completed',\\n// arguments: '{}',\\n// call_id: 'call_6YNYwCZRbmMPr5WaEo1FkbXx',\\n// name: 'get_date_time'\\n// },\\n// {\\n// id: 'msg_68624cb7c28c8191aff3106ebea965d4068902ce013ce130',\\n// type: 'message',\\n// status: 'completed',\\n// content: [\\n// {\\n// type: 'output_text',\\n// annotations: [],\\n// logprobs: [],\\n// text: 'Today is Friday, 12 May 2025. The current weather in Amsterdam, The Netherlands is 24°C.'\\n// }\\n// ],\\n// role: 'assistant'\\n// }\\n// ]\\n// };\\n// \\n// parseMessages(response, prompt);\\n// console.log(prompt.messages);\\n\"}"
|
|
8
|
+
}
|
|
9
|
+
}
|