micro-models-agent 0.13.1 → 0.13.2
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 +2 -1
- package/dist/cli/repl.js +42 -20
- package/dist/config/config.js +42 -0
- package/dist/config/defaults.js +1 -1
- package/dist/config/security.js +1 -1
- package/dist/core/agent-moe.js +98 -0
- package/dist/core/agent.js +30 -248
- package/dist/core/bootstrap.js +4 -3
- package/dist/core/session-logger.js +122 -0
- package/dist/i18n/en.json +1 -0
- package/dist/i18n/ru.json +1 -0
- package/dist/llm/openai-compat.js +6 -9
- package/dist/modules/execution/moe-executor.js +13 -0
- package/dist/modules/hallucination/confidence.js +22 -15
- package/dist/modules/hallucination/consistency.js +29 -1
- package/dist/modules/hallucination/factual.js +49 -7
- package/dist/modules/processes/registry.js +6 -0
- package/dist/modules/security/command-validator.js +57 -14
- package/dist/modules/security/encryption.js +8 -6
- package/dist/modules/session/module.js +0 -4
- package/dist/tools/bash.js +27 -0
- package/dist/tools/create-dir.js +3 -4
- package/dist/tools/delete-file.js +3 -4
- package/dist/tools/edit-file.js +3 -4
- package/dist/tools/executor.js +6 -0
- package/dist/tools/file-info.js +3 -4
- package/dist/tools/list-dir.js +4 -4
- package/dist/tools/path-utils.js +51 -0
- package/dist/tools/read-file.js +6 -3
- package/dist/tools/write-file.js +5 -5
- package/dist/ui/renderer.js +1 -1
- package/package.json +1 -1
package/dist/tools/create-dir.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { mkdirSync, existsSync } from "fs";
|
|
2
|
-
import { resolve, normalize } from "path";
|
|
3
2
|
import { t } from "../i18n/index";
|
|
4
3
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
5
4
|
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
6
5
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
6
|
+
import { safeResolvePath } from "./path-utils";
|
|
7
7
|
export const createDirTool = {
|
|
8
8
|
name: "create_dir",
|
|
9
9
|
description: "Create a directory (and any intermediate directories).",
|
|
@@ -17,8 +17,7 @@ export const createDirTool = {
|
|
|
17
17
|
},
|
|
18
18
|
handler: async (ctx, args) => {
|
|
19
19
|
const path = String(args.path);
|
|
20
|
-
const
|
|
21
|
-
const resolved = resolve(baseDir, normalize(path));
|
|
20
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
22
21
|
// Get session-specific security config
|
|
23
22
|
const securityConfig = ctx.sessionContext
|
|
24
23
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
@@ -34,7 +33,7 @@ export const createDirTool = {
|
|
|
34
33
|
};
|
|
35
34
|
}
|
|
36
35
|
// Check path permissions
|
|
37
|
-
const scopeCheck = isPathWritable(ctx.baseDir,
|
|
36
|
+
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
38
37
|
if (!scopeCheck.allowed) {
|
|
39
38
|
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
40
39
|
return {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { unlinkSync, existsSync, statSync, readFileSync } from "fs";
|
|
2
|
-
import { resolve, normalize } from "path";
|
|
3
2
|
import { t } from "../i18n/index";
|
|
4
3
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
5
4
|
import { logFileDelete, logSecurityBlock } from "../modules/security/audit-log";
|
|
6
5
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
7
6
|
import { generateDeleteDiff } from "../ui/diff";
|
|
7
|
+
import { safeResolvePath } from "./path-utils";
|
|
8
8
|
export const deleteFileTool = {
|
|
9
9
|
name: "delete_file",
|
|
10
10
|
description: "Delete a file from the filesystem.",
|
|
@@ -18,8 +18,7 @@ export const deleteFileTool = {
|
|
|
18
18
|
},
|
|
19
19
|
handler: async (ctx, args) => {
|
|
20
20
|
const path = String(args.path);
|
|
21
|
-
const
|
|
22
|
-
const resolved = resolve(baseDir, normalize(path));
|
|
21
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
23
22
|
// Get session-specific security config
|
|
24
23
|
const securityConfig = ctx.sessionContext
|
|
25
24
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
@@ -35,7 +34,7 @@ export const deleteFileTool = {
|
|
|
35
34
|
};
|
|
36
35
|
}
|
|
37
36
|
// Check path permissions
|
|
38
|
-
const scopeCheck = isPathWritable(ctx.baseDir,
|
|
37
|
+
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
39
38
|
if (!scopeCheck.allowed) {
|
|
40
39
|
logSecurityBlock(ctx.sessionId, "file_delete", scopeCheck.reason || "Path not allowed", path);
|
|
41
40
|
return {
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import { resolve, normalize } from "path";
|
|
3
2
|
import { t } from "../i18n/index";
|
|
4
3
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
5
4
|
import { scanContent } from "../modules/security/content-scanner";
|
|
6
5
|
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
7
6
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
8
7
|
import { generateDiff } from "../ui/diff";
|
|
8
|
+
import { safeResolvePath } from "./path-utils";
|
|
9
9
|
export const editFileTool = {
|
|
10
10
|
name: "edit_file",
|
|
11
11
|
description: "Find and replace text in an existing file. Uses exact string match (not regex).",
|
|
@@ -21,8 +21,7 @@ export const editFileTool = {
|
|
|
21
21
|
},
|
|
22
22
|
handler: async (ctx, args) => {
|
|
23
23
|
const path = String(args.path);
|
|
24
|
-
const
|
|
25
|
-
const resolved = resolve(baseDir, normalize(path));
|
|
24
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
26
25
|
// Get session-specific security config
|
|
27
26
|
const securityConfig = ctx.sessionContext
|
|
28
27
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
@@ -38,7 +37,7 @@ export const editFileTool = {
|
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
39
|
// Check path permissions
|
|
41
|
-
const scopeCheck = isPathWritable(ctx.baseDir,
|
|
40
|
+
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
42
41
|
if (!scopeCheck.allowed) {
|
|
43
42
|
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
44
43
|
return {
|
package/dist/tools/executor.js
CHANGED
package/dist/tools/file-info.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { statSync, existsSync } from 'fs';
|
|
2
|
-
import { resolve, normalize } from 'path';
|
|
3
2
|
import { t } from '../i18n/index';
|
|
4
3
|
import { isPathInScope } from '../modules/security/path-validator';
|
|
4
|
+
import { safeResolvePath } from './path-utils';
|
|
5
5
|
export const fileInfoTool = {
|
|
6
6
|
name: 'file_info',
|
|
7
7
|
description: 'Get metadata about a file or directory (size, creation date, modification date).',
|
|
@@ -15,10 +15,9 @@ export const fileInfoTool = {
|
|
|
15
15
|
},
|
|
16
16
|
handler: async (ctx, args) => {
|
|
17
17
|
const path = String(args.path);
|
|
18
|
-
const
|
|
19
|
-
const resolved = resolve(baseDir, normalize(path));
|
|
18
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
20
19
|
// Check path permissions
|
|
21
|
-
const scopeCheck = isPathInScope(ctx.baseDir,
|
|
20
|
+
const scopeCheck = isPathInScope(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
22
21
|
if (!scopeCheck.allowed) {
|
|
23
22
|
return {
|
|
24
23
|
success: false,
|
package/dist/tools/list-dir.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readdirSync, statSync, existsSync } from 'fs';
|
|
2
|
-
import { resolve
|
|
2
|
+
import { resolve } from 'path';
|
|
3
3
|
import { t } from '../i18n/index';
|
|
4
4
|
import { isPathInScope } from '../modules/security/path-validator';
|
|
5
|
+
import { safeResolvePath } from './path-utils';
|
|
5
6
|
export const listDirTool = {
|
|
6
7
|
name: 'list_dir',
|
|
7
8
|
description: 'List files and directories in a given path.',
|
|
@@ -15,10 +16,9 @@ export const listDirTool = {
|
|
|
15
16
|
},
|
|
16
17
|
handler: async (ctx, args) => {
|
|
17
18
|
const path = String(args.path);
|
|
18
|
-
const
|
|
19
|
-
const resolved = resolve(baseDir, normalize(path));
|
|
19
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
20
20
|
// Check path permissions
|
|
21
|
-
const scopeCheck = isPathInScope(ctx.baseDir,
|
|
21
|
+
const scopeCheck = isPathInScope(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
22
22
|
if (!scopeCheck.allowed) {
|
|
23
23
|
return {
|
|
24
24
|
success: false,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { resolve, normalize, dirname, basename, sep } from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a user-provided path against baseDir.
|
|
5
|
+
*
|
|
6
|
+
* Handles common model mistakes:
|
|
7
|
+
* 1. Leading slash: "/testing/cat3" → resolve relative to baseDir
|
|
8
|
+
* 2. Missing separator: "E:\agent_test" when baseDir is "E:\agent\_test"
|
|
9
|
+
* → inserts the missing backslash
|
|
10
|
+
*
|
|
11
|
+
* For new files the parent directory must exist; we check that instead of
|
|
12
|
+
* the full path so write_file still works.
|
|
13
|
+
*/
|
|
14
|
+
export function safeResolvePath(baseDir, userPath) {
|
|
15
|
+
const norm = normalize(userPath);
|
|
16
|
+
const stripped = norm.replace(/^[/\\]/, '');
|
|
17
|
+
const resolved = resolve(baseDir, stripped);
|
|
18
|
+
// Fast path: if it exists (or parent does for new files), return it
|
|
19
|
+
if (existsSync(resolved) || existsSync(dirname(resolved)))
|
|
20
|
+
return resolved;
|
|
21
|
+
const baseNorm = normalize(baseDir);
|
|
22
|
+
// Walk ancestors and look for missing separators
|
|
23
|
+
let cur = baseNorm;
|
|
24
|
+
while (cur && cur !== dirname(cur)) {
|
|
25
|
+
const name = basename(cur);
|
|
26
|
+
if (!name) {
|
|
27
|
+
cur = dirname(cur);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
let idx = stripped.toLowerCase().indexOf(name.toLowerCase());
|
|
31
|
+
while (idx >= 0) {
|
|
32
|
+
const afterIdx = idx + name.length;
|
|
33
|
+
const afterChar = stripped[afterIdx];
|
|
34
|
+
if (afterChar && afterChar !== '\\' && afterChar !== '/') {
|
|
35
|
+
const fixed = stripped.slice(0, afterIdx) + sep + stripped.slice(afterIdx);
|
|
36
|
+
const fixedResolved = resolve(baseDir, normalize(fixed));
|
|
37
|
+
if (existsSync(fixedResolved) || existsSync(dirname(fixedResolved))) {
|
|
38
|
+
return fixedResolved;
|
|
39
|
+
}
|
|
40
|
+
// Try from ancestor's parent
|
|
41
|
+
const fromParent = resolve(dirname(cur), normalize(fixed));
|
|
42
|
+
if (existsSync(fromParent) || existsSync(dirname(fromParent))) {
|
|
43
|
+
return fromParent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
idx = stripped.toLowerCase().indexOf(name.toLowerCase(), idx + 1);
|
|
47
|
+
}
|
|
48
|
+
cur = dirname(cur);
|
|
49
|
+
}
|
|
50
|
+
return resolved;
|
|
51
|
+
}
|
package/dist/tools/read-file.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "fs";
|
|
2
|
-
import {
|
|
2
|
+
import { extname } from "path";
|
|
3
3
|
import { t } from "../i18n/index";
|
|
4
4
|
import { isPathInScope } from "../modules/security/path-validator";
|
|
5
5
|
import { DEFAULT_SECURITY_CONFIG } from "../config/security";
|
|
6
6
|
import { logSecurityBlock } from "../modules/security/audit-log";
|
|
7
|
+
import { safeResolvePath } from "./path-utils";
|
|
7
8
|
/** Default number of lines returned when the caller omits `limit`. */
|
|
8
9
|
const DEFAULT_LIMIT = 50;
|
|
9
10
|
export const readFileTool = {
|
|
@@ -27,8 +28,9 @@ export const readFileTool = {
|
|
|
27
28
|
},
|
|
28
29
|
handler: async (ctx, args) => {
|
|
29
30
|
const path = String(args.path);
|
|
31
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
30
32
|
const securityPaths = ctx.config?.security?.paths || DEFAULT_SECURITY_CONFIG.paths;
|
|
31
|
-
const scopeCheck = isPathInScope(ctx.baseDir,
|
|
33
|
+
const scopeCheck = isPathInScope(ctx.baseDir, resolved, ctx.scope, securityPaths);
|
|
32
34
|
if (!scopeCheck.allowed) {
|
|
33
35
|
const pathStr = path;
|
|
34
36
|
logSecurityBlock(ctx.sessionId || undefined, "file_read", scopeCheck.reason || "Path not allowed", pathStr);
|
|
@@ -39,12 +41,13 @@ export const readFileTool = {
|
|
|
39
41
|
}),
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
|
-
const resolved = resolve(ctx.baseDir, normalize(path));
|
|
43
44
|
if (!existsSync(resolved)) {
|
|
44
45
|
return { success: false, output: t("file.notfound", { path }) };
|
|
45
46
|
}
|
|
46
47
|
ctx.trackReadPath?.(path);
|
|
47
48
|
const content = readFileSync(resolved, "utf-8");
|
|
49
|
+
// Track file paths mentioned in the document (e.g. structure.md, README)
|
|
50
|
+
ctx.trackDocumentContent?.(content);
|
|
48
51
|
const lines = content.split("\n");
|
|
49
52
|
const total = lines.length;
|
|
50
53
|
const offset = args.offset || 1;
|
package/dist/tools/write-file.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs";
|
|
2
|
-
import {
|
|
2
|
+
import { dirname } from "path";
|
|
3
3
|
import { t } from "../i18n/index";
|
|
4
4
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
5
5
|
import { scanContent } from "../modules/security/content-scanner";
|
|
6
6
|
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
7
7
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
8
8
|
import { generateDiff, generateNewFileDiff } from "../ui/diff";
|
|
9
|
+
import { safeResolvePath } from "./path-utils";
|
|
9
10
|
export const writeFileTool = {
|
|
10
11
|
name: "write_file",
|
|
11
12
|
description: "Create or overwrite a file with given content. Creates intermediate directories if needed.",
|
|
@@ -20,10 +21,9 @@ export const writeFileTool = {
|
|
|
20
21
|
},
|
|
21
22
|
handler: async (ctx, args) => {
|
|
22
23
|
const path = String(args.path);
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const scopeCheck = isPathWritable(ctx.baseDir, path, ctx.scope, ctx.config.security?.paths);
|
|
24
|
+
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
25
|
+
// Check path permissions using the resolved path
|
|
26
|
+
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
27
27
|
if (!scopeCheck.allowed) {
|
|
28
28
|
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
29
29
|
return {
|
package/dist/ui/renderer.js
CHANGED