@j-o-r/hello-dave 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -25
- package/README.md +81 -221
- package/TODO.md +173 -35
- package/agents/agent_creator.js +105 -0
- package/agents/agent_creator.prompt.md +371 -0
- package/agents/ask_agent.js +64 -127
- package/agents/claude_agent.js +68 -0
- package/agents/code_agent.js +55 -135
- package/agents/code_agent.prompt.md +50 -0
- package/agents/echo_agent.js +76 -0
- package/agents/financial_expert.js +75 -0
- package/agents/gpt_agent.js +52 -103
- package/agents/gpt_code.js +81 -0
- package/agents/grok_agent.js +58 -114
- package/agents/minimax_agent.js +92 -0
- package/agents/mureka_agent.js +77 -0
- package/agents/planner_agent.js +172 -0
- package/agents/stability_agent.js +87 -0
- package/agents/test_agent.js +75 -157
- package/agents/weather_agent.js +73 -0
- package/agents/workflow_agent.js +189 -0
- package/bin/dave.js +436 -184
- package/docs/bin-dave.md +85 -35
- package/docs/cdn-ssh.md +100 -0
- package/docs/creating-agents.md +301 -0
- package/docs/creating-toolsets.md +336 -0
- package/docs/docs-organization.md +48 -0
- package/docs/project-overview.md +86 -51
- package/lib/API/elevenlabs.io/music.compose.md +441 -0
- package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
- package/lib/API/elevenlabs.io/music.stream.md +425 -0
- package/lib/API/lalal.ai/lalal.js +445 -0
- package/lib/API/lalal.ai/openapi.json +2614 -0
- package/lib/API/minimax/ImageToolset.js +82 -37
- package/lib/API/minimax/MusicToolset.js +125 -79
- package/lib/API/minimax/VideoToolset.js +170 -167
- package/lib/API/minimax/image.js +5 -1
- package/lib/API/minimax/music.js +210 -23
- package/lib/API/minimax/video.js +242 -53
- package/lib/API/mureka/MusicToolset.js +646 -0
- package/lib/API/mureka/README.md +41 -0
- package/lib/API/mureka/index.js +7 -0
- package/lib/API/mureka/music.js +658 -0
- package/lib/API/openai.com/index.js +7 -0
- package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
- package/lib/API/openai.com/video.create.character.md +40 -0
- package/lib/API/openai.com/video.create.md +219 -0
- package/lib/API/openai.com/video.delete.md +44 -0
- package/lib/API/openai.com/video.download.md +31 -0
- package/lib/API/openai.com/video.edit.md +155 -0
- package/lib/API/openai.com/video.extend.md +166 -0
- package/lib/API/openai.com/video.fetch.character.md +43 -0
- package/lib/API/openai.com/video.js +784 -0
- package/lib/API/openai.com/video.list.md +201 -0
- package/lib/API/openai.com/video.remix.md +175 -0
- package/lib/API/openai.com/video.retrieve.md +139 -0
- package/lib/API/openai.com/videoToolset.js +616 -0
- package/lib/API/stability.ai/ImageToolset.js +131 -40
- package/lib/API/stability.ai/MusicToolset.js +79 -47
- package/lib/API/stability.ai/audio.js +63 -131
- package/lib/API/x.ai/chat.responses.md +1040 -0
- package/lib/API/x.ai/image.js +229 -59
- package/lib/API/x.ai/imageToolset.js +376 -0
- package/lib/API/x.ai/index.js +1 -1
- package/lib/API/x.ai/responses.js +9 -18
- package/lib/Agent.js +271 -0
- package/lib/Agent.js.old +284 -0
- package/lib/AgentLauncher.js +562 -0
- package/lib/Cli.js +87 -13
- package/lib/Prompt.js +23 -1
- package/lib/Session.js +5 -4
- package/lib/ToolSet.js +102 -6
- package/lib/agentLoader.js +369 -0
- package/lib/cdn.js +67 -231
- package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
- package/lib/defaultToolsets.js +43 -0
- package/lib/fafs.js +1 -1
- package/lib/genericToolset.js +442 -119
- package/lib/handOffToolset.js +179 -0
- package/lib/index.js +34 -27
- package/lib/toolsetLoader.js +248 -0
- package/package.json +11 -5
- package/types/API/lalal.ai/lalal.d.ts +116 -0
- package/types/API/minimax/image.d.ts +2 -1
- package/types/API/minimax/music.d.ts +189 -26
- package/types/API/minimax/video.d.ts +100 -31
- package/types/API/mureka/index.d.ts +7 -0
- package/types/API/mureka/music.d.ts +472 -0
- package/types/API/openai.com/index.d.ts +7 -0
- package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
- package/types/API/openai.com/video.d.ts +409 -0
- package/types/API/openai.com/videoToolset.d.ts +24 -0
- package/types/API/stability.ai/audio.d.ts +14 -103
- package/types/API/stability.ai/image.d.ts +2 -2
- package/types/API/x.ai/image.d.ts +138 -26
- package/types/API/x.ai/imageToolset.d.ts +3 -0
- package/types/API/x.ai/index.d.ts +1 -1
- package/types/API/x.ai/responses.d.ts +4 -4
- package/types/Agent.d.ts +123 -0
- package/types/AgentLauncher.d.ts +222 -0
- package/types/Cli.d.ts +28 -8
- package/types/Prompt.d.ts +23 -5
- package/types/Session.d.ts +1 -1
- package/types/ToolSet.d.ts +10 -0
- package/types/agentLoader.d.ts +78 -0
- package/types/cdn.d.ts +15 -90
- package/types/defaultToolsets.d.ts +9 -0
- package/types/fafs.d.ts +1 -1
- package/types/genericToolset.d.ts +1 -1
- package/types/handOffToolset.d.ts +28 -0
- package/types/index.d.ts +19 -17
- package/types/toolsetLoader.d.ts +114 -0
- package/utils/format_log.js +101 -23
- package/utils/launch_agent.js +18 -0
- package/utils/list_sessions.sh +13 -5
- package/utils/search_sessions.sh +65 -29
- package/utils/toolsets.js +33 -0
- package/README.md.bak.1779452127 +0 -240
- package/agents/codeserver.sh +0 -47
- package/agents/daisy_agent.js +0 -173
- package/agents/docs_agent.js +0 -148
- package/agents/memory_agent.js +0 -263
- package/agents/minimax.js +0 -173
- package/agents/npm_agent.js +0 -202
- package/agents/prompt_agent.js +0 -133
- package/agents/readme_agent.js +0 -148
- package/agents/spawn_agent.js +0 -160
- package/agents/stability.js +0 -173
- package/agents/todo_agent.js +0 -175
- package/bin/codeDave +0 -58
- package/docs/agent-dave-websocket-protocol.md +0 -180
- package/docs/agent-manager.md +0 -244
- package/docs/codeserver-pattern.md +0 -191
- package/docs/generic-toolset.md +0 -326
- package/docs/howtos/agent-networking.md +0 -253
- package/docs/howtos/spawn-agents.md.bak +0 -200
- package/docs/howtos/spawn-agents.md.bak_new +0 -200
- package/docs/multi-agent-clusters.md +0 -265
- package/docs/music-toolsets.md +0 -137
- package/docs/path-resolution-best-practices.md +0 -104
- package/docs/plans/minimax-music-generation.md +0 -80
- package/docs/plans/unified-agent-architecture.md +0 -146
- package/docs/plans/websocket-streaming-plan.md.bak +0 -317
- package/docs/prompt/spawn_agent.md +0 -175
- package/docs/prompt/spawn_agent.md.bak +0 -201
- package/docs/prompt/task_clarification_and_documentation.md +0 -35
- package/docs/prompt-class.md +0 -141
- package/docs/todo-archive-infra-2026-04-21.md +0 -15
- package/docs/todo-archive-v0.0.8.md +0 -1
- package/docs/todo-archive-v0.1.0.md +0 -32
- package/docs/todo-archive.md +0 -44
- package/docs/tools-syntax-validation.md +0 -121
- package/docs/toolset.md +0 -164
- package/docs/xai-responses.md +0 -111
- package/docs/xai_collections.md +0 -106
- package/lib/API/x.ai/ImageToolset.js +0 -165
- package/lib/API/x.ai/text.js +0 -415
- package/lib/AgentClient.js +0 -248
- package/lib/AgentManager.js +0 -245
- package/lib/AgentServer.js +0 -404
- package/lib/wsCli.js +0 -287
- package/lib/wsIO.js +0 -90
- package/types/API/x.ai/text.d.ts +0 -286
- package/types/AgentClient.d.ts +0 -109
- package/types/AgentManager.d.ts +0 -100
- package/types/AgentServer.d.ts +0 -89
- package/types/wsCli.d.ts +0 -17
- package/types/wsIO.d.ts +0 -30
- package/utils/test.sh +0 -46
- /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
- /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
- /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
- /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/agentLoader.js
|
|
3
|
+
*
|
|
4
|
+
* Internal loader and discovery utilities for clean Agent-based agents.
|
|
5
|
+
*
|
|
6
|
+
* Supports both default framework discovery and caller-bound discovery via:
|
|
7
|
+
*
|
|
8
|
+
* const loader = createAgentLoader({ from: import.meta.url });
|
|
9
|
+
*
|
|
10
|
+
* Search / discovery order:
|
|
11
|
+
* - When `from` is supplied: only <nearest-package-root-from-from>/agents
|
|
12
|
+
* - Otherwise: only <framework-package-root>/agents
|
|
13
|
+
*
|
|
14
|
+
* @module agentLoader
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs/promises';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Valid agent name pattern.
|
|
26
|
+
*
|
|
27
|
+
* @const {RegExp}
|
|
28
|
+
*/
|
|
29
|
+
const validAgentNameRegex = /^[a-z0-9_]{2,}$/;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {object} AgentLoaderOptions
|
|
33
|
+
* @property {string|URL} [from] - Caller module URL/path, usually caller import.meta.url.
|
|
34
|
+
* @property {(string|URL)[]} [agentDirs] - Explicit agent directories. Used only when `from` is not supplied.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Return true when a child path is the base path or nested under it.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} base - Real absolute base directory.
|
|
41
|
+
* @param {string} child - Real absolute candidate path.
|
|
42
|
+
* @returns {boolean} Whether child is inside base.
|
|
43
|
+
*/
|
|
44
|
+
const isInsidePath = (base, child) => {
|
|
45
|
+
const relative = path.relative(base, child);
|
|
46
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Convert a filesystem path or file URL to an absolute filesystem path.
|
|
51
|
+
*
|
|
52
|
+
* @param {string|URL} value - Path or file URL.
|
|
53
|
+
* @returns {string} Absolute filesystem path.
|
|
54
|
+
*/
|
|
55
|
+
function toPath(value) {
|
|
56
|
+
if (value instanceof URL) return fileURLToPath(value);
|
|
57
|
+
if (typeof value === 'string' && value.startsWith('file:')) return fileURLToPath(value);
|
|
58
|
+
return path.resolve(String(value));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Return true if a path exists and is readable.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} targetPath - Filesystem path to check.
|
|
65
|
+
* @returns {Promise<boolean>} True when the path is readable.
|
|
66
|
+
*/
|
|
67
|
+
async function canRead(targetPath) {
|
|
68
|
+
try {
|
|
69
|
+
await fs.access(targetPath, fs.constants.R_OK);
|
|
70
|
+
return true;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Find the nearest package root by walking upward until package.json is found.
|
|
78
|
+
*
|
|
79
|
+
* @param {string|URL} from - Starting file path, directory path, or file URL.
|
|
80
|
+
* @returns {Promise<string|null>} Package root path, or null.
|
|
81
|
+
*/
|
|
82
|
+
export async function findNearestPackageRoot(from) {
|
|
83
|
+
if (!from) return null;
|
|
84
|
+
|
|
85
|
+
let currentPath = toPath(from);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const stat = await fs.stat(currentPath);
|
|
89
|
+
if (stat.isFile()) currentPath = path.dirname(currentPath);
|
|
90
|
+
} catch {
|
|
91
|
+
currentPath = path.dirname(currentPath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let currentDir = path.resolve(currentPath);
|
|
95
|
+
|
|
96
|
+
while (true) {
|
|
97
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
98
|
+
if (await canRead(packageJsonPath)) return currentDir;
|
|
99
|
+
|
|
100
|
+
const parentDir = path.dirname(currentDir);
|
|
101
|
+
if (parentDir === currentDir) return null;
|
|
102
|
+
currentDir = parentDir;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Push a directory into a list if present.
|
|
108
|
+
*
|
|
109
|
+
* @param {string[]} dirs - Directory accumulator.
|
|
110
|
+
* @param {string|null|undefined} dir - Directory candidate.
|
|
111
|
+
* @returns {void}
|
|
112
|
+
*/
|
|
113
|
+
function pushDir(dirs, dir) {
|
|
114
|
+
if (dir) dirs.push(path.resolve(dir));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Build ordered agent directory candidates.
|
|
119
|
+
*
|
|
120
|
+
* When `options.from` is supplied, discovery is intentionally strict: the only
|
|
121
|
+
* directory searched is the `agents` directory under the nearest package root
|
|
122
|
+
* found from that caller module URL/path.
|
|
123
|
+
*
|
|
124
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
125
|
+
* @returns {Promise<string[]>} Absolute agent directory candidates.
|
|
126
|
+
*/
|
|
127
|
+
export async function getAgentDirs(options = {}) {
|
|
128
|
+
const dirs = [];
|
|
129
|
+
|
|
130
|
+
if (options.from) {
|
|
131
|
+
const callerRoot = await findNearestPackageRoot(options.from);
|
|
132
|
+
if (!callerRoot) {
|
|
133
|
+
throw new Error(`Could not find package.json from ${String(options.from)}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return [path.join(callerRoot, 'agents')];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (Array.isArray(options.agentDirs)) {
|
|
140
|
+
for (const dir of options.agentDirs) pushDir(dirs, toPath(dir));
|
|
141
|
+
return [...new Set(dirs)];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const frameworkRoot = await findNearestPackageRoot(import.meta.url);
|
|
145
|
+
return [frameworkRoot ? path.join(frameworkRoot, 'agents') : path.resolve(__dirname, '..', 'agents')];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Build roots allowed for direct path loading.
|
|
150
|
+
*
|
|
151
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
152
|
+
* @returns {Promise<string[]>} Real absolute allowed roots.
|
|
153
|
+
*/
|
|
154
|
+
async function getAllowedDirectPathRoots(options = {}) {
|
|
155
|
+
const roots = [];
|
|
156
|
+
|
|
157
|
+
if (options.from) {
|
|
158
|
+
const callerRoot = await findNearestPackageRoot(options.from);
|
|
159
|
+
if (!callerRoot) {
|
|
160
|
+
throw new Error(`Could not find package.json from ${String(options.from)}`);
|
|
161
|
+
}
|
|
162
|
+
roots.push(callerRoot);
|
|
163
|
+
} else if (Array.isArray(options.agentDirs)) {
|
|
164
|
+
for (const dir of options.agentDirs) roots.push(toPath(dir));
|
|
165
|
+
} else {
|
|
166
|
+
const frameworkRoot = await findNearestPackageRoot(import.meta.url);
|
|
167
|
+
roots.push(frameworkRoot || path.resolve(__dirname, '..'));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const realRoots = [];
|
|
171
|
+
for (const root of roots) {
|
|
172
|
+
try {
|
|
173
|
+
realRoots.push(await fs.realpath(root));
|
|
174
|
+
} catch {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return [...new Set(realRoots)];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Resolve a direct `.js` agent path supplied on the command line.
|
|
182
|
+
*
|
|
183
|
+
* @param {string} agentPath - Relative or absolute path to an agent module.
|
|
184
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
185
|
+
* @returns {Promise<string|null>} Real absolute path, or null when not path-like.
|
|
186
|
+
* @throws {Error} If the path-like spec is invalid or unsafe.
|
|
187
|
+
*/
|
|
188
|
+
const resolveDirectAgentPath = async (agentPath, options = {}) => {
|
|
189
|
+
if (!agentPath.includes('/') && !agentPath.includes('\\')) return null;
|
|
190
|
+
|
|
191
|
+
const resolved = path.resolve(process.cwd(), agentPath);
|
|
192
|
+
const parsed = path.parse(resolved);
|
|
193
|
+
if (parsed.ext !== '.js' || !validAgentNameRegex.test(parsed.name)) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Invalid agent path "${agentPath}". Direct agent paths must end in <valid_agent_name>.js, ` +
|
|
196
|
+
`where the basename matches /^[a-z0-9_]{2,}$/ (for example agents/gpt_agent.js).`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const realFile = await fs.realpath(resolved);
|
|
201
|
+
const allowedRoots = await getAllowedDirectPathRoots(options);
|
|
202
|
+
if (!allowedRoots.some(root => isInsidePath(root, realFile))) {
|
|
203
|
+
throw new Error(`Agent path "${agentPath}" escapes the configured project/package roots`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await fs.access(realFile, fs.constants.R_OK);
|
|
207
|
+
return realFile;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Discover all available agents by performing a lightweight filesystem scan.
|
|
212
|
+
*
|
|
213
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
214
|
+
* @returns {Promise<Array<{name: string, path: string, desc: string}>>}
|
|
215
|
+
* Array of discovered agents.
|
|
216
|
+
*/
|
|
217
|
+
export async function listAgents(options = {}) {
|
|
218
|
+
const dirCandidates = await getAgentDirs(options);
|
|
219
|
+
|
|
220
|
+
/** @type {Map<string, {name: string, path: string, desc: string}>} */
|
|
221
|
+
const found = new Map();
|
|
222
|
+
|
|
223
|
+
for (const dir of dirCandidates) {
|
|
224
|
+
try {
|
|
225
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
226
|
+
for (const entry of entries) {
|
|
227
|
+
if (!entry.isFile() || !entry.name.endsWith('.js')) continue;
|
|
228
|
+
|
|
229
|
+
const base = entry.name.replace(/\.js$/, '');
|
|
230
|
+
if (!validAgentNameRegex.test(base) || found.has(base)) continue;
|
|
231
|
+
|
|
232
|
+
const fullPath = path.join(dir, entry.name);
|
|
233
|
+
let desc = '[ERROR]';
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
237
|
+
let agent = mod.default ?? mod.agent ?? mod;
|
|
238
|
+
|
|
239
|
+
if (typeof agent === 'function') {
|
|
240
|
+
try {
|
|
241
|
+
agent = await agent();
|
|
242
|
+
} catch {
|
|
243
|
+
agent = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (agent && typeof agent === 'object') {
|
|
248
|
+
if (typeof agent.description === 'string' && agent.description.trim()) {
|
|
249
|
+
desc = agent.description.trim();
|
|
250
|
+
} else if (typeof agent.call_description === 'string' && agent.call_description.trim()) {
|
|
251
|
+
desc = agent.call_description.trim();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if ((!desc || desc === '[ERROR]') && typeof mod.call_description === 'string' && mod.call_description.trim()) {
|
|
256
|
+
desc = mod.call_description.trim();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!desc || typeof desc !== 'string' || desc.length === 0) {
|
|
260
|
+
desc = '[ERROR]';
|
|
261
|
+
} else if (desc.length > 180) {
|
|
262
|
+
desc = `${desc.slice(0, 177).trimEnd()}...`;
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
desc = '[ERROR]';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
found.set(base, { name: base, path: fullPath, desc });
|
|
269
|
+
}
|
|
270
|
+
} catch {
|
|
271
|
+
// Directory does not exist or is not readable — silently skip.
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return Array.from(found.values());
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Resolve the absolute path to an agent module by name.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} agentName - The agent identifier or safe direct path.
|
|
282
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
283
|
+
* @returns {Promise<string>} Absolute path to the matching .js file.
|
|
284
|
+
*/
|
|
285
|
+
export async function resolveAgentPath(agentName, options = {}) {
|
|
286
|
+
if (!agentName || typeof agentName !== 'string') {
|
|
287
|
+
throw new Error('agentName is required');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const directPath = await resolveDirectAgentPath(agentName, options);
|
|
291
|
+
if (directPath) return directPath;
|
|
292
|
+
|
|
293
|
+
if (!validAgentNameRegex.test(agentName)) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`Invalid agentName "${agentName}". ` +
|
|
296
|
+
`Must match /^[a-z0-9_]{2,}$/ (lowercase letters a-z, digits 0-9, and underscore _ only; min 2 characters). ` +
|
|
297
|
+
`Or pass a safe direct path like agents/gpt_agent.js.`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const fileName = `${agentName}.js`;
|
|
302
|
+
const dirCandidates = await getAgentDirs(options);
|
|
303
|
+
const candidates = dirCandidates.map(dir => path.join(dir, fileName));
|
|
304
|
+
|
|
305
|
+
for (const p of candidates) {
|
|
306
|
+
if (await canRead(p)) return p;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const searched = candidates.map(c => ` - ${c}`).join('\n');
|
|
310
|
+
throw new Error(`Agent "${agentName}" not found.\nSearched:\n${searched}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Load (import + validate) a clean Agent instance by name.
|
|
315
|
+
*
|
|
316
|
+
* @param {string} agentName - Agent identifier to load.
|
|
317
|
+
* @param {Array<any>} [extraArgs=[]] - Optional factory arguments.
|
|
318
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
319
|
+
* @returns {Promise<import('./Agent.js').default>} Loaded Agent instance.
|
|
320
|
+
*/
|
|
321
|
+
export async function loadAgent(agentName, extraArgs = [], options = {}) {
|
|
322
|
+
const resolved = await resolveAgentPath(agentName, options);
|
|
323
|
+
const mod = await import(pathToFileURL(resolved).href);
|
|
324
|
+
let agent = mod.default ?? mod.agent ?? mod;
|
|
325
|
+
|
|
326
|
+
if (!agent) {
|
|
327
|
+
throw new Error(`Module ${resolved} did not export a usable default.`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (typeof agent === 'function') {
|
|
331
|
+
agent = await agent(...extraArgs);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (agent && typeof agent === 'object') {
|
|
335
|
+
try {
|
|
336
|
+
if (Object.isExtensible(agent)) {
|
|
337
|
+
agent.__loadedFrom = resolved;
|
|
338
|
+
agent.__loadedAs = path.parse(resolved).name;
|
|
339
|
+
if (extraArgs.length) agent.__extraArgs = extraArgs;
|
|
340
|
+
}
|
|
341
|
+
} catch {}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!agent || typeof agent.getPrompt !== 'function' || typeof agent.name !== 'string') {
|
|
345
|
+
throw new Error(`Loaded "${agentName}" is not a valid Agent instance. Use clean Agent-based agents for handoff.`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return agent;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Create an isolated agent loader bound to a caller/project context.
|
|
353
|
+
*
|
|
354
|
+
* @param {AgentLoaderOptions} [options={}] - Loader options.
|
|
355
|
+
* @returns {{
|
|
356
|
+
* getAgentDirs: () => Promise<string[]>,
|
|
357
|
+
* listAgents: () => Promise<Array<{name: string, path: string, desc: string}>>,
|
|
358
|
+
* resolveAgentPath: (agentName: string) => Promise<string>,
|
|
359
|
+
* loadAgent: (agentName: string, extraArgs?: Array<any>) => Promise<import('./Agent.js').default>
|
|
360
|
+
* }} Configured loader API.
|
|
361
|
+
*/
|
|
362
|
+
export function createAgentLoader(options = {}) {
|
|
363
|
+
return {
|
|
364
|
+
getAgentDirs: () => getAgentDirs(options),
|
|
365
|
+
listAgents: () => listAgents(options),
|
|
366
|
+
resolveAgentPath: (agentName) => resolveAgentPath(agentName, options),
|
|
367
|
+
loadAgent: (agentName, extraArgs = []) => loadAgent(agentName, extraArgs, options)
|
|
368
|
+
};
|
|
369
|
+
}
|