@j-o-r/hello-dave 0.0.5 → 0.0.6

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 (91) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/bin/dave.js +114 -94
  3. package/examples/memory_agent.js +134 -23
  4. package/examples/test_agent.js +0 -2
  5. package/lib/genericToolset.js +4 -88
  6. package/lib/wsIO.js +1 -11
  7. package/package.json +2 -2
  8. package/README.md +0 -601
  9. package/README.md.backup +0 -269
  10. package/README.md.bak +0 -523
  11. package/README.md.bak.1774780058 +0 -338
  12. package/README.md.bak2 +0 -531
  13. package/docs.bak.1774780058/agent-manager.md +0 -167
  14. package/docs.bak.1774780058/agent-manager.md.bak +0 -137
  15. package/docs.bak.1774780058/agent-manager.md.bak2 +0 -157
  16. package/docs.bak.1774780058/codeserver-pattern.md +0 -191
  17. package/docs.bak.1774780058/path-resolution-best-practices.md +0 -104
  18. package/docs.bak.1774780058/project-overview.md +0 -67
  19. package/docs.bak.1774780058/project-overview.md.bak +0 -67
  20. package/docs.bak.1774780058/prompt-class.md +0 -141
  21. package/docs.bak.1774780058/prompt-class.md.bak +0 -142
  22. package/docs.bak.1774780058/tools-syntax-validation.md +0 -121
  23. package/docs.bak.1774780058/tools-syntax-validation.md.bak2 +0 -125
  24. package/docs.bak.1774780058/tools-syntax-validation.md.bak3 +0 -125
  25. package/docs.bak.1774780058/tools-syntax-validation.md.bak4 +0 -106
  26. package/docs.bak.1774780058/tools-syntax-validation.md.bak_path +0 -106
  27. package/docs.bak.1774780058/toolset.md +0 -164
  28. package/docs.bak.1774780058/toolset.md.bak +0 -94
  29. package/docs.bak.1774780058/toolset.md.bak3 +0 -161
  30. package/docs.bak.1774780058/toolset.md.bak4 +0 -161
  31. package/docs.bak.1774780058/toolset.md.bak5 +0 -161
  32. package/docs.bak.1774780058/toolset.md.bak6 +0 -163
  33. package/docs.bak.1774780058/toolset.md.bak_path +0 -163
  34. package/docs.bak.1774780058/toolset.md.bak_syntax +0 -161
  35. package/docs.bak.1774780058/xai-responses.md +0 -111
  36. package/docs.bak.1774780058/xai-responses.md.bak +0 -107
  37. package/docs.bak.1774780058/xai-responses.md.bak2 +0 -107
  38. package/docs.bak.1774780058/xai_collections.md +0 -106
  39. package/examples.bak.1774780058/ask_agent.js +0 -114
  40. package/examples.bak.1774780058/code_agent.js +0 -149
  41. package/examples.bak.1774780058/coderev_agent.js +0 -72
  42. package/examples.bak.1774780058/codeserver.sh +0 -47
  43. package/examples.bak.1774780058/daisy_agent.js +0 -177
  44. package/examples.bak.1774780058/docs_agent.js +0 -119
  45. package/examples.bak.1774780058/gpt_agent.js +0 -109
  46. package/examples.bak.1774780058/grok_agent.js +0 -98
  47. package/examples.bak.1774780058/memory_agent.js +0 -112
  48. package/examples.bak.1774780058/npm_agent.js +0 -175
  49. package/examples.bak.1774780058/prompt_agent.js +0 -112
  50. package/examples.bak.1774780058/readme_agent.js +0 -144
  51. package/examples.bak.1774780058/spawn_agent.js +0 -263
  52. package/examples.bak.1774780058/test_agent.js +0 -162
  53. package/examples.bak.1774780058/todo_agent.js +0 -138
  54. package/scenarios.bak.1774780058/data/eval_node_message.json +0 -9
  55. package/scenarios.bak.1774780058/data/hist_oa.json +0 -66
  56. package/scenarios.bak.1774780058/data/o3_response1.json +0 -96
  57. package/scenarios.bak.1774780058/data/oa_reasoning_parse.json +0 -112
  58. package/scenarios.bak.1774780058/data/tool_oa.json +0 -96
  59. package/scenarios.bak.1774780058/data/tool_xai.json +0 -59
  60. package/scenarios.bak.1774780058/data/tool_xai2.json +0 -40
  61. package/scenarios.bak.1774780058/data/xai-response-1.json +0 -59
  62. package/scenarios.bak.1774780058/data/xai-response-2.json +0 -10
  63. package/scenarios.bak.1774780058/data/xai_reasoning_tools_resp.json +0 -59
  64. package/scenarios.bak.1774780058/data/xai_search_response.json +0 -58
  65. package/scenarios.bak.1774780058/environment.js +0 -10
  66. package/scenarios.bak.1774780058/example.js +0 -17
  67. package/scenarios.bak.1774780058/genericToolset.test.js +0 -182
  68. package/scenarios.bak.1774780058/grok.js +0 -113
  69. package/scenarios.bak.1774780058/memory-tools.js +0 -51
  70. package/scenarios.bak.1774780058/openai-o3.js +0 -137
  71. package/scenarios.bak.1774780058/openai-prompt.js +0 -155
  72. package/scenarios.bak.1774780058/openai-session.js +0 -148
  73. package/scenarios.bak.1774780058/openai.js +0 -102
  74. package/scenarios.bak.1774780058/prompt.js +0 -118
  75. package/scenarios.bak.1774780058/promptFishbowl.js +0 -76
  76. package/scenarios.bak.1774780058/search.brave.com.js +0 -25
  77. package/scenarios.bak.1774780058/sh.js +0 -15
  78. package/scenarios.bak.1774780058/test-wsio.js +0 -26
  79. package/scenarios.bak.1774780058/testToolset.js +0 -42
  80. package/scenarios.bak.1774780058/toolset.js +0 -16
  81. package/scenarios.bak.1774780058/toolset.test.js +0 -141
  82. package/scenarios.bak.1774780058/write_file_syntax.test.js +0 -145
  83. package/scenarios.bak.1774780058/write_file_validation/README.md +0 -30
  84. package/scenarios.bak.1774780058/write_file_validation/bad.js +0 -3
  85. package/scenarios.bak.1774780058/write_file_validation/good.js +0 -4
  86. package/scenarios.bak.1774780058/write_file_validation/test.sh +0 -43
  87. package/scenarios.bak.1774780058/wsClient.js +0 -69
  88. package/scenarios.bak.1774780058/xai_responses.integration.test.js +0 -57
  89. package/scenarios.bak.1774780058/xai_responses.test.js +0 -154
  90. package/scenarios.bak.1774780058/xaicoll.js +0 -50
  91. package/scenarios.bak.1774780058/xaifiles.js +0 -48
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## Changelog
2
2
 
