micro-models-agent 0.28.17 → 0.29.0
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/dist/cli/commands.js +3 -116
- package/dist/cli/main.js +8 -35
- package/dist/cli/repl.js +611 -110
- package/dist/cli/setup.js +12 -32
- package/dist/config/config.js +30 -46
- package/dist/config/defaults.js +1 -10
- package/dist/config/security.js +8 -15
- package/dist/core/agent-moe.js +12 -24
- package/dist/core/agent.js +47 -281
- package/dist/core/bootstrap.js +36 -52
- package/dist/core/session-logger.js +2 -35
- package/dist/i18n/en.json +15 -79
- package/dist/i18n/index.js +9 -12
- package/dist/i18n/ru.json +15 -79
- package/dist/index.js +13 -13
- package/dist/llm/openai-compat.js +10 -39
- package/dist/logger/app-logger.js +16 -83
- package/dist/main.js +624 -243
- package/dist/modules/browser/session.js +60 -108
- package/dist/modules/context/history.js +15 -0
- package/dist/modules/context/manager.js +10 -119
- package/dist/modules/execution/auditor.js +39 -33
- package/dist/modules/execution/index.js +6 -8
- package/dist/modules/execution/module.js +32 -474
- package/dist/modules/execution/moe-executor.js +40 -97
- package/dist/modules/execution/planner.js +13 -63
- package/dist/modules/execution/stuck-detector.js +39 -252
- package/dist/modules/execution/tracker.js +7 -21
- package/dist/modules/execution/verifier.js +17 -46
- package/dist/modules/hallucination/confidence.js +2 -7
- package/dist/modules/hallucination/consistency.js +42 -8
- package/dist/modules/hallucination/detector.js +21 -26
- package/dist/modules/hallucination/factual.js +150 -170
- package/dist/modules/hallucination/index.js +4 -5
- package/dist/modules/index.js +5 -5
- package/dist/modules/mcp/client.js +2 -8
- package/dist/modules/memory/store.js +0 -4
- package/dist/modules/plugins/builtin/lint-on-write.js +38 -143
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +2 -1
- package/dist/modules/processes/registry.js +35 -125
- package/dist/modules/processes/runner.js +110 -9
- package/dist/modules/security/audit-log.js +10 -30
- package/dist/modules/security/command-validator.js +16 -42
- package/dist/modules/security/content-scanner.js +8 -9
- package/dist/modules/security/network-validator.js +2 -2
- package/dist/modules/security/path-validator.js +10 -64
- package/dist/modules/security/security-policies.js +67 -221
- package/dist/modules/security/session-encryption.js +25 -42
- package/dist/modules/session/manager.js +10 -15
- package/dist/modules/session/store.js +8 -62
- package/dist/modules/skills/index.js +3 -2
- package/dist/modules/skills/matcher.js +27 -0
- package/dist/modules/skills/module.js +23 -10
- package/dist/tools/bash.js +90 -287
- package/dist/tools/create-dir.js +1 -0
- package/dist/tools/delete-file.js +1 -0
- package/dist/tools/edit-file.js +8 -10
- package/dist/tools/executor.js +7 -57
- package/dist/tools/grep-tool.js +29 -51
- package/dist/tools/index.js +40 -55
- package/dist/tools/load-skill.js +18 -14
- package/dist/tools/move-file.js +2 -3
- package/dist/tools/pipeline-run.js +1 -1
- package/dist/tools/read-file.js +5 -15
- package/dist/tools/search-history.js +22 -42
- package/dist/tools/subagent.js +12 -21
- package/dist/tools/web-browse.js +25 -54
- package/dist/tools/web-fetch.js +34 -60
- package/dist/tools/web-search.js +20 -39
- package/dist/tools/write-file.js +10 -13
- package/dist/ui/diff.js +16 -9
- package/dist/ui/renderer.js +6 -69
- package/package.json +1 -1
- package/dist/cli/repl-commands.js +0 -633
- package/dist/core/workspace.js +0 -76
- package/dist/logger/file-log.js +0 -151
- package/dist/modules/certification/cli.js +0 -176
- package/dist/modules/certification/fact-checker.js +0 -84
- package/dist/modules/certification/loader.js +0 -111
- package/dist/modules/certification/manifest.js +0 -50
- package/dist/modules/certification/runner.js +0 -162
- package/dist/modules/certification/scenarios.js +0 -124
- package/dist/modules/certification/types.js +0 -1
- package/dist/modules/execution/plan-coverage.js +0 -68
- package/dist/modules/execution/plan-persister.js +0 -46
- package/dist/modules/execution/plan-store.js +0 -159
- package/dist/modules/hallucination/js-identifiers.js +0 -72
- package/dist/modules/hallucination/llm-judge.js +0 -103
- package/dist/modules/lsp/client.js +0 -235
- package/dist/modules/lsp/config.js +0 -81
- package/dist/modules/lsp/index.js +0 -3
- package/dist/modules/lsp/module.js +0 -68
- package/dist/modules/lsp/types.js +0 -1
package/dist/tools/create-dir.js
CHANGED
|
@@ -50,6 +50,7 @@ export const createDirTool = {
|
|
|
50
50
|
ctx.fileOperationsCount = currentCount + 1;
|
|
51
51
|
// Log directory creation
|
|
52
52
|
logFileWrite(ctx.sessionId, path, true, "Directory created");
|
|
53
|
+
ctx.trackCreatedPath?.(path);
|
|
53
54
|
return { success: true, output: t("file.created", { path }) };
|
|
54
55
|
},
|
|
55
56
|
};
|
|
@@ -57,6 +57,7 @@ export const deleteFileTool = {
|
|
|
57
57
|
ctx.fileOperationsCount = currentCount + 1;
|
|
58
58
|
// Log successful file deletion
|
|
59
59
|
logFileDelete(ctx.sessionId, path, true);
|
|
60
|
+
ctx.trackDeletedPath?.(path);
|
|
60
61
|
return { success: true, output: t("file.deleted", { path }), diff };
|
|
61
62
|
},
|
|
62
63
|
};
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -57,16 +57,14 @@ export const editFileTool = {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
const updated = content.replace(oldStr, newStr);
|
|
60
|
-
// Check new content for dangerous patterns
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
}
|
|
60
|
+
// Check new content for dangerous patterns
|
|
61
|
+
const scanResult = scanContent(updated, path, ctx.config.security?.contentScan);
|
|
62
|
+
if (!scanResult.allowed) {
|
|
63
|
+
logSecurityBlock(ctx.sessionId, "file_write", scanResult.reason || "Content contains dangerous patterns", path);
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
output: `[SECURITY BLOCKED] ${scanResult.reason}`,
|
|
67
|
+
};
|
|
70
68
|
}
|
|
71
69
|
writeFileSync(resolved, updated, "utf-8");
|
|
72
70
|
const diff = generateDiff(content, updated);
|
package/dist/tools/executor.js
CHANGED
|
@@ -10,32 +10,7 @@ export class ToolExecutor {
|
|
|
10
10
|
this.ctx = ctx;
|
|
11
11
|
this.pluginManager = pluginManager;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
* Execute a tool by name with plain arguments (no call id, no timeout
|
|
15
|
-
* wiring). Used internally — e.g. bash redirects a mistaken tool-call-as-
|
|
16
|
-
* command into the real tool. Runs through the same registry and plugin
|
|
17
|
-
* hooks as a normal call.
|
|
18
|
-
*/
|
|
19
|
-
async executeByName(name, args, ctx) {
|
|
20
|
-
const prevCtx = this.ctx;
|
|
21
|
-
if (ctx)
|
|
22
|
-
this.ctx = ctx;
|
|
23
|
-
try {
|
|
24
|
-
return await this.execute({
|
|
25
|
-
id: `redirect_${Date.now()}`,
|
|
26
|
-
name,
|
|
27
|
-
arguments: args,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
finally {
|
|
31
|
-
this.ctx = prevCtx;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/** Whether a tool with this name is registered (used before redirects). */
|
|
35
|
-
hasTool(name) {
|
|
36
|
-
return this.registry.has(name);
|
|
37
|
-
}
|
|
38
|
-
async execute(call, signal) {
|
|
13
|
+
async execute(call) {
|
|
39
14
|
const tool = this.registry.get(call.name);
|
|
40
15
|
if (!tool) {
|
|
41
16
|
return {
|
|
@@ -48,16 +23,12 @@ export class ToolExecutor {
|
|
|
48
23
|
if (plugin.onBeforeTool) {
|
|
49
24
|
try {
|
|
50
25
|
const proceed = await plugin.onBeforeTool(this.ctx, call);
|
|
51
|
-
if (proceed
|
|
52
|
-
const pluginName = plugin.name ||
|
|
53
|
-
plugin.constructor?.name ||
|
|
54
|
-
"unknown";
|
|
55
|
-
const reason = typeof proceed === "string" ? proceed : undefined;
|
|
26
|
+
if (!proceed) {
|
|
56
27
|
return {
|
|
57
28
|
success: false,
|
|
58
|
-
output:
|
|
59
|
-
|
|
60
|
-
|
|
29
|
+
output: t("tool.blocked", {
|
|
30
|
+
plugin: plugin.constructor?.name || "unknown",
|
|
31
|
+
}),
|
|
61
32
|
toolCallId: call.id,
|
|
62
33
|
};
|
|
63
34
|
}
|
|
@@ -85,26 +56,10 @@ export class ToolExecutor {
|
|
|
85
56
|
})));
|
|
86
57
|
}, TOOL_EXECUTION_TIMEOUT_MS);
|
|
87
58
|
});
|
|
88
|
-
|
|
59
|
+
result = await Promise.race([
|
|
89
60
|
tool.handler(this.ctx, call.arguments),
|
|
90
61
|
timeoutPromise,
|
|
91
|
-
];
|
|
92
|
-
if (signal) {
|
|
93
|
-
const abortPromise = new Promise((_, reject) => {
|
|
94
|
-
const onAbort = () => {
|
|
95
|
-
killByCallId(call.id);
|
|
96
|
-
reject(new Error(t("tool.aborted", { name: call.name })));
|
|
97
|
-
};
|
|
98
|
-
if (signal.aborted) {
|
|
99
|
-
onAbort();
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
racePromises.push(abortPromise);
|
|
106
|
-
}
|
|
107
|
-
result = await Promise.race(racePromises);
|
|
62
|
+
]);
|
|
108
63
|
}
|
|
109
64
|
result.toolCallId = call.id;
|
|
110
65
|
}
|
|
@@ -125,8 +80,6 @@ export class ToolExecutor {
|
|
|
125
80
|
}
|
|
126
81
|
}
|
|
127
82
|
}
|
|
128
|
-
this.ctx.logger.logToolCall(call.name, JSON.stringify(call.arguments)?.slice(0, 200) ?? "", result.success ? "OK" : `FAIL: ${result.output?.slice(0, 200)}`);
|
|
129
|
-
this.ctx.logger.logToolOutput(call.name, result.output ?? "", result.success ? 0 : 1);
|
|
130
83
|
this.ctx.logger.debug(`Tool ${call.name}: ${result.success ? "OK" : "FAIL"}`);
|
|
131
84
|
return result;
|
|
132
85
|
}
|
|
@@ -139,7 +92,4 @@ export class ToolExecutor {
|
|
|
139
92
|
setScope(scope) {
|
|
140
93
|
this.ctx.scope = scope;
|
|
141
94
|
}
|
|
142
|
-
updateProvider(provider) {
|
|
143
|
-
this.ctx.llmProvider = provider;
|
|
144
|
-
}
|
|
145
95
|
}
|
package/dist/tools/grep-tool.js
CHANGED
|
@@ -1,85 +1,63 @@
|
|
|
1
|
-
import { execFileSync } from
|
|
2
|
-
import { resolve } from
|
|
3
|
-
import { t } from
|
|
4
|
-
import { logBashCommand } from
|
|
5
|
-
import { MAX_PREVIEW_LINES } from
|
|
1
|
+
import { execFileSync, execSync } from 'child_process';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { t } from '../i18n/index';
|
|
4
|
+
import { logBashCommand } from '../modules/security/audit-log';
|
|
5
|
+
import { MAX_PREVIEW_LINES } from './preview';
|
|
6
6
|
function truncateLines(output) {
|
|
7
|
-
const lines = output.split(
|
|
7
|
+
const lines = output.split('\n');
|
|
8
8
|
if (lines.length <= MAX_PREVIEW_LINES)
|
|
9
9
|
return output;
|
|
10
|
-
return
|
|
11
|
-
`\n... (${lines.length - MAX_PREVIEW_LINES} more lines)`);
|
|
10
|
+
return lines.slice(0, MAX_PREVIEW_LINES).join('\n') + `\n... (${lines.length - MAX_PREVIEW_LINES} more lines)`;
|
|
12
11
|
}
|
|
13
12
|
export const grepTool = {
|
|
14
|
-
name:
|
|
13
|
+
name: 'grep',
|
|
15
14
|
description: `Search file contents using a regular expression. Shows up to ${MAX_PREVIEW_LINES} matching lines by default. Uses ripgrep (rg) if available, otherwise falls back to grep -r.`,
|
|
16
|
-
tags: [
|
|
15
|
+
tags: ['file', 'code', 'research'],
|
|
17
16
|
parameters: {
|
|
18
|
-
type:
|
|
17
|
+
type: 'object',
|
|
19
18
|
properties: {
|
|
20
|
-
pattern: { type:
|
|
21
|
-
include: {
|
|
22
|
-
|
|
23
|
-
description: "File pattern to filter (e.g. *.ts)",
|
|
24
|
-
},
|
|
25
|
-
path: {
|
|
26
|
-
type: "string",
|
|
27
|
-
description: "Directory to search (defaults to baseDir)",
|
|
28
|
-
},
|
|
19
|
+
pattern: { type: 'string', description: 'Regex pattern to search' },
|
|
20
|
+
include: { type: 'string', description: 'File pattern to filter (e.g. *.ts)' },
|
|
21
|
+
path: { type: 'string', description: 'Directory to search (defaults to baseDir)' },
|
|
29
22
|
},
|
|
30
|
-
required: [
|
|
23
|
+
required: ['pattern'],
|
|
31
24
|
},
|
|
32
25
|
handler: async (ctx, args) => {
|
|
33
26
|
const pattern = String(args.pattern);
|
|
34
|
-
const searchPath = args.path
|
|
35
|
-
? resolve(ctx.baseDir, String(args.path))
|
|
36
|
-
: ctx.baseDir;
|
|
27
|
+
const searchPath = args.path ? resolve(ctx.baseDir, String(args.path)) : ctx.baseDir;
|
|
37
28
|
// Build rg arguments as an array to avoid shell interpretation of regex
|
|
38
29
|
// metacharacters like |, (, ) — these are regex patterns, not shell operators.
|
|
39
|
-
const rgArgs = [
|
|
30
|
+
const rgArgs = ['-n', '--with-filename', pattern, searchPath];
|
|
40
31
|
if (args.include) {
|
|
41
|
-
rgArgs.push(
|
|
32
|
+
rgArgs.push('-g', String(args.include));
|
|
42
33
|
}
|
|
43
34
|
// Log the search (sanitized) for audit purposes
|
|
44
|
-
logBashCommand(ctx.sessionId, `rg ${rgArgs.join(
|
|
35
|
+
logBashCommand(ctx.sessionId, `rg ${rgArgs.join(' ')}`, false, 'grep-tool');
|
|
45
36
|
try {
|
|
46
|
-
const output = execFileSync(
|
|
47
|
-
encoding:
|
|
37
|
+
const output = execFileSync('rg', rgArgs, {
|
|
38
|
+
encoding: 'utf-8',
|
|
48
39
|
maxBuffer: 1024 * 1024,
|
|
49
40
|
cwd: ctx.baseDir,
|
|
50
41
|
});
|
|
51
|
-
return {
|
|
52
|
-
success: true,
|
|
53
|
-
output: truncateLines(output || t("file.no_matches")),
|
|
54
|
-
};
|
|
42
|
+
return { success: true, output: truncateLines(output || t('file.no_matches')) };
|
|
55
43
|
}
|
|
56
44
|
catch (e) {
|
|
57
45
|
if (e.status === 1)
|
|
58
|
-
return { success: true, output: t(
|
|
59
|
-
// Fall back to plain grep -r when rg is not available or fails
|
|
60
|
-
// Using execFileSync with array args avoids shell injection.
|
|
46
|
+
return { success: true, output: t('file.no_matches') };
|
|
47
|
+
// Fall back to plain grep -r when rg is not available or fails
|
|
61
48
|
try {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
const output = execFileSync("grep", grepArgs, {
|
|
67
|
-
encoding: "utf-8",
|
|
49
|
+
const cmd = `grep -rn "${pattern.replace(/"/g, '\\"')}" "${searchPath}"`;
|
|
50
|
+
const output = execSync(cmd, {
|
|
51
|
+
encoding: 'utf-8',
|
|
68
52
|
maxBuffer: 1024 * 1024,
|
|
69
53
|
cwd: ctx.baseDir,
|
|
70
54
|
});
|
|
71
|
-
return {
|
|
72
|
-
success: true,
|
|
73
|
-
output: truncateLines(output || t("file.no_matches")),
|
|
74
|
-
};
|
|
55
|
+
return { success: true, output: truncateLines(output || t('file.no_matches')) };
|
|
75
56
|
}
|
|
76
57
|
catch (e2) {
|
|
77
58
|
if (e2.status === 1)
|
|
78
|
-
return { success: true, output: t(
|
|
79
|
-
return {
|
|
80
|
-
success: false,
|
|
81
|
-
output: t("error.grep_failed", { message: e2.message }),
|
|
82
|
-
};
|
|
59
|
+
return { success: true, output: t('file.no_matches') };
|
|
60
|
+
return { success: false, output: t('error.grep_failed', { message: e2.message }) };
|
|
83
61
|
}
|
|
84
62
|
}
|
|
85
63
|
},
|
package/dist/tools/index.js
CHANGED
|
@@ -1,61 +1,46 @@
|
|
|
1
|
-
import { ToolRegistry } from
|
|
2
|
-
import { ToolExecutor } from
|
|
3
|
-
import { readFileTool } from
|
|
4
|
-
import { writeFileTool } from
|
|
5
|
-
import { editFileTool } from
|
|
6
|
-
import { globTool } from
|
|
7
|
-
import { grepTool } from
|
|
8
|
-
import { listDirTool } from
|
|
9
|
-
import { createDirTool } from
|
|
10
|
-
import { deleteFileTool } from
|
|
11
|
-
import { moveFileTool } from
|
|
12
|
-
import { fileInfoTool } from
|
|
13
|
-
import { bashTool } from
|
|
14
|
-
import { processListTool } from
|
|
15
|
-
import { processLogTool } from
|
|
16
|
-
import { processKillTool } from
|
|
17
|
-
import { subagentTool } from
|
|
18
|
-
import { webSearchTool } from
|
|
19
|
-
import { webFetchTool } from
|
|
20
|
-
import { webBrowseTool } from
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import { createBrowserTool } from "./browser";
|
|
32
|
-
import { attachImageTool } from "./attach-image";
|
|
1
|
+
import { ToolRegistry } from './registry';
|
|
2
|
+
import { ToolExecutor } from './executor';
|
|
3
|
+
import { readFileTool } from './read-file';
|
|
4
|
+
import { writeFileTool } from './write-file';
|
|
5
|
+
import { editFileTool } from './edit-file';
|
|
6
|
+
import { globTool } from './glob-tool';
|
|
7
|
+
import { grepTool } from './grep-tool';
|
|
8
|
+
import { listDirTool } from './list-dir';
|
|
9
|
+
import { createDirTool } from './create-dir';
|
|
10
|
+
import { deleteFileTool } from './delete-file';
|
|
11
|
+
import { moveFileTool } from './move-file';
|
|
12
|
+
import { fileInfoTool } from './file-info';
|
|
13
|
+
import { bashTool } from './bash';
|
|
14
|
+
import { processListTool } from './process-list';
|
|
15
|
+
import { processLogTool } from './process-log';
|
|
16
|
+
import { processKillTool } from './process-kill';
|
|
17
|
+
import { subagentTool } from './subagent';
|
|
18
|
+
import { webSearchTool } from './web-search';
|
|
19
|
+
import { webFetchTool } from './web-fetch';
|
|
20
|
+
import { webBrowseTool } from './web-browse';
|
|
21
|
+
import { questionTool } from './question';
|
|
22
|
+
import { approveTool } from './approve';
|
|
23
|
+
import { createLoadSkillTool } from './load-skill';
|
|
24
|
+
import { pipelineRunTool } from './pipeline-run';
|
|
25
|
+
import { mcpCallTool } from './mcp-call';
|
|
26
|
+
import { searchHistoryTool } from './search-history';
|
|
27
|
+
import { rememberTool } from './remember';
|
|
28
|
+
import { recallTool } from './recall';
|
|
29
|
+
import { createBrowserTool } from './browser';
|
|
30
|
+
import { attachImageTool } from './attach-image';
|
|
33
31
|
export { ToolRegistry, ToolExecutor };
|
|
34
|
-
export { filterToolsByTags } from
|
|
35
|
-
export { readFileTool, writeFileTool, editFileTool, globTool, grepTool, listDirTool, createDirTool, deleteFileTool, moveFileTool, fileInfoTool, bashTool, subagentTool, processListTool, processLogTool, processKillTool, webSearchTool, webFetchTool, webBrowseTool, createLoadSkillTool, pipelineRunTool, mcpCallTool, searchHistoryTool, rememberTool, recallTool, createBrowserTool, attachImageTool, };
|
|
32
|
+
export { filterToolsByTags } from './filter-tools';
|
|
33
|
+
export { readFileTool, writeFileTool, editFileTool, globTool, grepTool, listDirTool, createDirTool, deleteFileTool, moveFileTool, fileInfoTool, bashTool, subagentTool, processListTool, processLogTool, processKillTool, webSearchTool, webFetchTool, webBrowseTool, questionTool, approveTool, createLoadSkillTool, pipelineRunTool, mcpCallTool, searchHistoryTool, rememberTool, recallTool, createBrowserTool, attachImageTool, };
|
|
36
34
|
export function registerAllTools(registry, skillsModule) {
|
|
37
35
|
const tools = [
|
|
38
|
-
readFileTool,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
moveFileTool,
|
|
47
|
-
fileInfoTool,
|
|
48
|
-
bashTool,
|
|
49
|
-
subagentTool,
|
|
50
|
-
processListTool,
|
|
51
|
-
processLogTool,
|
|
52
|
-
processKillTool,
|
|
53
|
-
webSearchTool,
|
|
54
|
-
webFetchTool,
|
|
55
|
-
webBrowseTool,
|
|
56
|
-
pipelineRunTool,
|
|
57
|
-
mcpCallTool,
|
|
58
|
-
searchHistoryTool,
|
|
36
|
+
readFileTool, writeFileTool, editFileTool,
|
|
37
|
+
globTool, grepTool, listDirTool, createDirTool,
|
|
38
|
+
deleteFileTool, moveFileTool, fileInfoTool,
|
|
39
|
+
bashTool, subagentTool,
|
|
40
|
+
processListTool, processLogTool, processKillTool,
|
|
41
|
+
webSearchTool, webFetchTool, webBrowseTool,
|
|
42
|
+
questionTool, approveTool,
|
|
43
|
+
pipelineRunTool, mcpCallTool, searchHistoryTool,
|
|
59
44
|
attachImageTool,
|
|
60
45
|
];
|
|
61
46
|
if (skillsModule) {
|
package/dist/tools/load-skill.js
CHANGED
|
@@ -3,23 +3,33 @@ export function createLoadSkillTool(skillsModule) {
|
|
|
3
3
|
return {
|
|
4
4
|
name: "load_skill",
|
|
5
5
|
tags: ["core"],
|
|
6
|
-
description: "Load a skill by name. Skills provide specialized instructions and workflows.
|
|
6
|
+
description: "Load a skill by name or task description. Skills provide specialized instructions and workflows. Only use when the task clearly requires domain-specific knowledge. If loading fails because the skill is too large, continue the task without it.",
|
|
7
7
|
parameters: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
|
-
name: {
|
|
10
|
+
name: { type: "string", description: "Skill name to load directly" },
|
|
11
|
+
task: {
|
|
11
12
|
type: "string",
|
|
12
|
-
description: "
|
|
13
|
+
description: "Task description to find matching skill",
|
|
13
14
|
},
|
|
14
15
|
},
|
|
15
|
-
required: ["name"],
|
|
16
16
|
},
|
|
17
17
|
handler: async (_ctx, args) => {
|
|
18
18
|
const name = args.name ? String(args.name) : undefined;
|
|
19
|
-
|
|
19
|
+
const task = args.task ? String(args.task) : undefined;
|
|
20
|
+
if (!name && !task) {
|
|
21
|
+
return { success: false, output: t("tool.name_or_task") };
|
|
22
|
+
}
|
|
23
|
+
let result;
|
|
24
|
+
if (name) {
|
|
25
|
+
result = skillsModule.loadByName(name);
|
|
26
|
+
}
|
|
27
|
+
else if (task) {
|
|
28
|
+
result = skillsModule.loadByMatch(task);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
20
31
|
return { success: false, output: t("tool.invalid_params") };
|
|
21
32
|
}
|
|
22
|
-
const result = skillsModule.loadByName(name);
|
|
23
33
|
if (!result.success) {
|
|
24
34
|
const available = skillsModule.getAvailable();
|
|
25
35
|
const hint = available.length > 0
|
|
@@ -27,17 +37,11 @@ export function createLoadSkillTool(skillsModule) {
|
|
|
27
37
|
: "";
|
|
28
38
|
return { success: false, output: result.message + hint };
|
|
29
39
|
}
|
|
40
|
+
const skill = result.skill;
|
|
30
41
|
const budget = skillsModule.getBudget();
|
|
31
|
-
const tokens = result.skill
|
|
32
|
-
? Math.ceil(result.skill.content.length / 4)
|
|
33
|
-
: 0;
|
|
34
42
|
return {
|
|
35
43
|
success: true,
|
|
36
|
-
output: t("tool.skill_budget", {
|
|
37
|
-
name,
|
|
38
|
-
tokens,
|
|
39
|
-
remaining: budget.remaining,
|
|
40
|
-
}),
|
|
44
|
+
output: `${result.message}\n\n--- Skill Content ---\n${skill.content}\n\n${t("tool.skill_budget", { remaining: budget.remaining })}`,
|
|
41
45
|
};
|
|
42
46
|
},
|
|
43
47
|
};
|
package/dist/tools/move-file.js
CHANGED
|
@@ -5,7 +5,6 @@ import { isPathWritable } from "../modules/security/path-validator";
|
|
|
5
5
|
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
6
6
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
7
7
|
import { generateMoveDiff } from "../ui/diff";
|
|
8
|
-
import { safeResolvePath } from "./path-utils";
|
|
9
8
|
export const moveFileTool = {
|
|
10
9
|
name: "move_file",
|
|
11
10
|
description: "Move or rename a file or directory.",
|
|
@@ -22,8 +21,8 @@ export const moveFileTool = {
|
|
|
22
21
|
const fromPath = String(args.from);
|
|
23
22
|
const toPath = String(args.to);
|
|
24
23
|
const baseDir = resolve(ctx.baseDir);
|
|
25
|
-
const fromResolved =
|
|
26
|
-
const toResolved =
|
|
24
|
+
const fromResolved = resolve(baseDir, normalize(fromPath));
|
|
25
|
+
const toResolved = resolve(baseDir, normalize(toPath));
|
|
27
26
|
// Get session-specific security config
|
|
28
27
|
const securityConfig = ctx.sessionContext
|
|
29
28
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
@@ -30,7 +30,7 @@ async function runStep(ctx, step, params, outputs) {
|
|
|
30
30
|
toolExecutor: ctx.toolExecutor,
|
|
31
31
|
pluginManager: subPluginManager,
|
|
32
32
|
contextManager: subContextManager,
|
|
33
|
-
hallucinationDetector: new HallucinationDetector(
|
|
33
|
+
hallucinationDetector: new HallucinationDetector(),
|
|
34
34
|
logger: ctx.logger,
|
|
35
35
|
baseDir: ctx.baseDir,
|
|
36
36
|
scope: ctx.scope,
|
package/dist/tools/read-file.js
CHANGED
|
@@ -42,17 +42,12 @@ export const readFileTool = {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
if (!existsSync(resolved)) {
|
|
45
|
-
|
|
46
|
-
// "not found" alone is confusing (it cannot tell baseDir from cwd).
|
|
47
|
-
const output = resolved !== path
|
|
48
|
-
? t("file.notfound_resolved", {
|
|
49
|
-
path,
|
|
50
|
-
resolved,
|
|
51
|
-
})
|
|
52
|
-
: t("file.notfound", { path });
|
|
53
|
-
return { success: false, output };
|
|
45
|
+
return { success: false, output: t("file.notfound", { path }) };
|
|
54
46
|
}
|
|
47
|
+
ctx.trackReadPath?.(path);
|
|
55
48
|
const content = readFileSync(resolved, "utf-8");
|
|
49
|
+
// Track file paths mentioned in the document (e.g. structure.md, README)
|
|
50
|
+
ctx.trackDocumentContent?.(content);
|
|
56
51
|
const lines = content.split("\n");
|
|
57
52
|
const total = lines.length;
|
|
58
53
|
const offset = args.offset || 1;
|
|
@@ -73,11 +68,6 @@ export const readFileTool = {
|
|
|
73
68
|
next: String(end + 1),
|
|
74
69
|
})
|
|
75
70
|
: "";
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
success: true,
|
|
79
|
-
output: `${header}\n${selected}${truncated}`,
|
|
80
|
-
display,
|
|
81
|
-
};
|
|
71
|
+
return { success: true, output: `${header}\n${selected}${truncated}` };
|
|
82
72
|
},
|
|
83
73
|
};
|
|
@@ -1,56 +1,45 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { homedir } from
|
|
4
|
-
import { t } from
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { t } from '../i18n/index';
|
|
5
5
|
function searchFile(filePath, query, maxResults, results) {
|
|
6
6
|
if (!fs.existsSync(filePath))
|
|
7
7
|
return;
|
|
8
|
-
const content = fs.readFileSync(filePath,
|
|
9
|
-
for (const line of content.split(
|
|
8
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
9
|
+
for (const line of content.split('\n').filter(Boolean)) {
|
|
10
10
|
if (line.toLowerCase().includes(query)) {
|
|
11
11
|
try {
|
|
12
12
|
const entry = JSON.parse(line);
|
|
13
13
|
results.push(`[${filePath}] ${JSON.stringify(entry).slice(0, 200)}`);
|
|
14
14
|
}
|
|
15
|
-
catch {
|
|
16
|
-
/* skip */
|
|
17
|
-
}
|
|
15
|
+
catch { /* skip */ }
|
|
18
16
|
}
|
|
19
17
|
if (results.length >= maxResults)
|
|
20
18
|
break;
|
|
21
19
|
}
|
|
22
20
|
}
|
|
23
21
|
export const searchHistoryTool = {
|
|
24
|
-
name:
|
|
25
|
-
description:
|
|
26
|
-
tags: [
|
|
22
|
+
name: 'search_history',
|
|
23
|
+
description: 'Search through session history for past interactions across all sessions.',
|
|
24
|
+
tags: ['research'],
|
|
27
25
|
parameters: {
|
|
28
|
-
type:
|
|
26
|
+
type: 'object',
|
|
29
27
|
properties: {
|
|
30
|
-
query: { type:
|
|
31
|
-
maxResults: {
|
|
32
|
-
|
|
33
|
-
description: "Maximum results (default 5)",
|
|
34
|
-
},
|
|
35
|
-
sessionId: {
|
|
36
|
-
type: "string",
|
|
37
|
-
description: "Optional: limit search to a specific session",
|
|
38
|
-
},
|
|
28
|
+
query: { type: 'string', description: 'Search query' },
|
|
29
|
+
maxResults: { type: 'number', description: 'Maximum results (default 5)' },
|
|
30
|
+
sessionId: { type: 'string', description: 'Optional: limit search to a specific session' },
|
|
39
31
|
},
|
|
40
|
-
required: [
|
|
32
|
+
required: ['query'],
|
|
41
33
|
},
|
|
42
34
|
handler: async (_ctx, args) => {
|
|
43
|
-
const query = String(args.query ||
|
|
35
|
+
const query = String(args.query || '').toLowerCase();
|
|
44
36
|
const maxResults = Number(args.maxResults) || 5;
|
|
45
37
|
const sessionId = args.sessionId ? String(args.sessionId) : null;
|
|
46
|
-
const sessionDir = join(homedir(),
|
|
38
|
+
const sessionDir = join(homedir(), '.mma', 'sessions');
|
|
47
39
|
const results = [];
|
|
48
40
|
try {
|
|
49
41
|
if (!fs.existsSync(sessionDir)) {
|
|
50
|
-
return {
|
|
51
|
-
success: true,
|
|
52
|
-
output: t("tool.no_sessions_dir", { dir: sessionDir }),
|
|
53
|
-
};
|
|
42
|
+
return { success: true, output: t('tool.no_sessions_dir', { dir: sessionDir }) };
|
|
54
43
|
}
|
|
55
44
|
const entries = fs.readdirSync(sessionDir, { withFileTypes: true });
|
|
56
45
|
for (const entry of entries) {
|
|
@@ -58,27 +47,18 @@ export const searchHistoryTool = {
|
|
|
58
47
|
continue;
|
|
59
48
|
if (sessionId && entry.name !== sessionId)
|
|
60
49
|
continue;
|
|
61
|
-
const historyFile = join(sessionDir, entry.name,
|
|
50
|
+
const historyFile = join(sessionDir, entry.name, 'history.jsonl');
|
|
62
51
|
searchFile(historyFile, query, maxResults, results);
|
|
63
52
|
if (results.length >= maxResults)
|
|
64
53
|
break;
|
|
65
54
|
}
|
|
66
55
|
if (results.length === 0) {
|
|
67
|
-
return { success: true, output: t(
|
|
56
|
+
return { success: true, output: t('tool.no_history', { query }) };
|
|
68
57
|
}
|
|
69
|
-
return {
|
|
70
|
-
success: true,
|
|
71
|
-
output: t("tool.history_results", {
|
|
72
|
-
query,
|
|
73
|
-
results: results.join("\n"),
|
|
74
|
-
}),
|
|
75
|
-
};
|
|
58
|
+
return { success: true, output: t('tool.history_results', { query, results: results.join('\n') }) };
|
|
76
59
|
}
|
|
77
60
|
catch (err) {
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
output: t("tool.history_error", { error: String(err) }),
|
|
81
|
-
};
|
|
61
|
+
return { success: true, output: t('tool.history_error', { error: String(err) }) };
|
|
82
62
|
}
|
|
83
63
|
},
|
|
84
64
|
};
|
package/dist/tools/subagent.js
CHANGED
|
@@ -98,11 +98,7 @@ export const subagentTool = {
|
|
|
98
98
|
try {
|
|
99
99
|
const subContextManager = new ContextManager(ctx.config.contextWindow, ctx.config.contextBudget);
|
|
100
100
|
const subPluginManager = new PluginManager();
|
|
101
|
-
const hallucinationDetector = new HallucinationDetector(
|
|
102
|
-
// Save/restore the shared executor scope so the sub-agent doesn't
|
|
103
|
-
// permanently mutate the parent's scope.
|
|
104
|
-
const parentScope = ctx.scope;
|
|
105
|
-
ctx.toolExecutor.setScope(scope || { allowed_files: [], read_only_files: [] });
|
|
101
|
+
const hallucinationDetector = new HallucinationDetector();
|
|
106
102
|
const systemPrompt = {
|
|
107
103
|
content: `You are a sub-agent working on a specific task. ${context ? `Context: ${context}` : ""}`,
|
|
108
104
|
priority: "critical",
|
|
@@ -125,23 +121,18 @@ export const subagentTool = {
|
|
|
125
121
|
};
|
|
126
122
|
const subAgent = new Agent(subDeps);
|
|
127
123
|
const fullTask = context ? `${task}\n\nContext: ${context}` : task;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
output: `Sub-agent completed:\n${result.text}\n\nIterations: ${result.iterationCount}`,
|
|
135
|
-
}
|
|
136
|
-
: {
|
|
137
|
-
success: false,
|
|
138
|
-
output: `Sub-agent failed: ${result.error}\nPartial output: ${result.text}`,
|
|
139
|
-
};
|
|
124
|
+
const result = await subAgent.run(fullTask);
|
|
125
|
+
if (result.success) {
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
output: `Sub-agent completed:\n${result.text}\n\nIterations: ${result.iterationCount}`,
|
|
129
|
+
};
|
|
140
130
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
131
|
+
else {
|
|
132
|
+
return {
|
|
133
|
+
success: false,
|
|
134
|
+
output: `Sub-agent failed: ${result.error}\nPartial output: ${result.text}`,
|
|
135
|
+
};
|
|
145
136
|
}
|
|
146
137
|
}
|
|
147
138
|
catch (e) {
|