@j-o-r/hello-dave 0.0.5 → 0.0.7
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 +13 -26
- package/README.md +161 -522
- package/README.md.bak +144 -449
- package/{examples → agents}/ask_agent.js +5 -5
- package/{examples → agents}/codeserver.sh +14 -14
- package/{examples → agents}/daisy_agent.js +5 -5
- package/{examples → agents}/docs_agent.js +5 -5
- package/{examples → agents}/gpt_agent.js +5 -5
- package/{examples → agents}/grok_agent.js +5 -5
- package/agents/memory_agent.js +263 -0
- package/{examples → agents}/npm_agent.js +5 -5
- package/{examples → agents}/prompt_agent.js +5 -5
- package/agents/spawn_agent.js +137 -0
- package/{examples → agents}/test_agent.js +6 -8
- package/{examples → agents}/todo_agent.js +5 -5
- package/bin/codeDave +58 -0
- package/bin/dave.js +114 -96
- package/lib/AgentClient.js +111 -67
- package/lib/AgentManager.js +111 -80
- package/lib/AgentServer.js +144 -104
- package/lib/Cli.js +126 -93
- package/lib/Prompt.js +38 -5
- package/lib/Session.js +102 -79
- package/lib/ToolSet.js +79 -60
- package/lib/fafs.js +54 -19
- package/lib/genericToolset.js +109 -213
- package/lib/wsCli.js +50 -19
- package/lib/wsIO.js +11 -17
- package/package.json +2 -2
- package/types/AgentClient.d.ts +69 -35
- package/types/AgentManager.d.ts +50 -56
- package/types/AgentServer.d.ts +63 -16
- package/types/Cli.d.ts +56 -10
- package/types/Prompt.d.ts +36 -4
- package/types/Session.d.ts +23 -9
- package/types/ToolSet.d.ts +49 -32
- package/types/fafs.d.ts +68 -25
- package/types/wsCli.d.ts +14 -0
- package/types/wsIO.d.ts +9 -5
- package/utils/search_sessions.sh +100 -53
- package/README.md.backup +0 -269
- package/README.md.bak.1774780058 +0 -338
- package/README.md.bak2 +0 -531
- package/bin/spawn_agent.js +0 -293
- 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/memory_agent.js +0 -152
- 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/lib/genericToolset.js.bak_syntax +0 -402
- 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/{examples → agents}/code_agent.js +0 -0
- /package/{examples → agents}/readme_agent.js +0 -0
package/lib/Session.js
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Session manager for recording and retrieving chat sessions tied to a Prompt instance.
|
|
3
|
+
* Handles caching of messages, records, and logs in NDJSON files under `.cache/hello-dave/<name>/`.
|
|
4
|
+
* Automatically creates session names from first non-sticky user message.
|
|
5
|
+
* Listens to Prompt events (message, record, reset, truncated, others) for persistence.
|
|
6
|
+
*
|
|
7
|
+
* Properties:
|
|
8
|
+
* - `name` (string, readonly): Sanitized prompt name used as session root folder.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```
|
|
12
|
+
* const session = new Session('my-prompt', prompt, './custom-cache');
|
|
13
|
+
* console.log(session.info()); // Session details
|
|
14
|
+
* const sessions = session.sessionList(); // ['user query', 'another']
|
|
15
|
+
* const messages = session.set('user-query'); // Load/advance session
|
|
16
|
+
* session.empty(); // Clear all data
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @property {string} name - Sanitized prompt name (read-only).
|
|
20
|
+
* @fires Prompt#message - Appends message to current session cache.
|
|
21
|
+
* @fires Prompt#record - Appends record to daily records cache.
|
|
22
|
+
* @fires Prompt#reset - Resets session counter/name/queue to prompt.messages.
|
|
23
|
+
* @fires Prompt#truncated - Resets session like #reset.
|
|
5
24
|
*/
|
|
6
25
|
|
|
7
26
|
import path from 'path';
|
|
@@ -14,12 +33,12 @@ import Prompt from './Prompt.js';
|
|
|
14
33
|
*/
|
|
15
34
|
|
|
16
35
|
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
* Get the next version number for a given key in the array.
|
|
37
|
+
*
|
|
38
|
+
* @param {string[]} arr - The array of versioned strings.
|
|
39
|
+
* @param {string} key - The key to check for the highest version.
|
|
40
|
+
* @returns {number} The next version number.
|
|
41
|
+
*/
|
|
23
42
|
const getNextVersion = (arr, key) => {
|
|
24
43
|
const regex = new RegExp(`^${key}_(\\d+)$`);
|
|
25
44
|
const versions = arr
|
|
@@ -33,51 +52,44 @@ const getNextVersion = (arr, key) => {
|
|
|
33
52
|
};
|
|
34
53
|
|
|
35
54
|
/**
|
|
36
|
-
|
|
55
|
+
* Session class for managing persistent chat sessions with Prompt.
|
|
56
|
+
*/
|
|
37
57
|
class Session {
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*/
|
|
42
|
-
#que = [];
|
|
43
|
-
/** @type {string} session name */
|
|
58
|
+
/** @type {Message[]} Sticky message queue. */
|
|
59
|
+
#queue = [];
|
|
60
|
+
/** @type {string} Current session name. */
|
|
44
61
|
#sessionName = '';
|
|
45
|
-
/** @type {number}
|
|
62
|
+
/** @type {number} Current session iteration counter. */
|
|
46
63
|
#sessionCounter = 0;
|
|
47
|
-
/** @type {CacheSync} */
|
|
64
|
+
/** @type {CacheSync} Session messages cache. */
|
|
48
65
|
#sessionCache;
|
|
49
|
-
/** @type {CacheSync} */
|
|
66
|
+
/** @type {CacheSync} Records cache (billing/optimizing). */
|
|
50
67
|
#recordCache;
|
|
51
|
-
/** @type {CacheSync} */
|
|
68
|
+
/** @type {CacheSync} Logs cache (events). */
|
|
52
69
|
#logCache;
|
|
70
|
+
/** @type {string} Sanitized prompt name (public readonly). */
|
|
71
|
+
name;
|
|
53
72
|
|
|
54
73
|
/**
|
|
55
|
-
*
|
|
74
|
+
* Initializes cache folders and event listeners.
|
|
56
75
|
* @param {string} promptName - The name of the prompt.
|
|
57
|
-
* @param {Prompt} prompt - The
|
|
58
|
-
* @param {string} [baseFolder] -
|
|
76
|
+
* @param {Prompt} prompt - The Prompt instance.
|
|
77
|
+
* @param {string} [baseFolder] - Base storage folder; defaults to `.cache/hello-dave`.
|
|
78
|
+
* @private
|
|
59
79
|
*/
|
|
60
|
-
#
|
|
61
|
-
// If not base folder, we take FOLDERS.ROOT
|
|
62
|
-
// else we use the baseFolder
|
|
80
|
+
#initializeCache = (promptName, prompt, baseFolder) => {
|
|
63
81
|
if (!baseFolder) {
|
|
64
|
-
// Relative from cwd()
|
|
65
82
|
baseFolder = path.resolve('.cache', 'hello-dave');
|
|
66
83
|
}
|
|
67
|
-
// Create the root folder, just in case
|
|
68
84
|
const root = new CacheSync(baseFolder, true, 'any');
|
|
69
|
-
// this.#baseStorage = baseFolder;
|
|
70
85
|
this.name = root.sanitizeKey(promptName);
|
|
71
86
|
const sessionStorage = path.resolve(baseFolder, this.name);
|
|
72
|
-
// Create the session folder
|
|
73
87
|
new CacheSync(sessionStorage, true, 'any');
|
|
74
|
-
// Create the records cache
|
|
75
88
|
this.#logCache = new CacheSync(path.resolve(sessionStorage, 'logs'), true, 'ndjson');
|
|
76
89
|
this.#recordCache = new CacheSync(path.resolve(sessionStorage, 'records'), true, 'ndjson');
|
|
77
90
|
if (this.name.length === 0) {
|
|
78
91
|
throw new Error(`Invalid session name: ${promptName}`);
|
|
79
92
|
}
|
|
80
|
-
// create and init the session storage
|
|
81
93
|
this.#sessionCache = new CacheSync(path.resolve(sessionStorage, 'sessions'), true, 'ndjson');
|
|
82
94
|
prompt.on(prompt.EVENTS.message, (msg) => {
|
|
83
95
|
this.#msg(msg);
|
|
@@ -86,89 +98,93 @@ class Session {
|
|
|
86
98
|
this.#rec(rec);
|
|
87
99
|
});
|
|
88
100
|
prompt.on(prompt.EVENTS.reset, () => {
|
|
89
|
-
// Prompt has only sticky messages
|
|
90
|
-
// fill the que
|
|
91
101
|
this.#sessionCounter = 0;
|
|
92
102
|
this.#sessionName = '';
|
|
93
|
-
this.#
|
|
103
|
+
this.#queue = prompt.messages;
|
|
94
104
|
});
|
|
95
105
|
prompt.on(prompt.EVENTS.truncated, () => {
|
|
96
|
-
// Prompt has only sticky messages
|
|
97
|
-
// fill the que
|
|
98
106
|
this.#sessionCounter = 0;
|
|
99
107
|
this.#sessionName = '';
|
|
100
|
-
this.#
|
|
108
|
+
this.#queue = prompt.messages;
|
|
101
109
|
});
|
|
102
110
|
const events = Object.keys(prompt.EVENTS);
|
|
103
111
|
events.forEach((evt) => {
|
|
104
112
|
prompt.on(evt, (msg) => {
|
|
105
113
|
this.#log({ evt, msg });
|
|
106
114
|
});
|
|
107
|
-
})
|
|
108
|
-
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
109
118
|
/**
|
|
110
|
-
* Constructs a new
|
|
111
|
-
* If contextWindow = 0 (
|
|
119
|
+
* Constructs a new Session instance.
|
|
120
|
+
* If contextWindow = 0 (default) the prompt will have no context building up (ONE_SHOT).
|
|
121
|
+
*
|
|
112
122
|
* @param {string} name - The name of the prompt.
|
|
113
|
-
* @param {Prompt} prompt - The
|
|
114
|
-
* @param {string} [storage] - The base storage folder
|
|
123
|
+
* @param {Prompt} prompt - The Prompt instance.
|
|
124
|
+
* @param {string} [storage] - The base storage folder; defaults to process.cwd()/.cache/hello-dave.
|
|
115
125
|
*/
|
|
116
126
|
constructor(name, prompt, storage) {
|
|
117
|
-
this.#
|
|
127
|
+
this.#initializeCache(name, prompt, storage);
|
|
118
128
|
}
|
|
119
129
|
|
|
120
|
-
info() {
|
|
121
|
-
const name = this.name;
|
|
122
|
-
const session = this.#sessionName;
|
|
123
|
-
const sessionCount = this.#sessionCounter;
|
|
124
|
-
const path = this.#sessionCache.folder;
|
|
125
|
-
return `Name: ${name}\nSession: ${session}\nSessionCount: ${sessionCount}\nPath: ${path}`;
|
|
126
|
-
}
|
|
127
130
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
131
|
+
* Returns current session information.
|
|
132
|
+
*
|
|
133
|
+
* @returns {string} Formatted info: Name, Session, SessionCount, Path.
|
|
134
|
+
*/
|
|
135
|
+
info() {
|
|
136
|
+
const name = this.name;
|
|
137
|
+
const session = this.#sessionName;
|
|
138
|
+
const sessionCount = this.#sessionCounter;
|
|
139
|
+
const path = this.#sessionCache.folder;
|
|
140
|
+
return `Name: ${name}\nSession: ${session}\nSessionCount: ${sessionCount}\nPath: ${path}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Appends a message to the current session cache (private).
|
|
145
|
+
* Auto-creates session name from first non-sticky user message.
|
|
146
|
+
*
|
|
147
|
+
* @param {Message} msg - The message to append.
|
|
148
|
+
* @private
|
|
130
149
|
*/
|
|
131
150
|
#msg(msg) {
|
|
132
151
|
const role = msg.role;
|
|
133
152
|
const sticky = msg.sticky;
|
|
134
153
|
let sessKey;
|
|
135
154
|
if (this.#sessionName === '' && role === 'user' && sticky === false) {
|
|
136
|
-
// Create a sessioname
|
|
137
155
|
if (msg.content.length > 0 && msg.content[0].type === 'text') {
|
|
138
156
|
/**
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
* @param {string} str
|
|
158
|
+
*/
|
|
141
159
|
const truncateString = (str) => str.length > 50 ? str.slice(0, 80) : str;
|
|
142
|
-
// @ts-ignore
|
|
143
160
|
this.#sessionName = this.#sessionCache.sanitizeKey(truncateString(msg.content[0].text).trim());
|
|
144
161
|
this.#sessionCounter = getNextVersion(this.#sessionCache.list(), this.#sessionName);
|
|
145
162
|
sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
|
|
146
163
|
}
|
|
147
|
-
} else if(this.#sessionName !== '') {
|
|
164
|
+
} else if (this.#sessionName !== '') {
|
|
148
165
|
sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
|
|
149
166
|
}
|
|
150
167
|
if (sessKey) {
|
|
151
|
-
|
|
152
|
-
// May the must be written sticky nots or a truncated previous sessions
|
|
153
|
-
if (this.#que.length > 0) {
|
|
154
|
-
// Write outstanding messages
|
|
168
|
+
if (this.#queue.length > 0) {
|
|
155
169
|
let i = 0;
|
|
156
|
-
const len = this.#
|
|
170
|
+
const len = this.#queue.length;
|
|
157
171
|
for (; i < len; i++) {
|
|
158
|
-
this.#sessionCache.append(sessKey, this.#
|
|
172
|
+
this.#sessionCache.append(sessKey, this.#queue[i]);
|
|
159
173
|
}
|
|
160
|
-
this.#
|
|
174
|
+
this.#queue = [];
|
|
161
175
|
}
|
|
162
176
|
this.#sessionCache.append(sessKey, msg);
|
|
163
177
|
} else {
|
|
164
|
-
this.#
|
|
178
|
+
this.#queue.push(msg);
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
|
|
168
182
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
183
|
+
* Stores a record (billing/optimizing).
|
|
184
|
+
*
|
|
185
|
+
* @param {Record} record - The record to store.
|
|
171
186
|
* @throws {Error} If the record is invalid.
|
|
187
|
+
* @private
|
|
172
188
|
*/
|
|
173
189
|
#rec(record) {
|
|
174
190
|
const key = new Date().toISOString().split('T')[0];
|
|
@@ -176,21 +192,27 @@ class Session {
|
|
|
176
192
|
}
|
|
177
193
|
|
|
178
194
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* @
|
|
195
|
+
* Logs an event object.
|
|
196
|
+
*
|
|
197
|
+
* @param {*} o - The object to log.
|
|
198
|
+
* @throws {Error} If logging fails.
|
|
199
|
+
* @private
|
|
182
200
|
*/
|
|
183
201
|
#log(o) {
|
|
184
202
|
const key = new Date().toISOString().split('T')[0];
|
|
185
203
|
this.#logCache.append(key, o);
|
|
186
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Lists unique session names (processed: remove _\d+, _ → space).
|
|
208
|
+
*
|
|
209
|
+
* @returns {string[]} Array of unique session names.
|
|
210
|
+
*/
|
|
187
211
|
sessionList() {
|
|
188
212
|
const list = this.#sessionCache.list();
|
|
189
213
|
const processedSet = new Set(
|
|
190
214
|
list.map(str => {
|
|
191
|
-
// Remove the last '_[number]' part
|
|
192
215
|
const withoutNumber = str.replace(/_\d+$/, '');
|
|
193
|
-
// Replace '_' with ' '
|
|
194
216
|
return withoutNumber.replace(/_/g, ' ');
|
|
195
217
|
})
|
|
196
218
|
);
|
|
@@ -198,10 +220,11 @@ class Session {
|
|
|
198
220
|
}
|
|
199
221
|
|
|
200
222
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
223
|
+
* Sets/advances to a session and loads its messages.
|
|
224
|
+
* Updates counter to last version if exists.
|
|
225
|
+
*
|
|
203
226
|
* @param {string} sess - The session name.
|
|
204
|
-
* @
|
|
227
|
+
* @returns {Message[]} Messages from the session (or []).
|
|
205
228
|
*/
|
|
206
229
|
set(sess) {
|
|
207
230
|
const counter = getNextVersion(this.#sessionCache.list(), sess);
|
|
@@ -210,7 +233,6 @@ class Session {
|
|
|
210
233
|
} else {
|
|
211
234
|
this.#sessionCounter = counter - 1;
|
|
212
235
|
}
|
|
213
|
-
// Load last know session
|
|
214
236
|
const sessKey = this.#sessionCache.sanitizeKey(`${sess}_${this.#sessionCounter}`);
|
|
215
237
|
const res = this.#sessionCache.read(sessKey) || [];
|
|
216
238
|
|
|
@@ -219,8 +241,9 @@ class Session {
|
|
|
219
241
|
}
|
|
220
242
|
return res;
|
|
221
243
|
}
|
|
244
|
+
|
|
222
245
|
/**
|
|
223
|
-
*
|
|
246
|
+
* Empties all caches: sessions, records, logs.
|
|
224
247
|
*/
|
|
225
248
|
empty() {
|
|
226
249
|
this.#sessionCache.empty();
|
package/lib/ToolSet.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import Prompt from "./Prompt.js";
|
|
2
2
|
/**
|
|
3
|
-
* @module @j-o-r/toolset
|
|
4
|
-
* Register function_calls / tools
|
|
5
|
-
* This library can be used stand-alone or as a wrapper for AI functions_calls tools
|
|
6
|
-
*/
|
|
3
|
+
* @module @j-o-r/toolset
|
|
4
|
+
* Register function_calls / tools
|
|
5
|
+
* This library can be used stand-alone or as a wrapper for AI functions_calls tools
|
|
6
|
+
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @typedef {object} TSSchema
|
|
10
|
-
* @property {string} type - Type of the object
|
|
11
|
-
* @property {object} properties - Description of properties
|
|
12
|
-
* @property {string[]} [required] - Required properties
|
|
13
|
-
*/
|
|
9
|
+
* @typedef {object} TSSchema
|
|
10
|
+
* @property {string} type - Type of the object
|
|
11
|
+
* @property {object} properties - Description of properties
|
|
12
|
+
* @property {string[]} [required] - Required properties
|
|
13
|
+
*/
|
|
14
14
|
/**
|
|
15
|
-
* @typedef {object} TSTool
|
|
16
|
-
* @property {string} description - Command description
|
|
17
|
-
* @property {TSSchema} parameters - JS JSON schema
|
|
18
|
-
* @property {function(object): Promise} method
|
|
19
|
-
*/
|
|
15
|
+
* @typedef {object} TSTool
|
|
16
|
+
* @property {string} description - Command description
|
|
17
|
+
* @property {TSSchema} parameters - JS JSON schema
|
|
18
|
+
* @property {function(object): Promise<*>} method - The async method to execute
|
|
19
|
+
*/
|
|
20
20
|
/**
|
|
21
|
-
* @typedef {object} TSToolListItem
|
|
22
|
-
* @property {string} name - Command name
|
|
23
|
-
* @property {string} description - Command description
|
|
24
|
-
* @property {TSSchema} parameters - JS JSON schema
|
|
25
|
-
*/
|
|
21
|
+
* @typedef {object} TSToolListItem
|
|
22
|
+
* @property {string} name - Command name
|
|
23
|
+
* @property {string} description - Command description
|
|
24
|
+
* @property {TSSchema} parameters - JS JSON schema
|
|
25
|
+
*/
|
|
26
26
|
|
|
27
|
-
/** Type of choices */
|
|
27
|
+
/** @enum {string} Type of choices for tool usage */
|
|
28
28
|
const CHOICES = {
|
|
29
29
|
NONE: 'none',
|
|
30
30
|
AUTO: 'auto',
|
|
@@ -32,21 +32,27 @@ const CHOICES = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* @
|
|
38
|
-
|
|
35
|
+
* Validates if the given string is a valid tool name.
|
|
36
|
+
* Valid names match the regex /^[#!a-z_0-9]{2,}$/
|
|
37
|
+
* @param {string} s - The string to validate
|
|
38
|
+
* @returns {boolean} True if valid, false otherwise
|
|
39
|
+
*/
|
|
39
40
|
const isValidName = (s) => /^[#!a-z_0-9]{2,}$/.test(s);
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @classdesc ToolSet manages a collection of tools (functions) that can be registered and executed, typically for AI agent function calling.
|
|
44
|
+
* Supports schema definition compatible with JSON Schema for parameters.
|
|
45
|
+
*/
|
|
41
46
|
class ToolSet {
|
|
42
|
-
/** @type {Map<string, TSTool>} */
|
|
47
|
+
/** @type {Map<string, TSTool>} Private map storing registered tools */
|
|
43
48
|
#tools = new Map();
|
|
44
|
-
/**
|
|
49
|
+
/** @private Default tool choice setting */
|
|
45
50
|
#toolChoice = 'auto';
|
|
46
51
|
|
|
47
52
|
/**
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
* Constructs a new ToolSet instance.
|
|
54
|
+
* @param {string} [choice='auto'] - Default tool choice: 'auto' | 'none' | 'required'
|
|
55
|
+
*/
|
|
50
56
|
constructor(choice = 'auto') {
|
|
51
57
|
if (choice && Object.values(CHOICES).includes(choice)) {
|
|
52
58
|
this.#toolChoice = choice;
|
|
@@ -56,20 +62,21 @@ class ToolSet {
|
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
* Getter for the number of registered tools.
|
|
66
|
+
* @returns {number} The count of tools in the set
|
|
67
|
+
*/
|
|
62
68
|
get length() {
|
|
63
69
|
return this.#tools.size;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
* Registers a new tool/command/function in the set.
|
|
74
|
+
* Overwrites if already exists (no error thrown).
|
|
75
|
+
* @param {string} name - The lowercase string name of the tool e.g. 'get_node_version'. Must match /^[#!a-z_0-9]{2,}$/
|
|
76
|
+
* @param {string} description - Human-readable description of what the tool does
|
|
77
|
+
* @param {TSSchema} parameters - JSON Schema object describing the input parameters
|
|
78
|
+
* @param {function(object): Promise<*>} method - The async function to call with parsed parameters
|
|
79
|
+
*/
|
|
73
80
|
add(name, description, parameters, method) {
|
|
74
81
|
if (typeof name !== 'string') {
|
|
75
82
|
throw new Error('Tool name must be a string');
|
|
@@ -78,42 +85,45 @@ class ToolSet {
|
|
|
78
85
|
throw new Error(`Invalid tool name '${name}': must match /^[#!a-z_0-9]{2,}$/`);
|
|
79
86
|
}
|
|
80
87
|
if (this.has(name)) {
|
|
81
|
-
//
|
|
88
|
+
// Note: Allows overwrite without error
|
|
82
89
|
}
|
|
83
90
|
this.#tools.set(name, { description, parameters, method });
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
* Retrieves a registered tool by name.
|
|
95
|
+
* @param {string} name - The name of the tool
|
|
96
|
+
* @returns {TSTool} The tool object
|
|
97
|
+
* @throws {Error} If tool not found
|
|
98
|
+
*/
|
|
91
99
|
get(name) {
|
|
92
100
|
if (!this.has(name)) {
|
|
93
101
|
throw new Error('Function not found');
|
|
94
102
|
}
|
|
95
103
|
return this.#tools.get(name);
|
|
96
104
|
}
|
|
105
|
+
|
|
97
106
|
/**
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
* Deletes a tool from the set by name.
|
|
108
|
+
* @param {string} name - The name of the tool to delete
|
|
109
|
+
*/
|
|
101
110
|
delete(name) {
|
|
102
111
|
this.#tools.delete(name);
|
|
103
112
|
}
|
|
113
|
+
|
|
104
114
|
/**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
* Checks if a tool with the given name is registered.
|
|
116
|
+
* @param {string} name - The tool name to check
|
|
117
|
+
* @returns {boolean} True if the tool exists
|
|
118
|
+
*/
|
|
109
119
|
has(name) {
|
|
110
120
|
return this.#tools.has(name);
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
/**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
* Returns a sorted list of all registered tools (excluding methods).
|
|
125
|
+
* @returns {TSToolListItem[]} Array of tool summaries, sorted by name
|
|
126
|
+
*/
|
|
117
127
|
list() {
|
|
118
128
|
return Array.from(this.#tools.entries()).map(([name, value]) => ({
|
|
119
129
|
name,
|
|
@@ -122,15 +132,21 @@ class ToolSet {
|
|
|
122
132
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Getter for the current tool choice setting.
|
|
137
|
+
* @returns {string} The tool choice: 'auto', 'none', or 'required'
|
|
138
|
+
*/
|
|
125
139
|
get toolChoice() {
|
|
126
140
|
return this.#toolChoice;
|
|
127
141
|
}
|
|
142
|
+
|
|
128
143
|
/**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
* Executes a specific tool by name with given parameters.
|
|
145
|
+
* @param {string} name - The tool name
|
|
146
|
+
* @param {object} params - The parameters object
|
|
147
|
+
* @returns {Promise<*>} The result from the tool method
|
|
148
|
+
* @throws {Error} If tool not found or execution fails
|
|
149
|
+
*/
|
|
134
150
|
async call(name, params) {
|
|
135
151
|
if (!this.has(name)) {
|
|
136
152
|
throw new Error('Function not found');
|
|
@@ -138,9 +154,12 @@ class ToolSet {
|
|
|
138
154
|
const tool = this.#tools.get(name);
|
|
139
155
|
return tool.method(params);
|
|
140
156
|
}
|
|
157
|
+
|
|
141
158
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
159
|
+
* Processes and executes function requests from the last message in a Prompt.
|
|
160
|
+
* Automatically handles multiple calls, emits events, and adds responses back to the prompt.
|
|
161
|
+
* @param {Prompt} prompt - The Prompt instance containing the last message with function_request(s)
|
|
162
|
+
* @returns {Promise<void>}
|
|
144
163
|
*/
|
|
145
164
|
async execute(prompt) {
|
|
146
165
|
const lastMessage = prompt.getLastMessage();
|
|
@@ -187,4 +206,4 @@ class ToolSet {
|
|
|
187
206
|
}
|
|
188
207
|
}
|
|
189
208
|
|
|
190
|
-
export default ToolSet;
|
|
209
|
+
export default ToolSet;
|
package/lib/fafs.js
CHANGED
|
@@ -10,27 +10,55 @@ new CacheAsync(RELATIVE_CACHE, true, 'bin');
|
|
|
10
10
|
const APP_CACHE = path.resolve(RELATIVE_CACHE, APPNAME);
|
|
11
11
|
new CacheAsync(APP_CACHE, true, 'bin');
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Frequently Asked Functions (FAFS) - Utility module providing environment and system information utilities.
|
|
15
|
+
*
|
|
16
|
+
* Main features:
|
|
17
|
+
* - `env()`: Gathers user, system, location, and path info (cached 31 days).
|
|
18
|
+
* - `systemInfo()`: Detailed OS/architecture/working dir string.
|
|
19
|
+
* - `GLOBAL`: Shared config (max requests, cache path, secret).
|
|
20
|
+
*
|
|
21
|
+
* @module fafs
|
|
22
|
+
* @exports {function} env
|
|
23
|
+
* @exports {function} systemInfo
|
|
24
|
+
* @exports {Object} GLOBAL
|
|
25
|
+
* @exports {function} jsType
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} EnvironmentInfo
|
|
30
|
+
* @property {string} name - User's full name (from passwd GECOS field)
|
|
31
|
+
* @property {string} system - System information (OS release, architecture, kernel, cwd)
|
|
32
|
+
* @property {string} city - City from external IP geolocation
|
|
33
|
+
* @property {string} region - Region/State from IP
|
|
34
|
+
* @property {string} country - Country from IP
|
|
35
|
+
* @property {string} timezone - Timezone from IP
|
|
36
|
+
* @property {string} external_ip - External IP address
|
|
37
|
+
* @property {string} cwd - Current working directory (relative to home as ~)
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Global configuration and constants for the application.
|
|
42
|
+
* @type {Object}
|
|
43
|
+
* @property {number} max_recursive_requests - Maximum number of recursive requests allowed (default: 20)
|
|
44
|
+
* @property {string} default_cache - Path to the default application cache directory
|
|
45
|
+
* @property {string} secret - Secret key for cache encryption/security
|
|
46
|
+
*/
|
|
13
47
|
const GLOBAL = {
|
|
14
|
-
max_recursive_requests: 20,
|
|
48
|
+
max_recursive_requests: 20,
|
|
15
49
|
default_cache: APP_CACHE,
|
|
16
50
|
secret: 'tdb.e3a0cd73dd6a429283f921f9fc1bad41'
|
|
17
51
|
}
|
|
18
52
|
|
|
19
53
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @
|
|
24
|
-
* @
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
* @property {string} cwd - current working folder
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @returns {Promise<string>}
|
|
33
|
-
*/
|
|
54
|
+
* Retrieves detailed system information including OS release (/etc/issue), machine architecture (uname -m),
|
|
55
|
+
* operating system (uname -o), and current working directory.
|
|
56
|
+
*
|
|
57
|
+
* @returns {Promise<string>} Formatted multi-line string with system details.
|
|
58
|
+
* @example
|
|
59
|
+
* const info = await systemInfo();
|
|
60
|
+
* // "system: Ubuntu 25.10 \n\nx86_64\nGNU/Linux\ncwd: /home/user/project"
|
|
61
|
+
*/
|
|
34
62
|
async function systemInfo() {
|
|
35
63
|
let system = 'system: ' + (await SH`cat /etc/issue`.run()).trim().replace('\\n', '').replace('\\l', '');
|
|
36
64
|
system += (await SH`uname -m`.run()).trim().replace('\\n', '').replace('\\l', '');
|
|
@@ -48,9 +76,15 @@ async function systemInfo() {
|
|
|
48
76
|
}
|
|
49
77
|
|
|
50
78
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
79
|
+
* Gathers comprehensive environment information: user name, fresh system details, IP-based geolocation,
|
|
80
|
+
* and normalized current working directory. Results are cached in ~/.cache/hello-dave/env for 31 days,
|
|
81
|
+
* refreshed on expiry. On cache hit, updates with fresh system info and cwd.
|
|
82
|
+
*
|
|
83
|
+
* @returns {Promise<EnvironmentInfo>} The environment information object.
|
|
84
|
+
* @example
|
|
85
|
+
* const envInfo = await env();
|
|
86
|
+
* console.log(envInfo.name, envInfo.city, envInfo.system);
|
|
87
|
+
*/
|
|
54
88
|
async function env() {
|
|
55
89
|
const CACHE_PATH = (await SH`echo ~/.cache/${APPNAME}`.run()).trim();
|
|
56
90
|
const KEY = 'env';
|
|
@@ -82,6 +116,7 @@ async function env() {
|
|
|
82
116
|
timezone: userInfo.timezone,
|
|
83
117
|
external_ip: userInfo.ip
|
|
84
118
|
}
|
|
119
|
+
info.cwd = cwd;
|
|
85
120
|
await storage.write(KEY, JSON.stringify(info));
|
|
86
121
|
return info;
|
|
87
122
|
}
|
|
@@ -91,4 +126,4 @@ export {
|
|
|
91
126
|
GLOBAL,
|
|
92
127
|
env,
|
|
93
128
|
systemInfo
|
|
94
|
-
}
|
|
129
|
+
}
|