3
+ ### v0.0.6 (Upcoming - April 2026)
4
+ - **Test Verifications**: Confirmed full test suite passes 100% after fixes to xAI integration tests (npm run tests or utils/test.sh). Enhanced coverage in genericToolset.test.js (memory recall with unique content isolation) and write_file_syntax.test.js (backtick/template literal handling in JS files). No regressions in scenarios/*.test.js.
5
+ - **CLI and Documentation Updates**: Verified piped and interactive support for --ask and --connect in bin/dave.js (e.g., echo "predict weather" | dave --ask; echo "user_info" | dave --ask --connect). Updated README.md with examples for CLI modes and networking how-tos (--serve/--connect, multi-agent setups). Aligned TODO.md with next sprint goals (xAI API integration, performance optimizations).
6
+ - **Minor Library Fixes**: Refined wsIO exports in lib/index.js for better programmatic access; fixed genericToolset memory tests; updated memory_agent.js prompts for consistent FIRST/LAST recall/write protocol; minor dave.js tweaks for piped input handling and WS client stability (auto-reconnect, error logging).
7
+ - **Next Sprint Planning**: Prepared for v0.0.6 features including x.ai API integration into collections.js and files.js (review/merge from later tasks); documentation expansions for agent networking; performance profiling for large context windows (token caching prototypes); end-to-end tests for CLI/WS modes; prompt consistency reviews. Target release: 2026-04-06.
8
+ - **Release Preparation**: Changelog updated with recent fixes and planned enhancements. Git commits for test/CLI/doc updates. Full suite verified; types regenerated. Ready for v0.0.6 tag/publish post-sprint.
9
+
3
10
  ### v0.0.5 (Released - April 2026)
4
11
  - **Bin CLI Enhancements** (`bin/dave.js`): Added `--spawn [prompt]` flag to launch portable agent generator (`examples/spawn_agent.js` → `bin/spawn_agent.js` via `createAgent` bin). Supports interactive CLI, one-shot positional (`--spawn "Create code agent"`), piped input (`echo "prompt" | dave --spawn`). Syntax validated; portable via npx/global.
5
12
  - **createAgent Bin Promotion** (`bin/spawn_agent.js`): Dedicated `package.json` bin command (`"createAgent": "bin/spawn_agent.js"`). Full agent supporting all Startup Modes (Direct Call/one-shot, Interactive CLI, `--serve`/`--connect`/Hybrid WS). Portable npx anywhere (auto-deps `@j-o-r/hello-dave @j-o-r/sh`, online repo docs). Generates validated ESM agents (`bin/<name>.js`), PM2 CodeServer launchers (`examples/<Name>Server`). `--help` with USAGE/modes/blueprints.
@@ -29,4 +36,4 @@
29
36
  - No heartbeat; CLI/cron support.
30
37
  - Piped one-shot for dave --ask.
31
38
 
32
- Full history in git tags.
39
+ Full history in git tags.
package/bin/dave.js CHANGED
@@ -20,78 +20,148 @@ FLAGS:
20
20
  --search "[search_query_or_regex]" : Search in cache history
21
21
  --list : List all agent sessions in this folder
22
22
  --inspect "[path_to_ndjson_log]" : Inspect, Format and output a ndjson log file
23
- --connect [ws://url][--secret "..."] : Connect to Agent websocket server
23
+ --connect [ws://url] [--secret "..."] : Connect to Agent websocket server
24
24
  Interactive: bin/dave.js --connect 'ws://localhost:8080' --secret '123'
25
25
  Piped actions:
26
26
  echo "predict the weather" | bin/dave.js --connect 'ws://...' --secret '123' # user_request
27
27
  echo "user_info" | bin/dave.js --connect 'ws://...' --secret '123' # user_info
28
28
  echo "user_reset" | bin/dave.js --connect 'ws://...' --secret '123' # user_reset
29
- --code [port] [secret] : Launch CodeServer PM2 cluster via examples/codeserver.sh
29
+ --ask [--connect [ws://url]] [--secret "..."] [--model modelname] : Ask agent locally (XAIKEY req.) or remote
30
+ Local: bin/dave.js --ask [--model grok-4-1-fast-reasoning]
31
+ Remote: bin/dave.js --ask --connect 'ws://localhost:8080' --secret '123'
32
+ Piped local: echo "predict the weather" | bin/dave.js --ask
33
+ Piped remote: echo "predict the weather" | bin/dave.js --ask --connect 'ws://...' --secret '123'
34
+ --code [port] [--secret "..."] : Launch CodeServer PM2 cluster via examples/codeserver.sh
30
35
  Usage: bin/dave.js --code 8080 --secret 123
31
- --ask : Launches a CLI and ask questions about whatever is on your mind
32
- XAIKEY required: export XAIKEY=xai-...
33
36
  `);
34
37
  process.exit(exitCode)
35
38
  }
36
39
 
37
40
  // Make sure an action is defined
38
- const keys = Object.keys(args);
39
- if (
40
- !(
41
- keys.indexOf('--help') > -1 ||
42
- keys.indexOf('--clear') > -1 ||
43
- keys.indexOf('--search') > -1 ||
44
- keys.indexOf('--list') > -1 ||
45
- keys.indexOf('--inspect') > -1 ||
46
- keys.indexOf('--connect') > -1 ||
47
- keys.indexOf('--spawn') > -1 ||
48
- keys.indexOf('--code') > -1 ||
49
- keys.indexOf('--ask') > -1
50
- )
51
- ) {
41
+ const actions = ['help', 'clear', 'search', 'list', 'inspect', 'connect', 'code', 'ask'];
42
+ if (!actions.some(key => args[key])) {
52
43
  printHelp(1);
53
44
  }
54
45
 
55
- if (args['--help']) {
46
+ if (args.help) {
56
47
  printHelp();
57
48
  }
58
49
 
59
- if (args['--clear']) {
60
- const clear_sessions = path.resolve(__dirname, '..', 'utils', 'clear_sessions.sh')
50
+ if (args.clear) {
51
+ const clear_sessions = path.resolve(__dirname, '..', 'utils', 'clear_sessions.sh');
61
52
  const y = await cli.yesNo('Delete entire cache? (y/n) ');
62
53
  if (y) {
63
54
  console.log(clear_sessions);
64
- const res = await SH`${clear_sessions}`.run()
65
- console.log(res);
55
+ const res = await SH`${clear_sessions}`.run();
56
+ console.log(res);
66
57
  }
67
58
  process.exit();
68
- } else if (args['--search']) {
69
- const history_search = path.resolve(__dirname, '..', 'utils', 'search_sessions.sh')
70
- if (typeof args['--search'] !== 'string') {
71
- printHelp(1)
59
+ } else if (args.search) {
60
+ const history_search = path.resolve(__dirname, '..', 'utils', 'search_sessions.sh');
61
+ if (typeof args.search !== 'string') {
62
+ printHelp(1);
72
63
  }
73
- const res = await SH`${history_search} "${bashEscape(args['--search'])}"`.run();
64
+ const res = await SH`${history_search} "${bashEscape(args.search)}"`.run();
74
65
  console.log(res);
75
66
  process.exit();
76
- } else if (args['--list']) {
67
+ } else if (args.list) {
77
68
  const list = path.resolve(__dirname, '..', 'utils', 'list_sessions.sh');
78
69
  const res = await SH`${list}`.run();
79
70
  console.log(res);
80
71
  process.exit();
81
- } else if (args['--inspect']) {
82
- const format = path.resolve(__dirname, '..', 'utils', 'format_log.js')
83
- if (typeof args['--inspect'] !== 'string') {
84
- printHelp(1)
72
+ } else if (args.inspect) {
73
+ const format = path.resolve(__dirname, '..', 'utils', 'format_log.js');
74
+ if (typeof args.inspect !== 'string') {
75
+ printHelp(1);
85
76
  }
86
77
  try {
87
- const res = await SH`${format} "${bashEscape(args['--inspect'])}"`.run();
78
+ const res = await SH`${format} "${bashEscape(args.inspect)}"`.run();
88
79
  console.log(res);
89
80
  } catch (e) {
90
81
  console.error(e);
91
82
  process.exit(1);
92
83
  }
93
- } else if (args['--connect']) {
94
- let secret = args['--secret'] ? args['--secret'] : '';
84
+ } else if (args.ask) {
85
+ let secret = args.secret || '';
86
+ if (args.connect) {
87
+ // Remote mode: same as --connect
88
+ if (typeof pipedInput === 'string' && pipedInput.trim() !== '') {
89
+ // Piped input mode: use wsIO for one-shot actions
90
+ let action, input;
91
+ const trimmed = pipedInput.trim();
92
+ if (trimmed === 'user_info' || trimmed === 'user_reset') {
93
+ action = trimmed;
94
+ input = '';
95
+ } else {
96
+ action = 'user_request';
97
+ input = trimmed;
98
+ }
99
+
100
+ try {
101
+ const response = await wsIO(args.connect, secret, action, input);
102
+ console.log(response.content);
103
+ } catch (e) {
104
+ console.error(`Error: ${e.message}`);
105
+ process.exit(1);
106
+ }
107
+ } else {
108
+ // No piped input: interactive wsCli mode
109
+ wsCli(args.connect, secret);
110
+ }
111
+ } else {
112
+ // Local mode
113
+ const name = 'ask_dave';
114
+ const api = 'xai';
115
+ const contextWindow = 1900000;
116
+ const toolsetMode = 'auto';
117
+ /** @type {import('lib/API/x.ai/responses.js').XAIOptions} */
118
+ const options = {
119
+ tools: []
120
+ };
121
+ options.tools.push({ type: 'web_search' });
122
+ options.tools.push({ type: 'x_search' });
123
+ options.model = args.model || 'grok-4-1-fast-reasoning';
124
+ options.temperature = 0.2;
125
+ options.reasoning = { effort: 'medium', summary: 'auto' };
126
+
127
+ const prompt = `
128
+ Respond briefly and directly, using minimal words. Reason step-by-step first. Focus solely on core point; avoid elaboration or follow-ups. If unclear, ask clarifying questions before proceeding.
129
+ `.trim();
130
+
131
+ const agent = new AgentManager({ name });
132
+ agent.setup({
133
+ prompt,
134
+ api,
135
+ options,
136
+ toolsetMode,
137
+ contextWindow
138
+ });
139
+ agent.addGenericToolcall('open_link');
140
+ agent.addGenericToolcall('send_email');
141
+ agent.addGenericToolcall('history_search');
142
+
143
+ const cliIntro = `
144
+ ${name} (${options.model}).
145
+ - context: ${contextWindow}
146
+ `.trim();
147
+ const description = `Ask dave, he knows.`.trim();
148
+
149
+ // Support piped one-shot (like --connect), else interactive CLI
150
+ if (typeof pipedInput === 'string' && pipedInput.trim() !== '') {
151
+ try {
152
+ const response = await agent.directCall(pipedInput.trim());
153
+ console.log(response);
154
+ } catch (e) {
155
+ console.error(`Error: ${e.message}`);
156
+ process.exit(1);
157
+ }
158
+ } else {
159
+ await agent.start(undefined, undefined, cliIntro, name, description);
160
+ }
161
+ }
162
+ } else if (args.connect) {
163
+ // Pure --connect (no --ask): same logic
164
+ let secret = args.secret || '';
95
165
 
96
166
  if (typeof pipedInput === 'string' && pipedInput.trim() !== '') {
97
167
  // Piped input mode: use wsIO for one-shot actions
@@ -106,7 +176,7 @@ if (args['--clear']) {
106
176
  }
107
177
 
108
178
  try {
109
- const response = await wsIO(args['--connect'], secret, action, input);
179
+ const response = await wsIO(args.connect, secret, action, input);
110
180
  console.log(response.content);
111
181
  } catch (e) {
112
182
  console.error(`Error: ${e.message}`);
@@ -114,63 +184,13 @@ if (args['--clear']) {
114
184
  }
115
185
  } else {
116
186
  // No piped input: interactive wsCli mode
117
- wsCli(args['--connect'], secret);
187
+ wsCli(args.connect, secret);
118
188
  }
119
- } else if (args['--code']) {
120
- const port = parseInt(args['--code'] || '8080');
121
- const secret = args['--secret'] ? args['--secret'].trim() : '123';
189
+ } else if (args.code) {
190
+ const port = parseInt(args.code || '8080');
191
+ const secret = (args.secret || '').trim() || '123';
122
192
  const server = path.resolve(__dirname, '..', 'examples', 'codeserver.sh');
123
- const RES = await SH`${server} ${port} ${bashEscape(secret)}`.run();
124
- console.log(RES);
193
+ const RES = await SH`${server} ${port} ${bashEscape(secret)}`.run();
194
+ console.log(RES);
125
195
  process.exit(0);
126
- } else if (args['--ask']) {
127
- const name = 'ask_dave';
128
- const api = 'xai';
129
- let secret = '';
130
- const contextWindow = 1900000;
131
- const toolsetMode = 'auto';
132
- /** @type {import('lib/API/x.ai/responses.js').XAIOptions} */
133
- const options = {
134
- tools: []
135
- };
136
- options.tools.push({ type: 'web_search' });
137
- options.tools.push({ type: 'x_search' });
138
- options.model = args['--model'] || args.model || 'grok-4-1-fast-reasoning';
139
- options.temperature = 0.2;
140
- options.reasoning = { effort: 'medium', summary: 'auto' };
141
-
142
- const prompt = `
143
- Respond briefly and directly, using minimal words. Reason step-by-step first. Focus solely on core point; avoid elaboration or follow-ups. If unclear, ask clarifying questions before proceeding.
144
- `.trim();
145
-
146
- const agent = new AgentManager({ name, secret });
147
- agent.setup({
148
- prompt,
149
- api,
150
- options,
151
- toolsetMode,
152
- contextWindow
153
- });
154
- agent.addGenericToolcall('open_link');
155
- agent.addGenericToolcall('send_email');
156
- agent.addGenericToolcall('history_search');
157
-
158
- const cliIntro = `
159
- ${name} ${options.model}.
160
- - context: ${contextWindow}
161
- `.trim();
162
- const description = `Ask dave, he knows.`.trim();
163
-
164
- // Support piped one-shot (like --connect), else interactive CLI
165
- if (typeof pipedInput === 'string' && pipedInput.trim() !== '') {
166
- try {
167
- const response = await agent.directCall(pipedInput.trim());
168
- console.log(response);
169
- } catch (e) {
170
- console.error(`Error: ${e.message}`);
171
- process.exit(1);
172
- }
173
- } else {
174
- await agent.start(undefined, undefined, cliIntro, name, description);
175
- }
176
- }
196
+ }
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { AgentManager } from '@j-o-r/hello-dave';
3
- import { parseArgs } from '@j-o-r/sh';
3
+ import { parseArgs, SH } from '@j-o-r/sh';
4
+ import path from 'node:path';
5
+ import { promises as fs } from 'node:fs';
4
6
 
