aiden-runtime 3.18.0 → 3.19.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/README.md +153 -24
- package/config/devos.config.backup.json +225 -0
- package/config/devos.config.json +69 -33
- package/config/hardware.json +2 -2
- package/dist/api/server.js +126 -83
- package/dist/cli/commandCatalog.js +344 -0
- package/dist/core/actionVerbDetector.js +65 -0
- package/dist/core/agentLoop.js +279 -112
- package/dist/core/aidenPersonality.js +11 -36
- package/dist/core/auxiliaryClient.js +1 -0
- package/dist/core/computerControl.js +35 -17
- package/dist/core/contextHandoff.js +39 -0
- package/dist/core/diagnosticError.js +20 -0
- package/dist/core/fastPathExpansion.js +7 -0
- package/dist/core/memoryIds.js +16 -0
- package/dist/core/pluginLoader.js +8 -5
- package/dist/core/protectedContext.js +112 -0
- package/dist/core/skillTeacher.js +63 -0
- package/dist/core/slashAsTool.js +37 -0
- package/dist/core/toolRegistry.js +825 -54
- package/dist/core/tools/nowPlaying.js +66 -0
- package/dist/core/version.js +1 -1
- package/dist/providers/index.js +12 -0
- package/dist/providers/mistral.js +121 -0
- package/dist/providers/router.js +4 -2
- package/dist-bundle/cli.js +48052 -46832
- package/dist-bundle/index.js +37216 -22645
- package/package.json +9 -2
- package/scripts/uninstall.ps1 +147 -0
|
@@ -40,13 +40,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
40
40
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
41
|
};
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.TOOL_NAMES_ONLY = exports.TOOL_DESCRIPTIONS = exports.TOOLS = void 0;
|
|
43
|
+
exports.registryMcpDestructiveList = exports.registryMcpSafeList = exports.registrySequentialOnlySet = exports.registryParallelSafeSet = exports.registryNoRetrySet = exports.registryValidTools = exports.registryAllowedTools = exports.registryTimeouts = exports.registryCategories = exports.registryTiers = exports.registryDescriptions = exports.registryNames = exports.TOOL_REGISTRY = exports.TOOL_NAMES_ONLY = exports.TOOL_DESCRIPTIONS = exports.TOOLS = exports.APP_ALIASES = void 0;
|
|
44
|
+
exports.isCommandDenied = isCommandDenied;
|
|
45
|
+
exports.scanCodeForDestructivePaths = scanCodeForDestructivePaths;
|
|
46
|
+
exports.isCommandAllowed = isCommandAllowed;
|
|
44
47
|
exports.getActiveBrowserPage = getActiveBrowserPage;
|
|
45
48
|
exports.setProgressEmitter = setProgressEmitter;
|
|
49
|
+
exports.resolveWritePath = resolveWritePath;
|
|
50
|
+
exports.resolveLaunchCommand = resolveLaunchCommand;
|
|
46
51
|
exports.registerExternalTool = registerExternalTool;
|
|
47
52
|
exports.getExternalToolsMeta = getExternalToolsMeta;
|
|
53
|
+
exports.isKnownTool = isKnownTool;
|
|
48
54
|
exports.executeTool = executeTool;
|
|
49
55
|
exports.getToolTier = getToolTier;
|
|
56
|
+
exports.bumpGeneration = bumpGeneration;
|
|
57
|
+
exports.getGeneration = getGeneration;
|
|
50
58
|
exports.detectToolCategories = detectToolCategories;
|
|
51
59
|
exports.getToolsForCategories = getToolsForCategories;
|
|
52
60
|
// core/toolRegistry.ts — Centralized tool registry with real Playwright
|
|
@@ -55,6 +63,7 @@ const child_process_1 = require("child_process");
|
|
|
55
63
|
const util_1 = require("util");
|
|
56
64
|
const fs_1 = __importDefault(require("fs"));
|
|
57
65
|
const path_1 = __importDefault(require("path"));
|
|
66
|
+
const os_1 = __importDefault(require("os"));
|
|
58
67
|
const paths_1 = require("./paths");
|
|
59
68
|
const computerControl_1 = require("./computerControl");
|
|
60
69
|
const webSearch_1 = require("./webSearch");
|
|
@@ -71,6 +80,7 @@ const permissionSystem_1 = require("./permissionSystem");
|
|
|
71
80
|
const youtubeTranscript_1 = require("./youtubeTranscript");
|
|
72
81
|
const knowledgeBase_1 = require("./knowledgeBase");
|
|
73
82
|
const calendarTool_1 = require("./tools/calendarTool");
|
|
83
|
+
const nowPlaying_1 = require("./tools/nowPlaying");
|
|
74
84
|
const gmailTool_1 = require("./tools/gmailTool");
|
|
75
85
|
const index_1 = require("../providers/index");
|
|
76
86
|
const playwrightBridge_1 = require("./playwrightBridge");
|
|
@@ -134,6 +144,13 @@ const DENIED_COMMANDS = [
|
|
|
134
144
|
/\bnet\s+user\b/i,
|
|
135
145
|
/Set-ExecutionPolicy/i,
|
|
136
146
|
/\bNew-Service\b/i,
|
|
147
|
+
// ── C7: path-scoped deny — Remove-Item on critical system / user paths ──────
|
|
148
|
+
// Belt-and-suspenders: Remove-Item is also removed from SHELL_ALLOWLIST so it
|
|
149
|
+
// requires explicit approval. These patterns hard-block attempts to target
|
|
150
|
+
// system-owned directories regardless of approval state.
|
|
151
|
+
/Remove-Item\b.*[Cc]:[/\\][Uu]sers[/\\]/i,
|
|
152
|
+
/Remove-Item\b.*[Cc]:[/\\][Ww]indows[/\\]/i,
|
|
153
|
+
/Remove-Item\b.*[Cc]:[/\\][Pp]rogram/i,
|
|
137
154
|
];
|
|
138
155
|
function isCommandDenied(cmd) {
|
|
139
156
|
return DENIED_COMMANDS.some(p => p.test(cmd));
|
|
@@ -153,6 +170,60 @@ function isShellDangerous(cmd) {
|
|
|
153
170
|
const lower = cmd.toLowerCase();
|
|
154
171
|
return SHELL_DANGEROUS_PATTERNS.some(p => lower.includes(p.toLowerCase()));
|
|
155
172
|
}
|
|
173
|
+
// ── C8: Code-level destructive path guard for run_node / run_python ──────────
|
|
174
|
+
// Scans code strings for destructive filesystem operations targeting protected
|
|
175
|
+
// system paths. Closes the bypass where the planner re-routes through run_node
|
|
176
|
+
// or run_python after shell_exec is denied by DENIED_COMMANDS.
|
|
177
|
+
//
|
|
178
|
+
// Two-stage check: (1) code contains a destructive fs call, AND (2) code
|
|
179
|
+
// references a protected path. Both must match for denial — benign code that
|
|
180
|
+
// merely reads protected paths, or destructive code targeting workspace, passes.
|
|
181
|
+
const PROTECTED_PATH_PATTERNS = [
|
|
182
|
+
// {1,2} so we match both real paths (C:\Users\) and string-literal escapes (C:\\Users\\)
|
|
183
|
+
/[Cc]:[/\\]{1,2}[Uu]sers[/\\]{1,2}/,
|
|
184
|
+
/[Cc]:[/\\]{1,2}[Ww]indows[/\\]{1,2}/,
|
|
185
|
+
/[Cc]:[/\\]{1,2}[Pp]rogram\s?[Ff]iles/,
|
|
186
|
+
/[Cc]:[/\\]{1,2}[Ss]ystem/,
|
|
187
|
+
/['"`]\/etc[/'"` ]/,
|
|
188
|
+
/['"`]\/home[/'"` ]/,
|
|
189
|
+
/['"`]\/usr[/'"` ]/,
|
|
190
|
+
/['"`]\/var[/'"` ]/,
|
|
191
|
+
];
|
|
192
|
+
const CODE_DESTRUCTIVE_NODE = [
|
|
193
|
+
/\bfs\s*\.\s*rmSync\b/,
|
|
194
|
+
/\bfs\s*\.\s*unlinkSync\b/,
|
|
195
|
+
/\bfs\s*\.\s*rmdirSync\b/,
|
|
196
|
+
/\bfs\.promises\s*\.\s*rm\b/,
|
|
197
|
+
/\bfs\.promises\s*\.\s*unlink\b/,
|
|
198
|
+
/\bfs\.promises\s*\.\s*rmdir\b/,
|
|
199
|
+
/\brimraf\b/,
|
|
200
|
+
/\bfs\s*\.\s*rm\s*\(/,
|
|
201
|
+
/\bdel\s*\(/, // fs-extra del()
|
|
202
|
+
/\bunlinkSync\s*\(/, // bare import
|
|
203
|
+
];
|
|
204
|
+
const CODE_DESTRUCTIVE_PYTHON = [
|
|
205
|
+
/\bos\s*\.\s*remove\b/,
|
|
206
|
+
/\bos\s*\.\s*unlink\b/,
|
|
207
|
+
/\bos\s*\.\s*rmdir\b/,
|
|
208
|
+
/\bos\s*\.\s*removedirs\b/,
|
|
209
|
+
/\bshutil\s*\.\s*rmtree\b/,
|
|
210
|
+
/\bpathlib\b.*\bunlink\b/,
|
|
211
|
+
/\bsend2trash\b/,
|
|
212
|
+
];
|
|
213
|
+
function scanCodeForDestructivePaths(code, lang) {
|
|
214
|
+
const destructivePatterns = lang === 'node' ? CODE_DESTRUCTIVE_NODE : CODE_DESTRUCTIVE_PYTHON;
|
|
215
|
+
const matchedOp = destructivePatterns.find(p => p.test(code));
|
|
216
|
+
if (!matchedOp)
|
|
217
|
+
return { denied: false, reason: '' };
|
|
218
|
+
const matchedPath = PROTECTED_PATH_PATTERNS.find(p => p.test(code));
|
|
219
|
+
if (!matchedPath)
|
|
220
|
+
return { denied: false, reason: '' };
|
|
221
|
+
const opStr = code.match(matchedOp)?.[0] ?? 'destructive op';
|
|
222
|
+
const pathStr = code.match(matchedPath)?.[0] ?? 'protected path';
|
|
223
|
+
const reason = `[Security] ${lang} code blocked: "${opStr}" targeting "${pathStr}" — destructive operation on protected system path`;
|
|
224
|
+
process.stderr.write(reason + '\n');
|
|
225
|
+
return { denied: true, reason };
|
|
226
|
+
}
|
|
156
227
|
// ── Sprint 25: Shell command allowlist ────────────────────────
|
|
157
228
|
// Unknown commands (not in this list) are blocked and require explicit user approval.
|
|
158
229
|
const SHELL_ALLOWLIST = [
|
|
@@ -179,7 +250,9 @@ const SHELL_ALLOWLIST = [
|
|
|
179
250
|
// 11. Archive tools
|
|
180
251
|
/^(tar|zip|unzip|7z|gzip|gunzip)\b/i,
|
|
181
252
|
// 12. PowerShell safe cmdlets (read, navigate, item management, output)
|
|
182
|
-
|
|
253
|
+
// Note: Remove-Item intentionally absent — falls through to needsApproval:true (C7).
|
|
254
|
+
// Hard-deny for Remove-Item on critical paths is in DENIED_COMMANDS above.
|
|
255
|
+
/^(Get-|Select-|Where-|Sort-|Format-|Out-|Write-Output|Write-Host|ConvertTo-|ConvertFrom-|Measure-|Test-Path|Resolve-Path|Split-Path|Join-Path|Compare-Object|New-Item|Copy-Item|Move-Item|Rename-Item|Set-Content|Add-Content|Clear-Content|Set-Location|Push-Location|Pop-Location)/i,
|
|
183
256
|
// 13. Instant Actions: lock screen (rundll32) and volume one-liners (powershell -c)
|
|
184
257
|
/^rundll32\b/i,
|
|
185
258
|
/^powershell\s+-c\b/i,
|
|
@@ -330,6 +403,104 @@ let _emitProgress = null;
|
|
|
330
403
|
function setProgressEmitter(fn) {
|
|
331
404
|
_emitProgress = fn;
|
|
332
405
|
}
|
|
406
|
+
// ── resolveWritePath ──────────────────────────────────────────
|
|
407
|
+
// Pure path resolver for file_write. Exported for unit tests.
|
|
408
|
+
// Expands shorthands, resolves to absolute, then enforces the
|
|
409
|
+
// allow-list: workspace (cwd), Desktop, Documents.
|
|
410
|
+
// Throws with a clear message if the resolved path falls outside.
|
|
411
|
+
function resolveWritePath(rawPath, opts) {
|
|
412
|
+
const home = opts?.home ?? os_1.default.homedir();
|
|
413
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
414
|
+
const user = process.env.USERNAME || process.env.USER || os_1.default.userInfo().username || 'User';
|
|
415
|
+
// Expand shorthands
|
|
416
|
+
let p = rawPath
|
|
417
|
+
.replace(/^~[\/\\]/, home + path_1.default.sep)
|
|
418
|
+
.replace(/^Desktop[\/\\]/i, path_1.default.join(home, 'Desktop') + path_1.default.sep)
|
|
419
|
+
.replace(/^C:\\Users\\Aiden\\/i, `C:\\Users\\${user}\\`)
|
|
420
|
+
.replace(/^C:\/Users\/Aiden\//i, `C:/Users/${user}/`);
|
|
421
|
+
// Resolve relative paths against cwd
|
|
422
|
+
const resolved = /^[A-Za-z]:[/\\]/.test(p) || p.startsWith('/')
|
|
423
|
+
? p
|
|
424
|
+
: path_1.default.join(cwd, p);
|
|
425
|
+
// Allow-list: workspace root, Desktop, Documents
|
|
426
|
+
const allowedRoots = [
|
|
427
|
+
cwd,
|
|
428
|
+
path_1.default.join(home, 'Desktop'),
|
|
429
|
+
path_1.default.join(home, 'Documents'),
|
|
430
|
+
];
|
|
431
|
+
const norm = (s) => s.toLowerCase().replace(/\//g, '\\').replace(/\\$/, '');
|
|
432
|
+
const nr = norm(resolved);
|
|
433
|
+
const ok = allowedRoots.some(root => {
|
|
434
|
+
const r = norm(root);
|
|
435
|
+
return nr === r || nr.startsWith(r + '\\');
|
|
436
|
+
});
|
|
437
|
+
if (!ok) {
|
|
438
|
+
throw new Error(`Path '${resolved}' is outside allowed write locations. Allowed: workspace, Desktop, Documents.`);
|
|
439
|
+
}
|
|
440
|
+
return resolved;
|
|
441
|
+
}
|
|
442
|
+
exports.APP_ALIASES = {
|
|
443
|
+
spotify: { win32: { type: 'uri', value: 'spotify' }, darwin: { type: 'app', value: 'Spotify' }, linux: { type: 'cmd', value: 'spotify' } },
|
|
444
|
+
chrome: { win32: { type: 'cmd', value: 'chrome' }, darwin: { type: 'app', value: 'Google Chrome' }, linux: { type: 'cmd', value: 'google-chrome' } },
|
|
445
|
+
firefox: { win32: { type: 'cmd', value: 'firefox' }, darwin: { type: 'app', value: 'Firefox' }, linux: { type: 'cmd', value: 'firefox' } },
|
|
446
|
+
edge: { win32: { type: 'cmd', value: 'msedge' }, darwin: { type: 'app', value: 'Microsoft Edge' }, linux: { type: 'cmd', value: 'microsoft-edge' } },
|
|
447
|
+
discord: { win32: { type: 'uri', value: 'discord' }, darwin: { type: 'app', value: 'Discord' }, linux: { type: 'cmd', value: 'discord' } },
|
|
448
|
+
slack: { win32: { type: 'uri', value: 'slack' }, darwin: { type: 'app', value: 'Slack' }, linux: { type: 'cmd', value: 'slack' } },
|
|
449
|
+
zoom: { win32: { type: 'uri', value: 'zoommtg' }, darwin: { type: 'app', value: 'zoom.us' }, linux: { type: 'cmd', value: 'zoom' } },
|
|
450
|
+
teams: { win32: { type: 'uri', value: 'msteams' }, darwin: { type: 'app', value: 'Microsoft Teams' }, linux: { type: 'cmd', value: 'teams' } },
|
|
451
|
+
vscode: { win32: { type: 'cmd', value: 'code' }, darwin: { type: 'app', value: 'Visual Studio Code' }, linux: { type: 'cmd', value: 'code' } },
|
|
452
|
+
notepad: { win32: { type: 'cmd', value: 'notepad.exe' }, darwin: { type: 'app', value: 'TextEdit' }, linux: { type: 'cmd', value: 'gedit' } },
|
|
453
|
+
'notepad++': { win32: { type: 'cmd', value: 'notepad++' } },
|
|
454
|
+
calculator: { win32: { type: 'cmd', value: 'calc' }, darwin: { type: 'app', value: 'Calculator' }, linux: { type: 'cmd', value: 'gnome-calculator' } },
|
|
455
|
+
paint: { win32: { type: 'cmd', value: 'mspaint' } },
|
|
456
|
+
explorer: { win32: { type: 'cmd', value: 'explorer' }, darwin: { type: 'cmd', value: 'open ~' }, linux: { type: 'cmd', value: 'nautilus' } },
|
|
457
|
+
terminal: { win32: { type: 'cmd', value: 'wt' }, darwin: { type: 'app', value: 'Terminal' }, linux: { type: 'cmd', value: 'gnome-terminal' } },
|
|
458
|
+
word: { win32: { type: 'cmd', value: 'winword' } },
|
|
459
|
+
excel: { win32: { type: 'cmd', value: 'excel' } },
|
|
460
|
+
powershell: { win32: { type: 'cmd', value: 'powershell' } },
|
|
461
|
+
cmd: { win32: { type: 'cmd', value: 'cmd' } },
|
|
462
|
+
};
|
|
463
|
+
// Display name aliases → canonical key in APP_ALIASES
|
|
464
|
+
const DISPLAY_ALIASES = {
|
|
465
|
+
'google chrome': 'chrome', 'microsoft edge': 'edge',
|
|
466
|
+
'vs code': 'vscode', 'visual studio code': 'vscode',
|
|
467
|
+
'microsoft teams': 'teams', 'file explorer': 'explorer',
|
|
468
|
+
'windows terminal': 'terminal', 'task manager': 'calculator',
|
|
469
|
+
'calc': 'calculator',
|
|
470
|
+
};
|
|
471
|
+
/**
|
|
472
|
+
* C13: Resolve the shell command to launch an app on a given platform.
|
|
473
|
+
* Pure function — takes platform arg for testability.
|
|
474
|
+
* @param appName - user-facing app name (lowercase, trimmed)
|
|
475
|
+
* @param platform - override for process.platform (for testing)
|
|
476
|
+
*/
|
|
477
|
+
function resolveLaunchCommand(appName, platform) {
|
|
478
|
+
const plat = (platform ?? process.platform);
|
|
479
|
+
const canonical = DISPLAY_ALIASES[appName] ?? appName;
|
|
480
|
+
const entry = exports.APP_ALIASES[canonical]?.[plat];
|
|
481
|
+
if (entry) {
|
|
482
|
+
switch (entry.type) {
|
|
483
|
+
case 'uri':
|
|
484
|
+
return plat === 'win32' ? `cmd /c start "" "${entry.value}:"`
|
|
485
|
+
: plat === 'darwin' ? `open "${entry.value}://"`
|
|
486
|
+
: `xdg-open "${entry.value}://"`;
|
|
487
|
+
case 'app':
|
|
488
|
+
return `open -a "${entry.value}"`;
|
|
489
|
+
case 'cmd':
|
|
490
|
+
if (plat === 'win32')
|
|
491
|
+
return `cmd /c start "" "${entry.value}"`;
|
|
492
|
+
if (plat === 'darwin')
|
|
493
|
+
return `open -a "${entry.value}"`;
|
|
494
|
+
return entry.value;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// Fallback for unknown apps
|
|
498
|
+
if (plat === 'win32')
|
|
499
|
+
return `cmd /c start "" "${appName}"`;
|
|
500
|
+
if (plat === 'darwin')
|
|
501
|
+
return `open -a "${appName}"`;
|
|
502
|
+
return `xdg-open "${appName}"`;
|
|
503
|
+
}
|
|
333
504
|
// ── Tool implementations ──────────────────────────────────────
|
|
334
505
|
exports.TOOLS = {
|
|
335
506
|
// ── respond — direct conversational reply (no external tools needed) ──
|
|
@@ -740,17 +911,7 @@ exports.TOOLS = {
|
|
|
740
911
|
return { success: false, output: '', error: 'Access denied: protected path. Aiden cannot write credentials, SSH keys, or env files.' };
|
|
741
912
|
}
|
|
742
913
|
try {
|
|
743
|
-
|
|
744
|
-
const _user = process.env.USERNAME || process.env.USER || require('os').userInfo().username || 'User';
|
|
745
|
-
const _home = require('os').homedir();
|
|
746
|
-
filePath = filePath
|
|
747
|
-
.replace(/^~[\/\\]/i, _home + path_1.default.sep)
|
|
748
|
-
.replace(/^Desktop[\/\\]/i, path_1.default.join(_home, 'Desktop') + path_1.default.sep)
|
|
749
|
-
.replace(/^C:\\Users\\Aiden\\/i, `C:\\Users\\${_user}\\`)
|
|
750
|
-
.replace(/^C:\/Users\/Aiden\//i, `C:/Users/${_user}/`);
|
|
751
|
-
const resolved = filePath.match(/^[A-Z]:/i) || filePath.startsWith('/')
|
|
752
|
-
? filePath
|
|
753
|
-
: path_1.default.join(process.cwd(), filePath);
|
|
914
|
+
const resolved = resolveWritePath(filePath);
|
|
754
915
|
fs_1.default.mkdirSync(path_1.default.dirname(resolved), { recursive: true });
|
|
755
916
|
fs_1.default.writeFileSync(resolved, content, 'utf-8');
|
|
756
917
|
const written = fs_1.default.existsSync(resolved);
|
|
@@ -825,6 +986,10 @@ exports.TOOLS = {
|
|
|
825
986
|
const script = p.script || p.code || p.command || '';
|
|
826
987
|
if (!script)
|
|
827
988
|
return { success: false, output: '', error: 'No script' };
|
|
989
|
+
// ── C8: Destructive path guard ─────────────────────────────────
|
|
990
|
+
const pyGuard = scanCodeForDestructivePaths(script, 'python');
|
|
991
|
+
if (pyGuard.denied)
|
|
992
|
+
return { success: false, output: '', error: pyGuard.reason };
|
|
828
993
|
// ── N+34: Docker sandbox routing ───────────────────────────
|
|
829
994
|
const _pyMode = process.env.AIDEN_SANDBOX_MODE || 'off';
|
|
830
995
|
if (_pyMode === 'strict' || _pyMode === 'auto') {
|
|
@@ -876,6 +1041,10 @@ exports.TOOLS = {
|
|
|
876
1041
|
const script = p.script || p.code || p.command || '';
|
|
877
1042
|
if (!script)
|
|
878
1043
|
return { success: false, output: '', error: 'No script' };
|
|
1044
|
+
// ── C8: Destructive path guard ─────────────────────────────────
|
|
1045
|
+
const nodeGuard = scanCodeForDestructivePaths(script, 'node');
|
|
1046
|
+
if (nodeGuard.denied)
|
|
1047
|
+
return { success: false, output: '', error: nodeGuard.reason };
|
|
879
1048
|
const tmp = path_1.default.join(process.cwd(), 'workspace', `js_${Date.now()}.js`);
|
|
880
1049
|
fs_1.default.mkdirSync(path_1.default.dirname(tmp), { recursive: true });
|
|
881
1050
|
fs_1.default.writeFileSync(tmp, script);
|
|
@@ -905,6 +1074,15 @@ exports.TOOLS = {
|
|
|
905
1074
|
return { success: false, output: '', error: e.message };
|
|
906
1075
|
}
|
|
907
1076
|
},
|
|
1077
|
+
now_playing: async () => {
|
|
1078
|
+
try {
|
|
1079
|
+
const result = await (0, nowPlaying_1.getNowPlaying)();
|
|
1080
|
+
return { success: true, output: JSON.stringify(result) };
|
|
1081
|
+
}
|
|
1082
|
+
catch (e) {
|
|
1083
|
+
return { success: false, output: '', error: e.message };
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
908
1086
|
notify: async (p) => {
|
|
909
1087
|
const msg = (p.message || p.command || p.title || p.body || '')
|
|
910
1088
|
.replace(/'/g, '').replace(/"/g, '').replace(/`/g, '').replace(/\$/g, '').trim();
|
|
@@ -1481,7 +1659,7 @@ exports.TOOLS = {
|
|
|
1481
1659
|
},
|
|
1482
1660
|
screenshot: async (_p) => {
|
|
1483
1661
|
try {
|
|
1484
|
-
const filepath = await (0, computerControl_1.takeScreenshot)();
|
|
1662
|
+
const filepath = await (0, computerControl_1.takeScreenshot)(_p?.outputPath ? { outputPath: _p.outputPath } : undefined);
|
|
1485
1663
|
const stats = require('fs').statSync(filepath);
|
|
1486
1664
|
return { success: true, output: `Screenshot saved: ${filepath} (${Math.round(stats.size / 1024)}kb)`, path: filepath };
|
|
1487
1665
|
}
|
|
@@ -1602,48 +1780,15 @@ exports.TOOLS = {
|
|
|
1602
1780
|
const appName = (p.app_name ?? p.appName ?? p.app ?? p.path ?? p.command ?? p.name ?? p.target ?? '').toString().toLowerCase().trim();
|
|
1603
1781
|
if (!appName)
|
|
1604
1782
|
return { success: false, output: '', error: 'No app_name provided. Pass app_name e.g. "chrome" or "spotify".' };
|
|
1605
|
-
//
|
|
1606
|
-
const
|
|
1607
|
-
'chrome': 'chrome',
|
|
1608
|
-
'google chrome': 'chrome',
|
|
1609
|
-
'firefox': 'firefox',
|
|
1610
|
-
'edge': 'msedge',
|
|
1611
|
-
'microsoft edge': 'msedge',
|
|
1612
|
-
'spotify': 'spotify',
|
|
1613
|
-
'discord': 'discord',
|
|
1614
|
-
'vscode': 'code',
|
|
1615
|
-
'vs code': 'code',
|
|
1616
|
-
'visual studio code': 'code',
|
|
1617
|
-
'notepad': 'notepad',
|
|
1618
|
-
'notepad++': 'notepad++',
|
|
1619
|
-
'word': 'winword',
|
|
1620
|
-
'excel': 'excel',
|
|
1621
|
-
'powerpoint': 'powerpnt',
|
|
1622
|
-
'slack': 'slack',
|
|
1623
|
-
'zoom': 'zoom',
|
|
1624
|
-
'teams': 'teams',
|
|
1625
|
-
'microsoft teams': 'teams',
|
|
1626
|
-
'explorer': 'explorer',
|
|
1627
|
-
'file explorer': 'explorer',
|
|
1628
|
-
'task manager': 'taskmgr',
|
|
1629
|
-
'taskmgr': 'taskmgr',
|
|
1630
|
-
'calculator': 'calc',
|
|
1631
|
-
'calc': 'calc',
|
|
1632
|
-
'paint': 'mspaint',
|
|
1633
|
-
'terminal': 'wt',
|
|
1634
|
-
'windows terminal': 'wt',
|
|
1635
|
-
'cmd': 'cmd',
|
|
1636
|
-
'powershell': 'powershell',
|
|
1637
|
-
};
|
|
1638
|
-
const exe = exeMap[appName] ?? appName;
|
|
1783
|
+
// C13: cross-platform launch via resolveLaunchCommand()
|
|
1784
|
+
const cmd = resolveLaunchCommand(appName);
|
|
1639
1785
|
try {
|
|
1640
1786
|
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
return { success: true, output: `Launched: "${appName}"` };
|
|
1787
|
+
execSync(cmd, { timeout: 10000 });
|
|
1788
|
+
return { success: true, output: `Launched: "${appName}" via: ${cmd}` };
|
|
1644
1789
|
}
|
|
1645
1790
|
catch (e) {
|
|
1646
|
-
return { success: false, output: '', error: e.message };
|
|
1791
|
+
return { success: false, output: '', error: `Failed to launch "${appName}": ${e.message}` };
|
|
1647
1792
|
}
|
|
1648
1793
|
},
|
|
1649
1794
|
app_close: async (p) => {
|
|
@@ -2572,12 +2717,18 @@ public class AidenVolSet {
|
|
|
2572
2717
|
// ── Plugin-registered tools ───────────────────────────────────
|
|
2573
2718
|
const externalTools = {};
|
|
2574
2719
|
const externalToolsMeta = {};
|
|
2720
|
+
// v3.19 Phase 1 — registry generation counter (Hermes run_agent.py:113,159).
|
|
2721
|
+
// Incremented whenever a new external tool is registered so deriver caches
|
|
2722
|
+
// know to recompute. Declared here so registerExternalTool can reference it
|
|
2723
|
+
// before the deriver block (which appears after TOOL_REGISTRY).
|
|
2724
|
+
let _generation = 0;
|
|
2575
2725
|
function registerExternalTool(name, fn, source) {
|
|
2576
2726
|
externalTools[name] = async (input) => {
|
|
2577
2727
|
const r = await fn(input);
|
|
2578
2728
|
return { success: r.success, output: r.output };
|
|
2579
2729
|
};
|
|
2580
2730
|
externalToolsMeta[name] = { source };
|
|
2731
|
+
_generation++; // invalidate deriver caches
|
|
2581
2732
|
if ((process.env.AIDEN_LOG_LEVEL || 'info') === 'debug') {
|
|
2582
2733
|
console.log('[ToolRegistry] Plugin "' + source + '" registered tool: ' + name);
|
|
2583
2734
|
}
|
|
@@ -2586,6 +2737,13 @@ function registerExternalTool(name, fn, source) {
|
|
|
2586
2737
|
function getExternalToolsMeta() {
|
|
2587
2738
|
return { ...externalToolsMeta };
|
|
2588
2739
|
}
|
|
2740
|
+
/** Dynamic tool-existence check that includes both TOOLS (static) and
|
|
2741
|
+
* externalTools (registered at runtime via registerExternalTool / registerSlashMirrorTools).
|
|
2742
|
+
* Use this in the executor instead of the pre-computed ALLOWED_TOOLS constant, which
|
|
2743
|
+
* is frozen at module-load time before mirror tools are registered. */
|
|
2744
|
+
function isKnownTool(name) {
|
|
2745
|
+
return name in exports.TOOLS || name in externalTools;
|
|
2746
|
+
}
|
|
2589
2747
|
// ── Internal dispatcher — no retry, no timeout ────────────────
|
|
2590
2748
|
async function runTool(tool, input) {
|
|
2591
2749
|
// Build per-call context with tool-scoped progress emitter
|
|
@@ -2730,6 +2888,7 @@ exports.TOOL_DESCRIPTIONS = {
|
|
|
2730
2888
|
run_python: 'Execute a Python script and return stdout/stderr',
|
|
2731
2889
|
run_node: 'Execute Node.js/JavaScript code and return the output',
|
|
2732
2890
|
system_info: 'Get system hardware and OS information (CPU, RAM, disk, OS)',
|
|
2891
|
+
now_playing: 'Get the currently playing media (song, artist, app). Calls Windows MediaSession live — always reflects real-time state. Use whenever the user asks what is playing, whether music is paused, or what track is on.',
|
|
2733
2892
|
notify: 'Send a desktop notification to the user',
|
|
2734
2893
|
get_stocks: 'Get top gainers, losers, or most active stocks from NSE/BSE',
|
|
2735
2894
|
get_market_data: 'Get real-time price, change%, and volume for a stock symbol',
|
|
@@ -2739,7 +2898,7 @@ exports.TOOL_DESCRIPTIONS = {
|
|
|
2739
2898
|
mouse_click: 'Click the mouse at screen coordinates',
|
|
2740
2899
|
keyboard_type: 'Type text using the keyboard',
|
|
2741
2900
|
keyboard_press: 'Press a keyboard key or shortcut (e.g. ctrl+c)',
|
|
2742
|
-
screenshot: 'Take a screenshot of the entire screen',
|
|
2901
|
+
screenshot: 'Take a screenshot of the entire screen. Optional: outputPath (absolute path, e.g. C:\\Users\\shiva\\Desktop\\shot.png) to save to a specific location; defaults to workspace/screenshots/.',
|
|
2743
2902
|
screen_read: 'Read and describe the current screen contents',
|
|
2744
2903
|
vision_loop: 'Autonomously control the computer using vision to complete a goal',
|
|
2745
2904
|
wait: 'Pause execution for a specified number of milliseconds',
|
|
@@ -2778,6 +2937,9 @@ exports.TOOL_DESCRIPTIONS = {
|
|
|
2778
2937
|
swarm: 'Run N isolated subagents on the same task in parallel and aggregate their answers via voting or synthesis. Use for high-confidence research where multiple independent perspectives reduce error.',
|
|
2779
2938
|
send_file_local: 'Send a file to another device on the local network via LocalSend (op: discover | send)',
|
|
2780
2939
|
receive_file_local: 'Wait for an incoming LocalSend file transfer on the local network',
|
|
2940
|
+
ingest_youtube: 'Download and ingest a YouTube video into memory: transcribes audio, extracts metadata, and stores as a searchable memory entry.',
|
|
2941
|
+
memory_store: 'Persist a fact, preference, or note to permanent memory right now. Use when the user says "remember", "save this", "keep track of", or wants something stored. Pass { fact: "..." }.',
|
|
2942
|
+
memory_forget: 'Remove a fact or preference from permanent memory. Use when the user says "forget", "remove from memory", "delete from memory". Pass { fact: "keyword to match" }.',
|
|
2781
2943
|
};
|
|
2782
2944
|
// ── N+28: TOOL_NAMES_ONLY ──────────────────────────────────────
|
|
2783
2945
|
// One-liner per tool — first sentence of TOOL_DESCRIPTIONS, truncated to 60 chars.
|
|
@@ -2804,7 +2966,10 @@ const TOOL_TIERS = {
|
|
|
2804
2966
|
get_company_info: 1,
|
|
2805
2967
|
social_research: 1,
|
|
2806
2968
|
system_info: 1,
|
|
2969
|
+
now_playing: 1,
|
|
2807
2970
|
notify: 1,
|
|
2971
|
+
memory_store: 1,
|
|
2972
|
+
memory_forget: 1,
|
|
2808
2973
|
wait: 1,
|
|
2809
2974
|
get_briefing: 1,
|
|
2810
2975
|
get_natural_events: 1,
|
|
@@ -2920,6 +3085,7 @@ const TOOL_CATEGORIES = {
|
|
|
2920
3085
|
get_natural_events: ['data'],
|
|
2921
3086
|
notify: ['system'],
|
|
2922
3087
|
system_info: ['system'],
|
|
3088
|
+
now_playing: ['system'],
|
|
2923
3089
|
wait: ['system', 'browser', 'screen'],
|
|
2924
3090
|
clipboard_read: ['system', 'code'],
|
|
2925
3091
|
clipboard_write: ['system', 'code'],
|
|
@@ -2937,6 +3103,8 @@ const TOOL_CATEGORIES = {
|
|
|
2937
3103
|
analytics: ['introspection'],
|
|
2938
3104
|
spend: ['introspection'],
|
|
2939
3105
|
memory_show: ['introspection', 'memory'],
|
|
3106
|
+
memory_store: ['memory'],
|
|
3107
|
+
memory_forget: ['memory'],
|
|
2940
3108
|
lessons: ['introspection', 'memory'],
|
|
2941
3109
|
skills_list: ['introspection'],
|
|
2942
3110
|
tools_list: ['introspection'],
|
|
@@ -2957,6 +3125,607 @@ const TOOL_CATEGORIES = {
|
|
|
2957
3125
|
voice_clone: ['voice'],
|
|
2958
3126
|
voice_design: ['voice'],
|
|
2959
3127
|
};
|
|
3128
|
+
exports.TOOL_REGISTRY = {
|
|
3129
|
+
// ── Core / response ──────────────────────────────────────────────────────────
|
|
3130
|
+
respond: {
|
|
3131
|
+
description: 'Send a direct conversational response to the user. Use for greetings, capability questions, clarifications, simple factual answers, and anything that does NOT require external tools. This is the default tool when no other tool is needed.',
|
|
3132
|
+
tier: 1, category: ['core'],
|
|
3133
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3134
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3135
|
+
},
|
|
3136
|
+
manage_goals: {
|
|
3137
|
+
description: 'Track and manage goals and projects. Use when user asks what to work on, mentions a project, deadline, or launch plan. Actions: list, add, update, complete, remove, suggest.',
|
|
3138
|
+
tier: 1, category: ['core'],
|
|
3139
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3140
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3141
|
+
},
|
|
3142
|
+
compact_context: {
|
|
3143
|
+
description: 'Summarize and compress the current conversation context. Saves session to disk and extracts durable memories. Call when context is getting long.',
|
|
3144
|
+
tier: 1, category: ['core'],
|
|
3145
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3146
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3147
|
+
},
|
|
3148
|
+
run_agent: {
|
|
3149
|
+
description: 'Spawn a sub-agent to complete a sub-goal autonomously',
|
|
3150
|
+
tier: 1, category: ['core'],
|
|
3151
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3152
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3153
|
+
},
|
|
3154
|
+
lookup_skill: {
|
|
3155
|
+
description: 'Search learned skills for a matching pattern. Returns the SKILL.md of the best match. Use before planning multi-step tasks to check if Aiden already knows how to do it.',
|
|
3156
|
+
tier: 1, category: ['core'],
|
|
3157
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3158
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3159
|
+
},
|
|
3160
|
+
lookup_tool_schema: {
|
|
3161
|
+
description: 'Get the full description for a named tool. Call before using an unfamiliar tool.',
|
|
3162
|
+
tier: 1, category: ['core'],
|
|
3163
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3164
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3165
|
+
},
|
|
3166
|
+
// ── Web / research ───────────────────────────────────────────────────────────
|
|
3167
|
+
web_search: {
|
|
3168
|
+
description: 'Search the web for current information, news, or any topic',
|
|
3169
|
+
tier: 1, category: ['web', 'data'], timeoutMs: 15000,
|
|
3170
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3171
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3172
|
+
},
|
|
3173
|
+
fetch_url: {
|
|
3174
|
+
description: 'Fetch the content of any URL and return the text',
|
|
3175
|
+
tier: 1, category: ['web'], timeoutMs: 20000,
|
|
3176
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3177
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3178
|
+
},
|
|
3179
|
+
fetch_page: {
|
|
3180
|
+
description: 'Fetch a web page and extract its readable text content',
|
|
3181
|
+
tier: 1, category: ['web'], timeoutMs: 20000,
|
|
3182
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3183
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3184
|
+
},
|
|
3185
|
+
deep_research: {
|
|
3186
|
+
description: 'Conduct thorough multi-step research on a topic using multiple sources',
|
|
3187
|
+
tier: 1, category: ['web'], timeoutMs: 60000,
|
|
3188
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3189
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3190
|
+
},
|
|
3191
|
+
social_research: {
|
|
3192
|
+
description: 'Research a person or company across social and public sources',
|
|
3193
|
+
tier: 1, category: ['web', 'data'], timeoutMs: 30000,
|
|
3194
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3195
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3196
|
+
},
|
|
3197
|
+
ingest_youtube: {
|
|
3198
|
+
description: 'Ingest a YouTube video — downloads transcript or audio and extracts searchable text',
|
|
3199
|
+
tier: 1, category: ['web', 'memory'],
|
|
3200
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3201
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3202
|
+
},
|
|
3203
|
+
// ── Browser ──────────────────────────────────────────────────────────────────
|
|
3204
|
+
open_browser: {
|
|
3205
|
+
description: 'Open a URL in the system browser',
|
|
3206
|
+
tier: 3, category: ['browser'], timeoutMs: 15000,
|
|
3207
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3208
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3209
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3210
|
+
},
|
|
3211
|
+
browser_click: {
|
|
3212
|
+
description: 'Click on an element in the browser by selector',
|
|
3213
|
+
tier: 3, category: ['browser'], timeoutMs: 10000,
|
|
3214
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3215
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3216
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3217
|
+
},
|
|
3218
|
+
browser_scroll: {
|
|
3219
|
+
description: 'Scroll the browser page or a specific element. Params: direction (up|down|top|bottom, default down), amount (pixels, default 500), selector (optional CSS selector to scroll a specific element)',
|
|
3220
|
+
tier: 3, category: ['browser'], timeoutMs: 8000,
|
|
3221
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3222
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3223
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3224
|
+
},
|
|
3225
|
+
browser_type: {
|
|
3226
|
+
description: 'Type text into a browser input field',
|
|
3227
|
+
tier: 3, category: ['browser'], timeoutMs: 10000,
|
|
3228
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3229
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3230
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3231
|
+
},
|
|
3232
|
+
browser_extract: {
|
|
3233
|
+
description: 'Extract text content from the current browser page',
|
|
3234
|
+
tier: 3, category: ['browser'], timeoutMs: 10000,
|
|
3235
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3236
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3237
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3238
|
+
},
|
|
3239
|
+
browser_screenshot: {
|
|
3240
|
+
description: 'Take a screenshot of the current browser window',
|
|
3241
|
+
tier: 3, category: ['browser'], timeoutMs: 8000,
|
|
3242
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3243
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3244
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3245
|
+
},
|
|
3246
|
+
browser_get_url: {
|
|
3247
|
+
description: 'Return the URL currently loaded in the browser',
|
|
3248
|
+
tier: 3, category: ['browser'], timeoutMs: 5000,
|
|
3249
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3250
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3251
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3252
|
+
},
|
|
3253
|
+
// ── Files ────────────────────────────────────────────────────────────────────
|
|
3254
|
+
file_write: {
|
|
3255
|
+
description: 'Write content to a file at the specified path',
|
|
3256
|
+
tier: 2, category: ['files'],
|
|
3257
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3258
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3259
|
+
},
|
|
3260
|
+
file_read: {
|
|
3261
|
+
description: 'Read the contents of a file at the specified path',
|
|
3262
|
+
tier: 2, category: ['files'],
|
|
3263
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3264
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3265
|
+
},
|
|
3266
|
+
file_list: {
|
|
3267
|
+
description: 'List files in a directory',
|
|
3268
|
+
tier: 2, category: ['files'],
|
|
3269
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3270
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3271
|
+
},
|
|
3272
|
+
watch_folder: {
|
|
3273
|
+
description: 'Watch a folder and react automatically when new files appear',
|
|
3274
|
+
tier: 2, category: ['files', 'system'], timeoutMs: 10000,
|
|
3275
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3276
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3277
|
+
},
|
|
3278
|
+
watch_folder_list: {
|
|
3279
|
+
description: 'List all currently watched folder paths',
|
|
3280
|
+
tier: 2, category: ['files', 'system'], timeoutMs: 5000,
|
|
3281
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3282
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3283
|
+
},
|
|
3284
|
+
// ── Shell / code execution ───────────────────────────────────────────────────
|
|
3285
|
+
shell_exec: {
|
|
3286
|
+
description: 'Execute a shell/PowerShell command and return the output',
|
|
3287
|
+
tier: 2, category: ['code', 'system'], timeoutMs: 30000,
|
|
3288
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3289
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3290
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3291
|
+
},
|
|
3292
|
+
run_powershell: {
|
|
3293
|
+
description: 'Run a PowerShell command on Windows',
|
|
3294
|
+
tier: 2, category: ['code', 'system'], timeoutMs: 30000,
|
|
3295
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3296
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3297
|
+
},
|
|
3298
|
+
cmd: {
|
|
3299
|
+
description: 'Run a Windows cmd.exe command and return stdout/stderr/exitCode',
|
|
3300
|
+
tier: 2, category: ['code', 'system'], timeoutMs: 30000,
|
|
3301
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3302
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3303
|
+
},
|
|
3304
|
+
ps: {
|
|
3305
|
+
description: 'Run a PowerShell command directly (no temp file) and return stdout/stderr/exitCode',
|
|
3306
|
+
tier: 2, category: ['code', 'system'], timeoutMs: 30000,
|
|
3307
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3308
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3309
|
+
},
|
|
3310
|
+
wsl: {
|
|
3311
|
+
description: 'Run a bash command inside WSL (Windows Subsystem for Linux); auto-translates C:\\ paths to /mnt/c/',
|
|
3312
|
+
tier: 2, category: ['code', 'system'], timeoutMs: 30000,
|
|
3313
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3314
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3315
|
+
},
|
|
3316
|
+
run_python: {
|
|
3317
|
+
description: 'Execute a Python script and return stdout/stderr',
|
|
3318
|
+
tier: 2, category: ['code'], timeoutMs: 60000,
|
|
3319
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3320
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3321
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3322
|
+
},
|
|
3323
|
+
run_node: {
|
|
3324
|
+
description: 'Execute Node.js/JavaScript code and return the output',
|
|
3325
|
+
tier: 2, category: ['code'], timeoutMs: 60000,
|
|
3326
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3327
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3328
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3329
|
+
},
|
|
3330
|
+
run: {
|
|
3331
|
+
description: 'Run a command or script (generic alias for shell_exec)',
|
|
3332
|
+
tier: 2, category: ['code'],
|
|
3333
|
+
parallel: 'never', // agentLoop.ts:1965 — not in SEQUENTIAL_ONLY
|
|
3334
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3335
|
+
},
|
|
3336
|
+
code_interpreter_python: {
|
|
3337
|
+
description: 'Run Python code in a sandboxed interpreter with data science libraries',
|
|
3338
|
+
tier: 2, category: ['code'], timeoutMs: 35000,
|
|
3339
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3340
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3341
|
+
},
|
|
3342
|
+
code_interpreter_node: {
|
|
3343
|
+
description: 'Run Node.js code in a sandboxed interpreter',
|
|
3344
|
+
tier: 2, category: ['code'], timeoutMs: 35000,
|
|
3345
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3346
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3347
|
+
},
|
|
3348
|
+
// ── Screen / vision / input ──────────────────────────────────────────────────
|
|
3349
|
+
screenshot: {
|
|
3350
|
+
description: 'Take a screenshot of the entire screen. Optional param: outputPath (absolute path, e.g. C:\\Users\\shiva\\Desktop\\shot.png) — if omitted, saves to workspace/screenshots/.',
|
|
3351
|
+
tier: 4, category: ['screen'], timeoutMs: 10000,
|
|
3352
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3353
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3354
|
+
},
|
|
3355
|
+
screen_read: {
|
|
3356
|
+
description: 'Read and describe the current screen contents',
|
|
3357
|
+
tier: 4, category: ['screen'],
|
|
3358
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3359
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3360
|
+
},
|
|
3361
|
+
vision_loop: {
|
|
3362
|
+
description: 'Autonomously control the computer using vision to complete a goal',
|
|
3363
|
+
tier: 4, category: ['screen'], timeoutMs: 120000,
|
|
3364
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3365
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3366
|
+
},
|
|
3367
|
+
vision_analyze: {
|
|
3368
|
+
description: 'Analyze an image file using computer vision and return a structured description',
|
|
3369
|
+
tier: 4, category: ['screen', 'data'], timeoutMs: 45000,
|
|
3370
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3371
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3372
|
+
},
|
|
3373
|
+
mouse_move: {
|
|
3374
|
+
description: 'Move the mouse cursor to screen coordinates',
|
|
3375
|
+
tier: 4, category: ['screen'],
|
|
3376
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3377
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3378
|
+
},
|
|
3379
|
+
mouse_click: {
|
|
3380
|
+
description: 'Click the mouse at screen coordinates',
|
|
3381
|
+
tier: 4, category: ['screen'],
|
|
3382
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3383
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3384
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3385
|
+
},
|
|
3386
|
+
keyboard_type: {
|
|
3387
|
+
description: 'Type text using the keyboard',
|
|
3388
|
+
tier: 4, category: ['screen'],
|
|
3389
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3390
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3391
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3392
|
+
},
|
|
3393
|
+
keyboard_press: {
|
|
3394
|
+
description: 'Press a keyboard key or shortcut (e.g. ctrl+c)',
|
|
3395
|
+
tier: 4, category: ['screen'],
|
|
3396
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3397
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3398
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3399
|
+
},
|
|
3400
|
+
// ── Data / market ────────────────────────────────────────────────────────────
|
|
3401
|
+
get_stocks: {
|
|
3402
|
+
description: 'Get top gainers, losers, or most active stocks from NSE/BSE',
|
|
3403
|
+
tier: 1, category: ['data'], timeoutMs: 20000,
|
|
3404
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3405
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3406
|
+
},
|
|
3407
|
+
get_market_data: {
|
|
3408
|
+
description: 'Get real-time price, change%, and volume for a stock symbol',
|
|
3409
|
+
tier: 1, category: ['data'], timeoutMs: 15000,
|
|
3410
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3411
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3412
|
+
},
|
|
3413
|
+
get_company_info: {
|
|
3414
|
+
description: 'Get company profile, sector, P/E ratio, EPS, and revenue',
|
|
3415
|
+
tier: 1, category: ['data'], timeoutMs: 15000,
|
|
3416
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3417
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3418
|
+
},
|
|
3419
|
+
get_briefing: {
|
|
3420
|
+
description: 'Run the morning briefing: weather, markets, news, and daily summary',
|
|
3421
|
+
tier: 1, category: ['data'],
|
|
3422
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3423
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3424
|
+
},
|
|
3425
|
+
get_natural_events: {
|
|
3426
|
+
description: 'Fetch active natural events from NASA EONET API. Returns current earthquakes, wildfires, storms, floods, and other natural events worldwide.',
|
|
3427
|
+
tier: 1, category: ['data'],
|
|
3428
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3429
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3430
|
+
},
|
|
3431
|
+
// ── System / OS ──────────────────────────────────────────────────────────────
|
|
3432
|
+
system_info: {
|
|
3433
|
+
description: 'Get system hardware and OS information (CPU, RAM, disk, OS)',
|
|
3434
|
+
tier: 1, category: ['system'],
|
|
3435
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3436
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3437
|
+
},
|
|
3438
|
+
now_playing: {
|
|
3439
|
+
description: 'Get the currently playing media (song, artist, app). Calls Windows MediaSession live — always reflects real-time state. Use whenever the user asks what is playing, whether music is paused, or what track is on.',
|
|
3440
|
+
tier: 1, category: ['system'],
|
|
3441
|
+
parallel: 'safe', // read-only, no side effects
|
|
3442
|
+
mcp: 'safe',
|
|
3443
|
+
timeoutMs: 5000,
|
|
3444
|
+
},
|
|
3445
|
+
notify: {
|
|
3446
|
+
description: 'Send a desktop notification to the user',
|
|
3447
|
+
tier: 1, category: ['system'],
|
|
3448
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3449
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3450
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3451
|
+
},
|
|
3452
|
+
wait: {
|
|
3453
|
+
description: 'Pause execution for a specified number of milliseconds',
|
|
3454
|
+
tier: 1, category: ['system', 'browser', 'screen'], timeoutMs: 6000,
|
|
3455
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3456
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3457
|
+
},
|
|
3458
|
+
clipboard_read: {
|
|
3459
|
+
description: 'Read the current contents of the system clipboard',
|
|
3460
|
+
tier: 2, category: ['system', 'code'], timeoutMs: 5000,
|
|
3461
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3462
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3463
|
+
},
|
|
3464
|
+
clipboard_write: {
|
|
3465
|
+
description: 'Write text to the system clipboard',
|
|
3466
|
+
tier: 2, category: ['system', 'code'], timeoutMs: 5000,
|
|
3467
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3468
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3469
|
+
},
|
|
3470
|
+
window_list: {
|
|
3471
|
+
description: 'List all open windows on the desktop',
|
|
3472
|
+
tier: 3, category: ['browser', 'system'], timeoutMs: 10000,
|
|
3473
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3474
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3475
|
+
},
|
|
3476
|
+
window_focus: {
|
|
3477
|
+
description: 'Bring a specific window to the foreground by title',
|
|
3478
|
+
tier: 3, category: ['browser', 'system'], timeoutMs: 8000,
|
|
3479
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3480
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3481
|
+
},
|
|
3482
|
+
app_launch: {
|
|
3483
|
+
description: 'Launch an application by name or executable path',
|
|
3484
|
+
tier: 3, category: ['browser', 'system'], timeoutMs: 10000,
|
|
3485
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3486
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3487
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3488
|
+
},
|
|
3489
|
+
app_close: {
|
|
3490
|
+
description: 'Close an application by window title or process name',
|
|
3491
|
+
tier: 3, category: ['browser', 'system'], timeoutMs: 8000,
|
|
3492
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3493
|
+
retry: false, // agentLoop.ts:1881 NO_RETRY_TOOLS
|
|
3494
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3495
|
+
},
|
|
3496
|
+
system_volume: {
|
|
3497
|
+
description: 'Get or set Windows speaker volume (get/up/down/mute/unmute/set)',
|
|
3498
|
+
tier: 2, category: ['system'], timeoutMs: 8000,
|
|
3499
|
+
parallel: 'sequential', // agentLoop.ts:1965 SEQUENTIAL_ONLY
|
|
3500
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3501
|
+
},
|
|
3502
|
+
schedule_reminder: {
|
|
3503
|
+
description: "Schedule a desktop notification reminder. Params: message (string), delaySeconds or delayMs (number), recurring ('hourly'|'daily'|'weekly', optional). op='list' to see pending reminders, op='cancel' with id to cancel one.",
|
|
3504
|
+
tier: 0, category: ['system'],
|
|
3505
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3506
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3507
|
+
},
|
|
3508
|
+
// ── Git ──────────────────────────────────────────────────────────────────────
|
|
3509
|
+
git_status: {
|
|
3510
|
+
description: 'Show git status and recent commits for a repository. Provide path parameter for a specific directory.',
|
|
3511
|
+
tier: 2, category: ['git'], timeoutMs: 15000,
|
|
3512
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3513
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3514
|
+
},
|
|
3515
|
+
git_commit: {
|
|
3516
|
+
description: 'Stage and commit files to a local git repository',
|
|
3517
|
+
tier: 2, category: ['git'], timeoutMs: 30000,
|
|
3518
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3519
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3520
|
+
},
|
|
3521
|
+
git_push: {
|
|
3522
|
+
description: 'Push committed changes to a remote git repository',
|
|
3523
|
+
tier: 2, category: ['git'], timeoutMs: 60000,
|
|
3524
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3525
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3526
|
+
},
|
|
3527
|
+
// ── Comms / calendar / email ─────────────────────────────────────────────────
|
|
3528
|
+
get_calendar: {
|
|
3529
|
+
description: 'Get upcoming calendar events from Google Calendar (requires iCal URL in Settings → Channels). Parameters: daysAhead (number, default 7).',
|
|
3530
|
+
tier: 1, category: ['data', 'system'],
|
|
3531
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3532
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3533
|
+
},
|
|
3534
|
+
read_email: {
|
|
3535
|
+
description: 'Read recent unread emails from Gmail (requires App Password in Settings → Channels). Parameters: count (number, default 10), folder (string, default INBOX).',
|
|
3536
|
+
tier: 1, category: ['data', 'system'],
|
|
3537
|
+
parallel: 'safe', // agentLoop.ts:1957 PARALLEL_SAFE
|
|
3538
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3539
|
+
},
|
|
3540
|
+
send_email: {
|
|
3541
|
+
description: 'Send an email via Gmail (requires App Password in Settings → Channels). Parameters: to (string), subject (string), body (string).',
|
|
3542
|
+
tier: 1, category: ['data', 'system'],
|
|
3543
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3544
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3545
|
+
},
|
|
3546
|
+
send_file_local: {
|
|
3547
|
+
description: 'Send a file to another device on the local network via LocalSend (op: discover | send)',
|
|
3548
|
+
tier: 2, category: ['files', 'system'],
|
|
3549
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3550
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3551
|
+
},
|
|
3552
|
+
receive_file_local: {
|
|
3553
|
+
description: 'Wait for an incoming LocalSend file transfer on the local network',
|
|
3554
|
+
tier: 2, category: ['files', 'system'],
|
|
3555
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3556
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3557
|
+
},
|
|
3558
|
+
// ── Delegation / subagents ───────────────────────────────────────────────────
|
|
3559
|
+
spawn: {
|
|
3560
|
+
description: "Delegate a sub-task to an isolated subagent with its own context and half the remaining iteration budget. Returns the subagent's synthesized answer.",
|
|
3561
|
+
tier: 2, category: ['delegation'],
|
|
3562
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3563
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3564
|
+
},
|
|
3565
|
+
spawn_subagent: {
|
|
3566
|
+
description: "Spawn an isolated subagent to handle a parallel sub-task. The subagent runs in its own conversation context with half your remaining iteration budget. Use for: research that would bloat your context, parallel work where you need both results, sandboxed exploration. Returns the subagent's final reply text.",
|
|
3567
|
+
tier: 2, category: ['delegation'],
|
|
3568
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3569
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3570
|
+
},
|
|
3571
|
+
swarm: {
|
|
3572
|
+
description: 'Run N isolated subagents on the same task in parallel and aggregate their answers via voting or synthesis. Use for high-confidence research where multiple independent perspectives reduce error.',
|
|
3573
|
+
tier: 2, category: ['delegation'],
|
|
3574
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3575
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3576
|
+
},
|
|
3577
|
+
// ── Voice ────────────────────────────────────────────────────────────────────
|
|
3578
|
+
voice_speak: {
|
|
3579
|
+
description: 'Speak text aloud using the TTS provider chain (VoxCPM → Edge TTS → ElevenLabs → SAPI). Accepts text, voice, rate, volume, provider overrides.',
|
|
3580
|
+
tier: 2, category: ['voice'], timeoutMs: 60000,
|
|
3581
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3582
|
+
mcp: 'destructive', // api/mcp.ts:44
|
|
3583
|
+
},
|
|
3584
|
+
voice_transcribe: {
|
|
3585
|
+
description: 'Transcribe an audio file to text using the STT provider chain (Groq Whisper → OpenAI Whisper → Whisper.cpp). Returns { text, provider, durationMs }.',
|
|
3586
|
+
tier: 2, category: ['voice'], timeoutMs: 60000,
|
|
3587
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3588
|
+
mcp: 'safe', // api/mcp.ts:25
|
|
3589
|
+
},
|
|
3590
|
+
voice_clone: {
|
|
3591
|
+
description: 'Clone a voice from a reference audio file and synthesize new text. Requires text and referenceAudioPath. Uses VoxCPM when USE_VOXCPM=1.',
|
|
3592
|
+
tier: 2, category: ['voice'], timeoutMs: 120000,
|
|
3593
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3594
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3595
|
+
},
|
|
3596
|
+
voice_design: {
|
|
3597
|
+
description: 'Design a custom voice from a text description and synthesize text with it. Requires text and voiceDescription. Uses VoxCPM when USE_VOXCPM=1.',
|
|
3598
|
+
tier: 2, category: ['voice'], timeoutMs: 120000,
|
|
3599
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3600
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3601
|
+
},
|
|
3602
|
+
// ── Interaction / UX ─────────────────────────────────────────────────────────
|
|
3603
|
+
clarify: {
|
|
3604
|
+
description: 'Ask the user a clarifying question and wait for their typed response before proceeding',
|
|
3605
|
+
tier: 1, category: ['interaction', 'core'], timeoutMs: 300000,
|
|
3606
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3607
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3608
|
+
},
|
|
3609
|
+
todo: {
|
|
3610
|
+
description: 'Manage the current session todo list — add, check off, or display pending tasks',
|
|
3611
|
+
tier: 1, category: ['interaction', 'core'],
|
|
3612
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3613
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3614
|
+
},
|
|
3615
|
+
memory_store: {
|
|
3616
|
+
description: 'Persist a fact, preference, or observation to permanent memory right now. Use whenever the user says "remember", "save this", "keep track of", or similar. Pass { fact: "the thing to remember" }.',
|
|
3617
|
+
tier: 1, category: ['memory'],
|
|
3618
|
+
parallel: 'never',
|
|
3619
|
+
retry: false, // write operation — don't double-write on retry
|
|
3620
|
+
mcp: 'excluded',
|
|
3621
|
+
},
|
|
3622
|
+
memory_forget: {
|
|
3623
|
+
description: 'Remove a fact or preference from permanent memory. Use when the user says "forget X", "remove X from memory", "delete X from memory". Pass { fact: "keyword to match" }.',
|
|
3624
|
+
tier: 1, category: ['memory'],
|
|
3625
|
+
parallel: 'never',
|
|
3626
|
+
retry: false, // write operation — don't double-delete on retry
|
|
3627
|
+
mcp: 'excluded',
|
|
3628
|
+
},
|
|
3629
|
+
search: {
|
|
3630
|
+
description: 'Search workspace memory, session context, and file system for relevant stored information',
|
|
3631
|
+
tier: 1, category: ['memory', 'introspection'],
|
|
3632
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3633
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3634
|
+
},
|
|
3635
|
+
cronjob: {
|
|
3636
|
+
description: 'Schedule a recurring task using cron-style timing (alias for schedule_reminder with recurring param)',
|
|
3637
|
+
tier: 1, category: ['system', 'core'],
|
|
3638
|
+
parallel: 'never', // agentLoop.ts:1957 — not in PARALLEL_SAFE
|
|
3639
|
+
mcp: 'excluded', // api/mcp.ts — not in SAFE_TOOLS or DESTRUCTIVE_TOOLS
|
|
3640
|
+
},
|
|
3641
|
+
};
|
|
3642
|
+
// ── v3.19 Phase 1, Commit 2: deriver functions ───────────────────────────────
|
|
3643
|
+
// Inspired by Hermes run_agent.py:113,159. Each deriver caches its result and
|
|
3644
|
+
// recomputes only when _generation changes (i.e. when a new external tool is
|
|
3645
|
+
// registered via registerExternalTool). Callers should use these instead of
|
|
3646
|
+
// reading TOOL_REGISTRY directly — Commits 4-6 will swap every hand-maintained
|
|
3647
|
+
// list to call the appropriate deriver.
|
|
3648
|
+
/** Expose the current generation for consumers that manage their own caches. */
|
|
3649
|
+
function bumpGeneration() { _generation++; }
|
|
3650
|
+
function getGeneration() { return _generation; }
|
|
3651
|
+
/** Build a zero-argument memoiser that recomputes whenever _generation changes. */
|
|
3652
|
+
function makeCache(build) {
|
|
3653
|
+
let cached;
|
|
3654
|
+
let cachedGen = -1;
|
|
3655
|
+
return () => {
|
|
3656
|
+
if (cachedGen !== _generation) {
|
|
3657
|
+
cached = build();
|
|
3658
|
+
cachedGen = _generation;
|
|
3659
|
+
}
|
|
3660
|
+
return cached;
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
// ── 1. Names ──────────────────────────────────────────────────────────────────
|
|
3664
|
+
/** All core tool names (TOOL_REGISTRY keys only, excludes slash mirrors).
|
|
3665
|
+
* Replaces TOOL_NAMES_ONLY (toolRegistry.ts). */
|
|
3666
|
+
exports.registryNames = makeCache(() => Object.keys(exports.TOOL_REGISTRY));
|
|
3667
|
+
// ── 2. Descriptions ───────────────────────────────────────────────────────────
|
|
3668
|
+
/** Map of name → description string. Falls back to ''.
|
|
3669
|
+
* Replaces TOOL_DESCRIPTIONS (toolRegistry.ts). */
|
|
3670
|
+
exports.registryDescriptions = makeCache(() => Object.fromEntries(Object.entries(exports.TOOL_REGISTRY).map(([n, m]) => [n, m.description ?? ''])));
|
|
3671
|
+
// ── 3. Tiers ──────────────────────────────────────────────────────────────────
|
|
3672
|
+
/** Map of name → ToolTier. Falls back to tier 1.
|
|
3673
|
+
* Replaces TOOL_TIERS (toolRegistry.ts). */
|
|
3674
|
+
exports.registryTiers = makeCache(() => Object.fromEntries(Object.entries(exports.TOOL_REGISTRY).map(([n, m]) => [n, m.tier ?? 1])));
|
|
3675
|
+
// ── 4. Categories ─────────────────────────────────────────────────────────────
|
|
3676
|
+
/** Map of name → ToolCategory[]. Falls back to ['core'].
|
|
3677
|
+
* Replaces TOOL_CATEGORIES (toolRegistry.ts). */
|
|
3678
|
+
exports.registryCategories = makeCache(() => Object.fromEntries(Object.entries(exports.TOOL_REGISTRY).map(([n, m]) => [n, m.category ?? ['core']])));
|
|
3679
|
+
// ── 5. Timeouts ───────────────────────────────────────────────────────────────
|
|
3680
|
+
/** Map of name → timeout in ms. Falls back to 15 000 ms.
|
|
3681
|
+
* Replaces TOOL_TIMEOUTS (toolRegistry.ts). */
|
|
3682
|
+
exports.registryTimeouts = makeCache(() => Object.fromEntries(Object.entries(exports.TOOL_REGISTRY).map(([n, m]) => [n, m.timeoutMs ?? 15000])));
|
|
3683
|
+
// ── 6. Allowed / valid tools ──────────────────────────────────────────────────
|
|
3684
|
+
/** Complete allowed-tool list: TOOL_REGISTRY keys + registered external tools.
|
|
3685
|
+
* Replaces ALLOWED_TOOLS (agentLoop.ts:808) and VALID_TOOLS (agentLoop.ts:1521). */
|
|
3686
|
+
exports.registryAllowedTools = makeCache(() => [
|
|
3687
|
+
...Object.keys(exports.TOOL_REGISTRY),
|
|
3688
|
+
...Object.keys(externalTools),
|
|
3689
|
+
]);
|
|
3690
|
+
// ── 6b. Valid tools ───────────────────────────────────────────────────────────
|
|
3691
|
+
/** Valid-tool list for agent-loop routing: same data as registryAllowedTools,
|
|
3692
|
+
* kept as a separate deriver for independent traceability per call site.
|
|
3693
|
+
* Replaces VALID_TOOLS (agentLoop.ts:1521). */
|
|
3694
|
+
exports.registryValidTools = makeCache(() => [
|
|
3695
|
+
...Object.keys(exports.TOOL_REGISTRY),
|
|
3696
|
+
...Object.keys(externalTools),
|
|
3697
|
+
]);
|
|
3698
|
+
// ── 7. No-retry set ───────────────────────────────────────────────────────────
|
|
3699
|
+
/** Set of tools that must NOT be retried on failure (retry === false).
|
|
3700
|
+
* Replaces NO_RETRY_TOOLS (agentLoop.ts:1881). */
|
|
3701
|
+
exports.registryNoRetrySet = makeCache(() => new Set(Object.entries(exports.TOOL_REGISTRY)
|
|
3702
|
+
.filter(([, m]) => m.retry === false)
|
|
3703
|
+
.map(([n]) => n)));
|
|
3704
|
+
// ── 8. Parallel-safe set ──────────────────────────────────────────────────────
|
|
3705
|
+
/** Set of tools safe to execute in parallel (parallel === 'safe').
|
|
3706
|
+
* Replaces PARALLEL_SAFE (agentLoop.ts:1957). */
|
|
3707
|
+
exports.registryParallelSafeSet = makeCache(() => new Set(Object.entries(exports.TOOL_REGISTRY)
|
|
3708
|
+
.filter(([, m]) => m.parallel === 'safe')
|
|
3709
|
+
.map(([n]) => n)));
|
|
3710
|
+
// ── 9. Sequential-only set ────────────────────────────────────────────────────
|
|
3711
|
+
/** Set of tools that must always run sequentially (parallel === 'sequential').
|
|
3712
|
+
* Replaces SEQUENTIAL_ONLY (agentLoop.ts:1965). */
|
|
3713
|
+
exports.registrySequentialOnlySet = makeCache(() => new Set(Object.entries(exports.TOOL_REGISTRY)
|
|
3714
|
+
.filter(([, m]) => m.parallel === 'sequential')
|
|
3715
|
+
.map(([n]) => n)));
|
|
3716
|
+
// ── 10. MCP safe list ─────────────────────────────────────────────────────────
|
|
3717
|
+
/** Tools safe to expose via MCP (mcp === 'safe').
|
|
3718
|
+
* Replaces SAFE_TOOLS (api/mcp.ts:25). */
|
|
3719
|
+
exports.registryMcpSafeList = makeCache(() => Object.entries(exports.TOOL_REGISTRY)
|
|
3720
|
+
.filter(([, m]) => m.mcp === 'safe')
|
|
3721
|
+
.map(([n]) => n));
|
|
3722
|
+
// ── 11. MCP destructive list ──────────────────────────────────────────────────
|
|
3723
|
+
/** Tools exposed via MCP but flagged destructive (mcp === 'destructive').
|
|
3724
|
+
* Replaces DESTRUCTIVE_TOOLS (api/mcp.ts:44). */
|
|
3725
|
+
exports.registryMcpDestructiveList = makeCache(() => Object.entries(exports.TOOL_REGISTRY)
|
|
3726
|
+
.filter(([, m]) => m.mcp === 'destructive')
|
|
3727
|
+
.map(([n]) => n));
|
|
3728
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2960
3729
|
function detectToolCategories(message) {
|
|
2961
3730
|
const categories = new Set(['core']);
|
|
2962
3731
|
const msg = message.toLowerCase();
|
|
@@ -2972,7 +3741,9 @@ function detectToolCategories(message) {
|
|
|
2972
3741
|
categories.add('screen');
|
|
2973
3742
|
if (/stock|nifty|market|price|nse|bse|sensex|reliance|trading|shares|equity|briefing|weather|natural|earthquake/i.test(msg))
|
|
2974
3743
|
categories.add('data');
|
|
2975
|
-
if (/
|
|
3744
|
+
if (/email|inbox|mail|gmail|unread|read_email|send_email|calendar|meetings|events/i.test(msg))
|
|
3745
|
+
categories.add('data');
|
|
3746
|
+
if (/notify|notification|remind|alert|system info|cpu|ram|disk|hardware|clipboard|launch|close app|now.?playing|what.*playing|what.*song|what.*music|is.*playing|music.*paused|current.*track/i.test(msg))
|
|
2976
3747
|
categories.add('system');
|
|
2977
3748
|
if (/voice|speak|say aloud|listen|record audio|tts|text.to.speech|transcribe|speech.to.text|clone.*voice|voice.*design|voice.*clone|design.*voice/i.test(msg))
|
|
2978
3749
|
categories.add('voice');
|