@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.
- package/CHANGELOG.md +8 -1
- package/bin/dave.js +114 -94
- package/examples/memory_agent.js +134 -23
- package/examples/test_agent.js +0 -2
- package/lib/genericToolset.js +4 -88
- package/lib/wsIO.js +1 -11
- package/package.json +2 -2
- package/README.md +0 -601
- package/README.md.backup +0 -269
- package/README.md.bak +0 -523
- package/README.md.bak.1774780058 +0 -338
- package/README.md.bak2 +0 -531
- package/docs.bak.1774780058/agent-manager.md +0 -167
- package/docs.bak.1774780058/agent-manager.md.bak +0 -137
- package/docs.bak.1774780058/agent-manager.md.bak2 +0 -157
- package/docs.bak.1774780058/codeserver-pattern.md +0 -191
- package/docs.bak.1774780058/path-resolution-best-practices.md +0 -104
- package/docs.bak.1774780058/project-overview.md +0 -67
- package/docs.bak.1774780058/project-overview.md.bak +0 -67
- package/docs.bak.1774780058/prompt-class.md +0 -141
- package/docs.bak.1774780058/prompt-class.md.bak +0 -142
- package/docs.bak.1774780058/tools-syntax-validation.md +0 -121
- package/docs.bak.1774780058/tools-syntax-validation.md.bak2 +0 -125
- package/docs.bak.1774780058/tools-syntax-validation.md.bak3 +0 -125
- package/docs.bak.1774780058/tools-syntax-validation.md.bak4 +0 -106
- package/docs.bak.1774780058/tools-syntax-validation.md.bak_path +0 -106
- package/docs.bak.1774780058/toolset.md +0 -164
- package/docs.bak.1774780058/toolset.md.bak +0 -94
- package/docs.bak.1774780058/toolset.md.bak3 +0 -161
- package/docs.bak.1774780058/toolset.md.bak4 +0 -161
- package/docs.bak.1774780058/toolset.md.bak5 +0 -161
- package/docs.bak.1774780058/toolset.md.bak6 +0 -163
- package/docs.bak.1774780058/toolset.md.bak_path +0 -163
- package/docs.bak.1774780058/toolset.md.bak_syntax +0 -161
- package/docs.bak.1774780058/xai-responses.md +0 -111
- package/docs.bak.1774780058/xai-responses.md.bak +0 -107
- package/docs.bak.1774780058/xai-responses.md.bak2 +0 -107
- package/docs.bak.1774780058/xai_collections.md +0 -106
- package/examples.bak.1774780058/ask_agent.js +0 -114
- package/examples.bak.1774780058/code_agent.js +0 -149
- package/examples.bak.1774780058/coderev_agent.js +0 -72
- package/examples.bak.1774780058/codeserver.sh +0 -47
- package/examples.bak.1774780058/daisy_agent.js +0 -177
- package/examples.bak.1774780058/docs_agent.js +0 -119
- package/examples.bak.1774780058/gpt_agent.js +0 -109
- package/examples.bak.1774780058/grok_agent.js +0 -98
- package/examples.bak.1774780058/memory_agent.js +0 -112
- package/examples.bak.1774780058/npm_agent.js +0 -175
- package/examples.bak.1774780058/prompt_agent.js +0 -112
- package/examples.bak.1774780058/readme_agent.js +0 -144
- package/examples.bak.1774780058/spawn_agent.js +0 -263
- package/examples.bak.1774780058/test_agent.js +0 -162
- package/examples.bak.1774780058/todo_agent.js +0 -138
- package/scenarios.bak.1774780058/data/eval_node_message.json +0 -9
- package/scenarios.bak.1774780058/data/hist_oa.json +0 -66
- package/scenarios.bak.1774780058/data/o3_response1.json +0 -96
- package/scenarios.bak.1774780058/data/oa_reasoning_parse.json +0 -112
- package/scenarios.bak.1774780058/data/tool_oa.json +0 -96
- package/scenarios.bak.1774780058/data/tool_xai.json +0 -59
- package/scenarios.bak.1774780058/data/tool_xai2.json +0 -40
- package/scenarios.bak.1774780058/data/xai-response-1.json +0 -59
- package/scenarios.bak.1774780058/data/xai-response-2.json +0 -10
- package/scenarios.bak.1774780058/data/xai_reasoning_tools_resp.json +0 -59
- package/scenarios.bak.1774780058/data/xai_search_response.json +0 -58
- package/scenarios.bak.1774780058/environment.js +0 -10
- package/scenarios.bak.1774780058/example.js +0 -17
- package/scenarios.bak.1774780058/genericToolset.test.js +0 -182
- package/scenarios.bak.1774780058/grok.js +0 -113
- package/scenarios.bak.1774780058/memory-tools.js +0 -51
- package/scenarios.bak.1774780058/openai-o3.js +0 -137
- package/scenarios.bak.1774780058/openai-prompt.js +0 -155
- package/scenarios.bak.1774780058/openai-session.js +0 -148
- package/scenarios.bak.1774780058/openai.js +0 -102
- package/scenarios.bak.1774780058/prompt.js +0 -118
- package/scenarios.bak.1774780058/promptFishbowl.js +0 -76
- package/scenarios.bak.1774780058/search.brave.com.js +0 -25
- package/scenarios.bak.1774780058/sh.js +0 -15
- package/scenarios.bak.1774780058/test-wsio.js +0 -26
- package/scenarios.bak.1774780058/testToolset.js +0 -42
- package/scenarios.bak.1774780058/toolset.js +0 -16
- package/scenarios.bak.1774780058/toolset.test.js +0 -141
- package/scenarios.bak.1774780058/write_file_syntax.test.js +0 -145
- package/scenarios.bak.1774780058/write_file_validation/README.md +0 -30
- package/scenarios.bak.1774780058/write_file_validation/bad.js +0 -3
- package/scenarios.bak.1774780058/write_file_validation/good.js +0 -4
- package/scenarios.bak.1774780058/write_file_validation/test.sh +0 -43
- package/scenarios.bak.1774780058/wsClient.js +0 -69
- package/scenarios.bak.1774780058/xai_responses.integration.test.js +0 -57
- package/scenarios.bak.1774780058/xai_responses.test.js +0 -154
- package/scenarios.bak.1774780058/xaicoll.js +0 -50
- 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
|
-
--
|
|
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
|
|
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
|
|
46
|
+
if (args.help) {
|
|
56
47
|
printHelp();
|
|
57
48
|
}
|
|
58
49
|
|
|
59
|
-
if (args
|
|
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
|
-
|
|
65
|
-
|
|
55
|
+
const res = await SH`${clear_sessions}`.run();
|
|
56
|
+
console.log(res);
|
|
66
57
|
}
|
|
67
58
|
process.exit();
|
|
68
|
-
} else if (args
|
|
69
|
-
const history_search = path.resolve(__dirname, '..', 'utils', 'search_sessions.sh')
|
|
70
|
-
if (typeof args
|
|
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
|
|
64
|
+
const res = await SH`${history_search} "${bashEscape(args.search)}"`.run();
|
|
74
65
|
console.log(res);
|
|
75
66
|
process.exit();
|
|
76
|
-
} else if (args
|
|
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
|
|
82
|
-
const format = path.resolve(__dirname, '..', 'utils', 'format_log.js')
|
|
83
|
-
if (typeof args
|
|
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
|
|
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
|
|
94
|
-
let secret = args
|
|
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
|
|
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
|
|
187
|
+
wsCli(args.connect, secret);
|
|
118
188
|
}
|
|
119
|
-
} else if (args
|
|
120
|
-
const port = parseInt(args
|
|
121
|
-
const secret = args
|
|
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
|
-
|
|
124
|
-
|
|
193
|
+
const RES = await SH`${server} ${port} ${bashEscape(secret)}`.run();
|
|
194
|
+
console.log(RES);
|
|
125
195
|
process.exit(0);
|
|
126
|
-
}
|
|
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
|
+
}
|
package/examples/memory_agent.js
CHANGED
|
@@ -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 = `
|
|
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)
|
|
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.
|
|
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,
|
|
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 (
|
|
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
|
|
115
|
-
3. **WRITE IF CHANGED**: Use \`memory_write\` for decisions/tasks/errors/prefs
|
|
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" →
|
|
121
|
-
- "Clear old errors" → Recall +
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
+
}
|
package/examples/test_agent.js
CHANGED
|
@@ -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');
|
package/lib/genericToolset.js
CHANGED
|
@@ -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 (
|
|
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:
|
|
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.
|
|
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
|
+
}
|