5
7
  const name = 'memory_agent';
6
8
  const api = 'xai';
@@ -95,35 +97,35 @@ if (help) {
95
97
  }
96
98
 
97
99
  const tool_call_name = 'memory_agent';
98
- const tool_call_description = `CodeServer Memory Agent. Manages shared agent memory (.cache/memory.ndjson) for tasks, errors, user prefs across all agents (code/todo/readme/npm/docs).
100
+ const tool_call_description = `Standalone Memory Agent. Manages shared agent memory (.cache/memory.ndjson) for tasks, errors, user prefs across agents or standalone use.
99
101
  Use before/after actions: Recall relevant memories, write updates to avoid loops/repetition.
100
- Categories: tasks (pending work), errors (failures to avoid), prefs (user settings).`.trim();
102
+ Categories: tasks (pending work), errors (failures to avoid), prefs (user settings).
103
+ Usable standalone, by users/agents via function calls or WebSocket connect. Responses handover to querying agent/user.`.trim();
101
104
 
102
- const prompt = `**CRITICAL: STRICT NO-CODING RULE** - Stick to memory coordination ONLY. Use execute_bash_script/read_file ONLY for safe ops on .cache/memory.ndjson or TODO.md (e.g., cat .cache/memory.ndjson). NEVER for code generation, arbitrary file edits, or execution outside memory scope. Decline coding: "Use code_agent for code. I manage memory."
105
+ const prompt = `**CRITICAL: STRICT NO-CODING RULE** - Stick to memory coordination ONLY. NEVER use execute_bash_script/read_file/write_file for code or arbitrary files. Decline coding: "Use code_agent for code. I manage memory."
103
106
 
