micro-models-agent 0.41.2 → 0.43.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 +648 -183
- 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/bash.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { isCommandAllowed, sanitizeCommandForLog } from "../modules/security/command-validator";
|
|
2
|
-
import { logBashCommand, logSecurityBlock } from "../modules/security/audit-log";
|
|
1
|
+
import { isCommandAllowed, sanitizeCommandForLog, } from "../modules/security/command-validator";
|
|
2
|
+
import { logBashCommand, logSecurityBlock, } from "../modules/security/audit-log";
|
|
3
3
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
4
4
|
import { DEFAULT_SECURITY_CONFIG } from "../config/security";
|
|
5
|
-
import { processRegistry, registerKillable, unregisterKillable } from "../modules/processes";
|
|
5
|
+
import { processRegistry, registerKillable, unregisterKillable, } from "../modules/processes";
|
|
6
6
|
import { t } from "../i18n/index";
|
|
7
7
|
import { platform } from "os";
|
|
8
8
|
import { MAX_PREVIEW_LINES } from "./preview";
|
|
@@ -16,26 +16,11 @@ import { MAX_PREVIEW_LINES } from "./preview";
|
|
|
16
16
|
export const BASH_GRACE_MS = 5000;
|
|
17
17
|
/** Short window for explicit `background: true` — surfaces immediate spawn failures (bad cwd, missing shell). */
|
|
18
18
|
const SPAWN_SETTLE_MS = 100;
|
|
19
|
-
/** Number of already-emitted lines shown in the background start message. */
|
|
20
|
-
const BG_OUTPUT_PREVIEW_LINES = 15;
|
|
21
19
|
let bashGraceMs = BASH_GRACE_MS;
|
|
22
20
|
/** Test hook: override the auto-background grace window. */
|
|
23
21
|
export function setBashGraceMs(ms) {
|
|
24
22
|
bashGraceMs = ms;
|
|
25
23
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Consecutive failures per first command word (resets on success). A model
|
|
28
|
-
* that keeps calling a forbidden Unix command in cmd.exe (head/cat/grep/...)
|
|
29
|
-
* gets a hint the first couple of times; after HARD_BLOCK_THRESHOLD consecutive
|
|
30
|
-
* failures the tool hard-stops with a STOP directive instead of repeating the
|
|
31
|
-
* same hint forever (observed: `head` called 5×, `Select-String` 3× in one run).
|
|
32
|
-
*/
|
|
33
|
-
const FAILING_FIRST_WORDS = new Map();
|
|
34
|
-
const HARD_BLOCK_THRESHOLD = 3;
|
|
35
|
-
/** Test hook: clear the consecutive-failure tracking state. */
|
|
36
|
-
export function resetBashFailureTracking() {
|
|
37
|
-
FAILING_FIRST_WORDS.clear();
|
|
38
|
-
}
|
|
39
24
|
/**
|
|
40
25
|
* Detect a file write via `echo/printf ... > file` — common model habit that
|
|
41
26
|
* breaks in cmd.exe: single quotes are not grouping quotes, `>` only applies
|
|
@@ -123,12 +108,6 @@ const UNIX_TO_WIN_HINTS = {
|
|
|
123
108
|
echo: "echo works on Windows, but avoid pipes (|).",
|
|
124
109
|
"Get-Content": "Use the read_file tool instead.",
|
|
125
110
|
"Select-Object": "Use the read_file tool with offset/limit instead.",
|
|
126
|
-
"Write-Host": "PowerShell cmdlet — this shell is cmd.exe. Print with plain echo instead.",
|
|
127
|
-
"Select-String": "Use the grep tool instead.",
|
|
128
|
-
"Out-File": "Write files with the write_file tool instead.",
|
|
129
|
-
"Set-Content": "Write files with the write_file tool instead.",
|
|
130
|
-
"Get-ChildItem": "Use the list_dir tool instead.",
|
|
131
|
-
"Remove-Item": "Use the delete_file tool instead.",
|
|
132
111
|
};
|
|
133
112
|
/** Unix commands that can be transparently translated to cmd.exe equivalents. */
|
|
134
113
|
const UNIX_TO_WIN_TRANSLATE = {
|
|
@@ -219,94 +198,9 @@ export function parseToolArgs(raw) {
|
|
|
219
198
|
}
|
|
220
199
|
return args;
|
|
221
200
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Detect a test-runner report embedded in command output (bun test / vitest /
|
|
224
|
-
* jest / mocha / `go test` / pytest). Returns null when the output does not
|
|
225
|
-
* look like a test run. Used to inject a warning when tests fail so the model
|
|
226
|
-
* cannot close a "run the tests" step as done.
|
|
227
|
-
*/
|
|
228
|
-
export function detectTestResults(output) {
|
|
229
|
-
if (!output)
|
|
230
|
-
return null;
|
|
231
|
-
// Framework-specific quantifiers first (unambiguous).
|
|
232
|
-
const jest = output.match(/Tests:\s+(\d+)\s+passed,\s*(\d+)\s+failed/i);
|
|
233
|
-
if (jest) {
|
|
234
|
-
return { framework: "jest", passed: +jest[1], failed: +jest[2] };
|
|
235
|
-
}
|
|
236
|
-
const mochaPass = output.match(/(\d+)\s+passing/i);
|
|
237
|
-
const mochaFail = output.match(/(\d+)\s+failing/i);
|
|
238
|
-
if (mochaPass || mochaFail) {
|
|
239
|
-
return {
|
|
240
|
-
framework: "mocha",
|
|
241
|
-
passed: mochaPass ? +mochaPass[1] : 0,
|
|
242
|
-
failed: mochaFail ? +mochaFail[1] : 0,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
const pytest = output.match(/(\d+)\s+passed[^\n]*?(?:,\s*(\d+)\s+failed)?/i);
|
|
246
|
-
if (pytest &&
|
|
247
|
-
/pytest|passed|failed/i.test(output) &&
|
|
248
|
-
/(=====|short test summary|tests\s+ok)/i.test(output) === false &&
|
|
249
|
-
/pytest|collect/i.test(output)) {
|
|
250
|
-
return {
|
|
251
|
-
framework: "pytest",
|
|
252
|
-
passed: +pytest[1],
|
|
253
|
-
failed: pytest[2] ? +pytest[2] : 0,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
// bun test / vitest: per-case markers + trailing "N pass / M fail".
|
|
257
|
-
const failMarkers = output.match(/\(fail\)/g)?.length ?? 0;
|
|
258
|
-
const passMarkers = output.match(/\(pass\)/g)?.length ?? 0;
|
|
259
|
-
const xMarkers = output.match(/\s×\s/g)?.length ?? 0;
|
|
260
|
-
const summaryMatch = output.match(/^\s*(\d+)\s+pass[^\n]*$/m);
|
|
261
|
-
const failSummary = output.match(/^\s*(\d+)\s+fail[^\n]*$/m);
|
|
262
|
-
const ran = output.match(/(?:Ran|ran)\s+\d+\s+tests/i);
|
|
263
|
-
if (failMarkers > 0 || passMarkers > 0 || xMarkers > 0 || ran) {
|
|
264
|
-
const failed = Math.max(failMarkers + xMarkers, failSummary ? +failSummary[1] : 0);
|
|
265
|
-
const passed = Math.max(passMarkers, summaryMatch ? +summaryMatch[1] : 0);
|
|
266
|
-
return {
|
|
267
|
-
framework: ran ? "bun/vitest" : "vitest",
|
|
268
|
-
passed,
|
|
269
|
-
failed,
|
|
270
|
-
summary: summaryMatch?.[0] ?? failSummary?.[0],
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* R5: a command that runs a script file (bun/node/python/...) exited 0 but
|
|
277
|
-
* printed nothing. The most common cause in agent sessions: the program has
|
|
278
|
-
* no entry point — nobody calls its main() with the CLI args. Surface a
|
|
279
|
-
* concrete hint instead of letting the model stare at an empty output.
|
|
280
|
-
*/
|
|
281
|
-
const CLI_FILE_RUN_RE = /\b(bun|node|deno|python|python3|tsx|ts-node|php|ruby|go\s+run)\S*\s+(run\s+)?["']?[\w./\\-]+\.(ts|js|tsx|jsx|mjs|cjs|py)\b/;
|
|
282
|
-
export function emptyCliRunHint(command, output, code) {
|
|
283
|
-
if (code !== 0 || output.trim())
|
|
284
|
-
return null;
|
|
285
|
-
if (/[>|]/.test(command))
|
|
286
|
-
return null; // redirection/pipes — output went elsewhere
|
|
287
|
-
if (/\b(bun test|vitest|pytest|jest|mocha|--test)\b/i.test(command))
|
|
288
|
-
return null;
|
|
289
|
-
if (!CLI_FILE_RUN_RE.test(command))
|
|
290
|
-
return null;
|
|
291
|
-
return "the command exited 0 but printed NOTHING to stdout. If this should run a CLI program, the file probably has no entry point: read it with read_file and check the code actually calls its main function with command-line arguments (e.g. main(process.argv[2])) and prints results with console.log.";
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Detect npm/npx/bunx failing with "could not determine executable to run"
|
|
295
|
-
* — the package declares no `bin` (or `npm run <script>` named a missing
|
|
296
|
-
* script). Append a concrete hint instead of letting the model re-try blindly
|
|
297
|
-
* (observed in two agent sessions: the model burned many iterations re-running
|
|
298
|
-
* the same failing `npx`/`bunx`/`npm run` command).
|
|
299
|
-
*/
|
|
300
|
-
const NPM_EXEC_RE = /could not determine executable to run/i;
|
|
301
|
-
export function npmExecHint(output) {
|
|
302
|
-
if (NPM_EXEC_RE.test(output)) {
|
|
303
|
-
return `${output}\n\nHint: ${t("exec.npm_exec_hint")}`;
|
|
304
|
-
}
|
|
305
|
-
return output;
|
|
306
|
-
}
|
|
307
201
|
export const bashTool = {
|
|
308
202
|
name: "bash",
|
|
309
|
-
description: "Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers)
|
|
203
|
+
description: "Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).",
|
|
310
204
|
tags: ["shell", "code"],
|
|
311
205
|
parameters: {
|
|
312
206
|
type: "object",
|
|
@@ -393,55 +287,19 @@ export const bashTool = {
|
|
|
393
287
|
const code = entry.exitCode;
|
|
394
288
|
let output = entry.log.join("\n");
|
|
395
289
|
processRegistry.remove(entry.id);
|
|
396
|
-
// R5: script file ran with exit 0 but empty output — likely a
|
|
397
|
-
// missing entry point (main is never called with argv).
|
|
398
|
-
const cliHint = emptyCliRunHint(command, output, code);
|
|
399
|
-
if (cliHint) {
|
|
400
|
-
output = `(exit code 0, no output)\n\nHint: ${cliHint}`;
|
|
401
|
-
}
|
|
402
|
-
// Auto-verify test runs: a failing suite must not be reported as a
|
|
403
|
-
// clean success. Inject a prominent marker the model cannot miss
|
|
404
|
-
// (the tpical failure: tests fail but the CLI exit code is 0).
|
|
405
|
-
const testRun = detectTestResults(output);
|
|
406
|
-
if (testRun && testRun.failed > 0) {
|
|
407
|
-
output =
|
|
408
|
-
`[test-runner] ${testRun.framework}: ${testRun.failed} test(s) FAILING, ${testRun.passed} passing — do NOT mark verification steps as done while tests fail. Investigate the failures, fix the code, then re-run the tests.\n\n` +
|
|
409
|
-
output;
|
|
410
|
-
}
|
|
411
|
-
else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
|
|
412
|
-
output = `[test-runner] ${testRun.framework}: all ${testRun.passed} test(s) passing.\n\n${output}`;
|
|
413
|
-
}
|
|
414
290
|
if (!output && code !== 0) {
|
|
415
291
|
output = `(exit code ${code})`;
|
|
416
292
|
}
|
|
417
|
-
//
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
// word left `cat` invisible to the hint/block (observed: model
|
|
428
|
-
// ran cat 4+ times in one session with no guidance).
|
|
429
|
-
if (platform() === "win32") {
|
|
430
|
-
const originalFirstWord = originalCommand.trim().split(/\s+/)[0]?.split(/[\\/]/).pop();
|
|
431
|
-
if (originalFirstWord && originalFirstWord in UNIX_TO_WIN_HINTS) {
|
|
432
|
-
if (code === 0) {
|
|
433
|
-
FAILING_FIRST_WORDS.delete(originalFirstWord);
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
const failures = (FAILING_FIRST_WORDS.get(originalFirstWord) || 0) + 1;
|
|
437
|
-
FAILING_FIRST_WORDS.set(originalFirstWord, failures);
|
|
438
|
-
if (failures >= HARD_BLOCK_THRESHOLD) {
|
|
439
|
-
output = `STOP using "${originalFirstWord}" — it does not work in this cmd.exe shell and has failed ${failures} times in a row. ${UNIX_TO_WIN_HINTS[originalFirstWord]}`;
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
output = `${output}\n\nHint: "${originalFirstWord}" may not work on Windows. ${UNIX_TO_WIN_HINTS[originalFirstWord]}`;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
293
|
+
// On Windows, hint about Unix commands that don't work
|
|
294
|
+
if (platform() === "win32" && code !== 0) {
|
|
295
|
+
const firstWord = command
|
|
296
|
+
.trim()
|
|
297
|
+
.split(/\s+/)[0]
|
|
298
|
+
?.split(/[\\/]/)
|
|
299
|
+
.pop();
|
|
300
|
+
const hint = firstWord ? UNIX_TO_WIN_HINTS[firstWord] : undefined;
|
|
301
|
+
if (hint) {
|
|
302
|
+
output = `${output}\n\nHint: "${firstWord}" may not work on Windows. ${hint}`;
|
|
445
303
|
}
|
|
446
304
|
}
|
|
447
305
|
// Update audit log with result
|
|
@@ -449,18 +307,7 @@ export const bashTool = {
|
|
|
449
307
|
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), code === 0, `Working directory: ${workdir}, Output length: ${output.length}`);
|
|
450
308
|
}
|
|
451
309
|
const lines = output.split("\n");
|
|
452
|
-
if (
|
|
453
|
-
// Keep the failure details: test runners print passing markers
|
|
454
|
-
// first and the errors + summary at the very end. A short head
|
|
455
|
-
// preview hides exactly what the model needs to fix the failures.
|
|
456
|
-
const TEST_TAIL_LINES = 400;
|
|
457
|
-
const kept = lines.slice(-TEST_TAIL_LINES);
|
|
458
|
-
const skipped = lines.length - kept.length;
|
|
459
|
-
output =
|
|
460
|
-
(skipped > 0 ? `[... ${skipped} earlier lines omitted — failing tests below]\n` : "") +
|
|
461
|
-
kept.join("\n");
|
|
462
|
-
}
|
|
463
|
-
else if (lines.length > MAX_PREVIEW_LINES) {
|
|
310
|
+
if (lines.length > MAX_PREVIEW_LINES) {
|
|
464
311
|
output =
|
|
465
312
|
lines.slice(0, MAX_PREVIEW_LINES).join("\n") +
|
|
466
313
|
`\n... (${lines.length - MAX_PREVIEW_LINES} more lines)`;
|
|
@@ -469,19 +316,13 @@ export const bashTool = {
|
|
|
469
316
|
}
|
|
470
317
|
// Still running — promote to a background process.
|
|
471
318
|
const explicit = args.background === true;
|
|
472
|
-
const
|
|
473
|
-
.filter((l) => l.trim() !== "")
|
|
474
|
-
.slice(0, BG_OUTPUT_PREVIEW_LINES)
|
|
475
|
-
.join("\n");
|
|
476
|
-
let output = `${t("proc.started", {
|
|
319
|
+
const output = `${t("proc.started", {
|
|
477
320
|
id: entry.id,
|
|
478
321
|
pid: entry.pid,
|
|
479
322
|
command,
|
|
480
|
-
})}${explicit ? "" : `\n${t("proc.promoted_hint", { ms: settleMs })}`}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
output += `\n${t("proc.manage_hint", { id: entry.id })}`;
|
|
323
|
+
})}${explicit ? "" : `\n${t("proc.promoted_hint", { ms: settleMs })}`}\n${t("proc.manage_hint", {
|
|
324
|
+
id: entry.id,
|
|
325
|
+
})}`;
|
|
485
326
|
if (securityConfig?.logCommands) {
|
|
486
327
|
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), true, `Started in background: ${entry.id} (PID ${entry.pid})`);
|
|
487
328
|
}
|
package/dist/tools/browser.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { BrowserSession } from
|
|
2
|
-
import { DEFAULT_BROWSER_CONFIG } from
|
|
3
|
-
import { t } from
|
|
4
|
-
import { join } from
|
|
1
|
+
import { BrowserSession } from '../modules/browser/session';
|
|
2
|
+
import { DEFAULT_BROWSER_CONFIG } from '../modules/browser/types';
|
|
3
|
+
import { t } from '../i18n/index';
|
|
4
|
+
import { join } from 'path';
|
|
5
5
|
let session = null;
|
|
6
6
|
function getSession(ctx) {
|
|
7
7
|
if (!session) {
|
|
8
|
-
const cookieDir = join(ctx.baseDir,
|
|
8
|
+
const cookieDir = join(ctx.baseDir, '.mma', 'browser');
|
|
9
9
|
session = new BrowserSession({
|
|
10
10
|
...DEFAULT_BROWSER_CONFIG,
|
|
11
11
|
headless: ctx.config.browser?.headless ?? true,
|
|
12
12
|
maxElements: ctx.config.browser?.maxElements ?? 30,
|
|
13
|
-
maxContentChars: ctx.config.browser?.maxContentChars ?? 2500,
|
|
14
|
-
maxConsoleEntries: ctx.config.browser?.maxConsoleEntries ?? 40,
|
|
15
|
-
maxConsoleLineChars: ctx.config.browser?.maxConsoleLineChars ?? 400,
|
|
16
13
|
navigationTimeout: ctx.config.browser?.navigationTimeout ?? 15000,
|
|
17
14
|
viewportWidth: ctx.config.browser?.viewportWidth ?? 1280,
|
|
18
15
|
viewportHeight: ctx.config.browser?.viewportHeight ?? 720,
|
|
@@ -22,80 +19,66 @@ function getSession(ctx) {
|
|
|
22
19
|
return session;
|
|
23
20
|
}
|
|
24
21
|
export function formatScreenshotForTextModel(snapshot) {
|
|
25
|
-
return `${t(
|
|
22
|
+
return `${t('tool.screenshot_unavailable')}\n\n${snapshot}`;
|
|
26
23
|
}
|
|
27
24
|
export function formatScreenshotResult(snapshot, screenshot, supportsVision) {
|
|
28
25
|
if (!supportsVision) {
|
|
29
26
|
return formatScreenshotForTextModel(snapshot);
|
|
30
27
|
}
|
|
31
|
-
const base64 = screenshot.toString(
|
|
28
|
+
const base64 = screenshot.toString('base64');
|
|
32
29
|
return `${snapshot}\n\n[Screenshot: data:image/png;base64,${base64}]`;
|
|
33
30
|
}
|
|
34
31
|
export function createBrowserTool() {
|
|
35
32
|
return {
|
|
36
|
-
name:
|
|
37
|
-
tags: [
|
|
33
|
+
name: 'browser',
|
|
34
|
+
tags: ['browser', 'vision'],
|
|
38
35
|
description: [
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"Use the Content section to understand what the page says; use element numbers as targets for click/type.",
|
|
44
|
-
"Actions: open (url), click (target), type (target, text), scroll (direction), back, forward, screenshot, snapshot, close, wait (ms).",
|
|
45
|
-
].join(" "),
|
|
36
|
+
'Control a headless browser. Navigate pages, click elements, type text, scroll, take screenshots.',
|
|
37
|
+
'Returns a numbered list of interactive elements — use element numbers as targets for click/type.',
|
|
38
|
+
'Actions: open (url), click (target), type (target, text), scroll (direction), back, forward, screenshot, snapshot, close, wait (ms).',
|
|
39
|
+
].join(' '),
|
|
46
40
|
parameters: {
|
|
47
|
-
type:
|
|
41
|
+
type: 'object',
|
|
48
42
|
properties: {
|
|
49
43
|
action: {
|
|
50
|
-
type:
|
|
51
|
-
enum: [
|
|
52
|
-
|
|
53
|
-
"click",
|
|
54
|
-
"type",
|
|
55
|
-
"scroll",
|
|
56
|
-
"back",
|
|
57
|
-
"forward",
|
|
58
|
-
"screenshot",
|
|
59
|
-
"snapshot",
|
|
60
|
-
"close",
|
|
61
|
-
"wait",
|
|
62
|
-
],
|
|
63
|
-
description: "Browser action to perform",
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['open', 'click', 'type', 'scroll', 'back', 'forward', 'screenshot', 'snapshot', 'close', 'wait'],
|
|
46
|
+
description: 'Browser action to perform',
|
|
64
47
|
},
|
|
65
48
|
url: {
|
|
66
|
-
type:
|
|
49
|
+
type: 'string',
|
|
67
50
|
description: 'URL to open (for action "open")',
|
|
68
51
|
},
|
|
69
52
|
target: {
|
|
70
|
-
type:
|
|
53
|
+
type: 'number',
|
|
71
54
|
description: 'Element number from snapshot (for action "click" or "type")',
|
|
72
55
|
},
|
|
73
56
|
text: {
|
|
74
|
-
type:
|
|
57
|
+
type: 'string',
|
|
75
58
|
description: 'Text to type (for action "type")',
|
|
76
59
|
},
|
|
77
60
|
direction: {
|
|
78
|
-
type:
|
|
79
|
-
enum: [
|
|
61
|
+
type: 'string',
|
|
62
|
+
enum: ['up', 'down', 'top', 'bottom'],
|
|
80
63
|
description: 'Scroll direction (for action "scroll", default "down")',
|
|
81
64
|
},
|
|
82
65
|
ms: {
|
|
83
|
-
type:
|
|
66
|
+
type: 'number',
|
|
84
67
|
description: 'Milliseconds to wait (for action "wait", default 1000)',
|
|
85
68
|
},
|
|
86
69
|
},
|
|
87
|
-
required: [
|
|
70
|
+
required: ['action'],
|
|
88
71
|
},
|
|
89
72
|
handler: async (ctx, args) => {
|
|
90
|
-
const action = String(args.action ||
|
|
73
|
+
const action = String(args.action || '');
|
|
91
74
|
if (!action) {
|
|
92
|
-
return { success: false, output: t(
|
|
75
|
+
return { success: false, output: t('tool.action_required') };
|
|
93
76
|
}
|
|
94
77
|
const s = getSession(ctx);
|
|
95
78
|
const result = await s.execute(action, args);
|
|
96
|
-
const supportsVision = ctx.config.model.includes(
|
|
97
|
-
ctx.config.model.includes(
|
|
98
|
-
ctx.config.model.includes(
|
|
79
|
+
const supportsVision = ctx.config.model.includes('vision') ||
|
|
80
|
+
ctx.config.model.includes('gpt-4o') ||
|
|
81
|
+
ctx.config.model.includes('claude');
|
|
99
82
|
if (result.screenshot) {
|
|
100
83
|
return {
|
|
101
84
|
success: result.success,
|
package/dist/tools/executor.js
CHANGED
|
@@ -49,7 +49,9 @@ export class ToolExecutor {
|
|
|
49
49
|
try {
|
|
50
50
|
const proceed = await plugin.onBeforeTool(this.ctx, call);
|
|
51
51
|
if (proceed === false || typeof proceed === "string") {
|
|
52
|
-
const pluginName = plugin.name ||
|
|
52
|
+
const pluginName = plugin.name ||
|
|
53
|
+
plugin.constructor?.name ||
|
|
54
|
+
"unknown";
|
|
53
55
|
const reason = typeof proceed === "string" ? proceed : undefined;
|
|
54
56
|
return {
|
|
55
57
|
success: false,
|
|
@@ -73,16 +75,15 @@ export class ToolExecutor {
|
|
|
73
75
|
result = await tool.handler(this.ctx, call.arguments);
|
|
74
76
|
}
|
|
75
77
|
else {
|
|
76
|
-
const timeoutMs = tool.timeoutMs ?? TOOL_EXECUTION_TIMEOUT_MS;
|
|
77
78
|
const timeoutPromise = new Promise((_, reject) => {
|
|
78
79
|
setTimeout(() => {
|
|
79
80
|
// Kill any long-running child process registered for this call.
|
|
80
81
|
killByCallId(call.id);
|
|
81
82
|
reject(new Error(t("tool.timeout", {
|
|
82
83
|
name: call.name,
|
|
83
|
-
seconds:
|
|
84
|
+
seconds: TOOL_EXECUTION_TIMEOUT_MS / 1000,
|
|
84
85
|
})));
|
|
85
|
-
},
|
|
86
|
+
}, TOOL_EXECUTION_TIMEOUT_MS);
|
|
86
87
|
});
|
|
87
88
|
const racePromises = [
|
|
88
89
|
tool.handler(this.ctx, call.arguments),
|
package/dist/tools/file-info.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { statSync, existsSync } from
|
|
2
|
-
import { t } from
|
|
3
|
-
import { isPathInScope } from
|
|
4
|
-
import { safeResolvePath } from
|
|
1
|
+
import { statSync, existsSync } from 'fs';
|
|
2
|
+
import { t } from '../i18n/index';
|
|
3
|
+
import { isPathInScope } from '../modules/security/path-validator';
|
|
4
|
+
import { safeResolvePath } from './path-utils';
|
|
5
5
|
export const fileInfoTool = {
|
|
6
|
-
name:
|
|
7
|
-
description:
|
|
8
|
-
tags: [
|
|
9
|
-
boundedOutput: true,
|
|
6
|
+
name: 'file_info',
|
|
7
|
+
description: 'Get metadata about a file or directory (size, creation date, modification date).',
|
|
8
|
+
tags: ['file'],
|
|
10
9
|
parameters: {
|
|
11
|
-
type:
|
|
10
|
+
type: 'object',
|
|
12
11
|
properties: {
|
|
13
|
-
path: { type:
|
|
12
|
+
path: { type: 'string', description: 'File path' },
|
|
14
13
|
},
|
|
15
|
-
required: [
|
|
14
|
+
required: ['path'],
|
|
16
15
|
},
|
|
17
16
|
handler: async (ctx, args) => {
|
|
18
17
|
const path = String(args.path);
|
|
@@ -22,13 +21,13 @@ export const fileInfoTool = {
|
|
|
22
21
|
if (!scopeCheck.allowed) {
|
|
23
22
|
return {
|
|
24
23
|
success: false,
|
|
25
|
-
output: t(
|
|
24
|
+
output: t('file.path_not_allowed', {
|
|
26
25
|
path: `${path} — ${scopeCheck.reason}`,
|
|
27
26
|
}),
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
29
|
if (!existsSync(resolved)) {
|
|
31
|
-
return { success: false, output: t(
|
|
30
|
+
return { success: false, output: t('file.not_found_short', { path }) };
|
|
32
31
|
}
|
|
33
32
|
const stat = statSync(resolved);
|
|
34
33
|
return {
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Filter tools by a tag set, always keeping `alwaysOn` tools. Used by MoE
|
|
3
|
-
* expert routing and subagent tool_tags so structural tools (plan/todo/verify,
|
|
4
|
-
* project_map, lsp_check, enable_tools) survive narrowing.
|
|
5
|
-
*/
|
|
6
1
|
export function filterToolsByTags(tools, toolTags) {
|
|
7
2
|
if (!toolTags || toolTags.length === 0)
|
|
8
3
|
return tools;
|
|
9
4
|
const tagSet = new Set(toolTags);
|
|
10
|
-
return tools.filter(
|
|
11
|
-
if (t.alwaysOn)
|
|
12
|
-
return true;
|
|
5
|
+
return tools.filter(t => {
|
|
13
6
|
if (!t.tags || t.tags.length === 0)
|
|
14
7
|
return false;
|
|
15
|
-
return t.tags.some(
|
|
8
|
+
return t.tags.some(tag => tagSet.has(tag));
|
|
16
9
|
});
|
|
17
10
|
}
|
package/dist/tools/glob-tool.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { globSync } from
|
|
2
|
-
import { t } from
|
|
3
|
-
import { MAX_PREVIEW_LINES } from
|
|
1
|
+
import { globSync } from 'fs';
|
|
2
|
+
import { t } from '../i18n/index';
|
|
3
|
+
import { MAX_PREVIEW_LINES } from './preview';
|
|
4
4
|
export const globTool = {
|
|
5
|
-
name:
|
|
5
|
+
name: 'glob',
|
|
6
6
|
description: `Search for files matching a glob pattern. Shows up to ${MAX_PREVIEW_LINES} results by default. Uses standard glob syntax (e.g., **/*.ts, src/**/*.test.ts).`,
|
|
7
|
-
tags: [
|
|
7
|
+
tags: ['file', 'code', 'research'],
|
|
8
8
|
parameters: {
|
|
9
|
-
type:
|
|
9
|
+
type: 'object',
|
|
10
10
|
properties: {
|
|
11
|
-
pattern: { type:
|
|
11
|
+
pattern: { type: 'string', description: 'Glob pattern' },
|
|
12
12
|
},
|
|
13
|
-
required: [
|
|
13
|
+
required: ['pattern'],
|
|
14
14
|
},
|
|
15
15
|
handler: async (ctx, args) => {
|
|
16
16
|
const pattern = String(args.pattern);
|
|
17
17
|
const results = globSync(pattern, { cwd: ctx.baseDir });
|
|
18
18
|
if (results.length === 0) {
|
|
19
|
-
return { success: true, output: t(
|
|
19
|
+
return { success: true, output: t('file.no_matches') };
|
|
20
20
|
}
|
|
21
21
|
const truncated = results.length > MAX_PREVIEW_LINES
|
|
22
22
|
? `\n... (${results.length - MAX_PREVIEW_LINES} more files)`
|
|
23
|
-
:
|
|
24
|
-
return { success: true, output: results.slice(0, MAX_PREVIEW_LINES).join(
|
|
23
|
+
: '';
|
|
24
|
+
return { success: true, output: results.slice(0, MAX_PREVIEW_LINES).join('\n') + truncated };
|
|
25
25
|
},
|
|
26
26
|
};
|
package/dist/tools/grep-tool.js
CHANGED
|
@@ -31,7 +31,9 @@ export const grepTool = {
|
|
|
31
31
|
},
|
|
32
32
|
handler: async (ctx, args) => {
|
|
33
33
|
const pattern = String(args.pattern);
|
|
34
|
-
const searchPath = args.path
|
|
34
|
+
const searchPath = args.path
|
|
35
|
+
? resolve(ctx.baseDir, String(args.path))
|
|
36
|
+
: ctx.baseDir;
|
|
35
37
|
// Build rg arguments as an array to avoid shell interpretation of regex
|
|
36
38
|
// metacharacters like |, (, ) — these are regex patterns, not shell operators.
|
|
37
39
|
const rgArgs = ["-n", "--with-filename", pattern, searchPath];
|
package/dist/tools/index.js
CHANGED
|
@@ -15,11 +15,9 @@ import { processListTool } from "./process-list";
|
|
|
15
15
|
import { processLogTool } from "./process-log";
|
|
16
16
|
import { processKillTool } from "./process-kill";
|
|
17
17
|
import { subagentTool } from "./subagent";
|
|
18
|
-
import { chunkQueryTool } from "./chunk-query";
|
|
19
18
|
import { webSearchTool } from "./web-search";
|
|
20
19
|
import { webFetchTool } from "./web-fetch";
|
|
21
20
|
import { webBrowseTool } from "./web-browse";
|
|
22
|
-
import { downloadFileTool } from "./download-file";
|
|
23
21
|
// questionTool and approveTool removed — interactive menus are too disruptive for small models.
|
|
24
22
|
// The model should ask questions as plain text in its response instead.
|
|
25
23
|
// import { questionTool } from './question'
|
|
@@ -32,11 +30,10 @@ import { rememberTool } from "./remember";
|
|
|
32
30
|
import { recallTool } from "./recall";
|
|
33
31
|
import { createBrowserTool } from "./browser";
|
|
34
32
|
import { attachImageTool } from "./attach-image";
|
|
35
|
-
import { enableToolsTool } from "./enable-tools";
|
|
36
33
|
export { ToolRegistry, ToolExecutor };
|
|
37
34
|
export { filterToolsByTags } from "./filter-tools";
|
|
38
|
-
export { readFileTool, writeFileTool, editFileTool, globTool, grepTool, listDirTool, createDirTool, deleteFileTool, moveFileTool, fileInfoTool, bashTool, subagentTool,
|
|
39
|
-
export function registerAllTools(registry, skillsModule
|
|
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, };
|
|
36
|
+
export function registerAllTools(registry, skillsModule) {
|
|
40
37
|
const tools = [
|
|
41
38
|
readFileTool,
|
|
42
39
|
writeFileTool,
|
|
@@ -50,25 +47,17 @@ export function registerAllTools(registry, skillsModule, opts) {
|
|
|
50
47
|
fileInfoTool,
|
|
51
48
|
bashTool,
|
|
52
49
|
subagentTool,
|
|
53
|
-
chunkQueryTool,
|
|
54
50
|
processListTool,
|
|
55
51
|
processLogTool,
|
|
56
52
|
processKillTool,
|
|
57
53
|
webSearchTool,
|
|
58
54
|
webFetchTool,
|
|
59
55
|
webBrowseTool,
|
|
60
|
-
downloadFileTool,
|
|
61
56
|
pipelineRunTool,
|
|
62
57
|
mcpCallTool,
|
|
63
58
|
searchHistoryTool,
|
|
64
59
|
attachImageTool,
|
|
65
60
|
];
|
|
66
|
-
// On-demand tool activation is the default. When explicitly disabled
|
|
67
|
-
// (enableOnDemand: false), the tool is not registered at all — combined
|
|
68
|
-
// with an empty defaultTags this restores the legacy all-tools behavior.
|
|
69
|
-
if (opts?.enableOnDemand !== false) {
|
|
70
|
-
tools.push(enableToolsTool);
|
|
71
|
-
}
|
|
72
61
|
if (skillsModule) {
|
|
73
62
|
tools.push(createLoadSkillTool(skillsModule));
|
|
74
63
|
}
|