micro-models-agent 0.28.8 → 0.28.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands.js +333 -0
- package/dist/cli/completer.js +168 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/main.js +140 -0
- package/dist/cli/repl-commands.js +633 -0
- package/dist/cli/repl.js +486 -0
- package/dist/cli/security-commands.js +166 -0
- package/dist/cli/setup.js +249 -0
- package/dist/config/config.js +202 -0
- package/dist/config/defaults.js +100 -0
- package/dist/config/experts.js +15 -0
- package/dist/config/index.js +3 -0
- package/dist/config/security.js +200 -0
- package/dist/config/types.js +1 -0
- package/dist/core/agent-moe.js +110 -0
- package/dist/core/agent.js +695 -0
- package/dist/core/bootstrap.js +337 -0
- package/dist/core/index.js +2 -0
- package/dist/core/prompt-builder.js +55 -0
- package/dist/core/session-logger.js +155 -0
- package/dist/core/types.js +1 -0
- package/dist/core/workspace.js +76 -0
- package/dist/i18n/en.json +525 -0
- package/dist/i18n/index.js +46 -0
- package/dist/i18n/ru.json +525 -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 +353 -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 +143 -0
- package/dist/logger/file-log.js +151 -0
- package/dist/logger/index.js +1 -0
- package/dist/main.js +1758 -612
- 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 +335 -0
- package/dist/modules/browser/snapshot.js +114 -0
- package/dist/modules/browser/types.js +9 -0
- package/dist/modules/certification/cli.js +176 -0
- package/dist/modules/certification/fact-checker.js +84 -0
- package/dist/modules/certification/loader.js +111 -0
- package/dist/modules/certification/manifest.js +50 -0
- package/dist/modules/certification/runner.js +162 -0
- package/dist/modules/certification/scenarios.js +124 -0
- package/dist/modules/certification/types.js +1 -0
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +349 -0
- package/dist/modules/execution/auditor.js +66 -0
- package/dist/modules/execution/index.js +8 -0
- package/dist/modules/execution/module.js +779 -0
- package/dist/modules/execution/moe-executor.js +266 -0
- package/dist/modules/execution/plan-coverage.js +68 -0
- package/dist/modules/execution/plan-persister.js +46 -0
- package/dist/modules/execution/plan-store.js +159 -0
- package/dist/modules/execution/plan-validator.js +153 -0
- package/dist/modules/execution/planner.js +85 -0
- package/dist/modules/execution/stuck-detector.js +347 -0
- package/dist/modules/execution/tracker.js +67 -0
- package/dist/modules/execution/types.js +1 -0
- package/dist/modules/execution/verifier.js +178 -0
- package/dist/modules/hallucination/confidence.js +59 -0
- package/dist/modules/hallucination/consistency.js +26 -0
- package/dist/modules/hallucination/detector.js +46 -0
- package/dist/modules/hallucination/factual.js +190 -0
- package/dist/modules/hallucination/index.js +5 -0
- package/dist/modules/hallucination/js-identifiers.js +72 -0
- package/dist/modules/hallucination/llm-judge.js +103 -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/lsp/client.js +235 -0
- package/dist/modules/lsp/config.js +81 -0
- package/dist/modules/lsp/index.js +3 -0
- package/dist/modules/lsp/module.js +68 -0
- package/dist/modules/lsp/types.js +1 -0
- package/dist/modules/mcp/client.js +399 -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 +69 -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 +226 -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/index.js +2 -0
- package/dist/modules/processes/registry.js +238 -0
- package/dist/modules/processes/runner.js +23 -0
- package/dist/modules/registry.js +45 -0
- package/dist/modules/security/audit-log.js +136 -0
- package/dist/modules/security/audit-notifier.js +292 -0
- package/dist/modules/security/command-validator.js +211 -0
- package/dist/modules/security/content-scanner.js +53 -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 +209 -0
- package/dist/modules/security/rate-limiter.js +119 -0
- package/dist/modules/security/security-policies.js +547 -0
- package/dist/modules/security/session-encryption.js +210 -0
- package/dist/modules/security/session-isolation.js +95 -0
- package/dist/modules/session/index.js +3 -0
- package/dist/modules/session/manager.js +172 -0
- package/dist/modules/session/module.js +24 -0
- package/dist/modules/session/store.js +228 -0
- package/dist/modules/session/types.js +1 -0
- package/dist/modules/skills/index.js +2 -0
- package/dist/modules/skills/loader.js +72 -0
- package/dist/modules/skills/module.js +130 -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 +337 -0
- package/dist/tools/browser.js +97 -0
- package/dist/tools/create-dir.js +55 -0
- package/dist/tools/delete-file.js +62 -0
- package/dist/tools/edit-file.js +79 -0
- package/dist/tools/executor.js +145 -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 +86 -0
- package/dist/tools/index.js +67 -0
- package/dist/tools/list-dir.js +47 -0
- package/dist/tools/load-skill.js +44 -0
- package/dist/tools/mcp-call.js +68 -0
- package/dist/tools/move-file.js +85 -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 +83 -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 +84 -0
- package/dist/tools/subagent.js +151 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/user-input.js +123 -0
- package/dist/tools/web-browse.js +86 -0
- package/dist/tools/web-fetch.js +98 -0
- package/dist/tools/web-search.js +78 -0
- package/dist/tools/write-file.js +83 -0
- package/dist/ui/box.js +81 -0
- package/dist/ui/colors.js +4 -0
- package/dist/ui/diff.js +178 -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 +204 -0
- package/dist/ui/spinner.js +70 -0
- package/dist/ui/table.js +144 -0
- package/package.json +4 -4
package/dist/main.js
CHANGED
|
@@ -2278,6 +2278,7 @@ var init_en = __esm(() => {
|
|
|
2278
2278
|
{str}`,
|
|
2279
2279
|
"file.moved": "Moved {from} → {to}",
|
|
2280
2280
|
"file.not_found_short": "Not found: {path}",
|
|
2281
|
+
"file.notfound_resolved": "File not found: {path} (resolved to {resolved})",
|
|
2281
2282
|
"file.empty": "(empty)",
|
|
2282
2283
|
"file.no_matches": "No matches",
|
|
2283
2284
|
"file.truncated": `
|
|
@@ -2295,6 +2296,7 @@ var init_en = __esm(() => {
|
|
|
2295
2296
|
"error.llm": "LLM error: {message}",
|
|
2296
2297
|
"error.response_blocked": "Response blocked: {reason}",
|
|
2297
2298
|
"error.max_iters": "Max iterations ({max}) reached",
|
|
2299
|
+
"error.empty_response": "Model returned an empty response after retries",
|
|
2298
2300
|
"error.grep_failed": "Grep failed: {message}",
|
|
2299
2301
|
"error.search_failed": "Search failed: {message}",
|
|
2300
2302
|
"error.fetch_failed": "Fetch failed: {message}",
|
|
@@ -2332,6 +2334,7 @@ var init_en = __esm(() => {
|
|
|
2332
2334
|
"tool.failed": "Tool {name} failed: {error}",
|
|
2333
2335
|
"tool.unknown": "Unknown tool: {name}",
|
|
2334
2336
|
"tool.blocked": "Blocked by plugin: {plugin}",
|
|
2337
|
+
"tool.blocked_reason": "Blocked by plugin: {plugin}. Reason: {reason}",
|
|
2335
2338
|
"tool.using": "[{label}]",
|
|
2336
2339
|
"tool.friendly.write_file": "Writing file",
|
|
2337
2340
|
"tool.friendly.read_file": "Reading file",
|
|
@@ -2344,6 +2347,7 @@ var init_en = __esm(() => {
|
|
|
2344
2347
|
"tool.friendly.glob": "Searching files",
|
|
2345
2348
|
"tool.friendly.grep": "Searching content",
|
|
2346
2349
|
"tool.friendly.bash": "Running command",
|
|
2350
|
+
"bash.echo_write_blocked": "Writing files via echo/printf is unreliable in Windows cmd.exe (quotes and multi-line break). Use the write_file tool instead (target: {path}).",
|
|
2347
2351
|
"tool.friendly.load_skill": "Loading skill",
|
|
2348
2352
|
"tool.friendly.plan": "Planning",
|
|
2349
2353
|
"tool.friendly.todo": "Updating tasks",
|
|
@@ -2433,6 +2437,13 @@ Command: {command}`,
|
|
|
2433
2437
|
"plan.step_not_found": "Step not found",
|
|
2434
2438
|
"plan.show_header": "Plan status:",
|
|
2435
2439
|
"plan.show_empty": "(plan has no steps)",
|
|
2440
|
+
"plan.list_header": "Plans:",
|
|
2441
|
+
"plan.list_empty": "No plans",
|
|
2442
|
+
"plan.switch_no_id": "Provide a plan id to switch to",
|
|
2443
|
+
"plan.not_found": "Plan not found: {id}",
|
|
2444
|
+
"plan.switched": "Switched to plan {id}: {title}",
|
|
2445
|
+
"plan.replanned": "Plan re-planned: {kept} completed steps kept, {steps} new steps added",
|
|
2446
|
+
"plan.replan_no_steps": "Provide new steps for re-planning",
|
|
2436
2447
|
"todo.added": "Added {count} todo(s): {items}",
|
|
2437
2448
|
"todo.marked_done": "Marked {count} item(s) as done",
|
|
2438
2449
|
"todo.no_active": "No active todos",
|
|
@@ -2706,12 +2717,14 @@ Use this knowledge to answer the user's question.`,
|
|
|
2706
2717
|
"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.',
|
|
2707
2718
|
"exec.plan_blocked": "{max} consecutive calls outside the current step. Finish step {step} before proceeding — other steps should wait until this one is complete.",
|
|
2708
2719
|
"exec.off_track": 'Step {stepId} — "{description}" — but you are using {tool} on a different path. Return to the current step.',
|
|
2709
|
-
"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.]',
|
|
2720
|
+
"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. If this step is NOT actually needed (no external dependencies), skip it: plan update step={step} status=skipped note="deps not needed".]',
|
|
2721
|
+
"exec.step_gate_deps_force": '[⚠ Step {step}: dependencies still not installed (no lock file) — 2nd warning. If this step is NOT needed, IMMEDIATELY call: plan update step={step} status=skipped note="deps not needed". If it IS needed, run the install command right now. Do NOT make other tool calls before updating the plan.]',
|
|
2710
2722
|
"exec.step_gate_empty": "[⚠ Step {step}: files exist but appear empty: {files}. Add real code to these files before advancing to the next step.]",
|
|
2711
2723
|
"exec.step_gate_ok": '[✓] Step {step} completed and verified. MOVING to step {nextStep}: "{nextDesc}". Work ONLY on this step.',
|
|
2712
2724
|
"exec.step_gate_last": "[✓] Step {step} completed — that was the final step. Verify everything together and provide the final answer.]",
|
|
2713
2725
|
"exec.audit_pass": "[✓] Task complete: {done}/{total} steps done, {files} files verified",
|
|
2714
2726
|
"exec.audit_fail": "[✗] Task incomplete: {done}/{total} steps done, {files} files missing",
|
|
2727
|
+
"exec.audit_fail_tests": "[✗] Task incomplete: {done}/{total} steps done, tests FAILING: {failed} failed / {passed} passed — {detail}",
|
|
2715
2728
|
"exec.audit_fail_typecheck": "[✗] Task incomplete: {done}/{total} steps done, {missing} files missing, typecheck error: {typeError}",
|
|
2716
2729
|
"exec.audit_incomplete": "[⚠ Final audit incomplete: {summary}. Task is NOT finished — continue working. Remaining steps: {steps}]",
|
|
2717
2730
|
"exec.mass_edit_warning": "⚠️ Plan affects {count} files — review the full list before proceeding.",
|
|
@@ -2843,6 +2856,7 @@ var init_ru = __esm(() => {
|
|
|
2843
2856
|
"error.llm": "Ошибка LLM: {message}",
|
|
2844
2857
|
"error.response_blocked": "Ответ заблокирован: {reason}",
|
|
2845
2858
|
"error.max_iters": "Достигнут максимум итераций ({max})",
|
|
2859
|
+
"error.empty_response": "Модель вернула пустой ответ после повторных попыток",
|
|
2846
2860
|
"error.grep_failed": "Ошибка grep: {message}",
|
|
2847
2861
|
"error.search_failed": "Ошибка поиска: {message}",
|
|
2848
2862
|
"error.fetch_failed": "Ошибка загрузки: {message}",
|
|
@@ -2880,6 +2894,7 @@ var init_ru = __esm(() => {
|
|
|
2880
2894
|
"tool.failed": "Инструмент {name} упал: {error}",
|
|
2881
2895
|
"tool.unknown": "Неизвестный инструмент: {name}",
|
|
2882
2896
|
"tool.blocked": "Заблокировано плагином: {plugin}",
|
|
2897
|
+
"tool.blocked_reason": "Заблокировано плагином: {plugin}. Причина: {reason}",
|
|
2883
2898
|
"tool.using": "[{label}]",
|
|
2884
2899
|
"tool.friendly.write_file": "Запись файла",
|
|
2885
2900
|
"tool.friendly.read_file": "Чтение файла",
|
|
@@ -2981,6 +2996,13 @@ var init_ru = __esm(() => {
|
|
|
2981
2996
|
"plan.step_not_found": "Шаг не найден",
|
|
2982
2997
|
"plan.show_header": "Статус плана:",
|
|
2983
2998
|
"plan.show_empty": "(в плане нет шагов)",
|
|
2999
|
+
"plan.list_header": "Планы:",
|
|
3000
|
+
"plan.list_empty": "Нет планов",
|
|
3001
|
+
"plan.switch_no_id": "Укажите id плана для переключения",
|
|
3002
|
+
"plan.not_found": "План не найден: {id}",
|
|
3003
|
+
"plan.switched": "Переключено на план {id}: {title}",
|
|
3004
|
+
"plan.replanned": "План перепланирован: {kept} выполненных шагов сохранено, {steps} новых шагов добавлено",
|
|
3005
|
+
"plan.replan_no_steps": "Укажите новые шаги для перепланирования",
|
|
2984
3006
|
"todo.added": "Добавлено {count} задач: {items}",
|
|
2985
3007
|
"todo.marked_done": "Отмечено выполненными: {count}",
|
|
2986
3008
|
"todo.no_active": "Нет активных задач",
|
|
@@ -3254,12 +3276,14 @@ var init_ru = __esm(() => {
|
|
|
3254
3276
|
"exec.plan_warning": 'Текущий шаг плана {step} — "{description}", но вызывается {tool} для файлов вне этого шага. Завершите текущий шаг, вызовите plan update step={step} status=done, затем переходите к следующему.',
|
|
3255
3277
|
"exec.plan_blocked": "{max} вызовов подряд вне текущего шага. Завершите шаг {step} — остальные шаги ждут пока этот не будет выполнен.",
|
|
3256
3278
|
"exec.off_track": 'Шаг {stepId} — "{description}", но используется {tool} для другого пути. Вернитесь к текущему шагу.',
|
|
3257
|
-
"exec.step_gate_deps": '[⚠ Шаг {step} "{description}": зависимости не установлены. Сначала выполните команду установки (npm install, pip install и т.д.). Проверьте что lock-файл или директория зависимостей существует.]',
|
|
3279
|
+
"exec.step_gate_deps": '[⚠ Шаг {step} "{description}": зависимости не установлены. Сначала выполните команду установки (npm install, pip install и т.д.). Проверьте что lock-файл или директория зависимостей существует. Если этот шаг на самом деле не нужен (внешних зависимостей нет) — пропусти его: plan update step={step} status=skipped note="зависимости не нужны".]',
|
|
3280
|
+
"exec.step_gate_deps_force": '[⚠ Шаг {step}: зависимости всё ещё не установлены (нет lock-файла) — второе предупреждение. Если этот шаг НЕ нужен — НЕМЕДЛЕННО вызови: plan update step={step} status=skipped note="зависимости не нужны". Если нужен — выполни установку прямо сейчас. Не делай других вызовов инструментов до обновления плана.]',
|
|
3258
3281
|
"exec.step_gate_empty": "[⚠ Шаг {step}: файлы существуют, но выглядят пустыми: {files}. Добавьте реальный код в эти файлы перед тем как переходить к следующему шагу.]",
|
|
3259
3282
|
"exec.step_gate_ok": '[✓] Шаг {step} завершён и проверен. ПЕРЕХОДИМ к шагу {nextStep}: "{nextDesc}". Работайте ТОЛЬКО над этим шагом.',
|
|
3260
3283
|
"exec.step_gate_last": "[✓] Шаг {step} завершён — это был последний шаг. Проверьте всё вместе и предоставьте финальный ответ.]",
|
|
3261
3284
|
"exec.audit_pass": "[✓] Задача выполнена: {done}/{total} шагов, {files} файлов проверено",
|
|
3262
3285
|
"exec.audit_fail": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов отсутствует",
|
|
3286
|
+
"exec.audit_fail_tests": "[✗] Задача не выполнена: {done}/{total} шагов, тесты ПАДАЮТ: {failed} failed / {passed} passed — {detail}",
|
|
3263
3287
|
"exec.audit_fail_typecheck": "[✗] Задача не выполнена: {done}/{total} шагов, {missing} файлов отсутствует, ошибка typecheck: {typeError}",
|
|
3264
3288
|
"exec.audit_incomplete": "[⚠ Финальная проверка не пройдена: {summary}. Задача НЕ завершена — продолжайте работу. Оставшиеся шаги: {steps}]",
|
|
3265
3289
|
"exec.mass_edit_warning": "⚠️ План затрагивает {count} файлов — проверьте полный список перед продолжением.",
|
|
@@ -3352,7 +3376,9 @@ var init_ru = __esm(() => {
|
|
|
3352
3376
|
"ctx.quality": "качество: {percent}%",
|
|
3353
3377
|
"ctx.delta_pos": "контекст +{tokens}",
|
|
3354
3378
|
"ctx.delta_neg": "контекст -{tokens} ↓",
|
|
3355
|
-
"ctx.delta_zero": "контекст ±0"
|
|
3379
|
+
"ctx.delta_zero": "контекст ±0",
|
|
3380
|
+
"file.notfound_resolved": "Файл не найден: {path} (резолвится в {resolved})",
|
|
3381
|
+
"bash.echo_write_blocked": "Запись файлов через echo/printf ненадёжна в Windows cmd.exe (кавычки и многострочность ломаются). Используй инструмент write_file вместо этого (цель: {path})."
|
|
3356
3382
|
};
|
|
3357
3383
|
});
|
|
3358
3384
|
|
|
@@ -3932,6 +3958,132 @@ var init_data_sanitizer = __esm(() => {
|
|
|
3932
3958
|
];
|
|
3933
3959
|
});
|
|
3934
3960
|
|
|
3961
|
+
// src/logger/file-log.ts
|
|
3962
|
+
import {
|
|
3963
|
+
appendFileSync,
|
|
3964
|
+
mkdirSync as mkdirSync3,
|
|
3965
|
+
existsSync as existsSync5,
|
|
3966
|
+
readdirSync as readdirSync3,
|
|
3967
|
+
renameSync,
|
|
3968
|
+
statSync,
|
|
3969
|
+
unlinkSync as unlinkSync2
|
|
3970
|
+
} from "node:fs";
|
|
3971
|
+
import { join as join5 } from "node:path";
|
|
3972
|
+
|
|
3973
|
+
class FileLogWriter {
|
|
3974
|
+
logDir = null;
|
|
3975
|
+
sessionLogPath = null;
|
|
3976
|
+
setLogDir(dir) {
|
|
3977
|
+
this.logDir = dir;
|
|
3978
|
+
if (!existsSync5(dir)) {
|
|
3979
|
+
mkdirSync3(dir, { recursive: true });
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
initSessionLog(sessionId) {
|
|
3983
|
+
if (!this.logDir)
|
|
3984
|
+
return;
|
|
3985
|
+
if (!existsSync5(this.logDir)) {
|
|
3986
|
+
mkdirSync3(this.logDir, { recursive: true });
|
|
3987
|
+
}
|
|
3988
|
+
this.sessionLogPath = join5(this.logDir, `${sessionId}.log`);
|
|
3989
|
+
this.cleanupOldLogs();
|
|
3990
|
+
}
|
|
3991
|
+
closeSessionLog() {
|
|
3992
|
+
this.sessionLogPath = null;
|
|
3993
|
+
}
|
|
3994
|
+
getLogPath() {
|
|
3995
|
+
if (this.sessionLogPath)
|
|
3996
|
+
return this.sessionLogPath;
|
|
3997
|
+
if (!this.logDir)
|
|
3998
|
+
return "";
|
|
3999
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
4000
|
+
return join5(this.logDir, `${date}.log`);
|
|
4001
|
+
}
|
|
4002
|
+
rotateIfNeeded(filePath) {
|
|
4003
|
+
if (!existsSync5(filePath))
|
|
4004
|
+
return;
|
|
4005
|
+
const size = statSync(filePath).size;
|
|
4006
|
+
if (size < MAX_LOG_SIZE)
|
|
4007
|
+
return;
|
|
4008
|
+
let idx = 1;
|
|
4009
|
+
while (existsSync5(`${filePath}.${idx}`))
|
|
4010
|
+
idx++;
|
|
4011
|
+
renameSync(filePath, `${filePath}.${idx}`);
|
|
4012
|
+
}
|
|
4013
|
+
log(level, tag, message) {
|
|
4014
|
+
const fp = this.getLogPath();
|
|
4015
|
+
if (!fp)
|
|
4016
|
+
return;
|
|
4017
|
+
if (this.logDir && !existsSync5(this.logDir)) {
|
|
4018
|
+
mkdirSync3(this.logDir, { recursive: true });
|
|
4019
|
+
}
|
|
4020
|
+
this.rotateIfNeeded(fp);
|
|
4021
|
+
const line = `[${new Date().toISOString()}] [${level}] [${tag}] ${message}
|
|
4022
|
+
`;
|
|
4023
|
+
try {
|
|
4024
|
+
appendFileSync(fp, line, "utf-8");
|
|
4025
|
+
} catch {}
|
|
4026
|
+
}
|
|
4027
|
+
logLLMRequest(model, messagesCount, promptPreview, caller) {
|
|
4028
|
+
const tag = caller ? `LLM/${caller}` : "LLM";
|
|
4029
|
+
this.log("INFO", tag, `→ ${model} | ${messagesCount} messages | "${promptPreview.slice(0, 100)}"`);
|
|
4030
|
+
}
|
|
4031
|
+
logLLMResponse(model, responseLength, genTimeMs, error, caller) {
|
|
4032
|
+
const tag = caller ? `LLM/${caller}` : "LLM";
|
|
4033
|
+
if (error) {
|
|
4034
|
+
this.log("ERROR", tag, `← ${model} | ${responseLength} chars | ${genTimeMs}ms | ERROR: ${error}`);
|
|
4035
|
+
} else {
|
|
4036
|
+
this.log("INFO", tag, `← ${model} | ${responseLength} chars | ${genTimeMs}ms`);
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
logToolCall(tool, preview, result) {
|
|
4040
|
+
const previewTrimmed = preview.length > 200 ? preview.slice(0, 200) + "..." : preview;
|
|
4041
|
+
if (result !== undefined) {
|
|
4042
|
+
const resultTrimmed = result.length > 200 ? result.slice(0, 200) + "..." : result;
|
|
4043
|
+
this.log("INFO", "TOOL", `${tool} | ${previewTrimmed} → ${resultTrimmed}`);
|
|
4044
|
+
} else {
|
|
4045
|
+
this.log("INFO", "TOOL", `${tool} | ${previewTrimmed}`);
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
logToolOutput(tool, output, exitCode) {
|
|
4049
|
+
const trimmed = output.length > TOOL_OUTPUT_LIMIT ? output.slice(0, TOOL_OUTPUT_LIMIT) + `
|
|
4050
|
+
... (truncated ${output.length - TOOL_OUTPUT_LIMIT} chars)` : output;
|
|
4051
|
+
this.log("DEBUG", "TOOL_OUTPUT", `${tool} exit:${exitCode}
|
|
4052
|
+
${trimmed}`);
|
|
4053
|
+
}
|
|
4054
|
+
logREPL(tag, content) {
|
|
4055
|
+
const trimmed = content.length > 400 ? content.slice(0, 400) + "..." : content;
|
|
4056
|
+
this.log("INFO", `REPL/${tag}`, trimmed);
|
|
4057
|
+
}
|
|
4058
|
+
cleanupOldLogs(maxDays = DEFAULT_MAX_DAYS, maxFiles = MAX_LOG_FILES) {
|
|
4059
|
+
if (!this.logDir || !existsSync5(this.logDir))
|
|
4060
|
+
return;
|
|
4061
|
+
const now = Date.now();
|
|
4062
|
+
const list = () => readdirSync3(this.logDir).filter((f) => f.endsWith(".log") || /\.log\.\d+$/.test(f)).map((f) => ({
|
|
4063
|
+
name: f,
|
|
4064
|
+
path: join5(this.logDir, f),
|
|
4065
|
+
mtime: statSync(join5(this.logDir, f)).mtimeMs
|
|
4066
|
+
})).sort((a, b) => b.mtime - a.mtime);
|
|
4067
|
+
for (const f of list()) {
|
|
4068
|
+
if (now - f.mtime > maxDays * 24 * 60 * 60 * 1000) {
|
|
4069
|
+
try {
|
|
4070
|
+
unlinkSync2(f.path);
|
|
4071
|
+
} catch {}
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
const remaining = list();
|
|
4075
|
+
for (let i = maxFiles;i < remaining.length; i++) {
|
|
4076
|
+
try {
|
|
4077
|
+
unlinkSync2(remaining[i].path);
|
|
4078
|
+
} catch {}
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
var MAX_LOG_SIZE, MAX_LOG_FILES = 30, DEFAULT_MAX_DAYS = 30, TOOL_OUTPUT_LIMIT = 2000;
|
|
4083
|
+
var init_file_log = __esm(() => {
|
|
4084
|
+
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
4085
|
+
});
|
|
4086
|
+
|
|
3935
4087
|
// node_modules/picocolors/picocolors.js
|
|
3936
4088
|
var require_picocolors = __commonJS((exports, module) => {
|
|
3937
4089
|
var p = process || {};
|
|
@@ -4003,8 +4155,8 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
4003
4155
|
});
|
|
4004
4156
|
|
|
4005
4157
|
// src/logger/app-logger.ts
|
|
4006
|
-
import { appendFileSync, mkdirSync as
|
|
4007
|
-
import { join as
|
|
4158
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
|
|
4159
|
+
import { join as join6 } from "path";
|
|
4008
4160
|
function isColorEnabled() {
|
|
4009
4161
|
return !process.env.NO_COLOR && !process.env.CI && process.stdout.isTTY === true;
|
|
4010
4162
|
}
|
|
@@ -4014,28 +4166,58 @@ class Logger {
|
|
|
4014
4166
|
prefix;
|
|
4015
4167
|
logDir = null;
|
|
4016
4168
|
sessionDir = null;
|
|
4169
|
+
fileLog;
|
|
4017
4170
|
constructor(level = "info", prefix = "") {
|
|
4018
4171
|
this.level = level;
|
|
4019
4172
|
this.prefix = prefix;
|
|
4173
|
+
this.fileLog = new FileLogWriter;
|
|
4020
4174
|
}
|
|
4021
4175
|
setLevel(level) {
|
|
4022
4176
|
this.level = level;
|
|
4023
4177
|
}
|
|
4024
4178
|
setLogDir(dir) {
|
|
4025
4179
|
this.logDir = dir;
|
|
4026
|
-
if (!
|
|
4027
|
-
|
|
4180
|
+
if (!existsSync6(dir)) {
|
|
4181
|
+
mkdirSync4(dir, { recursive: true });
|
|
4028
4182
|
}
|
|
4183
|
+
this.fileLog.setLogDir(dir);
|
|
4029
4184
|
}
|
|
4030
4185
|
setSessionDir(dir) {
|
|
4031
4186
|
this.sessionDir = dir;
|
|
4032
|
-
if (!
|
|
4033
|
-
|
|
4187
|
+
if (!existsSync6(dir)) {
|
|
4188
|
+
mkdirSync4(dir, { recursive: true });
|
|
4034
4189
|
}
|
|
4035
4190
|
}
|
|
4036
4191
|
clearSessionDir() {
|
|
4037
4192
|
this.sessionDir = null;
|
|
4038
4193
|
}
|
|
4194
|
+
initSessionLog(sessionId) {
|
|
4195
|
+
this.fileLog.initSessionLog(sessionId);
|
|
4196
|
+
}
|
|
4197
|
+
closeSessionLog() {
|
|
4198
|
+
this.fileLog.closeSessionLog();
|
|
4199
|
+
}
|
|
4200
|
+
getLogPath() {
|
|
4201
|
+
return this.fileLog.getLogPath();
|
|
4202
|
+
}
|
|
4203
|
+
cleanupOldLogs(maxDays, maxFiles) {
|
|
4204
|
+
this.fileLog.cleanupOldLogs(maxDays, maxFiles);
|
|
4205
|
+
}
|
|
4206
|
+
logLLMRequest(model, messagesCount, promptPreview, caller) {
|
|
4207
|
+
this.fileLog.logLLMRequest(model, messagesCount, promptPreview, caller);
|
|
4208
|
+
}
|
|
4209
|
+
logLLMResponse(model, responseLength, genTimeMs, error, caller) {
|
|
4210
|
+
this.fileLog.logLLMResponse(model, responseLength, genTimeMs, error, caller);
|
|
4211
|
+
}
|
|
4212
|
+
logToolCall(tool, preview, result) {
|
|
4213
|
+
this.fileLog.logToolCall(tool, preview, result);
|
|
4214
|
+
}
|
|
4215
|
+
logToolOutput(tool, output, exitCode) {
|
|
4216
|
+
this.fileLog.logToolOutput(tool, output, exitCode);
|
|
4217
|
+
}
|
|
4218
|
+
logREPL(tag, content) {
|
|
4219
|
+
this.fileLog.logREPL(tag, content);
|
|
4220
|
+
}
|
|
4039
4221
|
child(prefix) {
|
|
4040
4222
|
const childLogger = new Logger(this.level, this.prefix ? `${this.prefix}:${prefix}` : prefix);
|
|
4041
4223
|
if (this.logDir)
|
|
@@ -4066,10 +4248,11 @@ class Logger {
|
|
|
4066
4248
|
const metaStr = sanitizedMeta ? ` ${JSON.stringify(sanitizedMeta)}` : "";
|
|
4067
4249
|
const line = `[${level.toUpperCase()}]${prefix} ${ts} — ${sanitizedMsg}${metaStr}`;
|
|
4068
4250
|
console.log(isColorEnabled() ? LEVEL_COLORS[level](line) : line);
|
|
4251
|
+
this.fileLog.log(level.toUpperCase(), this.prefix || "MMA", `${ts} — ${sanitizedMsg}${metaStr}`);
|
|
4069
4252
|
const logTarget = this.sessionDir ?? this.logDir;
|
|
4070
4253
|
if (logTarget) {
|
|
4071
4254
|
try {
|
|
4072
|
-
|
|
4255
|
+
appendFileSync2(join6(logTarget, "app.jsonl"), JSON.stringify({
|
|
4073
4256
|
level,
|
|
4074
4257
|
ts,
|
|
4075
4258
|
prefix: this.prefix,
|
|
@@ -4097,6 +4280,7 @@ class Logger {
|
|
|
4097
4280
|
var import_picocolors, LEVELS, LEVEL_COLORS;
|
|
4098
4281
|
var init_app_logger = __esm(() => {
|
|
4099
4282
|
init_data_sanitizer();
|
|
4283
|
+
init_file_log();
|
|
4100
4284
|
import_picocolors = __toESM(require_picocolors(), 1);
|
|
4101
4285
|
LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
4102
4286
|
LEVEL_COLORS = {
|
|
@@ -5125,11 +5309,18 @@ class ToolExecutor {
|
|
|
5125
5309
|
if (ctx)
|
|
5126
5310
|
this.ctx = ctx;
|
|
5127
5311
|
try {
|
|
5128
|
-
return await this.execute({
|
|
5312
|
+
return await this.execute({
|
|
5313
|
+
id: `redirect_${Date.now()}`,
|
|
5314
|
+
name,
|
|
5315
|
+
arguments: args
|
|
5316
|
+
});
|
|
5129
5317
|
} finally {
|
|
5130
5318
|
this.ctx = prevCtx;
|
|
5131
5319
|
}
|
|
5132
5320
|
}
|
|
5321
|
+
hasTool(name) {
|
|
5322
|
+
return this.registry.has(name);
|
|
5323
|
+
}
|
|
5133
5324
|
async execute(call, signal) {
|
|
5134
5325
|
const tool = this.registry.get(call.name);
|
|
5135
5326
|
if (!tool) {
|
|
@@ -5143,12 +5334,12 @@ class ToolExecutor {
|
|
|
5143
5334
|
if (plugin.onBeforeTool) {
|
|
5144
5335
|
try {
|
|
5145
5336
|
const proceed = await plugin.onBeforeTool(this.ctx, call);
|
|
5146
|
-
if (
|
|
5337
|
+
if (proceed === false || typeof proceed === "string") {
|
|
5338
|
+
const pluginName = plugin.name || plugin.constructor?.name || "unknown";
|
|
5339
|
+
const reason = typeof proceed === "string" ? proceed : undefined;
|
|
5147
5340
|
return {
|
|
5148
5341
|
success: false,
|
|
5149
|
-
output: t("tool.blocked", {
|
|
5150
|
-
plugin: plugin.constructor?.name || "unknown"
|
|
5151
|
-
}),
|
|
5342
|
+
output: reason ? t("tool.blocked_reason", { plugin: pluginName, reason }) : t("tool.blocked", { plugin: pluginName }),
|
|
5152
5343
|
toolCallId: call.id
|
|
5153
5344
|
};
|
|
5154
5345
|
}
|
|
@@ -5209,6 +5400,8 @@ class ToolExecutor {
|
|
|
5209
5400
|
}
|
|
5210
5401
|
}
|
|
5211
5402
|
}
|
|
5403
|
+
this.ctx.logger.logToolCall(call.name, JSON.stringify(call.arguments)?.slice(0, 200) ?? "", result.success ? "OK" : `FAIL: ${result.output?.slice(0, 200)}`);
|
|
5404
|
+
this.ctx.logger.logToolOutput(call.name, result.output ?? "", result.success ? 0 : 1);
|
|
5212
5405
|
this.ctx.logger.debug(`Tool ${call.name}: ${result.success ? "OK" : "FAIL"}`);
|
|
5213
5406
|
return result;
|
|
5214
5407
|
}
|
|
@@ -5417,8 +5610,8 @@ __export(exports_audit_notifier, {
|
|
|
5417
5610
|
DEFAULT_AUDIT_NOTIFIER_CONFIG: () => DEFAULT_AUDIT_NOTIFIER_CONFIG,
|
|
5418
5611
|
AuditNotifier: () => AuditNotifier
|
|
5419
5612
|
});
|
|
5420
|
-
import { writeFileSync as writeFileSync4, appendFileSync as
|
|
5421
|
-
import { join as
|
|
5613
|
+
import { writeFileSync as writeFileSync4, appendFileSync as appendFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
5614
|
+
import { join as join7, dirname as dirname2 } from "path";
|
|
5422
5615
|
import { homedir as homedir2 } from "os";
|
|
5423
5616
|
import { readFileSync as readFileSync4 } from "fs";
|
|
5424
5617
|
|
|
@@ -5446,7 +5639,7 @@ class AuditNotifier {
|
|
|
5446
5639
|
ensureLogDirectory() {
|
|
5447
5640
|
if (this.config.filePath) {
|
|
5448
5641
|
const dir = dirname2(this.config.filePath);
|
|
5449
|
-
|
|
5642
|
+
mkdirSync5(dir, { recursive: true });
|
|
5450
5643
|
}
|
|
5451
5644
|
}
|
|
5452
5645
|
shouldNotify(eventType) {
|
|
@@ -5512,7 +5705,7 @@ class AuditNotifier {
|
|
|
5512
5705
|
return;
|
|
5513
5706
|
try {
|
|
5514
5707
|
const line = JSON.stringify(notification);
|
|
5515
|
-
|
|
5708
|
+
appendFileSync3(this.config.filePath, line + `
|
|
5516
5709
|
`, "utf8");
|
|
5517
5710
|
} catch (error) {
|
|
5518
5711
|
console.error("[AuditNotifier] Failed to write to file:", error);
|
|
@@ -5568,7 +5761,7 @@ class AuditNotifier {
|
|
|
5568
5761
|
this.isProcessing = false;
|
|
5569
5762
|
}
|
|
5570
5763
|
readNotifications(limit = 100) {
|
|
5571
|
-
if (!this.config.filePath || !
|
|
5764
|
+
if (!this.config.filePath || !existsSync7(this.config.filePath)) {
|
|
5572
5765
|
return [];
|
|
5573
5766
|
}
|
|
5574
5767
|
try {
|
|
@@ -5625,7 +5818,7 @@ var init_audit_notifier = __esm(() => {
|
|
|
5625
5818
|
};
|
|
5626
5819
|
DEFAULT_AUDIT_NOTIFIER_CONFIG = {
|
|
5627
5820
|
enabled: false,
|
|
5628
|
-
filePath:
|
|
5821
|
+
filePath: join7(homedir2(), ".mma", "logs", "audit-notifications.jsonl"),
|
|
5629
5822
|
webhookTimeout: 5000,
|
|
5630
5823
|
minSeverity: "medium",
|
|
5631
5824
|
eventTypes: [
|
|
@@ -5642,26 +5835,26 @@ var init_audit_notifier = __esm(() => {
|
|
|
5642
5835
|
});
|
|
5643
5836
|
|
|
5644
5837
|
// src/modules/security/audit-log.ts
|
|
5645
|
-
import { existsSync as
|
|
5646
|
-
import { resolve as resolve2, join as
|
|
5838
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, appendFileSync as appendFileSync4 } from "fs";
|
|
5839
|
+
import { resolve as resolve2, join as join8 } from "path";
|
|
5647
5840
|
import { homedir as homedir3 } from "os";
|
|
5648
5841
|
function getAuditDir() {
|
|
5649
5842
|
return _sessionAuditDir ?? _globalAuditDir;
|
|
5650
5843
|
}
|
|
5651
5844
|
function setAuditSessionDir(dir) {
|
|
5652
5845
|
_sessionAuditDir = dir;
|
|
5653
|
-
if (!
|
|
5654
|
-
|
|
5846
|
+
if (!existsSync8(dir)) {
|
|
5847
|
+
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
5655
5848
|
}
|
|
5656
5849
|
}
|
|
5657
5850
|
function logAudit(entry) {
|
|
5658
5851
|
const dir = getAuditDir();
|
|
5659
|
-
if (!
|
|
5660
|
-
|
|
5852
|
+
if (!existsSync8(dir)) {
|
|
5853
|
+
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
5661
5854
|
}
|
|
5662
5855
|
try {
|
|
5663
5856
|
const logEntry = JSON.stringify(entry);
|
|
5664
|
-
|
|
5857
|
+
appendFileSync4(join8(dir, "audit.jsonl"), logEntry + `
|
|
5665
5858
|
`, "utf8");
|
|
5666
5859
|
} catch {}
|
|
5667
5860
|
try {
|
|
@@ -5726,12 +5919,12 @@ var init_audit_log = __esm(() => {
|
|
|
5726
5919
|
|
|
5727
5920
|
// src/tools/path-utils.ts
|
|
5728
5921
|
import { resolve as resolve3, normalize as normalize2, dirname as dirname3, basename, sep } from "path";
|
|
5729
|
-
import { existsSync as
|
|
5922
|
+
import { existsSync as existsSync9 } from "fs";
|
|
5730
5923
|
function safeResolvePath(baseDir, userPath) {
|
|
5731
5924
|
const norm = normalize2(userPath);
|
|
5732
5925
|
const stripped = norm.replace(/^[/\\]/, "");
|
|
5733
5926
|
const resolved = resolve3(baseDir, stripped);
|
|
5734
|
-
if (
|
|
5927
|
+
if (existsSync9(resolved) || existsSync9(dirname3(resolved)))
|
|
5735
5928
|
return resolved;
|
|
5736
5929
|
const baseNorm = normalize2(baseDir);
|
|
5737
5930
|
let cur = baseNorm;
|
|
@@ -5748,11 +5941,11 @@ function safeResolvePath(baseDir, userPath) {
|
|
|
5748
5941
|
if (afterChar && afterChar !== "\\" && afterChar !== "/") {
|
|
5749
5942
|
const fixed = stripped.slice(0, afterIdx) + sep + stripped.slice(afterIdx);
|
|
5750
5943
|
const fixedResolved = resolve3(baseDir, normalize2(fixed));
|
|
5751
|
-
if (
|
|
5944
|
+
if (existsSync9(fixedResolved) || existsSync9(dirname3(fixedResolved))) {
|
|
5752
5945
|
return fixedResolved;
|
|
5753
5946
|
}
|
|
5754
5947
|
const fromParent = resolve3(dirname3(cur), normalize2(fixed));
|
|
5755
|
-
if (
|
|
5948
|
+
if (existsSync9(fromParent) || existsSync9(dirname3(fromParent))) {
|
|
5756
5949
|
return fromParent;
|
|
5757
5950
|
}
|
|
5758
5951
|
}
|
|
@@ -5768,7 +5961,7 @@ var init_path_utils = () => {};
|
|
|
5768
5961
|
var MAX_PREVIEW_LINES = 15;
|
|
5769
5962
|
|
|
5770
5963
|
// src/tools/read-file.ts
|
|
5771
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
5964
|
+
import { readFileSync as readFileSync5, existsSync as existsSync10 } from "fs";
|
|
5772
5965
|
import { extname } from "path";
|
|
5773
5966
|
var DEFAULT_LIMIT, readFileTool;
|
|
5774
5967
|
var init_read_file = __esm(() => {
|
|
@@ -5812,8 +6005,12 @@ var init_read_file = __esm(() => {
|
|
|
5812
6005
|
})
|
|
5813
6006
|
};
|
|
5814
6007
|
}
|
|
5815
|
-
if (!
|
|
5816
|
-
|
|
6008
|
+
if (!existsSync10(resolved)) {
|
|
6009
|
+
const output = resolved !== path ? t("file.notfound_resolved", {
|
|
6010
|
+
path,
|
|
6011
|
+
resolved
|
|
6012
|
+
}) : t("file.notfound", { path });
|
|
6013
|
+
return { success: false, output };
|
|
5817
6014
|
}
|
|
5818
6015
|
const content = readFileSync5(resolved, "utf-8");
|
|
5819
6016
|
const lines = content.split(`
|
|
@@ -5863,7 +6060,7 @@ function scanContent(content, filePath, config) {
|
|
|
5863
6060
|
if (matches.length > 0) {
|
|
5864
6061
|
return {
|
|
5865
6062
|
allowed: false,
|
|
5866
|
-
reason: `Content contains dangerous patterns: ${matches.slice(0, 3).join(", ")}`,
|
|
6063
|
+
reason: `Content contains dangerous patterns in ${filePath}: ${matches.slice(0, 3).join(", ")}`,
|
|
5867
6064
|
matches
|
|
5868
6065
|
};
|
|
5869
6066
|
}
|
|
@@ -5881,32 +6078,27 @@ var init_content_scanner = __esm(() => {
|
|
|
5881
6078
|
/require\('net'\).createServer/gi,
|
|
5882
6079
|
/require\('fs'\).writeFileSync/gi,
|
|
5883
6080
|
/require\('fs'\).unlinkSync/gi,
|
|
5884
|
-
/process\.exit\(/gi,
|
|
5885
|
-
/setInterval\(/gi,
|
|
5886
|
-
/setTimeout\(/gi,
|
|
5887
6081
|
/while\(true\)/gi,
|
|
5888
6082
|
/for\(;;\)/gi,
|
|
5889
6083
|
/rm -rf/gi,
|
|
5890
6084
|
/dd if=/gi,
|
|
5891
6085
|
/chmod 777/gi,
|
|
5892
6086
|
/wget.*\|.*sh/gi,
|
|
5893
|
-
/curl.*\|.*sh/gi
|
|
5894
|
-
/\$\{/gi,
|
|
5895
|
-
/`[^`]+`/gi
|
|
6087
|
+
/curl.*\|.*sh/gi
|
|
5896
6088
|
];
|
|
5897
6089
|
});
|
|
5898
6090
|
|
|
5899
6091
|
// src/modules/security/session-isolation.ts
|
|
5900
|
-
import { join as
|
|
6092
|
+
import { join as join9, resolve as resolve5 } from "path";
|
|
5901
6093
|
import { homedir as homedir4 } from "os";
|
|
5902
|
-
import { mkdirSync as
|
|
6094
|
+
import { mkdirSync as mkdirSync7, existsSync as existsSync11 } from "fs";
|
|
5903
6095
|
function createSessionContext(sessionId, projectDir, isolationConfig, securityOverrides) {
|
|
5904
6096
|
const config = { ...DEFAULT_SESSION_ISOLATION, ...isolationConfig };
|
|
5905
|
-
const baseDir = config.baseDir ||
|
|
5906
|
-
const tempDir =
|
|
5907
|
-
if (config.isolateTempFiles && !
|
|
6097
|
+
const baseDir = config.baseDir || join9(homedir4(), ".mma", "sessions", sessionId);
|
|
6098
|
+
const tempDir = join9(baseDir, "temp");
|
|
6099
|
+
if (config.isolateTempFiles && !existsSync11(tempDir)) {
|
|
5908
6100
|
try {
|
|
5909
|
-
|
|
6101
|
+
mkdirSync7(tempDir, { recursive: true, mode: 448 });
|
|
5910
6102
|
} catch {}
|
|
5911
6103
|
}
|
|
5912
6104
|
return {
|
|
@@ -6136,7 +6328,7 @@ var init_diff = __esm(() => {
|
|
|
6136
6328
|
});
|
|
6137
6329
|
|
|
6138
6330
|
// src/tools/write-file.ts
|
|
6139
|
-
import { writeFileSync as writeFileSync5, mkdirSync as
|
|
6331
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync8, existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
|
|
6140
6332
|
import { dirname as dirname4 } from "path";
|
|
6141
6333
|
var writeFileTool;
|
|
6142
6334
|
var init_write_file = __esm(() => {
|
|
@@ -6183,19 +6375,21 @@ var init_write_file = __esm(() => {
|
|
|
6183
6375
|
};
|
|
6184
6376
|
}
|
|
6185
6377
|
const content = String(args.content);
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6378
|
+
if (securityConfig?.enabled && securityConfig?.contentScan?.enabled) {
|
|
6379
|
+
const scanResult = scanContent(content, path, securityConfig.contentScan);
|
|
6380
|
+
if (!scanResult.allowed) {
|
|
6381
|
+
logSecurityBlock(ctx.sessionId, "file_write", scanResult.reason || "Content contains dangerous patterns", path);
|
|
6382
|
+
return {
|
|
6383
|
+
success: false,
|
|
6384
|
+
output: `[SECURITY BLOCKED] ${scanResult.reason}`
|
|
6385
|
+
};
|
|
6386
|
+
}
|
|
6193
6387
|
}
|
|
6194
6388
|
const dir = dirname4(resolved);
|
|
6195
|
-
if (!
|
|
6196
|
-
|
|
6389
|
+
if (!existsSync12(dir)) {
|
|
6390
|
+
mkdirSync8(dir, { recursive: true });
|
|
6197
6391
|
}
|
|
6198
|
-
const fileExists =
|
|
6392
|
+
const fileExists = existsSync12(resolved);
|
|
6199
6393
|
let oldContent = "";
|
|
6200
6394
|
if (fileExists) {
|
|
6201
6395
|
oldContent = readFileSync6(resolved, "utf-8");
|
|
@@ -6266,13 +6460,15 @@ var init_edit_file = __esm(() => {
|
|
|
6266
6460
|
};
|
|
6267
6461
|
}
|
|
6268
6462
|
const updated = content.replace(oldStr, newStr);
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6463
|
+
if (securityConfig?.enabled && securityConfig?.contentScan?.enabled) {
|
|
6464
|
+
const scanResult = scanContent(updated, path, securityConfig.contentScan);
|
|
6465
|
+
if (!scanResult.allowed) {
|
|
6466
|
+
logSecurityBlock(ctx.sessionId, "file_write", scanResult.reason || "Content contains dangerous patterns", path);
|
|
6467
|
+
return {
|
|
6468
|
+
success: false,
|
|
6469
|
+
output: `[SECURITY BLOCKED] ${scanResult.reason}`
|
|
6470
|
+
};
|
|
6471
|
+
}
|
|
6276
6472
|
}
|
|
6277
6473
|
writeFileSync6(resolved, updated, "utf-8");
|
|
6278
6474
|
const diff = generateDiff(content, updated);
|
|
@@ -6397,7 +6593,7 @@ var init_grep_tool = __esm(() => {
|
|
|
6397
6593
|
});
|
|
6398
6594
|
|
|
6399
6595
|
// src/tools/list-dir.ts
|
|
6400
|
-
import { readdirSync as
|
|
6596
|
+
import { readdirSync as readdirSync4, statSync as statSync2, existsSync as existsSync13 } from "fs";
|
|
6401
6597
|
import { resolve as resolve8 } from "path";
|
|
6402
6598
|
var listDirTool;
|
|
6403
6599
|
var init_list_dir = __esm(() => {
|
|
@@ -6427,13 +6623,13 @@ var init_list_dir = __esm(() => {
|
|
|
6427
6623
|
})
|
|
6428
6624
|
};
|
|
6429
6625
|
}
|
|
6430
|
-
if (!
|
|
6626
|
+
if (!existsSync13(resolved)) {
|
|
6431
6627
|
return { success: false, output: t("file.dir_notfound", { path }) };
|
|
6432
6628
|
}
|
|
6433
|
-
const entries =
|
|
6629
|
+
const entries = readdirSync4(resolved);
|
|
6434
6630
|
const lines = entries.map((e) => {
|
|
6435
6631
|
const full = resolve8(resolved, e);
|
|
6436
|
-
return
|
|
6632
|
+
return statSync2(full).isDirectory() ? `${e}/` : e;
|
|
6437
6633
|
});
|
|
6438
6634
|
if (lines.length === 0) {
|
|
6439
6635
|
return { success: true, output: t("file.empty") };
|
|
@@ -6447,7 +6643,7 @@ var init_list_dir = __esm(() => {
|
|
|
6447
6643
|
});
|
|
6448
6644
|
|
|
6449
6645
|
// src/tools/create-dir.ts
|
|
6450
|
-
import { mkdirSync as
|
|
6646
|
+
import { mkdirSync as mkdirSync9, existsSync as existsSync14 } from "fs";
|
|
6451
6647
|
var createDirTool;
|
|
6452
6648
|
var init_create_dir = __esm(() => {
|
|
6453
6649
|
init_i18n();
|
|
@@ -6489,8 +6685,8 @@ var init_create_dir = __esm(() => {
|
|
|
6489
6685
|
})
|
|
6490
6686
|
};
|
|
6491
6687
|
}
|
|
6492
|
-
if (!
|
|
6493
|
-
|
|
6688
|
+
if (!existsSync14(resolved)) {
|
|
6689
|
+
mkdirSync9(resolved, { recursive: true });
|
|
6494
6690
|
}
|
|
6495
6691
|
ctx.fileOperationsCount = currentCount + 1;
|
|
6496
6692
|
logFileWrite(ctx.sessionId, path, true, "Directory created");
|
|
@@ -6500,7 +6696,7 @@ var init_create_dir = __esm(() => {
|
|
|
6500
6696
|
});
|
|
6501
6697
|
|
|
6502
6698
|
// src/tools/delete-file.ts
|
|
6503
|
-
import { unlinkSync as
|
|
6699
|
+
import { unlinkSync as unlinkSync3, existsSync as existsSync15, statSync as statSync3, readFileSync as readFileSync8 } from "fs";
|
|
6504
6700
|
var deleteFileTool;
|
|
6505
6701
|
var init_delete_file = __esm(() => {
|
|
6506
6702
|
init_i18n();
|
|
@@ -6543,14 +6739,14 @@ var init_delete_file = __esm(() => {
|
|
|
6543
6739
|
})
|
|
6544
6740
|
};
|
|
6545
6741
|
}
|
|
6546
|
-
if (!
|
|
6742
|
+
if (!existsSync15(resolved)) {
|
|
6547
6743
|
return { success: false, output: t("file.notfound", { path }) };
|
|
6548
6744
|
}
|
|
6549
|
-
if (
|
|
6745
|
+
if (statSync3(resolved).isDirectory()) {
|
|
6550
6746
|
return { success: false, output: t("file.is_directory", { path }) };
|
|
6551
6747
|
}
|
|
6552
6748
|
const content = readFileSync8(resolved, "utf-8");
|
|
6553
|
-
|
|
6749
|
+
unlinkSync3(resolved);
|
|
6554
6750
|
const diff = generateDeleteDiff(content);
|
|
6555
6751
|
ctx.fileOperationsCount = currentCount + 1;
|
|
6556
6752
|
logFileDelete(ctx.sessionId, path, true);
|
|
@@ -6560,7 +6756,7 @@ var init_delete_file = __esm(() => {
|
|
|
6560
6756
|
});
|
|
6561
6757
|
|
|
6562
6758
|
// src/tools/move-file.ts
|
|
6563
|
-
import { renameSync, existsSync as
|
|
6759
|
+
import { renameSync as renameSync2, existsSync as existsSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
6564
6760
|
import { resolve as resolve9, normalize as normalize3, dirname as dirname5 } from "path";
|
|
6565
6761
|
var moveFileTool;
|
|
6566
6762
|
var init_move_file = __esm(() => {
|
|
@@ -6618,17 +6814,17 @@ var init_move_file = __esm(() => {
|
|
|
6618
6814
|
})
|
|
6619
6815
|
};
|
|
6620
6816
|
}
|
|
6621
|
-
if (!
|
|
6817
|
+
if (!existsSync16(fromResolved)) {
|
|
6622
6818
|
return {
|
|
6623
6819
|
success: false,
|
|
6624
6820
|
output: t("file.not_found_short", { path: fromPath })
|
|
6625
6821
|
};
|
|
6626
6822
|
}
|
|
6627
6823
|
const toDir = dirname5(toResolved);
|
|
6628
|
-
if (!
|
|
6629
|
-
|
|
6824
|
+
if (!existsSync16(toDir)) {
|
|
6825
|
+
mkdirSync10(toDir, { recursive: true });
|
|
6630
6826
|
}
|
|
6631
|
-
|
|
6827
|
+
renameSync2(fromResolved, toResolved);
|
|
6632
6828
|
const diff = generateMoveDiff(fromPath, toPath);
|
|
6633
6829
|
ctx.fileOperationsCount = currentCount + 1;
|
|
6634
6830
|
logFileWrite(ctx.sessionId, `${fromPath} -> ${toPath}`, true, "File moved");
|
|
@@ -6642,7 +6838,7 @@ var init_move_file = __esm(() => {
|
|
|
6642
6838
|
});
|
|
6643
6839
|
|
|
6644
6840
|
// src/tools/file-info.ts
|
|
6645
|
-
import { statSync as
|
|
6841
|
+
import { statSync as statSync4, existsSync as existsSync17 } from "fs";
|
|
6646
6842
|
var fileInfoTool;
|
|
6647
6843
|
var init_file_info = __esm(() => {
|
|
6648
6844
|
init_i18n();
|
|
@@ -6671,10 +6867,10 @@ var init_file_info = __esm(() => {
|
|
|
6671
6867
|
})
|
|
6672
6868
|
};
|
|
6673
6869
|
}
|
|
6674
|
-
if (!
|
|
6870
|
+
if (!existsSync17(resolved)) {
|
|
6675
6871
|
return { success: false, output: t("file.not_found_short", { path }) };
|
|
6676
6872
|
}
|
|
6677
|
-
const stat =
|
|
6873
|
+
const stat = statSync4(resolved);
|
|
6678
6874
|
return {
|
|
6679
6875
|
success: true,
|
|
6680
6876
|
output: JSON.stringify({
|
|
@@ -6846,17 +7042,60 @@ var init_command_validator = __esm(() => {
|
|
|
6846
7042
|
});
|
|
6847
7043
|
|
|
6848
7044
|
// src/modules/processes/registry.ts
|
|
6849
|
-
import { spawn } from "child_process";
|
|
7045
|
+
import { spawn, spawnSync } from "child_process";
|
|
6850
7046
|
import { platform } from "os";
|
|
7047
|
+
function getOemDecoder() {
|
|
7048
|
+
if (oemDecoder !== undefined)
|
|
7049
|
+
return oemDecoder;
|
|
7050
|
+
oemDecoder = null;
|
|
7051
|
+
try {
|
|
7052
|
+
const cpOut = spawnSync("chcp.com", {
|
|
7053
|
+
encoding: "buffer",
|
|
7054
|
+
timeout: 2000,
|
|
7055
|
+
windowsHide: true
|
|
7056
|
+
}).stdout;
|
|
7057
|
+
const m = cpOut ? cpOut.toString("latin1").match(/(\d+)/) : null;
|
|
7058
|
+
if (m) {
|
|
7059
|
+
try {
|
|
7060
|
+
oemDecoder = new TextDecoder("ibm" + m[1]);
|
|
7061
|
+
} catch {
|
|
7062
|
+
oemDecoder = null;
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
} catch {}
|
|
7066
|
+
if (!oemDecoder) {
|
|
7067
|
+
try {
|
|
7068
|
+
oemDecoder = new TextDecoder("ibm866");
|
|
7069
|
+
} catch {
|
|
7070
|
+
oemDecoder = null;
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
return oemDecoder;
|
|
7074
|
+
}
|
|
7075
|
+
function decodeLineToUtf8(buf) {
|
|
7076
|
+
try {
|
|
7077
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
7078
|
+
} catch {}
|
|
7079
|
+
const oem = getOemDecoder();
|
|
7080
|
+
if (oem) {
|
|
7081
|
+
try {
|
|
7082
|
+
return oem.decode(buf);
|
|
7083
|
+
} catch {}
|
|
7084
|
+
}
|
|
7085
|
+
return buf.toString("latin1");
|
|
7086
|
+
}
|
|
6851
7087
|
function killTree(child) {
|
|
6852
7088
|
const pid = child.pid;
|
|
6853
7089
|
if (!pid)
|
|
6854
7090
|
return;
|
|
6855
7091
|
if (platform() === "win32") {
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
7092
|
+
try {
|
|
7093
|
+
spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
7094
|
+
windowsHide: true,
|
|
7095
|
+
stdio: "ignore",
|
|
7096
|
+
timeout: 3000
|
|
7097
|
+
});
|
|
7098
|
+
} catch {}
|
|
6860
7099
|
return;
|
|
6861
7100
|
}
|
|
6862
7101
|
try {
|
|
@@ -6904,20 +7143,10 @@ class ProcessRegistry {
|
|
|
6904
7143
|
}
|
|
6905
7144
|
this.children.set(id, child);
|
|
6906
7145
|
entry.pid = child.pid ?? 0;
|
|
6907
|
-
const decoder = platform() === "win32" ? (() => {
|
|
6908
|
-
try {
|
|
6909
|
-
const cpOut = __require("child_process").execSync("chcp.com", {
|
|
6910
|
-
encoding: "buffer",
|
|
6911
|
-
timeout: 2000,
|
|
6912
|
-
windowsHide: true
|
|
6913
|
-
}).toString("latin1");
|
|
6914
|
-
const m = cpOut.match(/(\d+)/);
|
|
6915
|
-
if (m)
|
|
6916
|
-
return new TextDecoder("ibm" + m[1]);
|
|
6917
|
-
} catch {}
|
|
6918
|
-
return new TextDecoder("ibm866");
|
|
6919
|
-
})() : new TextDecoder("utf-8");
|
|
6920
7146
|
let buf = "";
|
|
7147
|
+
let byteBuf = Buffer.alloc(0);
|
|
7148
|
+
const isWin = platform() === "win32";
|
|
7149
|
+
const unixDecoder = new TextDecoder("utf-8");
|
|
6921
7150
|
const pushLine = (line) => {
|
|
6922
7151
|
if (entry.log.length >= MAX_LOG_LINES) {
|
|
6923
7152
|
entry.log.shift();
|
|
@@ -6925,19 +7154,36 @@ class ProcessRegistry {
|
|
|
6925
7154
|
entry.log.push(line);
|
|
6926
7155
|
};
|
|
6927
7156
|
const append = (chunk) => {
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
7157
|
+
if (!isWin) {
|
|
7158
|
+
const decoded = unixDecoder.decode(chunk, { stream: true });
|
|
7159
|
+
buf += decoded;
|
|
7160
|
+
const lines = buf.split(/\r?\n/);
|
|
7161
|
+
buf = lines.pop() ?? "";
|
|
7162
|
+
for (const line of lines) {
|
|
7163
|
+
pushLine(line);
|
|
7164
|
+
}
|
|
7165
|
+
return;
|
|
7166
|
+
}
|
|
7167
|
+
byteBuf = byteBuf.length ? Buffer.concat([byteBuf, chunk]) : Buffer.from(chunk);
|
|
7168
|
+
let nl;
|
|
7169
|
+
while ((nl = byteBuf.indexOf(10)) !== -1) {
|
|
7170
|
+
const raw = byteBuf.subarray(0, nl);
|
|
7171
|
+
byteBuf = byteBuf.subarray(nl + 1);
|
|
7172
|
+
pushLine(decodeLineToUtf8(raw).replace(/\r$/, ""));
|
|
6934
7173
|
}
|
|
6935
7174
|
};
|
|
6936
7175
|
const flush = () => {
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
7176
|
+
if (!isWin) {
|
|
7177
|
+
buf += unixDecoder.decode();
|
|
7178
|
+
if (buf) {
|
|
7179
|
+
pushLine(buf);
|
|
7180
|
+
buf = "";
|
|
7181
|
+
}
|
|
7182
|
+
return;
|
|
7183
|
+
}
|
|
7184
|
+
if (byteBuf.length) {
|
|
7185
|
+
pushLine(decodeLineToUtf8(byteBuf));
|
|
7186
|
+
byteBuf = Buffer.alloc(0);
|
|
6941
7187
|
}
|
|
6942
7188
|
};
|
|
6943
7189
|
const resolveExit = () => {
|
|
@@ -7046,7 +7292,7 @@ class ProcessRegistry {
|
|
|
7046
7292
|
}
|
|
7047
7293
|
}
|
|
7048
7294
|
}
|
|
7049
|
-
var MAX_LOG_LINES = 2000, MAX_KEPT_PROCESSES = 20, seq = 0, processRegistry;
|
|
7295
|
+
var MAX_LOG_LINES = 2000, MAX_KEPT_PROCESSES = 20, seq = 0, oemDecoder, processRegistry;
|
|
7050
7296
|
var init_registry = __esm(() => {
|
|
7051
7297
|
processRegistry = new ProcessRegistry;
|
|
7052
7298
|
});
|
|
@@ -7059,6 +7305,28 @@ var init_processes = __esm(() => {
|
|
|
7059
7305
|
|
|
7060
7306
|
// src/tools/bash.ts
|
|
7061
7307
|
import { platform as platform2 } from "os";
|
|
7308
|
+
function extractEchoFileWrite(command) {
|
|
7309
|
+
if (!/^\s*(?:echo|printf)\b/i.test(command))
|
|
7310
|
+
return null;
|
|
7311
|
+
const m = command.match(/[>»]{1,2}\s*"?([^"'\s&|]+)"?/i);
|
|
7312
|
+
if (!m)
|
|
7313
|
+
return null;
|
|
7314
|
+
return m[1].replace(/["'']$/g, "");
|
|
7315
|
+
}
|
|
7316
|
+
function translateSemicolonsForCmd(command) {
|
|
7317
|
+
let out = "";
|
|
7318
|
+
let inQuotes = false;
|
|
7319
|
+
for (let i = 0;i < command.length; i++) {
|
|
7320
|
+
const ch = command[i];
|
|
7321
|
+
if (ch === '"') {
|
|
7322
|
+
inQuotes = !inQuotes;
|
|
7323
|
+
out += ch;
|
|
7324
|
+
continue;
|
|
7325
|
+
}
|
|
7326
|
+
out += ch === ";" && !inQuotes ? "&" : ch;
|
|
7327
|
+
}
|
|
7328
|
+
return out;
|
|
7329
|
+
}
|
|
7062
7330
|
function adaptCommandForWindows(command) {
|
|
7063
7331
|
if (platform2() !== "win32")
|
|
7064
7332
|
return command;
|
|
@@ -7077,13 +7345,15 @@ function adaptCommandForWindows(command) {
|
|
|
7077
7345
|
if (translated && !trimmed.includes("|") && !trimmed.includes(">") && !trimmed.includes("&&") && !trimmed.includes(";")) {
|
|
7078
7346
|
return trimmed.replace(firstWord, translated);
|
|
7079
7347
|
}
|
|
7080
|
-
return command;
|
|
7348
|
+
return translateSemicolonsForCmd(command);
|
|
7081
7349
|
}
|
|
7082
7350
|
function detectToolCallInBash(command) {
|
|
7083
7351
|
const match = command.trim().match(/^([\w-]+)\s+(.+)$/s);
|
|
7084
7352
|
if (!match)
|
|
7085
7353
|
return null;
|
|
7086
7354
|
const tool = match[1];
|
|
7355
|
+
if (NEVER_TOOL_CALLS.has(tool))
|
|
7356
|
+
return null;
|
|
7087
7357
|
const rest = match[2].trim();
|
|
7088
7358
|
if (!/^[a-z][a-z0-9_]+$/.test(tool))
|
|
7089
7359
|
return null;
|
|
@@ -7110,7 +7380,60 @@ function parseToolArgs(raw) {
|
|
|
7110
7380
|
}
|
|
7111
7381
|
return args;
|
|
7112
7382
|
}
|
|
7113
|
-
|
|
7383
|
+
function detectTestResults(output) {
|
|
7384
|
+
if (!output)
|
|
7385
|
+
return null;
|
|
7386
|
+
const jest = output.match(/Tests:\s+(\d+)\s+passed,\s*(\d+)\s+failed/i);
|
|
7387
|
+
if (jest) {
|
|
7388
|
+
return { framework: "jest", passed: +jest[1], failed: +jest[2] };
|
|
7389
|
+
}
|
|
7390
|
+
const mochaPass = output.match(/(\d+)\s+passing/i);
|
|
7391
|
+
const mochaFail = output.match(/(\d+)\s+failing/i);
|
|
7392
|
+
if (mochaPass || mochaFail) {
|
|
7393
|
+
return {
|
|
7394
|
+
framework: "mocha",
|
|
7395
|
+
passed: mochaPass ? +mochaPass[1] : 0,
|
|
7396
|
+
failed: mochaFail ? +mochaFail[1] : 0
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7399
|
+
const pytest = output.match(/(\d+)\s+passed[^\n]*?(?:,\s*(\d+)\s+failed)?/i);
|
|
7400
|
+
if (pytest && /pytest|passed|failed/i.test(output) && /(=====|short test summary|tests\s+ok)/i.test(output) === false && /pytest|collect/i.test(output)) {
|
|
7401
|
+
return {
|
|
7402
|
+
framework: "pytest",
|
|
7403
|
+
passed: +pytest[1],
|
|
7404
|
+
failed: pytest[2] ? +pytest[2] : 0
|
|
7405
|
+
};
|
|
7406
|
+
}
|
|
7407
|
+
const failMarkers = output.match(/\(fail\)/g)?.length ?? 0;
|
|
7408
|
+
const passMarkers = output.match(/\(pass\)/g)?.length ?? 0;
|
|
7409
|
+
const xMarkers = output.match(/\s×\s/g)?.length ?? 0;
|
|
7410
|
+
const summaryMatch = output.match(/^\s*(\d+)\s+pass[^\n]*$/m);
|
|
7411
|
+
const failSummary = output.match(/^\s*(\d+)\s+fail[^\n]*$/m);
|
|
7412
|
+
const ran = output.match(/(?:Ran|ran)\s+\d+\s+tests/i);
|
|
7413
|
+
if (failMarkers > 0 || passMarkers > 0 || xMarkers > 0 || ran) {
|
|
7414
|
+
const failed = Math.max(failMarkers + xMarkers, failSummary ? +failSummary[1] : 0);
|
|
7415
|
+
const passed = Math.max(passMarkers, summaryMatch ? +summaryMatch[1] : 0);
|
|
7416
|
+
return {
|
|
7417
|
+
framework: ran ? "bun/vitest" : "vitest",
|
|
7418
|
+
passed,
|
|
7419
|
+
failed,
|
|
7420
|
+
summary: summaryMatch?.[0] ?? failSummary?.[0]
|
|
7421
|
+
};
|
|
7422
|
+
}
|
|
7423
|
+
return null;
|
|
7424
|
+
}
|
|
7425
|
+
function emptyCliRunHint(command, output, code) {
|
|
7426
|
+
if (code !== 0 || output.trim())
|
|
7427
|
+
return null;
|
|
7428
|
+
if (/[>|]/.test(command))
|
|
7429
|
+
return null;
|
|
7430
|
+
if (/\b(bun test|vitest|pytest|jest|mocha|--test)\b/i.test(command))
|
|
7431
|
+
return null;
|
|
7432
|
+
if (!CLI_FILE_RUN_RE.test(command))
|
|
7433
|
+
return null;
|
|
7434
|
+
return "the command exited 0 but printed NOTHING to stdout. If this should run a CLI program, the file probably has no entry point: read it with read_file and check the code actually calls its main function with command-line arguments (e.g. main(process.argv[2])) and prints results with console.log.";
|
|
7435
|
+
}
|
|
7436
|
+
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, bashGraceMs, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, bashTool;
|
|
7114
7437
|
var init_bash = __esm(() => {
|
|
7115
7438
|
init_command_validator();
|
|
7116
7439
|
init_audit_log();
|
|
@@ -7137,17 +7460,48 @@ var init_bash = __esm(() => {
|
|
|
7137
7460
|
which: 'Use "where" instead.',
|
|
7138
7461
|
echo: "echo works on Windows, but avoid pipes (|).",
|
|
7139
7462
|
"Get-Content": "Use the read_file tool instead.",
|
|
7140
|
-
"Select-Object": "Use the read_file tool with offset/limit instead."
|
|
7463
|
+
"Select-Object": "Use the read_file tool with offset/limit instead.",
|
|
7464
|
+
"Write-Host": "PowerShell cmdlet — this shell is cmd.exe. Print with plain echo instead.",
|
|
7465
|
+
"Select-String": "Use the grep tool instead.",
|
|
7466
|
+
"Out-File": "Write files with the write_file tool instead.",
|
|
7467
|
+
"Set-Content": "Write files with the write_file tool instead.",
|
|
7468
|
+
"Get-ChildItem": "Use the list_dir tool instead.",
|
|
7469
|
+
"Remove-Item": "Use the delete_file tool instead."
|
|
7141
7470
|
};
|
|
7142
7471
|
UNIX_TO_WIN_TRANSLATE = {
|
|
7143
|
-
ls: "
|
|
7144
|
-
pwd: "
|
|
7145
|
-
cat: "
|
|
7146
|
-
wc: "@(Get-Content).Count"
|
|
7472
|
+
ls: "dir",
|
|
7473
|
+
pwd: "cd",
|
|
7474
|
+
cat: "type"
|
|
7147
7475
|
};
|
|
7476
|
+
NEVER_TOOL_CALLS = new Set([
|
|
7477
|
+
"echo",
|
|
7478
|
+
"cat",
|
|
7479
|
+
"type",
|
|
7480
|
+
"printf",
|
|
7481
|
+
"touch",
|
|
7482
|
+
"mkdir",
|
|
7483
|
+
"cp",
|
|
7484
|
+
"mv",
|
|
7485
|
+
"rm",
|
|
7486
|
+
"ls",
|
|
7487
|
+
"dir",
|
|
7488
|
+
"cd",
|
|
7489
|
+
"pwd",
|
|
7490
|
+
"grep",
|
|
7491
|
+
"find",
|
|
7492
|
+
"head",
|
|
7493
|
+
"tail",
|
|
7494
|
+
"wc",
|
|
7495
|
+
"chmod",
|
|
7496
|
+
"sed",
|
|
7497
|
+
"awk"
|
|
7498
|
+
]);
|
|
7499
|
+
CLI_FILE_RUN_RE = /\b(bun|node|deno|python|python3|tsx|ts-node|php|ruby|go\s+run)\S*\s+(run\s+)?["']?[\w./\\-]+\.(ts|js|tsx|jsx|mjs|cjs|py)\b/;
|
|
7148
7500
|
bashTool = {
|
|
7149
7501
|
name: "bash",
|
|
7150
|
-
description:
|
|
7502
|
+
description: `Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).
|
|
7503
|
+
|
|
7504
|
+
Windows notes: the shell is cmd.exe — PowerShell cmdlets (Write-Host, Get-Content, Select-String, Out-File) and bash heredocs (cat << EOF) do NOT work there. Use the read_file/write_file tools instead of cat/echo redirection. Sequential commands: use && (a leading ; is auto-converted to &).`,
|
|
7151
7505
|
tags: ["shell", "code"],
|
|
7152
7506
|
parameters: {
|
|
7153
7507
|
type: "object",
|
|
@@ -7167,7 +7521,7 @@ var init_bash = __esm(() => {
|
|
|
7167
7521
|
handler: async (ctx, args) => {
|
|
7168
7522
|
const originalCommand = String(args.command);
|
|
7169
7523
|
const toolCall = detectToolCallInBash(originalCommand);
|
|
7170
|
-
if (toolCall && toolCall.tool !== "bash" && ctx.toolExecutor) {
|
|
7524
|
+
if (toolCall && toolCall.tool !== "bash" && ctx.toolExecutor && ctx.toolExecutor.hasTool(toolCall.tool)) {
|
|
7171
7525
|
const redirected = await ctx.toolExecutor.executeByName(toolCall.tool, parseToolArgs(toolCall.args), ctx);
|
|
7172
7526
|
return {
|
|
7173
7527
|
success: redirected.success,
|
|
@@ -7176,6 +7530,15 @@ ${redirected.output}`
|
|
|
7176
7530
|
};
|
|
7177
7531
|
}
|
|
7178
7532
|
const command = adaptCommandForWindows(originalCommand);
|
|
7533
|
+
if (platform2() === "win32") {
|
|
7534
|
+
const echoWrite = extractEchoFileWrite(originalCommand);
|
|
7535
|
+
if (echoWrite) {
|
|
7536
|
+
return {
|
|
7537
|
+
success: false,
|
|
7538
|
+
output: t("bash.echo_write_blocked", { path: echoWrite })
|
|
7539
|
+
};
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7179
7542
|
const workdir = args.workdir ? String(args.workdir) : ctx.baseDir;
|
|
7180
7543
|
const appConfig = ctx.config || {};
|
|
7181
7544
|
const fullSecurityConfig = ctx.sessionContext ? getSessionSecurityConfig(appConfig, ctx.sessionContext) : appConfig.security || DEFAULT_SECURITY_CONFIG;
|
|
@@ -7211,6 +7574,22 @@ Hint: check the "workdir" path exists and the command is valid for this OS.`
|
|
|
7211
7574
|
let output2 = entry.log.join(`
|
|
7212
7575
|
`);
|
|
7213
7576
|
processRegistry.remove(entry.id);
|
|
7577
|
+
const cliHint = emptyCliRunHint(command, output2, code);
|
|
7578
|
+
if (cliHint) {
|
|
7579
|
+
output2 = `(exit code 0, no output)
|
|
7580
|
+
|
|
7581
|
+
Hint: ${cliHint}`;
|
|
7582
|
+
}
|
|
7583
|
+
const testRun = detectTestResults(output2);
|
|
7584
|
+
if (testRun && testRun.failed > 0) {
|
|
7585
|
+
output2 = `[test-runner] ${testRun.framework}: ${testRun.failed} test(s) FAILING, ${testRun.passed} passing — do NOT mark verification steps as done while tests fail. Investigate the failures, fix the code, then re-run the tests.
|
|
7586
|
+
|
|
7587
|
+
` + output2;
|
|
7588
|
+
} else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
|
|
7589
|
+
output2 = `[test-runner] ${testRun.framework}: all ${testRun.passed} test(s) passing.
|
|
7590
|
+
|
|
7591
|
+
${output2}`;
|
|
7592
|
+
}
|
|
7214
7593
|
if (!output2 && code !== 0) {
|
|
7215
7594
|
output2 = `(exit code ${code})`;
|
|
7216
7595
|
}
|
|
@@ -7228,7 +7607,14 @@ Hint: "${firstWord}" may not work on Windows. ${hint}`;
|
|
|
7228
7607
|
}
|
|
7229
7608
|
const lines = output2.split(`
|
|
7230
7609
|
`);
|
|
7231
|
-
if (
|
|
7610
|
+
if (testRun && testRun.failed > 0) {
|
|
7611
|
+
const TEST_TAIL_LINES = 400;
|
|
7612
|
+
const kept = lines.slice(-TEST_TAIL_LINES);
|
|
7613
|
+
const skipped = lines.length - kept.length;
|
|
7614
|
+
output2 = (skipped > 0 ? `[... ${skipped} earlier lines omitted — failing tests below]
|
|
7615
|
+
` : "") + kept.join(`
|
|
7616
|
+
`);
|
|
7617
|
+
} else if (lines.length > MAX_PREVIEW_LINES) {
|
|
7232
7618
|
output2 = lines.slice(0, MAX_PREVIEW_LINES).join(`
|
|
7233
7619
|
`) + `
|
|
7234
7620
|
... (${lines.length - MAX_PREVIEW_LINES} more lines)`;
|
|
@@ -7453,6 +7839,7 @@ class SessionLogger {
|
|
|
7453
7839
|
const dir = this.session.getSessionDirectory(meta.id);
|
|
7454
7840
|
if (this.logger) {
|
|
7455
7841
|
this.logger.setSessionDir(dir);
|
|
7842
|
+
this.logger.initSessionLog(meta.id);
|
|
7456
7843
|
}
|
|
7457
7844
|
setAuditSessionDir(dir);
|
|
7458
7845
|
}
|
|
@@ -8726,6 +9113,9 @@ class StuckDetector {
|
|
|
8726
9113
|
escalationThreshold = 3;
|
|
8727
9114
|
fileRewriteCount = new Map;
|
|
8728
9115
|
fileRewriteThreshold = 3;
|
|
9116
|
+
lastBashCommand = "";
|
|
9117
|
+
lastBashOutput = "";
|
|
9118
|
+
emptyBashRunCount = 0;
|
|
8729
9119
|
constructor(threshold = 6, errorThreshold = 3) {
|
|
8730
9120
|
this.threshold = threshold;
|
|
8731
9121
|
this.errorThreshold = errorThreshold;
|
|
@@ -8754,6 +9144,22 @@ class StuckDetector {
|
|
|
8754
9144
|
getLastErrorOutput() {
|
|
8755
9145
|
return this.lastErrorOutput;
|
|
8756
9146
|
}
|
|
9147
|
+
recordBashOutput(command, output) {
|
|
9148
|
+
const trimmed = output.trim();
|
|
9149
|
+
if (command === this.lastBashCommand && !trimmed) {
|
|
9150
|
+
this.emptyBashRunCount++;
|
|
9151
|
+
} else {
|
|
9152
|
+
this.emptyBashRunCount = 0;
|
|
9153
|
+
}
|
|
9154
|
+
this.lastBashCommand = command;
|
|
9155
|
+
this.lastBashOutput = output;
|
|
9156
|
+
}
|
|
9157
|
+
getLastBashCommand() {
|
|
9158
|
+
return this.lastBashCommand;
|
|
9159
|
+
}
|
|
9160
|
+
getLastBashOutput() {
|
|
9161
|
+
return this.lastBashOutput;
|
|
9162
|
+
}
|
|
8757
9163
|
getLastFailedTool() {
|
|
8758
9164
|
return this.lastFailedTool;
|
|
8759
9165
|
}
|
|
@@ -8784,15 +9190,26 @@ class StuckDetector {
|
|
|
8784
9190
|
if (this.recentToolCalls.length < this.repetitionThreshold)
|
|
8785
9191
|
return false;
|
|
8786
9192
|
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
8787
|
-
let
|
|
9193
|
+
let consecutiveCount = 1;
|
|
8788
9194
|
for (let i = this.recentToolCalls.length - 2;i >= 0; i--) {
|
|
8789
9195
|
if (this.recentToolCalls[i].name === last.name && this.recentToolCalls[i].argsKey === last.argsKey) {
|
|
8790
|
-
|
|
9196
|
+
consecutiveCount++;
|
|
8791
9197
|
} else {
|
|
8792
9198
|
break;
|
|
8793
9199
|
}
|
|
8794
9200
|
}
|
|
8795
|
-
|
|
9201
|
+
if (consecutiveCount >= this.repetitionThreshold)
|
|
9202
|
+
return true;
|
|
9203
|
+
const counts = new Map;
|
|
9204
|
+
for (const call of this.recentToolCalls) {
|
|
9205
|
+
const key = `${call.name}::${call.argsKey}`;
|
|
9206
|
+
counts.set(key, (counts.get(key) || 0) + 1);
|
|
9207
|
+
}
|
|
9208
|
+
for (const count of counts.values()) {
|
|
9209
|
+
if (count >= this.repetitionThreshold)
|
|
9210
|
+
return true;
|
|
9211
|
+
}
|
|
9212
|
+
return false;
|
|
8796
9213
|
}
|
|
8797
9214
|
hasConsecutiveFailures() {
|
|
8798
9215
|
return this.consecutiveFailures >= this.errorThreshold;
|
|
@@ -8838,6 +9255,10 @@ class StuckDetector {
|
|
|
8838
9255
|
getActionableHints() {
|
|
8839
9256
|
const hints = [];
|
|
8840
9257
|
const error = this.lastErrorOutput;
|
|
9258
|
+
const isScriptRun = /(^|[\s&])(bun|node|tsx|ts-node|deno|python|python3)\S*\s+[^|&]+\s+[^\s]+$/.test(this.lastBashCommand);
|
|
9259
|
+
if (isScriptRun && !this.lastBashOutput.trim() && this.emptyBashRunCount >= 2) {
|
|
9260
|
+
hints.push(`The command "${this.lastBashCommand}" ran ${this.emptyBashRunCount} times with EMPTY output. The program likely has no entry point — read the file with read_file and check that it actually calls its main function with process.argv / CLI arguments and prints results (console.log). Then run it again.`);
|
|
9261
|
+
}
|
|
8841
9262
|
if (!error)
|
|
8842
9263
|
return hints;
|
|
8843
9264
|
if (/Cannot read properties of undefined|is not a function|is not a constructor/i.test(error)) {
|
|
@@ -8867,6 +9288,18 @@ class StuckDetector {
|
|
|
8867
9288
|
const file = this.getExcessiveRewriteFile();
|
|
8868
9289
|
hints.push(`File ${file} has been rewritten ${this.getFileRewriteCount(file)} times without success. Stop rewriting and try a fundamentally different approach.`);
|
|
8869
9290
|
}
|
|
9291
|
+
if (/Write-Host|Get-Content|Select-String|Out-File|Set-Content/i.test(error)) {
|
|
9292
|
+
hints.push("That looks like a PowerShell cmdlet — the shell here is cmd.exe. Use echo/type for output or the read_file/write_file tools instead.");
|
|
9293
|
+
}
|
|
9294
|
+
if (/Bun\.\w+ is not a function|Bun\.\w+ is not defined|Bun\.\w+ is not a constructor/i.test(error)) {
|
|
9295
|
+
hints.push("That Bun API does not exist. Verify the API name in the Bun docs — common ones are Bun.file, Bun.write, Bun.spawn, Bun.serve. For file checks use fs.existsSync from node:fs.");
|
|
9296
|
+
}
|
|
9297
|
+
if (/error TS\d+|typecheck failed|syntax check failed/i.test(error)) {
|
|
9298
|
+
hints.push("The file still has a type/syntax error (see the error line in the output). Read the file with read_file around the reported line, fix the actual error, then re-run — rewriting the whole file blindly usually makes it worse.");
|
|
9299
|
+
}
|
|
9300
|
+
if (/unexpected.*<<|Непредвиденное появление/i.test(error)) {
|
|
9301
|
+
hints.push("Heredoc (<< EOF) is a bash feature — this shell is cmd.exe and does not support it. Write the file with write_file instead.");
|
|
9302
|
+
}
|
|
8870
9303
|
return hints;
|
|
8871
9304
|
}
|
|
8872
9305
|
getToolAlternative() {
|
|
@@ -8969,6 +9402,9 @@ class StuckDetector {
|
|
|
8969
9402
|
this.recentToolCalls = [];
|
|
8970
9403
|
this.fileRewriteCount.clear();
|
|
8971
9404
|
this.escalationCount = 0;
|
|
9405
|
+
this.lastBashCommand = "";
|
|
9406
|
+
this.lastBashOutput = "";
|
|
9407
|
+
this.emptyBashRunCount = 0;
|
|
8972
9408
|
}
|
|
8973
9409
|
resetStepState(stepId) {
|
|
8974
9410
|
this.currentStepId = stepId;
|
|
@@ -8979,7 +9415,9 @@ class StuckDetector {
|
|
|
8979
9415
|
this.lastErrorOutput = "";
|
|
8980
9416
|
this.recentToolCalls = [];
|
|
8981
9417
|
this.fileRewriteCount.clear();
|
|
8982
|
-
this.
|
|
9418
|
+
this.lastBashCommand = "";
|
|
9419
|
+
this.lastBashOutput = "";
|
|
9420
|
+
this.emptyBashRunCount = 0;
|
|
8983
9421
|
}
|
|
8984
9422
|
}
|
|
8985
9423
|
var init_stuck_detector = __esm(() => {
|
|
@@ -9263,7 +9701,7 @@ var init_moe_executor = __esm(() => {
|
|
|
9263
9701
|
});
|
|
9264
9702
|
|
|
9265
9703
|
// src/modules/execution/verifier.ts
|
|
9266
|
-
import { existsSync as
|
|
9704
|
+
import { existsSync as existsSync18 } from "fs";
|
|
9267
9705
|
import { resolve as resolve10, extname as extname2 } from "path";
|
|
9268
9706
|
import { spawn as spawn2 } from "child_process";
|
|
9269
9707
|
|
|
@@ -9274,7 +9712,7 @@ class StepVerifier {
|
|
|
9274
9712
|
}
|
|
9275
9713
|
async checkFileExists(path) {
|
|
9276
9714
|
const resolved = resolve10(this.baseDir, path);
|
|
9277
|
-
const exists =
|
|
9715
|
+
const exists = existsSync18(resolved);
|
|
9278
9716
|
return {
|
|
9279
9717
|
passed: exists,
|
|
9280
9718
|
message: exists ? t("verify.file_exists", { path }) : t("verify.file_not_found", { path })
|
|
@@ -9290,7 +9728,7 @@ class StepVerifier {
|
|
|
9290
9728
|
}
|
|
9291
9729
|
async runTypeCheck() {
|
|
9292
9730
|
const tsconfigPath = resolve10(this.baseDir, "tsconfig.json");
|
|
9293
|
-
if (!
|
|
9731
|
+
if (!existsSync18(tsconfigPath)) {
|
|
9294
9732
|
return { passed: true, message: "No tsconfig.json found — skipping type check" };
|
|
9295
9733
|
}
|
|
9296
9734
|
try {
|
|
@@ -9303,7 +9741,7 @@ class StepVerifier {
|
|
|
9303
9741
|
}
|
|
9304
9742
|
async runTests() {
|
|
9305
9743
|
const pkgPath = resolve10(this.baseDir, "package.json");
|
|
9306
|
-
if (!
|
|
9744
|
+
if (!existsSync18(pkgPath)) {
|
|
9307
9745
|
return { passed: true, message: "No package.json found — skipping tests" };
|
|
9308
9746
|
}
|
|
9309
9747
|
try {
|
|
@@ -9553,8 +9991,8 @@ var init_agent_moe = __esm(() => {
|
|
|
9553
9991
|
});
|
|
9554
9992
|
|
|
9555
9993
|
// src/modules/memory/search.ts
|
|
9556
|
-
import { readFileSync as readFileSync9, existsSync as
|
|
9557
|
-
import { join as
|
|
9994
|
+
import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
|
|
9995
|
+
import { join as join10 } from "path";
|
|
9558
9996
|
|
|
9559
9997
|
class MemorySearch {
|
|
9560
9998
|
memoryDir;
|
|
@@ -9565,8 +10003,8 @@ class MemorySearch {
|
|
|
9565
10003
|
const results = [];
|
|
9566
10004
|
const lowerQuery = query.toLowerCase();
|
|
9567
10005
|
for (const name of MEMORY_FILES) {
|
|
9568
|
-
const path =
|
|
9569
|
-
if (!
|
|
10006
|
+
const path = join10(this.memoryDir, `${name}.md`);
|
|
10007
|
+
if (!existsSync19(path))
|
|
9570
10008
|
continue;
|
|
9571
10009
|
const content = readFileSync9(path, "utf-8");
|
|
9572
10010
|
const lines = content.split(`
|
|
@@ -9577,8 +10015,8 @@ class MemorySearch {
|
|
|
9577
10015
|
}
|
|
9578
10016
|
}
|
|
9579
10017
|
}
|
|
9580
|
-
const prefsPath =
|
|
9581
|
-
if (
|
|
10018
|
+
const prefsPath = join10(this.memoryDir, "preferences.json");
|
|
10019
|
+
if (existsSync19(prefsPath)) {
|
|
9582
10020
|
try {
|
|
9583
10021
|
const prefs = JSON.parse(readFileSync9(prefsPath, "utf-8"));
|
|
9584
10022
|
for (const [key, value] of Object.entries(prefs)) {
|
|
@@ -9598,8 +10036,8 @@ var init_search = __esm(() => {
|
|
|
9598
10036
|
});
|
|
9599
10037
|
|
|
9600
10038
|
// src/modules/memory/store.ts
|
|
9601
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, appendFileSync as
|
|
9602
|
-
import { join as
|
|
10039
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, appendFileSync as appendFileSync5, existsSync as existsSync20, mkdirSync as mkdirSync11 } from "fs";
|
|
10040
|
+
import { join as join11 } from "path";
|
|
9603
10041
|
|
|
9604
10042
|
class MemoryStore {
|
|
9605
10043
|
memoryDir;
|
|
@@ -9607,8 +10045,8 @@ class MemoryStore {
|
|
|
9607
10045
|
this.memoryDir = memoryDir;
|
|
9608
10046
|
this.ensureDir();
|
|
9609
10047
|
for (const name of MEMORY_FILES2) {
|
|
9610
|
-
const path =
|
|
9611
|
-
if (!
|
|
10048
|
+
const path = join11(this.memoryDir, `${name}.md`);
|
|
10049
|
+
if (!existsSync20(path)) {
|
|
9612
10050
|
writeFileSync7(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}
|
|
9613
10051
|
|
|
9614
10052
|
`, "utf-8");
|
|
@@ -9616,33 +10054,33 @@ class MemoryStore {
|
|
|
9616
10054
|
}
|
|
9617
10055
|
}
|
|
9618
10056
|
ensureDir() {
|
|
9619
|
-
if (!
|
|
9620
|
-
|
|
10057
|
+
if (!existsSync20(this.memoryDir)) {
|
|
10058
|
+
mkdirSync11(this.memoryDir, { recursive: true });
|
|
9621
10059
|
}
|
|
9622
10060
|
}
|
|
9623
10061
|
read(name) {
|
|
9624
|
-
const path =
|
|
9625
|
-
if (!
|
|
10062
|
+
const path = join11(this.memoryDir, `${name}.md`);
|
|
10063
|
+
if (!existsSync20(path))
|
|
9626
10064
|
return "";
|
|
9627
10065
|
return readFileSync10(path, "utf-8");
|
|
9628
10066
|
}
|
|
9629
10067
|
append(name, entry) {
|
|
9630
|
-
const path =
|
|
10068
|
+
const path = join11(this.memoryDir, `${name}.md`);
|
|
9631
10069
|
const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
9632
10070
|
const formatted = `- **${timestamp}** — ${entry}
|
|
9633
10071
|
`;
|
|
9634
|
-
|
|
10072
|
+
appendFileSync5(path, formatted, "utf-8");
|
|
9635
10073
|
}
|
|
9636
10074
|
search(query) {
|
|
9637
10075
|
const searchModule = new MemorySearch(this.memoryDir);
|
|
9638
10076
|
return searchModule.query(query);
|
|
9639
10077
|
}
|
|
9640
10078
|
prefsPath() {
|
|
9641
|
-
return
|
|
10079
|
+
return join11(this.memoryDir, "preferences.json");
|
|
9642
10080
|
}
|
|
9643
10081
|
getPreferences() {
|
|
9644
10082
|
const path = this.prefsPath();
|
|
9645
|
-
if (!
|
|
10083
|
+
if (!existsSync20(path))
|
|
9646
10084
|
return {};
|
|
9647
10085
|
try {
|
|
9648
10086
|
return JSON.parse(readFileSync10(path, "utf-8"));
|
|
@@ -9677,7 +10115,7 @@ var init_store = __esm(() => {
|
|
|
9677
10115
|
});
|
|
9678
10116
|
|
|
9679
10117
|
// src/core/agent.ts
|
|
9680
|
-
import { join as
|
|
10118
|
+
import { join as join12 } from "path";
|
|
9681
10119
|
function isToolCallJson(text) {
|
|
9682
10120
|
const trimmed = text.trim();
|
|
9683
10121
|
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
@@ -9716,12 +10154,14 @@ class Agent {
|
|
|
9716
10154
|
if (dynamic.length > 0) {
|
|
9717
10155
|
builder.addBlocks(dynamic);
|
|
9718
10156
|
}
|
|
9719
|
-
const pluginBlocks = (this.deps.pluginManager.runOnBuildPrompt?.() ?? []).
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
10157
|
+
const pluginBlocks = (this.deps.pluginManager.runOnBuildPrompt?.() ?? []).flatMap((content) => content && content.trim() !== "" ? [
|
|
10158
|
+
{
|
|
10159
|
+
content,
|
|
10160
|
+
priority: "low",
|
|
10161
|
+
essential: false,
|
|
10162
|
+
estimatedTokens: this.deps.llmProvider.countTokens(content)
|
|
10163
|
+
}
|
|
10164
|
+
] : []);
|
|
9725
10165
|
if (pluginBlocks.length > 0) {
|
|
9726
10166
|
builder.addBlocks(pluginBlocks);
|
|
9727
10167
|
}
|
|
@@ -9732,6 +10172,18 @@ class Agent {
|
|
|
9732
10172
|
const tokenCount = this.deps.llmProvider.countTokens(prompt);
|
|
9733
10173
|
return { text: prompt, tokenCount, excluded };
|
|
9734
10174
|
}
|
|
10175
|
+
resolveUsageTokens(apiPromptTokens, apiCompletionTokens, estimatedPromptTokens, completionChars) {
|
|
10176
|
+
if (apiPromptTokens > 0 || apiCompletionTokens > 0) {
|
|
10177
|
+
return {
|
|
10178
|
+
prompt: apiPromptTokens,
|
|
10179
|
+
completion: apiCompletionTokens,
|
|
10180
|
+
total: apiPromptTokens + apiCompletionTokens
|
|
10181
|
+
};
|
|
10182
|
+
}
|
|
10183
|
+
const prompt = Math.max(1, estimatedPromptTokens);
|
|
10184
|
+
const completion = Math.max(0, Math.ceil(completionChars / 4));
|
|
10185
|
+
return { prompt, completion, total: prompt + completion };
|
|
10186
|
+
}
|
|
9735
10187
|
refreshSystemPrompt() {
|
|
9736
10188
|
const { prompt } = this.buildSystemPrompt();
|
|
9737
10189
|
const current = this.deps.contextManager.getActiveHistory().find((m) => m.role === "system");
|
|
@@ -9830,11 +10282,20 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9830
10282
|
let lastToolSignature = "";
|
|
9831
10283
|
let apiPromptTokens = 0;
|
|
9832
10284
|
let apiCompletionTokens = 0;
|
|
10285
|
+
let apiCompletionChars = 0;
|
|
9833
10286
|
const MAX_HALLUCINATION_RETRIES = 3;
|
|
9834
10287
|
let consecutiveToolFailures = 0;
|
|
9835
10288
|
const MAX_CONSECUTIVE_TOOL_FAILURES = 5;
|
|
9836
10289
|
let auditRetries = 0;
|
|
9837
10290
|
const MAX_AUDIT_RETRIES = 3;
|
|
10291
|
+
let emptyResponseRetries = 0;
|
|
10292
|
+
const MAX_EMPTY_RESPONSE_RETRIES = 2;
|
|
10293
|
+
let emptyResponseExhausted = false;
|
|
10294
|
+
let repeatedToolCount = 0;
|
|
10295
|
+
const MAX_REPEATED_TOOL_CALLS = 2;
|
|
10296
|
+
const allToolsForBudget = toolExecutor.getToolDefinitions(this.deps.toolTags);
|
|
10297
|
+
const toolTokenEstimate = allToolsForBudget.reduce((sum, t2) => sum + Math.ceil((t2.description.length + JSON.stringify(t2.parameters).length) / 4), 0);
|
|
10298
|
+
contextManager.setToolTokens(toolTokenEstimate);
|
|
9838
10299
|
while (iteration < config.maxToolIterations && !this.shutdownRequested) {
|
|
9839
10300
|
iteration++;
|
|
9840
10301
|
contextManager.noteIteration();
|
|
@@ -9868,8 +10329,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9868
10329
|
}
|
|
9869
10330
|
this.refreshSystemPrompt();
|
|
9870
10331
|
const history = contextManager.getActiveHistory();
|
|
9871
|
-
|
|
9872
|
-
slog.logToolDefs(allTools.length, allTools.map((t2) => t2.name), iteration);
|
|
10332
|
+
slog.logToolDefs(allToolsForBudget.length, allToolsForBudget.map((t2) => t2.name), iteration);
|
|
9873
10333
|
let textContent = "";
|
|
9874
10334
|
let reasoningContent = "";
|
|
9875
10335
|
const toolCalls = [];
|
|
@@ -9877,8 +10337,10 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9877
10337
|
let emittedReasoning = false;
|
|
9878
10338
|
const textChunks = [];
|
|
9879
10339
|
this.emitPhase(iteration, "thinking", onPhase);
|
|
10340
|
+
const llmStart = Date.now();
|
|
10341
|
+
logger.logLLMRequest(config.model, history.length, input, "agent");
|
|
9880
10342
|
try {
|
|
9881
|
-
for await (const chunk of llmProvider.chat(history,
|
|
10343
|
+
for await (const chunk of llmProvider.chat(history, allToolsForBudget, this.abortController?.signal)) {
|
|
9882
10344
|
if (this.shutdownRequested)
|
|
9883
10345
|
break;
|
|
9884
10346
|
if (chunk.type === "text" && chunk.content) {
|
|
@@ -9924,6 +10386,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9924
10386
|
logger.info("LLM call aborted (interrupt)");
|
|
9925
10387
|
break;
|
|
9926
10388
|
}
|
|
10389
|
+
logger.logLLMResponse(config.model, textContent.length, Date.now() - llmStart, err.message, "agent");
|
|
9927
10390
|
logger.error(`LLM call failed: ${err.message}`);
|
|
9928
10391
|
slog.logError(err.message);
|
|
9929
10392
|
pluginManager.runOnError({ iteration, logger }, err);
|
|
@@ -9936,6 +10399,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9936
10399
|
} finally {
|
|
9937
10400
|
this.emitPhase(iteration, "done", onPhase);
|
|
9938
10401
|
}
|
|
10402
|
+
apiCompletionChars += (textContent || reasoningContent).length;
|
|
10403
|
+
logger.logLLMResponse(config.model, (textContent || reasoningContent).length, Date.now() - llmStart, undefined, "agent");
|
|
9939
10404
|
if (this.shutdownRequested) {
|
|
9940
10405
|
break;
|
|
9941
10406
|
}
|
|
@@ -9959,8 +10424,15 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9959
10424
|
if (this.deps.exitOnComplete && sawToolCall) {
|
|
9960
10425
|
const signature = toolCalls.map((tc) => `${tc.name}:${JSON.stringify(tc.arguments)}`).join("|");
|
|
9961
10426
|
if (signature && signature === lastToolSignature) {
|
|
9962
|
-
|
|
9963
|
-
|
|
10427
|
+
repeatedToolCount++;
|
|
10428
|
+
if (repeatedToolCount >= MAX_REPEATED_TOOL_CALLS) {
|
|
10429
|
+
logger.debug("Exit-on-complete: repeated identical tool call, stopping");
|
|
10430
|
+
break;
|
|
10431
|
+
}
|
|
10432
|
+
contextManager.addMessage({
|
|
10433
|
+
role: "user",
|
|
10434
|
+
content: `<system-summary>You just called the same tool with identical arguments. If the task is done, answer with a final text response NOW. If the command failed, try a different approach.</system-summary>`
|
|
10435
|
+
});
|
|
9964
10436
|
}
|
|
9965
10437
|
lastToolSignature = signature;
|
|
9966
10438
|
}
|
|
@@ -10070,7 +10542,7 @@ ${taskReminder}</system-summary>`
|
|
|
10070
10542
|
if (sessionManager) {
|
|
10071
10543
|
const activeSession = sessionManager.getActiveMeta();
|
|
10072
10544
|
if (activeSession) {
|
|
10073
|
-
const memDir =
|
|
10545
|
+
const memDir = join12(baseDir, ".mma", "memory");
|
|
10074
10546
|
const memStore = new MemoryStore(memDir);
|
|
10075
10547
|
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");
|
|
10076
10548
|
}
|
|
@@ -10134,8 +10606,9 @@ ${warnLine}
|
|
|
10134
10606
|
}
|
|
10135
10607
|
}
|
|
10136
10608
|
if (hallucinationResult.status === "retry") {
|
|
10137
|
-
if (this.deps.exitOnComplete) {
|
|
10609
|
+
if (this.deps.exitOnComplete && textContent?.trim()) {
|
|
10138
10610
|
logger.debug("Exit-on-complete: stopping on first response");
|
|
10611
|
+
lastText = textContent;
|
|
10139
10612
|
break;
|
|
10140
10613
|
}
|
|
10141
10614
|
if (hallucinationRetries >= MAX_HALLUCINATION_RETRIES) {
|
|
@@ -10165,7 +10638,9 @@ ${warnLine}
|
|
|
10165
10638
|
}
|
|
10166
10639
|
if (textContent) {
|
|
10167
10640
|
contextManager.addMessage({ role: "assistant", content: textContent });
|
|
10168
|
-
|
|
10641
|
+
if (config.session.autoSave) {
|
|
10642
|
+
slog.saveAssistantMessage(textContent);
|
|
10643
|
+
}
|
|
10169
10644
|
slog.logAssistant(textContent, reasoningContent, undefined, iteration);
|
|
10170
10645
|
}
|
|
10171
10646
|
lastText = textContent;
|
|
@@ -10178,6 +10653,19 @@ ${warnLine}
|
|
|
10178
10653
|
}
|
|
10179
10654
|
}
|
|
10180
10655
|
if (!sawToolCall) {
|
|
10656
|
+
if (!textContent?.trim()) {
|
|
10657
|
+
if (emptyResponseRetries < MAX_EMPTY_RESPONSE_RETRIES) {
|
|
10658
|
+
emptyResponseRetries++;
|
|
10659
|
+
logger.warn(`Empty response on iteration ${iteration} (retry ${emptyResponseRetries}/${MAX_EMPTY_RESPONSE_RETRIES})`);
|
|
10660
|
+
contextManager.addMessage({
|
|
10661
|
+
role: "user",
|
|
10662
|
+
content: `<system-summary>Your previous response was empty. Answer the user's task now with a final text response or call a tool. Do not reply with reasoning only.</system-summary>`
|
|
10663
|
+
});
|
|
10664
|
+
continue;
|
|
10665
|
+
}
|
|
10666
|
+
emptyResponseExhausted = true;
|
|
10667
|
+
logger.warn(`Empty response retries exhausted after ${MAX_EMPTY_RESPONSE_RETRIES} attempts`);
|
|
10668
|
+
}
|
|
10181
10669
|
if (this.deps.finalAudit) {
|
|
10182
10670
|
const audit = await this.deps.finalAudit();
|
|
10183
10671
|
if (audit && !audit.passed) {
|
|
@@ -10204,6 +10692,7 @@ ${warnLine}
|
|
|
10204
10692
|
}
|
|
10205
10693
|
const tokensUsed = contextManager.getEstimatedTokens();
|
|
10206
10694
|
const budget = contextManager.getBudget();
|
|
10695
|
+
const usageTokens = this.resolveUsageTokens(apiPromptTokens, apiCompletionTokens, tokensUsed, apiCompletionChars);
|
|
10207
10696
|
if (iteration >= config.maxToolIterations) {
|
|
10208
10697
|
return {
|
|
10209
10698
|
success: false,
|
|
@@ -10212,22 +10701,23 @@ ${warnLine}
|
|
|
10212
10701
|
iterationCount: iteration,
|
|
10213
10702
|
contextUsed: tokensUsed,
|
|
10214
10703
|
contextLimit: budget.history,
|
|
10215
|
-
promptTokens:
|
|
10216
|
-
completionTokens:
|
|
10217
|
-
totalTokens:
|
|
10704
|
+
promptTokens: usageTokens.prompt,
|
|
10705
|
+
completionTokens: usageTokens.completion,
|
|
10706
|
+
totalTokens: usageTokens.total,
|
|
10218
10707
|
compactionCount: contextManager.getCompactionCount(),
|
|
10219
10708
|
contextQuality: contextManager.getQuality()
|
|
10220
10709
|
};
|
|
10221
10710
|
}
|
|
10222
10711
|
return {
|
|
10223
|
-
success: true,
|
|
10712
|
+
success: emptyResponseExhausted ? false : true,
|
|
10224
10713
|
text: lastText,
|
|
10714
|
+
error: emptyResponseExhausted ? t("error.empty_response") : undefined,
|
|
10225
10715
|
iterationCount: iteration,
|
|
10226
10716
|
contextUsed: tokensUsed,
|
|
10227
10717
|
contextLimit: budget.history,
|
|
10228
|
-
promptTokens:
|
|
10229
|
-
completionTokens:
|
|
10230
|
-
totalTokens:
|
|
10718
|
+
promptTokens: usageTokens.prompt,
|
|
10719
|
+
completionTokens: usageTokens.completion,
|
|
10720
|
+
totalTokens: usageTokens.total,
|
|
10231
10721
|
compactionCount: contextManager.getCompactionCount(),
|
|
10232
10722
|
contextQuality: contextManager.getQuality()
|
|
10233
10723
|
};
|
|
@@ -10279,6 +10769,7 @@ ${warnLine}
|
|
|
10279
10769
|
if (killed > 0) {
|
|
10280
10770
|
logger.info(`Killed ${killed} background process(es) on shutdown`);
|
|
10281
10771
|
}
|
|
10772
|
+
logger.closeSessionLog();
|
|
10282
10773
|
pluginManager.runOnSessionEnd({
|
|
10283
10774
|
logger,
|
|
10284
10775
|
sessionManager: sessionManager?.getActiveMeta()
|
|
@@ -10349,8 +10840,10 @@ class ContextManager {
|
|
|
10349
10840
|
fileFacts = [];
|
|
10350
10841
|
decisionFacts = [];
|
|
10351
10842
|
errorFacts = [];
|
|
10843
|
+
static MAX_FACTS = 20;
|
|
10352
10844
|
tokenCounter;
|
|
10353
10845
|
pendingImageParts = [];
|
|
10846
|
+
toolTokens = 0;
|
|
10354
10847
|
onCompact = null;
|
|
10355
10848
|
constructor(contextWindow, contextBudget, tokenCounter) {
|
|
10356
10849
|
this.contextWindow = contextWindow;
|
|
@@ -10505,10 +10998,16 @@ ${lines.join(`
|
|
|
10505
10998
|
content: `<system-summary>${this.compactedBlock}</system-summary>`
|
|
10506
10999
|
};
|
|
10507
11000
|
const firstSystem = this.messages.find((m) => m.role === "system");
|
|
11001
|
+
const freshRecent = recentTurns.filter((m) => {
|
|
11002
|
+
if (m.role !== "user")
|
|
11003
|
+
return true;
|
|
11004
|
+
const text = getMessageText(m.content);
|
|
11005
|
+
return !text.startsWith("<system-summary>");
|
|
11006
|
+
});
|
|
10508
11007
|
this.messages = [
|
|
10509
11008
|
...firstSystem ? [firstSystem] : [],
|
|
10510
11009
|
summary,
|
|
10511
|
-
...
|
|
11010
|
+
...freshRecent
|
|
10512
11011
|
];
|
|
10513
11012
|
this.iterationsSinceCompaction = 0;
|
|
10514
11013
|
if (this.onCompact) {
|
|
@@ -10521,19 +11020,22 @@ ${lines.join(`
|
|
|
10521
11020
|
/Файл (?:создан|обновлён|записан|удалён|перемещён):? ([\w./\\-]+\.[a-z]+)/gi,
|
|
10522
11021
|
/file (?:created|updated|written|deleted|moved):? ([\w./\\-]+\.[a-z]+)/gi
|
|
10523
11022
|
];
|
|
11023
|
+
const newFiles = [];
|
|
11024
|
+
const newDecisions = [];
|
|
11025
|
+
const newErrors = [];
|
|
10524
11026
|
for (const msg of turns) {
|
|
10525
11027
|
const content = getMessageText(msg.content);
|
|
10526
11028
|
if (msg.role === "tool") {
|
|
10527
11029
|
for (const pattern of filePatterns) {
|
|
10528
11030
|
for (const match of content.matchAll(pattern)) {
|
|
10529
|
-
|
|
11031
|
+
newFiles.push(match[1]);
|
|
10530
11032
|
}
|
|
10531
11033
|
}
|
|
10532
11034
|
if (content.includes("Plan:") && content.includes("[")) {
|
|
10533
11035
|
const planLine = content.split(`
|
|
10534
11036
|
`).find((line) => line.includes("Plan:"));
|
|
10535
11037
|
if (planLine)
|
|
10536
|
-
|
|
11038
|
+
newDecisions.push(planLine.trim());
|
|
10537
11039
|
}
|
|
10538
11040
|
}
|
|
10539
11041
|
if (msg.role === "assistant") {
|
|
@@ -10541,16 +11043,20 @@ ${lines.join(`
|
|
|
10541
11043
|
const line = content.split(`
|
|
10542
11044
|
`).find((l) => l.includes("decided:") || l.includes("decision:"));
|
|
10543
11045
|
if (line)
|
|
10544
|
-
|
|
11046
|
+
newDecisions.push(line.trim().slice(0, 200));
|
|
10545
11047
|
}
|
|
10546
11048
|
}
|
|
10547
11049
|
if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
|
|
10548
11050
|
const line = content.split(`
|
|
10549
11051
|
`).find((l) => l.includes("Error:") || l.includes("failed") || l.includes("Ошибка:") || l.includes("не удалось"));
|
|
10550
11052
|
if (line)
|
|
10551
|
-
|
|
11053
|
+
newErrors.push(line.trim().slice(0, 250));
|
|
10552
11054
|
}
|
|
10553
11055
|
}
|
|
11056
|
+
const dedup = (arr) => [...new Set(arr)];
|
|
11057
|
+
this.fileFacts = dedup([...this.fileFacts, ...newFiles]).slice(-ContextManager.MAX_FACTS);
|
|
11058
|
+
this.decisionFacts = dedup([...this.decisionFacts, ...newDecisions]).slice(-ContextManager.MAX_FACTS);
|
|
11059
|
+
this.errorFacts = dedup([...this.errorFacts, ...newErrors]).slice(-ContextManager.MAX_FACTS);
|
|
10554
11060
|
}
|
|
10555
11061
|
updateSystemPrompt(content) {
|
|
10556
11062
|
const idx = this.messages.findIndex((m) => m.role === "system");
|
|
@@ -10571,7 +11077,10 @@ ${lines.join(`
|
|
|
10571
11077
|
this.peakTokens = 0;
|
|
10572
11078
|
}
|
|
10573
11079
|
getEstimatedTokens() {
|
|
10574
|
-
return this.messages.reduce((sum, m) => sum + this.estimateMessageTokens(m), 0);
|
|
11080
|
+
return this.messages.reduce((sum, m) => sum + this.estimateMessageTokens(m), 0) + this.toolTokens;
|
|
11081
|
+
}
|
|
11082
|
+
setToolTokens(tokens) {
|
|
11083
|
+
this.toolTokens = tokens;
|
|
10575
11084
|
}
|
|
10576
11085
|
resize(contextWindow, contextBudget, tokenCounter) {
|
|
10577
11086
|
this.contextWindow = contextWindow;
|
|
@@ -10668,15 +11177,83 @@ var init_confidence = __esm(() => {
|
|
|
10668
11177
|
init_i18n();
|
|
10669
11178
|
});
|
|
10670
11179
|
|
|
11180
|
+
// src/modules/hallucination/js-identifiers.ts
|
|
11181
|
+
function isJsMemberAccess(candidate) {
|
|
11182
|
+
const lower = candidate.toLowerCase();
|
|
11183
|
+
const m = lower.match(/^([\w-]+)\./);
|
|
11184
|
+
return m !== null && JS_GLOBALS.has(m[1]);
|
|
11185
|
+
}
|
|
11186
|
+
var JS_GLOBALS;
|
|
11187
|
+
var init_js_identifiers = __esm(() => {
|
|
11188
|
+
JS_GLOBALS = new Set([
|
|
11189
|
+
"process",
|
|
11190
|
+
"console",
|
|
11191
|
+
"math",
|
|
11192
|
+
"json",
|
|
11193
|
+
"global",
|
|
11194
|
+
"globalthis",
|
|
11195
|
+
"window",
|
|
11196
|
+
"document",
|
|
11197
|
+
"buffer",
|
|
11198
|
+
"module",
|
|
11199
|
+
"require",
|
|
11200
|
+
"exports",
|
|
11201
|
+
"url",
|
|
11202
|
+
"promise",
|
|
11203
|
+
"array",
|
|
11204
|
+
"object",
|
|
11205
|
+
"string",
|
|
11206
|
+
"number",
|
|
11207
|
+
"boolean",
|
|
11208
|
+
"symbol",
|
|
11209
|
+
"date",
|
|
11210
|
+
"regexp",
|
|
11211
|
+
"error",
|
|
11212
|
+
"map",
|
|
11213
|
+
"set",
|
|
11214
|
+
"weakmap",
|
|
11215
|
+
"weakset",
|
|
11216
|
+
"proxy",
|
|
11217
|
+
"reflect",
|
|
11218
|
+
"intl",
|
|
11219
|
+
"crypto",
|
|
11220
|
+
"performance",
|
|
11221
|
+
"fetch",
|
|
11222
|
+
"navigator",
|
|
11223
|
+
"location",
|
|
11224
|
+
"settimeout",
|
|
11225
|
+
"setinterval",
|
|
11226
|
+
"cleartimeout",
|
|
11227
|
+
"clearinterval",
|
|
11228
|
+
"queuemicrotask",
|
|
11229
|
+
"structuredclone",
|
|
11230
|
+
"textencoder",
|
|
11231
|
+
"textdecoder",
|
|
11232
|
+
"atomics",
|
|
11233
|
+
"sharedarraybuffer",
|
|
11234
|
+
"dataview",
|
|
11235
|
+
"arraybuffer",
|
|
11236
|
+
"bigint",
|
|
11237
|
+
"infinity",
|
|
11238
|
+
"nan",
|
|
11239
|
+
"undefined",
|
|
11240
|
+
"bun",
|
|
11241
|
+
"deno",
|
|
11242
|
+
"node"
|
|
11243
|
+
]);
|
|
11244
|
+
});
|
|
11245
|
+
|
|
10671
11246
|
// src/modules/hallucination/factual.ts
|
|
10672
|
-
import { existsSync as
|
|
10673
|
-
import { resolve as resolve11, isAbsolute, join as
|
|
11247
|
+
import { existsSync as existsSync21, readdirSync as readdirSync5 } from "fs";
|
|
11248
|
+
import { resolve as resolve11, isAbsolute, join as join13 } from "path";
|
|
10674
11249
|
function looksLikeFilePath(s) {
|
|
10675
11250
|
const lower = s.toLowerCase();
|
|
10676
11251
|
if (TECH_NAMES.has(lower))
|
|
10677
11252
|
return false;
|
|
10678
11253
|
if (!s.includes("/") && !s.includes("\\") && !s.match(/^\w+\.[a-z]{2,4}$/i))
|
|
10679
11254
|
return false;
|
|
11255
|
+
if (isJsMemberAccess(s))
|
|
11256
|
+
return false;
|
|
10680
11257
|
const ext = s.split(".").pop()?.toLowerCase() || "";
|
|
10681
11258
|
return !NON_FILE_EXTENSIONS.has(ext);
|
|
10682
11259
|
}
|
|
@@ -10714,14 +11291,14 @@ class FactualCheck {
|
|
|
10714
11291
|
}
|
|
10715
11292
|
pathExists(fp) {
|
|
10716
11293
|
if (isAbsolute(fp))
|
|
10717
|
-
return
|
|
11294
|
+
return existsSync21(fp);
|
|
10718
11295
|
if (!fp.includes("/") && !fp.includes("\\")) {
|
|
10719
11296
|
return this.bareNameExists(fp);
|
|
10720
11297
|
}
|
|
10721
|
-
return
|
|
11298
|
+
return existsSync21(resolve11(this.baseDir, fp));
|
|
10722
11299
|
}
|
|
10723
11300
|
bareNameExists(name) {
|
|
10724
|
-
if (
|
|
11301
|
+
if (existsSync21(resolve11(this.baseDir, name)))
|
|
10725
11302
|
return true;
|
|
10726
11303
|
if (this.indexHas(name))
|
|
10727
11304
|
return true;
|
|
@@ -10754,14 +11331,14 @@ class FactualCheck {
|
|
|
10754
11331
|
return count;
|
|
10755
11332
|
let entries;
|
|
10756
11333
|
try {
|
|
10757
|
-
entries =
|
|
11334
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
10758
11335
|
} catch {
|
|
10759
11336
|
return count;
|
|
10760
11337
|
}
|
|
10761
11338
|
for (const entry of entries) {
|
|
10762
11339
|
if (count >= FactualCheck.MAX_INDEXED_FILES)
|
|
10763
11340
|
break;
|
|
10764
|
-
const full =
|
|
11341
|
+
const full = join13(dir, entry.name);
|
|
10765
11342
|
if (entry.isDirectory()) {
|
|
10766
11343
|
if (!IGNORED_DIRS.has(entry.name)) {
|
|
10767
11344
|
count = this.scanDir(full, index, count);
|
|
@@ -10786,6 +11363,7 @@ class FactualCheck {
|
|
|
10786
11363
|
var IGNORED_DIRS, NON_FILE_EXTENSIONS, TECH_NAMES;
|
|
10787
11364
|
var init_factual = __esm(() => {
|
|
10788
11365
|
init_i18n();
|
|
11366
|
+
init_js_identifiers();
|
|
10789
11367
|
IGNORED_DIRS = new Set([
|
|
10790
11368
|
"node_modules",
|
|
10791
11369
|
".git",
|
|
@@ -12388,7 +12966,7 @@ ${JSON.stringify(result, null, 2)}`
|
|
|
12388
12966
|
|
|
12389
12967
|
// src/tools/search-history.ts
|
|
12390
12968
|
import * as fs from "fs";
|
|
12391
|
-
import { join as
|
|
12969
|
+
import { join as join14 } from "path";
|
|
12392
12970
|
import { homedir as homedir5 } from "os";
|
|
12393
12971
|
function searchFile(filePath, query, maxResults, results) {
|
|
12394
12972
|
if (!fs.existsSync(filePath))
|
|
@@ -12432,7 +13010,7 @@ var init_search_history = __esm(() => {
|
|
|
12432
13010
|
const query = String(args.query || "").toLowerCase();
|
|
12433
13011
|
const maxResults = Number(args.maxResults) || 5;
|
|
12434
13012
|
const sessionId = args.sessionId ? String(args.sessionId) : null;
|
|
12435
|
-
const sessionDir =
|
|
13013
|
+
const sessionDir = join14(homedir5(), ".mma", "sessions");
|
|
12436
13014
|
const results = [];
|
|
12437
13015
|
try {
|
|
12438
13016
|
if (!fs.existsSync(sessionDir)) {
|
|
@@ -12447,7 +13025,7 @@ var init_search_history = __esm(() => {
|
|
|
12447
13025
|
continue;
|
|
12448
13026
|
if (sessionId && entry.name !== sessionId)
|
|
12449
13027
|
continue;
|
|
12450
|
-
const historyFile =
|
|
13028
|
+
const historyFile = join14(sessionDir, entry.name, "history.jsonl");
|
|
12451
13029
|
searchFile(historyFile, query, maxResults, results);
|
|
12452
13030
|
if (results.length >= maxResults)
|
|
12453
13031
|
break;
|
|
@@ -12475,7 +13053,7 @@ var init_search_history = __esm(() => {
|
|
|
12475
13053
|
|
|
12476
13054
|
// src/tools/remember.ts
|
|
12477
13055
|
import { homedir as homedir6 } from "os";
|
|
12478
|
-
import { join as
|
|
13056
|
+
import { join as join15 } from "path";
|
|
12479
13057
|
var CATEGORIES, rememberTool;
|
|
12480
13058
|
var init_remember = __esm(() => {
|
|
12481
13059
|
init_i18n();
|
|
@@ -12513,7 +13091,7 @@ var init_remember = __esm(() => {
|
|
|
12513
13091
|
if (!CATEGORIES.includes(category)) {
|
|
12514
13092
|
return { success: false, output: t("tool.invalid_params") };
|
|
12515
13093
|
}
|
|
12516
|
-
const memoryDir =
|
|
13094
|
+
const memoryDir = join15(homedir6(), ".mma", "memory");
|
|
12517
13095
|
const store = new MemoryStore(memoryDir);
|
|
12518
13096
|
try {
|
|
12519
13097
|
if (category === "preferences") {
|
|
@@ -12546,7 +13124,7 @@ var init_remember = __esm(() => {
|
|
|
12546
13124
|
|
|
12547
13125
|
// src/tools/recall.ts
|
|
12548
13126
|
import { homedir as homedir7 } from "os";
|
|
12549
|
-
import { join as
|
|
13127
|
+
import { join as join16 } from "path";
|
|
12550
13128
|
function formatAll(store) {
|
|
12551
13129
|
const parts = [];
|
|
12552
13130
|
const prefs = store.getPreferences();
|
|
@@ -12629,7 +13207,7 @@ var init_recall = __esm(() => {
|
|
|
12629
13207
|
handler: async (_ctx, args) => {
|
|
12630
13208
|
const query = args.query ? String(args.query) : "";
|
|
12631
13209
|
const category = args.category ? String(args.category) : "";
|
|
12632
|
-
const memoryDir =
|
|
13210
|
+
const memoryDir = join16(homedir7(), ".mma", "memory");
|
|
12633
13211
|
const store = new MemoryStore(memoryDir);
|
|
12634
13212
|
try {
|
|
12635
13213
|
if (!query && !category) {
|
|
@@ -12825,15 +13403,15 @@ function buildIndexInjectionScript() {
|
|
|
12825
13403
|
|
|
12826
13404
|
// src/modules/browser/cookie-store.ts
|
|
12827
13405
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
12828
|
-
import { join as
|
|
13406
|
+
import { join as join17 } from "path";
|
|
12829
13407
|
|
|
12830
13408
|
class CookieStore {
|
|
12831
13409
|
filePath;
|
|
12832
13410
|
constructor(cookieDir) {
|
|
12833
|
-
this.filePath =
|
|
13411
|
+
this.filePath = join17(cookieDir, "cookies.json");
|
|
12834
13412
|
}
|
|
12835
13413
|
async save(cookies) {
|
|
12836
|
-
await mkdir(
|
|
13414
|
+
await mkdir(join17(this.filePath, ".."), { recursive: true });
|
|
12837
13415
|
await writeFile(this.filePath, JSON.stringify(cookies, null, 2), "utf-8");
|
|
12838
13416
|
}
|
|
12839
13417
|
async load() {
|
|
@@ -12851,7 +13429,9 @@ class CookieStore {
|
|
|
12851
13429
|
var init_cookie_store = () => {};
|
|
12852
13430
|
|
|
12853
13431
|
// src/modules/browser/session.ts
|
|
12854
|
-
import {
|
|
13432
|
+
import {
|
|
13433
|
+
chromium
|
|
13434
|
+
} from "playwright";
|
|
12855
13435
|
|
|
12856
13436
|
class BrowserActionTracker {
|
|
12857
13437
|
history = [];
|
|
@@ -12866,7 +13446,10 @@ class BrowserActionTracker {
|
|
|
12866
13446
|
const recent = this.history.slice(-this.threshold);
|
|
12867
13447
|
const allSame = recent.every((h) => h.action === recent[0].action && h.url === recent[0].url && JSON.stringify(h.args) === JSON.stringify(recent[0].args));
|
|
12868
13448
|
if (allSame) {
|
|
12869
|
-
return t("browser.repeated_action", {
|
|
13449
|
+
return t("browser.repeated_action", {
|
|
13450
|
+
action,
|
|
13451
|
+
threshold: this.threshold
|
|
13452
|
+
});
|
|
12870
13453
|
}
|
|
12871
13454
|
return null;
|
|
12872
13455
|
}
|
|
@@ -12881,7 +13464,12 @@ class BrowserSession {
|
|
|
12881
13464
|
page = null;
|
|
12882
13465
|
config;
|
|
12883
13466
|
cookieStore;
|
|
12884
|
-
state = {
|
|
13467
|
+
state = {
|
|
13468
|
+
isOpen: false,
|
|
13469
|
+
url: null,
|
|
13470
|
+
title: null,
|
|
13471
|
+
elementCount: 0
|
|
13472
|
+
};
|
|
12885
13473
|
actionTracker = new BrowserActionTracker(3);
|
|
12886
13474
|
constructor(config) {
|
|
12887
13475
|
this.config = config;
|
|
@@ -12925,7 +13513,10 @@ class BrowserSession {
|
|
|
12925
13513
|
result = await this.wait(Number(args.ms || 1000));
|
|
12926
13514
|
break;
|
|
12927
13515
|
default:
|
|
12928
|
-
return {
|
|
13516
|
+
return {
|
|
13517
|
+
success: false,
|
|
13518
|
+
output: t("browser.unknown_action", { action })
|
|
13519
|
+
};
|
|
12929
13520
|
}
|
|
12930
13521
|
if (action !== "open") {
|
|
12931
13522
|
const warning = this.actionTracker.record(action, args, this.page?.url() || "");
|
|
@@ -12937,7 +13528,10 @@ class BrowserSession {
|
|
|
12937
13528
|
}
|
|
12938
13529
|
return result;
|
|
12939
13530
|
} catch (err) {
|
|
12940
|
-
return {
|
|
13531
|
+
return {
|
|
13532
|
+
success: false,
|
|
13533
|
+
output: t("browser.error", { message: err.message })
|
|
13534
|
+
};
|
|
12941
13535
|
}
|
|
12942
13536
|
}
|
|
12943
13537
|
async ensurePage() {
|
|
@@ -12951,7 +13545,10 @@ class BrowserSession {
|
|
|
12951
13545
|
return;
|
|
12952
13546
|
this.browser = await chromium.launch({ headless: this.config.headless });
|
|
12953
13547
|
this.context = await this.browser.newContext({
|
|
12954
|
-
viewport: {
|
|
13548
|
+
viewport: {
|
|
13549
|
+
width: this.config.viewportWidth,
|
|
13550
|
+
height: this.config.viewportHeight
|
|
13551
|
+
}
|
|
12955
13552
|
});
|
|
12956
13553
|
const savedCookies = await this.cookieStore.load();
|
|
12957
13554
|
if (savedCookies.length > 0) {
|
|
@@ -12990,17 +13587,24 @@ class BrowserSession {
|
|
|
12990
13587
|
async open(url) {
|
|
12991
13588
|
if (!url)
|
|
12992
13589
|
return { success: false, output: t("browser.url_required") };
|
|
12993
|
-
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
12994
|
-
|
|
13590
|
+
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file://")) {
|
|
13591
|
+
const isLocalhost = /^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?$/i.test(url);
|
|
13592
|
+
url = isLocalhost ? "http://" + url : "https://" + url;
|
|
12995
13593
|
}
|
|
12996
13594
|
await this.launch();
|
|
12997
13595
|
if (!this.page)
|
|
12998
13596
|
return { success: false, output: t("browser.create_page_failed") };
|
|
12999
13597
|
this.actionTracker.reset();
|
|
13000
13598
|
try {
|
|
13001
|
-
await this.page.goto(url, {
|
|
13599
|
+
await this.page.goto(url, {
|
|
13600
|
+
waitUntil: "domcontentloaded",
|
|
13601
|
+
timeout: this.config.navigationTimeout
|
|
13602
|
+
});
|
|
13002
13603
|
} catch (err) {
|
|
13003
|
-
return {
|
|
13604
|
+
return {
|
|
13605
|
+
success: false,
|
|
13606
|
+
output: t("browser.nav_failed", { message: err.message })
|
|
13607
|
+
};
|
|
13004
13608
|
}
|
|
13005
13609
|
const snapshot = await this.takeSnapshot();
|
|
13006
13610
|
await this.saveCookies();
|
|
@@ -13024,7 +13628,10 @@ class BrowserSession {
|
|
|
13024
13628
|
await this.saveCookies();
|
|
13025
13629
|
return { success: true, output: snapshot };
|
|
13026
13630
|
} catch (err2) {
|
|
13027
|
-
return {
|
|
13631
|
+
return {
|
|
13632
|
+
success: false,
|
|
13633
|
+
output: t("browser.click_failed", { message: err2.message })
|
|
13634
|
+
};
|
|
13028
13635
|
}
|
|
13029
13636
|
}
|
|
13030
13637
|
async type(target, text) {
|
|
@@ -13045,7 +13652,10 @@ class BrowserSession {
|
|
|
13045
13652
|
const snapshot = await this.takeSnapshot();
|
|
13046
13653
|
return { success: true, output: snapshot };
|
|
13047
13654
|
} catch (err2) {
|
|
13048
|
-
return {
|
|
13655
|
+
return {
|
|
13656
|
+
success: false,
|
|
13657
|
+
output: t("browser.type_failed", { message: err2.message })
|
|
13658
|
+
};
|
|
13049
13659
|
}
|
|
13050
13660
|
}
|
|
13051
13661
|
async scroll(direction) {
|
|
@@ -13064,7 +13674,10 @@ class BrowserSession {
|
|
|
13064
13674
|
const snapshot = await this.takeSnapshot();
|
|
13065
13675
|
return { success: true, output: snapshot };
|
|
13066
13676
|
} catch (err2) {
|
|
13067
|
-
return {
|
|
13677
|
+
return {
|
|
13678
|
+
success: false,
|
|
13679
|
+
output: t("browser.scroll_failed", { message: err2.message })
|
|
13680
|
+
};
|
|
13068
13681
|
}
|
|
13069
13682
|
}
|
|
13070
13683
|
async back() {
|
|
@@ -13073,7 +13686,10 @@ class BrowserSession {
|
|
|
13073
13686
|
return err;
|
|
13074
13687
|
if (!this.page)
|
|
13075
13688
|
return { success: false, output: "No page" };
|
|
13076
|
-
await this.page.goBack({
|
|
13689
|
+
await this.page.goBack({
|
|
13690
|
+
waitUntil: "domcontentloaded",
|
|
13691
|
+
timeout: this.config.navigationTimeout
|
|
13692
|
+
}).catch(() => {});
|
|
13077
13693
|
await new Promise((r) => setTimeout(r, 300));
|
|
13078
13694
|
const snapshot = await this.takeSnapshot();
|
|
13079
13695
|
return { success: true, output: snapshot };
|
|
@@ -13084,7 +13700,10 @@ class BrowserSession {
|
|
|
13084
13700
|
return err;
|
|
13085
13701
|
if (!this.page)
|
|
13086
13702
|
return { success: false, output: "No page" };
|
|
13087
|
-
await this.page.goForward({
|
|
13703
|
+
await this.page.goForward({
|
|
13704
|
+
waitUntil: "domcontentloaded",
|
|
13705
|
+
timeout: this.config.navigationTimeout
|
|
13706
|
+
}).catch(() => {});
|
|
13088
13707
|
await new Promise((r) => setTimeout(r, 300));
|
|
13089
13708
|
const snapshot = await this.takeSnapshot();
|
|
13090
13709
|
return { success: true, output: snapshot };
|
|
@@ -13153,10 +13772,10 @@ var init_types = __esm(() => {
|
|
|
13153
13772
|
});
|
|
13154
13773
|
|
|
13155
13774
|
// src/tools/browser.ts
|
|
13156
|
-
import { join as
|
|
13775
|
+
import { join as join18 } from "path";
|
|
13157
13776
|
function getSession(ctx) {
|
|
13158
13777
|
if (!session) {
|
|
13159
|
-
const cookieDir =
|
|
13778
|
+
const cookieDir = join18(ctx.baseDir, ".mma", "browser");
|
|
13160
13779
|
session = new BrowserSession({
|
|
13161
13780
|
...DEFAULT_BROWSER_CONFIG,
|
|
13162
13781
|
headless: ctx.config.browser?.headless ?? true,
|
|
@@ -13285,9 +13904,9 @@ async function readClipboardImage() {
|
|
|
13285
13904
|
async function readClipboardFallback() {
|
|
13286
13905
|
const { platform: platform3 } = await import("os");
|
|
13287
13906
|
const { execSync } = await import("child_process");
|
|
13288
|
-
const { readFileSync: readFileSync13, unlinkSync:
|
|
13289
|
-
const { join:
|
|
13290
|
-
const tmpPath =
|
|
13907
|
+
const { readFileSync: readFileSync13, unlinkSync: unlinkSync4 } = await import("fs");
|
|
13908
|
+
const { join: join19 } = await import("path");
|
|
13909
|
+
const tmpPath = join19(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
|
|
13291
13910
|
try {
|
|
13292
13911
|
if (platform3() === "linux") {
|
|
13293
13912
|
execSync(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, { timeout: 5000 });
|
|
@@ -13295,11 +13914,11 @@ async function readClipboardFallback() {
|
|
|
13295
13914
|
return null;
|
|
13296
13915
|
}
|
|
13297
13916
|
const buf = readFileSync13(tmpPath);
|
|
13298
|
-
|
|
13917
|
+
unlinkSync4(tmpPath);
|
|
13299
13918
|
return buf.length > 0 ? buf : null;
|
|
13300
13919
|
} catch {
|
|
13301
13920
|
try {
|
|
13302
|
-
|
|
13921
|
+
unlinkSync4(tmpPath);
|
|
13303
13922
|
} catch {}
|
|
13304
13923
|
return null;
|
|
13305
13924
|
}
|
|
@@ -13365,7 +13984,7 @@ var init_image_utils = __esm(() => {
|
|
|
13365
13984
|
});
|
|
13366
13985
|
|
|
13367
13986
|
// src/tools/attach-image.ts
|
|
13368
|
-
import { existsSync as
|
|
13987
|
+
import { existsSync as existsSync23 } from "fs";
|
|
13369
13988
|
import { resolve as resolve12 } from "path";
|
|
13370
13989
|
var attachImageTool;
|
|
13371
13990
|
var init_attach_image = __esm(() => {
|
|
@@ -13408,7 +14027,7 @@ var init_attach_image = __esm(() => {
|
|
|
13408
14027
|
dataUrl = result.dataUrl;
|
|
13409
14028
|
} else {
|
|
13410
14029
|
const absPath = resolve12(ctx.baseDir, source);
|
|
13411
|
-
if (!
|
|
14030
|
+
if (!existsSync23(absPath)) {
|
|
13412
14031
|
return {
|
|
13413
14032
|
success: false,
|
|
13414
14033
|
output: t("image.not_found", { path: source })
|
|
@@ -13564,17 +14183,17 @@ class ModuleRegistry {
|
|
|
13564
14183
|
}
|
|
13565
14184
|
|
|
13566
14185
|
// src/modules/plugins/loader.ts
|
|
13567
|
-
import { readdirSync as
|
|
13568
|
-
import { join as
|
|
14186
|
+
import { readdirSync as readdirSync7, existsSync as existsSync24, statSync as statSync5 } from "fs";
|
|
14187
|
+
import { join as join19 } from "path";
|
|
13569
14188
|
|
|
13570
14189
|
class PluginLoader {
|
|
13571
14190
|
loadFromDir(dirPath, pluginManager, logger) {
|
|
13572
|
-
if (!
|
|
14191
|
+
if (!existsSync24(dirPath))
|
|
13573
14192
|
return;
|
|
13574
|
-
const entries =
|
|
14193
|
+
const entries = readdirSync7(dirPath);
|
|
13575
14194
|
for (const entry of entries) {
|
|
13576
|
-
const fullPath =
|
|
13577
|
-
if (!
|
|
14195
|
+
const fullPath = join19(dirPath, entry);
|
|
14196
|
+
if (!statSync5(fullPath).isFile())
|
|
13578
14197
|
continue;
|
|
13579
14198
|
if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
|
|
13580
14199
|
continue;
|
|
@@ -13597,9 +14216,16 @@ var init_loader = __esm(() => {
|
|
|
13597
14216
|
|
|
13598
14217
|
// src/modules/plugins/builtin/lint-on-write.ts
|
|
13599
14218
|
import { spawn as spawn4, execSync } from "child_process";
|
|
13600
|
-
import { existsSync as
|
|
13601
|
-
import { resolve as resolve13, extname as extname4, join as
|
|
14219
|
+
import { existsSync as existsSync25, readFileSync as readFileSync13 } from "fs";
|
|
14220
|
+
import { resolve as resolve13, extname as extname4, join as join20 } from "path";
|
|
13602
14221
|
import { platform as platform3 } from "os";
|
|
14222
|
+
function contentHash(content) {
|
|
14223
|
+
let h = 5381;
|
|
14224
|
+
for (let i = 0;i < content.length; i++) {
|
|
14225
|
+
h = (h << 5) + h + content.charCodeAt(i) | 0;
|
|
14226
|
+
}
|
|
14227
|
+
return String(h);
|
|
14228
|
+
}
|
|
13603
14229
|
function getWinDecoder() {
|
|
13604
14230
|
if (_winDecoder === undefined) {
|
|
13605
14231
|
if (platform3() !== "win32") {
|
|
@@ -13637,7 +14263,7 @@ class LintOnWritePlugin {
|
|
|
13637
14263
|
if (!path)
|
|
13638
14264
|
return;
|
|
13639
14265
|
const fullPath = resolve13(ctx.baseDir, path);
|
|
13640
|
-
if (!
|
|
14266
|
+
if (!existsSync25(fullPath))
|
|
13641
14267
|
return;
|
|
13642
14268
|
const ext = extname4(fullPath);
|
|
13643
14269
|
const syntaxError = await this.checkSyntax(fullPath, ext, ctx.baseDir);
|
|
@@ -13652,20 +14278,30 @@ class LintOnWritePlugin {
|
|
|
13652
14278
|
}
|
|
13653
14279
|
async checkSyntax(filePath, ext, baseDir) {
|
|
13654
14280
|
if (ext === ".ts" || ext === ".tsx") {
|
|
14281
|
+
let content = "";
|
|
13655
14282
|
try {
|
|
13656
|
-
|
|
14283
|
+
content = readFileSync13(filePath, "utf-8");
|
|
14284
|
+
} catch {
|
|
14285
|
+
return null;
|
|
14286
|
+
}
|
|
14287
|
+
const hash = contentHash(content);
|
|
14288
|
+
const cached = syntaxCache.get(filePath);
|
|
14289
|
+
if (cached && cached.hash === hash) {
|
|
14290
|
+
return cached.error;
|
|
14291
|
+
}
|
|
14292
|
+
try {
|
|
14293
|
+
await runAsync(`bun build --no-bundle --target=bun "${filePath}"`, baseDir, 1e4);
|
|
14294
|
+
syntaxCache.set(filePath, { hash, error: null });
|
|
13657
14295
|
return null;
|
|
13658
14296
|
} catch (err) {
|
|
13659
14297
|
if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
|
|
13660
14298
|
return null;
|
|
13661
14299
|
}
|
|
13662
14300
|
const stderr = err.stderr?.toString() || err.stdout?.toString() || "";
|
|
13663
|
-
|
|
13664
|
-
|
|
13665
|
-
|
|
13666
|
-
|
|
13667
|
-
}
|
|
13668
|
-
return null;
|
|
14301
|
+
const firstError = stderr.split(`
|
|
14302
|
+
`).find((line) => line.trim()) || "TypeScript syntax error";
|
|
14303
|
+
syntaxCache.set(filePath, { hash, error: firstError.trim() });
|
|
14304
|
+
return firstError.trim();
|
|
13669
14305
|
}
|
|
13670
14306
|
}
|
|
13671
14307
|
if (ext === ".js" || ext === ".jsx") {
|
|
@@ -13683,8 +14319,8 @@ class LintOnWritePlugin {
|
|
|
13683
14319
|
}
|
|
13684
14320
|
async runProjectLint(ctx, result) {
|
|
13685
14321
|
try {
|
|
13686
|
-
const packageJsonPath =
|
|
13687
|
-
if (!
|
|
14322
|
+
const packageJsonPath = join20(ctx.baseDir, "package.json");
|
|
14323
|
+
if (!existsSync25(packageJsonPath)) {
|
|
13688
14324
|
return;
|
|
13689
14325
|
}
|
|
13690
14326
|
const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf-8"));
|
|
@@ -13703,8 +14339,8 @@ class LintOnWritePlugin {
|
|
|
13703
14339
|
}
|
|
13704
14340
|
}
|
|
13705
14341
|
async runProjectTypeCheck(ctx, result) {
|
|
13706
|
-
const tsconfigPath =
|
|
13707
|
-
if (!
|
|
14342
|
+
const tsconfigPath = join20(ctx.baseDir, "tsconfig.json");
|
|
14343
|
+
if (!existsSync25(tsconfigPath)) {
|
|
13708
14344
|
return;
|
|
13709
14345
|
}
|
|
13710
14346
|
const now = Date.now();
|
|
@@ -13785,8 +14421,9 @@ function runAsync(command, cwd, timeoutMs) {
|
|
|
13785
14421
|
});
|
|
13786
14422
|
});
|
|
13787
14423
|
}
|
|
13788
|
-
var TYPE_CHECK_DEBOUNCE_MS = 2000, _winDecoder, plugin;
|
|
14424
|
+
var TYPE_CHECK_DEBOUNCE_MS = 2000, syntaxCache, _winDecoder, plugin;
|
|
13789
14425
|
var init_lint_on_write = __esm(() => {
|
|
14426
|
+
syntaxCache = new Map;
|
|
13790
14427
|
plugin = new LintOnWritePlugin;
|
|
13791
14428
|
});
|
|
13792
14429
|
|
|
@@ -13847,6 +14484,25 @@ class PlanCreator {
|
|
|
13847
14484
|
baseDir
|
|
13848
14485
|
};
|
|
13849
14486
|
}
|
|
14487
|
+
static replan(plan, newSteps, title) {
|
|
14488
|
+
const kept = plan.steps.filter((s) => s.status === "done" || s.status === "skipped");
|
|
14489
|
+
const baseTitle = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
|
|
14490
|
+
const newTitle = title || baseTitle;
|
|
14491
|
+
const totalSteps = kept.length + newSteps.length;
|
|
14492
|
+
const added = newSteps.map((desc, i) => ({
|
|
14493
|
+
id: kept.length + i + 1,
|
|
14494
|
+
description: desc,
|
|
14495
|
+
status: "pending"
|
|
14496
|
+
}));
|
|
14497
|
+
return {
|
|
14498
|
+
id: plan.id,
|
|
14499
|
+
title: `[${totalSteps}] ${newTitle}`,
|
|
14500
|
+
steps: [...kept, ...added],
|
|
14501
|
+
createdAt: plan.createdAt,
|
|
14502
|
+
baseDir: plan.baseDir,
|
|
14503
|
+
name: plan.name
|
|
14504
|
+
};
|
|
14505
|
+
}
|
|
13850
14506
|
static toPromptBlock(plan, currentStepIndex) {
|
|
13851
14507
|
const date = plan.createdAt.slice(0, 10);
|
|
13852
14508
|
const lines = [
|
|
@@ -13919,15 +14575,89 @@ class PlanTracker {
|
|
|
13919
14575
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
13920
14576
|
return `[${this.plan.id}] ${this.plan.title} ${done}/${total} ${bar} ${pct}%`;
|
|
13921
14577
|
}
|
|
13922
|
-
toPromptBlock() {
|
|
13923
|
-
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
14578
|
+
toPromptBlock() {
|
|
14579
|
+
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
14580
|
+
}
|
|
14581
|
+
}
|
|
14582
|
+
var init_tracker = () => {};
|
|
14583
|
+
|
|
14584
|
+
// src/modules/execution/auditor.ts
|
|
14585
|
+
import { existsSync as existsSync26, readdirSync as readdirSync8 } from "fs";
|
|
14586
|
+
import { resolve as resolve14, join as join21 } from "path";
|
|
14587
|
+
function findTestFile(dir, depth = 0) {
|
|
14588
|
+
if (depth > 5)
|
|
14589
|
+
return null;
|
|
14590
|
+
let entries;
|
|
14591
|
+
try {
|
|
14592
|
+
entries = readdirSync8(dir, { withFileTypes: true });
|
|
14593
|
+
} catch {
|
|
14594
|
+
return null;
|
|
14595
|
+
}
|
|
14596
|
+
for (const e of entries) {
|
|
14597
|
+
const full = join21(dir, e.name);
|
|
14598
|
+
if (e.isDirectory()) {
|
|
14599
|
+
if (SKIP_DIRS.has(e.name))
|
|
14600
|
+
continue;
|
|
14601
|
+
const found = findTestFile(full, depth + 1);
|
|
14602
|
+
if (found)
|
|
14603
|
+
return found;
|
|
14604
|
+
} else if (TEST_EXT_RE.test(e.name)) {
|
|
14605
|
+
return full;
|
|
14606
|
+
}
|
|
14607
|
+
}
|
|
14608
|
+
return null;
|
|
14609
|
+
}
|
|
14610
|
+
function hasTestStep(plan) {
|
|
14611
|
+
return plan.steps.some((s) => TEST_STEP_RE.test(s.description));
|
|
14612
|
+
}
|
|
14613
|
+
function extractFailingNames(output, limit = 5) {
|
|
14614
|
+
const names = [];
|
|
14615
|
+
for (const m of output.matchAll(/\(fail\)\s*([^\n]+)/g)) {
|
|
14616
|
+
const name = m[1].trim();
|
|
14617
|
+
if (name && !names.includes(name))
|
|
14618
|
+
names.push(name);
|
|
14619
|
+
if (names.length >= limit)
|
|
14620
|
+
break;
|
|
14621
|
+
}
|
|
14622
|
+
return names;
|
|
14623
|
+
}
|
|
14624
|
+
async function runTests(baseDir) {
|
|
14625
|
+
const entry = processRegistry.start("bun test", baseDir);
|
|
14626
|
+
const exited = await processRegistry.waitForExit(entry.id, 90000);
|
|
14627
|
+
const output = entry.log.join(`
|
|
14628
|
+
`);
|
|
14629
|
+
processRegistry.remove(entry.id);
|
|
14630
|
+
if (!exited) {
|
|
14631
|
+
return {
|
|
14632
|
+
checked: true,
|
|
14633
|
+
passed: true,
|
|
14634
|
+
failed: 0,
|
|
14635
|
+
passedCount: 0,
|
|
14636
|
+
detail: "test run timed out after 90s — result unknown",
|
|
14637
|
+
command: "bun test"
|
|
14638
|
+
};
|
|
14639
|
+
}
|
|
14640
|
+
const run = detectTestResults(output);
|
|
14641
|
+
if (!run) {
|
|
14642
|
+
return {
|
|
14643
|
+
checked: true,
|
|
14644
|
+
passed: entry.exitCode === 0,
|
|
14645
|
+
failed: entry.exitCode === 0 ? 0 : -1,
|
|
14646
|
+
passedCount: 0,
|
|
14647
|
+
detail: output.slice(0, 200).trim(),
|
|
14648
|
+
command: "bun test"
|
|
14649
|
+
};
|
|
13924
14650
|
}
|
|
14651
|
+
const names = extractFailingNames(output);
|
|
14652
|
+
return {
|
|
14653
|
+
checked: true,
|
|
14654
|
+
passed: run.failed === 0,
|
|
14655
|
+
failed: run.failed,
|
|
14656
|
+
passedCount: run.passed,
|
|
14657
|
+
detail: names.length ? names.join("; ") : run.summary || `${run.failed} failed / ${run.passed} passed`,
|
|
14658
|
+
command: "bun test"
|
|
14659
|
+
};
|
|
13925
14660
|
}
|
|
13926
|
-
var init_tracker = () => {};
|
|
13927
|
-
|
|
13928
|
-
// src/modules/execution/auditor.ts
|
|
13929
|
-
import { existsSync as existsSync25 } from "fs";
|
|
13930
|
-
import { resolve as resolve14 } from "path";
|
|
13931
14661
|
|
|
13932
14662
|
class Auditor {
|
|
13933
14663
|
baseDir;
|
|
@@ -13937,29 +14667,58 @@ class Auditor {
|
|
|
13937
14667
|
async audit(plan) {
|
|
13938
14668
|
const allStepText = plan.steps.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
13939
14669
|
const fileMatches = allStepText.match(/\b[\w./-]+\.[a-z]+/gi) || [];
|
|
13940
|
-
const uniqueFiles = [
|
|
14670
|
+
const uniqueFiles = [
|
|
14671
|
+
...new Set(fileMatches.filter((f) => !isJsMemberAccess(f)))
|
|
14672
|
+
];
|
|
13941
14673
|
const missingFiles = [];
|
|
13942
14674
|
const existingFiles = [];
|
|
13943
14675
|
for (const filePath of uniqueFiles) {
|
|
13944
14676
|
const resolved = resolve14(this.baseDir, filePath);
|
|
13945
|
-
if (
|
|
14677
|
+
if (existsSync26(resolved)) {
|
|
13946
14678
|
existingFiles.push(filePath);
|
|
13947
14679
|
} else {
|
|
13948
14680
|
missingFiles.push(filePath);
|
|
13949
14681
|
}
|
|
13950
14682
|
}
|
|
14683
|
+
let testRun = null;
|
|
14684
|
+
if (hasTestStep(plan) && findTestFile(this.baseDir)) {
|
|
14685
|
+
try {
|
|
14686
|
+
testRun = await runTests(this.baseDir);
|
|
14687
|
+
} catch {
|
|
14688
|
+
testRun = null;
|
|
14689
|
+
}
|
|
14690
|
+
}
|
|
13951
14691
|
const doneSteps = plan.steps.filter((s) => s.status === "done").length;
|
|
13952
14692
|
const totalSteps = plan.steps.length;
|
|
13953
14693
|
let massEditWarning = null;
|
|
13954
14694
|
if (uniqueFiles.length > MASS_EDIT_THRESHOLD) {
|
|
13955
|
-
massEditWarning = t("exec.mass_edit_warning", {
|
|
14695
|
+
massEditWarning = t("exec.mass_edit_warning", {
|
|
14696
|
+
count: String(uniqueFiles.length)
|
|
14697
|
+
});
|
|
13956
14698
|
}
|
|
13957
|
-
const
|
|
14699
|
+
const testsFailing = testRun !== null && testRun.failed > 0;
|
|
14700
|
+
const passed = missingFiles.length === 0 && !testsFailing;
|
|
13958
14701
|
let summary;
|
|
13959
|
-
if (
|
|
13960
|
-
summary = t("exec.
|
|
14702
|
+
if (missingFiles.length > 0) {
|
|
14703
|
+
summary = t("exec.audit_fail", {
|
|
14704
|
+
done: doneSteps,
|
|
14705
|
+
total: totalSteps,
|
|
14706
|
+
files: missingFiles.length
|
|
14707
|
+
});
|
|
14708
|
+
} else if (testsFailing) {
|
|
14709
|
+
summary = t("exec.audit_fail_tests", {
|
|
14710
|
+
done: doneSteps,
|
|
14711
|
+
total: totalSteps,
|
|
14712
|
+
failed: String(testRun.failed),
|
|
14713
|
+
passed: String(testRun.passedCount),
|
|
14714
|
+
detail: testRun.detail
|
|
14715
|
+
});
|
|
13961
14716
|
} else {
|
|
13962
|
-
summary = t("exec.
|
|
14717
|
+
summary = t("exec.audit_pass", {
|
|
14718
|
+
done: doneSteps,
|
|
14719
|
+
total: totalSteps,
|
|
14720
|
+
files: existingFiles.length
|
|
14721
|
+
});
|
|
13963
14722
|
}
|
|
13964
14723
|
return {
|
|
13965
14724
|
passed,
|
|
@@ -13967,63 +14726,183 @@ class Auditor {
|
|
|
13967
14726
|
createdFiles: existingFiles,
|
|
13968
14727
|
modifiedFiles: [],
|
|
13969
14728
|
summary,
|
|
13970
|
-
massEditWarning
|
|
14729
|
+
massEditWarning,
|
|
14730
|
+
testRun
|
|
13971
14731
|
};
|
|
13972
14732
|
}
|
|
13973
14733
|
}
|
|
13974
|
-
var MASS_EDIT_THRESHOLD = 10;
|
|
14734
|
+
var MASS_EDIT_THRESHOLD = 10, SKIP_DIRS, TEST_EXT_RE, TEST_STEP_RE;
|
|
13975
14735
|
var init_auditor = __esm(() => {
|
|
13976
14736
|
init_i18n();
|
|
14737
|
+
init_js_identifiers();
|
|
14738
|
+
init_bash();
|
|
14739
|
+
init_processes();
|
|
14740
|
+
SKIP_DIRS = new Set([
|
|
14741
|
+
"node_modules",
|
|
14742
|
+
".git",
|
|
14743
|
+
".mma",
|
|
14744
|
+
"dist",
|
|
14745
|
+
"build",
|
|
14746
|
+
"coverage",
|
|
14747
|
+
".next",
|
|
14748
|
+
".nuxt",
|
|
14749
|
+
"vendor"
|
|
14750
|
+
]);
|
|
14751
|
+
TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
|
|
14752
|
+
TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
|
|
13977
14753
|
});
|
|
13978
14754
|
|
|
13979
|
-
// src/modules/execution/plan-
|
|
13980
|
-
import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, mkdirSync as
|
|
13981
|
-
import { join as
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
const mmaDir = join20(baseDir, ".mma");
|
|
13987
|
-
if (!existsSync26(mmaDir)) {
|
|
13988
|
-
mkdirSync11(mmaDir, { recursive: true });
|
|
13989
|
-
}
|
|
13990
|
-
this.filePath = join20(mmaDir, "plan.json");
|
|
13991
|
-
}
|
|
13992
|
-
save(plan) {
|
|
13993
|
-
const file = {
|
|
13994
|
-
id: plan.id,
|
|
13995
|
-
title: plan.title,
|
|
13996
|
-
steps: plan.steps,
|
|
13997
|
-
createdAt: plan.createdAt,
|
|
13998
|
-
updatedAt: new Date().toISOString(),
|
|
13999
|
-
baseDir: plan.baseDir
|
|
14000
|
-
};
|
|
14001
|
-
writeFileSync8(this.filePath, JSON.stringify(file, null, 2), "utf-8");
|
|
14002
|
-
}
|
|
14003
|
-
load() {
|
|
14004
|
-
if (!existsSync26(this.filePath))
|
|
14755
|
+
// src/modules/execution/plan-store.ts
|
|
14756
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, mkdirSync as mkdirSync12, existsSync as existsSync27, readdirSync as readdirSync9, rmSync } from "fs";
|
|
14757
|
+
import { join as join22 } from "path";
|
|
14758
|
+
function readPlanFile(path, fallbackBaseDir) {
|
|
14759
|
+
try {
|
|
14760
|
+
const raw = readFileSync14(path, "utf-8");
|
|
14761
|
+
if (!raw.trim())
|
|
14005
14762
|
return null;
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
const file = JSON.parse(raw);
|
|
14009
|
-
return {
|
|
14010
|
-
id: file.id || "plan_legacy",
|
|
14011
|
-
title: file.title,
|
|
14012
|
-
steps: file.steps,
|
|
14013
|
-
createdAt: file.createdAt,
|
|
14014
|
-
baseDir: file.baseDir || process.cwd()
|
|
14015
|
-
};
|
|
14016
|
-
} catch {
|
|
14763
|
+
const parsed = JSON.parse(raw);
|
|
14764
|
+
if (!parsed || !Array.isArray(parsed.steps))
|
|
14017
14765
|
return null;
|
|
14018
|
-
|
|
14766
|
+
return {
|
|
14767
|
+
id: parsed.id || "plan_legacy",
|
|
14768
|
+
title: parsed.title || "Legacy plan",
|
|
14769
|
+
steps: parsed.steps,
|
|
14770
|
+
createdAt: parsed.createdAt || new Date().toISOString(),
|
|
14771
|
+
baseDir: parsed.baseDir || fallbackBaseDir,
|
|
14772
|
+
name: parsed.name
|
|
14773
|
+
};
|
|
14774
|
+
} catch {
|
|
14775
|
+
return null;
|
|
14019
14776
|
}
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14777
|
+
}
|
|
14778
|
+
function writePlanFile(path, plan) {
|
|
14779
|
+
writeFileSync8(path, JSON.stringify(plan, null, 2), "utf-8");
|
|
14780
|
+
}
|
|
14781
|
+
function listDir(dir, baseDir) {
|
|
14782
|
+
if (!existsSync27(dir))
|
|
14783
|
+
return [];
|
|
14784
|
+
const files = readdirSync9(dir).filter((f) => f.endsWith(".json"));
|
|
14785
|
+
return files.map((f) => readPlanFile(join22(dir, f), baseDir)).filter((p) => p !== null);
|
|
14786
|
+
}
|
|
14787
|
+
function toMeta(plan, status) {
|
|
14788
|
+
return {
|
|
14789
|
+
id: plan.id,
|
|
14790
|
+
name: plan.name,
|
|
14791
|
+
title: plan.title,
|
|
14792
|
+
status,
|
|
14793
|
+
createdAt: plan.createdAt,
|
|
14794
|
+
stepCount: plan.steps.length,
|
|
14795
|
+
doneCount: plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length
|
|
14796
|
+
};
|
|
14797
|
+
}
|
|
14798
|
+
|
|
14799
|
+
class PlanStore {
|
|
14800
|
+
baseDir;
|
|
14801
|
+
plansDir;
|
|
14802
|
+
draftsDir;
|
|
14803
|
+
archiveDir;
|
|
14804
|
+
legacyPath;
|
|
14805
|
+
constructor(baseDir) {
|
|
14806
|
+
const mmaDir = join22(baseDir, ".mma");
|
|
14807
|
+
if (!existsSync27(mmaDir))
|
|
14808
|
+
mkdirSync12(mmaDir, { recursive: true });
|
|
14809
|
+
this.baseDir = baseDir;
|
|
14810
|
+
this.plansDir = join22(mmaDir, "plans");
|
|
14811
|
+
this.draftsDir = join22(this.plansDir, "drafts");
|
|
14812
|
+
this.archiveDir = join22(this.plansDir, "archive");
|
|
14813
|
+
this.legacyPath = join22(mmaDir, LEGACY_FILE);
|
|
14814
|
+
for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
|
|
14815
|
+
if (!existsSync27(dir))
|
|
14816
|
+
mkdirSync12(dir, { recursive: true });
|
|
14817
|
+
}
|
|
14818
|
+
}
|
|
14819
|
+
activePath() {
|
|
14820
|
+
return join22(this.plansDir, "active.json");
|
|
14821
|
+
}
|
|
14822
|
+
saveActive(plan) {
|
|
14823
|
+
writePlanFile(this.activePath(), plan);
|
|
14824
|
+
}
|
|
14825
|
+
loadActive() {
|
|
14826
|
+
const activePath = this.activePath();
|
|
14827
|
+
if (existsSync27(activePath)) {
|
|
14828
|
+
const plan = readPlanFile(activePath, this.baseDir);
|
|
14829
|
+
if (plan)
|
|
14830
|
+
return plan;
|
|
14831
|
+
}
|
|
14832
|
+
if (existsSync27(this.legacyPath)) {
|
|
14833
|
+
const legacy = readPlanFile(this.legacyPath, this.baseDir);
|
|
14834
|
+
if (legacy) {
|
|
14835
|
+
this.saveActive(legacy);
|
|
14836
|
+
try {
|
|
14837
|
+
rmSync(this.legacyPath, { force: true });
|
|
14838
|
+
} catch {}
|
|
14839
|
+
return legacy;
|
|
14840
|
+
}
|
|
14023
14841
|
}
|
|
14842
|
+
return null;
|
|
14843
|
+
}
|
|
14844
|
+
clearActive() {
|
|
14845
|
+
const p = this.activePath();
|
|
14846
|
+
if (existsSync27(p))
|
|
14847
|
+
rmSync(p, { force: true });
|
|
14848
|
+
}
|
|
14849
|
+
saveDraft(plan) {
|
|
14850
|
+
writePlanFile(join22(this.draftsDir, `${plan.id}.json`), plan);
|
|
14851
|
+
}
|
|
14852
|
+
loadDraft(id) {
|
|
14853
|
+
const p = join22(this.draftsDir, `${id}.json`);
|
|
14854
|
+
return existsSync27(p) ? readPlanFile(p, this.baseDir) : null;
|
|
14855
|
+
}
|
|
14856
|
+
removeDraft(id) {
|
|
14857
|
+
const p = join22(this.draftsDir, `${id}.json`);
|
|
14858
|
+
if (existsSync27(p))
|
|
14859
|
+
rmSync(p, { force: true });
|
|
14860
|
+
}
|
|
14861
|
+
listDrafts() {
|
|
14862
|
+
return listDir(this.draftsDir, this.baseDir);
|
|
14863
|
+
}
|
|
14864
|
+
archivePlan(plan) {
|
|
14865
|
+
writePlanFile(join22(this.archiveDir, `${plan.id}.json`), plan);
|
|
14866
|
+
this.removeDraft(plan.id);
|
|
14867
|
+
const active = this.loadActive();
|
|
14868
|
+
if (active && active.id === plan.id) {
|
|
14869
|
+
this.clearActive();
|
|
14870
|
+
}
|
|
14871
|
+
}
|
|
14872
|
+
listArchived() {
|
|
14873
|
+
return listDir(this.archiveDir, this.baseDir);
|
|
14874
|
+
}
|
|
14875
|
+
removeArchived(id) {
|
|
14876
|
+
const p = join22(this.archiveDir, `${id}.json`);
|
|
14877
|
+
if (existsSync27(p))
|
|
14878
|
+
rmSync(p, { force: true });
|
|
14879
|
+
}
|
|
14880
|
+
listAll() {
|
|
14881
|
+
const metas = [];
|
|
14882
|
+
const active = this.loadActive();
|
|
14883
|
+
if (active)
|
|
14884
|
+
metas.push(toMeta(active, "active"));
|
|
14885
|
+
for (const p of this.listDrafts())
|
|
14886
|
+
metas.push(toMeta(p, "draft"));
|
|
14887
|
+
for (const p of this.listArchived())
|
|
14888
|
+
metas.push(toMeta(p, "archived"));
|
|
14889
|
+
return metas;
|
|
14890
|
+
}
|
|
14891
|
+
find(id) {
|
|
14892
|
+
const active = this.loadActive();
|
|
14893
|
+
if (active && active.id === id)
|
|
14894
|
+
return { plan: active, status: "active" };
|
|
14895
|
+
const draft = this.loadDraft(id);
|
|
14896
|
+
if (draft)
|
|
14897
|
+
return { plan: draft, status: "draft" };
|
|
14898
|
+
const archived = this.listArchived().find((p) => p.id === id);
|
|
14899
|
+
if (archived)
|
|
14900
|
+
return { plan: archived, status: "archived" };
|
|
14901
|
+
return null;
|
|
14024
14902
|
}
|
|
14025
14903
|
}
|
|
14026
|
-
var
|
|
14904
|
+
var LEGACY_FILE = "plan.json";
|
|
14905
|
+
var init_plan_store = () => {};
|
|
14027
14906
|
|
|
14028
14907
|
// src/modules/execution/plan-coverage.ts
|
|
14029
14908
|
function extractFilePaths(text) {
|
|
@@ -14089,7 +14968,7 @@ var init_plan_coverage = __esm(() => {
|
|
|
14089
14968
|
});
|
|
14090
14969
|
|
|
14091
14970
|
// src/modules/execution/module.ts
|
|
14092
|
-
import { existsSync as
|
|
14971
|
+
import { existsSync as existsSync28, readFileSync as readFileSync15 } from "fs";
|
|
14093
14972
|
import { resolve as resolve15 } from "path";
|
|
14094
14973
|
|
|
14095
14974
|
class ExecutionModule {
|
|
@@ -14098,26 +14977,29 @@ class ExecutionModule {
|
|
|
14098
14977
|
verifier;
|
|
14099
14978
|
stuckDetector;
|
|
14100
14979
|
auditor;
|
|
14101
|
-
|
|
14980
|
+
store;
|
|
14102
14981
|
baseDir;
|
|
14103
14982
|
lastRecoveryIteration = -STUCK_RECOVERY_COOLDOWN;
|
|
14104
14983
|
consecutivePlanWarnings = 0;
|
|
14105
14984
|
lastStepId = -1;
|
|
14985
|
+
depsGateHints = new Map;
|
|
14106
14986
|
_auditSkipsRemaining = 0;
|
|
14987
|
+
stuckNotified = false;
|
|
14988
|
+
pendingMessages = [];
|
|
14107
14989
|
constructor(baseDir, stuckThreshold = 6) {
|
|
14108
14990
|
this.baseDir = baseDir;
|
|
14109
14991
|
this.verifier = new StepVerifier(baseDir);
|
|
14110
14992
|
this.stuckDetector = new StuckDetector(stuckThreshold);
|
|
14111
14993
|
this.auditor = new Auditor(baseDir);
|
|
14112
|
-
this.
|
|
14994
|
+
this.store = new PlanStore(baseDir);
|
|
14113
14995
|
}
|
|
14114
14996
|
setPlan(plan) {
|
|
14115
14997
|
this.tracker = new PlanTracker(plan);
|
|
14116
|
-
this.
|
|
14998
|
+
this.store.saveActive(plan);
|
|
14117
14999
|
this._auditSkipsRemaining = 0;
|
|
14118
15000
|
}
|
|
14119
15001
|
restorePlan() {
|
|
14120
|
-
const plan = this.
|
|
15002
|
+
const plan = this.store.loadActive();
|
|
14121
15003
|
if (!plan)
|
|
14122
15004
|
return false;
|
|
14123
15005
|
this.tracker = new PlanTracker(plan);
|
|
@@ -14128,6 +15010,9 @@ class ExecutionModule {
|
|
|
14128
15010
|
getTracker() {
|
|
14129
15011
|
return this.tracker;
|
|
14130
15012
|
}
|
|
15013
|
+
getStore() {
|
|
15014
|
+
return this.store;
|
|
15015
|
+
}
|
|
14131
15016
|
getStuckDetector() {
|
|
14132
15017
|
return this.stuckDetector;
|
|
14133
15018
|
}
|
|
@@ -14140,7 +15025,7 @@ class ExecutionModule {
|
|
|
14140
15025
|
}
|
|
14141
15026
|
const plan = this.tracker.getPlan();
|
|
14142
15027
|
const audit = await this.auditor.audit(plan);
|
|
14143
|
-
const pendingSteps = plan.steps.
|
|
15028
|
+
const pendingSteps = plan.steps.flatMap((s) => s.status !== "done" && s.status !== "skipped" ? [`${s.id}. ${s.description}`] : []);
|
|
14144
15029
|
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
14145
15030
|
const passed = audit.passed && pendingSteps.length === 0;
|
|
14146
15031
|
return {
|
|
@@ -14166,7 +15051,16 @@ class ExecutionModule {
|
|
|
14166
15051
|
return [
|
|
14167
15052
|
{
|
|
14168
15053
|
name: "plan",
|
|
14169
|
-
description: `Create, update, show,
|
|
15054
|
+
description: `Create, update, show, abort, list, switch, or re-plan multi-step plans.
|
|
15055
|
+
|
|
15056
|
+
Actions:
|
|
15057
|
+
- create: Start a new plan. Previous active plan is auto-preserved: incomplete → draft, complete → archive.
|
|
15058
|
+
- update: Mark step status (done/failed/skipped), or rebuild plan with new steps.
|
|
15059
|
+
- show: Print current plan checklist.
|
|
15060
|
+
- abort: Archive current plan and clear active slot.
|
|
15061
|
+
- list: Show all plans (active, drafts, archived) with progress.
|
|
15062
|
+
- switch: Make a different plan active (by plan id).
|
|
15063
|
+
- re-plan: Iterative replanning: keep completed steps, replace remaining with new steps.
|
|
14170
15064
|
|
|
14171
15065
|
Write CONCRETE steps with exact file paths and commands:
|
|
14172
15066
|
- Specify WHICH files to create with exact paths (e.g. "create src/components/Header.tsx with navigation and logo")
|
|
@@ -14180,24 +15074,102 @@ Write CONCRETE steps with exact file paths and commands:
|
|
|
14180
15074
|
properties: {
|
|
14181
15075
|
action: {
|
|
14182
15076
|
type: "string",
|
|
14183
|
-
enum: [
|
|
15077
|
+
enum: [
|
|
15078
|
+
"create",
|
|
15079
|
+
"update",
|
|
15080
|
+
"show",
|
|
15081
|
+
"abort",
|
|
15082
|
+
"list",
|
|
15083
|
+
"switch",
|
|
15084
|
+
"re-plan"
|
|
15085
|
+
]
|
|
14184
15086
|
},
|
|
14185
15087
|
title: { type: "string" },
|
|
14186
15088
|
steps: { type: "array", items: { type: "string" } },
|
|
14187
15089
|
step: { type: "number" },
|
|
14188
15090
|
status: { type: "string", enum: ["done", "failed", "skipped"] },
|
|
14189
|
-
note: { type: "string" }
|
|
15091
|
+
note: { type: "string" },
|
|
15092
|
+
id: { type: "string", description: "Plan id (for switch action)" }
|
|
14190
15093
|
},
|
|
14191
15094
|
required: ["action"]
|
|
14192
15095
|
},
|
|
14193
15096
|
handler: async (_ctx, args) => {
|
|
14194
15097
|
const action = String(args.action);
|
|
15098
|
+
if (action === "list") {
|
|
15099
|
+
const metas = this.store.listAll();
|
|
15100
|
+
if (metas.length === 0) {
|
|
15101
|
+
return { success: true, output: t("plan.list_empty") };
|
|
15102
|
+
}
|
|
15103
|
+
const lines = metas.map((m) => {
|
|
15104
|
+
const icon = m.status === "active" ? "[*]" : m.status === "draft" ? "[ ]" : "[-]";
|
|
15105
|
+
const namePart = m.name ? ` (${m.name})` : "";
|
|
15106
|
+
return `${icon} ${m.id}${namePart} — ${m.title} ${m.doneCount}/${m.stepCount}`;
|
|
15107
|
+
});
|
|
15108
|
+
return {
|
|
15109
|
+
success: true,
|
|
15110
|
+
output: `${t("plan.list_header")}
|
|
15111
|
+
${lines.join(`
|
|
15112
|
+
`)}`
|
|
15113
|
+
};
|
|
15114
|
+
}
|
|
15115
|
+
if (action === "switch") {
|
|
15116
|
+
const planId = String(args.id || "");
|
|
15117
|
+
if (!planId) {
|
|
15118
|
+
return { success: false, output: t("plan.switch_no_id") };
|
|
15119
|
+
}
|
|
15120
|
+
const found = this.store.find(planId);
|
|
15121
|
+
if (!found) {
|
|
15122
|
+
return {
|
|
15123
|
+
success: false,
|
|
15124
|
+
output: t("plan.not_found", { id: planId })
|
|
15125
|
+
};
|
|
15126
|
+
}
|
|
15127
|
+
this.preserveActive();
|
|
15128
|
+
if (found.status === "draft") {
|
|
15129
|
+
this.store.removeDraft(found.plan.id);
|
|
15130
|
+
} else if (found.status === "archived") {
|
|
15131
|
+
this.store.removeArchived(found.plan.id);
|
|
15132
|
+
}
|
|
15133
|
+
this.setPlan(found.plan);
|
|
15134
|
+
const display = PlanCreator.toPromptBlock(found.plan, 0);
|
|
15135
|
+
return {
|
|
15136
|
+
success: true,
|
|
15137
|
+
output: t("plan.switched", {
|
|
15138
|
+
id: found.plan.id,
|
|
15139
|
+
title: found.plan.title
|
|
15140
|
+
}),
|
|
15141
|
+
display
|
|
15142
|
+
};
|
|
15143
|
+
}
|
|
15144
|
+
if (action === "re-plan") {
|
|
15145
|
+
if (!this.tracker) {
|
|
15146
|
+
return { success: false, output: t("plan.no_active") };
|
|
15147
|
+
}
|
|
15148
|
+
const newSteps = Array.isArray(args.steps) ? args.steps.map(String) : [];
|
|
15149
|
+
if (newSteps.length === 0) {
|
|
15150
|
+
return { success: false, output: t("plan.replan_no_steps") };
|
|
15151
|
+
}
|
|
15152
|
+
const oldPlan = this.tracker.getPlan();
|
|
15153
|
+
const replanned = PlanCreator.replan(oldPlan, newSteps, args.title ? String(args.title) : undefined);
|
|
15154
|
+
const keptCount = replanned.steps.length - newSteps.length;
|
|
15155
|
+
this.setPlan(replanned);
|
|
15156
|
+
const display = PlanCreator.toPromptBlock(replanned, keptCount);
|
|
15157
|
+
return {
|
|
15158
|
+
success: true,
|
|
15159
|
+
output: t("plan.replanned", {
|
|
15160
|
+
kept: String(keptCount),
|
|
15161
|
+
steps: String(newSteps.length)
|
|
15162
|
+
}),
|
|
15163
|
+
display
|
|
15164
|
+
};
|
|
15165
|
+
}
|
|
14195
15166
|
if (action === "create") {
|
|
14196
15167
|
const title = String(args.title || "Task Plan");
|
|
14197
15168
|
const steps = Array.isArray(args.steps) ? args.steps.map(String) : [];
|
|
14198
15169
|
if (steps.length === 0) {
|
|
14199
15170
|
return { success: false, output: t("plan.no_steps") };
|
|
14200
15171
|
}
|
|
15172
|
+
this.preserveActive();
|
|
14201
15173
|
const plan = PlanCreator.createPlan(title, steps, this.baseDir);
|
|
14202
15174
|
this.setPlan(plan);
|
|
14203
15175
|
const display = PlanCreator.toPromptBlock(plan, 0);
|
|
@@ -14242,7 +15214,7 @@ ${display}`,
|
|
|
14242
15214
|
if (args.note)
|
|
14243
15215
|
this.tracker.addNote(Number(args.step), String(args.note));
|
|
14244
15216
|
this.tracker.syncCurrentStep();
|
|
14245
|
-
this.
|
|
15217
|
+
this.store.saveActive(this.tracker.getPlan());
|
|
14246
15218
|
const progress = this.tracker.getProgressString();
|
|
14247
15219
|
const display = this.tracker.toPromptBlock();
|
|
14248
15220
|
_ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
|
|
@@ -14270,8 +15242,11 @@ ${progress}`,
|
|
|
14270
15242
|
};
|
|
14271
15243
|
}
|
|
14272
15244
|
if (action === "abort") {
|
|
15245
|
+
if (this.tracker) {
|
|
15246
|
+
this.store.archivePlan(this.tracker.getPlan());
|
|
15247
|
+
}
|
|
14273
15248
|
this.tracker = null;
|
|
14274
|
-
this.
|
|
15249
|
+
this.store.clearActive();
|
|
14275
15250
|
return { success: true, output: t("plan.aborted") };
|
|
14276
15251
|
}
|
|
14277
15252
|
if (!this.tracker) {
|
|
@@ -14320,7 +15295,7 @@ ${progress}`,
|
|
|
14320
15295
|
}
|
|
14321
15296
|
this.tracker.updateStepStatus(currentStep.id, "done");
|
|
14322
15297
|
this.tracker.syncCurrentStep();
|
|
14323
|
-
this.
|
|
15298
|
+
this.store.saveActive(this.tracker.getPlan());
|
|
14324
15299
|
const display = this.tracker.toPromptBlock();
|
|
14325
15300
|
return {
|
|
14326
15301
|
success: true,
|
|
@@ -14375,36 +15350,55 @@ Sub-tasks: ${note}`
|
|
|
14375
15350
|
return {
|
|
14376
15351
|
name: "execution",
|
|
14377
15352
|
onBeforeThink: (ctx) => {
|
|
14378
|
-
|
|
14379
|
-
|
|
14380
|
-
|
|
14381
|
-
this.consecutivePlanWarnings = 0;
|
|
14382
|
-
this.lastStepId = step.id;
|
|
15353
|
+
if (ctx.contextManager && this.pendingMessages.length > 0) {
|
|
15354
|
+
for (const m of this.pendingMessages.splice(0)) {
|
|
15355
|
+
ctx.contextManager.addMessage(m);
|
|
14383
15356
|
}
|
|
14384
|
-
|
|
14385
|
-
|
|
14386
|
-
} else {
|
|
15357
|
+
}
|
|
15358
|
+
if (this.tracker?.isComplete()) {
|
|
14387
15359
|
this.stuckDetector.reset();
|
|
14388
15360
|
this.consecutivePlanWarnings = 0;
|
|
14389
15361
|
this.lastStepId = -1;
|
|
14390
|
-
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14394
|
-
|
|
14395
|
-
|
|
14396
|
-
|
|
14397
|
-
|
|
14398
|
-
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
15362
|
+
this.stuckNotified = false;
|
|
15363
|
+
} else {
|
|
15364
|
+
const step = this.tracker?.getCurrentStep();
|
|
15365
|
+
if (this.tracker && step) {
|
|
15366
|
+
if (step.id !== this.lastStepId) {
|
|
15367
|
+
this.consecutivePlanWarnings = 0;
|
|
15368
|
+
this.lastStepId = step.id;
|
|
15369
|
+
this.stuckNotified = false;
|
|
15370
|
+
}
|
|
15371
|
+
this.stuckDetector.setCurrentStep(step.id, step.description);
|
|
15372
|
+
this.stuckDetector.recordIteration(step.id);
|
|
15373
|
+
} else {
|
|
15374
|
+
this.stuckDetector.reset();
|
|
15375
|
+
this.consecutivePlanWarnings = 0;
|
|
15376
|
+
this.lastStepId = -1;
|
|
15377
|
+
this.stuckNotified = false;
|
|
15378
|
+
const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
15379
|
+
if (iter === 3 && !this.tracker && ctx.contextManager) {
|
|
15380
|
+
ctx.contextManager.addMessage({
|
|
15381
|
+
role: "user",
|
|
15382
|
+
content: `<system-summary>You have made 3 tool calls without creating a plan. For any task that involves creating files, installing packages, or multiple steps — you MUST use plan create BEFORE continuing. Use the plan tool now with concrete steps (exact filenames, commands, deliverables). Do NOT make any more write/edit/bash calls until you have a plan.</system-summary>`
|
|
15383
|
+
});
|
|
15384
|
+
}
|
|
15385
|
+
if (iter >= 6 && !this.tracker && ctx.contextManager) {
|
|
15386
|
+
ctx.contextManager.addMessage({
|
|
15387
|
+
role: "user",
|
|
15388
|
+
content: `<system-summary>STOP. ${iter} iterations without a plan. You MUST call plan create RIGHT NOW. No more tool calls until you create a plan.</system-summary>`
|
|
15389
|
+
});
|
|
15390
|
+
}
|
|
14402
15391
|
}
|
|
14403
15392
|
}
|
|
14404
15393
|
const stuckReason = this.stuckDetector.getStuckReason();
|
|
14405
15394
|
if (stuckReason) {
|
|
14406
|
-
|
|
14407
|
-
|
|
15395
|
+
const logIt = this.stuckDetector.isStuck() ? !this.stuckNotified : true;
|
|
15396
|
+
if (logIt) {
|
|
15397
|
+
ctx.logger?.warn(stuckReason);
|
|
15398
|
+
ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
|
|
15399
|
+
if (this.stuckDetector.isStuck())
|
|
15400
|
+
this.stuckNotified = true;
|
|
15401
|
+
}
|
|
14408
15402
|
}
|
|
14409
15403
|
if (this.stuckDetector.isStuck() || this.stuckDetector.hasRepetitiveToolCalls()) {
|
|
14410
15404
|
const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
@@ -14447,29 +15441,32 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14447
15441
|
ctx.onMeta(escalation);
|
|
14448
15442
|
}
|
|
14449
15443
|
if (this.stuckDetector.getIterationsOnCurrentStep() >= FORCE_SKIP_THRESHOLD && ctx.contextManager) {
|
|
14450
|
-
const
|
|
15444
|
+
const step = this.tracker?.getCurrentStep();
|
|
14451
15445
|
ctx.contextManager.addMessage({
|
|
14452
15446
|
role: "user",
|
|
14453
|
-
content: `<system-summary>STOP. Step ${
|
|
15447
|
+
content: `<system-summary>STOP. Step ${step?.id ?? "?"} ("${step?.description ?? ""}") took ${this.stuckDetector.getIterationsOnCurrentStep()} iterations with no progress. DO NOT continue this step. Immediately call: plan update step=${step?.id ?? "?"} status=done (if code works despite warnings) OR plan update step=${step?.id ?? "?"} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.</system-summary>`
|
|
14454
15448
|
});
|
|
14455
15449
|
}
|
|
14456
15450
|
}
|
|
14457
15451
|
}
|
|
14458
15452
|
},
|
|
14459
|
-
onBeforeTool: (
|
|
15453
|
+
onBeforeTool: (_ctx, call) => {
|
|
14460
15454
|
const warning = this.checkPlanAlignment(call);
|
|
14461
|
-
if (warning
|
|
14462
|
-
|
|
15455
|
+
if (warning) {
|
|
15456
|
+
this.pendingMessages.push({
|
|
14463
15457
|
role: "user",
|
|
14464
15458
|
content: `<system-summary>${warning}</system-summary>`
|
|
14465
15459
|
});
|
|
14466
15460
|
this.consecutivePlanWarnings++;
|
|
14467
15461
|
if (this.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
|
|
14468
|
-
|
|
15462
|
+
this.pendingMessages.push({
|
|
14469
15463
|
role: "user",
|
|
14470
15464
|
content: `<system-summary>${t("exec.plan_blocked", { step: String(this.tracker?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`
|
|
14471
15465
|
});
|
|
14472
|
-
return
|
|
15466
|
+
return t("exec.plan_blocked", {
|
|
15467
|
+
step: String(this.tracker?.getCurrentStep()?.id ?? "?"),
|
|
15468
|
+
max: MAX_PLAN_WARNINGS_BEFORE_BLOCK
|
|
15469
|
+
});
|
|
14473
15470
|
}
|
|
14474
15471
|
} else {
|
|
14475
15472
|
this.consecutivePlanWarnings = 0;
|
|
@@ -14484,16 +15481,23 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14484
15481
|
}
|
|
14485
15482
|
},
|
|
14486
15483
|
onAfterTool: (ctx, call, result) => {
|
|
15484
|
+
if (call.name === "bash") {
|
|
15485
|
+
this.stuckDetector.recordBashOutput(String(call.arguments?.command ?? ""), String(result.output ?? ""));
|
|
15486
|
+
}
|
|
15487
|
+
const toolText = String(result.output ?? "");
|
|
15488
|
+
if (/error TS\d+|\[Project typecheck failed\]|\[Syntax check failed\]/.test(toolText)) {
|
|
15489
|
+
this.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
|
|
15490
|
+
}
|
|
14487
15491
|
if (!result.success) {
|
|
14488
15492
|
this.stuckDetector.recordToolError(call.name, result.output);
|
|
14489
15493
|
const actionableHints = this.stuckDetector.getActionableHints();
|
|
14490
15494
|
const alternative = this.stuckDetector.getToolAlternative();
|
|
14491
|
-
if (
|
|
15495
|
+
if (actionableHints.length > 0 || alternative) {
|
|
14492
15496
|
const parts = [...actionableHints];
|
|
14493
15497
|
if (alternative) {
|
|
14494
15498
|
parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
|
|
14495
15499
|
}
|
|
14496
|
-
|
|
15500
|
+
this.pendingMessages.push({
|
|
14497
15501
|
role: "user",
|
|
14498
15502
|
content: `<system-summary>${t("exec.hints", { hints: parts.map((h) => `- ${h}`).join(`
|
|
14499
15503
|
`) })}</system-summary>`
|
|
@@ -14501,10 +15505,21 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14501
15505
|
}
|
|
14502
15506
|
} else {
|
|
14503
15507
|
this.stuckDetector.recordToolSuccess();
|
|
14504
|
-
if (call.name === "bash" && result.success
|
|
15508
|
+
if (call.name === "bash" && result.success) {
|
|
14505
15509
|
const cmd = String(call.arguments?.command ?? "");
|
|
14506
|
-
|
|
14507
|
-
|
|
15510
|
+
const testRun = detectTestResults(String(result.output ?? ""));
|
|
15511
|
+
if (testRun && testRun.failed > 0) {
|
|
15512
|
+
this.pendingMessages.push({
|
|
15513
|
+
role: "user",
|
|
15514
|
+
content: `<system-summary>${testRun.framework} reported ${testRun.failed} FAILING test(s) (${testRun.passed} passing). Do NOT mark the current step as done — fix the failing tests (read the failure output, correct the code) and re-run them until all pass.</system-summary>`
|
|
15515
|
+
});
|
|
15516
|
+
} else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
|
|
15517
|
+
this.pendingMessages.push({
|
|
15518
|
+
role: "user",
|
|
15519
|
+
content: `<system-summary>${testRun.framework}: all ${testRun.passed} test(s) passed for "${cmd}". You may mark the current step as done via plan update step=N status=done.</system-summary>`
|
|
15520
|
+
});
|
|
15521
|
+
} else if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
|
|
15522
|
+
this.pendingMessages.push({
|
|
14508
15523
|
role: "user",
|
|
14509
15524
|
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>`
|
|
14510
15525
|
});
|
|
@@ -14531,6 +15546,16 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14531
15546
|
}
|
|
14532
15547
|
};
|
|
14533
15548
|
}
|
|
15549
|
+
preserveActive() {
|
|
15550
|
+
if (!this.tracker)
|
|
15551
|
+
return;
|
|
15552
|
+
const plan = this.tracker.getPlan();
|
|
15553
|
+
if (plan.steps.every((s) => s.status === "done" || s.status === "skipped")) {
|
|
15554
|
+
this.store.archivePlan(plan);
|
|
15555
|
+
} else {
|
|
15556
|
+
this.store.saveDraft(plan);
|
|
15557
|
+
}
|
|
15558
|
+
}
|
|
14534
15559
|
checkPlanAlignment(call) {
|
|
14535
15560
|
if (!this.tracker)
|
|
14536
15561
|
return null;
|
|
@@ -14557,7 +15582,20 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14557
15582
|
const callPaths = (argStr.match(/\b[\w./\\-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
|
|
14558
15583
|
if (callPaths.length === 0)
|
|
14559
15584
|
return null;
|
|
14560
|
-
const
|
|
15585
|
+
const plan = this.tracker.getPlan();
|
|
15586
|
+
const finishedPaths = new Set;
|
|
15587
|
+
for (const s of plan.steps) {
|
|
15588
|
+
if (s.status === "done" || s.status === "skipped") {
|
|
15589
|
+
const ps = s.description.match(/\b[\w./\\-]+\.[a-z]+/gi)?.map((p) => p.toLowerCase()) ?? [];
|
|
15590
|
+
ps.forEach((p) => finishedPaths.add(p));
|
|
15591
|
+
}
|
|
15592
|
+
}
|
|
15593
|
+
const offPath = callPaths.some((p) => {
|
|
15594
|
+
if (finishedPaths.has(p) || [...finishedPaths].some((s) => s.includes(p))) {
|
|
15595
|
+
return false;
|
|
15596
|
+
}
|
|
15597
|
+
return !stepPaths.some((s) => p.includes(s) || s.includes(p));
|
|
15598
|
+
});
|
|
14561
15599
|
if (!offPath)
|
|
14562
15600
|
return null;
|
|
14563
15601
|
return t("exec.plan_warning", {
|
|
@@ -14586,12 +15624,19 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14586
15624
|
"poetry.lock",
|
|
14587
15625
|
"requirements.txt"
|
|
14588
15626
|
];
|
|
14589
|
-
const hasLockFile = lockFiles.some((f) =>
|
|
15627
|
+
const hasLockFile = lockFiles.some((f) => existsSync28(resolve15(this.baseDir, f)));
|
|
14590
15628
|
if (!hasLockFile) {
|
|
14591
15629
|
if (contextManager) {
|
|
15630
|
+
const hints = this.depsGateHints.get(step.id) || 0;
|
|
15631
|
+
this.depsGateHints.set(step.id, hints + 1);
|
|
15632
|
+
const force = hints >= 1;
|
|
15633
|
+
const content = force ? t("exec.step_gate_deps_force", { step: String(step.id) }) : t("exec.step_gate_deps", {
|
|
15634
|
+
step: String(step.id),
|
|
15635
|
+
description: step.description
|
|
15636
|
+
});
|
|
14592
15637
|
contextManager.addMessage({
|
|
14593
15638
|
role: "user",
|
|
14594
|
-
content: `<system-summary>${
|
|
15639
|
+
content: `<system-summary>${content}</system-summary>`
|
|
14595
15640
|
});
|
|
14596
15641
|
}
|
|
14597
15642
|
return;
|
|
@@ -14599,7 +15644,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14599
15644
|
}
|
|
14600
15645
|
if (stepPaths.length === 0)
|
|
14601
15646
|
return;
|
|
14602
|
-
const allExist = stepPaths.every((p) =>
|
|
15647
|
+
const allExist = stepPaths.every((p) => existsSync28(resolve15(this.baseDir, p)));
|
|
14603
15648
|
if (!allExist)
|
|
14604
15649
|
return;
|
|
14605
15650
|
const emptyFiles = [];
|
|
@@ -14621,7 +15666,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
14621
15666
|
this.tracker?.updateStepStatus(step.id, "done");
|
|
14622
15667
|
const hadNext = this.tracker?.advance() ?? false;
|
|
14623
15668
|
if (this.tracker) {
|
|
14624
|
-
this.
|
|
15669
|
+
this.store.saveActive(this.tracker.getPlan());
|
|
14625
15670
|
sessionLog?.plan("auto-advance", `Step ${step.id} auto-completed | ${this.tracker.getProgressString()} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
|
|
14626
15671
|
}
|
|
14627
15672
|
if (contextManager) {
|
|
@@ -14655,13 +15700,20 @@ var init_module = __esm(() => {
|
|
|
14655
15700
|
init_verifier();
|
|
14656
15701
|
init_stuck_detector();
|
|
14657
15702
|
init_auditor();
|
|
14658
|
-
|
|
15703
|
+
init_plan_store();
|
|
14659
15704
|
init_plan_coverage();
|
|
15705
|
+
init_bash();
|
|
14660
15706
|
});
|
|
14661
15707
|
|
|
14662
15708
|
// src/modules/security/session-encryption.ts
|
|
14663
|
-
import {
|
|
14664
|
-
|
|
15709
|
+
import {
|
|
15710
|
+
readFileSync as readFileSync16,
|
|
15711
|
+
writeFileSync as writeFileSync9,
|
|
15712
|
+
existsSync as existsSync29,
|
|
15713
|
+
readdirSync as readdirSync10,
|
|
15714
|
+
unlinkSync as unlinkSync4
|
|
15715
|
+
} from "fs";
|
|
15716
|
+
import { join as join23 } from "path";
|
|
14665
15717
|
import { homedir as homedir8 } from "os";
|
|
14666
15718
|
|
|
14667
15719
|
class SessionFileEncryptor {
|
|
@@ -14670,7 +15722,7 @@ class SessionFileEncryptor {
|
|
|
14670
15722
|
constructor(config) {
|
|
14671
15723
|
this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
|
|
14672
15724
|
this.encryptor = new ConfigEncryptor({
|
|
14673
|
-
keyPath: config?.keyPath ||
|
|
15725
|
+
keyPath: config?.keyPath || join23(homedir8(), ".mma", ".session-encryption-key")
|
|
14674
15726
|
});
|
|
14675
15727
|
}
|
|
14676
15728
|
isEnabled() {
|
|
@@ -14694,10 +15746,15 @@ class SessionFileEncryptor {
|
|
|
14694
15746
|
return this.encryptFileContent(JSON.stringify(obj));
|
|
14695
15747
|
}
|
|
14696
15748
|
decryptJSON(content) {
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
15749
|
+
let plain = content;
|
|
15750
|
+
if (this.config.enabled) {
|
|
15751
|
+
plain = this.decryptFileContent(content);
|
|
15752
|
+
}
|
|
15753
|
+
try {
|
|
15754
|
+
return JSON.parse(plain);
|
|
15755
|
+
} catch {
|
|
15756
|
+
throw new Error("Invalid JSON content in session file — file may be corrupted or encrypted with a different key");
|
|
15757
|
+
}
|
|
14701
15758
|
}
|
|
14702
15759
|
encryptJSONL(lines) {
|
|
14703
15760
|
if (!this.config.enabled)
|
|
@@ -14730,25 +15787,34 @@ class SessionFileEncryptor {
|
|
|
14730
15787
|
const lines = content.split(`
|
|
14731
15788
|
`).filter((line) => line.trim());
|
|
14732
15789
|
const decryptedLines = this.decryptJSONL(lines);
|
|
14733
|
-
return decryptedLines.
|
|
15790
|
+
return decryptedLines.flatMap((line) => {
|
|
15791
|
+
try {
|
|
15792
|
+
return [JSON.parse(line)];
|
|
15793
|
+
} catch {
|
|
15794
|
+
return [];
|
|
15795
|
+
}
|
|
15796
|
+
});
|
|
14734
15797
|
}
|
|
14735
15798
|
appendToSessionJSONL(filePath, obj) {
|
|
14736
15799
|
const encryptedLine = this.encryptFileContent(JSON.stringify(obj));
|
|
14737
15800
|
writeFileSync9(filePath, encryptedLine + `
|
|
14738
|
-
`, {
|
|
15801
|
+
`, {
|
|
15802
|
+
flag: "a",
|
|
15803
|
+
encoding: "utf8"
|
|
15804
|
+
});
|
|
14739
15805
|
}
|
|
14740
15806
|
encryptSessionDirectory(sessionDir) {
|
|
14741
15807
|
if (!this.config.enabled)
|
|
14742
15808
|
return;
|
|
14743
|
-
const files =
|
|
15809
|
+
const files = readdirSync10(sessionDir);
|
|
14744
15810
|
for (const file of files) {
|
|
14745
|
-
const filePath =
|
|
14746
|
-
if (
|
|
15811
|
+
const filePath = join23(sessionDir, file);
|
|
15812
|
+
if (existsSync29(filePath) && !file.endsWith(".enc")) {
|
|
14747
15813
|
try {
|
|
14748
15814
|
const content = readFileSync16(filePath, "utf8");
|
|
14749
15815
|
const encrypted = this.encryptFileContent(content);
|
|
14750
15816
|
writeFileSync9(filePath + ".enc", encrypted, "utf8");
|
|
14751
|
-
|
|
15817
|
+
unlinkSync4(filePath);
|
|
14752
15818
|
} catch {}
|
|
14753
15819
|
}
|
|
14754
15820
|
}
|
|
@@ -14756,16 +15822,16 @@ class SessionFileEncryptor {
|
|
|
14756
15822
|
decryptSessionDirectory(sessionDir) {
|
|
14757
15823
|
if (!this.config.enabled)
|
|
14758
15824
|
return;
|
|
14759
|
-
const files =
|
|
15825
|
+
const files = readdirSync10(sessionDir);
|
|
14760
15826
|
for (const file of files) {
|
|
14761
15827
|
if (file.endsWith(".enc")) {
|
|
14762
|
-
const encFilePath =
|
|
15828
|
+
const encFilePath = join23(sessionDir, file);
|
|
14763
15829
|
const decFilePath = encFilePath.slice(0, -4);
|
|
14764
15830
|
try {
|
|
14765
15831
|
const content = readFileSync16(encFilePath, "utf8");
|
|
14766
15832
|
const decrypted = this.decryptFileContent(content);
|
|
14767
15833
|
writeFileSync9(decFilePath, decrypted, "utf8");
|
|
14768
|
-
|
|
15834
|
+
unlinkSync4(encFilePath);
|
|
14769
15835
|
} catch {}
|
|
14770
15836
|
}
|
|
14771
15837
|
}
|
|
@@ -14784,15 +15850,15 @@ var init_session_encryption = __esm(() => {
|
|
|
14784
15850
|
|
|
14785
15851
|
// src/modules/session/store.ts
|
|
14786
15852
|
import {
|
|
14787
|
-
existsSync as
|
|
14788
|
-
mkdirSync as
|
|
14789
|
-
readdirSync as
|
|
15853
|
+
existsSync as existsSync30,
|
|
15854
|
+
mkdirSync as mkdirSync13,
|
|
15855
|
+
readdirSync as readdirSync11,
|
|
14790
15856
|
readFileSync as readFileSync17,
|
|
14791
|
-
rmSync,
|
|
15857
|
+
rmSync as rmSync2,
|
|
14792
15858
|
writeFileSync as writeFileSync10,
|
|
14793
|
-
appendFileSync as
|
|
15859
|
+
appendFileSync as appendFileSync6
|
|
14794
15860
|
} from "fs";
|
|
14795
|
-
import { join as
|
|
15861
|
+
import { join as join24 } from "path";
|
|
14796
15862
|
import { gzipSync } from "zlib";
|
|
14797
15863
|
|
|
14798
15864
|
class SessionStore {
|
|
@@ -14806,7 +15872,7 @@ class SessionStore {
|
|
|
14806
15872
|
}
|
|
14807
15873
|
}
|
|
14808
15874
|
getSessionDir(id) {
|
|
14809
|
-
return
|
|
15875
|
+
return join24(this.baseDir, id);
|
|
14810
15876
|
}
|
|
14811
15877
|
updateEncryption(config) {
|
|
14812
15878
|
if (config?.enabled) {
|
|
@@ -14819,27 +15885,27 @@ class SessionStore {
|
|
|
14819
15885
|
return this.encryptor?.isEnabled() ?? false;
|
|
14820
15886
|
}
|
|
14821
15887
|
init() {
|
|
14822
|
-
|
|
15888
|
+
mkdirSync13(this.baseDir, { recursive: true });
|
|
14823
15889
|
}
|
|
14824
15890
|
sessionDir(id) {
|
|
14825
|
-
return
|
|
15891
|
+
return join24(this.baseDir, id);
|
|
14826
15892
|
}
|
|
14827
15893
|
metaPath(id) {
|
|
14828
|
-
return
|
|
15894
|
+
return join24(this.sessionDir(id), "meta.json");
|
|
14829
15895
|
}
|
|
14830
15896
|
historyPath(id) {
|
|
14831
|
-
return
|
|
15897
|
+
return join24(this.sessionDir(id), "history.jsonl");
|
|
14832
15898
|
}
|
|
14833
15899
|
sessionLogPath(id) {
|
|
14834
|
-
return
|
|
15900
|
+
return join24(this.sessionDir(id), "session.jsonl");
|
|
14835
15901
|
}
|
|
14836
15902
|
sessionExists(id) {
|
|
14837
|
-
return
|
|
15903
|
+
return existsSync30(this.metaPath(id));
|
|
14838
15904
|
}
|
|
14839
15905
|
saveMeta(id, meta) {
|
|
14840
15906
|
this._metaCache.set(id, meta);
|
|
14841
15907
|
const dir = this.sessionDir(id);
|
|
14842
|
-
|
|
15908
|
+
mkdirSync13(dir, { recursive: true });
|
|
14843
15909
|
const content = JSON.stringify(meta, null, 2);
|
|
14844
15910
|
if (this.encryptor) {
|
|
14845
15911
|
writeFileSync10(this.metaPath(id), this.encryptor.encryptFileContent(content), "utf-8");
|
|
@@ -14852,7 +15918,7 @@ class SessionStore {
|
|
|
14852
15918
|
if (cached)
|
|
14853
15919
|
return cached;
|
|
14854
15920
|
const path = this.metaPath(id);
|
|
14855
|
-
if (!
|
|
15921
|
+
if (!existsSync30(path))
|
|
14856
15922
|
return null;
|
|
14857
15923
|
try {
|
|
14858
15924
|
const raw = readFileSync17(path, "utf-8");
|
|
@@ -14866,13 +15932,13 @@ class SessionStore {
|
|
|
14866
15932
|
}
|
|
14867
15933
|
appendMessage(id, msg) {
|
|
14868
15934
|
const dir = this.sessionDir(id);
|
|
14869
|
-
|
|
15935
|
+
mkdirSync13(dir, { recursive: true });
|
|
14870
15936
|
const line = JSON.stringify(msg);
|
|
14871
15937
|
if (this.encryptor?.isEnabled()) {
|
|
14872
|
-
|
|
15938
|
+
appendFileSync6(this.historyPath(id), this.encryptor.encryptFileContent(line) + `
|
|
14873
15939
|
`, "utf-8");
|
|
14874
15940
|
} else {
|
|
14875
|
-
|
|
15941
|
+
appendFileSync6(this.historyPath(id), line + `
|
|
14876
15942
|
`, "utf-8");
|
|
14877
15943
|
}
|
|
14878
15944
|
const meta = this.loadMeta(id);
|
|
@@ -14884,7 +15950,7 @@ class SessionStore {
|
|
|
14884
15950
|
}
|
|
14885
15951
|
loadHistory(id) {
|
|
14886
15952
|
const path = this.historyPath(id);
|
|
14887
|
-
if (!
|
|
15953
|
+
if (!existsSync30(path))
|
|
14888
15954
|
return [];
|
|
14889
15955
|
try {
|
|
14890
15956
|
const raw = readFileSync17(path, "utf-8");
|
|
@@ -14913,19 +15979,19 @@ class SessionStore {
|
|
|
14913
15979
|
}
|
|
14914
15980
|
appendSessionLog(id, entry) {
|
|
14915
15981
|
const dir = this.sessionDir(id);
|
|
14916
|
-
|
|
15982
|
+
mkdirSync13(dir, { recursive: true });
|
|
14917
15983
|
const line = JSON.stringify(entry);
|
|
14918
15984
|
if (this.encryptor?.isEnabled()) {
|
|
14919
|
-
|
|
15985
|
+
appendFileSync6(this.sessionLogPath(id), this.encryptor.encryptFileContent(line) + `
|
|
14920
15986
|
`, "utf-8");
|
|
14921
15987
|
} else {
|
|
14922
|
-
|
|
15988
|
+
appendFileSync6(this.sessionLogPath(id), line + `
|
|
14923
15989
|
`, "utf-8");
|
|
14924
15990
|
}
|
|
14925
15991
|
}
|
|
14926
15992
|
loadSessionLog(id) {
|
|
14927
15993
|
const path = this.sessionLogPath(id);
|
|
14928
|
-
if (!
|
|
15994
|
+
if (!existsSync30(path))
|
|
14929
15995
|
return [];
|
|
14930
15996
|
try {
|
|
14931
15997
|
const raw = readFileSync17(path, "utf-8");
|
|
@@ -14953,9 +16019,9 @@ class SessionStore {
|
|
|
14953
16019
|
}
|
|
14954
16020
|
}
|
|
14955
16021
|
listSessions() {
|
|
14956
|
-
if (!
|
|
16022
|
+
if (!existsSync30(this.baseDir))
|
|
14957
16023
|
return [];
|
|
14958
|
-
const entries =
|
|
16024
|
+
const entries = readdirSync11(this.baseDir, { withFileTypes: true });
|
|
14959
16025
|
const sessions = [];
|
|
14960
16026
|
for (const entry of entries) {
|
|
14961
16027
|
if (entry.isDirectory()) {
|
|
@@ -14970,8 +16036,8 @@ class SessionStore {
|
|
|
14970
16036
|
deleteSession(id) {
|
|
14971
16037
|
this._metaCache.delete(id);
|
|
14972
16038
|
const dir = this.sessionDir(id);
|
|
14973
|
-
if (
|
|
14974
|
-
|
|
16039
|
+
if (existsSync30(dir)) {
|
|
16040
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
14975
16041
|
}
|
|
14976
16042
|
}
|
|
14977
16043
|
rotateOldSessions() {
|
|
@@ -14982,12 +16048,12 @@ class SessionStore {
|
|
|
14982
16048
|
const updatedAt = new Date(session2.updatedAt);
|
|
14983
16049
|
if (updatedAt < thirtyDaysAgo) {
|
|
14984
16050
|
const historyPath = this.historyPath(session2.id);
|
|
14985
|
-
if (
|
|
16051
|
+
if (existsSync30(historyPath)) {
|
|
14986
16052
|
const content = readFileSync17(historyPath, "utf-8");
|
|
14987
16053
|
const compressed = gzipSync(content);
|
|
14988
|
-
const gzPath =
|
|
16054
|
+
const gzPath = join24(this.baseDir, `${session2.id}.jsonl.gz`);
|
|
14989
16055
|
writeFileSync10(gzPath, compressed);
|
|
14990
|
-
|
|
16056
|
+
rmSync2(historyPath);
|
|
14991
16057
|
}
|
|
14992
16058
|
}
|
|
14993
16059
|
}
|
|
@@ -15195,8 +16261,8 @@ class ProfileCompressor {
|
|
|
15195
16261
|
}
|
|
15196
16262
|
|
|
15197
16263
|
// src/modules/user-profile/profile.ts
|
|
15198
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, existsSync as
|
|
15199
|
-
import { join as
|
|
16264
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, existsSync as existsSync31, mkdirSync as mkdirSync14 } from "fs";
|
|
16265
|
+
import { join as join25 } from "path";
|
|
15200
16266
|
import { homedir as homedir9, hostname, platform as platform4, type } from "os";
|
|
15201
16267
|
import { env } from "process";
|
|
15202
16268
|
|
|
@@ -15220,14 +16286,14 @@ class UserProfile {
|
|
|
15220
16286
|
return this.info;
|
|
15221
16287
|
}
|
|
15222
16288
|
save() {
|
|
15223
|
-
if (!
|
|
15224
|
-
|
|
16289
|
+
if (!existsSync31(this.profileDir)) {
|
|
16290
|
+
mkdirSync14(this.profileDir, { recursive: true });
|
|
15225
16291
|
}
|
|
15226
|
-
writeFileSync11(
|
|
16292
|
+
writeFileSync11(join25(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
|
|
15227
16293
|
}
|
|
15228
16294
|
load() {
|
|
15229
|
-
const path =
|
|
15230
|
-
if (!
|
|
16295
|
+
const path = join25(this.profileDir, "profile.json");
|
|
16296
|
+
if (!existsSync31(path))
|
|
15231
16297
|
return null;
|
|
15232
16298
|
try {
|
|
15233
16299
|
const data = JSON.parse(readFileSync18(path, "utf-8"));
|
|
@@ -15265,22 +16331,22 @@ class UserProfile {
|
|
|
15265
16331
|
var init_profile = () => {};
|
|
15266
16332
|
|
|
15267
16333
|
// src/modules/skills/loader.ts
|
|
15268
|
-
import { readdirSync as
|
|
15269
|
-
import { join as
|
|
16334
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync19, existsSync as existsSync32, statSync as statSync6 } from "fs";
|
|
16335
|
+
import { join as join26 } from "path";
|
|
15270
16336
|
|
|
15271
16337
|
class SkillsLoader {
|
|
15272
16338
|
loadFromDir(dirPath) {
|
|
15273
|
-
if (!
|
|
16339
|
+
if (!existsSync32(dirPath))
|
|
15274
16340
|
return [];
|
|
15275
16341
|
const skills = [];
|
|
15276
16342
|
this.scanDir(dirPath, skills);
|
|
15277
16343
|
return skills;
|
|
15278
16344
|
}
|
|
15279
16345
|
scanDir(dirPath, skills) {
|
|
15280
|
-
const entries =
|
|
16346
|
+
const entries = readdirSync12(dirPath);
|
|
15281
16347
|
for (const entry of entries) {
|
|
15282
|
-
const fullPath =
|
|
15283
|
-
const stat =
|
|
16348
|
+
const fullPath = join26(dirPath, entry);
|
|
16349
|
+
const stat = statSync6(fullPath);
|
|
15284
16350
|
if (stat.isDirectory()) {
|
|
15285
16351
|
this.scanDir(fullPath, skills);
|
|
15286
16352
|
continue;
|
|
@@ -15752,7 +16818,7 @@ var DEFAULT_TIMEOUT = 15000;
|
|
|
15752
16818
|
var init_client2 = () => {};
|
|
15753
16819
|
|
|
15754
16820
|
// src/modules/lsp/module.ts
|
|
15755
|
-
import { existsSync as
|
|
16821
|
+
import { existsSync as existsSync33 } from "fs";
|
|
15756
16822
|
import { resolve as resolve17 } from "path";
|
|
15757
16823
|
|
|
15758
16824
|
class LspModule {
|
|
@@ -15778,7 +16844,7 @@ class LspModule {
|
|
|
15778
16844
|
if (!filePath)
|
|
15779
16845
|
return;
|
|
15780
16846
|
const fullPath = resolve17(_ctx.baseDir, filePath);
|
|
15781
|
-
if (!
|
|
16847
|
+
if (!existsSync33(fullPath))
|
|
15782
16848
|
return;
|
|
15783
16849
|
const serverConfig = getServerForFile(fullPath, self.config);
|
|
15784
16850
|
if (!serverConfig)
|
|
@@ -15836,8 +16902,8 @@ var init_lsp = __esm(() => {
|
|
|
15836
16902
|
});
|
|
15837
16903
|
|
|
15838
16904
|
// src/modules/indexer/walker.ts
|
|
15839
|
-
import { readdirSync as
|
|
15840
|
-
import { join as
|
|
16905
|
+
import { readdirSync as readdirSync13, readFileSync as readFileSync20, statSync as statSync7, existsSync as existsSync34, watch } from "fs";
|
|
16906
|
+
import { join as join27, relative, extname as extname5 } from "path";
|
|
15841
16907
|
|
|
15842
16908
|
class Indexer {
|
|
15843
16909
|
baseDir;
|
|
@@ -15864,20 +16930,20 @@ class Indexer {
|
|
|
15864
16930
|
let totalSize = 0;
|
|
15865
16931
|
let count = 0;
|
|
15866
16932
|
const walkDir = (dir) => {
|
|
15867
|
-
if (!
|
|
16933
|
+
if (!existsSync34(dir))
|
|
15868
16934
|
return;
|
|
15869
16935
|
let entries;
|
|
15870
16936
|
try {
|
|
15871
|
-
entries =
|
|
16937
|
+
entries = readdirSync13(dir);
|
|
15872
16938
|
} catch {
|
|
15873
16939
|
return;
|
|
15874
16940
|
}
|
|
15875
16941
|
for (const entry of entries) {
|
|
15876
16942
|
if (count >= this.MAX_FILES)
|
|
15877
16943
|
return;
|
|
15878
|
-
const fullPath =
|
|
16944
|
+
const fullPath = join27(dir, entry);
|
|
15879
16945
|
const relPath = relative(this.baseDir, fullPath);
|
|
15880
|
-
const stat =
|
|
16946
|
+
const stat = statSync7(fullPath);
|
|
15881
16947
|
if (stat.isDirectory()) {
|
|
15882
16948
|
if (!IGNORE_DIRS.has(entry)) {
|
|
15883
16949
|
walkDir(fullPath);
|
|
@@ -15938,19 +17004,19 @@ var init_walker = __esm(() => {
|
|
|
15938
17004
|
});
|
|
15939
17005
|
|
|
15940
17006
|
// src/modules/indexer/cache.ts
|
|
15941
|
-
import { readFileSync as readFileSync21, writeFileSync as writeFileSync12, existsSync as
|
|
15942
|
-
import { join as
|
|
17007
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync12, existsSync as existsSync35, mkdirSync as mkdirSync15, rmSync as rmSync3 } from "fs";
|
|
17008
|
+
import { join as join28 } from "path";
|
|
15943
17009
|
|
|
15944
17010
|
class IndexCache {
|
|
15945
17011
|
cachePath;
|
|
15946
17012
|
cache = null;
|
|
15947
17013
|
constructor(cacheDir) {
|
|
15948
|
-
this.cachePath =
|
|
17014
|
+
this.cachePath = join28(cacheDir, "index-cache.json");
|
|
15949
17015
|
}
|
|
15950
17016
|
load() {
|
|
15951
17017
|
if (this.cache)
|
|
15952
17018
|
return this.cache;
|
|
15953
|
-
if (!
|
|
17019
|
+
if (!existsSync35(this.cachePath))
|
|
15954
17020
|
return null;
|
|
15955
17021
|
try {
|
|
15956
17022
|
this.cache = JSON.parse(readFileSync21(this.cachePath, "utf-8"));
|
|
@@ -15961,16 +17027,16 @@ class IndexCache {
|
|
|
15961
17027
|
}
|
|
15962
17028
|
save(result) {
|
|
15963
17029
|
this.cache = result;
|
|
15964
|
-
const dir =
|
|
15965
|
-
if (!
|
|
15966
|
-
|
|
17030
|
+
const dir = join28(this.cachePath, "..");
|
|
17031
|
+
if (!existsSync35(dir))
|
|
17032
|
+
mkdirSync15(dir, { recursive: true });
|
|
15967
17033
|
writeFileSync12(this.cachePath, JSON.stringify(result), "utf-8");
|
|
15968
17034
|
}
|
|
15969
17035
|
invalidate() {
|
|
15970
17036
|
this.cache = null;
|
|
15971
|
-
if (
|
|
17037
|
+
if (existsSync35(this.cachePath)) {
|
|
15972
17038
|
try {
|
|
15973
|
-
|
|
17039
|
+
rmSync3(this.cachePath);
|
|
15974
17040
|
} catch {}
|
|
15975
17041
|
}
|
|
15976
17042
|
}
|
|
@@ -15978,7 +17044,7 @@ class IndexCache {
|
|
|
15978
17044
|
var init_cache = () => {};
|
|
15979
17045
|
|
|
15980
17046
|
// src/modules/indexer/module.ts
|
|
15981
|
-
import { dirname as
|
|
17047
|
+
import { dirname as dirname7 } from "path";
|
|
15982
17048
|
|
|
15983
17049
|
class IndexerModule {
|
|
15984
17050
|
name = "indexer";
|
|
@@ -16095,7 +17161,7 @@ ${t("indexer.and_more", { count: result.files.length - 100 })}` : "";
|
|
|
16095
17161
|
const counts = {};
|
|
16096
17162
|
for (const f of result.files) {
|
|
16097
17163
|
const normalized = f.path.replace(/\\/g, "/");
|
|
16098
|
-
const dir =
|
|
17164
|
+
const dir = dirname7(normalized);
|
|
16099
17165
|
const key = dir === "." ? "(root)" : dir;
|
|
16100
17166
|
counts[key] = (counts[key] || 0) + 1;
|
|
16101
17167
|
}
|
|
@@ -16326,13 +17392,13 @@ var init_mcp = __esm(() => {
|
|
|
16326
17392
|
|
|
16327
17393
|
// src/modules/memory/module.ts
|
|
16328
17394
|
import { homedir as homedir10 } from "os";
|
|
16329
|
-
import { join as
|
|
17395
|
+
import { join as join29 } from "path";
|
|
16330
17396
|
|
|
16331
17397
|
class MemoryModule {
|
|
16332
17398
|
name = "memory";
|
|
16333
17399
|
store;
|
|
16334
17400
|
constructor(memoryDir) {
|
|
16335
|
-
const dir = memoryDir ||
|
|
17401
|
+
const dir = memoryDir || join29(homedir10(), ".mma", "memory");
|
|
16336
17402
|
this.store = new MemoryStore(dir);
|
|
16337
17403
|
}
|
|
16338
17404
|
getSystemPromptBlock() {
|
|
@@ -16376,46 +17442,38 @@ var init_module8 = __esm(() => {
|
|
|
16376
17442
|
// src/core/bootstrap.ts
|
|
16377
17443
|
var exports_bootstrap = {};
|
|
16378
17444
|
__export(exports_bootstrap, {
|
|
17445
|
+
buildSystemInfo: () => buildSystemInfo,
|
|
16379
17446
|
bootstrap: () => bootstrap
|
|
16380
17447
|
});
|
|
16381
17448
|
import { homedir as homedir11 } from "os";
|
|
16382
|
-
import { join as
|
|
16383
|
-
import { existsSync as
|
|
17449
|
+
import { join as join30, resolve as resolve18 } from "path";
|
|
17450
|
+
import { existsSync as existsSync36, readFileSync as readFileSync22, writeFileSync as writeFileSync13 } from "fs";
|
|
16384
17451
|
function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
16385
17452
|
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
16386
17453
|
const isWin = profileCompressed.toLowerCase().includes("win32");
|
|
16387
17454
|
const lines = [
|
|
16388
|
-
`You are MMA v2, an AI coding agent for small models (${config.model}).`,
|
|
16389
|
-
`
|
|
16390
|
-
`
|
|
16391
|
-
`${profileCompressed}`,
|
|
16392
|
-
`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.`,
|
|
16393
|
-
`Use tools when needed — explain briefly what you're doing if it's not obvious.`,
|
|
16394
|
-
`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.`,
|
|
16395
|
-
``,
|
|
16396
|
-
`Design principles:`,
|
|
16397
|
-
`- YAGNI: Do not add code, files, or abstractions not needed right now.`,
|
|
16398
|
-
`- KISS: Prefer simple, straightforward solutions over clever or complex ones.`,
|
|
16399
|
-
`- DRY: Do not duplicate code, logic, or configuration — reuse existing utilities and patterns.`
|
|
17455
|
+
`You are MMA v2, an AI coding agent for small models (${config.model}). Date: ${now}. Workspace: ${baseDir}. ${profileCompressed}.`,
|
|
17456
|
+
`Reply in the user's language. Use tools for file ops (read/write/edit/delete), search (glob/grep), shell (bash), web, subagents, browser, MCP. Explain briefly if not obvious. On tool failure: analyze, fix the call, retry up to 2x with different approaches, then ask the user.`,
|
|
17457
|
+
`Design: YAGNI (no unneeded code), KISS (simple over clever), DRY (reuse existing utilities).`
|
|
16400
17458
|
];
|
|
16401
17459
|
if (isWin) {
|
|
16402
|
-
lines.push(
|
|
17460
|
+
lines.push(`Windows (PowerShell): use list_dir/read_file/delete_file/create_dir tools instead of dir/type/del/mkdir. No PowerShell cmdlets (Get-Content, Select-Object, Write-Output), no head/tail/grep/cat. Use forward slashes in paths. CWD: ${baseDir}`);
|
|
16403
17461
|
}
|
|
16404
|
-
lines.push(
|
|
16405
|
-
lines.push(
|
|
17462
|
+
lines.push(`Runtime: Bun is available — run TypeScript directly with "bun <file.ts>" and tests with "bun test" (no tsx/ts-node/npm install needed for that).`);
|
|
17463
|
+
lines.push(`Bash: use "workdir" param instead of "cd dir && cmd". One command per call. Long-running processes: pass "background: true" (id immediately), otherwise auto-backgrounded after a few seconds — check with process_log.`, `DEVELOPMENT RULES (strict): 1) install deps BEFORE writing source (npm/pip/cargo, verify lockfile exists; never import uninstalled packages); 2) framework/toolkit init BEFORE app code; 3) follow plan sequentially, after each step verify deliverables exist with real content, then "plan update step=N status=done"; 4) verify work on disk + command output — don't assume success; 5) no premature work (no files/imports for future steps); 6) stuck after 2+ failures: STOP, try a different approach, write files directly, ask the user.`, `PLAN QUALITY: each step = CONCRETE deliverables (exact file paths with extensions, exact packages, exact commands). Vague steps ("Setup the project") forbidden. Cover init → deps → framework → code → verification. 5-8 steps.`);
|
|
16406
17464
|
if (config.autoPlan) {
|
|
16407
|
-
lines.push(
|
|
17465
|
+
lines.push(`Plan rule (MANDATORY): any task creating files, installing packages, or requiring multiple actions MUST create a plan with the "plan" tool BEFORE starting. Each step = a concrete deliverable.`);
|
|
16408
17466
|
}
|
|
16409
17467
|
const hasMCP = config.mcpServers && Object.values(config.mcpServers).some((s) => s.enabled !== false);
|
|
16410
17468
|
if (hasMCP) {
|
|
16411
|
-
lines.push(
|
|
17469
|
+
lines.push(`MCP servers are available — use the named MCP tools (prefixed mcp__) to query external services.`);
|
|
16412
17470
|
}
|
|
16413
17471
|
return lines.join(`
|
|
16414
17472
|
`);
|
|
16415
17473
|
}
|
|
16416
17474
|
async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
16417
|
-
const dir = configDir ||
|
|
16418
|
-
const projectConfigPath = projectDir ?
|
|
17475
|
+
const dir = configDir || join30(homedir11(), ".mma");
|
|
17476
|
+
const projectConfigPath = projectDir ? join30(projectDir, ".mmrc") : join30(process.cwd(), ".mmrc");
|
|
16419
17477
|
const config = loadConfig({ configDir: dir, projectConfigPath });
|
|
16420
17478
|
setLocale(config.locale);
|
|
16421
17479
|
try {
|
|
@@ -16425,7 +17483,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16425
17483
|
}
|
|
16426
17484
|
} catch {}
|
|
16427
17485
|
const logger = new Logger(config.logLevel);
|
|
16428
|
-
logger.setLogDir(
|
|
17486
|
+
logger.setLogDir(join30(dir, "logs"));
|
|
16429
17487
|
logger.debug("MMA bootstrap", {
|
|
16430
17488
|
version: config.version,
|
|
16431
17489
|
model: config.model
|
|
@@ -16447,7 +17505,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16447
17505
|
logger.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
|
|
16448
17506
|
}
|
|
16449
17507
|
}
|
|
16450
|
-
const profile = new UserProfile(
|
|
17508
|
+
const profile = new UserProfile(join30(dir));
|
|
16451
17509
|
profile.load() || profile.collect();
|
|
16452
17510
|
profile.save();
|
|
16453
17511
|
const llmProvider = new OpenAICompatProvider({
|
|
@@ -16459,7 +17517,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16459
17517
|
rateLimits: config.security?.rateLimits
|
|
16460
17518
|
});
|
|
16461
17519
|
const baseDir = projectDir ? resolve18(projectDir) : process.cwd();
|
|
16462
|
-
const projectMapCacheDir =
|
|
17520
|
+
const projectMapCacheDir = join30(baseDir, ".mma");
|
|
16463
17521
|
const indexerModule = new IndexerModule({
|
|
16464
17522
|
baseDir,
|
|
16465
17523
|
cacheDir: projectMapCacheDir
|
|
@@ -16470,26 +17528,27 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16470
17528
|
logger.warn(`Project indexing failed: ${err.message}`);
|
|
16471
17529
|
}
|
|
16472
17530
|
const skillsLoader = new SkillsLoader;
|
|
16473
|
-
const builtinDir =
|
|
16474
|
-
const globalDir =
|
|
16475
|
-
const projectSkillsDir =
|
|
17531
|
+
const builtinDir = join30(import.meta.dirname, "skills", "builtin");
|
|
17532
|
+
const globalDir = join30(homedir11(), ".agents", "skills");
|
|
17533
|
+
const projectSkillsDir = join30(baseDir, ".mma", "skills");
|
|
16476
17534
|
const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
|
|
16477
17535
|
const skillsBudget = Math.floor(config.contextWindow * config.skills.budget);
|
|
16478
17536
|
const skillsModule = new SkillsModule(availableSkills, skillsBudget);
|
|
16479
17537
|
const toolRegistry = new ToolRegistry;
|
|
16480
17538
|
registerAllTools(toolRegistry, skillsModule);
|
|
16481
17539
|
const pluginManager = new PluginManager;
|
|
17540
|
+
const systemInfoContent = buildSystemInfo(config, baseDir, profile.compress());
|
|
16482
17541
|
const systemInfoPrompt = {
|
|
16483
|
-
content:
|
|
17542
|
+
content: systemInfoContent,
|
|
16484
17543
|
priority: "critical",
|
|
16485
17544
|
essential: true,
|
|
16486
|
-
estimatedTokens:
|
|
17545
|
+
estimatedTokens: Math.ceil(systemInfoContent.length / 4)
|
|
16487
17546
|
};
|
|
16488
|
-
const agentsMdGlobal =
|
|
16489
|
-
if (!
|
|
17547
|
+
const agentsMdGlobal = join30(dir, "AGENTS.md");
|
|
17548
|
+
if (!existsSync36(agentsMdGlobal)) {
|
|
16490
17549
|
writeFileSync13(agentsMdGlobal, "", "utf-8");
|
|
16491
17550
|
}
|
|
16492
|
-
const sessionDir =
|
|
17551
|
+
const sessionDir = join30(dir, "sessions");
|
|
16493
17552
|
const sessionStore = new SessionStore(sessionDir);
|
|
16494
17553
|
sessionStore.init();
|
|
16495
17554
|
const sessionManager = new SessionManager(sessionStore, {
|
|
@@ -16556,7 +17615,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16556
17615
|
const mcpModule = new MCPModule(config);
|
|
16557
17616
|
await mcpModule.initialize();
|
|
16558
17617
|
moduleRegistry.register(mcpModule);
|
|
16559
|
-
const memoryModule = new MemoryModule(
|
|
17618
|
+
const memoryModule = new MemoryModule(join30(dir, "memory"));
|
|
16560
17619
|
moduleRegistry.register(memoryModule);
|
|
16561
17620
|
if (config.browser.enabled) {
|
|
16562
17621
|
const browserModule = new BrowserModule;
|
|
@@ -16606,8 +17665,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16606
17665
|
pluginManager.register(plugin);
|
|
16607
17666
|
pluginManager.register(plugin2);
|
|
16608
17667
|
const pluginLoader = new PluginLoader;
|
|
16609
|
-
const globalPluginsDir =
|
|
16610
|
-
const projectPluginsDir =
|
|
17668
|
+
const globalPluginsDir = join30(homedir11(), ".mma", "plugins");
|
|
17669
|
+
const projectPluginsDir = join30(baseDir, ".mma", "plugins");
|
|
16611
17670
|
pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
|
|
16612
17671
|
pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
|
|
16613
17672
|
contextManager.onCompact = (summary) => {
|
|
@@ -16625,12 +17684,12 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
16625
17684
|
const skipAgentsMd = noAgentsMd === true;
|
|
16626
17685
|
if (!skipAgentsMd) {
|
|
16627
17686
|
const agentsMdCandidates = [
|
|
16628
|
-
|
|
16629
|
-
|
|
16630
|
-
|
|
17687
|
+
join30(baseDir, "AGENTS.md"),
|
|
17688
|
+
join30(baseDir, ".mma", "AGENTS.md"),
|
|
17689
|
+
join30(dir, "AGENTS.md")
|
|
16631
17690
|
];
|
|
16632
17691
|
for (const p of agentsMdCandidates) {
|
|
16633
|
-
if (
|
|
17692
|
+
if (existsSync36(p)) {
|
|
16634
17693
|
const content = readFileSync22(p, "utf-8").trim();
|
|
16635
17694
|
if (content) {
|
|
16636
17695
|
agentsMdBlocks.push({
|
|
@@ -16719,7 +17778,7 @@ function ansiRegex({ onlyFirst = false } = {}) {
|
|
|
16719
17778
|
return new RegExp(pattern, onlyFirst ? undefined : "g");
|
|
16720
17779
|
}
|
|
16721
17780
|
|
|
16722
|
-
// node_modules/
|
|
17781
|
+
// node_modules/strip-ansi/index.js
|
|
16723
17782
|
function stripAnsi(string) {
|
|
16724
17783
|
if (typeof string !== "string") {
|
|
16725
17784
|
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
|
|
@@ -16818,65 +17877,135 @@ var init_get_east_asian_width = __esm(() => {
|
|
|
16818
17877
|
init_lookup();
|
|
16819
17878
|
});
|
|
16820
17879
|
|
|
16821
|
-
// node_modules/emoji-regex/index.js
|
|
16822
|
-
var require_emoji_regex = __commonJS((exports, module) => {
|
|
16823
|
-
module.exports = () => {
|
|
16824
|
-
return /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E-\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED8\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])))?))?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE8A\uDE8E-\uDEC2\uDEC6\uDEC8\uDECD-\uDEDC\uDEDF-\uDEEA\uDEEF]|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;
|
|
16825
|
-
};
|
|
16826
|
-
});
|
|
16827
|
-
|
|
16828
17880
|
// node_modules/string-width/index.js
|
|
16829
|
-
function
|
|
16830
|
-
if (
|
|
17881
|
+
function isDoubleWidthNonRgiEmojiSequence(segment) {
|
|
17882
|
+
if (segment.length > 50) {
|
|
17883
|
+
return false;
|
|
17884
|
+
}
|
|
17885
|
+
if (unqualifiedKeycapRegex.test(segment)) {
|
|
17886
|
+
return true;
|
|
17887
|
+
}
|
|
17888
|
+
if (segment.includes("")) {
|
|
17889
|
+
const pictographics = segment.match(extendedPictographicRegex);
|
|
17890
|
+
return pictographics !== null && pictographics.length >= 2;
|
|
17891
|
+
}
|
|
17892
|
+
return false;
|
|
17893
|
+
}
|
|
17894
|
+
function baseVisible(segment) {
|
|
17895
|
+
return segment.replace(leadingNonPrintingRegex, "");
|
|
17896
|
+
}
|
|
17897
|
+
function isZeroWidthCluster(segment) {
|
|
17898
|
+
return zeroWidthClusterRegex.test(segment);
|
|
17899
|
+
}
|
|
17900
|
+
function isHangulLeadingJamo(codePoint) {
|
|
17901
|
+
return codePoint >= 4352 && codePoint <= 4447 || codePoint >= 43360 && codePoint <= 43388;
|
|
17902
|
+
}
|
|
17903
|
+
function isHangulVowelJamo(codePoint) {
|
|
17904
|
+
return codePoint >= 4448 && codePoint <= 4519 || codePoint >= 55216 && codePoint <= 55238;
|
|
17905
|
+
}
|
|
17906
|
+
function isHangulTrailingJamo(codePoint) {
|
|
17907
|
+
return codePoint >= 4520 && codePoint <= 4607 || codePoint >= 55243 && codePoint <= 55291;
|
|
17908
|
+
}
|
|
17909
|
+
function isHangulJamo(codePoint) {
|
|
17910
|
+
return isHangulLeadingJamo(codePoint) || isHangulVowelJamo(codePoint) || isHangulTrailingJamo(codePoint);
|
|
17911
|
+
}
|
|
17912
|
+
function hangulClusterWidth(visibleSegment, eastAsianWidthOptions) {
|
|
17913
|
+
const codePoints = [];
|
|
17914
|
+
for (const character of visibleSegment) {
|
|
17915
|
+
if (zeroWidthClusterRegex.test(character)) {
|
|
17916
|
+
continue;
|
|
17917
|
+
}
|
|
17918
|
+
codePoints.push(character.codePointAt(0));
|
|
17919
|
+
}
|
|
17920
|
+
if (codePoints.length === 0) {
|
|
17921
|
+
return;
|
|
17922
|
+
}
|
|
17923
|
+
let width = 0;
|
|
17924
|
+
for (let index = 0;index < codePoints.length; index++) {
|
|
17925
|
+
const codePoint = codePoints[index];
|
|
17926
|
+
if (!isHangulJamo(codePoint)) {
|
|
17927
|
+
if (width === 0) {
|
|
17928
|
+
return;
|
|
17929
|
+
}
|
|
17930
|
+
for (let remaining = index;remaining < codePoints.length; remaining++) {
|
|
17931
|
+
width += eastAsianWidth(codePoints[remaining], eastAsianWidthOptions);
|
|
17932
|
+
}
|
|
17933
|
+
return width;
|
|
17934
|
+
}
|
|
17935
|
+
if (isHangulLeadingJamo(codePoint) && isHangulVowelJamo(codePoints[index + 1])) {
|
|
17936
|
+
width += 2;
|
|
17937
|
+
index += isHangulTrailingJamo(codePoints[index + 2]) ? 2 : 1;
|
|
17938
|
+
continue;
|
|
17939
|
+
}
|
|
17940
|
+
width += eastAsianWidth(codePoint, eastAsianWidthOptions);
|
|
17941
|
+
}
|
|
17942
|
+
return width;
|
|
17943
|
+
}
|
|
17944
|
+
function trailingWidth(visibleSegment, eastAsianWidthOptions) {
|
|
17945
|
+
let extra = 0;
|
|
17946
|
+
let first = true;
|
|
17947
|
+
for (const character of visibleSegment) {
|
|
17948
|
+
if (first) {
|
|
17949
|
+
first = false;
|
|
17950
|
+
continue;
|
|
17951
|
+
}
|
|
17952
|
+
if (spacingMarkRegex.test(character) || character >= "" && character <= "") {
|
|
17953
|
+
extra += eastAsianWidth(character.codePointAt(0), eastAsianWidthOptions);
|
|
17954
|
+
}
|
|
17955
|
+
}
|
|
17956
|
+
return extra;
|
|
17957
|
+
}
|
|
17958
|
+
function stringWidth(input, options = {}) {
|
|
17959
|
+
if (typeof input !== "string" || input.length === 0) {
|
|
16831
17960
|
return 0;
|
|
16832
17961
|
}
|
|
16833
17962
|
const {
|
|
16834
17963
|
ambiguousIsNarrow = true,
|
|
16835
17964
|
countAnsiEscapeCodes = false
|
|
16836
17965
|
} = options;
|
|
16837
|
-
|
|
17966
|
+
let string = input;
|
|
17967
|
+
if (!countAnsiEscapeCodes && (string.includes("\x1B") || string.includes(""))) {
|
|
16838
17968
|
string = stripAnsi(string);
|
|
16839
17969
|
}
|
|
16840
17970
|
if (string.length === 0) {
|
|
16841
17971
|
return 0;
|
|
16842
17972
|
}
|
|
17973
|
+
if (/^[\u0020-\u007E]*$/.test(string)) {
|
|
17974
|
+
return string.length;
|
|
17975
|
+
}
|
|
16843
17976
|
let width = 0;
|
|
16844
17977
|
const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
|
|
16845
|
-
for (const { segment
|
|
16846
|
-
|
|
16847
|
-
if (codePoint <= 31 || codePoint >= 127 && codePoint <= 159) {
|
|
16848
|
-
continue;
|
|
16849
|
-
}
|
|
16850
|
-
if (codePoint >= 8203 && codePoint <= 8207 || codePoint === 65279) {
|
|
16851
|
-
continue;
|
|
16852
|
-
}
|
|
16853
|
-
if (codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071) {
|
|
16854
|
-
continue;
|
|
16855
|
-
}
|
|
16856
|
-
if (codePoint >= 55296 && codePoint <= 57343) {
|
|
16857
|
-
continue;
|
|
16858
|
-
}
|
|
16859
|
-
if (codePoint >= 65024 && codePoint <= 65039) {
|
|
17978
|
+
for (const { segment } of segmenter.segment(string)) {
|
|
17979
|
+
if (isZeroWidthCluster(segment)) {
|
|
16860
17980
|
continue;
|
|
16861
17981
|
}
|
|
16862
|
-
if (
|
|
17982
|
+
if (rgiEmojiRegex.test(segment) || isDoubleWidthNonRgiEmojiSequence(segment)) {
|
|
17983
|
+
width += 2;
|
|
16863
17984
|
continue;
|
|
16864
17985
|
}
|
|
16865
|
-
|
|
16866
|
-
|
|
17986
|
+
const visibleSegment = baseVisible(segment);
|
|
17987
|
+
const hangulWidth = hangulClusterWidth(visibleSegment, eastAsianWidthOptions);
|
|
17988
|
+
if (hangulWidth !== undefined) {
|
|
17989
|
+
width += hangulWidth;
|
|
16867
17990
|
continue;
|
|
16868
17991
|
}
|
|
17992
|
+
const codePoint = visibleSegment.codePointAt(0);
|
|
16869
17993
|
width += eastAsianWidth(codePoint, eastAsianWidthOptions);
|
|
17994
|
+
width += trailingWidth(visibleSegment, eastAsianWidthOptions);
|
|
16870
17995
|
}
|
|
16871
17996
|
return width;
|
|
16872
17997
|
}
|
|
16873
|
-
var
|
|
17998
|
+
var segmenter, zeroWidthClusterRegex, leadingNonPrintingRegex, spacingMarkRegex, rgiEmojiRegex, unqualifiedKeycapRegex, extendedPictographicRegex;
|
|
16874
17999
|
var init_string_width = __esm(() => {
|
|
16875
18000
|
init_strip_ansi();
|
|
16876
18001
|
init_get_east_asian_width();
|
|
16877
|
-
import_emoji_regex = __toESM(require_emoji_regex(), 1);
|
|
16878
18002
|
segmenter = new Intl.Segmenter;
|
|
16879
|
-
|
|
18003
|
+
zeroWidthClusterRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Format}|\p{Nonspacing_Mark}|\p{Enclosing_Mark}|\p{Surrogate})+$/v;
|
|
18004
|
+
leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Nonspacing_Mark}\p{Enclosing_Mark}\p{Surrogate}]+/v;
|
|
18005
|
+
spacingMarkRegex = /\p{Spacing_Mark}/v;
|
|
18006
|
+
rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
|
|
18007
|
+
unqualifiedKeycapRegex = /^[\d#*]\u20E3$/;
|
|
18008
|
+
extendedPictographicRegex = /\p{Extended_Pictographic}/gu;
|
|
16880
18009
|
});
|
|
16881
18010
|
|
|
16882
18011
|
// src/ui/table.ts
|
|
@@ -17410,12 +18539,12 @@ __export(exports_manifest, {
|
|
|
17410
18539
|
getCertMark: () => getCertMark,
|
|
17411
18540
|
MANIFEST_PATH: () => MANIFEST_PATH
|
|
17412
18541
|
});
|
|
17413
|
-
import { existsSync as
|
|
18542
|
+
import { existsSync as existsSync37, readFileSync as readFileSync23, mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
17414
18543
|
import { homedir as homedir13 } from "os";
|
|
17415
|
-
import { join as
|
|
18544
|
+
import { join as join32 } from "path";
|
|
17416
18545
|
function readManifest(path = MANIFEST_PATH) {
|
|
17417
18546
|
try {
|
|
17418
|
-
if (
|
|
18547
|
+
if (existsSync37(path)) {
|
|
17419
18548
|
const raw = JSON.parse(readFileSync23(path, "utf-8"));
|
|
17420
18549
|
return { version: 1, certifications: raw.certifications ?? [] };
|
|
17421
18550
|
}
|
|
@@ -17423,7 +18552,7 @@ function readManifest(path = MANIFEST_PATH) {
|
|
|
17423
18552
|
return { version: 1, certifications: [] };
|
|
17424
18553
|
}
|
|
17425
18554
|
function saveManifest(m, path = MANIFEST_PATH) {
|
|
17426
|
-
|
|
18555
|
+
mkdirSync16(join32(homedir13(), ".mma"), { recursive: true });
|
|
17427
18556
|
writeFileSync14(path, JSON.stringify(m, null, 2), "utf-8");
|
|
17428
18557
|
}
|
|
17429
18558
|
function upsertCertification(entry, path = MANIFEST_PATH) {
|
|
@@ -17458,7 +18587,7 @@ function getCertMark(model, providerUrl, currentVersion, path = MANIFEST_PATH) {
|
|
|
17458
18587
|
}
|
|
17459
18588
|
var MANIFEST_PATH;
|
|
17460
18589
|
var init_manifest = __esm(() => {
|
|
17461
|
-
MANIFEST_PATH =
|
|
18590
|
+
MANIFEST_PATH = join32(homedir13(), ".mma", "certifications.json");
|
|
17462
18591
|
});
|
|
17463
18592
|
|
|
17464
18593
|
// node_modules/yaml/dist/nodes/identity.js
|
|
@@ -24581,8 +25710,8 @@ var init_scenarios = __esm(() => {
|
|
|
24581
25710
|
});
|
|
24582
25711
|
|
|
24583
25712
|
// src/modules/certification/loader.ts
|
|
24584
|
-
import { existsSync as
|
|
24585
|
-
import { join as
|
|
25713
|
+
import { existsSync as existsSync38, readdirSync as readdirSync14, readFileSync as readFileSync24 } from "fs";
|
|
25714
|
+
import { join as join33 } from "path";
|
|
24586
25715
|
function validateScenario(s) {
|
|
24587
25716
|
const errors2 = [];
|
|
24588
25717
|
const isSkip = s.mode === "skip";
|
|
@@ -24631,12 +25760,12 @@ function loadScenarios(userDir) {
|
|
|
24631
25760
|
else
|
|
24632
25761
|
scenarios.push(s);
|
|
24633
25762
|
}
|
|
24634
|
-
if (userDir &&
|
|
24635
|
-
for (const file of
|
|
25763
|
+
if (userDir && existsSync38(userDir)) {
|
|
25764
|
+
for (const file of readdirSync14(userDir)) {
|
|
24636
25765
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
|
|
24637
25766
|
continue;
|
|
24638
25767
|
try {
|
|
24639
|
-
const raw = readFileSync24(
|
|
25768
|
+
const raw = readFileSync24(join33(userDir, file), "utf-8");
|
|
24640
25769
|
const data = $parse(raw);
|
|
24641
25770
|
const parsed = normalizeScenario(data, file);
|
|
24642
25771
|
const errs = validateScenario(parsed);
|
|
@@ -24689,8 +25818,8 @@ var init_loader3 = __esm(() => {
|
|
|
24689
25818
|
});
|
|
24690
25819
|
|
|
24691
25820
|
// src/modules/certification/fact-checker.ts
|
|
24692
|
-
import { existsSync as
|
|
24693
|
-
import { join as
|
|
25821
|
+
import { existsSync as existsSync39, readFileSync as readFileSync25, statSync as statSync8 } from "fs";
|
|
25822
|
+
import { join as join34 } from "path";
|
|
24694
25823
|
function checkSandbox(sandboxDir, checks, exitCode, output) {
|
|
24695
25824
|
const failures = [];
|
|
24696
25825
|
for (const check of checks) {
|
|
@@ -24707,13 +25836,13 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
24707
25836
|
case "outputContains":
|
|
24708
25837
|
return output.includes(check.text);
|
|
24709
25838
|
case "fileExists":
|
|
24710
|
-
return isFile(
|
|
25839
|
+
return isFile(join34(sandboxDir, check.path));
|
|
24711
25840
|
case "fileNotExists":
|
|
24712
|
-
return !
|
|
25841
|
+
return !existsSync39(join34(sandboxDir, check.path));
|
|
24713
25842
|
case "dirExists":
|
|
24714
|
-
return isDir(
|
|
25843
|
+
return isDir(join34(sandboxDir, check.path));
|
|
24715
25844
|
case "fileContent": {
|
|
24716
|
-
const abs =
|
|
25845
|
+
const abs = join34(sandboxDir, check.path);
|
|
24717
25846
|
if (!isFile(abs))
|
|
24718
25847
|
return false;
|
|
24719
25848
|
const content = readFileSync25(abs, "utf-8");
|
|
@@ -24724,7 +25853,7 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
24724
25853
|
return false;
|
|
24725
25854
|
}
|
|
24726
25855
|
case "fileRegex": {
|
|
24727
|
-
const abs =
|
|
25856
|
+
const abs = join34(sandboxDir, check.path);
|
|
24728
25857
|
if (!isFile(abs))
|
|
24729
25858
|
return false;
|
|
24730
25859
|
return new RegExp(check.pattern).test(readFileSync25(abs, "utf-8"));
|
|
@@ -24735,14 +25864,14 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
24735
25864
|
}
|
|
24736
25865
|
function isFile(p) {
|
|
24737
25866
|
try {
|
|
24738
|
-
return
|
|
25867
|
+
return existsSync39(p) && statSync8(p).isFile();
|
|
24739
25868
|
} catch {
|
|
24740
25869
|
return false;
|
|
24741
25870
|
}
|
|
24742
25871
|
}
|
|
24743
25872
|
function isDir(p) {
|
|
24744
25873
|
try {
|
|
24745
|
-
return
|
|
25874
|
+
return existsSync39(p) && statSync8(p).isDirectory();
|
|
24746
25875
|
} catch {
|
|
24747
25876
|
return false;
|
|
24748
25877
|
}
|
|
@@ -24773,9 +25902,9 @@ var init_fact_checker = () => {};
|
|
|
24773
25902
|
|
|
24774
25903
|
// src/modules/certification/runner.ts
|
|
24775
25904
|
import { spawn as spawn6 } from "child_process";
|
|
24776
|
-
import { existsSync as
|
|
25905
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync17, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
|
|
24777
25906
|
import { platform as platform5 } from "os";
|
|
24778
|
-
import { join as
|
|
25907
|
+
import { join as join35, resolve as resolve19, dirname as dirname8 } from "path";
|
|
24779
25908
|
async function runScenario(scenario, opts) {
|
|
24780
25909
|
if (scenario.mode === "skip") {
|
|
24781
25910
|
return {
|
|
@@ -24787,14 +25916,14 @@ async function runScenario(scenario, opts) {
|
|
|
24787
25916
|
};
|
|
24788
25917
|
}
|
|
24789
25918
|
const reps = scenario.reps ?? opts.defaultReps;
|
|
24790
|
-
const threshold = scenario.passThreshold ?? opts.defaultThreshold;
|
|
25919
|
+
const threshold = Math.min(scenario.passThreshold ?? opts.defaultThreshold, reps);
|
|
24791
25920
|
const runner = opts.runner ?? defaultRunner;
|
|
24792
25921
|
const timeoutMs = opts.timeoutMs ?? 120000;
|
|
24793
25922
|
const entryPoint = resolveMmaEntry(opts.mmaRoot);
|
|
24794
25923
|
let passed = 0;
|
|
24795
25924
|
let firstError;
|
|
24796
25925
|
for (let i = 1;i <= reps; i++) {
|
|
24797
|
-
const sandbox =
|
|
25926
|
+
const sandbox = join35(opts.sandboxBase, `run-${scenario.id}-${i}`);
|
|
24798
25927
|
let failures = [];
|
|
24799
25928
|
let exitCode = -1;
|
|
24800
25929
|
let output = "";
|
|
@@ -24852,23 +25981,23 @@ ${res.stderr}`;
|
|
|
24852
25981
|
};
|
|
24853
25982
|
}
|
|
24854
25983
|
function prepareSandbox(sandbox, scenario, mmaRoot) {
|
|
24855
|
-
|
|
24856
|
-
|
|
25984
|
+
rmSync4(sandbox, { recursive: true, force: true });
|
|
25985
|
+
mkdirSync17(sandbox, { recursive: true });
|
|
24857
25986
|
for (const f of scenario.fixtures ?? []) {
|
|
24858
|
-
const src =
|
|
24859
|
-
if (!
|
|
25987
|
+
const src = join35(mmaRoot, f.source);
|
|
25988
|
+
if (!existsSync40(src)) {
|
|
24860
25989
|
throw new Error(`fixture missing: ${f.source}`);
|
|
24861
25990
|
}
|
|
24862
|
-
const dest =
|
|
24863
|
-
|
|
25991
|
+
const dest = join35(sandbox, f.dest);
|
|
25992
|
+
mkdirSync17(dirname8(dest), { recursive: true });
|
|
24864
25993
|
cpSync2(src, dest);
|
|
24865
25994
|
}
|
|
24866
25995
|
}
|
|
24867
25996
|
function resolveMmaEntry(mmaRoot) {
|
|
24868
|
-
const dev =
|
|
24869
|
-
if (
|
|
25997
|
+
const dev = join35(mmaRoot, "src", "cli", "main.ts");
|
|
25998
|
+
if (existsSync40(dev))
|
|
24870
25999
|
return dev;
|
|
24871
|
-
return
|
|
26000
|
+
return join35(mmaRoot, "dist", "main.js");
|
|
24872
26001
|
}
|
|
24873
26002
|
function findMmaRoot(fromDir) {
|
|
24874
26003
|
const candidates = [
|
|
@@ -24876,7 +26005,7 @@ function findMmaRoot(fromDir) {
|
|
|
24876
26005
|
resolve19(fromDir, "..")
|
|
24877
26006
|
];
|
|
24878
26007
|
for (const c of candidates) {
|
|
24879
|
-
if (
|
|
26008
|
+
if (existsSync40(join35(c, "package.json")))
|
|
24880
26009
|
return c;
|
|
24881
26010
|
}
|
|
24882
26011
|
return process.cwd();
|
|
@@ -24942,20 +26071,20 @@ __export(exports_cli, {
|
|
|
24942
26071
|
certStatus: () => certStatus,
|
|
24943
26072
|
certList: () => certList
|
|
24944
26073
|
});
|
|
24945
|
-
import { rmSync as
|
|
26074
|
+
import { rmSync as rmSync5 } from "fs";
|
|
24946
26075
|
import { homedir as homedir14 } from "os";
|
|
24947
|
-
import { join as
|
|
26076
|
+
import { join as join36, dirname as dirname9 } from "path";
|
|
24948
26077
|
import { fileURLToPath } from "url";
|
|
24949
|
-
import { existsSync as
|
|
26078
|
+
import { existsSync as existsSync41, readFileSync as readFileSync26 } from "fs";
|
|
24950
26079
|
function readVersion() {
|
|
24951
|
-
const candidates = [
|
|
24952
|
-
join34(MMA_ROOT, "package.json")
|
|
24953
|
-
];
|
|
26080
|
+
const candidates = [join36(MMA_ROOT, "package.json")];
|
|
24954
26081
|
for (const p of candidates) {
|
|
24955
|
-
if (
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
26082
|
+
if (existsSync41(p)) {
|
|
26083
|
+
try {
|
|
26084
|
+
const raw = JSON.parse(readFileSync26(p, "utf-8"));
|
|
26085
|
+
if (raw.version)
|
|
26086
|
+
return raw.version;
|
|
26087
|
+
} catch {}
|
|
24959
26088
|
}
|
|
24960
26089
|
}
|
|
24961
26090
|
return "0.0.0";
|
|
@@ -24988,7 +26117,7 @@ async function certify(opts) {
|
|
|
24988
26117
|
return;
|
|
24989
26118
|
}
|
|
24990
26119
|
console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
|
|
24991
|
-
const sandboxBase =
|
|
26120
|
+
const sandboxBase = join36(process.cwd(), ".mma", "certification");
|
|
24992
26121
|
const results = [];
|
|
24993
26122
|
const total = selected.length;
|
|
24994
26123
|
let idx = 0;
|
|
@@ -24996,7 +26125,13 @@ async function certify(opts) {
|
|
|
24996
26125
|
idx++;
|
|
24997
26126
|
if (scenario.mode === "skip") {
|
|
24998
26127
|
console.log(pc2.dim(`[${idx}/${total}] ${scenario.id} ... skipped`));
|
|
24999
|
-
results.push({
|
|
26128
|
+
results.push({
|
|
26129
|
+
id: scenario.id,
|
|
26130
|
+
title: scenario.title,
|
|
26131
|
+
status: "skipped",
|
|
26132
|
+
passed: 0,
|
|
26133
|
+
of: 0
|
|
26134
|
+
});
|
|
25000
26135
|
continue;
|
|
25001
26136
|
}
|
|
25002
26137
|
const res = await runScenario(scenario, {
|
|
@@ -25019,7 +26154,7 @@ async function certify(opts) {
|
|
|
25019
26154
|
}
|
|
25020
26155
|
if (opts.clean) {
|
|
25021
26156
|
try {
|
|
25022
|
-
|
|
26157
|
+
rmSync5(sandboxBase, { recursive: true, force: true });
|
|
25023
26158
|
} catch {}
|
|
25024
26159
|
}
|
|
25025
26160
|
const suite = summarize(results);
|
|
@@ -25094,9 +26229,9 @@ var init_cli = __esm(() => {
|
|
|
25094
26229
|
init_loader3();
|
|
25095
26230
|
init_runner2();
|
|
25096
26231
|
init_manifest();
|
|
25097
|
-
HERE =
|
|
26232
|
+
HERE = dirname9(fileURLToPath(import.meta.url));
|
|
25098
26233
|
MMA_ROOT = findMmaRoot(HERE);
|
|
25099
|
-
USER_SCENARIO_DIR =
|
|
26234
|
+
USER_SCENARIO_DIR = join36(homedir14(), ".mma", "certification", "scenarios");
|
|
25100
26235
|
});
|
|
25101
26236
|
|
|
25102
26237
|
// src/cli/repl-commands.ts
|
|
@@ -25105,21 +26240,23 @@ __export(exports_repl_commands, {
|
|
|
25105
26240
|
registerAllCommands: () => registerAllCommands,
|
|
25106
26241
|
COMMAND_GROUPS: () => COMMAND_GROUPS
|
|
25107
26242
|
});
|
|
25108
|
-
import { join as
|
|
26243
|
+
import { join as join38, dirname as dirname11 } from "path";
|
|
25109
26244
|
import { homedir as homedir16 } from "os";
|
|
25110
|
-
import { existsSync as
|
|
26245
|
+
import { existsSync as existsSync43, readFileSync as readFileSync28 } from "fs";
|
|
25111
26246
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
25112
26247
|
function readVersion3() {
|
|
25113
|
-
const here =
|
|
26248
|
+
const here = dirname11(fileURLToPath3(import.meta.url));
|
|
25114
26249
|
const candidates = [
|
|
25115
|
-
|
|
25116
|
-
|
|
26250
|
+
join38(here, "..", "..", "package.json"),
|
|
26251
|
+
join38(here, "..", "package.json")
|
|
25117
26252
|
];
|
|
25118
26253
|
for (const p of candidates) {
|
|
25119
|
-
if (
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
26254
|
+
if (existsSync43(p)) {
|
|
26255
|
+
try {
|
|
26256
|
+
const raw = JSON.parse(readFileSync28(p, "utf8"));
|
|
26257
|
+
if (raw.version)
|
|
26258
|
+
return raw.version;
|
|
26259
|
+
} catch {}
|
|
25123
26260
|
}
|
|
25124
26261
|
}
|
|
25125
26262
|
return "0.0.0";
|
|
@@ -25180,7 +26317,7 @@ function registerMmaCommands(ctx) {
|
|
|
25180
26317
|
}
|
|
25181
26318
|
try {
|
|
25182
26319
|
const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
|
|
25183
|
-
const { existsSync:
|
|
26320
|
+
const { existsSync: existsSync44 } = await import("fs");
|
|
25184
26321
|
const { resolve: resolve20 } = await import("path");
|
|
25185
26322
|
let dataUrl;
|
|
25186
26323
|
let label;
|
|
@@ -25200,7 +26337,7 @@ function registerMmaCommands(ctx) {
|
|
|
25200
26337
|
label = source;
|
|
25201
26338
|
} else {
|
|
25202
26339
|
const absPath = resolve20(process.cwd(), source);
|
|
25203
|
-
if (!
|
|
26340
|
+
if (!existsSync44(absPath)) {
|
|
25204
26341
|
console.log(pc2.red(t("image.not_found", { path: source })));
|
|
25205
26342
|
return;
|
|
25206
26343
|
}
|
|
@@ -25279,7 +26416,7 @@ function registerMmaCommands(ctx) {
|
|
|
25279
26416
|
console.log(pc2.yellow(t("repl.wizard_running")));
|
|
25280
26417
|
await ctx.withExclusiveInput(async () => {
|
|
25281
26418
|
const answers = await runSetup(ctx.rl);
|
|
25282
|
-
const configPath =
|
|
26419
|
+
const configPath = join38(homedir16(), ".mma", "config.json");
|
|
25283
26420
|
ctx.config.provider.type = answers.provider;
|
|
25284
26421
|
ctx.config.provider.baseUrl = answers.apiBase;
|
|
25285
26422
|
ctx.config.provider.apiKey = answers.apiKey;
|
|
@@ -25333,7 +26470,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
25333
26470
|
return;
|
|
25334
26471
|
}
|
|
25335
26472
|
ctx.config.provider.type = name;
|
|
25336
|
-
const configPath =
|
|
26473
|
+
const configPath = join38(homedir16(), ".mma", "config.json");
|
|
25337
26474
|
saveConfig(ctx.config, configPath);
|
|
25338
26475
|
await ctx.agent.reconfigure(ctx.config);
|
|
25339
26476
|
console.log(pc2.green(t("repl.provider_set", { name })));
|
|
@@ -25389,7 +26526,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
25389
26526
|
return;
|
|
25390
26527
|
}
|
|
25391
26528
|
ctx.config.model = name;
|
|
25392
|
-
const configPath =
|
|
26529
|
+
const configPath = join38(homedir16(), ".mma", "config.json");
|
|
25393
26530
|
saveConfig(ctx.config, configPath);
|
|
25394
26531
|
await ctx.agent.reconfigure(ctx.config);
|
|
25395
26532
|
console.log(pc2.green(t("repl.model_set", { name })));
|
|
@@ -25414,7 +26551,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
25414
26551
|
return;
|
|
25415
26552
|
}
|
|
25416
26553
|
ctx.config.contextWindow = size;
|
|
25417
|
-
const configPath =
|
|
26554
|
+
const configPath = join38(homedir16(), ".mma", "config.json");
|
|
25418
26555
|
saveConfig(ctx.config, configPath);
|
|
25419
26556
|
await ctx.agent.reconfigure(ctx.config);
|
|
25420
26557
|
console.log(pc2.green(t("cli.context_set", { size })));
|
|
@@ -25433,10 +26570,10 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
25433
26570
|
ctx.agent.shutdown();
|
|
25434
26571
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
|
|
25435
26572
|
const { homedir: homedir17 } = await import("os");
|
|
25436
|
-
const { join:
|
|
26573
|
+
const { join: join39 } = await import("path");
|
|
25437
26574
|
const configDir = ctx.configDir;
|
|
25438
26575
|
const baseDir = ctx.baseDir;
|
|
25439
|
-
const projectConfigPath =
|
|
26576
|
+
const projectConfigPath = join39(baseDir, ".mmrc");
|
|
25440
26577
|
const freshConfig = loadConfig2({ configDir, projectConfigPath });
|
|
25441
26578
|
Object.assign(ctx.config, freshConfig);
|
|
25442
26579
|
const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
|
|
@@ -25736,14 +26873,14 @@ init_bootstrap();
|
|
|
25736
26873
|
init_config2();
|
|
25737
26874
|
init_setup();
|
|
25738
26875
|
init_i18n();
|
|
25739
|
-
import { join as
|
|
26876
|
+
import { join as join37, dirname as dirname10 } from "path";
|
|
25740
26877
|
import { homedir as homedir15 } from "os";
|
|
25741
|
-
import { existsSync as
|
|
26878
|
+
import { existsSync as existsSync42, readFileSync as readFileSync27 } from "fs";
|
|
25742
26879
|
|
|
25743
26880
|
// src/cli/security-commands.ts
|
|
25744
26881
|
init_bootstrap();
|
|
25745
26882
|
init_config2();
|
|
25746
|
-
import { join as
|
|
26883
|
+
import { join as join31 } from "path";
|
|
25747
26884
|
import { homedir as homedir12 } from "os";
|
|
25748
26885
|
|
|
25749
26886
|
// src/modules/security/security-policies.ts
|
|
@@ -26273,7 +27410,7 @@ function createSecurityCommand(program2) {
|
|
|
26273
27410
|
}
|
|
26274
27411
|
});
|
|
26275
27412
|
securityCmd.command("set-policy").argument("<preset>", t("cli.security.preset")).description(t("cli.security.set_policy")).action(async (preset) => {
|
|
26276
|
-
const configPath =
|
|
27413
|
+
const configPath = join31(homedir12(), ".mma", "config.json");
|
|
26277
27414
|
const { config: appConfig } = await bootstrap();
|
|
26278
27415
|
const validPresets = ["strict", "balanced", "permissive"];
|
|
26279
27416
|
if (!validPresets.includes(preset)) {
|
|
@@ -26288,7 +27425,7 @@ function createSecurityCommand(program2) {
|
|
|
26288
27425
|
console.log(t("cli.security.policy_description", { description: policy.description }));
|
|
26289
27426
|
});
|
|
26290
27427
|
securityCmd.command("enable-encryption").description(t("cli.security.enable_encryption")).action(async () => {
|
|
26291
|
-
const configPath =
|
|
27428
|
+
const configPath = join31(homedir12(), ".mma", "config.json");
|
|
26292
27429
|
const { config: appConfig } = await bootstrap();
|
|
26293
27430
|
appConfig.security = appConfig.security || {};
|
|
26294
27431
|
appConfig.security.sessionEncryption = {
|
|
@@ -26300,7 +27437,7 @@ function createSecurityCommand(program2) {
|
|
|
26300
27437
|
console.log(t("cli.security.encryption_enabled"));
|
|
26301
27438
|
});
|
|
26302
27439
|
securityCmd.command("disable-encryption").description(t("cli.security.disable_encryption")).action(async () => {
|
|
26303
|
-
const configPath =
|
|
27440
|
+
const configPath = join31(homedir12(), ".mma", "config.json");
|
|
26304
27441
|
const { config: appConfig } = await bootstrap();
|
|
26305
27442
|
appConfig.security = appConfig.security || {};
|
|
26306
27443
|
appConfig.security.sessionEncryption = {
|
|
@@ -26312,7 +27449,7 @@ function createSecurityCommand(program2) {
|
|
|
26312
27449
|
console.log(t("cli.security.encryption_disabled"));
|
|
26313
27450
|
});
|
|
26314
27451
|
securityCmd.command("enable-audit").description(t("cli.security.enable_audit")).action(async () => {
|
|
26315
|
-
const configPath =
|
|
27452
|
+
const configPath = join31(homedir12(), ".mma", "config.json");
|
|
26316
27453
|
const { config: appConfig } = await bootstrap();
|
|
26317
27454
|
appConfig.security = appConfig.security || {};
|
|
26318
27455
|
appConfig.security.auditNotifier = {
|
|
@@ -26326,7 +27463,7 @@ function createSecurityCommand(program2) {
|
|
|
26326
27463
|
console.log(t("cli.security.audit_enabled"));
|
|
26327
27464
|
});
|
|
26328
27465
|
securityCmd.command("disable-audit").description(t("cli.security.disable_audit")).action(async () => {
|
|
26329
|
-
const configPath =
|
|
27466
|
+
const configPath = join31(homedir12(), ".mma", "config.json");
|
|
26330
27467
|
const { config: appConfig } = await bootstrap();
|
|
26331
27468
|
appConfig.security = appConfig.security || {};
|
|
26332
27469
|
appConfig.security.auditNotifier = {
|
|
@@ -26360,16 +27497,18 @@ function createSecurityCommand(program2) {
|
|
|
26360
27497
|
// src/cli/commands.ts
|
|
26361
27498
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
26362
27499
|
function readVersion2() {
|
|
26363
|
-
const here =
|
|
27500
|
+
const here = dirname10(fileURLToPath2(import.meta.url));
|
|
26364
27501
|
const candidates = [
|
|
26365
|
-
|
|
26366
|
-
|
|
27502
|
+
join37(here, "..", "..", "package.json"),
|
|
27503
|
+
join37(here, "..", "package.json")
|
|
26367
27504
|
];
|
|
26368
27505
|
for (const p of candidates) {
|
|
26369
|
-
if (
|
|
26370
|
-
|
|
26371
|
-
|
|
26372
|
-
|
|
27506
|
+
if (existsSync42(p)) {
|
|
27507
|
+
try {
|
|
27508
|
+
const raw = JSON.parse(readFileSync27(p, "utf8"));
|
|
27509
|
+
if (raw.version)
|
|
27510
|
+
return raw.version;
|
|
27511
|
+
} catch {}
|
|
26373
27512
|
}
|
|
26374
27513
|
}
|
|
26375
27514
|
return "0.0.0";
|
|
@@ -26379,7 +27518,7 @@ function createProgram() {
|
|
|
26379
27518
|
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"));
|
|
26380
27519
|
program2.command("init").description(t("cli.init")).action(async () => {
|
|
26381
27520
|
const answers = await runSetup();
|
|
26382
|
-
const configPath =
|
|
27521
|
+
const configPath = join37(homedir15(), ".mma", "config.json");
|
|
26383
27522
|
const { config } = await bootstrap();
|
|
26384
27523
|
config.provider.type = answers.provider;
|
|
26385
27524
|
config.provider.baseUrl = answers.apiBase;
|
|
@@ -26424,7 +27563,7 @@ function createProgram() {
|
|
|
26424
27563
|
});
|
|
26425
27564
|
const configCmd = program2.command("config").description(t("cli.manage_config"));
|
|
26426
27565
|
configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
|
|
26427
|
-
const configPath =
|
|
27566
|
+
const configPath = join37(homedir15(), ".mma", "config.json");
|
|
26428
27567
|
const { config } = await bootstrap();
|
|
26429
27568
|
const keys = key.split(".");
|
|
26430
27569
|
let obj = config;
|
|
@@ -26487,7 +27626,7 @@ function createProgram() {
|
|
|
26487
27626
|
console.log(t("cli.model_hint"));
|
|
26488
27627
|
});
|
|
26489
27628
|
model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
|
|
26490
|
-
const configPath =
|
|
27629
|
+
const configPath = join37(homedir15(), ".mma", "config.json");
|
|
26491
27630
|
const { config } = await bootstrap();
|
|
26492
27631
|
config.model = name;
|
|
26493
27632
|
saveConfig(config, configPath);
|
|
@@ -26523,7 +27662,7 @@ function createProgram() {
|
|
|
26523
27662
|
await uncertify2(name, config);
|
|
26524
27663
|
});
|
|
26525
27664
|
program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
|
|
26526
|
-
const configPath =
|
|
27665
|
+
const configPath = join37(homedir15(), ".mma", "config.json");
|
|
26527
27666
|
const { config } = await bootstrap();
|
|
26528
27667
|
const contextWindow = parseInt(size, 10);
|
|
26529
27668
|
if (isNaN(contextWindow) || contextWindow < 1024) {
|
|
@@ -26541,7 +27680,7 @@ function createProgram() {
|
|
|
26541
27680
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
26542
27681
|
});
|
|
26543
27682
|
provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
|
|
26544
|
-
const configPath =
|
|
27683
|
+
const configPath = join37(homedir15(), ".mma", "config.json");
|
|
26545
27684
|
const { config } = await bootstrap();
|
|
26546
27685
|
config.provider.type = name;
|
|
26547
27686
|
saveConfig(config, configPath);
|
|
@@ -26593,8 +27732,8 @@ init_bootstrap();
|
|
|
26593
27732
|
// src/cli/repl.ts
|
|
26594
27733
|
init_colors();
|
|
26595
27734
|
import * as readline2 from "readline";
|
|
26596
|
-
import { existsSync as
|
|
26597
|
-
import { join as
|
|
27735
|
+
import { existsSync as existsSync44, readFileSync as readFileSync29, writeFileSync as writeFileSync15 } from "fs";
|
|
27736
|
+
import { join as join39, dirname as dirname12 } from "path";
|
|
26598
27737
|
import { homedir as homedir17 } from "os";
|
|
26599
27738
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
26600
27739
|
|
|
@@ -27106,16 +28245,18 @@ init_box();
|
|
|
27106
28245
|
init_i18n();
|
|
27107
28246
|
init_repl_commands();
|
|
27108
28247
|
function readVersion4() {
|
|
27109
|
-
const here =
|
|
28248
|
+
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
27110
28249
|
const candidates = [
|
|
27111
|
-
|
|
27112
|
-
|
|
28250
|
+
join39(here, "..", "..", "package.json"),
|
|
28251
|
+
join39(here, "..", "package.json")
|
|
27113
28252
|
];
|
|
27114
28253
|
for (const p of candidates) {
|
|
27115
|
-
if (
|
|
27116
|
-
|
|
27117
|
-
|
|
27118
|
-
|
|
28254
|
+
if (existsSync44(p)) {
|
|
28255
|
+
try {
|
|
28256
|
+
const raw = JSON.parse(readFileSync29(p, "utf8"));
|
|
28257
|
+
if (raw.version)
|
|
28258
|
+
return raw.version;
|
|
28259
|
+
} catch {}
|
|
27119
28260
|
}
|
|
27120
28261
|
}
|
|
27121
28262
|
return "0.0.0";
|
|
@@ -27158,16 +28299,18 @@ class Repl {
|
|
|
27158
28299
|
sessionManager;
|
|
27159
28300
|
skillsModule;
|
|
27160
28301
|
pluginManager;
|
|
27161
|
-
|
|
28302
|
+
logger;
|
|
28303
|
+
constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger) {
|
|
27162
28304
|
this.agent = agent;
|
|
27163
28305
|
this.config = config;
|
|
27164
28306
|
this.sessionManager = sessionManager;
|
|
27165
28307
|
this.skillsModule = skillsModule;
|
|
27166
28308
|
this.pluginManager = pluginManager;
|
|
27167
|
-
this.
|
|
28309
|
+
this.logger = logger;
|
|
28310
|
+
this.configDir = configDir || join39(homedir17(), ".mma");
|
|
27168
28311
|
this.baseDir = baseDir || process.cwd();
|
|
27169
28312
|
this.noAgentsMd = noAgentsMd === true;
|
|
27170
|
-
this.historyPath =
|
|
28313
|
+
this.historyPath = join39(homedir17(), ".mma", "repl-history");
|
|
27171
28314
|
this.loadHistory();
|
|
27172
28315
|
this.rl = readline2.createInterface({
|
|
27173
28316
|
input: process.stdin,
|
|
@@ -27188,7 +28331,7 @@ class Repl {
|
|
|
27188
28331
|
this.setupListeners();
|
|
27189
28332
|
}
|
|
27190
28333
|
loadHistory() {
|
|
27191
|
-
if (
|
|
28334
|
+
if (existsSync44(this.historyPath)) {
|
|
27192
28335
|
try {
|
|
27193
28336
|
const raw = readFileSync29(this.historyPath, "utf-8");
|
|
27194
28337
|
this.history = raw.split(`
|
|
@@ -27365,6 +28508,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
27365
28508
|
}
|
|
27366
28509
|
this.pendingClipboardImage = null;
|
|
27367
28510
|
}
|
|
28511
|
+
this.logger?.logREPL("user", input);
|
|
27368
28512
|
process.stdout.write(`
|
|
27369
28513
|
` + pc2.green(t("repl.agent")));
|
|
27370
28514
|
const renderer = new Renderer({
|
|
@@ -27387,6 +28531,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
27387
28531
|
renderer.flush();
|
|
27388
28532
|
process.stdout.write(`
|
|
27389
28533
|
`);
|
|
28534
|
+
this.logger?.logREPL(result.success ? "assistant" : "system", result.text?.slice(0, 400) || result.error || "");
|
|
27390
28535
|
if (!result.success) {
|
|
27391
28536
|
console.error(pc2.red(`${t("error.prefix")}${result.error}`));
|
|
27392
28537
|
}
|
|
@@ -27519,11 +28664,11 @@ ${t("image.clipboard_empty")}`));
|
|
|
27519
28664
|
row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
|
|
27520
28665
|
} else {
|
|
27521
28666
|
const agentsMdCandidates = [
|
|
27522
|
-
|
|
27523
|
-
|
|
27524
|
-
|
|
28667
|
+
join39(this.baseDir, "AGENTS.md"),
|
|
28668
|
+
join39(this.baseDir, ".mma", "AGENTS.md"),
|
|
28669
|
+
join39(this.configDir, "AGENTS.md")
|
|
27525
28670
|
];
|
|
27526
|
-
const foundAgents = agentsMdCandidates.filter((p) =>
|
|
28671
|
+
const foundAgents = agentsMdCandidates.filter((p) => existsSync44(p));
|
|
27527
28672
|
if (foundAgents.length > 0) {
|
|
27528
28673
|
for (const p of foundAgents) {
|
|
27529
28674
|
row(t("repl.agents_label"), pc2.dim(p));
|
|
@@ -27534,7 +28679,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
27534
28679
|
}
|
|
27535
28680
|
const meta = this.sessionManager?.getActiveMeta();
|
|
27536
28681
|
if (meta) {
|
|
27537
|
-
const sessionPath =
|
|
28682
|
+
const sessionPath = join39(this.configDir, "sessions", meta.id);
|
|
27538
28683
|
row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
|
|
27539
28684
|
}
|
|
27540
28685
|
const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
|
|
@@ -27560,8 +28705,8 @@ init_setup();
|
|
|
27560
28705
|
init_config2();
|
|
27561
28706
|
init_i18n();
|
|
27562
28707
|
init_colors();
|
|
27563
|
-
import { existsSync as
|
|
27564
|
-
import { join as
|
|
28708
|
+
import { existsSync as existsSync45 } from "fs";
|
|
28709
|
+
import { join as join40 } from "path";
|
|
27565
28710
|
import { homedir as homedir18 } from "os";
|
|
27566
28711
|
async function main() {
|
|
27567
28712
|
const program2 = createProgram();
|
|
@@ -27628,15 +28773,15 @@ async function main() {
|
|
|
27628
28773
|
}
|
|
27629
28774
|
agent.shutdown();
|
|
27630
28775
|
} else {
|
|
27631
|
-
const configPath =
|
|
27632
|
-
if (!
|
|
28776
|
+
const configPath = join40(homedir18(), ".mma", "config.json");
|
|
28777
|
+
if (!existsSync45(configPath)) {
|
|
27633
28778
|
console.log(pc2.yellow(`
|
|
27634
28779
|
` + t("cli.first_run") + `
|
|
27635
28780
|
`));
|
|
27636
28781
|
const answers = await runSetup();
|
|
27637
28782
|
const config2 = loadConfig({
|
|
27638
|
-
configDir:
|
|
27639
|
-
projectConfigPath: projectDir ?
|
|
28783
|
+
configDir: join40(homedir18(), ".mma"),
|
|
28784
|
+
projectConfigPath: projectDir ? join40(projectDir, ".mmrc") : join40(process.cwd(), ".mmrc")
|
|
27640
28785
|
});
|
|
27641
28786
|
config2.provider.type = answers.provider;
|
|
27642
28787
|
config2.provider.baseUrl = answers.apiBase;
|
|
@@ -27685,9 +28830,10 @@ async function main() {
|
|
|
27685
28830
|
skillsModule,
|
|
27686
28831
|
pluginManager,
|
|
27687
28832
|
configDir,
|
|
27688
|
-
baseDir
|
|
28833
|
+
baseDir,
|
|
28834
|
+
logger
|
|
27689
28835
|
} = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
27690
|
-
const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd);
|
|
28836
|
+
const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger);
|
|
27691
28837
|
repl.start();
|
|
27692
28838
|
}
|
|
27693
28839
|
}
|