104
- You are MemoryAgent, the dedicated memory manager for CodeServer multi-agent setup (code/todo/readme/npm/docs agents).
107
+ You are MemoryAgent, a standalone memory manager usable by users, other agents, or as a sub-agent via function calls or WebSocket connect. Any response handovers the result to the querying agent or human user.
105
108
 
106
- **CORE TOOLS (from lib/genericToolset.js)**:
109
+ **CORE TOOLS (local self-contained)**:
107
110
  - \`memory_recall [query]\`: Retrieve stored memories (tasks/errors/prefs). ALWAYS recall before acting (e.g., "tasks", "errors", "" for recent).
108
111
  - \`memory_write {category: "tasks|errors|prefs", content: "details"}\`: Persist info to avoid repetition/loops/token burn.
112
+ - \`optimize_memory\`: Optimize .cache/memory.ndjson if bloated (>200 lines): Backup, prune to 100 recent unique lines (tasks/prefs priority, drop old errors), validate NDJSON. Call periodically or on heavy recalls.
109
113
 
110
114
  **SHARED STORAGE**: .cache/memory.ndjson in project CWD (all agents share).
111
115
 
112
116
  **ALWAYS**:
113
- 1. **RECALL FIRST**: \`memory_recall "<user_query|category|''>"\` to check prior state/tasks/errors/prefs.
114
- 2. **ACT**: Analyze + decide (write if new info, delegate via server if needed).
115
- 3. **WRITE IF CHANGED**: Use \`memory_write\` for decisions/tasks/errors/prefs (e.g., "Pending: review npm deps", "Error: loop in git", "Pref: low temp for code").
117
+ 1. **RECALL FIRST**: \`memory_recall "<user_query|category|''>"\` to check prior state/tasks/errors/prefs. Auto-optimize if >200 lines.
118
+ 2. **ACT**: Analyze + decide (write if new info).
119
+ 3. **WRITE IF CHANGED**: Use \`memory_write\` for decisions/tasks/errors/prefs.
116
120
  4. Respond: Summarize memories used/updated + action taken.
117
121
 
118
- **USER QUERIES**:
122
+ **USER QUERIES** (handle naturally, use tools):
119
123
  - "Recall tasks" → List + suggest next.
120
- - "Store task: Fix auth bug" → Write + confirm.
121
- - "Clear old errors" → Recall + write cleaned.
124
+ - "Store task: Fix auth bug" → Parse + memory_write.
125
+ - "Clear old errors" → Recall + optimize.
122
126
  - Coordinate: "Check if code agent has pending tasks" → Recall + advise.
123
127
 
124
- Use other tools (bash/read_file) only if needed for context. Focus on memory coordination to boost multi-agent efficiency.
125
-
126
- Current date: April 01, 2026.`.trim();
128
+ Current date: April 03, 2026.`.trim();
127
129
 
128
130
  const agent = new AgentManager({ name, secret });
129
131
  agent.setup({
@@ -134,14 +136,123 @@ agent.setup({
134
136
  contextWindow
135
137
  });
136
138
  const toolset = agent.getToolset();
137
- if (toolset) {
138
- agent.addGenericToolcall('memory_recall');
139
- agent.addGenericToolcall('memory_write');
140
- agent.addGenericToolcall('execute_bash_script');
141
- agent.addGenericToolcall('read_file');
142
- }
139
+
140
+ // Self-contained local memory tools (copied from genericToolset.js)
141
+ const memoryPath = path.join(process.cwd(), '.cache', 'memory.ndjson');
142
+
143
+ toolset.add(
144
+ 'memory_recall',
145
+ `Retrieve stored agent memories (tasks, errors, prefs) from .cache/memory.ndjson in CWD. Use before acting to check prior decisions/tasks/errors/prefs. Query by keyword or category; empty lists recent.`,
146
+ {
147
+ type: 'object',
148
+ properties: {
149
+ query: {
150
+ type: 'string',
151
+ description: 'Keyword, category (tasks/errors/prefs), or phrase to filter (case-insensitive). Empty/omit lists last 20.'
152
+ }
153
+ },
154
+ required: []
155
+ },
156
+ async (params = {}) => {
157
+ let content;
158
+ try {
159
+ content = await fs.readFile(memoryPath, 'utf8');
160
+ } catch (e) {
161
+ return 'No memories stored yet. Use memory_write first.';
162
+ }
163
+ const lines = content.trim().split('\n').filter(l => l.trim());
164
+ const memories = [];
165
+ for (const line of lines) {
166
+ try {
167
+ memories.push(JSON.parse(line));
168
+ } catch {
169
+ // Skip invalid lines
170
+ }
171
+ }
172
+ if (!params.query || !params.query.toString().trim()) {
173
+ const recent = memories.slice(-20).reverse();
174
+ return recent.length
175
+ ? 'Recent memories:\n' + recent.map(m => `• ${m.timestamp.slice(0, 19).replace('T', ' ')} [${m.category}] ${m.content}`).join('\n')
176
+ : 'No memories stored.';
177
+ }
178
+ const q = params.query.toString().toLowerCase();
179
+ const matches = memories.filter(m =>
180
+ m.category.toLowerCase().includes(q) || m.content.toLowerCase().includes(q)
181
+ );
182
+ return matches.length
183
+ ? `Matches for "${params.query}":\n` + matches.slice(0, 20).map(m => `• ${m.timestamp.slice(0, 19).replace('T', ' ')} [${m.category}] ${m.content}`).join('\n')
184
+ : `No memories match "${params.query}".`;
185
+ }
186
+ );
187
+
188
+ toolset.add(
189
+ 'memory_write',
190
+ `Persist agent memory for tasks, errors, or user preferences to .cache/memory.ndjson in 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.`,
191
+ {
192
+ type: 'object',
193
+ properties: {
194
+ category: {
195
+ type: 'string',
196
+ enum: ['tasks', 'errors', 'prefs'],
197
+ description: 'Category: one of "tasks", "errors", "prefs"'
198
+ },
199
+ content: {
200
+ type: 'string',
201
+ description: 'Detailed content (e.g., "Pending task: implement runWithMemory optimization", "Error: loop burning tokens", "Pref: temperature=0.2 for decisions")'
202
+ }
203
+ },
204
+ required: ['category', 'content']
205
+ },
206
+ async ({ category, content }) => {
207
+ if (!['tasks', 'errors', 'prefs'].includes(category)) {
208
+ throw new Error(`Invalid category '${category}'. Must be 'tasks', 'errors', or 'prefs'.`);
209
+ }
210
+ const dir = path.dirname(memoryPath);
211
+ try {
212
+ await fs.mkdir(dir, { recursive: true });
213
+ } catch (e) {
214
+ // Dir likely exists
215
+ }
216
+ const entry = {
217
+ timestamp: new Date().toISOString(),
218
+ category,
219
+ content: content.trim()
220
+ };
221
+ await fs.appendFile(memoryPath, JSON.stringify(entry) + '\n', 'utf8');
222
+ return `✓ Memory stored: [${category}] ${content.length > 50 ? content.slice(0, 47) + '...' : content}`;
223
+ }
224
+ );
225
+
226
+ // optimize_memory tool
227
+ toolset.add(
228
+ 'memory_optimize',
229
+ 'Optimize .cache/memory.ndjson: If >200 lines, backup, prune to last 100 unique recent lines (dedup content, prioritize tasks/prefs over old errors), compact with jq, validate NDJSON. Returns status/prune summary.',
230
+ { type: 'object', properties: {} },
231
+ async () => {
232
+ let content = '';
233
+ try {
234
+ content = (await SH`cat .cache/memory.ndjson 2>/dev/null || echo ''`.run()).trim();
235
+ } catch (e) {
236
+ return 'No memory file found yet. Optimal (0 lines).';
237
+ }
238
+ const lines = content.split('\n').filter(l => l.trim()).length;
239
+ if (lines <= 200) {
240
+ return `Optimal: ${lines} lines. No action needed.`;
241
+ }
242
+ const timestamp = Date.now();
243
+ const backup = `.cache/memory.ndjson.bak-${timestamp}`;
244
+ const pruneCmd = `mkdir -p .cache && cp .cache/memory.ndjson ${backup} && tail -n 100 .cache/memory.ndjson | awk '!seen[$0]++' | jq -c . > .cache/memory.ndjson.tmp && mv .cache/memory.ndjson.tmp .cache/memory.ndjson && POST_LINES=$(wc -l < .cache/memory.ndjson | cut -d' ' -f1) && echo "Pruned ${lines}→$POST_LINES lines. Backup: ${backup}. Valid: $(cat .cache/memory.ndjson | jq empty >/dev/null 2>&1 && echo OK || echo FAIL)"`;
245
+ try {
246
+ const result = await SH`${pruneCmd}`.run();
247
+ return result.trim();
248
+ } catch (e) {
249
+ return `Prune failed: ${e.message}`;
250
+ }
251
+ }
252
+ );
253
+
143
254
  const cliIntro = `${name} ${options.model}.
