micro-models-agent 0.41.2 → 0.42.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/bin/mma.mjs +41 -41
- package/dist/cli/commands.js +19 -9
- package/dist/cli/completer.js +37 -36
- package/dist/cli/index.js +2 -2
- package/dist/cli/main.js +23 -48
- package/dist/cli/repl-commands.js +12 -40
- package/dist/cli/repl.js +87 -217
- package/dist/cli/security-commands.js +7 -5
- package/dist/cli/setup.js +26 -8
- package/dist/config/config.js +5 -52
- package/dist/config/defaults.js +5 -29
- package/dist/config/experts.js +1 -1
- package/dist/config/index.js +3 -3
- package/dist/config/security.js +10 -3
- package/dist/core/agent-moe.js +10 -2
- package/dist/core/agent.js +82 -273
- package/dist/core/bootstrap.js +13 -80
- package/dist/core/index.js +2 -2
- package/dist/core/prompt-builder.js +2 -23
- package/dist/core/session-logger.js +4 -46
- package/dist/i18n/en.json +2 -75
- package/dist/i18n/ru.json +1 -74
- package/dist/index.js +1 -1
- package/dist/llm/image-utils.js +5 -4
- package/dist/llm/index.js +4 -4
- package/dist/llm/model-loader.js +6 -6
- package/dist/llm/openai-compat.js +34 -40
- package/dist/llm/orchestrator.js +29 -33
- package/dist/llm/response.js +9 -9
- package/dist/logger/app-logger.js +1 -1
- package/dist/logger/index.js +1 -1
- package/dist/main.js +299 -55
- package/dist/migration/backup.js +13 -13
- package/dist/migration/detect.js +11 -11
- package/dist/migration/index.js +2 -2
- package/dist/modules/browser/actions.js +4 -34
- package/dist/modules/browser/bridge-server.mjs +202 -202
- package/dist/modules/browser/cookie-store.js +6 -6
- package/dist/modules/browser/index.js +5 -7
- package/dist/modules/browser/module.js +7 -8
- package/dist/modules/browser/session.js +84 -87
- package/dist/modules/browser/snapshot.js +58 -92
- package/dist/modules/browser/types.js +1 -4
- package/dist/modules/certification/cli.js +4 -2
- package/dist/modules/certification/fact-checker.js +3 -1
- package/dist/modules/certification/loader.js +9 -3
- package/dist/modules/certification/runner.js +4 -1
- package/dist/modules/context/index.js +1 -1
- package/dist/modules/context/manager.js +86 -160
- package/dist/modules/execution/auditor.js +25 -177
- package/dist/modules/execution/module.js +544 -201
- package/dist/modules/execution/moe-executor.js +0 -25
- package/dist/modules/execution/plan-store.js +3 -1
- package/dist/modules/execution/plan-validator.js +10 -10
- package/dist/modules/execution/planner.js +1 -6
- package/dist/modules/execution/stuck-detector.js +10 -173
- package/dist/modules/execution/verifier.js +42 -86
- package/dist/modules/hallucination/confidence.js +1 -8
- package/dist/modules/hallucination/detector.js +5 -2
- package/dist/modules/hallucination/factual.js +64 -3
- package/dist/modules/hallucination/index.js +1 -1
- package/dist/modules/hallucination/js-identifiers.js +0 -190
- package/dist/modules/hallucination/llm-judge.js +3 -1
- package/dist/modules/indexer/cache.js +7 -9
- package/dist/modules/indexer/index.js +3 -3
- package/dist/modules/indexer/module.js +42 -95
- package/dist/modules/indexer/walker.js +17 -17
- package/dist/modules/lsp/client.js +31 -74
- package/dist/modules/lsp/config.js +33 -87
- package/dist/modules/lsp/index.js +3 -3
- package/dist/modules/lsp/module.js +21 -185
- package/dist/modules/mcp/module.js +6 -2
- package/dist/modules/memory/index.js +1 -1
- package/dist/modules/memory/module.js +23 -71
- package/dist/modules/memory/search.js +9 -11
- package/dist/modules/memory/store.js +13 -13
- package/dist/modules/pipelines/engine.js +10 -10
- package/dist/modules/pipelines/index.js +3 -3
- package/dist/modules/pipelines/parser.js +14 -17
- package/dist/modules/pipelines/template.js +1 -1
- package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
- package/dist/modules/plugins/builtin/notify.js +2 -3
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/loader.js +17 -59
- package/dist/modules/plugins/manager.js +17 -73
- package/dist/modules/processes/index.js +1 -1
- package/dist/modules/processes/registry.js +46 -135
- package/dist/modules/registry.js +2 -4
- package/dist/modules/security/audit-notifier.js +39 -39
- package/dist/modules/security/command-validator.js +8 -2
- package/dist/modules/security/data-sanitizer.js +9 -1
- package/dist/modules/security/encryption.js +56 -58
- package/dist/modules/security/network-validator.js +9 -1
- package/dist/modules/security/path-validator.js +3 -1
- package/dist/modules/security/security-policies.js +19 -3
- package/dist/modules/security/session-encryption.js +1 -1
- package/dist/modules/security/session-isolation.js +8 -8
- package/dist/modules/session/index.js +3 -3
- package/dist/modules/session/module.js +5 -5
- package/dist/modules/session/store.js +9 -3
- package/dist/modules/skills/module.js +2 -1
- package/dist/modules/updater/checker.js +6 -70
- package/dist/modules/updater/index.js +1 -2
- package/dist/modules/user-profile/compressor.js +2 -2
- package/dist/modules/user-profile/index.js +1 -1
- package/dist/modules/user-profile/profile.js +9 -9
- package/dist/tools/attach-image.js +1 -1
- package/dist/tools/bash.js +19 -178
- package/dist/tools/browser.js +29 -46
- package/dist/tools/executor.js +5 -4
- package/dist/tools/file-info.js +12 -13
- package/dist/tools/filter-tools.js +2 -9
- package/dist/tools/glob-tool.js +11 -11
- package/dist/tools/grep-tool.js +3 -1
- package/dist/tools/index.js +2 -13
- package/dist/tools/list-dir.js +17 -18
- package/dist/tools/load-skill.js +3 -1
- package/dist/tools/path-utils.js +4 -4
- package/dist/tools/pipeline-run.js +25 -25
- package/dist/tools/process-kill.js +11 -11
- package/dist/tools/process-list.js +22 -20
- package/dist/tools/process-log.js +18 -22
- package/dist/tools/question.js +3 -1
- package/dist/tools/read-file.js +2 -10
- package/dist/tools/recall.js +37 -44
- package/dist/tools/registry.js +4 -15
- package/dist/tools/remember.js +29 -29
- package/dist/tools/scope-check.js +9 -9
- package/dist/tools/subagent.js +9 -54
- package/dist/tools/user-input.js +1 -1
- package/dist/tools/web-browse.js +3 -3
- package/dist/tools/web-fetch.js +3 -3
- package/dist/tools/web-search.js +3 -3
- package/dist/tools/write-file.js +3 -1
- package/dist/ui/box.js +5 -1
- package/dist/ui/index.js +6 -6
- package/dist/ui/md-formatter.js +33 -33
- package/dist/ui/output.js +5 -5
- package/dist/ui/renderer.js +10 -15
- package/dist/ui/table.js +1 -1
- package/package.json +48 -48
- package/dist/cli/plugin-commands.js +0 -36
- package/dist/cli/run-result.js +0 -22
- package/dist/core/version.js +0 -24
- package/dist/modules/artifacts/store.js +0 -61
- package/dist/modules/browser/bridge-client.js +0 -199
- package/dist/modules/browser/bridge-path.js +0 -10
- package/dist/modules/browser/driver.js +0 -136
- package/dist/modules/context/chunk-query.js +0 -100
- package/dist/modules/context/fact-extractor.js +0 -162
- package/dist/modules/context/history.js +0 -15
- package/dist/modules/execution/audit-runners.js +0 -152
- package/dist/modules/execution/execution-plugin.js +0 -272
- package/dist/modules/execution/plan-tool.js +0 -508
- package/dist/modules/execution/windows-commands.js +0 -41
- package/dist/modules/indexer/project-profile.js +0 -183
- package/dist/modules/lsp/check-tool.js +0 -58
- package/dist/modules/lsp/command.js +0 -60
- package/dist/modules/lsp/probe.js +0 -76
- package/dist/modules/lsp/project-root.js +0 -32
- package/dist/modules/lsp/startup-check.js +0 -141
- package/dist/modules/processes/detect.js +0 -34
- package/dist/modules/skills/matcher.js +0 -27
- package/dist/modules/updater/module.js +0 -116
- package/dist/tools/chunk-query.js +0 -99
- package/dist/tools/download-file.js +0 -116
- package/dist/tools/enable-tools.js +0 -58
- package/dist/tools/hidden-tools-block.js +0 -37
- package/dist/ui/line-editor.js +0 -703
- package/dist/ui/line-math.js +0 -69
- package/dist/ui/plan-view.js +0 -103
package/dist/tools/list-dir.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { readdirSync, statSync, existsSync } from
|
|
2
|
-
import { resolve } from
|
|
3
|
-
import { t } from
|
|
4
|
-
import { isPathInScope } from
|
|
5
|
-
import { safeResolvePath } from
|
|
6
|
-
import { MAX_PREVIEW_LINES } from
|
|
1
|
+
import { readdirSync, statSync, existsSync } from 'fs';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { t } from '../i18n/index';
|
|
4
|
+
import { isPathInScope } from '../modules/security/path-validator';
|
|
5
|
+
import { safeResolvePath } from './path-utils';
|
|
6
|
+
import { MAX_PREVIEW_LINES } from './preview';
|
|
7
7
|
export const listDirTool = {
|
|
8
|
-
name:
|
|
8
|
+
name: 'list_dir',
|
|
9
9
|
description: `List files and directories in a given path. Shows up to ${MAX_PREVIEW_LINES} entries by default.`,
|
|
10
|
-
tags: [
|
|
11
|
-
boundedOutput: true,
|
|
10
|
+
tags: ['file'],
|
|
12
11
|
parameters: {
|
|
13
|
-
type:
|
|
12
|
+
type: 'object',
|
|
14
13
|
properties: {
|
|
15
|
-
path: { type:
|
|
14
|
+
path: { type: 'string', description: 'Directory path' },
|
|
16
15
|
},
|
|
17
|
-
required: [
|
|
16
|
+
required: ['path'],
|
|
18
17
|
},
|
|
19
18
|
handler: async (ctx, args) => {
|
|
20
19
|
const path = String(args.path);
|
|
@@ -24,25 +23,25 @@ export const listDirTool = {
|
|
|
24
23
|
if (!scopeCheck.allowed) {
|
|
25
24
|
return {
|
|
26
25
|
success: false,
|
|
27
|
-
output: t(
|
|
26
|
+
output: t('file.path_not_allowed', {
|
|
28
27
|
path: `${path} — ${scopeCheck.reason}`,
|
|
29
28
|
}),
|
|
30
29
|
};
|
|
31
30
|
}
|
|
32
31
|
if (!existsSync(resolved)) {
|
|
33
|
-
return { success: false, output: t(
|
|
32
|
+
return { success: false, output: t('file.dir_notfound', { path }) };
|
|
34
33
|
}
|
|
35
34
|
const entries = readdirSync(resolved);
|
|
36
|
-
const lines = entries.map(
|
|
35
|
+
const lines = entries.map(e => {
|
|
37
36
|
const full = resolve(resolved, e);
|
|
38
37
|
return statSync(full).isDirectory() ? `${e}/` : e;
|
|
39
38
|
});
|
|
40
39
|
if (lines.length === 0) {
|
|
41
|
-
return { success: true, output: t(
|
|
40
|
+
return { success: true, output: t('file.empty') };
|
|
42
41
|
}
|
|
43
42
|
const truncated = lines.length > MAX_PREVIEW_LINES
|
|
44
43
|
? `\n... (${lines.length - MAX_PREVIEW_LINES} more entries)`
|
|
45
|
-
:
|
|
46
|
-
return { success: true, output: lines.slice(0, MAX_PREVIEW_LINES).join(
|
|
44
|
+
: '';
|
|
45
|
+
return { success: true, output: lines.slice(0, MAX_PREVIEW_LINES).join('\n') + truncated };
|
|
47
46
|
},
|
|
48
47
|
};
|
package/dist/tools/load-skill.js
CHANGED
|
@@ -28,7 +28,9 @@ export function createLoadSkillTool(skillsModule) {
|
|
|
28
28
|
return { success: false, output: result.message + hint };
|
|
29
29
|
}
|
|
30
30
|
const budget = skillsModule.getBudget();
|
|
31
|
-
const tokens = result.skill
|
|
31
|
+
const tokens = result.skill
|
|
32
|
+
? Math.ceil(result.skill.content.length / 4)
|
|
33
|
+
: 0;
|
|
32
34
|
return {
|
|
33
35
|
success: true,
|
|
34
36
|
output: t("tool.skill_budget", {
|
package/dist/tools/path-utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolve, normalize, dirname, basename, sep } from
|
|
2
|
-
import { existsSync } from
|
|
1
|
+
import { resolve, normalize, dirname, basename, sep } from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
3
|
/**
|
|
4
4
|
* Resolve a user-provided path against baseDir.
|
|
5
5
|
*
|
|
@@ -13,7 +13,7 @@ import { existsSync } from "fs";
|
|
|
13
13
|
*/
|
|
14
14
|
export function safeResolvePath(baseDir, userPath) {
|
|
15
15
|
const norm = normalize(userPath);
|
|
16
|
-
const stripped = norm.replace(/^[/\\]/,
|
|
16
|
+
const stripped = norm.replace(/^[/\\]/, '');
|
|
17
17
|
const resolved = resolve(baseDir, stripped);
|
|
18
18
|
// Fast path: if it exists (or parent does for new files), return it
|
|
19
19
|
if (existsSync(resolved) || existsSync(dirname(resolved)))
|
|
@@ -31,7 +31,7 @@ export function safeResolvePath(baseDir, userPath) {
|
|
|
31
31
|
while (idx >= 0) {
|
|
32
32
|
const afterIdx = idx + name.length;
|
|
33
33
|
const afterChar = stripped[afterIdx];
|
|
34
|
-
if (afterChar && afterChar !==
|
|
34
|
+
if (afterChar && afterChar !== '\\' && afterChar !== '/') {
|
|
35
35
|
const fixed = stripped.slice(0, afterIdx) + sep + stripped.slice(afterIdx);
|
|
36
36
|
const fixedResolved = resolve(baseDir, normalize(fixed));
|
|
37
37
|
if (existsSync(fixedResolved) || existsSync(dirname(fixedResolved))) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { t } from
|
|
2
|
-
import { PipelineEngine } from
|
|
3
|
-
import { PipelineParser } from
|
|
4
|
-
import { TemplateEngine } from
|
|
5
|
-
import { Agent } from
|
|
6
|
-
import { ContextManager } from
|
|
7
|
-
import { PluginManager } from
|
|
8
|
-
import { HallucinationDetector } from
|
|
9
|
-
import { logSecurityBlock } from
|
|
10
|
-
import { getSessionSecurityConfig } from
|
|
1
|
+
import { t } from '../i18n/index';
|
|
2
|
+
import { PipelineEngine } from '../modules/pipelines/engine';
|
|
3
|
+
import { PipelineParser } from '../modules/pipelines/parser';
|
|
4
|
+
import { TemplateEngine } from '../modules/pipelines/template';
|
|
5
|
+
import { Agent } from '../core/agent';
|
|
6
|
+
import { ContextManager } from '../modules/context/manager';
|
|
7
|
+
import { PluginManager } from '../modules/plugins/manager';
|
|
8
|
+
import { HallucinationDetector } from '../modules/hallucination/detector';
|
|
9
|
+
import { logSecurityBlock } from '../modules/security/audit-log';
|
|
10
|
+
import { getSessionSecurityConfig } from '../modules/security/session-isolation';
|
|
11
11
|
const engine = new PipelineEngine();
|
|
12
12
|
const MAX_CONCURRENT = 3;
|
|
13
13
|
const MAX_ATTEMPTS = 3;
|
|
@@ -52,29 +52,29 @@ async function runStep(ctx, step, params, outputs) {
|
|
|
52
52
|
return { ok: false, output: "", error: lastError };
|
|
53
53
|
}
|
|
54
54
|
export const pipelineRunTool = {
|
|
55
|
-
name:
|
|
56
|
-
description:
|
|
57
|
-
tags: [
|
|
55
|
+
name: 'pipeline_run',
|
|
56
|
+
description: 'Run a named pipeline with YAML definition. Creates a DAG of sub-agents that execute in dependency order.',
|
|
57
|
+
tags: ['shell', 'code'],
|
|
58
58
|
parameters: {
|
|
59
|
-
type:
|
|
59
|
+
type: 'object',
|
|
60
60
|
properties: {
|
|
61
|
-
name: { type:
|
|
62
|
-
yaml: { type:
|
|
63
|
-
params: { type:
|
|
61
|
+
name: { type: 'string', description: 'Pipeline name' },
|
|
62
|
+
yaml: { type: 'string', description: 'Pipeline YAML definition with steps' },
|
|
63
|
+
params: { type: 'object', description: 'Template params for {key} placeholders' },
|
|
64
64
|
},
|
|
65
|
-
required: [
|
|
65
|
+
required: ['name', 'yaml'],
|
|
66
66
|
},
|
|
67
67
|
handler: async (ctx, args) => {
|
|
68
|
-
const name = String(args.name ||
|
|
69
|
-
const yaml = String(args.yaml ||
|
|
68
|
+
const name = String(args.name || '');
|
|
69
|
+
const yaml = String(args.yaml || '');
|
|
70
70
|
const params = args.params || {};
|
|
71
71
|
if (!yaml) {
|
|
72
|
-
return { success: false, output: t(
|
|
72
|
+
return { success: false, output: t('pipeline.invalid') };
|
|
73
73
|
}
|
|
74
74
|
if (!ctx.llmProvider || !ctx.toolExecutor) {
|
|
75
75
|
return {
|
|
76
76
|
success: false,
|
|
77
|
-
output:
|
|
77
|
+
output: 'Pipeline cannot run: missing llmProvider or toolExecutor in context',
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
const securityConfig = ctx.sessionContext
|
|
@@ -104,7 +104,7 @@ export const pipelineRunTool = {
|
|
|
104
104
|
if (ready.length === 0) {
|
|
105
105
|
if (failed.size > 0)
|
|
106
106
|
break;
|
|
107
|
-
throw new Error(t(
|
|
107
|
+
throw new Error(t('pipeline.circular', { stepId: order.join(', ') }));
|
|
108
108
|
}
|
|
109
109
|
const batch = ready.slice(0, MAX_CONCURRENT);
|
|
110
110
|
const results = await Promise.all(batch.map((step) => runStep(ctx, step, params, outputs)));
|
|
@@ -114,12 +114,12 @@ export const pipelineRunTool = {
|
|
|
114
114
|
if (result.ok) {
|
|
115
115
|
completed.add(step.id);
|
|
116
116
|
outputs[step.id] = { output: result.output };
|
|
117
|
-
engine.setStepStatus(step.id,
|
|
117
|
+
engine.setStepStatus(step.id, 'done');
|
|
118
118
|
logs.push(` ✓ ${step.id}: ${result.output.split("\n")[0]}`);
|
|
119
119
|
}
|
|
120
120
|
else {
|
|
121
121
|
failed.add(step.id);
|
|
122
|
-
engine.setStepStatus(step.id,
|
|
122
|
+
engine.setStepStatus(step.id, 'failed');
|
|
123
123
|
logs.push(` ✗ ${step.id}: ${result.error}`);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { processRegistry } from
|
|
2
|
-
import { t } from
|
|
1
|
+
import { processRegistry } from '../modules/processes';
|
|
2
|
+
import { t } from '../i18n/index';
|
|
3
3
|
export const processKillTool = {
|
|
4
|
-
name:
|
|
5
|
-
description:
|
|
6
|
-
tags: [
|
|
4
|
+
name: 'process_kill',
|
|
5
|
+
description: 'Stop a background process started via bash (dev server, watcher). Kills the whole process tree (children included). Use the id returned by bash or process_list.',
|
|
6
|
+
tags: ['shell'],
|
|
7
7
|
parameters: {
|
|
8
|
-
type:
|
|
8
|
+
type: 'object',
|
|
9
9
|
properties: {
|
|
10
|
-
id: { type:
|
|
10
|
+
id: { type: 'string', description: 'Process id from bash output or process_list' },
|
|
11
11
|
},
|
|
12
|
-
required: [
|
|
12
|
+
required: ['id'],
|
|
13
13
|
},
|
|
14
14
|
handler: async (ctx, args) => {
|
|
15
15
|
const id = String(args.id);
|
|
16
16
|
const entry = processRegistry.get(id);
|
|
17
17
|
if (!entry) {
|
|
18
|
-
return { success: false, output: t(
|
|
18
|
+
return { success: false, output: t('proc.not_found', { id }) };
|
|
19
19
|
}
|
|
20
20
|
const killed = processRegistry.kill(id);
|
|
21
21
|
if (!killed) {
|
|
22
|
-
return { success: false, output: t(
|
|
22
|
+
return { success: false, output: t('proc.kill_failed', { id }) };
|
|
23
23
|
}
|
|
24
24
|
return {
|
|
25
25
|
success: true,
|
|
26
|
-
output: t(
|
|
26
|
+
output: t('proc.killed', { id, pid: entry.pid }),
|
|
27
27
|
};
|
|
28
28
|
},
|
|
29
29
|
};
|
|
@@ -1,36 +1,38 @@
|
|
|
1
|
-
import { processRegistry } from
|
|
2
|
-
import { t } from
|
|
1
|
+
import { processRegistry } from '../modules/processes';
|
|
2
|
+
import { t } from '../i18n/index';
|
|
3
3
|
export const processListTool = {
|
|
4
|
-
name:
|
|
5
|
-
description:
|
|
6
|
-
tags: [
|
|
4
|
+
name: 'process_list',
|
|
5
|
+
description: 'List background processes started via the bash tool (dev servers, watchers, long-running commands). Shows id, pid, command, status, and recent output. Use with process_log and process_kill to inspect or stop them.',
|
|
6
|
+
tags: ['shell'],
|
|
7
7
|
parameters: {
|
|
8
|
-
type:
|
|
8
|
+
type: 'object',
|
|
9
9
|
properties: {},
|
|
10
10
|
},
|
|
11
11
|
handler: async (ctx) => {
|
|
12
12
|
const list = processRegistry.list(ctx.sessionId);
|
|
13
13
|
if (list.length === 0) {
|
|
14
|
-
return { success: true, output: t(
|
|
14
|
+
return { success: true, output: t('proc.none') };
|
|
15
15
|
}
|
|
16
16
|
const statusLabel = (status) => {
|
|
17
|
-
if (status ===
|
|
18
|
-
return t(
|
|
19
|
-
if (status ===
|
|
20
|
-
return t(
|
|
21
|
-
return t(
|
|
17
|
+
if (status === 'running')
|
|
18
|
+
return t('proc.status_running');
|
|
19
|
+
if (status === 'exited')
|
|
20
|
+
return t('proc.status_exited');
|
|
21
|
+
return t('proc.status_killed');
|
|
22
22
|
};
|
|
23
|
-
const lines = [`${t(
|
|
23
|
+
const lines = [`${t('proc.list_header')} (${list.length}):`];
|
|
24
24
|
for (const entry of list) {
|
|
25
|
-
const tail = entry.log.length > 0 ? entry.log[entry.log.length - 1] :
|
|
26
|
-
const detail = tail
|
|
25
|
+
const tail = entry.log.length > 0 ? entry.log[entry.log.length - 1] : '';
|
|
26
|
+
const detail = tail
|
|
27
|
+
? ` — ${tail.slice(0, 80)}${tail.length > 80 ? '…' : ''}`
|
|
28
|
+
: '';
|
|
27
29
|
lines.push(` ${entry.id} PID ${entry.pid} ${statusLabel(entry.status)} ${entry.command}${detail}`);
|
|
28
30
|
}
|
|
29
|
-
lines.push(`\n${t(
|
|
30
|
-
list:
|
|
31
|
-
log:
|
|
32
|
-
kill:
|
|
31
|
+
lines.push(`\n${t('proc.hint', {
|
|
32
|
+
list: 'process_list',
|
|
33
|
+
log: 'process_log',
|
|
34
|
+
kill: 'process_kill',
|
|
33
35
|
})}`);
|
|
34
|
-
return { success: true, output: lines.join(
|
|
36
|
+
return { success: true, output: lines.join('\n') };
|
|
35
37
|
},
|
|
36
38
|
};
|
|
@@ -1,45 +1,41 @@
|
|
|
1
|
-
import { processRegistry } from
|
|
2
|
-
import { t } from
|
|
3
|
-
import { MAX_PREVIEW_LINES } from
|
|
1
|
+
import { processRegistry } from '../modules/processes';
|
|
2
|
+
import { t } from '../i18n/index';
|
|
3
|
+
import { MAX_PREVIEW_LINES } from './preview';
|
|
4
4
|
const DEFAULT_TAIL = MAX_PREVIEW_LINES;
|
|
5
5
|
export const processLogTool = {
|
|
6
|
-
name:
|
|
6
|
+
name: 'process_log',
|
|
7
7
|
description: `Show the buffered output of a background process started via bash. Shows the last ${DEFAULT_TAIL} lines by default. Use after starting a dev server to verify it came up without errors, and while it runs to check its state.`,
|
|
8
|
-
tags: [
|
|
9
|
-
boundedOutput: true,
|
|
8
|
+
tags: ['shell'],
|
|
10
9
|
parameters: {
|
|
11
|
-
type:
|
|
10
|
+
type: 'object',
|
|
12
11
|
properties: {
|
|
13
|
-
id: { type:
|
|
14
|
-
tail: {
|
|
15
|
-
type: "number",
|
|
16
|
-
description: `Number of trailing lines to show (default: ${DEFAULT_TAIL}, max 300)`,
|
|
17
|
-
},
|
|
12
|
+
id: { type: 'string', description: 'Process id from bash output or process_list' },
|
|
13
|
+
tail: { type: 'number', description: `Number of trailing lines to show (default: ${DEFAULT_TAIL}, max 300)` },
|
|
18
14
|
},
|
|
19
|
-
required: [
|
|
15
|
+
required: ['id'],
|
|
20
16
|
},
|
|
21
17
|
handler: async (ctx, args) => {
|
|
22
18
|
const id = String(args.id);
|
|
23
19
|
const entry = processRegistry.get(id);
|
|
24
20
|
if (!entry) {
|
|
25
|
-
return { success: false, output: t(
|
|
21
|
+
return { success: false, output: t('proc.not_found', { id }) };
|
|
26
22
|
}
|
|
27
|
-
const status = entry.status ===
|
|
28
|
-
? t(
|
|
29
|
-
: entry.status ===
|
|
30
|
-
? t(
|
|
31
|
-
: t(
|
|
32
|
-
const tail = typeof args.tail ===
|
|
23
|
+
const status = entry.status === 'running'
|
|
24
|
+
? t('proc.status_running')
|
|
25
|
+
: entry.status === 'exited'
|
|
26
|
+
? t('proc.status_exited')
|
|
27
|
+
: t('proc.status_killed');
|
|
28
|
+
const tail = typeof args.tail === 'number' ? args.tail : DEFAULT_TAIL;
|
|
33
29
|
const log = processRegistry.getLog(id, tail);
|
|
34
30
|
if (!log) {
|
|
35
31
|
return {
|
|
36
32
|
success: true,
|
|
37
|
-
output: `${t(
|
|
33
|
+
output: `${t('proc.log_header', { id, status })} ${t('proc.log_empty')}`,
|
|
38
34
|
};
|
|
39
35
|
}
|
|
40
36
|
return {
|
|
41
37
|
success: true,
|
|
42
|
-
output: `${t(
|
|
38
|
+
output: `${t('proc.log_header', { id, status })}\n${log}`,
|
|
43
39
|
};
|
|
44
40
|
},
|
|
45
41
|
};
|
package/dist/tools/question.js
CHANGED
|
@@ -28,7 +28,9 @@ function normalizeQuestions(args) {
|
|
|
28
28
|
const q = item;
|
|
29
29
|
if (typeof q.question !== "string" || !q.question.trim())
|
|
30
30
|
continue;
|
|
31
|
-
const options = Array.isArray(q.options)
|
|
31
|
+
const options = Array.isArray(q.options)
|
|
32
|
+
? q.options.filter(isOption)
|
|
33
|
+
: undefined;
|
|
32
34
|
// An explicitly provided options array with no valid entries is a
|
|
33
35
|
// malformed call — skip the question instead of blocking on stdin.
|
|
34
36
|
if (Array.isArray(q.options) && options.length === 0)
|
package/dist/tools/read-file.js
CHANGED
|
@@ -5,20 +5,12 @@ 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
7
|
import { safeResolvePath } from "./path-utils";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* MAX_PREVIEW_LINES (15): a 15-line default forces the model to issue 10-17
|
|
11
|
-
* calls per file, which floods the context and drives compaction every 15
|
|
12
|
-
* iterations (observed: 96 read_file calls, App.tsx read 23x, then 11
|
|
13
|
-
* compactions in ~20 min that deleted the user's task mid-turn). 300 lines
|
|
14
|
-
* covers typical component files in one call while still capping output.
|
|
15
|
-
*/
|
|
16
|
-
const DEFAULT_LIMIT = 300;
|
|
8
|
+
import { MAX_PREVIEW_LINES } from "./preview";
|
|
9
|
+
const DEFAULT_LIMIT = MAX_PREVIEW_LINES;
|
|
17
10
|
export const readFileTool = {
|
|
18
11
|
name: "read_file",
|
|
19
12
|
description: `Read a file from the filesystem. Reads up to ${DEFAULT_LIMIT} lines at a time by default; use offset to page through large files.`,
|
|
20
13
|
tags: ["file", "code"],
|
|
21
|
-
boundedOutput: true,
|
|
22
14
|
parameters: {
|
|
23
15
|
type: "object",
|
|
24
16
|
properties: {
|
package/dist/tools/recall.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { homedir } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { t } from
|
|
4
|
-
import { MemoryStore } from
|
|
5
|
-
const CATEGORIES = [
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { t } from '../i18n/index';
|
|
4
|
+
import { MemoryStore } from '../modules/memory/store';
|
|
5
|
+
const CATEGORIES = ['preferences', 'conventions', 'decisions', 'errors', 'facts'];
|
|
6
6
|
export const recallTool = {
|
|
7
|
-
name:
|
|
8
|
-
description:
|
|
9
|
-
tags: [
|
|
7
|
+
name: 'recall',
|
|
8
|
+
description: 'Recall stored information from memory. Search across preferences, facts, conventions, decisions, errors.',
|
|
9
|
+
tags: ['memory'],
|
|
10
10
|
parameters: {
|
|
11
|
-
type:
|
|
11
|
+
type: 'object',
|
|
12
12
|
properties: {
|
|
13
13
|
query: {
|
|
14
|
-
type:
|
|
15
|
-
description:
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Search query (full-text search across all memory)',
|
|
16
16
|
},
|
|
17
17
|
category: {
|
|
18
|
-
type:
|
|
19
|
-
description:
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Limit to specific category: preferences, conventions, decisions, errors, facts',
|
|
20
20
|
enum: CATEGORIES,
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
handler: async (_ctx, args) => {
|
|
25
|
-
const query = args.query ? String(args.query) :
|
|
26
|
-
const category = args.category ? String(args.category) :
|
|
27
|
-
const memoryDir = join(homedir(),
|
|
25
|
+
const query = args.query ? String(args.query) : '';
|
|
26
|
+
const category = args.category ? String(args.category) : '';
|
|
27
|
+
const memoryDir = join(homedir(), '.mma', 'memory');
|
|
28
28
|
const store = new MemoryStore(memoryDir);
|
|
29
29
|
try {
|
|
30
30
|
// No query, no category → show everything
|
|
@@ -39,26 +39,20 @@ export const recallTool = {
|
|
|
39
39
|
if (category) {
|
|
40
40
|
const results = searchCategory(store, category, query);
|
|
41
41
|
if (results.length === 0) {
|
|
42
|
-
return { success: true, output: t(
|
|
42
|
+
return { success: true, output: t('tool.recall.empty', { query }) };
|
|
43
43
|
}
|
|
44
|
-
return {
|
|
45
|
-
success: true,
|
|
46
|
-
output: t("tool.recall.search_results", { category, results: results.join("\n") }),
|
|
47
|
-
};
|
|
44
|
+
return { success: true, output: t('tool.recall.search_results', { category, results: results.join('\n') }) };
|
|
48
45
|
}
|
|
49
46
|
// Query across all
|
|
50
47
|
const results = store.search(query);
|
|
51
48
|
if (results.length === 0) {
|
|
52
|
-
return { success: true, output: t(
|
|
49
|
+
return { success: true, output: t('tool.recall.empty', { query }) };
|
|
53
50
|
}
|
|
54
|
-
const formatted = results.map(
|
|
55
|
-
return {
|
|
56
|
-
success: true,
|
|
57
|
-
output: t("tool.recall.search_results", { category: "all", results: formatted }),
|
|
58
|
-
};
|
|
51
|
+
const formatted = results.map(r => `[${r.file}] ${r.match}`).join('\n');
|
|
52
|
+
return { success: true, output: t('tool.recall.search_results', { category: 'all', results: formatted }) };
|
|
59
53
|
}
|
|
60
54
|
catch (err) {
|
|
61
|
-
return { success: true, output: t(
|
|
55
|
+
return { success: true, output: t('tool.memory_error', { error: String(err) }) };
|
|
62
56
|
}
|
|
63
57
|
},
|
|
64
58
|
};
|
|
@@ -66,14 +60,14 @@ function formatAll(store) {
|
|
|
66
60
|
const parts = [];
|
|
67
61
|
const prefs = store.getPreferences();
|
|
68
62
|
if (Object.keys(prefs).length > 0) {
|
|
69
|
-
parts.push(
|
|
63
|
+
parts.push('Preferences:');
|
|
70
64
|
for (const [k, v] of Object.entries(prefs)) {
|
|
71
65
|
parts.push(` ${k} = ${v}`);
|
|
72
66
|
}
|
|
73
67
|
}
|
|
74
|
-
for (const cat of [
|
|
68
|
+
for (const cat of ['facts', 'conventions', 'decisions', 'errors']) {
|
|
75
69
|
const content = store.read(cat);
|
|
76
|
-
const entries = content.split(
|
|
70
|
+
const entries = content.split('\n').filter(l => l.startsWith('- '));
|
|
77
71
|
if (entries.length > 0) {
|
|
78
72
|
parts.push(`\n${cat.charAt(0).toUpperCase() + cat.slice(1)} (last 5):`);
|
|
79
73
|
for (const e of entries.slice(-5)) {
|
|
@@ -81,27 +75,27 @@ function formatAll(store) {
|
|
|
81
75
|
}
|
|
82
76
|
}
|
|
83
77
|
}
|
|
84
|
-
return parts.length > 0 ? parts.join(
|
|
78
|
+
return parts.length > 0 ? parts.join('\n') : t('tool.recall.no_memory');
|
|
85
79
|
}
|
|
86
80
|
function formatCategory(store, category) {
|
|
87
|
-
if (category ===
|
|
81
|
+
if (category === 'preferences') {
|
|
88
82
|
const prefs = store.getPreferences();
|
|
89
83
|
if (Object.keys(prefs).length === 0)
|
|
90
|
-
return t(
|
|
91
|
-
const lines = [
|
|
84
|
+
return t('tool.recall.no_memory');
|
|
85
|
+
const lines = ['Preferences:'];
|
|
92
86
|
for (const [k, v] of Object.entries(prefs)) {
|
|
93
87
|
lines.push(` ${k} = ${v}`);
|
|
94
88
|
}
|
|
95
|
-
return lines.join(
|
|
89
|
+
return lines.join('\n');
|
|
96
90
|
}
|
|
97
91
|
const content = store.read(category);
|
|
98
|
-
const entries = content.split(
|
|
92
|
+
const entries = content.split('\n').filter(l => l.startsWith('- '));
|
|
99
93
|
if (entries.length === 0)
|
|
100
|
-
return t(
|
|
101
|
-
return `${category.charAt(0).toUpperCase() + category.slice(1)} (${entries.length} entries):\n${entries.join(
|
|
94
|
+
return t('tool.recall.no_memory');
|
|
95
|
+
return `${category.charAt(0).toUpperCase() + category.slice(1)} (${entries.length} entries):\n${entries.join('\n')}`;
|
|
102
96
|
}
|
|
103
97
|
function searchCategory(store, category, query) {
|
|
104
|
-
if (category ===
|
|
98
|
+
if (category === 'preferences') {
|
|
105
99
|
const prefs = store.getPreferences();
|
|
106
100
|
const lower = query.toLowerCase();
|
|
107
101
|
return Object.entries(prefs)
|
|
@@ -110,8 +104,7 @@ function searchCategory(store, category, query) {
|
|
|
110
104
|
}
|
|
111
105
|
const content = store.read(category);
|
|
112
106
|
const lower = query.toLowerCase();
|
|
113
|
-
return content
|
|
114
|
-
.
|
|
115
|
-
.
|
|
116
|
-
.map((l) => ` ${l}`);
|
|
107
|
+
return content.split('\n')
|
|
108
|
+
.filter(l => l.startsWith('- ') && l.toLowerCase().includes(lower))
|
|
109
|
+
.map(l => ` ${l}`);
|
|
117
110
|
}
|
package/dist/tools/registry.js
CHANGED
|
@@ -19,29 +19,18 @@ export class ToolRegistry {
|
|
|
19
19
|
return Array.from(this.tools.values());
|
|
20
20
|
}
|
|
21
21
|
getByTags(tags) {
|
|
22
|
-
return this.getAll().filter(
|
|
22
|
+
return this.getAll().filter(t => {
|
|
23
23
|
if (!t.tags || t.tags.length === 0)
|
|
24
24
|
return false;
|
|
25
|
-
return tags.some(
|
|
25
|
+
return tags.some(tag => t.tags.includes(tag));
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Tools to expose to the LLM. Always includes `alwaysOn` tools, then tools
|
|
30
|
-
* matching the given tags. When no tags are given, returns every registered
|
|
31
|
-
* tool (backward-compatible). Duplicates are impossible because the
|
|
32
|
-
* alwaysOn + tagged sets are unioned by name.
|
|
33
|
-
*/
|
|
34
28
|
getAllForLLM(tags) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const selected = tagSet.size === 0
|
|
38
|
-
? all
|
|
39
|
-
: all.filter((t) => t.alwaysOn || (t.tags && t.tags.some((tag) => tagSet.has(tag))));
|
|
40
|
-
return selected.map((t) => ({
|
|
29
|
+
const tools = tags ? this.getByTags(tags) : this.getAll();
|
|
30
|
+
return tools.map(t => ({
|
|
41
31
|
name: t.name,
|
|
42
32
|
description: t.description,
|
|
43
33
|
parameters: t.parameters,
|
|
44
|
-
boundedOutput: t.boundedOutput,
|
|
45
34
|
}));
|
|
46
35
|
}
|
|
47
36
|
}
|