micro-models-agent 0.21.1 → 0.23.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/README.md +312 -312
- package/bin/mma.mjs +8 -8
- package/dist/cli/commands.js +220 -0
- package/dist/cli/completer.js +168 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/main.js +113 -0
- package/dist/cli/repl.js +987 -0
- package/dist/cli/security-commands.js +166 -0
- package/dist/cli/setup.js +229 -0
- package/dist/config/config.js +186 -0
- package/dist/config/defaults.js +91 -0
- package/dist/config/experts.js +15 -0
- package/dist/config/index.js +3 -0
- package/dist/config/security.js +193 -0
- package/dist/config/types.js +1 -0
- package/dist/core/agent-moe.js +98 -0
- package/dist/core/agent.js +461 -0
- package/dist/core/bootstrap.js +321 -0
- package/dist/core/index.js +2 -0
- package/dist/core/prompt-builder.js +55 -0
- package/dist/core/session-logger.js +122 -0
- package/dist/core/types.js +1 -0
- package/dist/i18n/en.json +461 -0
- package/dist/i18n/index.js +43 -0
- package/dist/i18n/ru.json +461 -0
- package/dist/index.js +22 -0
- package/dist/llm/image-utils.js +144 -0
- package/dist/llm/index.js +4 -0
- package/dist/llm/model-loader.js +78 -0
- package/dist/llm/openai-compat.js +324 -0
- package/dist/llm/orchestrator.js +194 -0
- package/dist/llm/provider.js +10 -0
- package/dist/llm/response.js +39 -0
- package/dist/llm/token-counter.js +39 -0
- package/dist/llm/types.js +1 -0
- package/dist/logger/app-logger.js +76 -0
- package/dist/logger/index.js +1 -0
- package/dist/main.js +883 -293
- package/dist/migration/backup.js +45 -0
- package/dist/migration/detect.js +50 -0
- package/dist/migration/index.js +2 -0
- package/dist/modules/browser/actions.js +46 -0
- package/dist/modules/browser/cookie-store.js +24 -0
- package/dist/modules/browser/index.js +5 -0
- package/dist/modules/browser/module.js +28 -0
- package/dist/modules/browser/session.js +287 -0
- package/dist/modules/browser/snapshot.js +114 -0
- package/dist/modules/browser/types.js +9 -0
- package/dist/modules/context/history.js +15 -0
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +240 -0
- package/dist/modules/execution/auditor.js +72 -0
- package/dist/modules/execution/index.js +6 -0
- package/dist/modules/execution/module.js +337 -0
- package/dist/modules/execution/moe-executor.js +209 -0
- package/dist/modules/execution/plan-validator.js +153 -0
- package/dist/modules/execution/planner.js +35 -0
- package/dist/modules/execution/stuck-detector.js +134 -0
- package/dist/modules/execution/tracker.js +53 -0
- package/dist/modules/execution/types.js +1 -0
- package/dist/modules/execution/verifier.js +149 -0
- package/dist/modules/hallucination/confidence.js +54 -0
- package/dist/modules/hallucination/consistency.js +60 -0
- package/dist/modules/hallucination/detector.js +41 -0
- package/dist/modules/hallucination/factual.js +170 -0
- package/dist/modules/hallucination/index.js +4 -0
- package/dist/modules/index.js +5 -0
- package/dist/modules/indexer/cache.js +38 -0
- package/dist/modules/indexer/index.js +3 -0
- package/dist/modules/indexer/module.js +192 -0
- package/dist/modules/indexer/walker.js +101 -0
- package/dist/modules/mcp/client.js +393 -0
- package/dist/modules/mcp/index.js +3 -0
- package/dist/modules/mcp/module.js +146 -0
- package/dist/modules/mcp/registry.js +15 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +48 -0
- package/dist/modules/memory/search.js +40 -0
- package/dist/modules/memory/store.js +65 -0
- package/dist/modules/pipelines/engine.js +60 -0
- package/dist/modules/pipelines/index.js +3 -0
- package/dist/modules/pipelines/parser.js +53 -0
- package/dist/modules/pipelines/template.js +14 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +121 -0
- package/dist/modules/plugins/builtin/notify.js +8 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +28 -0
- package/dist/modules/plugins/manager.js +161 -0
- package/dist/modules/plugins/types.js +1 -0
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +3 -0
- package/dist/modules/processes/registry.js +148 -0
- package/dist/modules/processes/runner.js +124 -0
- package/dist/modules/registry.js +45 -0
- package/dist/modules/security/audit-log.js +116 -0
- package/dist/modules/security/audit-notifier.js +292 -0
- package/dist/modules/security/command-validator.js +185 -0
- package/dist/modules/security/content-scanner.js +52 -0
- package/dist/modules/security/data-sanitizer.js +97 -0
- package/dist/modules/security/encryption.js +240 -0
- package/dist/modules/security/index.js +14 -0
- package/dist/modules/security/network-validator.js +79 -0
- package/dist/modules/security/path-validator.js +155 -0
- package/dist/modules/security/rate-limiter.js +119 -0
- package/dist/modules/security/security-policies.js +393 -0
- package/dist/modules/security/session-encryption.js +193 -0
- package/dist/modules/security/session-isolation.js +95 -0
- package/dist/modules/session/index.js +3 -0
- package/dist/modules/session/manager.js +167 -0
- package/dist/modules/session/module.js +24 -0
- package/dist/modules/session/store.js +174 -0
- package/dist/modules/session/types.js +1 -0
- package/dist/modules/skills/index.js +3 -0
- package/dist/modules/skills/loader.js +72 -0
- package/dist/modules/skills/matcher.js +27 -0
- package/dist/modules/skills/module.js +143 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/updater/checker.js +32 -0
- package/dist/modules/updater/index.js +1 -0
- package/dist/modules/user-profile/compressor.js +16 -0
- package/dist/modules/user-profile/index.js +1 -0
- package/dist/modules/user-profile/profile.js +68 -0
- package/dist/tools/approve.js +32 -0
- package/dist/tools/attach-image.js +89 -0
- package/dist/tools/bash.js +140 -0
- package/dist/tools/browser.js +97 -0
- package/dist/tools/create-dir.js +56 -0
- package/dist/tools/delete-file.js +63 -0
- package/dist/tools/edit-file.js +77 -0
- package/dist/tools/executor.js +95 -0
- package/dist/tools/file-info.js +45 -0
- package/dist/tools/filter-tools.js +10 -0
- package/dist/tools/glob-tool.js +26 -0
- package/dist/tools/grep-tool.js +64 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/list-dir.js +47 -0
- package/dist/tools/load-skill.js +48 -0
- package/dist/tools/mcp-call.js +68 -0
- package/dist/tools/move-file.js +84 -0
- package/dist/tools/path-utils.js +51 -0
- package/dist/tools/pipeline-run.js +144 -0
- package/dist/tools/preview.js +2 -0
- package/dist/tools/process-kill.js +29 -0
- package/dist/tools/process-list.js +38 -0
- package/dist/tools/process-log.js +41 -0
- package/dist/tools/question.js +142 -0
- package/dist/tools/read-file.js +73 -0
- package/dist/tools/recall.js +110 -0
- package/dist/tools/registry.js +36 -0
- package/dist/tools/remember.js +67 -0
- package/dist/tools/scope-check.js +30 -0
- package/dist/tools/search-history.js +64 -0
- package/dist/tools/subagent.js +142 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/user-input.js +123 -0
- package/dist/tools/web-browse.js +57 -0
- package/dist/tools/web-fetch.js +72 -0
- package/dist/tools/web-search.js +59 -0
- package/dist/tools/write-file.js +80 -0
- package/dist/ui/box.js +81 -0
- package/dist/ui/colors.js +4 -0
- package/dist/ui/diff.js +185 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/md-formatter.js +212 -0
- package/dist/ui/output.js +13 -0
- package/dist/ui/renderer.js +141 -0
- package/dist/ui/spinner.js +70 -0
- package/dist/ui/table.js +144 -0
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1888,6 +1888,7 @@ var require_commander = __commonJS((exports) => {
|
|
|
1888
1888
|
// src/config/security.ts
|
|
1889
1889
|
function mergeSecurityConfig(userConfig) {
|
|
1890
1890
|
return {
|
|
1891
|
+
enabled: userConfig?.enabled ?? DEFAULT_SECURITY_CONFIG.enabled,
|
|
1891
1892
|
bash: {
|
|
1892
1893
|
...DEFAULT_SECURITY_CONFIG.bash,
|
|
1893
1894
|
...userConfig?.bash
|
|
@@ -1923,7 +1924,9 @@ function mergeSecurityConfig(userConfig) {
|
|
|
1923
1924
|
var DEFAULT_SECURITY_CONFIG;
|
|
1924
1925
|
var init_security = __esm(() => {
|
|
1925
1926
|
DEFAULT_SECURITY_CONFIG = {
|
|
1927
|
+
enabled: false,
|
|
1926
1928
|
bash: {
|
|
1929
|
+
enabled: false,
|
|
1927
1930
|
blacklist: [
|
|
1928
1931
|
"rm",
|
|
1929
1932
|
"dd",
|
|
@@ -1985,6 +1988,7 @@ var init_security = __esm(() => {
|
|
|
1985
1988
|
dangerousOperators: [">", ">>", "2>", "2>>", "`"]
|
|
1986
1989
|
},
|
|
1987
1990
|
paths: {
|
|
1991
|
+
enabled: false,
|
|
1988
1992
|
denied: [
|
|
1989
1993
|
".git/",
|
|
1990
1994
|
"node_modules/",
|
|
@@ -2004,6 +2008,7 @@ var init_security = __esm(() => {
|
|
|
2004
2008
|
allowed: []
|
|
2005
2009
|
},
|
|
2006
2010
|
network: {
|
|
2011
|
+
enabled: false,
|
|
2007
2012
|
deniedDomains: ["localhost", "127.0.0.1", "::1"],
|
|
2008
2013
|
allowedDomains: [],
|
|
2009
2014
|
requestTimeout: 15000,
|
|
@@ -2016,7 +2021,7 @@ var init_security = __esm(() => {
|
|
|
2016
2021
|
maxParallelTasks: 5
|
|
2017
2022
|
},
|
|
2018
2023
|
contentScan: {
|
|
2019
|
-
enabled:
|
|
2024
|
+
enabled: false,
|
|
2020
2025
|
dangerousPatterns: [
|
|
2021
2026
|
/eval\(/,
|
|
2022
2027
|
/new Function\(/,
|
|
@@ -2305,6 +2310,7 @@ Command: {command}`,
|
|
|
2305
2310
|
"tool.friendly.process_kill": "Stopping process",
|
|
2306
2311
|
"plan.no_steps": "No plan steps specified. Provide concrete steps with files and commands.",
|
|
2307
2312
|
"plan.created": "Plan created: {title} ({steps} steps)",
|
|
2313
|
+
"plan.coverage_warning": "Plan may be missing required files from the task: {missing}. Add steps covering them.",
|
|
2308
2314
|
"plan.step_done": "Step {n}/{total}: {description} ✓",
|
|
2309
2315
|
"plan.complete": "Task complete: {summary}",
|
|
2310
2316
|
"plan.title_steps": 'Plan "{title}" created with {count} steps',
|
|
@@ -2548,16 +2554,15 @@ Available commands:`,
|
|
|
2548
2554
|
{content}
|
|
2549
2555
|
|
|
2550
2556
|
Use this knowledge to answer the user's question.`,
|
|
2551
|
-
"exec.stuck": "
|
|
2552
|
-
"exec.stuck_recovery": '
|
|
2553
|
-
"exec.tool_errors": "Tool {tool} failed {count} times.
|
|
2554
|
-
"exec.tool_errors_recovery": "
|
|
2555
|
-
"exec.repetitive_tool": "
|
|
2556
|
-
"exec.consecutive_failures_recovery": "
|
|
2557
|
-
"exec.
|
|
2558
|
-
"exec.
|
|
2559
|
-
"exec.
|
|
2560
|
-
"exec.off_track": `[⚠ OFF PLAN: step {stepId} is "{description}", but you're using {tool} on a different path. Return to the current step immediately.]`,
|
|
2557
|
+
"exec.stuck": "No progress on step {stepId} ({description}) for {iterations} iterations.",
|
|
2558
|
+
"exec.stuck_recovery": 'Step {stepId} — "{description}" — has had no progress for {iterations} iterations. Try a different approach: review what this step needs, check if dependencies are installed, or create files directly via write_file instead of shell commands. After completing the step call plan update step={stepId} status=done.',
|
|
2559
|
+
"exec.tool_errors": "Tool {tool} failed {count} times. Consider using a different tool.",
|
|
2560
|
+
"exec.tool_errors_recovery": "Tool {tool} has failed {count} times in a row. Try an alternative: create files directly via write_file, use a different command, or if nothing works — skip this step via plan update step=N status=skipped with a note explaining why.",
|
|
2561
|
+
"exec.repetitive_tool": "Called {tool} {count} times with identical arguments and result. Try a different approach — create files directly, change arguments, or check process status via process_log.",
|
|
2562
|
+
"exec.consecutive_failures_recovery": "{count} consecutive tool failures. Create files directly via write_file instead of terminal commands. Check that dependencies are installed (npm install). Do not run build/tests until all files are created.",
|
|
2563
|
+
"exec.plan_warning": 'Current plan step {step} is "{description}", but {tool} is being called for files outside this step. Complete the current step first, then call plan update step={step} status=done before moving to the next step.',
|
|
2564
|
+
"exec.plan_blocked": "{max} consecutive calls outside the current step. Finish step {step} before proceeding — other steps should wait until this one is complete.",
|
|
2565
|
+
"exec.off_track": 'Step {stepId} — "{description}" — but you are using {tool} on a different path. Return to the current step.',
|
|
2561
2566
|
"exec.step_gate_deps": '[⚠ Step {step} "{description}": dependencies are not installed. First run the install command (npm install, pip install, etc.). Verify the lock file or dependency directory exists.]',
|
|
2562
2567
|
"exec.step_gate_empty": "[⚠ Step {step}: files exist but appear empty: {files}. Add real code to these files before advancing to the next step.]",
|
|
2563
2568
|
"exec.step_gate_ok": '[✓] Step {step} completed and verified. MOVING to step {nextStep}: "{nextDesc}". Work ONLY on this step.',
|
|
@@ -2566,6 +2571,14 @@ Use this knowledge to answer the user's question.`,
|
|
|
2566
2571
|
"exec.audit_fail": "[✗] Task incomplete: {done}/{total} steps done, {files} files missing",
|
|
2567
2572
|
"exec.audit_fail_typecheck": "[✗] Task incomplete: {done}/{total} steps done, {missing} files missing, typecheck error: {typeError}",
|
|
2568
2573
|
"exec.audit_incomplete": "[⚠ Final audit incomplete: {summary}. Task is NOT finished — continue working. Remaining steps: {steps}]",
|
|
2574
|
+
"exec.mass_edit_warning": "⚠️ Plan affects {count} files — review the full list before proceeding.",
|
|
2575
|
+
"exec.escalation": `
|
|
2576
|
+
|
|
2577
|
+
⚠️ Agent stuck on step {stepId} ({description}). Escalating to user — please provide guidance.`,
|
|
2578
|
+
"exec.hints": `
|
|
2579
|
+
[Hints]
|
|
2580
|
+
{hints}`,
|
|
2581
|
+
"exec.file_rewrite_warning": "⚠️ File {file} has been rewritten {count} times. Consider a different approach — the current fix strategy is not working.",
|
|
2569
2582
|
"hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
|
|
2570
2583
|
"hall.short_response": "Response too short or empty",
|
|
2571
2584
|
"hall.repetitive": "Response too repetitive ({pct}% overlap)",
|
|
@@ -2643,7 +2656,12 @@ Use this knowledge to answer the user's question.`,
|
|
|
2643
2656
|
"tool.recall.empty": 'Nothing found for "{query}"',
|
|
2644
2657
|
"tool.recall.no_memory": "Memory is empty",
|
|
2645
2658
|
"tool.recall.search_results": `{category} results:
|
|
2646
|
-
{results}
|
|
2659
|
+
{results}`,
|
|
2660
|
+
"ctx.compactions": "compactions: {count}",
|
|
2661
|
+
"ctx.quality": "quality: {percent}%",
|
|
2662
|
+
"ctx.delta_pos": "ctx +{tokens}",
|
|
2663
|
+
"ctx.delta_neg": "ctx -{tokens} ↓",
|
|
2664
|
+
"ctx.delta_zero": "ctx ±0"
|
|
2647
2665
|
};
|
|
2648
2666
|
});
|
|
2649
2667
|
|
|
@@ -2803,6 +2821,7 @@ var init_ru = __esm(() => {
|
|
|
2803
2821
|
"tool.friendly.process_kill": "Остановка процесса",
|
|
2804
2822
|
"plan.no_steps": "Не указаны шаги плана. Укажите конкретные шаги с файлами и командами.",
|
|
2805
2823
|
"plan.created": "План создан: {title} ({steps} шагов)",
|
|
2824
|
+
"plan.coverage_warning": "План может не покрывать требуемые файлы из постановки: {missing}. Добавьте шаги, покрывающие их.",
|
|
2806
2825
|
"plan.step_done": "Шаг {n}/{total}: {description} ✓",
|
|
2807
2826
|
"plan.complete": "Задача выполнена: {summary}",
|
|
2808
2827
|
"plan.title_steps": 'План "{title}" создан с {count} шагами',
|
|
@@ -3046,16 +3065,15 @@ var init_ru = __esm(() => {
|
|
|
3046
3065
|
{content}
|
|
3047
3066
|
|
|
3048
3067
|
Используйте эти знания для ответа на вопрос пользователя.`,
|
|
3049
|
-
"exec.stuck": "
|
|
3050
|
-
"exec.stuck_recovery": '
|
|
3051
|
-
"exec.tool_errors": "Инструмент {tool}
|
|
3052
|
-
"exec.tool_errors_recovery": "
|
|
3053
|
-
"exec.repetitive_tool": "
|
|
3054
|
-
"exec.consecutive_failures_recovery": "
|
|
3055
|
-
"exec.
|
|
3056
|
-
"exec.
|
|
3057
|
-
"exec.
|
|
3058
|
-
"exec.off_track": '[⚠ ВНЕ ПЛАНА: шаг {stepId} — "{description}", но вы используете {tool} для другого пути. Вернитесь к текущему шагу немедленно.]',
|
|
3068
|
+
"exec.stuck": "Нет прогресса на шаге {stepId} ({description}) — {iterations} итераций.",
|
|
3069
|
+
"exec.stuck_recovery": 'Шаг {stepId} — "{description}" — без прогресса уже {iterations} итераций. Попробуйте другой подход: проверьте что нужно для этого шага, установлены ли зависимости, или создайте файлы напрямую через write_file вместо команд терминала. После завершения шага вызовите plan update step={stepId} status=done.',
|
|
3070
|
+
"exec.tool_errors": "Инструмент {tool} упал {count} раз. Попробуйте другой инструмент.",
|
|
3071
|
+
"exec.tool_errors_recovery": "Инструмент {tool} упал {count} раз подряд. Попробуйте альтернативу: создайте файлы напрямую через write_file, используйте другую команду, или пропустите этот шаг через plan update step=N status=skipped с пометкой почему.",
|
|
3072
|
+
"exec.repetitive_tool": "Инструмент {tool} вызван {count} раз с одинаковыми аргументами и результатом. Попробуйте другой подход — создайте файлы напрямую, измените аргументы или проверьте статус процесса через process_log.",
|
|
3073
|
+
"exec.consecutive_failures_recovery": "{count} инструментов подряд упали. Создавайте файлы напрямую через write_file вместо команд терминала. Проверьте что зависимости установлены (npm install). Не запускайте сборку/тесты пока не созданы все файлы.",
|
|
3074
|
+
"exec.plan_warning": 'Текущий шаг плана {step} — "{description}", но вызывается {tool} для файлов вне этого шага. Завершите текущий шаг, вызовите plan update step={step} status=done, затем переходите к следующему.',
|
|
3075
|
+
"exec.plan_blocked": "{max} вызовов подряд вне текущего шага. Завершите шаг {step} — остальные шаги ждут пока этот не будет выполнен.",
|
|
3076
|
+
"exec.off_track": 'Шаг {stepId} — "{description}", но используется {tool} для другого пути. Вернитесь к текущему шагу.',
|
|
3059
3077
|
"exec.step_gate_deps": '[⚠ Шаг {step} "{description}": зависимости не установлены. Сначала выполните команду установки (npm install, pip install и т.д.). Проверьте что lock-файл или директория зависимостей существует.]',
|
|
3060
3078
|
"exec.step_gate_empty": "[⚠ Шаг {step}: файлы существуют, но выглядят пустыми: {files}. Добавьте реальный код в эти файлы перед тем как переходить к следующему шагу.]",
|
|
3061
3079
|
"exec.step_gate_ok": '[✓] Шаг {step} завершён и проверен. ПЕРЕХОДИМ к шагу {nextStep}: "{nextDesc}". Работайте ТОЛЬКО над этим шагом.',
|
|
@@ -3064,6 +3082,14 @@ var init_ru = __esm(() => {
|
|
|
3064
3082
|
"exec.audit_fail": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов отсутствует",
|
|
3065
3083
|
"exec.audit_fail_typecheck": "[✗] Задача не выполнена: {done}/{total} шагов, {missing} файлов отсутствует, ошибка typecheck: {typeError}",
|
|
3066
3084
|
"exec.audit_incomplete": "[⚠ Финальная проверка не пройдена: {summary}. Задача НЕ завершена — продолжайте работу. Оставшиеся шаги: {steps}]",
|
|
3085
|
+
"exec.mass_edit_warning": "⚠️ План затрагивает {count} файлов — проверьте полный список перед продолжением.",
|
|
3086
|
+
"exec.escalation": `
|
|
3087
|
+
|
|
3088
|
+
⚠️ Агент застрял на шаге {stepId} ({description}). Эскалация к пользователю — пожалуйста, подскажите как действовать.`,
|
|
3089
|
+
"exec.hints": `
|
|
3090
|
+
[Подсказки]
|
|
3091
|
+
{hints}`,
|
|
3092
|
+
"exec.file_rewrite_warning": "⚠️ Файл {file} был перезаписан {count} раз. Попробуйте другой подход — текущая стратегия исправлений не работает.",
|
|
3067
3093
|
"hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
|
|
3068
3094
|
"hall.short_response": "Слишком короткий или пустой ответ",
|
|
3069
3095
|
"hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
|
|
@@ -3141,7 +3167,12 @@ var init_ru = __esm(() => {
|
|
|
3141
3167
|
"tool.recall.empty": 'Ничего не найдено по "{query}"',
|
|
3142
3168
|
"tool.recall.no_memory": "Память пуста",
|
|
3143
3169
|
"tool.recall.search_results": `Результаты {category}:
|
|
3144
|
-
{results}
|
|
3170
|
+
{results}`,
|
|
3171
|
+
"ctx.compactions": "сжатий: {count}",
|
|
3172
|
+
"ctx.quality": "качество: {percent}%",
|
|
3173
|
+
"ctx.delta_pos": "контекст +{tokens}",
|
|
3174
|
+
"ctx.delta_neg": "контекст -{tokens} ↓",
|
|
3175
|
+
"ctx.delta_zero": "контекст ±0"
|
|
3145
3176
|
};
|
|
3146
3177
|
});
|
|
3147
3178
|
|
|
@@ -4936,12 +4967,33 @@ function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
4936
4967
|
const baseResolved = resolve(baseDir);
|
|
4937
4968
|
const targetResolved = resolve(baseDir, normalize(targetPath));
|
|
4938
4969
|
if (!targetResolved.startsWith(baseResolved)) {
|
|
4939
|
-
return {
|
|
4970
|
+
return {
|
|
4971
|
+
allowed: false,
|
|
4972
|
+
reason: "Path is outside the base working directory"
|
|
4973
|
+
};
|
|
4974
|
+
}
|
|
4975
|
+
if (!securityConfig?.enabled) {
|
|
4976
|
+
if (scope) {
|
|
4977
|
+
const isReadScope = scope.read_only_files.some((f) => targetResolved.startsWith(resolve(baseDir, f)));
|
|
4978
|
+
const isWriteScope = scope.allowed_files.some((f) => targetResolved.startsWith(resolve(baseDir, f)));
|
|
4979
|
+
if (isWriteScope)
|
|
4980
|
+
return { allowed: true };
|
|
4981
|
+
if (isReadScope)
|
|
4982
|
+
return { allowed: true, reason: "Read-only file" };
|
|
4983
|
+
return {
|
|
4984
|
+
allowed: false,
|
|
4985
|
+
reason: "Path is not within the allowed scope for this sub-agent"
|
|
4986
|
+
};
|
|
4987
|
+
}
|
|
4988
|
+
return { allowed: true };
|
|
4940
4989
|
}
|
|
4941
4990
|
if (securityConfig?.denied?.length) {
|
|
4942
4991
|
for (const pattern of securityConfig.denied) {
|
|
4943
4992
|
if (matchesGlobPattern(targetResolved, baseDir, pattern)) {
|
|
4944
|
-
return {
|
|
4993
|
+
return {
|
|
4994
|
+
allowed: false,
|
|
4995
|
+
reason: `Path matches denied pattern: ${pattern}`
|
|
4996
|
+
};
|
|
4945
4997
|
}
|
|
4946
4998
|
}
|
|
4947
4999
|
}
|
|
@@ -4954,7 +5006,10 @@ function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
4954
5006
|
}
|
|
4955
5007
|
}
|
|
4956
5008
|
if (!isAllowed) {
|
|
4957
|
-
return {
|
|
5009
|
+
return {
|
|
5010
|
+
allowed: false,
|
|
5011
|
+
reason: "Path does not match any allowed pattern"
|
|
5012
|
+
};
|
|
4958
5013
|
}
|
|
4959
5014
|
}
|
|
4960
5015
|
if (!scope) {
|
|
@@ -4975,12 +5030,30 @@ function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
4975
5030
|
const baseResolved = resolve(baseDir);
|
|
4976
5031
|
const targetResolved = resolve(baseDir, normalize(targetPath));
|
|
4977
5032
|
if (!targetResolved.startsWith(baseResolved)) {
|
|
4978
|
-
return {
|
|
5033
|
+
return {
|
|
5034
|
+
allowed: false,
|
|
5035
|
+
reason: "Path is outside the base working directory"
|
|
5036
|
+
};
|
|
5037
|
+
}
|
|
5038
|
+
if (!securityConfig?.enabled) {
|
|
5039
|
+
if (scope) {
|
|
5040
|
+
const isWriteScope = scope.allowed_files.some((f) => targetResolved.startsWith(resolve(baseDir, f)));
|
|
5041
|
+
if (isWriteScope)
|
|
5042
|
+
return { allowed: true };
|
|
5043
|
+
return {
|
|
5044
|
+
allowed: false,
|
|
5045
|
+
reason: "Path is not within the allowed scope for this sub-agent"
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
return { allowed: true };
|
|
4979
5049
|
}
|
|
4980
5050
|
if (securityConfig?.denied?.length) {
|
|
4981
5051
|
for (const pattern of securityConfig.denied) {
|
|
4982
5052
|
if (matchesGlobPattern(targetResolved, baseDir, pattern)) {
|
|
4983
|
-
return {
|
|
5053
|
+
return {
|
|
5054
|
+
allowed: false,
|
|
5055
|
+
reason: `Path matches denied pattern: ${pattern}`
|
|
5056
|
+
};
|
|
4984
5057
|
}
|
|
4985
5058
|
}
|
|
4986
5059
|
}
|
|
@@ -4993,7 +5066,10 @@ function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
4993
5066
|
}
|
|
4994
5067
|
}
|
|
4995
5068
|
if (!isAllowed) {
|
|
4996
|
-
return {
|
|
5069
|
+
return {
|
|
5070
|
+
allowed: false,
|
|
5071
|
+
reason: "Path does not match any allowed pattern"
|
|
5072
|
+
};
|
|
4997
5073
|
}
|
|
4998
5074
|
}
|
|
4999
5075
|
if (!scope) {
|
|
@@ -6417,9 +6493,15 @@ function containsOperator(command, op) {
|
|
|
6417
6493
|
return new RegExp(escaped).test(command);
|
|
6418
6494
|
}
|
|
6419
6495
|
function isCommandAllowed(command, securityConfig) {
|
|
6420
|
-
|
|
6496
|
+
if (!securityConfig?.enabled) {
|
|
6497
|
+
return { allowed: true, sanitizedCommand: command };
|
|
6498
|
+
}
|
|
6499
|
+
let config = securityConfig;
|
|
6421
6500
|
if (!config.blacklist || !Array.isArray(config.blacklist)) {
|
|
6422
|
-
config =
|
|
6501
|
+
config = {
|
|
6502
|
+
...FALLBACK_BASH_CONFIG,
|
|
6503
|
+
enabled: config.enabled ?? FALLBACK_BASH_CONFIG.enabled
|
|
6504
|
+
};
|
|
6423
6505
|
}
|
|
6424
6506
|
const trimmedCommand = command.trim();
|
|
6425
6507
|
if (!trimmedCommand) {
|
|
@@ -6508,7 +6590,21 @@ var FALLBACK_BASH_CONFIG, DEFAULT_BASH_CONFIG;
|
|
|
6508
6590
|
var init_command_validator = __esm(() => {
|
|
6509
6591
|
init_security();
|
|
6510
6592
|
FALLBACK_BASH_CONFIG = {
|
|
6511
|
-
|
|
6593
|
+
enabled: false,
|
|
6594
|
+
blacklist: [
|
|
6595
|
+
"rm",
|
|
6596
|
+
"dd",
|
|
6597
|
+
"chmod",
|
|
6598
|
+
"wget",
|
|
6599
|
+
"curl",
|
|
6600
|
+
"scp",
|
|
6601
|
+
"ssh",
|
|
6602
|
+
"nc",
|
|
6603
|
+
"netcat",
|
|
6604
|
+
"powershell",
|
|
6605
|
+
"pwsh",
|
|
6606
|
+
"cmd"
|
|
6607
|
+
],
|
|
6512
6608
|
whitelist: [],
|
|
6513
6609
|
blockDangerousFlags: false,
|
|
6514
6610
|
dangerousFlags: ["--force", "-rf", "--no-preserve-root"],
|
|
@@ -8729,7 +8825,133 @@ var init_agent_moe = __esm(() => {
|
|
|
8729
8825
|
init_verifier();
|
|
8730
8826
|
});
|
|
8731
8827
|
|
|
8828
|
+
// src/modules/memory/search.ts
|
|
8829
|
+
import { readFileSync as readFileSync9, existsSync as existsSync18 } from "fs";
|
|
8830
|
+
import { join as join8 } from "path";
|
|
8831
|
+
|
|
8832
|
+
class MemorySearch {
|
|
8833
|
+
memoryDir;
|
|
8834
|
+
constructor(memoryDir) {
|
|
8835
|
+
this.memoryDir = memoryDir;
|
|
8836
|
+
}
|
|
8837
|
+
query(query) {
|
|
8838
|
+
const results = [];
|
|
8839
|
+
const lowerQuery = query.toLowerCase();
|
|
8840
|
+
for (const name of MEMORY_FILES) {
|
|
8841
|
+
const path = join8(this.memoryDir, `${name}.md`);
|
|
8842
|
+
if (!existsSync18(path))
|
|
8843
|
+
continue;
|
|
8844
|
+
const content = readFileSync9(path, "utf-8");
|
|
8845
|
+
const lines = content.split(`
|
|
8846
|
+
`);
|
|
8847
|
+
for (const line of lines) {
|
|
8848
|
+
if (line.toLowerCase().includes(lowerQuery)) {
|
|
8849
|
+
results.push({ file: name, match: line.trim() });
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8852
|
+
}
|
|
8853
|
+
const prefsPath = join8(this.memoryDir, "preferences.json");
|
|
8854
|
+
if (existsSync18(prefsPath)) {
|
|
8855
|
+
try {
|
|
8856
|
+
const prefs = JSON.parse(readFileSync9(prefsPath, "utf-8"));
|
|
8857
|
+
for (const [key, value] of Object.entries(prefs)) {
|
|
8858
|
+
const searchStr = `${key}=${value}`;
|
|
8859
|
+
if (searchStr.toLowerCase().includes(lowerQuery)) {
|
|
8860
|
+
results.push({ file: "preferences", match: `${key} = ${value}` });
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8863
|
+
} catch {}
|
|
8864
|
+
}
|
|
8865
|
+
return results;
|
|
8866
|
+
}
|
|
8867
|
+
}
|
|
8868
|
+
var MEMORY_FILES;
|
|
8869
|
+
var init_search = __esm(() => {
|
|
8870
|
+
MEMORY_FILES = ["conventions", "decisions", "errors", "facts"];
|
|
8871
|
+
});
|
|
8872
|
+
|
|
8873
|
+
// src/modules/memory/store.ts
|
|
8874
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, appendFileSync as appendFileSync4, existsSync as existsSync19, mkdirSync as mkdirSync10 } from "fs";
|
|
8875
|
+
import { join as join9 } from "path";
|
|
8876
|
+
|
|
8877
|
+
class MemoryStore {
|
|
8878
|
+
memoryDir;
|
|
8879
|
+
constructor(memoryDir) {
|
|
8880
|
+
this.memoryDir = memoryDir;
|
|
8881
|
+
this.ensureDir();
|
|
8882
|
+
for (const name of MEMORY_FILES2) {
|
|
8883
|
+
const path = join9(this.memoryDir, `${name}.md`);
|
|
8884
|
+
if (!existsSync19(path)) {
|
|
8885
|
+
writeFileSync8(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}
|
|
8886
|
+
|
|
8887
|
+
`, "utf-8");
|
|
8888
|
+
}
|
|
8889
|
+
}
|
|
8890
|
+
}
|
|
8891
|
+
ensureDir() {
|
|
8892
|
+
if (!existsSync19(this.memoryDir)) {
|
|
8893
|
+
mkdirSync10(this.memoryDir, { recursive: true });
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
read(name) {
|
|
8897
|
+
const path = join9(this.memoryDir, `${name}.md`);
|
|
8898
|
+
if (!existsSync19(path))
|
|
8899
|
+
return "";
|
|
8900
|
+
return readFileSync10(path, "utf-8");
|
|
8901
|
+
}
|
|
8902
|
+
append(name, entry) {
|
|
8903
|
+
const path = join9(this.memoryDir, `${name}.md`);
|
|
8904
|
+
const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
8905
|
+
const formatted = `- **${timestamp}** — ${entry}
|
|
8906
|
+
`;
|
|
8907
|
+
appendFileSync4(path, formatted, "utf-8");
|
|
8908
|
+
}
|
|
8909
|
+
search(query) {
|
|
8910
|
+
const searchModule = new MemorySearch(this.memoryDir);
|
|
8911
|
+
return searchModule.query(query);
|
|
8912
|
+
}
|
|
8913
|
+
prefsPath() {
|
|
8914
|
+
return join9(this.memoryDir, "preferences.json");
|
|
8915
|
+
}
|
|
8916
|
+
getPreferences() {
|
|
8917
|
+
const path = this.prefsPath();
|
|
8918
|
+
if (!existsSync19(path))
|
|
8919
|
+
return {};
|
|
8920
|
+
try {
|
|
8921
|
+
return JSON.parse(readFileSync10(path, "utf-8"));
|
|
8922
|
+
} catch {
|
|
8923
|
+
return {};
|
|
8924
|
+
}
|
|
8925
|
+
}
|
|
8926
|
+
setPreference(key, value) {
|
|
8927
|
+
const prefs = this.getPreferences();
|
|
8928
|
+
prefs[key] = value;
|
|
8929
|
+
writeFileSync8(this.prefsPath(), JSON.stringify(prefs, null, 2), "utf-8");
|
|
8930
|
+
}
|
|
8931
|
+
deletePreference(key) {
|
|
8932
|
+
const prefs = this.getPreferences();
|
|
8933
|
+
if (!(key in prefs))
|
|
8934
|
+
return false;
|
|
8935
|
+
delete prefs[key];
|
|
8936
|
+
writeFileSync8(this.prefsPath(), JSON.stringify(prefs, null, 2), "utf-8");
|
|
8937
|
+
return true;
|
|
8938
|
+
}
|
|
8939
|
+
appendRule(category, pattern, cause, solution) {
|
|
8940
|
+
const entry = `**pattern:** ${pattern}
|
|
8941
|
+
**cause:** ${cause}
|
|
8942
|
+
**solution:** ${solution}`;
|
|
8943
|
+
this.append(category, entry);
|
|
8944
|
+
}
|
|
8945
|
+
}
|
|
8946
|
+
var MEMORY_FILES2;
|
|
8947
|
+
var init_store = __esm(() => {
|
|
8948
|
+
init_search();
|
|
8949
|
+
MEMORY_FILES2 = ["conventions", "decisions", "errors", "facts"];
|
|
8950
|
+
});
|
|
8951
|
+
|
|
8732
8952
|
// src/core/agent.ts
|
|
8953
|
+
import { join as join10 } from "path";
|
|
8954
|
+
|
|
8733
8955
|
class Agent {
|
|
8734
8956
|
deps;
|
|
8735
8957
|
systemPromptAdded = false;
|
|
@@ -8797,7 +9019,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
8797
9019
|
pluginManager,
|
|
8798
9020
|
contextManager,
|
|
8799
9021
|
logger,
|
|
8800
|
-
sessionManager
|
|
9022
|
+
sessionManager,
|
|
9023
|
+
baseDir
|
|
8801
9024
|
} = this.deps;
|
|
8802
9025
|
const slog = new SessionLogger(sessionManager);
|
|
8803
9026
|
if (sessionManager && !sessionManager.getActive()) {
|
|
@@ -8839,7 +9062,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
8839
9062
|
contextManager,
|
|
8840
9063
|
hallucinationDetector,
|
|
8841
9064
|
logger,
|
|
8842
|
-
sessionManager
|
|
9065
|
+
sessionManager,
|
|
9066
|
+
baseDir
|
|
8843
9067
|
} = this.deps;
|
|
8844
9068
|
const slog = new SessionLogger(sessionManager);
|
|
8845
9069
|
let iteration = 0;
|
|
@@ -8851,6 +9075,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
8851
9075
|
const MAX_HALLUCINATION_RETRIES = 3;
|
|
8852
9076
|
let consecutiveToolFailures = 0;
|
|
8853
9077
|
const MAX_CONSECUTIVE_TOOL_FAILURES = 5;
|
|
9078
|
+
let auditRetries = 0;
|
|
9079
|
+
const MAX_AUDIT_RETRIES = 3;
|
|
8854
9080
|
while (iteration < config.maxToolIterations && !this.shutdownRequested) {
|
|
8855
9081
|
iteration++;
|
|
8856
9082
|
pluginManager.runOnBeforeThink({
|
|
@@ -8987,6 +9213,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
8987
9213
|
pluginManager.runOnToolStart({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments });
|
|
8988
9214
|
onTool?.({ type: "start", tool: call.name, args: call.arguments });
|
|
8989
9215
|
slog.logToolCall(call, iteration);
|
|
9216
|
+
const tokensBeforeTool = contextManager.getEstimatedTokens();
|
|
8990
9217
|
const result = await toolExecutor.execute(call);
|
|
8991
9218
|
const duration = Date.now() - startTime;
|
|
8992
9219
|
if (!result.success)
|
|
@@ -9006,12 +9233,14 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9006
9233
|
` + result.diff + `
|
|
9007
9234
|
`);
|
|
9008
9235
|
}
|
|
9236
|
+
const tokensAfterTool = contextManager.getEstimatedTokens();
|
|
9009
9237
|
onTool?.({
|
|
9010
9238
|
type: "end",
|
|
9011
9239
|
tool: call.name,
|
|
9012
9240
|
args: call.arguments,
|
|
9013
9241
|
duration,
|
|
9014
|
-
error: !result.success
|
|
9242
|
+
error: !result.success,
|
|
9243
|
+
ctxDelta: tokensAfterTool - tokensBeforeTool
|
|
9015
9244
|
});
|
|
9016
9245
|
const currentTokens2 = contextManager.getEstimatedTokens();
|
|
9017
9246
|
const budget3 = contextManager.getBudget();
|
|
@@ -9020,7 +9249,9 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9020
9249
|
role: "tool",
|
|
9021
9250
|
content: truncatedOutput,
|
|
9022
9251
|
name: call.name,
|
|
9023
|
-
tool_call_id: call.id
|
|
9252
|
+
tool_call_id: call.id,
|
|
9253
|
+
success: result.success,
|
|
9254
|
+
arguments: call.arguments
|
|
9024
9255
|
});
|
|
9025
9256
|
summaries.push(`[Tool: ${call.name} (${JSON.stringify(call.arguments)}) → ${truncatedOutput.slice(0, 200)}]`);
|
|
9026
9257
|
if (config.session.autoSave) {
|
|
@@ -9048,6 +9279,14 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9048
9279
|
content: `<system-summary>${recoveryMsg}
|
|
9049
9280
|
${taskReminder}</system-summary>`
|
|
9050
9281
|
});
|
|
9282
|
+
if (sessionManager) {
|
|
9283
|
+
const activeSession = sessionManager.getActiveMeta();
|
|
9284
|
+
if (activeSession) {
|
|
9285
|
+
const memDir = join10(baseDir, ".mma", "memory");
|
|
9286
|
+
const memStore = new MemoryStore(memDir);
|
|
9287
|
+
memStore.appendRule("errors", `${consecutiveToolFailures} consecutive tool failures`, "Multiple tools failing suggests environment or configuration issue", "Check dependencies, verify file paths, try write_file directly instead of shell commands");
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9051
9290
|
}
|
|
9052
9291
|
contextManager.addMessage({
|
|
9053
9292
|
role: "user",
|
|
@@ -9061,8 +9300,11 @@ ${taskReminder}</system-summary>`
|
|
|
9061
9300
|
const filled = Math.round(ctxPct / 100 * barLen);
|
|
9062
9301
|
const ctxBar = pc.green("█".repeat(filled)) + pc.dim("░".repeat(barLen - filled));
|
|
9063
9302
|
const pctColor = ctxPct >= 75 ? pc.yellow : pc.dim;
|
|
9303
|
+
const compCount = contextManager.getCompactionCount();
|
|
9304
|
+
const quality = contextManager.getQuality();
|
|
9305
|
+
const qualityColor = quality >= 70 ? pc.green : quality >= 40 ? pc.yellow : pc.red;
|
|
9064
9306
|
onMeta?.(`
|
|
9065
|
-
${ctxBar} ${pctColor(`${ctxPct}%`)} ${pc.dim(`ctx: ${ctxTokens}/${ctxBudget.history}`)}
|
|
9307
|
+
${ctxBar} ${pctColor(`${ctxPct}%`)} ${pc.dim(`ctx: ${ctxTokens}/${ctxBudget.history}`)} ${pc.dim(`compactions: ${compCount}`)} ${qualityColor(`quality: ${quality}%`)}
|
|
9066
9308
|
`);
|
|
9067
9309
|
continue;
|
|
9068
9310
|
}
|
|
@@ -9136,7 +9378,9 @@ ${taskReminder}</system-summary>`
|
|
|
9136
9378
|
})}</system-summary>`
|
|
9137
9379
|
});
|
|
9138
9380
|
slog.logAudit(audit.summary, iteration);
|
|
9139
|
-
|
|
9381
|
+
auditRetries++;
|
|
9382
|
+
if (auditRetries >= MAX_AUDIT_RETRIES || iteration >= config.maxToolIterations - 1) {
|
|
9383
|
+
logger.warn(`Final audit still incomplete after ${auditRetries} retries — finishing anyway`);
|
|
9140
9384
|
break;
|
|
9141
9385
|
}
|
|
9142
9386
|
continue;
|
|
@@ -9157,7 +9401,9 @@ ${taskReminder}</system-summary>`
|
|
|
9157
9401
|
contextLimit: budget.history,
|
|
9158
9402
|
promptTokens: apiPromptTokens,
|
|
9159
9403
|
completionTokens: apiCompletionTokens,
|
|
9160
|
-
totalTokens: apiPromptTokens + apiCompletionTokens
|
|
9404
|
+
totalTokens: apiPromptTokens + apiCompletionTokens,
|
|
9405
|
+
compactionCount: contextManager.getCompactionCount(),
|
|
9406
|
+
contextQuality: contextManager.getQuality()
|
|
9161
9407
|
};
|
|
9162
9408
|
}
|
|
9163
9409
|
return {
|
|
@@ -9168,7 +9414,9 @@ ${taskReminder}</system-summary>`
|
|
|
9168
9414
|
contextLimit: budget.history,
|
|
9169
9415
|
promptTokens: apiPromptTokens,
|
|
9170
9416
|
completionTokens: apiCompletionTokens,
|
|
9171
|
-
totalTokens: apiPromptTokens + apiCompletionTokens
|
|
9417
|
+
totalTokens: apiPromptTokens + apiCompletionTokens,
|
|
9418
|
+
compactionCount: contextManager.getCompactionCount(),
|
|
9419
|
+
contextQuality: contextManager.getQuality()
|
|
9172
9420
|
};
|
|
9173
9421
|
}
|
|
9174
9422
|
clearContext() {
|
|
@@ -9212,14 +9460,56 @@ var init_agent = __esm(() => {
|
|
|
9212
9460
|
init_prompt_builder();
|
|
9213
9461
|
init_processes();
|
|
9214
9462
|
init_agent_moe();
|
|
9463
|
+
init_store();
|
|
9215
9464
|
});
|
|
9216
9465
|
|
|
9217
9466
|
// src/modules/context/manager.ts
|
|
9467
|
+
function summarizeArgs(args) {
|
|
9468
|
+
if (!args)
|
|
9469
|
+
return "";
|
|
9470
|
+
if (typeof args === "string")
|
|
9471
|
+
return args.slice(0, 80);
|
|
9472
|
+
try {
|
|
9473
|
+
const keys = Object.keys(args);
|
|
9474
|
+
return keys.slice(0, 3).join(", ");
|
|
9475
|
+
} catch {
|
|
9476
|
+
return String(args).slice(0, 80);
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
function truncate(s, max) {
|
|
9480
|
+
if (s.length <= max)
|
|
9481
|
+
return s;
|
|
9482
|
+
return s.slice(0, max - 3) + "...";
|
|
9483
|
+
}
|
|
9484
|
+
function extractTriedAndFailed(messages) {
|
|
9485
|
+
const failures = new Map;
|
|
9486
|
+
for (const msg of messages) {
|
|
9487
|
+
if (msg.role === "tool" && msg.name && msg.success === false) {
|
|
9488
|
+
const key = `${msg.name}:${summarizeArgs(msg.arguments)}`;
|
|
9489
|
+
const existing = failures.get(key);
|
|
9490
|
+
const errorText = truncate(typeof msg.content === "string" ? msg.content : getMessageText(msg.content), 100);
|
|
9491
|
+
if (existing) {
|
|
9492
|
+
existing.count++;
|
|
9493
|
+
} else {
|
|
9494
|
+
failures.set(key, {
|
|
9495
|
+
tool: msg.name,
|
|
9496
|
+
args: summarizeArgs(msg.arguments),
|
|
9497
|
+
error: errorText,
|
|
9498
|
+
count: 1
|
|
9499
|
+
});
|
|
9500
|
+
}
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
return Array.from(failures.values()).filter((f) => f.count >= 2);
|
|
9504
|
+
}
|
|
9505
|
+
|
|
9218
9506
|
class ContextManager {
|
|
9219
9507
|
contextWindow;
|
|
9220
9508
|
messages = [];
|
|
9221
9509
|
compactedBlock = null;
|
|
9222
9510
|
iterationsSinceCompaction = 0;
|
|
9511
|
+
compactionCount = 0;
|
|
9512
|
+
peakTokens = 0;
|
|
9223
9513
|
budget;
|
|
9224
9514
|
compactionThreshold;
|
|
9225
9515
|
fileFacts = [];
|
|
@@ -9250,6 +9540,17 @@ class ContextManager {
|
|
|
9250
9540
|
getBudget() {
|
|
9251
9541
|
return { ...this.budget };
|
|
9252
9542
|
}
|
|
9543
|
+
getCompactionCount() {
|
|
9544
|
+
return this.compactionCount;
|
|
9545
|
+
}
|
|
9546
|
+
getIterationsSinceCompaction() {
|
|
9547
|
+
return this.iterationsSinceCompaction;
|
|
9548
|
+
}
|
|
9549
|
+
getQuality() {
|
|
9550
|
+
const freshness = 1 - this.iterationsSinceCompaction / COMPACTION_INTERVAL;
|
|
9551
|
+
const depth = 1 / (1 + this.compactionCount);
|
|
9552
|
+
return Math.round(freshness * depth * 100);
|
|
9553
|
+
}
|
|
9253
9554
|
addMessage(msg) {
|
|
9254
9555
|
if (msg.role === "user" && this.pendingImageParts.length > 0) {
|
|
9255
9556
|
const textPart = {
|
|
@@ -9264,6 +9565,9 @@ class ContextManager {
|
|
|
9264
9565
|
}
|
|
9265
9566
|
this.messages.push(msg);
|
|
9266
9567
|
this.iterationsSinceCompaction++;
|
|
9568
|
+
const tokens = this.getEstimatedTokens();
|
|
9569
|
+
if (tokens > this.peakTokens)
|
|
9570
|
+
this.peakTokens = tokens;
|
|
9267
9571
|
}
|
|
9268
9572
|
addPendingImage(part) {
|
|
9269
9573
|
this.pendingImageParts.push(part);
|
|
@@ -9330,6 +9634,7 @@ class ContextManager {
|
|
|
9330
9634
|
compact() {
|
|
9331
9635
|
if (this.messages.length <= KEEP_LAST_N * 2)
|
|
9332
9636
|
return;
|
|
9637
|
+
this.compactionCount++;
|
|
9333
9638
|
const cutoff = this.messages.length - KEEP_LAST_N * 2;
|
|
9334
9639
|
const oldTurns = this.messages.slice(0, cutoff);
|
|
9335
9640
|
const recentTurns = this.messages.slice(cutoff);
|
|
@@ -9345,6 +9650,13 @@ class ContextManager {
|
|
|
9345
9650
|
if (this.errorFacts.length > 0) {
|
|
9346
9651
|
parts.push(`[Errors: ${this.errorFacts.slice(-3).join("; ")}]`);
|
|
9347
9652
|
}
|
|
9653
|
+
const triedAndFailed = extractTriedAndFailed(this.messages);
|
|
9654
|
+
if (triedAndFailed.length > 0) {
|
|
9655
|
+
const lines = triedAndFailed.map((t2) => `- ${t2.tool}(${t2.args}): ${t2.error} (failed ${t2.count}x)`);
|
|
9656
|
+
parts.push(`[Already tried & failed — do NOT repeat:]
|
|
9657
|
+
${lines.join(`
|
|
9658
|
+
`)}`);
|
|
9659
|
+
}
|
|
9348
9660
|
this.compactedBlock = parts.join(" ");
|
|
9349
9661
|
const summary = {
|
|
9350
9662
|
role: "user",
|
|
@@ -9413,6 +9725,8 @@ class ContextManager {
|
|
|
9413
9725
|
this.messages = [];
|
|
9414
9726
|
this.compactedBlock = null;
|
|
9415
9727
|
this.iterationsSinceCompaction = 0;
|
|
9728
|
+
this.compactionCount = 0;
|
|
9729
|
+
this.peakTokens = 0;
|
|
9416
9730
|
}
|
|
9417
9731
|
getEstimatedTokens() {
|
|
9418
9732
|
return this.messages.reduce((sum, m) => sum + this.estimateMessageTokens(m), 0);
|
|
@@ -9868,7 +10182,7 @@ Partial output: ${result.text}`
|
|
|
9868
10182
|
|
|
9869
10183
|
// src/modules/security/network-validator.ts
|
|
9870
10184
|
function isUrlAllowed(url, securityConfig) {
|
|
9871
|
-
if (!securityConfig) {
|
|
10185
|
+
if (!securityConfig?.enabled) {
|
|
9872
10186
|
return { allowed: true };
|
|
9873
10187
|
}
|
|
9874
10188
|
try {
|
|
@@ -10931,7 +11245,7 @@ ${JSON.stringify(result, null, 2)}`
|
|
|
10931
11245
|
|
|
10932
11246
|
// src/tools/search-history.ts
|
|
10933
11247
|
import * as fs from "fs";
|
|
10934
|
-
import { join as
|
|
11248
|
+
import { join as join11 } from "path";
|
|
10935
11249
|
import { homedir as homedir5 } from "os";
|
|
10936
11250
|
function searchFile(filePath, query, maxResults, results) {
|
|
10937
11251
|
if (!fs.existsSync(filePath))
|
|
@@ -10969,7 +11283,7 @@ var init_search_history = __esm(() => {
|
|
|
10969
11283
|
const query = String(args.query || "").toLowerCase();
|
|
10970
11284
|
const maxResults = Number(args.maxResults) || 5;
|
|
10971
11285
|
const sessionId = args.sessionId ? String(args.sessionId) : null;
|
|
10972
|
-
const sessionDir =
|
|
11286
|
+
const sessionDir = join11(homedir5(), ".mma", "sessions");
|
|
10973
11287
|
const results = [];
|
|
10974
11288
|
try {
|
|
10975
11289
|
if (!fs.existsSync(sessionDir)) {
|
|
@@ -10981,7 +11295,7 @@ var init_search_history = __esm(() => {
|
|
|
10981
11295
|
continue;
|
|
10982
11296
|
if (sessionId && entry.name !== sessionId)
|
|
10983
11297
|
continue;
|
|
10984
|
-
const historyFile =
|
|
11298
|
+
const historyFile = join11(sessionDir, entry.name, "history.jsonl");
|
|
10985
11299
|
searchFile(historyFile, query, maxResults, results);
|
|
10986
11300
|
if (results.length >= maxResults)
|
|
10987
11301
|
break;
|
|
@@ -10998,127 +11312,9 @@ var init_search_history = __esm(() => {
|
|
|
10998
11312
|
};
|
|
10999
11313
|
});
|
|
11000
11314
|
|
|
11001
|
-
// src/modules/memory/search.ts
|
|
11002
|
-
import { readFileSync as readFileSync10, existsSync as existsSync19 } from "fs";
|
|
11003
|
-
import { join as join9 } from "path";
|
|
11004
|
-
|
|
11005
|
-
class MemorySearch {
|
|
11006
|
-
memoryDir;
|
|
11007
|
-
constructor(memoryDir) {
|
|
11008
|
-
this.memoryDir = memoryDir;
|
|
11009
|
-
}
|
|
11010
|
-
query(query) {
|
|
11011
|
-
const results = [];
|
|
11012
|
-
const lowerQuery = query.toLowerCase();
|
|
11013
|
-
for (const name of MEMORY_FILES) {
|
|
11014
|
-
const path = join9(this.memoryDir, `${name}.md`);
|
|
11015
|
-
if (!existsSync19(path))
|
|
11016
|
-
continue;
|
|
11017
|
-
const content = readFileSync10(path, "utf-8");
|
|
11018
|
-
const lines = content.split(`
|
|
11019
|
-
`);
|
|
11020
|
-
for (const line of lines) {
|
|
11021
|
-
if (line.toLowerCase().includes(lowerQuery)) {
|
|
11022
|
-
results.push({ file: name, match: line.trim() });
|
|
11023
|
-
}
|
|
11024
|
-
}
|
|
11025
|
-
}
|
|
11026
|
-
const prefsPath = join9(this.memoryDir, "preferences.json");
|
|
11027
|
-
if (existsSync19(prefsPath)) {
|
|
11028
|
-
try {
|
|
11029
|
-
const prefs = JSON.parse(readFileSync10(prefsPath, "utf-8"));
|
|
11030
|
-
for (const [key, value] of Object.entries(prefs)) {
|
|
11031
|
-
const searchStr = `${key}=${value}`;
|
|
11032
|
-
if (searchStr.toLowerCase().includes(lowerQuery)) {
|
|
11033
|
-
results.push({ file: "preferences", match: `${key} = ${value}` });
|
|
11034
|
-
}
|
|
11035
|
-
}
|
|
11036
|
-
} catch {}
|
|
11037
|
-
}
|
|
11038
|
-
return results;
|
|
11039
|
-
}
|
|
11040
|
-
}
|
|
11041
|
-
var MEMORY_FILES;
|
|
11042
|
-
var init_search = __esm(() => {
|
|
11043
|
-
MEMORY_FILES = ["conventions", "decisions", "errors", "facts"];
|
|
11044
|
-
});
|
|
11045
|
-
|
|
11046
|
-
// src/modules/memory/store.ts
|
|
11047
|
-
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, appendFileSync as appendFileSync4, existsSync as existsSync20, mkdirSync as mkdirSync10 } from "fs";
|
|
11048
|
-
import { join as join10 } from "path";
|
|
11049
|
-
|
|
11050
|
-
class MemoryStore {
|
|
11051
|
-
memoryDir;
|
|
11052
|
-
constructor(memoryDir) {
|
|
11053
|
-
this.memoryDir = memoryDir;
|
|
11054
|
-
this.ensureDir();
|
|
11055
|
-
for (const name of MEMORY_FILES2) {
|
|
11056
|
-
const path = join10(this.memoryDir, `${name}.md`);
|
|
11057
|
-
if (!existsSync20(path)) {
|
|
11058
|
-
writeFileSync8(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}
|
|
11059
|
-
|
|
11060
|
-
`, "utf-8");
|
|
11061
|
-
}
|
|
11062
|
-
}
|
|
11063
|
-
}
|
|
11064
|
-
ensureDir() {
|
|
11065
|
-
if (!existsSync20(this.memoryDir)) {
|
|
11066
|
-
mkdirSync10(this.memoryDir, { recursive: true });
|
|
11067
|
-
}
|
|
11068
|
-
}
|
|
11069
|
-
read(name) {
|
|
11070
|
-
const path = join10(this.memoryDir, `${name}.md`);
|
|
11071
|
-
if (!existsSync20(path))
|
|
11072
|
-
return "";
|
|
11073
|
-
return readFileSync11(path, "utf-8");
|
|
11074
|
-
}
|
|
11075
|
-
append(name, entry) {
|
|
11076
|
-
const path = join10(this.memoryDir, `${name}.md`);
|
|
11077
|
-
const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
11078
|
-
const formatted = `- **${timestamp}** — ${entry}
|
|
11079
|
-
`;
|
|
11080
|
-
appendFileSync4(path, formatted, "utf-8");
|
|
11081
|
-
}
|
|
11082
|
-
search(query) {
|
|
11083
|
-
const searchModule = new MemorySearch(this.memoryDir);
|
|
11084
|
-
return searchModule.query(query);
|
|
11085
|
-
}
|
|
11086
|
-
prefsPath() {
|
|
11087
|
-
return join10(this.memoryDir, "preferences.json");
|
|
11088
|
-
}
|
|
11089
|
-
getPreferences() {
|
|
11090
|
-
const path = this.prefsPath();
|
|
11091
|
-
if (!existsSync20(path))
|
|
11092
|
-
return {};
|
|
11093
|
-
try {
|
|
11094
|
-
return JSON.parse(readFileSync11(path, "utf-8"));
|
|
11095
|
-
} catch {
|
|
11096
|
-
return {};
|
|
11097
|
-
}
|
|
11098
|
-
}
|
|
11099
|
-
setPreference(key, value) {
|
|
11100
|
-
const prefs = this.getPreferences();
|
|
11101
|
-
prefs[key] = value;
|
|
11102
|
-
writeFileSync8(this.prefsPath(), JSON.stringify(prefs, null, 2), "utf-8");
|
|
11103
|
-
}
|
|
11104
|
-
deletePreference(key) {
|
|
11105
|
-
const prefs = this.getPreferences();
|
|
11106
|
-
if (!(key in prefs))
|
|
11107
|
-
return false;
|
|
11108
|
-
delete prefs[key];
|
|
11109
|
-
writeFileSync8(this.prefsPath(), JSON.stringify(prefs, null, 2), "utf-8");
|
|
11110
|
-
return true;
|
|
11111
|
-
}
|
|
11112
|
-
}
|
|
11113
|
-
var MEMORY_FILES2;
|
|
11114
|
-
var init_store = __esm(() => {
|
|
11115
|
-
init_search();
|
|
11116
|
-
MEMORY_FILES2 = ["conventions", "decisions", "errors", "facts"];
|
|
11117
|
-
});
|
|
11118
|
-
|
|
11119
11315
|
// src/tools/remember.ts
|
|
11120
11316
|
import { homedir as homedir6 } from "os";
|
|
11121
|
-
import { join as
|
|
11317
|
+
import { join as join12 } from "path";
|
|
11122
11318
|
var CATEGORIES, rememberTool;
|
|
11123
11319
|
var init_remember = __esm(() => {
|
|
11124
11320
|
init_i18n();
|
|
@@ -11156,7 +11352,7 @@ var init_remember = __esm(() => {
|
|
|
11156
11352
|
if (!CATEGORIES.includes(category)) {
|
|
11157
11353
|
return { success: false, output: t("tool.invalid_params") };
|
|
11158
11354
|
}
|
|
11159
|
-
const memoryDir =
|
|
11355
|
+
const memoryDir = join12(homedir6(), ".mma", "memory");
|
|
11160
11356
|
const store = new MemoryStore(memoryDir);
|
|
11161
11357
|
try {
|
|
11162
11358
|
if (category === "preferences") {
|
|
@@ -11189,7 +11385,7 @@ var init_remember = __esm(() => {
|
|
|
11189
11385
|
|
|
11190
11386
|
// src/tools/recall.ts
|
|
11191
11387
|
import { homedir as homedir7 } from "os";
|
|
11192
|
-
import { join as
|
|
11388
|
+
import { join as join13 } from "path";
|
|
11193
11389
|
function formatAll(store) {
|
|
11194
11390
|
const parts = [];
|
|
11195
11391
|
const prefs = store.getPreferences();
|
|
@@ -11272,7 +11468,7 @@ var init_recall = __esm(() => {
|
|
|
11272
11468
|
handler: async (_ctx, args) => {
|
|
11273
11469
|
const query = args.query ? String(args.query) : "";
|
|
11274
11470
|
const category = args.category ? String(args.category) : "";
|
|
11275
|
-
const memoryDir =
|
|
11471
|
+
const memoryDir = join13(homedir7(), ".mma", "memory");
|
|
11276
11472
|
const store = new MemoryStore(memoryDir);
|
|
11277
11473
|
try {
|
|
11278
11474
|
if (!query && !category) {
|
|
@@ -11468,15 +11664,15 @@ function buildIndexInjectionScript() {
|
|
|
11468
11664
|
|
|
11469
11665
|
// src/modules/browser/cookie-store.ts
|
|
11470
11666
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
11471
|
-
import { join as
|
|
11667
|
+
import { join as join14 } from "path";
|
|
11472
11668
|
|
|
11473
11669
|
class CookieStore {
|
|
11474
11670
|
filePath;
|
|
11475
11671
|
constructor(cookieDir) {
|
|
11476
|
-
this.filePath =
|
|
11672
|
+
this.filePath = join14(cookieDir, "cookies.json");
|
|
11477
11673
|
}
|
|
11478
11674
|
async save(cookies) {
|
|
11479
|
-
await mkdir(
|
|
11675
|
+
await mkdir(join14(this.filePath, ".."), { recursive: true });
|
|
11480
11676
|
await writeFile(this.filePath, JSON.stringify(cookies, null, 2), "utf-8");
|
|
11481
11677
|
}
|
|
11482
11678
|
async load() {
|
|
@@ -11796,10 +11992,10 @@ var init_types = __esm(() => {
|
|
|
11796
11992
|
});
|
|
11797
11993
|
|
|
11798
11994
|
// src/tools/browser.ts
|
|
11799
|
-
import { join as
|
|
11995
|
+
import { join as join15 } from "path";
|
|
11800
11996
|
function getSession(ctx) {
|
|
11801
11997
|
if (!session) {
|
|
11802
|
-
const cookieDir =
|
|
11998
|
+
const cookieDir = join15(ctx.baseDir, ".mma", "browser");
|
|
11803
11999
|
session = new BrowserSession({
|
|
11804
12000
|
...DEFAULT_BROWSER_CONFIG,
|
|
11805
12001
|
headless: ctx.config.browser?.headless ?? true,
|
|
@@ -11929,8 +12125,8 @@ async function readClipboardFallback() {
|
|
|
11929
12125
|
const { platform: platform4 } = await import("os");
|
|
11930
12126
|
const { execSync: execSync3 } = await import("child_process");
|
|
11931
12127
|
const { readFileSync: readFileSync13, unlinkSync: unlinkSync3 } = await import("fs");
|
|
11932
|
-
const { join:
|
|
11933
|
-
const tmpPath =
|
|
12128
|
+
const { join: join16 } = await import("path");
|
|
12129
|
+
const tmpPath = join16(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
|
|
11934
12130
|
try {
|
|
11935
12131
|
if (platform4() === "linux") {
|
|
11936
12132
|
execSync3(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, { timeout: 5000 });
|
|
@@ -12208,7 +12404,7 @@ class ModuleRegistry {
|
|
|
12208
12404
|
|
|
12209
12405
|
// src/modules/plugins/loader.ts
|
|
12210
12406
|
import { readdirSync as readdirSync5, existsSync as existsSync22, statSync as statSync4 } from "fs";
|
|
12211
|
-
import { join as
|
|
12407
|
+
import { join as join16 } from "path";
|
|
12212
12408
|
|
|
12213
12409
|
class PluginLoader {
|
|
12214
12410
|
loadFromDir(dirPath, pluginManager, logger) {
|
|
@@ -12216,7 +12412,7 @@ class PluginLoader {
|
|
|
12216
12412
|
return;
|
|
12217
12413
|
const entries = readdirSync5(dirPath);
|
|
12218
12414
|
for (const entry of entries) {
|
|
12219
|
-
const fullPath =
|
|
12415
|
+
const fullPath = join16(dirPath, entry);
|
|
12220
12416
|
if (!statSync4(fullPath).isFile())
|
|
12221
12417
|
continue;
|
|
12222
12418
|
if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
|
|
@@ -12241,7 +12437,7 @@ var init_loader = __esm(() => {
|
|
|
12241
12437
|
// src/modules/plugins/builtin/lint-on-write.ts
|
|
12242
12438
|
import { execSync as execSync3 } from "child_process";
|
|
12243
12439
|
import { existsSync as existsSync23, readFileSync as readFileSync13 } from "fs";
|
|
12244
|
-
import { resolve as resolve12, extname as extname4, join as
|
|
12440
|
+
import { resolve as resolve12, extname as extname4, join as join17 } from "path";
|
|
12245
12441
|
|
|
12246
12442
|
class LintOnWritePlugin {
|
|
12247
12443
|
name = "lint-on-write";
|
|
@@ -12303,7 +12499,7 @@ class LintOnWritePlugin {
|
|
|
12303
12499
|
}
|
|
12304
12500
|
async runProjectLint(ctx, result) {
|
|
12305
12501
|
try {
|
|
12306
|
-
const packageJsonPath =
|
|
12502
|
+
const packageJsonPath = join17(ctx.baseDir, "package.json");
|
|
12307
12503
|
if (!existsSync23(packageJsonPath)) {
|
|
12308
12504
|
return;
|
|
12309
12505
|
}
|
|
@@ -12323,7 +12519,7 @@ class LintOnWritePlugin {
|
|
|
12323
12519
|
}
|
|
12324
12520
|
}
|
|
12325
12521
|
async runProjectTypeCheck(ctx, result) {
|
|
12326
|
-
const tsconfigPath =
|
|
12522
|
+
const tsconfigPath = join17(ctx.baseDir, "tsconfig.json");
|
|
12327
12523
|
if (!existsSync23(tsconfigPath)) {
|
|
12328
12524
|
return;
|
|
12329
12525
|
}
|
|
@@ -12512,6 +12708,11 @@ class StuckDetector {
|
|
|
12512
12708
|
repetitionThreshold = 3;
|
|
12513
12709
|
consecutiveFailures = 0;
|
|
12514
12710
|
lastFailedTool = "";
|
|
12711
|
+
lastErrorOutput = "";
|
|
12712
|
+
escalationCount = 0;
|
|
12713
|
+
escalationThreshold = 3;
|
|
12714
|
+
fileRewriteCount = new Map;
|
|
12715
|
+
fileRewriteThreshold = 3;
|
|
12515
12716
|
constructor(threshold = 8, errorThreshold = 3) {
|
|
12516
12717
|
this.threshold = threshold;
|
|
12517
12718
|
this.errorThreshold = errorThreshold;
|
|
@@ -12531,14 +12732,26 @@ class StuckDetector {
|
|
|
12531
12732
|
this.recentToolCalls.shift();
|
|
12532
12733
|
}
|
|
12533
12734
|
}
|
|
12534
|
-
recordToolError(toolName) {
|
|
12735
|
+
recordToolError(toolName, output) {
|
|
12535
12736
|
this.toolErrors.set(toolName, (this.toolErrors.get(toolName) || 0) + 1);
|
|
12536
12737
|
this.consecutiveFailures++;
|
|
12537
12738
|
this.lastFailedTool = toolName;
|
|
12739
|
+
if (output)
|
|
12740
|
+
this.lastErrorOutput = output;
|
|
12741
|
+
}
|
|
12742
|
+
getLastErrorOutput() {
|
|
12743
|
+
return this.lastErrorOutput;
|
|
12744
|
+
}
|
|
12745
|
+
getLastFailedTool() {
|
|
12746
|
+
return this.lastFailedTool;
|
|
12747
|
+
}
|
|
12748
|
+
getIterationsOnCurrentStep() {
|
|
12749
|
+
return this.iterationsOnCurrentStep;
|
|
12538
12750
|
}
|
|
12539
12751
|
recordToolSuccess() {
|
|
12540
12752
|
this.consecutiveFailures = 0;
|
|
12541
12753
|
this.lastFailedTool = "";
|
|
12754
|
+
this.escalationCount = 0;
|
|
12542
12755
|
}
|
|
12543
12756
|
setCurrentStep(stepId, description) {
|
|
12544
12757
|
if (stepId !== this.currentStepId) {
|
|
@@ -12576,6 +12789,117 @@ class StuckDetector {
|
|
|
12576
12789
|
getConsecutiveFailuresCount() {
|
|
12577
12790
|
return this.consecutiveFailures;
|
|
12578
12791
|
}
|
|
12792
|
+
recordEscalation() {
|
|
12793
|
+
this.escalationCount++;
|
|
12794
|
+
}
|
|
12795
|
+
shouldEscalate() {
|
|
12796
|
+
return this.escalationCount >= this.escalationThreshold;
|
|
12797
|
+
}
|
|
12798
|
+
resetEscalation() {
|
|
12799
|
+
this.escalationCount = 0;
|
|
12800
|
+
}
|
|
12801
|
+
getEscalationCount() {
|
|
12802
|
+
return this.escalationCount;
|
|
12803
|
+
}
|
|
12804
|
+
getHints() {
|
|
12805
|
+
const hints = [];
|
|
12806
|
+
const desc = this.currentStepDescription.toLowerCase();
|
|
12807
|
+
if (desc.includes("install") || desc.includes("npm") || desc.includes("pip")) {
|
|
12808
|
+
hints.push("Check if a lock file exists (package-lock.json, poetry.lock). If missing, run the install command first.");
|
|
12809
|
+
}
|
|
12810
|
+
if (desc.includes("test") || desc.includes("spec")) {
|
|
12811
|
+
hints.push("Make sure the source files exist and have real code before running tests.");
|
|
12812
|
+
}
|
|
12813
|
+
if (desc.includes("build") || desc.includes("compile")) {
|
|
12814
|
+
hints.push("Check that all dependencies are installed and source files are not empty.");
|
|
12815
|
+
}
|
|
12816
|
+
if (desc.includes("deploy") || desc.includes("publish")) {
|
|
12817
|
+
hints.push("Verify credentials and network access before deploying.");
|
|
12818
|
+
}
|
|
12819
|
+
if (this.hasRepetitiveToolCalls()) {
|
|
12820
|
+
hints.push("You are calling the same tool repeatedly with the same arguments. Try a different approach.");
|
|
12821
|
+
}
|
|
12822
|
+
if (this.hasConsecutiveFailures()) {
|
|
12823
|
+
hints.push("Multiple different tools are failing. Check if the environment is set up correctly.");
|
|
12824
|
+
}
|
|
12825
|
+
return hints;
|
|
12826
|
+
}
|
|
12827
|
+
getActionableHints() {
|
|
12828
|
+
const hints = [];
|
|
12829
|
+
const error = this.lastErrorOutput;
|
|
12830
|
+
if (!error)
|
|
12831
|
+
return hints;
|
|
12832
|
+
if (/Cannot read properties of undefined|is not a function|is not a constructor/i.test(error)) {
|
|
12833
|
+
if (/node_modules/.test(error)) {
|
|
12834
|
+
hints.push("A dependency is incompatible with your Node.js version. Check if there is an alternative package or use a different runtime.");
|
|
12835
|
+
}
|
|
12836
|
+
}
|
|
12837
|
+
if (/TS1005|TS1003|TS1109|TS1128|TS1434/i.test(error)) {
|
|
12838
|
+
hints.push("TypeScript syntax error. Read the error message carefully — it tells you the exact line and column. Fix the syntax before retrying.");
|
|
12839
|
+
}
|
|
12840
|
+
if (/TS2322|TS2345|TS2769|TS7006|TS7016/i.test(error)) {
|
|
12841
|
+
hints.push("TypeScript type mismatch. Check the type signature of the function/API you are using. If a package lacks type declarations, install @types/<package> or use skipLibCheck.");
|
|
12842
|
+
}
|
|
12843
|
+
if (/Cannot find module|Module not found|ERR_MODULE_NOT_FOUND/i.test(error)) {
|
|
12844
|
+
hints.push("Module not found. Run npm install or check if the import path is correct.");
|
|
12845
|
+
}
|
|
12846
|
+
if (/EACCES|EPERM|permission denied/i.test(error)) {
|
|
12847
|
+
hints.push("Permission denied. Check file permissions or run with appropriate privileges.");
|
|
12848
|
+
}
|
|
12849
|
+
if (/ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch failed/i.test(error)) {
|
|
12850
|
+
hints.push("Network error. Check if the server is running and accessible.");
|
|
12851
|
+
}
|
|
12852
|
+
if (/is not recognized|command not found|not found in path/i.test(error)) {
|
|
12853
|
+
hints.push("Command not found. Check if the tool is installed and in PATH.");
|
|
12854
|
+
}
|
|
12855
|
+
if (this.hasExcessiveRewrites()) {
|
|
12856
|
+
const file = this.getExcessiveRewriteFile();
|
|
12857
|
+
hints.push(`File ${file} has been rewritten ${this.getFileRewriteCount(file)} times without success. Stop rewriting and try a fundamentally different approach.`);
|
|
12858
|
+
}
|
|
12859
|
+
return hints;
|
|
12860
|
+
}
|
|
12861
|
+
getToolAlternative() {
|
|
12862
|
+
const tool = this.lastFailedTool;
|
|
12863
|
+
const error = this.lastErrorOutput;
|
|
12864
|
+
if (!tool)
|
|
12865
|
+
return null;
|
|
12866
|
+
if (/Cannot read properties of undefined|is not a function|TypeError|ReferenceError/i.test(error)) {
|
|
12867
|
+
const alternatives = {
|
|
12868
|
+
"ts-node": "tsx",
|
|
12869
|
+
jest: "vitest",
|
|
12870
|
+
mocha: "vitest",
|
|
12871
|
+
webpack: "vite",
|
|
12872
|
+
rollup: "vite",
|
|
12873
|
+
parcel: "vite",
|
|
12874
|
+
babel: "tsc",
|
|
12875
|
+
eslint: "biome",
|
|
12876
|
+
prettier: "biome"
|
|
12877
|
+
};
|
|
12878
|
+
for (const [failed, alt] of Object.entries(alternatives)) {
|
|
12879
|
+
if (tool.includes(failed) || error.includes(failed)) {
|
|
12880
|
+
return alt;
|
|
12881
|
+
}
|
|
12882
|
+
}
|
|
12883
|
+
}
|
|
12884
|
+
return null;
|
|
12885
|
+
}
|
|
12886
|
+
recordFileRewrite(filePath) {
|
|
12887
|
+
const count = (this.fileRewriteCount.get(filePath) || 0) + 1;
|
|
12888
|
+
this.fileRewriteCount.set(filePath, count);
|
|
12889
|
+
}
|
|
12890
|
+
getFileRewriteCount(filePath) {
|
|
12891
|
+
return this.fileRewriteCount.get(filePath) || 0;
|
|
12892
|
+
}
|
|
12893
|
+
hasExcessiveRewrites() {
|
|
12894
|
+
return Array.from(this.fileRewriteCount.values()).some((c) => c >= this.fileRewriteThreshold);
|
|
12895
|
+
}
|
|
12896
|
+
getExcessiveRewriteFile() {
|
|
12897
|
+
for (const [file, count] of this.fileRewriteCount) {
|
|
12898
|
+
if (count >= this.fileRewriteThreshold)
|
|
12899
|
+
return file;
|
|
12900
|
+
}
|
|
12901
|
+
return null;
|
|
12902
|
+
}
|
|
12579
12903
|
getRepetitiveToolMessage() {
|
|
12580
12904
|
if (!this.hasRepetitiveToolCalls())
|
|
12581
12905
|
return "";
|
|
@@ -12654,7 +12978,7 @@ var init_stuck_detector = __esm(() => {
|
|
|
12654
12978
|
// src/modules/execution/auditor.ts
|
|
12655
12979
|
import { execSync as execSync4 } from "child_process";
|
|
12656
12980
|
import { existsSync as existsSync24 } from "fs";
|
|
12657
|
-
import { resolve as resolve13, join as
|
|
12981
|
+
import { resolve as resolve13, join as join18 } from "path";
|
|
12658
12982
|
|
|
12659
12983
|
class Auditor {
|
|
12660
12984
|
baseDir;
|
|
@@ -12662,7 +12986,7 @@ class Auditor {
|
|
|
12662
12986
|
this.baseDir = baseDir;
|
|
12663
12987
|
}
|
|
12664
12988
|
async audit(plan) {
|
|
12665
|
-
const allStepText = plan.steps.map((s) => s.description).join(" ");
|
|
12989
|
+
const allStepText = plan.steps.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
12666
12990
|
const fileMatches = allStepText.match(/\b[\w./-]+\.[a-z]+/gi) || [];
|
|
12667
12991
|
const uniqueFiles = [...new Set(fileMatches)];
|
|
12668
12992
|
const missingFiles = [];
|
|
@@ -12678,6 +13002,10 @@ class Auditor {
|
|
|
12678
13002
|
const doneSteps = plan.steps.filter((s) => s.status === "done").length;
|
|
12679
13003
|
const totalSteps = plan.steps.length;
|
|
12680
13004
|
const typeCheckError = await this.runProjectTypeCheck();
|
|
13005
|
+
let massEditWarning = null;
|
|
13006
|
+
if (uniqueFiles.length > MASS_EDIT_THRESHOLD) {
|
|
13007
|
+
massEditWarning = t("exec.mass_edit_warning", { count: String(uniqueFiles.length) });
|
|
13008
|
+
}
|
|
12681
13009
|
const passed = missingFiles.length === 0 && !typeCheckError;
|
|
12682
13010
|
let summary;
|
|
12683
13011
|
if (passed) {
|
|
@@ -12696,11 +13024,12 @@ class Auditor {
|
|
|
12696
13024
|
createdFiles: existingFiles,
|
|
12697
13025
|
modifiedFiles: [],
|
|
12698
13026
|
summary,
|
|
12699
|
-
typeCheckError
|
|
13027
|
+
typeCheckError,
|
|
13028
|
+
massEditWarning
|
|
12700
13029
|
};
|
|
12701
13030
|
}
|
|
12702
13031
|
async runProjectTypeCheck() {
|
|
12703
|
-
const tsconfigPath =
|
|
13032
|
+
const tsconfigPath = join18(this.baseDir, "tsconfig.json");
|
|
12704
13033
|
if (!existsSync24(tsconfigPath)) {
|
|
12705
13034
|
return null;
|
|
12706
13035
|
}
|
|
@@ -12721,22 +13050,23 @@ class Auditor {
|
|
|
12721
13050
|
}
|
|
12722
13051
|
}
|
|
12723
13052
|
}
|
|
13053
|
+
var MASS_EDIT_THRESHOLD = 10;
|
|
12724
13054
|
var init_auditor = __esm(() => {
|
|
12725
13055
|
init_i18n();
|
|
12726
13056
|
});
|
|
12727
13057
|
|
|
12728
13058
|
// src/modules/execution/plan-persister.ts
|
|
12729
13059
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11, existsSync as existsSync25 } from "fs";
|
|
12730
|
-
import { join as
|
|
13060
|
+
import { join as join19 } from "path";
|
|
12731
13061
|
|
|
12732
13062
|
class PlanPersister {
|
|
12733
13063
|
filePath;
|
|
12734
13064
|
constructor(baseDir) {
|
|
12735
|
-
const mmaDir =
|
|
13065
|
+
const mmaDir = join19(baseDir, ".mma");
|
|
12736
13066
|
if (!existsSync25(mmaDir)) {
|
|
12737
13067
|
mkdirSync11(mmaDir, { recursive: true });
|
|
12738
13068
|
}
|
|
12739
|
-
this.filePath =
|
|
13069
|
+
this.filePath = join19(mmaDir, "plan.json");
|
|
12740
13070
|
}
|
|
12741
13071
|
save(plan) {
|
|
12742
13072
|
const file = {
|
|
@@ -12774,6 +13104,92 @@ class PlanPersister {
|
|
|
12774
13104
|
}
|
|
12775
13105
|
var init_plan_persister = () => {};
|
|
12776
13106
|
|
|
13107
|
+
// src/modules/execution/plan-coverage.ts
|
|
13108
|
+
function extractFilePaths(text) {
|
|
13109
|
+
if (!text)
|
|
13110
|
+
return [];
|
|
13111
|
+
const seen = new Set;
|
|
13112
|
+
const result = [];
|
|
13113
|
+
for (const m of text.matchAll(FILE_PATH_RE)) {
|
|
13114
|
+
let p = m[0];
|
|
13115
|
+
p = p.replace(/[.,;:)\]>]+$/g, "");
|
|
13116
|
+
const ext = p.split(".").pop()?.toLowerCase() ?? "";
|
|
13117
|
+
if (!p || IGNORED_EXT.has(ext))
|
|
13118
|
+
continue;
|
|
13119
|
+
const key = p.toLowerCase();
|
|
13120
|
+
if (seen.has(key))
|
|
13121
|
+
continue;
|
|
13122
|
+
seen.add(key);
|
|
13123
|
+
result.push(p);
|
|
13124
|
+
}
|
|
13125
|
+
return result;
|
|
13126
|
+
}
|
|
13127
|
+
function basename2(p) {
|
|
13128
|
+
const parts = p.split(/[/\\]/);
|
|
13129
|
+
return parts[parts.length - 1] ?? p;
|
|
13130
|
+
}
|
|
13131
|
+
function checkPlanCoverage(taskText, stepDescriptions) {
|
|
13132
|
+
const taskPaths = extractFilePaths(taskText);
|
|
13133
|
+
if (taskPaths.length === 0)
|
|
13134
|
+
return { missing: [] };
|
|
13135
|
+
const stepText = stepDescriptions.map((s) => s.toLowerCase()).join(`
|
|
13136
|
+
`);
|
|
13137
|
+
const missing = taskPaths.filter((p) => {
|
|
13138
|
+
const lower = p.toLowerCase();
|
|
13139
|
+
const base = basename2(lower);
|
|
13140
|
+
return !stepText.includes(lower) && !stepText.includes(base);
|
|
13141
|
+
});
|
|
13142
|
+
return { missing };
|
|
13143
|
+
}
|
|
13144
|
+
var FILE_PATH_RE, IGNORED_EXT;
|
|
13145
|
+
var init_plan_coverage = __esm(() => {
|
|
13146
|
+
FILE_PATH_RE = /\b[\w./\\-]+\.[a-z]+\b/gi;
|
|
13147
|
+
IGNORED_EXT = new Set([
|
|
13148
|
+
"tsx",
|
|
13149
|
+
"jsx",
|
|
13150
|
+
"json5",
|
|
13151
|
+
"mdx",
|
|
13152
|
+
"yml",
|
|
13153
|
+
"yaml",
|
|
13154
|
+
"toml",
|
|
13155
|
+
"lock",
|
|
13156
|
+
"svg",
|
|
13157
|
+
"png",
|
|
13158
|
+
"jpg",
|
|
13159
|
+
"jpeg",
|
|
13160
|
+
"gif",
|
|
13161
|
+
"webp",
|
|
13162
|
+
"ico",
|
|
13163
|
+
"css",
|
|
13164
|
+
"scss",
|
|
13165
|
+
"less",
|
|
13166
|
+
"html",
|
|
13167
|
+
"jsx"
|
|
13168
|
+
]);
|
|
13169
|
+
});
|
|
13170
|
+
|
|
13171
|
+
// src/modules/skills/error-skill-map.ts
|
|
13172
|
+
function suggestSkill(errorOutput) {
|
|
13173
|
+
for (const { pattern, skill } of ERROR_SKILL_MAP) {
|
|
13174
|
+
if (pattern.test(errorOutput))
|
|
13175
|
+
return skill;
|
|
13176
|
+
}
|
|
13177
|
+
return null;
|
|
13178
|
+
}
|
|
13179
|
+
var ERROR_SKILL_MAP;
|
|
13180
|
+
var init_error_skill_map = __esm(() => {
|
|
13181
|
+
ERROR_SKILL_MAP = [
|
|
13182
|
+
{ pattern: /TS\d{4}:/i, skill: "typescript-expert" },
|
|
13183
|
+
{ pattern: /Cannot find name|No overload matches/i, skill: "typescript-type-expert" },
|
|
13184
|
+
{ pattern: /ts-node|tsx.*error/i, skill: "typescript-expert" },
|
|
13185
|
+
{ pattern: /Module not found|Cannot resolve module/i, skill: "typescript-expert" },
|
|
13186
|
+
{ pattern: /ECONNREFUSED|ETIMEDOUT|fetch failed/i, skill: "devops-expert" },
|
|
13187
|
+
{ pattern: /permission denied|EACCES/i, skill: "linux-server-expert" },
|
|
13188
|
+
{ pattern: /docker|container/i, skill: "docker-expert" },
|
|
13189
|
+
{ pattern: /jest|vitest|test.*fail/i, skill: "vitest-testing-expert" }
|
|
13190
|
+
];
|
|
13191
|
+
});
|
|
13192
|
+
|
|
12777
13193
|
// src/modules/execution/module.ts
|
|
12778
13194
|
import { existsSync as existsSync26, readFileSync as readFileSync15 } from "fs";
|
|
12779
13195
|
import { resolve as resolve14 } from "path";
|
|
@@ -12850,13 +13266,13 @@ class ExecutionModule {
|
|
|
12850
13266
|
name: "plan",
|
|
12851
13267
|
description: `Create, update, show, or abort a multi-step plan. Use "create" at the start of complex tasks. Use "update" after completing each step to track progress. Use "show" to re-print the current plan checklist.
|
|
12852
13268
|
|
|
12853
|
-
|
|
13269
|
+
Write CONCRETE steps with exact file paths and commands:
|
|
12854
13270
|
- Specify WHICH files to create with exact paths (e.g. "create src/components/Header.tsx with navigation and logo")
|
|
12855
|
-
- Specify WHICH packages to install
|
|
13271
|
+
- Specify WHICH packages to install (e.g. "run npm install react react-dom")
|
|
12856
13272
|
- Specify WHICH CLI commands to run with exact arguments
|
|
12857
|
-
-
|
|
12858
|
-
-
|
|
12859
|
-
-
|
|
13273
|
+
- Each step should include at least one file extension (.ts, .tsx, .json, etc.) or a command verb (install, create, build, run, add, init)
|
|
13274
|
+
- Good: "Создать src/components/Header.tsx с навигацией и логотипом"
|
|
13275
|
+
- Bad: "Настройка проекта" (too vague — what exactly needs to be configured?)`,
|
|
12860
13276
|
parameters: {
|
|
12861
13277
|
type: "object",
|
|
12862
13278
|
properties: {
|
|
@@ -12883,10 +13299,25 @@ IMPORTANT — each step MUST be detailed and concrete (≥50 chars):
|
|
|
12883
13299
|
const plan = PlanCreator.createPlan(title, steps, this.baseDir);
|
|
12884
13300
|
this.setPlan(plan);
|
|
12885
13301
|
const display = PlanCreator.toPromptBlock(plan, 0);
|
|
13302
|
+
let output = t("plan.created", { title, steps: String(steps.length) });
|
|
13303
|
+
let displayOut = display;
|
|
13304
|
+
const taskText = this.getTaskText(_ctx);
|
|
13305
|
+
if (taskText) {
|
|
13306
|
+
const { missing } = checkPlanCoverage(taskText, steps);
|
|
13307
|
+
if (missing.length > 0) {
|
|
13308
|
+
const warn = t("plan.coverage_warning", {
|
|
13309
|
+
missing: missing.join(", ")
|
|
13310
|
+
});
|
|
13311
|
+
output += `
|
|
13312
|
+
⚠️ ${warn}`;
|
|
13313
|
+
displayOut = `${display}
|
|
13314
|
+
⚠️ ${warn}`;
|
|
13315
|
+
}
|
|
13316
|
+
}
|
|
12886
13317
|
return {
|
|
12887
13318
|
success: true,
|
|
12888
|
-
output
|
|
12889
|
-
display
|
|
13319
|
+
output,
|
|
13320
|
+
display: displayOut
|
|
12890
13321
|
};
|
|
12891
13322
|
}
|
|
12892
13323
|
if (action === "show") {
|
|
@@ -13032,12 +13463,47 @@ Sub-tasks: ${note}`
|
|
|
13032
13463
|
if (currentIter - this.lastRecoveryIteration >= STUCK_RECOVERY_COOLDOWN) {
|
|
13033
13464
|
const recovery = this.stuckDetector.getRecoveryMessage();
|
|
13034
13465
|
if (recovery && ctx.contextManager) {
|
|
13466
|
+
const lastError = this.stuckDetector.getLastErrorOutput();
|
|
13467
|
+
const suggestedSkill = suggestSkill(lastError);
|
|
13468
|
+
const skillHint = suggestedSkill ? `
|
|
13469
|
+
Consider loading the "${suggestedSkill}" skill for expert guidance on this error.` : "";
|
|
13470
|
+
const actionableHints = this.stuckDetector.getActionableHints();
|
|
13471
|
+
const actionableHintStr = actionableHints.length > 0 ? `
|
|
13472
|
+
${t("exec.hints", { hints: actionableHints.map((h) => `- ${h}`).join(`
|
|
13473
|
+
`) })}` : "";
|
|
13474
|
+
const alternative = this.stuckDetector.getToolAlternative();
|
|
13475
|
+
const altHint = alternative ? `
|
|
13476
|
+
Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}" instead.` : "";
|
|
13477
|
+
ctx.contextManager.addMessage({
|
|
13478
|
+
role: "user",
|
|
13479
|
+
content: `<system-summary>${recovery}${skillHint}${actionableHintStr}${altHint}</system-summary>`
|
|
13480
|
+
});
|
|
13481
|
+
}
|
|
13482
|
+
const hints = this.stuckDetector.getHints();
|
|
13483
|
+
if (hints.length > 0 && ctx.contextManager) {
|
|
13484
|
+
const hintMsg = t("exec.hints", { hints: hints.map((h) => `- ${h}`).join(`
|
|
13485
|
+
`) });
|
|
13035
13486
|
ctx.contextManager.addMessage({
|
|
13036
13487
|
role: "user",
|
|
13037
|
-
content: `<system-summary>${
|
|
13488
|
+
content: `<system-summary>${hintMsg}</system-summary>`
|
|
13038
13489
|
});
|
|
13039
13490
|
}
|
|
13491
|
+
this.stuckDetector.recordEscalation();
|
|
13040
13492
|
this.lastRecoveryIteration = currentIter;
|
|
13493
|
+
if (this.stuckDetector.shouldEscalate() && ctx.onMeta) {
|
|
13494
|
+
const escalation = t("exec.escalation", {
|
|
13495
|
+
stepId: String(this.tracker?.getCurrentStep()?.id ?? "?"),
|
|
13496
|
+
description: this.tracker?.getCurrentStep()?.description ?? ""
|
|
13497
|
+
});
|
|
13498
|
+
ctx.onMeta(escalation);
|
|
13499
|
+
}
|
|
13500
|
+
if (this.stuckDetector.getIterationsOnCurrentStep() >= FORCE_SKIP_THRESHOLD && ctx.contextManager) {
|
|
13501
|
+
const step2 = this.tracker?.getCurrentStep();
|
|
13502
|
+
ctx.contextManager.addMessage({
|
|
13503
|
+
role: "user",
|
|
13504
|
+
content: `<system-summary>CRITICAL: You have been stuck on step ${step2?.id ?? "?"} ("${step2?.description ?? ""}") for ${this.stuckDetector.getIterationsOnCurrentStep()} iterations. You MUST stop trying the same approach. Options: 1) Skip this step via plan update step=${step2?.id ?? "?"} status=skipped with a note explaining why. 2) Mark the step as done if the code runs correctly, even if there are type errors. 3) Try a completely different approach. Do NOT continue with the current approach.</system-summary>`
|
|
13505
|
+
});
|
|
13506
|
+
}
|
|
13041
13507
|
}
|
|
13042
13508
|
}
|
|
13043
13509
|
},
|
|
@@ -13070,11 +13536,44 @@ Sub-tasks: ${note}`
|
|
|
13070
13536
|
},
|
|
13071
13537
|
onAfterTool: (ctx, call, result) => {
|
|
13072
13538
|
if (!result.success) {
|
|
13073
|
-
this.stuckDetector.recordToolError(call.name);
|
|
13539
|
+
this.stuckDetector.recordToolError(call.name, result.output);
|
|
13540
|
+
const actionableHints = this.stuckDetector.getActionableHints();
|
|
13541
|
+
const alternative = this.stuckDetector.getToolAlternative();
|
|
13542
|
+
if ((actionableHints.length > 0 || alternative) && ctx.contextManager) {
|
|
13543
|
+
const parts = [...actionableHints];
|
|
13544
|
+
if (alternative) {
|
|
13545
|
+
parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
|
|
13546
|
+
}
|
|
13547
|
+
ctx.contextManager.addMessage({
|
|
13548
|
+
role: "user",
|
|
13549
|
+
content: `<system-summary>${t("exec.hints", { hints: parts.map((h) => `- ${h}`).join(`
|
|
13550
|
+
`) })}</system-summary>`
|
|
13551
|
+
});
|
|
13552
|
+
}
|
|
13074
13553
|
} else {
|
|
13075
13554
|
this.stuckDetector.recordToolSuccess();
|
|
13555
|
+
if (call.name === "bash" && result.success && ctx.contextManager) {
|
|
13556
|
+
const cmd = String(call.arguments?.command ?? "");
|
|
13557
|
+
if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
|
|
13558
|
+
ctx.contextManager.addMessage({
|
|
13559
|
+
role: "user",
|
|
13560
|
+
content: `<system-summary>The command "${cmd}" completed successfully. If this was testing your code, mark the current step as done via plan update step=N status=done.</system-summary>`
|
|
13561
|
+
});
|
|
13562
|
+
}
|
|
13563
|
+
}
|
|
13076
13564
|
}
|
|
13077
|
-
if (
|
|
13565
|
+
if (result.success && (call.name === "write_file" || call.name === "edit_file")) {
|
|
13566
|
+
const filePath = call.arguments?.path;
|
|
13567
|
+
if (filePath) {
|
|
13568
|
+
this.stuckDetector.recordFileRewrite(filePath);
|
|
13569
|
+
if (this.stuckDetector.hasExcessiveRewrites()) {
|
|
13570
|
+
const file = this.stuckDetector.getExcessiveRewriteFile();
|
|
13571
|
+
const count = this.stuckDetector.getFileRewriteCount(file);
|
|
13572
|
+
if (ctx.onMeta) {
|
|
13573
|
+
ctx.onMeta(t("exec.file_rewrite_warning", { file, count: String(count) }));
|
|
13574
|
+
}
|
|
13575
|
+
}
|
|
13576
|
+
}
|
|
13078
13577
|
this.advancePlanIfStepComplete(ctx.contextManager);
|
|
13079
13578
|
}
|
|
13080
13579
|
}
|
|
@@ -13185,8 +13684,18 @@ Sub-tasks: ${note}`
|
|
|
13185
13684
|
});
|
|
13186
13685
|
}
|
|
13187
13686
|
}
|
|
13687
|
+
getTaskText(ctx) {
|
|
13688
|
+
const manager = ctx?.contextManager;
|
|
13689
|
+
if (!manager)
|
|
13690
|
+
return "";
|
|
13691
|
+
const history = manager.getActiveHistory();
|
|
13692
|
+
const firstUser = history.find((m) => m.role === "user");
|
|
13693
|
+
if (!firstUser)
|
|
13694
|
+
return "";
|
|
13695
|
+
return getMessageText(firstUser.content).trim();
|
|
13696
|
+
}
|
|
13188
13697
|
}
|
|
13189
|
-
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3;
|
|
13698
|
+
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 15;
|
|
13190
13699
|
var init_module = __esm(() => {
|
|
13191
13700
|
init_i18n();
|
|
13192
13701
|
init_tracker();
|
|
@@ -13194,11 +13703,13 @@ var init_module = __esm(() => {
|
|
|
13194
13703
|
init_stuck_detector();
|
|
13195
13704
|
init_auditor();
|
|
13196
13705
|
init_plan_persister();
|
|
13706
|
+
init_plan_coverage();
|
|
13707
|
+
init_error_skill_map();
|
|
13197
13708
|
});
|
|
13198
13709
|
|
|
13199
13710
|
// src/modules/security/session-encryption.ts
|
|
13200
13711
|
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync27, readdirSync as readdirSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
13201
|
-
import { join as
|
|
13712
|
+
import { join as join20 } from "path";
|
|
13202
13713
|
import { homedir as homedir8 } from "os";
|
|
13203
13714
|
|
|
13204
13715
|
class SessionFileEncryptor {
|
|
@@ -13207,7 +13718,7 @@ class SessionFileEncryptor {
|
|
|
13207
13718
|
constructor(config) {
|
|
13208
13719
|
this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
|
|
13209
13720
|
this.encryptor = new ConfigEncryptor({
|
|
13210
|
-
keyPath: config?.keyPath ||
|
|
13721
|
+
keyPath: config?.keyPath || join20(homedir8(), ".mma", ".session-encryption-key")
|
|
13211
13722
|
});
|
|
13212
13723
|
}
|
|
13213
13724
|
isEnabled() {
|
|
@@ -13279,7 +13790,7 @@ class SessionFileEncryptor {
|
|
|
13279
13790
|
return;
|
|
13280
13791
|
const files = readdirSync6(sessionDir);
|
|
13281
13792
|
for (const file of files) {
|
|
13282
|
-
const filePath =
|
|
13793
|
+
const filePath = join20(sessionDir, file);
|
|
13283
13794
|
if (existsSync27(filePath) && !file.endsWith(".enc")) {
|
|
13284
13795
|
try {
|
|
13285
13796
|
const content = readFileSync16(filePath, "utf8");
|
|
@@ -13296,7 +13807,7 @@ class SessionFileEncryptor {
|
|
|
13296
13807
|
const files = readdirSync6(sessionDir);
|
|
13297
13808
|
for (const file of files) {
|
|
13298
13809
|
if (file.endsWith(".enc")) {
|
|
13299
|
-
const encFilePath =
|
|
13810
|
+
const encFilePath = join20(sessionDir, file);
|
|
13300
13811
|
const decFilePath = encFilePath.slice(0, -4);
|
|
13301
13812
|
try {
|
|
13302
13813
|
const content = readFileSync16(encFilePath, "utf8");
|
|
@@ -13329,7 +13840,7 @@ import {
|
|
|
13329
13840
|
writeFileSync as writeFileSync11,
|
|
13330
13841
|
appendFileSync as appendFileSync5
|
|
13331
13842
|
} from "fs";
|
|
13332
|
-
import { join as
|
|
13843
|
+
import { join as join21 } from "path";
|
|
13333
13844
|
import { gzipSync } from "zlib";
|
|
13334
13845
|
|
|
13335
13846
|
class SessionStore {
|
|
@@ -13355,16 +13866,16 @@ class SessionStore {
|
|
|
13355
13866
|
mkdirSync12(this.baseDir, { recursive: true });
|
|
13356
13867
|
}
|
|
13357
13868
|
sessionDir(id) {
|
|
13358
|
-
return
|
|
13869
|
+
return join21(this.baseDir, id);
|
|
13359
13870
|
}
|
|
13360
13871
|
metaPath(id) {
|
|
13361
|
-
return
|
|
13872
|
+
return join21(this.sessionDir(id), "meta.json");
|
|
13362
13873
|
}
|
|
13363
13874
|
historyPath(id) {
|
|
13364
|
-
return
|
|
13875
|
+
return join21(this.sessionDir(id), "history.jsonl");
|
|
13365
13876
|
}
|
|
13366
13877
|
sessionLogPath(id) {
|
|
13367
|
-
return
|
|
13878
|
+
return join21(this.sessionDir(id), "session.jsonl");
|
|
13368
13879
|
}
|
|
13369
13880
|
sessionExists(id) {
|
|
13370
13881
|
return existsSync28(this.metaPath(id));
|
|
@@ -13487,7 +13998,7 @@ class SessionStore {
|
|
|
13487
13998
|
if (existsSync28(historyPath)) {
|
|
13488
13999
|
const content = readFileSync17(historyPath, "utf-8");
|
|
13489
14000
|
const compressed = gzipSync(content);
|
|
13490
|
-
const gzPath =
|
|
14001
|
+
const gzPath = join21(this.baseDir, `${session2.id}.jsonl.gz`);
|
|
13491
14002
|
writeFileSync11(gzPath, compressed);
|
|
13492
14003
|
rmSync(historyPath);
|
|
13493
14004
|
}
|
|
@@ -13695,7 +14206,7 @@ class ProfileCompressor {
|
|
|
13695
14206
|
|
|
13696
14207
|
// src/modules/user-profile/profile.ts
|
|
13697
14208
|
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync29, mkdirSync as mkdirSync13 } from "fs";
|
|
13698
|
-
import { join as
|
|
14209
|
+
import { join as join22 } from "path";
|
|
13699
14210
|
import { homedir as homedir9, hostname, platform as platform4, type } from "os";
|
|
13700
14211
|
import { env } from "process";
|
|
13701
14212
|
|
|
@@ -13722,10 +14233,10 @@ class UserProfile {
|
|
|
13722
14233
|
if (!existsSync29(this.profileDir)) {
|
|
13723
14234
|
mkdirSync13(this.profileDir, { recursive: true });
|
|
13724
14235
|
}
|
|
13725
|
-
writeFileSync12(
|
|
14236
|
+
writeFileSync12(join22(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
|
|
13726
14237
|
}
|
|
13727
14238
|
load() {
|
|
13728
|
-
const path =
|
|
14239
|
+
const path = join22(this.profileDir, "profile.json");
|
|
13729
14240
|
if (!existsSync29(path))
|
|
13730
14241
|
return null;
|
|
13731
14242
|
try {
|
|
@@ -13765,7 +14276,7 @@ var init_profile = () => {};
|
|
|
13765
14276
|
|
|
13766
14277
|
// src/modules/skills/loader.ts
|
|
13767
14278
|
import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync30, statSync as statSync5 } from "fs";
|
|
13768
|
-
import { join as
|
|
14279
|
+
import { join as join23 } from "path";
|
|
13769
14280
|
|
|
13770
14281
|
class SkillsLoader {
|
|
13771
14282
|
loadFromDir(dirPath) {
|
|
@@ -13778,7 +14289,7 @@ class SkillsLoader {
|
|
|
13778
14289
|
scanDir(dirPath, skills) {
|
|
13779
14290
|
const entries = readdirSync8(dirPath);
|
|
13780
14291
|
for (const entry of entries) {
|
|
13781
|
-
const fullPath =
|
|
14292
|
+
const fullPath = join23(dirPath, entry);
|
|
13782
14293
|
const stat = statSync5(fullPath);
|
|
13783
14294
|
if (stat.isDirectory()) {
|
|
13784
14295
|
this.scanDir(fullPath, skills);
|
|
@@ -14059,7 +14570,7 @@ var init_browser2 = __esm(() => {
|
|
|
14059
14570
|
|
|
14060
14571
|
// src/modules/indexer/walker.ts
|
|
14061
14572
|
import { readdirSync as readdirSync9, readFileSync as readFileSync20, statSync as statSync6, existsSync as existsSync31, watch } from "fs";
|
|
14062
|
-
import { join as
|
|
14573
|
+
import { join as join24, relative, extname as extname5 } from "path";
|
|
14063
14574
|
|
|
14064
14575
|
class Indexer {
|
|
14065
14576
|
baseDir;
|
|
@@ -14097,7 +14608,7 @@ class Indexer {
|
|
|
14097
14608
|
for (const entry of entries) {
|
|
14098
14609
|
if (count >= this.MAX_FILES)
|
|
14099
14610
|
return;
|
|
14100
|
-
const fullPath =
|
|
14611
|
+
const fullPath = join24(dir, entry);
|
|
14101
14612
|
const relPath = relative(this.baseDir, fullPath);
|
|
14102
14613
|
const stat = statSync6(fullPath);
|
|
14103
14614
|
if (stat.isDirectory()) {
|
|
@@ -14161,13 +14672,13 @@ var init_walker = __esm(() => {
|
|
|
14161
14672
|
|
|
14162
14673
|
// src/modules/indexer/cache.ts
|
|
14163
14674
|
import { readFileSync as readFileSync21, writeFileSync as writeFileSync13, existsSync as existsSync32, mkdirSync as mkdirSync14, rmSync as rmSync2 } from "fs";
|
|
14164
|
-
import { join as
|
|
14675
|
+
import { join as join25 } from "path";
|
|
14165
14676
|
|
|
14166
14677
|
class IndexCache {
|
|
14167
14678
|
cachePath;
|
|
14168
14679
|
cache = null;
|
|
14169
14680
|
constructor(cacheDir) {
|
|
14170
|
-
this.cachePath =
|
|
14681
|
+
this.cachePath = join25(cacheDir, "index-cache.json");
|
|
14171
14682
|
}
|
|
14172
14683
|
load() {
|
|
14173
14684
|
if (this.cache)
|
|
@@ -14183,7 +14694,7 @@ class IndexCache {
|
|
|
14183
14694
|
}
|
|
14184
14695
|
save(result) {
|
|
14185
14696
|
this.cache = result;
|
|
14186
|
-
const dir =
|
|
14697
|
+
const dir = join25(this.cachePath, "..");
|
|
14187
14698
|
if (!existsSync32(dir))
|
|
14188
14699
|
mkdirSync14(dir, { recursive: true });
|
|
14189
14700
|
writeFileSync13(this.cachePath, JSON.stringify(result), "utf-8");
|
|
@@ -14548,13 +15059,13 @@ var init_mcp = __esm(() => {
|
|
|
14548
15059
|
|
|
14549
15060
|
// src/modules/memory/module.ts
|
|
14550
15061
|
import { homedir as homedir10 } from "os";
|
|
14551
|
-
import { join as
|
|
15062
|
+
import { join as join26 } from "path";
|
|
14552
15063
|
|
|
14553
15064
|
class MemoryModule {
|
|
14554
15065
|
name = "memory";
|
|
14555
15066
|
store;
|
|
14556
15067
|
constructor(memoryDir) {
|
|
14557
|
-
const dir = memoryDir ||
|
|
15068
|
+
const dir = memoryDir || join26(homedir10(), ".mma", "memory");
|
|
14558
15069
|
this.store = new MemoryStore(dir);
|
|
14559
15070
|
}
|
|
14560
15071
|
getSystemPromptBlock() {
|
|
@@ -14601,7 +15112,7 @@ __export(exports_bootstrap, {
|
|
|
14601
15112
|
bootstrap: () => bootstrap
|
|
14602
15113
|
});
|
|
14603
15114
|
import { homedir as homedir11 } from "os";
|
|
14604
|
-
import { join as
|
|
15115
|
+
import { join as join27, resolve as resolve15 } from "path";
|
|
14605
15116
|
import { existsSync as existsSync33, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
|
|
14606
15117
|
function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
14607
15118
|
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
@@ -14611,21 +15122,20 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
14611
15122
|
`Date: ${now}`,
|
|
14612
15123
|
`Workspace: ${baseDir}`,
|
|
14613
15124
|
`${profileCompressed}`,
|
|
14614
|
-
`Reply in the user's language. Use tools for
|
|
14615
|
-
`Use tools when needed
|
|
14616
|
-
`
|
|
14617
|
-
`If a tool call fails, report the error and ask the user how to proceed.`,
|
|
15125
|
+
`Reply in the user's language. Use tools for: read/write/edit/delete files, search (glob/grep), shell commands (bash), web access (web_search, web_fetch), subagents, browser, MCP.`,
|
|
15126
|
+
`Use tools when needed — explain briefly what you're doing if it's not obvious.`,
|
|
15127
|
+
`If a tool call fails, analyze the error and correct the call — try up to 2 times with different approaches before asking the user for help.`,
|
|
14618
15128
|
``,
|
|
14619
|
-
`Design principles
|
|
14620
|
-
`- Do not add code, files, or abstractions
|
|
14621
|
-
`- Prefer simple, straightforward solutions over clever or complex ones
|
|
14622
|
-
`- Do not duplicate code, logic, or configuration
|
|
15129
|
+
`Design principles:`,
|
|
15130
|
+
`- YAGNI: Do not add code, files, or abstractions not needed right now.`,
|
|
15131
|
+
`- KISS: Prefer simple, straightforward solutions over clever or complex ones.`,
|
|
15132
|
+
`- DRY: Do not duplicate code, logic, or configuration — reuse existing utilities and patterns.`
|
|
14623
15133
|
];
|
|
14624
15134
|
if (isWin) {
|
|
14625
15135
|
lines.push(``, `Windows environment — use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type" or "Get-Content" instead of "cat".`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" — Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
|
|
14626
15136
|
}
|
|
14627
|
-
lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory.
|
|
14628
|
-
lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`, ``, `=== PLAN QUALITY RULES — your plan MUST follow these ===`, ``, `- Each step must describe CONCRETE deliverables: exact filenames with paths, exact packages to install, exact CLI commands to run.
|
|
15137
|
+
lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Prefer workdir over "cd dir && cmd" chaining.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`, `- Background processes (dev servers, watchers, long-running npm install): use the "background: true" parameter or the tool will auto-detect and background them. Check output with process_log.`);
|
|
15138
|
+
lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`, ``, `=== PLAN QUALITY RULES — your plan MUST follow these ===`, ``, `- Each step must describe CONCRETE deliverables: exact filenames with paths, exact packages to install, exact CLI commands to run. Avoid vague steps — be specific.`, `- A step like "Настройка проекта" or "Setup the project" is too vague — describe what exactly needs to be configured or set up.`, `- A step like "Создать src/components/Header.tsx с навигацией и логотипом, добавить в src/App.tsx импорт <Header />" is GOOD.`, `- Include file extensions (.tsx, .css, .json) and directory paths. Every step must mention at least one file or command.`, `- The plan must cover EVERYTHING needed: init → deps → framework setup → code → verification.`, `- Number of steps: 5-8 for a typical task. Too few means you're being vague. Too many means you're over-splitting.`);
|
|
14629
15139
|
if (config.autoPlan) {
|
|
14630
15140
|
lines.push(``, `Plan rule (MANDATORY): For ANY task that requires creating files, installing packages, or multiple actions — you MUST create a plan using the "plan" tool BEFORE starting work. Each step must describe a concrete deliverable (specific files to create, packages to install, commands to run). Do not combine unrelated work into one step.`);
|
|
14631
15141
|
}
|
|
@@ -14637,8 +15147,8 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
14637
15147
|
`);
|
|
14638
15148
|
}
|
|
14639
15149
|
async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
14640
|
-
const dir = configDir ||
|
|
14641
|
-
const projectConfigPath = projectDir ?
|
|
15150
|
+
const dir = configDir || join27(homedir11(), ".mma");
|
|
15151
|
+
const projectConfigPath = projectDir ? join27(projectDir, ".mmrc") : join27(process.cwd(), ".mmrc");
|
|
14642
15152
|
const config = loadConfig({ configDir: dir, projectConfigPath });
|
|
14643
15153
|
setLocale(config.locale);
|
|
14644
15154
|
try {
|
|
@@ -14648,7 +15158,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14648
15158
|
}
|
|
14649
15159
|
} catch {}
|
|
14650
15160
|
const logger = new Logger(config.logLevel);
|
|
14651
|
-
logger.setLogDir(
|
|
15161
|
+
logger.setLogDir(join27(dir, "logs"));
|
|
14652
15162
|
logger.debug("MMA bootstrap", {
|
|
14653
15163
|
version: config.version,
|
|
14654
15164
|
model: config.model
|
|
@@ -14670,7 +15180,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14670
15180
|
logger.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
|
|
14671
15181
|
}
|
|
14672
15182
|
}
|
|
14673
|
-
const profile = new UserProfile(
|
|
15183
|
+
const profile = new UserProfile(join27(dir));
|
|
14674
15184
|
profile.load() || profile.collect();
|
|
14675
15185
|
profile.save();
|
|
14676
15186
|
const llmProvider = new OpenAICompatProvider({
|
|
@@ -14682,7 +15192,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14682
15192
|
rateLimits: config.security?.rateLimits
|
|
14683
15193
|
});
|
|
14684
15194
|
const baseDir = projectDir ? resolve15(projectDir) : process.cwd();
|
|
14685
|
-
const projectMapCacheDir =
|
|
15195
|
+
const projectMapCacheDir = join27(baseDir, ".mma");
|
|
14686
15196
|
const indexerModule = new IndexerModule({
|
|
14687
15197
|
baseDir,
|
|
14688
15198
|
cacheDir: projectMapCacheDir
|
|
@@ -14693,9 +15203,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14693
15203
|
logger.warn(`Project indexing failed: ${err.message}`);
|
|
14694
15204
|
}
|
|
14695
15205
|
const skillsLoader = new SkillsLoader;
|
|
14696
|
-
const builtinDir =
|
|
14697
|
-
const globalDir =
|
|
14698
|
-
const projectSkillsDir =
|
|
15206
|
+
const builtinDir = join27(import.meta.dirname, "skills", "builtin");
|
|
15207
|
+
const globalDir = join27(homedir11(), ".agents", "skills");
|
|
15208
|
+
const projectSkillsDir = join27(baseDir, ".mma", "skills");
|
|
14699
15209
|
const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
|
|
14700
15210
|
const skillsMatcher = new SkillsMatcher;
|
|
14701
15211
|
const skillsBudget = Math.floor(config.contextWindow * 0.1);
|
|
@@ -14709,11 +15219,11 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14709
15219
|
essential: true,
|
|
14710
15220
|
estimatedTokens: 250
|
|
14711
15221
|
};
|
|
14712
|
-
const agentsMdGlobal =
|
|
15222
|
+
const agentsMdGlobal = join27(dir, "AGENTS.md");
|
|
14713
15223
|
if (!existsSync33(agentsMdGlobal)) {
|
|
14714
15224
|
writeFileSync14(agentsMdGlobal, "", "utf-8");
|
|
14715
15225
|
}
|
|
14716
|
-
const sessionDir =
|
|
15226
|
+
const sessionDir = join27(dir, "sessions");
|
|
14717
15227
|
const sessionStore = new SessionStore(sessionDir);
|
|
14718
15228
|
sessionStore.init();
|
|
14719
15229
|
const sessionManager = new SessionManager(sessionStore, {
|
|
@@ -14767,7 +15277,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14767
15277
|
const mcpModule = new MCPModule(config);
|
|
14768
15278
|
await mcpModule.initialize();
|
|
14769
15279
|
moduleRegistry.register(mcpModule);
|
|
14770
|
-
const memoryModule = new MemoryModule(
|
|
15280
|
+
const memoryModule = new MemoryModule(join27(dir, "memory"));
|
|
14771
15281
|
moduleRegistry.register(memoryModule);
|
|
14772
15282
|
if (config.browser.enabled) {
|
|
14773
15283
|
const browserModule = new BrowserModule;
|
|
@@ -14810,8 +15320,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14810
15320
|
pluginManager.register(plugin);
|
|
14811
15321
|
pluginManager.register(plugin2);
|
|
14812
15322
|
const pluginLoader = new PluginLoader;
|
|
14813
|
-
const globalPluginsDir =
|
|
14814
|
-
const projectPluginsDir =
|
|
15323
|
+
const globalPluginsDir = join27(homedir11(), ".mma", "plugins");
|
|
15324
|
+
const projectPluginsDir = join27(dir, ".mma", "plugins");
|
|
14815
15325
|
pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
|
|
14816
15326
|
pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
|
|
14817
15327
|
contextManager.onCompact = (summary) => {
|
|
@@ -14829,9 +15339,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
14829
15339
|
const skipAgentsMd = noAgentsMd === true;
|
|
14830
15340
|
if (!skipAgentsMd) {
|
|
14831
15341
|
const agentsMdCandidates = [
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
15342
|
+
join27(baseDir, "AGENTS.md"),
|
|
15343
|
+
join27(baseDir, ".mma", "AGENTS.md"),
|
|
15344
|
+
join27(dir, "AGENTS.md")
|
|
14835
15345
|
];
|
|
14836
15346
|
for (const p of agentsMdCandidates) {
|
|
14837
15347
|
if (existsSync33(p)) {
|
|
@@ -15158,7 +15668,7 @@ function pad(text, width, align) {
|
|
|
15158
15668
|
}
|
|
15159
15669
|
return text + " ".repeat(gap);
|
|
15160
15670
|
}
|
|
15161
|
-
function
|
|
15671
|
+
function truncate2(text, width) {
|
|
15162
15672
|
if (stringWidth(text) <= width)
|
|
15163
15673
|
return text;
|
|
15164
15674
|
let acc = "";
|
|
@@ -15244,7 +15754,7 @@ function formatTable(tableLines, opts = {}) {
|
|
|
15244
15754
|
const cells = [];
|
|
15245
15755
|
for (let c = 0;c < columnCount; c++) {
|
|
15246
15756
|
const raw = row[c] ?? "";
|
|
15247
|
-
const cell =
|
|
15757
|
+
const cell = truncate2(raw, widths[c]);
|
|
15248
15758
|
const a = isHeader && align[c] === undefined ? "center" : align[c] ?? "left";
|
|
15249
15759
|
cells.push(pad(cell, widths[c], a));
|
|
15250
15760
|
}
|
|
@@ -15668,14 +16178,14 @@ async function runSetup() {
|
|
|
15668
16178
|
|
|
15669
16179
|
// src/cli/commands.ts
|
|
15670
16180
|
init_i18n();
|
|
15671
|
-
import { join as
|
|
16181
|
+
import { join as join29, dirname as dirname9 } from "path";
|
|
15672
16182
|
import { homedir as homedir13 } from "os";
|
|
15673
16183
|
import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
|
|
15674
16184
|
|
|
15675
16185
|
// src/cli/security-commands.ts
|
|
15676
16186
|
init_bootstrap();
|
|
15677
16187
|
init_config();
|
|
15678
|
-
import { join as
|
|
16188
|
+
import { join as join28 } from "path";
|
|
15679
16189
|
import { homedir as homedir12 } from "os";
|
|
15680
16190
|
|
|
15681
16191
|
// src/modules/security/security-policies.ts
|
|
@@ -15686,7 +16196,9 @@ var STRICT_POLICY = {
|
|
|
15686
16196
|
preset: "strict",
|
|
15687
16197
|
recommendedFor: ["production", "sensitive-data", "multi-user", "enterprise"],
|
|
15688
16198
|
config: {
|
|
16199
|
+
enabled: true,
|
|
15689
16200
|
bash: {
|
|
16201
|
+
enabled: true,
|
|
15690
16202
|
blacklist: [
|
|
15691
16203
|
"rm",
|
|
15692
16204
|
"dd",
|
|
@@ -15745,10 +16257,22 @@ var STRICT_POLICY = {
|
|
|
15745
16257
|
"-R",
|
|
15746
16258
|
"--no-clobber"
|
|
15747
16259
|
],
|
|
15748
|
-
dangerousOperators: [
|
|
16260
|
+
dangerousOperators: [
|
|
16261
|
+
">",
|
|
16262
|
+
">>",
|
|
16263
|
+
"2>",
|
|
16264
|
+
"2>>",
|
|
16265
|
+
"|",
|
|
16266
|
+
"&&",
|
|
16267
|
+
"||",
|
|
16268
|
+
";",
|
|
16269
|
+
"&",
|
|
16270
|
+
"`"
|
|
16271
|
+
],
|
|
15749
16272
|
logCommands: true
|
|
15750
16273
|
},
|
|
15751
16274
|
paths: {
|
|
16275
|
+
enabled: true,
|
|
15752
16276
|
denied: [
|
|
15753
16277
|
".git/",
|
|
15754
16278
|
"node_modules/",
|
|
@@ -15781,6 +16305,7 @@ var STRICT_POLICY = {
|
|
|
15781
16305
|
allowed: []
|
|
15782
16306
|
},
|
|
15783
16307
|
network: {
|
|
16308
|
+
enabled: true,
|
|
15784
16309
|
deniedDomains: [
|
|
15785
16310
|
"localhost",
|
|
15786
16311
|
"127.0.0.1",
|
|
@@ -15852,7 +16377,9 @@ var BALANCED_POLICY = {
|
|
|
15852
16377
|
preset: "balanced",
|
|
15853
16378
|
recommendedFor: ["development", "single-user", "general-use", "testing"],
|
|
15854
16379
|
config: {
|
|
16380
|
+
enabled: true,
|
|
15855
16381
|
bash: {
|
|
16382
|
+
enabled: true,
|
|
15856
16383
|
blacklist: [
|
|
15857
16384
|
"rm",
|
|
15858
16385
|
"dd",
|
|
@@ -15898,6 +16425,7 @@ var BALANCED_POLICY = {
|
|
|
15898
16425
|
logCommands: true
|
|
15899
16426
|
},
|
|
15900
16427
|
paths: {
|
|
16428
|
+
enabled: true,
|
|
15901
16429
|
denied: [
|
|
15902
16430
|
".git/",
|
|
15903
16431
|
"node_modules/",
|
|
@@ -15918,6 +16446,7 @@ var BALANCED_POLICY = {
|
|
|
15918
16446
|
allowed: []
|
|
15919
16447
|
},
|
|
15920
16448
|
network: {
|
|
16449
|
+
enabled: true,
|
|
15921
16450
|
deniedDomains: [],
|
|
15922
16451
|
allowedDomains: [],
|
|
15923
16452
|
requestTimeout: 15000,
|
|
@@ -15953,7 +16482,12 @@ var BALANCED_POLICY = {
|
|
|
15953
16482
|
auditNotifier: {
|
|
15954
16483
|
enabled: false,
|
|
15955
16484
|
minSeverity: "medium",
|
|
15956
|
-
eventTypes: [
|
|
16485
|
+
eventTypes: [
|
|
16486
|
+
"security_block",
|
|
16487
|
+
"bash_command",
|
|
16488
|
+
"file_operation",
|
|
16489
|
+
"network_request"
|
|
16490
|
+
],
|
|
15957
16491
|
maxRetries: 3
|
|
15958
16492
|
}
|
|
15959
16493
|
}
|
|
@@ -15964,7 +16498,9 @@ var PERMISSIVE_POLICY = {
|
|
|
15964
16498
|
preset: "permissive",
|
|
15965
16499
|
recommendedFor: ["trusted-environment", "local-dev", "testing", "sandbox"],
|
|
15966
16500
|
config: {
|
|
16501
|
+
enabled: true,
|
|
15967
16502
|
bash: {
|
|
16503
|
+
enabled: true,
|
|
15968
16504
|
blacklist: [
|
|
15969
16505
|
"rm",
|
|
15970
16506
|
"dd",
|
|
@@ -15984,10 +16520,12 @@ var PERMISSIVE_POLICY = {
|
|
|
15984
16520
|
logCommands: true
|
|
15985
16521
|
},
|
|
15986
16522
|
paths: {
|
|
16523
|
+
enabled: true,
|
|
15987
16524
|
denied: [".env", "*.key", "*.pem", "*.crt", "*.p12", "*.pfx"],
|
|
15988
16525
|
allowed: []
|
|
15989
16526
|
},
|
|
15990
16527
|
network: {
|
|
16528
|
+
enabled: true,
|
|
15991
16529
|
deniedDomains: [],
|
|
15992
16530
|
allowedDomains: [],
|
|
15993
16531
|
requestTimeout: 30000,
|
|
@@ -16177,7 +16715,7 @@ function createSecurityCommand(program2) {
|
|
|
16177
16715
|
}
|
|
16178
16716
|
});
|
|
16179
16717
|
securityCmd.command("set-policy").argument("<preset>", t("cli.security.preset")).description(t("cli.security.set_policy")).action(async (preset) => {
|
|
16180
|
-
const configPath =
|
|
16718
|
+
const configPath = join28(homedir12(), ".mma", "config.json");
|
|
16181
16719
|
const { config: appConfig } = await bootstrap();
|
|
16182
16720
|
const validPresets = ["strict", "balanced", "permissive"];
|
|
16183
16721
|
if (!validPresets.includes(preset)) {
|
|
@@ -16192,7 +16730,7 @@ function createSecurityCommand(program2) {
|
|
|
16192
16730
|
console.log(t("cli.security.policy_description", { description: policy.description }));
|
|
16193
16731
|
});
|
|
16194
16732
|
securityCmd.command("enable-encryption").description(t("cli.security.enable_encryption")).action(async () => {
|
|
16195
|
-
const configPath =
|
|
16733
|
+
const configPath = join28(homedir12(), ".mma", "config.json");
|
|
16196
16734
|
const { config: appConfig } = await bootstrap();
|
|
16197
16735
|
appConfig.security = appConfig.security || {};
|
|
16198
16736
|
appConfig.security.sessionEncryption = {
|
|
@@ -16204,7 +16742,7 @@ function createSecurityCommand(program2) {
|
|
|
16204
16742
|
console.log(t("cli.security.encryption_enabled"));
|
|
16205
16743
|
});
|
|
16206
16744
|
securityCmd.command("disable-encryption").description(t("cli.security.disable_encryption")).action(async () => {
|
|
16207
|
-
const configPath =
|
|
16745
|
+
const configPath = join28(homedir12(), ".mma", "config.json");
|
|
16208
16746
|
const { config: appConfig } = await bootstrap();
|
|
16209
16747
|
appConfig.security = appConfig.security || {};
|
|
16210
16748
|
appConfig.security.sessionEncryption = {
|
|
@@ -16216,7 +16754,7 @@ function createSecurityCommand(program2) {
|
|
|
16216
16754
|
console.log(t("cli.security.encryption_disabled"));
|
|
16217
16755
|
});
|
|
16218
16756
|
securityCmd.command("enable-audit").description(t("cli.security.enable_audit")).action(async () => {
|
|
16219
|
-
const configPath =
|
|
16757
|
+
const configPath = join28(homedir12(), ".mma", "config.json");
|
|
16220
16758
|
const { config: appConfig } = await bootstrap();
|
|
16221
16759
|
appConfig.security = appConfig.security || {};
|
|
16222
16760
|
appConfig.security.auditNotifier = {
|
|
@@ -16230,7 +16768,7 @@ function createSecurityCommand(program2) {
|
|
|
16230
16768
|
console.log(t("cli.security.audit_enabled"));
|
|
16231
16769
|
});
|
|
16232
16770
|
securityCmd.command("disable-audit").description(t("cli.security.disable_audit")).action(async () => {
|
|
16233
|
-
const configPath =
|
|
16771
|
+
const configPath = join28(homedir12(), ".mma", "config.json");
|
|
16234
16772
|
const { config: appConfig } = await bootstrap();
|
|
16235
16773
|
appConfig.security = appConfig.security || {};
|
|
16236
16774
|
appConfig.security.auditNotifier = {
|
|
@@ -16266,8 +16804,8 @@ import { fileURLToPath } from "url";
|
|
|
16266
16804
|
function readVersion() {
|
|
16267
16805
|
const here = dirname9(fileURLToPath(import.meta.url));
|
|
16268
16806
|
const candidates = [
|
|
16269
|
-
|
|
16270
|
-
|
|
16807
|
+
join29(here, "..", "..", "package.json"),
|
|
16808
|
+
join29(here, "..", "package.json")
|
|
16271
16809
|
];
|
|
16272
16810
|
for (const p of candidates) {
|
|
16273
16811
|
if (existsSync34(p)) {
|
|
@@ -16283,7 +16821,7 @@ function createProgram() {
|
|
|
16283
16821
|
const program2 = new Command().name("mma").description(t("cli.description")).version(version).option("--no-agents-md", t("cli.no_agents_md")).option("-d, --dir <path>", t("cli.dir")).option("-e, --exit-on-complete", t("cli.exit_on_complete")).option("-j, --json", t("cli.json"));
|
|
16284
16822
|
program2.command("init").description(t("cli.init")).action(async () => {
|
|
16285
16823
|
const answers = await runSetup();
|
|
16286
|
-
const configPath =
|
|
16824
|
+
const configPath = join29(homedir13(), ".mma", "config.json");
|
|
16287
16825
|
const { config } = await bootstrap();
|
|
16288
16826
|
config.provider.type = answers.provider;
|
|
16289
16827
|
config.provider.baseUrl = answers.apiBase;
|
|
@@ -16292,12 +16830,43 @@ function createProgram() {
|
|
|
16292
16830
|
config.contextWindow = answers.contextWindow;
|
|
16293
16831
|
config.maxToolIterations = answers.maxToolIterations;
|
|
16294
16832
|
config.locale = answers.locale;
|
|
16833
|
+
if (config.security) {
|
|
16834
|
+
config.security.enabled = answers.securityBashBlock || answers.securityFlagsBlock || !answers.securityPathsDeny;
|
|
16835
|
+
config.security.bash.enabled = config.security.enabled;
|
|
16836
|
+
config.security.bash.blockDangerousFlags = answers.securityFlagsBlock;
|
|
16837
|
+
if (answers.securityBashBlock) {
|
|
16838
|
+
config.security.bash.blacklist = [
|
|
16839
|
+
...new Set([
|
|
16840
|
+
...config.security.bash.blacklist,
|
|
16841
|
+
"rm",
|
|
16842
|
+
"dd",
|
|
16843
|
+
"chmod",
|
|
16844
|
+
"wget",
|
|
16845
|
+
"curl",
|
|
16846
|
+
"scp",
|
|
16847
|
+
"ssh",
|
|
16848
|
+
"nc",
|
|
16849
|
+
"netcat",
|
|
16850
|
+
"sudo",
|
|
16851
|
+
"su",
|
|
16852
|
+
"kill",
|
|
16853
|
+
"pkill",
|
|
16854
|
+
"killall",
|
|
16855
|
+
"shutdown",
|
|
16856
|
+
"reboot"
|
|
16857
|
+
])
|
|
16858
|
+
];
|
|
16859
|
+
}
|
|
16860
|
+
if (!answers.securityPathsDeny) {
|
|
16861
|
+
config.security.paths.denied = [];
|
|
16862
|
+
}
|
|
16863
|
+
}
|
|
16295
16864
|
saveConfig(config, configPath);
|
|
16296
16865
|
console.log(t("cli.config_saved"));
|
|
16297
16866
|
});
|
|
16298
16867
|
const configCmd = program2.command("config").description(t("cli.manage_config"));
|
|
16299
16868
|
configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
|
|
16300
|
-
const configPath =
|
|
16869
|
+
const configPath = join29(homedir13(), ".mma", "config.json");
|
|
16301
16870
|
const { config } = await bootstrap();
|
|
16302
16871
|
const keys = key.split(".");
|
|
16303
16872
|
let obj = config;
|
|
@@ -16357,14 +16926,14 @@ function createProgram() {
|
|
|
16357
16926
|
console.log(t("cli.model_hint"));
|
|
16358
16927
|
});
|
|
16359
16928
|
model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
|
|
16360
|
-
const configPath =
|
|
16929
|
+
const configPath = join29(homedir13(), ".mma", "config.json");
|
|
16361
16930
|
const { config } = await bootstrap();
|
|
16362
16931
|
config.model = name;
|
|
16363
16932
|
saveConfig(config, configPath);
|
|
16364
16933
|
console.log(t("cli.model_set", { name }));
|
|
16365
16934
|
});
|
|
16366
16935
|
program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
|
|
16367
|
-
const configPath =
|
|
16936
|
+
const configPath = join29(homedir13(), ".mma", "config.json");
|
|
16368
16937
|
const { config } = await bootstrap();
|
|
16369
16938
|
const contextWindow = parseInt(size, 10);
|
|
16370
16939
|
if (isNaN(contextWindow) || contextWindow < 1024) {
|
|
@@ -16382,7 +16951,7 @@ function createProgram() {
|
|
|
16382
16951
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
16383
16952
|
});
|
|
16384
16953
|
provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
|
|
16385
|
-
const configPath =
|
|
16954
|
+
const configPath = join29(homedir13(), ".mma", "config.json");
|
|
16386
16955
|
const { config } = await bootstrap();
|
|
16387
16956
|
config.provider.type = name;
|
|
16388
16957
|
saveConfig(config, configPath);
|
|
@@ -16435,7 +17004,7 @@ init_bootstrap();
|
|
|
16435
17004
|
init_colors();
|
|
16436
17005
|
import * as readline2 from "readline";
|
|
16437
17006
|
import { existsSync as existsSync35, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
|
|
16438
|
-
import { join as
|
|
17007
|
+
import { join as join30, dirname as dirname10 } from "path";
|
|
16439
17008
|
import { homedir as homedir14 } from "os";
|
|
16440
17009
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16441
17010
|
|
|
@@ -16751,7 +17320,7 @@ init_i18n();
|
|
|
16751
17320
|
function isRichTerminal() {
|
|
16752
17321
|
return Boolean(process.stdout.isTTY) && !process.env.CI;
|
|
16753
17322
|
}
|
|
16754
|
-
function
|
|
17323
|
+
function summarizeArgs2(args) {
|
|
16755
17324
|
const preferred = ["path", "file", "query", "url", "command", "name"];
|
|
16756
17325
|
for (const key of preferred) {
|
|
16757
17326
|
const value = args[key];
|
|
@@ -16817,7 +17386,7 @@ class Renderer {
|
|
|
16817
17386
|
toolStart(tool, args) {
|
|
16818
17387
|
this.endCard();
|
|
16819
17388
|
this.spinner.stop();
|
|
16820
|
-
const summary =
|
|
17389
|
+
const summary = summarizeArgs2(args);
|
|
16821
17390
|
if (!this.rich) {
|
|
16822
17391
|
this.out.write(`
|
|
16823
17392
|
${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}
|
|
@@ -16827,15 +17396,21 @@ ${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}
|
|
|
16827
17396
|
this.card = { tool, args, body: [], start: Date.now() };
|
|
16828
17397
|
this.spinner.start(`${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}`);
|
|
16829
17398
|
}
|
|
16830
|
-
toolEnd(_tool, duration, error) {
|
|
17399
|
+
toolEnd(_tool, duration, error, ctxDelta) {
|
|
16831
17400
|
this.spinner.stop();
|
|
16832
|
-
if (!this.rich)
|
|
17401
|
+
if (!this.rich) {
|
|
17402
|
+
if (ctxDelta !== undefined && ctxDelta !== 0) {
|
|
17403
|
+
const deltaStr = ctxDelta > 0 ? pc.green(`+${ctxDelta}`) : pc.yellow(`${ctxDelta} ↓`);
|
|
17404
|
+
this.out.write(`${pc.dim("ctx")} ${deltaStr}
|
|
17405
|
+
`);
|
|
17406
|
+
}
|
|
16833
17407
|
return;
|
|
17408
|
+
}
|
|
16834
17409
|
if (!this.card)
|
|
16835
17410
|
return;
|
|
16836
17411
|
const { tool, args, body } = this.card;
|
|
16837
17412
|
const lines = [];
|
|
16838
|
-
const summary =
|
|
17413
|
+
const summary = summarizeArgs2(args);
|
|
16839
17414
|
if (summary)
|
|
16840
17415
|
lines.push(pc.dim(summary));
|
|
16841
17416
|
for (const chunk of body) {
|
|
@@ -16846,7 +17421,12 @@ ${pc.dim("⚙")} ${friendlyTool(tool)}${summary ? ` ${pc.dim(summary)}` : ""}
|
|
|
16846
17421
|
}
|
|
16847
17422
|
}
|
|
16848
17423
|
const marker = error ? pc.red("✗") : pc.green("✓");
|
|
16849
|
-
|
|
17424
|
+
let footer = `${marker} ${pc.dim(`${duration}ms`)}`;
|
|
17425
|
+
if (ctxDelta !== undefined && ctxDelta !== 0) {
|
|
17426
|
+
const deltaStr = ctxDelta > 0 ? pc.green(`+${ctxDelta}`) : pc.yellow(`${ctxDelta} ↓`);
|
|
17427
|
+
footer += ` ${pc.dim("ctx")} ${deltaStr}`;
|
|
17428
|
+
}
|
|
17429
|
+
lines.push(footer);
|
|
16850
17430
|
const title = `${marker} ${friendlyTool(tool)}`;
|
|
16851
17431
|
for (const line of box(lines, { title, width: this.width })) {
|
|
16852
17432
|
this.out.write(`${line}
|
|
@@ -16885,8 +17465,8 @@ init_config();
|
|
|
16885
17465
|
function readVersion2() {
|
|
16886
17466
|
const here = dirname10(fileURLToPath2(import.meta.url));
|
|
16887
17467
|
const candidates = [
|
|
16888
|
-
|
|
16889
|
-
|
|
17468
|
+
join30(here, "..", "..", "package.json"),
|
|
17469
|
+
join30(here, "..", "package.json")
|
|
16890
17470
|
];
|
|
16891
17471
|
for (const p of candidates) {
|
|
16892
17472
|
if (existsSync35(p)) {
|
|
@@ -16919,13 +17499,21 @@ var COMMAND_GROUPS = {
|
|
|
16919
17499
|
delete: "session",
|
|
16920
17500
|
skill: "skill"
|
|
16921
17501
|
};
|
|
16922
|
-
function formatContextBar(used, limit) {
|
|
17502
|
+
function formatContextBar(used, limit, compactions, quality) {
|
|
16923
17503
|
const pct = Math.min(100, Math.round(used / limit * 100));
|
|
16924
17504
|
const barLen = 20;
|
|
16925
17505
|
const filled = Math.round(pct / 100 * barLen);
|
|
16926
17506
|
const bar = pc.green("█".repeat(filled)) + pc.dim("░".repeat(barLen - filled));
|
|
16927
17507
|
const pctStr = pct >= 75 ? pc.yellow(`${pct}%`) : pc.dim(`${pct}%`);
|
|
16928
|
-
|
|
17508
|
+
let line = ` ${bar} ${pctStr} ${pc.dim(`(${used} / ${limit} tokens)`)}`;
|
|
17509
|
+
if (compactions !== undefined) {
|
|
17510
|
+
line += pc.dim(` compactions: ${compactions}`);
|
|
17511
|
+
}
|
|
17512
|
+
if (quality !== undefined) {
|
|
17513
|
+
const qColor = quality >= 70 ? pc.green : quality >= 40 ? pc.yellow : pc.red;
|
|
17514
|
+
line += ` ${qColor(`quality: ${quality}%`)}`;
|
|
17515
|
+
}
|
|
17516
|
+
return line;
|
|
16929
17517
|
}
|
|
16930
17518
|
|
|
16931
17519
|
class Repl {
|
|
@@ -16954,10 +17542,10 @@ class Repl {
|
|
|
16954
17542
|
this.sessionManager = sessionManager;
|
|
16955
17543
|
this.skillsModule = skillsModule;
|
|
16956
17544
|
this.pluginManager = pluginManager;
|
|
16957
|
-
this.configDir = configDir ||
|
|
17545
|
+
this.configDir = configDir || join30(homedir14(), ".mma");
|
|
16958
17546
|
this.baseDir = baseDir || process.cwd();
|
|
16959
17547
|
this.noAgentsMd = noAgentsMd === true;
|
|
16960
|
-
this.historyPath =
|
|
17548
|
+
this.historyPath = join30(homedir14(), ".mma", "repl-history");
|
|
16961
17549
|
this.loadHistory();
|
|
16962
17550
|
this.registerBuiltinCommands();
|
|
16963
17551
|
this.registerMmaCommands();
|
|
@@ -17143,7 +17731,7 @@ class Repl {
|
|
|
17143
17731
|
action: async () => {
|
|
17144
17732
|
console.log(pc.yellow(t("repl.wizard_running")));
|
|
17145
17733
|
const answers = await runSetup();
|
|
17146
|
-
const configPath =
|
|
17734
|
+
const configPath = join30(homedir14(), ".mma", "config.json");
|
|
17147
17735
|
this.config.provider.type = answers.provider;
|
|
17148
17736
|
this.config.provider.baseUrl = answers.apiBase;
|
|
17149
17737
|
this.config.provider.apiKey = answers.apiKey;
|
|
@@ -17173,7 +17761,7 @@ class Repl {
|
|
|
17173
17761
|
return;
|
|
17174
17762
|
}
|
|
17175
17763
|
this.config.provider.type = name;
|
|
17176
|
-
const configPath =
|
|
17764
|
+
const configPath = join30(homedir14(), ".mma", "config.json");
|
|
17177
17765
|
saveConfig(this.config, configPath);
|
|
17178
17766
|
console.log(pc.green(t("repl.provider_set", { name })));
|
|
17179
17767
|
return;
|
|
@@ -17225,7 +17813,7 @@ class Repl {
|
|
|
17225
17813
|
return;
|
|
17226
17814
|
}
|
|
17227
17815
|
this.config.model = name;
|
|
17228
|
-
const configPath =
|
|
17816
|
+
const configPath = join30(homedir14(), ".mma", "config.json");
|
|
17229
17817
|
saveConfig(this.config, configPath);
|
|
17230
17818
|
console.log(pc.green(t("repl.model_set", { name })));
|
|
17231
17819
|
return;
|
|
@@ -17249,7 +17837,7 @@ class Repl {
|
|
|
17249
17837
|
return;
|
|
17250
17838
|
}
|
|
17251
17839
|
this.config.contextWindow = size;
|
|
17252
|
-
const configPath =
|
|
17840
|
+
const configPath = join30(homedir14(), ".mma", "config.json");
|
|
17253
17841
|
saveConfig(this.config, configPath);
|
|
17254
17842
|
console.log(pc.green(t("cli.context_set", { size })));
|
|
17255
17843
|
}
|
|
@@ -17267,9 +17855,9 @@ class Repl {
|
|
|
17267
17855
|
this.agent.shutdown();
|
|
17268
17856
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
17269
17857
|
const { homedir: homedir15 } = await import("os");
|
|
17270
|
-
const { join:
|
|
17271
|
-
const configDir =
|
|
17272
|
-
const projectConfigPath =
|
|
17858
|
+
const { join: join31 } = await import("path");
|
|
17859
|
+
const configDir = join31(homedir15(), ".mma");
|
|
17860
|
+
const projectConfigPath = join31(process.cwd(), ".mmrc");
|
|
17273
17861
|
const freshConfig = loadConfig2({ configDir, projectConfigPath });
|
|
17274
17862
|
Object.assign(this.config, freshConfig);
|
|
17275
17863
|
const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
|
|
@@ -17679,7 +18267,7 @@ ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
|
17679
18267
|
if (ev.type === "start") {
|
|
17680
18268
|
renderer.toolStart(ev.tool, ev.args);
|
|
17681
18269
|
} else {
|
|
17682
|
-
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error);
|
|
18270
|
+
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
|
|
17683
18271
|
}
|
|
17684
18272
|
}, (phase) => {
|
|
17685
18273
|
if (phase === "thinking") {
|
|
@@ -17759,7 +18347,7 @@ ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
|
17759
18347
|
showContextBar(result) {
|
|
17760
18348
|
if (result.contextUsed !== undefined && result.contextLimit !== undefined && result.contextLimit > 0) {
|
|
17761
18349
|
console.log();
|
|
17762
|
-
const ctxLine = formatContextBar(result.contextUsed, result.contextLimit);
|
|
18350
|
+
const ctxLine = formatContextBar(result.contextUsed, result.contextLimit, result.compactionCount, result.contextQuality);
|
|
17763
18351
|
console.log(ctxLine);
|
|
17764
18352
|
if (result.totalTokens !== undefined && result.totalTokens > 0) {
|
|
17765
18353
|
const apiLine = pc.dim(` API: ${result.promptTokens} prompt + ${result.completionTokens} completion = ${result.totalTokens} total`);
|
|
@@ -17803,9 +18391,9 @@ ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
|
17803
18391
|
row(t("repl.agents_label"), pc.red(t("repl.disabled")));
|
|
17804
18392
|
} else {
|
|
17805
18393
|
const agentsMdCandidates = [
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
18394
|
+
join30(this.baseDir, "AGENTS.md"),
|
|
18395
|
+
join30(this.baseDir, ".mma", "AGENTS.md"),
|
|
18396
|
+
join30(this.configDir, "AGENTS.md")
|
|
17809
18397
|
];
|
|
17810
18398
|
const foundAgents = agentsMdCandidates.filter((p) => existsSync35(p));
|
|
17811
18399
|
if (foundAgents.length > 0) {
|
|
@@ -17818,7 +18406,7 @@ ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
|
17818
18406
|
}
|
|
17819
18407
|
const meta = this.sessionManager?.getActiveMeta();
|
|
17820
18408
|
if (meta) {
|
|
17821
|
-
const sessionPath =
|
|
18409
|
+
const sessionPath = join30(this.configDir, "sessions", meta.id);
|
|
17822
18410
|
row(t("repl.session_label"), `${pc.cyan(meta.name)} ${pc.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc.dim(sessionPath)}`);
|
|
17823
18411
|
}
|
|
17824
18412
|
const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
|
|
@@ -17844,7 +18432,7 @@ init_config();
|
|
|
17844
18432
|
init_i18n();
|
|
17845
18433
|
init_colors();
|
|
17846
18434
|
import { existsSync as existsSync36 } from "fs";
|
|
17847
|
-
import { join as
|
|
18435
|
+
import { join as join31 } from "path";
|
|
17848
18436
|
import { homedir as homedir15 } from "os";
|
|
17849
18437
|
async function main() {
|
|
17850
18438
|
const program2 = createProgram();
|
|
@@ -17885,7 +18473,7 @@ async function main() {
|
|
|
17885
18473
|
if (ev.type === "start") {
|
|
17886
18474
|
renderer.toolStart(ev.tool, ev.args);
|
|
17887
18475
|
} else {
|
|
17888
|
-
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error);
|
|
18476
|
+
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
|
|
17889
18477
|
}
|
|
17890
18478
|
}, (phase) => {
|
|
17891
18479
|
if (phase === "thinking") {
|
|
@@ -17908,7 +18496,7 @@ async function main() {
|
|
|
17908
18496
|
}
|
|
17909
18497
|
agent.shutdown();
|
|
17910
18498
|
} else {
|
|
17911
|
-
const configPath =
|
|
18499
|
+
const configPath = join31(homedir15(), ".mma", "config.json");
|
|
17912
18500
|
if (!existsSync36(configPath)) {
|
|
17913
18501
|
console.log(pc.yellow(`
|
|
17914
18502
|
` + t("cli.first_run") + `
|
|
@@ -17923,6 +18511,8 @@ async function main() {
|
|
|
17923
18511
|
config2.maxToolIterations = answers.maxToolIterations;
|
|
17924
18512
|
config2.locale = answers.locale;
|
|
17925
18513
|
if (config2.security) {
|
|
18514
|
+
config2.security.enabled = answers.securityBashBlock || answers.securityFlagsBlock || !answers.securityPathsDeny;
|
|
18515
|
+
config2.security.bash.enabled = config2.security.enabled;
|
|
17926
18516
|
config2.security.bash.blockDangerousFlags = answers.securityFlagsBlock;
|
|
17927
18517
|
if (answers.securityBashBlock) {
|
|
17928
18518
|
config2.security.bash.blacklist = [
|