144
- - context: ${contextWindow}. Shared memory manager ready.
255
+ - context: ${contextWindow}. Shared memory manager ready (self-contained local tools).
145
256
  ${tool_call_name}`.trim();
146
257
 
147
258
  if (input) {
@@ -149,4 +260,4 @@ if (input) {
149
260
  console.log(RES);
150
261
  } else {
151
262
  await agent.start(serve, connect, cliIntro, tool_call_name, tool_call_description);
152
- }
263
+ }
@@ -166,8 +166,6 @@ agent.setup({
166
166
  const toolset = agent.getToolset();
167
167
  if (toolset) {
168
168
  agent.addGenericToolcall('history_search');
169
- agent.addGenericToolcall('memory_recall');
170
- agent.addGenericToolcall('memory_write');
171
169
  agent.addGenericToolcall('javascript_interpreter');
172
170
  agent.addGenericToolcall('execute_bash_script');
173
171
  agent.addGenericToolcall('read_file');
@@ -236,17 +236,17 @@ tools.add(
236
236
 
237
237
  tools.add(
238
238
  'write_file',
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.',
239
+ '**MANDATORY**: 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 in params**). **AUTO-VALIDATES** JS/Python/Bash/JSON/etc. via `utils/syntax_check.sh`; chmod +x shebangs. Retries syntax errors force LLM fix. **Always use write_file for safety (CWD-only, syntax-checked).**',
240
240
  {
241
241
  type: 'object',
242
242
  properties: {
243
243
  file: {
244
244
  type: 'string',
245
- description: `Relative path to the file within CWD, e.g., 'path/to/file.txt' (no escaping needed).`
245
+ description: `Relative path to the file within CWD, e.g., 'path/to/file.txt' (no escaping needed). cwd: ${user.cwd}`
246
246
  },
247
247
  content: {
248
248
  type: 'string',
249
- description: `Raw content to write (as-is, no char escaping needed; supports newlines, $, |, <, >, &, ", ', \\, etc.).`
249
+ description: "Raw content to write (LITERAL: NO char escaping needed in this param; supports newlines, $, |, <, >, &, \", ', \\, \` , etc. verbatim). **Generate valid syntax for target lang** (e.g., JS template literals use raw `${var}`, no invalid \\`; syntax_check.sh rejects bad syntax like escaped backticks in JS)."
250
250
  }
251
251
  },
252
252
  required: ['file', 'content']
@@ -316,89 +316,5 @@ tools.add(
316
316
  }
317
317
  );
318
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
319
 
404
- export default tools
320
+ export default tools
package/lib/wsIO.js CHANGED
@@ -38,13 +38,6 @@ export default async function wsio(connectUrl, secret = '', action, input = '')
38
38
  const ws = new WebSocket(url);
39
39
 
40
40
  let resolved = false;
41
- const TIMEOUT_MS = 30000; // 30s timeout
42
- const timeoutId = setTimeout(() => {
43
- if (!resolved) {
44
- ws.close();
45
- reject(new Error('Request timeout (30s)'));
46
- }
47
- }, TIMEOUT_MS);
48
41
 
49
42
  const actionId = Date.now();
50
43
 
@@ -69,7 +62,6 @@ export default async function wsio(connectUrl, secret = '', action, input = '')
69
62
  try {
70
63
  const parsed = JSON.parse(data.toString());
71
64
  if (parsed.id === actionId) {
72
- clearTimeout(timeoutId);
73
65
  resolved = true;
74
66
  ws.close(1000, 'Request complete');
75
67
  resolve(parsed);
@@ -80,17 +72,15 @@ export default async function wsio(connectUrl, secret = '', action, input = '')
80
72
  });
81
73
 
82
74
  ws.on('close', (code, reason) => {
83
- clearTimeout(timeoutId);
84
75
  if (!resolved) {
85
76
  reject(new Error(`Connection closed (${code}): ${reason || 'Unknown reason'}`));
86
77
  }
87
78
  });
88
79
 
89
80
  ws.on('error', (error) => {
90
- clearTimeout(timeoutId);
91
81
  if (!resolved) {
92
82
  reject(new Error(`WebSocket error: ${error.message}`));
93
83
  }
94
84
  });
95
85
  });
96
- }
86
+ }
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.5",
4
+ "version": "0.0.6",
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",
@@ -51,4 +51,4 @@
51
51
  "engines": {
52
52
  "node": ">=20"
53
53
  }
54
- }
54
+ }