micro-models-agent 0.47.1 → 0.48.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -312
- package/dist/cli/commands.js +323 -0
- package/dist/cli/completer.js +167 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/main.js +165 -0
- package/dist/cli/plugin-commands.js +36 -0
- package/dist/cli/repl-commands.js +661 -0
- package/dist/cli/repl.js +616 -0
- package/dist/cli/run-result.js +22 -0
- package/dist/cli/security-commands.js +164 -0
- package/dist/cli/setup.js +231 -0
- package/dist/config/config.js +249 -0
- package/dist/config/defaults.js +124 -0
- package/dist/config/experts.js +15 -0
- package/dist/config/index.js +3 -0
- package/dist/config/security.js +193 -0
- package/dist/config/types.js +1 -0
- package/dist/core/agent-moe.js +102 -0
- package/dist/core/agent.js +886 -0
- package/dist/core/bootstrap.js +404 -0
- package/dist/core/index.js +2 -0
- package/dist/core/prompt-builder.js +76 -0
- package/dist/core/session-logger.js +197 -0
- package/dist/core/types.js +1 -0
- package/dist/core/version.js +24 -0
- package/dist/core/workspace.js +76 -0
- package/dist/i18n/en.json +598 -0
- package/dist/i18n/index.js +46 -0
- package/dist/i18n/ru.json +598 -0
- package/dist/index.js +22 -0
- package/dist/llm/image-utils.js +143 -0
- package/dist/llm/index.js +4 -0
- package/dist/llm/model-loader.js +78 -0
- package/dist/llm/openai-compat.js +359 -0
- package/dist/llm/orchestrator.js +198 -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 +677 -358
- package/dist/migration/backup.js +45 -0
- package/dist/migration/detect.js +50 -0
- package/dist/migration/index.js +2 -0
- package/dist/modules/artifacts/store.js +61 -0
- package/dist/modules/browser/actions.js +76 -0
- package/dist/modules/browser/bridge-client.js +199 -0
- package/dist/modules/browser/bridge-path.js +10 -0
- package/dist/modules/browser/bridge-server.mjs +202 -202
- package/dist/modules/browser/cookie-store.js +24 -0
- package/dist/modules/browser/driver.js +136 -0
- package/dist/modules/browser/index.js +7 -0
- package/dist/modules/browser/module.js +29 -0
- package/dist/modules/browser/session.js +338 -0
- package/dist/modules/browser/snapshot.js +148 -0
- package/dist/modules/browser/types.js +12 -0
- package/dist/modules/certification/cli.js +174 -0
- package/dist/modules/certification/fact-checker.js +82 -0
- package/dist/modules/certification/loader.js +105 -0
- package/dist/modules/certification/manifest.js +50 -0
- package/dist/modules/certification/runner.js +159 -0
- package/dist/modules/certification/scenarios.js +124 -0
- package/dist/modules/certification/types.js +1 -0
- package/dist/modules/context/chunk-query.js +100 -0
- package/dist/modules/context/fact-extractor.js +162 -0
- package/dist/modules/context/history.js +15 -0
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +423 -0
- package/dist/modules/execution/audit-runners.js +152 -0
- package/dist/modules/execution/auditor.js +218 -0
- package/dist/modules/execution/execution-plugin.js +272 -0
- package/dist/modules/execution/index.js +8 -0
- package/dist/modules/execution/module.js +436 -0
- package/dist/modules/execution/moe-executor.js +291 -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 +157 -0
- package/dist/modules/execution/plan-tool.js +508 -0
- package/dist/modules/execution/plan-validator.js +153 -0
- package/dist/modules/execution/planner.js +90 -0
- package/dist/modules/execution/stuck-detector.js +510 -0
- package/dist/modules/execution/tracker.js +67 -0
- package/dist/modules/execution/types.js +1 -0
- package/dist/modules/execution/verifier.js +222 -0
- package/dist/modules/execution/windows-commands.js +41 -0
- package/dist/modules/hallucination/confidence.js +66 -0
- package/dist/modules/hallucination/consistency.js +26 -0
- package/dist/modules/hallucination/detector.js +43 -0
- package/dist/modules/hallucination/factual.js +129 -0
- package/dist/modules/hallucination/index.js +5 -0
- package/dist/modules/hallucination/js-identifiers.js +262 -0
- package/dist/modules/hallucination/llm-judge.js +101 -0
- package/dist/modules/index.js +5 -0
- package/dist/modules/indexer/cache.js +40 -0
- package/dist/modules/indexer/index.js +3 -0
- package/dist/modules/indexer/module.js +245 -0
- package/dist/modules/indexer/project-profile.js +183 -0
- package/dist/modules/indexer/walker.js +101 -0
- package/dist/modules/lsp/check-tool.js +58 -0
- package/dist/modules/lsp/client.js +278 -0
- package/dist/modules/lsp/command.js +60 -0
- package/dist/modules/lsp/config.js +135 -0
- package/dist/modules/lsp/index.js +3 -0
- package/dist/modules/lsp/module.js +232 -0
- package/dist/modules/lsp/probe.js +76 -0
- package/dist/modules/lsp/project-root.js +32 -0
- package/dist/modules/lsp/startup-check.js +141 -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 +142 -0
- package/dist/modules/mcp/registry.js +15 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +96 -0
- package/dist/modules/memory/search.js +42 -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 +56 -0
- package/dist/modules/pipelines/template.js +14 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +231 -0
- package/dist/modules/plugins/builtin/notify.js +9 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +70 -0
- package/dist/modules/plugins/manager.js +217 -0
- package/dist/modules/plugins/types.js +1 -0
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +2 -0
- package/dist/modules/processes/registry.js +327 -0
- package/dist/modules/processes/runner.js +23 -0
- package/dist/modules/registry.js +47 -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 +205 -0
- package/dist/modules/security/content-scanner.js +53 -0
- package/dist/modules/security/data-sanitizer.js +89 -0
- package/dist/modules/security/encryption.js +242 -0
- package/dist/modules/security/index.js +14 -0
- package/dist/modules/security/network-validator.js +71 -0
- package/dist/modules/security/path-validator.js +207 -0
- package/dist/modules/security/rate-limiter.js +119 -0
- package/dist/modules/security/security-policies.js +531 -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 +222 -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/matcher.js +27 -0
- package/dist/modules/skills/module.js +129 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/updater/checker.js +96 -0
- package/dist/modules/updater/index.js +2 -0
- package/dist/modules/updater/module.js +116 -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/skills/builtin/git.md +36 -36
- package/dist/skills/builtin/typescript.md +35 -35
- package/dist/tools/approve.js +32 -0
- package/dist/tools/attach-image.js +89 -0
- package/dist/tools/bash.js +496 -0
- package/dist/tools/browser.js +114 -0
- package/dist/tools/chunk-query.js +99 -0
- package/dist/tools/create-dir.js +55 -0
- package/dist/tools/delete-file.js +62 -0
- package/dist/tools/download-file.js +116 -0
- package/dist/tools/edit-file.js +79 -0
- package/dist/tools/enable-tools.js +58 -0
- package/dist/tools/executor.js +144 -0
- package/dist/tools/file-info.js +46 -0
- package/dist/tools/filter-tools.js +17 -0
- package/dist/tools/glob-tool.js +26 -0
- package/dist/tools/grep-tool.js +84 -0
- package/dist/tools/hidden-tools-block.js +37 -0
- package/dist/tools/index.js +78 -0
- package/dist/tools/list-dir.js +48 -0
- package/dist/tools/load-skill.js +42 -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 +36 -0
- package/dist/tools/process-log.js +45 -0
- package/dist/tools/question.js +140 -0
- package/dist/tools/read-file.js +91 -0
- package/dist/tools/recall.js +117 -0
- package/dist/tools/registry.js +47 -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 +196 -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 +81 -0
- package/dist/ui/box.js +77 -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/line-editor.js +703 -0
- package/dist/ui/line-math.js +69 -0
- package/dist/ui/md-formatter.js +212 -0
- package/dist/ui/output.js +13 -0
- package/dist/ui/plan-view.js +103 -0
- package/dist/ui/renderer.js +209 -0
- package/dist/ui/spinner.js +70 -0
- package/dist/ui/table.js +144 -0
- package/package.json +48 -48
package/dist/main.js
CHANGED
|
@@ -1942,9 +1942,9 @@ function mergeSecurityConfig(userConfig) {
|
|
|
1942
1942
|
var DEFAULT_SECURITY_CONFIG;
|
|
1943
1943
|
var init_security = __esm(() => {
|
|
1944
1944
|
DEFAULT_SECURITY_CONFIG = {
|
|
1945
|
-
enabled:
|
|
1945
|
+
enabled: true,
|
|
1946
1946
|
bash: {
|
|
1947
|
-
enabled:
|
|
1947
|
+
enabled: true,
|
|
1948
1948
|
blacklist: [
|
|
1949
1949
|
"rm",
|
|
1950
1950
|
"dd",
|
|
@@ -1983,10 +1983,24 @@ var init_security = __esm(() => {
|
|
|
1983
1983
|
"umount",
|
|
1984
1984
|
"powershell",
|
|
1985
1985
|
"pwsh",
|
|
1986
|
-
"cmd"
|
|
1986
|
+
"cmd",
|
|
1987
|
+
"bash",
|
|
1988
|
+
"sh",
|
|
1989
|
+
"zsh",
|
|
1990
|
+
"dash",
|
|
1991
|
+
"python",
|
|
1992
|
+
"python3",
|
|
1993
|
+
"node",
|
|
1994
|
+
"bun",
|
|
1995
|
+
"perl",
|
|
1996
|
+
"ruby",
|
|
1997
|
+
"del",
|
|
1998
|
+
"rd",
|
|
1999
|
+
"erase",
|
|
2000
|
+
"taskkill"
|
|
1987
2001
|
],
|
|
1988
2002
|
whitelist: [],
|
|
1989
|
-
blockDangerousFlags:
|
|
2003
|
+
blockDangerousFlags: true,
|
|
1990
2004
|
logCommands: true,
|
|
1991
2005
|
dangerousFlags: [
|
|
1992
2006
|
"--force",
|
|
@@ -2003,7 +2017,7 @@ var init_security = __esm(() => {
|
|
|
2003
2017
|
"-y",
|
|
2004
2018
|
"--no-confirm"
|
|
2005
2019
|
],
|
|
2006
|
-
dangerousOperators: [">", ">>", "2>", "2>>", "`"]
|
|
2020
|
+
dangerousOperators: [">", ">>", "2>", "2>>", ";", "&&", "||", "|", "`", "$("]
|
|
2007
2021
|
},
|
|
2008
2022
|
paths: {
|
|
2009
2023
|
enabled: false,
|
|
@@ -2026,7 +2040,7 @@ var init_security = __esm(() => {
|
|
|
2026
2040
|
allowed: []
|
|
2027
2041
|
},
|
|
2028
2042
|
network: {
|
|
2029
|
-
enabled:
|
|
2043
|
+
enabled: true,
|
|
2030
2044
|
deniedDomains: ["localhost", "127.0.0.1", "::1"],
|
|
2031
2045
|
allowedDomains: [],
|
|
2032
2046
|
requestTimeout: 15000,
|
|
@@ -2357,7 +2371,8 @@ var init_en = __esm(() => {
|
|
|
2357
2371
|
"file.deleted": "Deleted {path}",
|
|
2358
2372
|
"file.updated": "Updated {path}",
|
|
2359
2373
|
"file.written": "Written {path}",
|
|
2360
|
-
"file.notfound":
|
|
2374
|
+
"file.notfound": `File not found: {path}
|
|
2375
|
+
This does NOT mean the project is missing — the path may be wrong. Run list_dir on the working directory first; create files only after confirming what exists.`,
|
|
2361
2376
|
"file.dir_notfound": "Directory not found: {path}",
|
|
2362
2377
|
"file.path_not_allowed": "Path not allowed: {path}",
|
|
2363
2378
|
"file.path_not_allowed_short": "Path not allowed",
|
|
@@ -2369,7 +2384,8 @@ var init_en = __esm(() => {
|
|
|
2369
2384
|
Use read_file on {path} to see the current content before editing — the target may differ (whitespace, line endings) or was already replaced.`,
|
|
2370
2385
|
"file.moved": "Moved {from} → {to}",
|
|
2371
2386
|
"file.not_found_short": "Not found: {path}",
|
|
2372
|
-
"file.notfound_resolved":
|
|
2387
|
+
"file.notfound_resolved": `File not found: {path} (resolved to {resolved})
|
|
2388
|
+
The path was joined onto the working directory because it does not exist as given. Run list_dir to find the correct path.`,
|
|
2373
2389
|
"file.empty": "(empty)",
|
|
2374
2390
|
"file.no_matches": "No matches",
|
|
2375
2391
|
"file.truncated": `
|
|
@@ -2867,6 +2883,7 @@ Available commands:`,
|
|
|
2867
2883
|
"setup.security_header": `
|
|
2868
2884
|
--- Security ---`,
|
|
2869
2885
|
"setup.security_status_off": " Security: OFF (default — all commands allowed)",
|
|
2886
|
+
"setup.security_status_on": " Security: ON (balanced policy by default)",
|
|
2870
2887
|
"setup.security_configure": " Configure security? (y/N)",
|
|
2871
2888
|
"setup.security_bash_block": " Block dangerous commands (rm, sudo, ssh, etc.)? (y/N)",
|
|
2872
2889
|
"setup.security_flags_block": " Block dangerous flags (--force, -rf, etc.)? (y/N)",
|
|
@@ -2922,6 +2939,7 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
2922
2939
|
"exec.error_search_failed": 'Web search returned nothing for "{query}".',
|
|
2923
2940
|
"exec.error_search_no_query": "Error output is not meaningful — skipping the web search.",
|
|
2924
2941
|
"exec.npm_exec_hint": '"could not determine executable to run" — no "bin" for that package/script. Use "npm run <script>" (script must exist in package.json) or "bunx <pkg>" for a package that declares a bin.',
|
|
2942
|
+
"exec.task_reminder": "Task: {task}. Continue making progress — do not repeat failed actions.",
|
|
2925
2943
|
"hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
|
|
2926
2944
|
"hall.short_response": "Response too short or empty",
|
|
2927
2945
|
"hall.repetitive": "Response too repetitive ({pct}% overlap)",
|
|
@@ -3030,7 +3048,22 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
3030
3048
|
"tool.friendly.enable_tools": "Enable tools",
|
|
3031
3049
|
"cli.provider_base_hint": "Base URL set to: {baseUrl}",
|
|
3032
3050
|
"repl.cost": "Total cost: {cost}",
|
|
3033
|
-
"repl.tokens": "Tokens used: {tokens}"
|
|
3051
|
+
"repl.tokens": "Tokens used: {tokens}",
|
|
3052
|
+
"repl.ctrl_c_interrupt": `
|
|
3053
|
+
[Ctrl+C] Stopping agent... (press again to force)`,
|
|
3054
|
+
"exec.stop_directive": 'STOP. Step {stepId} ("{description}") took {iterations} iterations with no progress. DO NOT continue this step. Immediately call: plan update step={stepId} status=done (if code works despite warnings) OR plan update step={stepId} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.',
|
|
3055
|
+
"exec.test_runner_fail": "[test-runner] {framework}: {failed} test(s) FAILING, {passed} passing — do NOT mark verification steps as done while tests fail. Investigate the failures, fix the code, then re-run the tests.",
|
|
3056
|
+
"exec.test_runner_pass": "[test-runner] {framework}: all {passed} test(s) passing.",
|
|
3057
|
+
"plan.subtasks_done": "All sub-tasks done — call plan update step={stepId} status=done to complete this step.",
|
|
3058
|
+
"plan.no_subtasks": "(no sub-tasks)",
|
|
3059
|
+
"plan.step_label": "Step {stepId}: {description}",
|
|
3060
|
+
"exec.hint_deps": "Check if a lock file exists (package-lock.json, poetry.lock). If missing, run the install command first.",
|
|
3061
|
+
"exec.hint_test": "Make sure the source files exist and have real code before running tests.",
|
|
3062
|
+
"exec.hint_build": "Check that all dependencies are installed and source files are not empty.",
|
|
3063
|
+
"exec.hint_deploy": "Verify credentials and network access before deploying.",
|
|
3064
|
+
"exec.hint_repetitive": "You are calling the same tool repeatedly with the same arguments. Try a different approach.",
|
|
3065
|
+
"exec.hint_consecutive": "Multiple different tools are failing. Check if the environment is set up correctly.",
|
|
3066
|
+
"exec.hint_read_only": "You have made many read-only tool calls (read_file/glob/grep/browser) without writing anything. Stop exploring — make the edit/write the task needs, or use the plan tool to decide next steps."
|
|
3034
3067
|
};
|
|
3035
3068
|
});
|
|
3036
3069
|
|
|
@@ -3042,8 +3075,10 @@ var init_ru = __esm(() => {
|
|
|
3042
3075
|
"file.deleted": "Удалён {path}",
|
|
3043
3076
|
"file.updated": "Обновлён {path}",
|
|
3044
3077
|
"file.written": "Записан {path}",
|
|
3045
|
-
"file.notfound":
|
|
3046
|
-
|
|
3078
|
+
"file.notfound": `Файл не найден: {path}
|
|
3079
|
+
Это НЕ значит, что проект отсутствует — возможно, путь неверный. Выполни list_dir по рабочей директории; создавай файлы только после проверки того, что уже есть.`,
|
|
3080
|
+
"file.dir_notfound": `Каталог не найден: {path}
|
|
3081
|
+
Это НЕ значит, что проект отсутствует — возможно, путь неверный. Выполни list_dir по рабочей директории (и родителю этого пути), чтобы найти реальное расположение, прежде что-то создавать.`,
|
|
3047
3082
|
"file.path_not_allowed": "Путь недопустим: {path}",
|
|
3048
3083
|
"file.path_not_allowed_short": "Путь недопустим",
|
|
3049
3084
|
"file.is_directory": "Это каталог, используйте rmdir: {path}",
|
|
@@ -3550,6 +3585,7 @@ var init_ru = __esm(() => {
|
|
|
3550
3585
|
"setup.security_header": `
|
|
3551
3586
|
--- Безопасность ---`,
|
|
3552
3587
|
"setup.security_status_off": " Безопасность: ВЫКЛ (по умолчанию — все команды разрешены)",
|
|
3588
|
+
"setup.security_status_on": " Безопасность: ВКЛ (по умолчанию — политика balanced)",
|
|
3553
3589
|
"setup.security_configure": " Настроить безопасность? (y/N)",
|
|
3554
3590
|
"setup.security_bash_block": " Блокировать опасные команды (rm, sudo, ssh и т.д.)? (y/N)",
|
|
3555
3591
|
"setup.security_flags_block": " Блокировать опасные флаги (--force, -rf и т.д.)? (y/N)",
|
|
@@ -3605,6 +3641,7 @@ var init_ru = __esm(() => {
|
|
|
3605
3641
|
"exec.error_search_failed": 'Поиск в интернете для "{query}" ничего не дал.',
|
|
3606
3642
|
"exec.error_search_no_query": "Текст ошибки незначимый — поиск в интернете пропущен.",
|
|
3607
3643
|
"exec.npm_exec_hint": '"could not determine executable to run" — у пакета/скрипта нет "bin". Используй "npm run <script>" (скрипт должен быть в package.json) или "bunx <pkg>" для пакета с объявленным bin.',
|
|
3644
|
+
"exec.task_reminder": "Задача: {task}. Продолжай работу — не повторяй неудачные действия.",
|
|
3608
3645
|
"hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
|
|
3609
3646
|
"hall.short_response": "Слишком короткий или пустой ответ",
|
|
3610
3647
|
"hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
|
|
@@ -3700,7 +3737,8 @@ var init_ru = __esm(() => {
|
|
|
3700
3737
|
"ctx.delta_pos": "контекст +{tokens}",
|
|
3701
3738
|
"ctx.delta_neg": "контекст -{tokens} ↓",
|
|
3702
3739
|
"ctx.delta_zero": "контекст ±0",
|
|
3703
|
-
"file.notfound_resolved":
|
|
3740
|
+
"file.notfound_resolved": `Файл не найден: {path} (резолвится в {resolved})
|
|
3741
|
+
Путь был склеен с рабочей директорией, потому что в исходном виде он не существует. Выполни list_dir, чтобы найти корректный путь.`,
|
|
3704
3742
|
"bash.echo_write_blocked": "Запись файлов через echo/printf ненадёжна в Windows cmd.exe (кавычки и многострочность ломаются). Используй инструмент write_file вместо этого (цель: {path}).",
|
|
3705
3743
|
"updater.check_error": "[updater] Ошибка проверки обновления: {error}",
|
|
3706
3744
|
"updater.available": "[updater] Доступно обновление: {current} → {latest}. Выполните `npm install -g micro-models-agent` для обновления.",
|
|
@@ -3715,7 +3753,22 @@ var init_ru = __esm(() => {
|
|
|
3715
3753
|
"tool.friendly.enable_tools": "Включить тулы",
|
|
3716
3754
|
"cli.provider_base_hint": "Базовый URL установлен: {baseUrl}",
|
|
3717
3755
|
"repl.cost": "Итого потрачено: {cost}",
|
|
3718
|
-
"repl.tokens": "Потрачено токенов: {tokens}"
|
|
3756
|
+
"repl.tokens": "Потрачено токенов: {tokens}",
|
|
3757
|
+
"repl.ctrl_c_interrupt": `
|
|
3758
|
+
[Ctrl+C] Остановка агента... (ещё раз — принудительно)`,
|
|
3759
|
+
"exec.stop_directive": 'STOP. Step {stepId} ("{description}") took {iterations} iterations with no progress. DO NOT continue this step. Immediately call: plan update step={stepId} status=done (if code works despite warnings) OR plan update step={stepId} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.',
|
|
3760
|
+
"exec.test_runner_fail": "[test-runner] {framework}: {failed} test(s) FAILING, {passed} passing — do NOT mark verification steps as done while tests fail. Investigate the failures, fix the code, then re-run the tests.",
|
|
3761
|
+
"exec.test_runner_pass": "[test-runner] {framework}: all {passed} test(s) passing.",
|
|
3762
|
+
"plan.subtasks_done": "All sub-tasks done — call plan update step={stepId} status=done to complete this step.",
|
|
3763
|
+
"plan.no_subtasks": "(no sub-tasks)",
|
|
3764
|
+
"plan.step_label": "Step {stepId}: {description}",
|
|
3765
|
+
"exec.hint_deps": "Check if a lock file exists (package-lock.json, poetry.lock). If missing, run the install command first.",
|
|
3766
|
+
"exec.hint_test": "Make sure the source files exist and have real code before running tests.",
|
|
3767
|
+
"exec.hint_build": "Check that all dependencies are installed and source files are not empty.",
|
|
3768
|
+
"exec.hint_deploy": "Verify credentials and network access before deploying.",
|
|
3769
|
+
"exec.hint_repetitive": "You are calling the same tool repeatedly with the same arguments. Try a different approach.",
|
|
3770
|
+
"exec.hint_consecutive": "Multiple different tools are failing. Check if the environment is set up correctly.",
|
|
3771
|
+
"exec.hint_read_only": "You have made many read-only tool calls (read_file/glob/grep/browser) without writing anything. Stop exploring — make the edit/write the task needs, or use the plan tool to decide next steps."
|
|
3719
3772
|
};
|
|
3720
3773
|
});
|
|
3721
3774
|
|
|
@@ -3886,7 +3939,7 @@ function validateExpertConfig(config, allToolTags) {
|
|
|
3886
3939
|
}
|
|
3887
3940
|
|
|
3888
3941
|
// src/modules/security/encryption.ts
|
|
3889
|
-
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
|
|
3942
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
3890
3943
|
import { homedir } from "os";
|
|
3891
3944
|
import { join as join3 } from "path";
|
|
3892
3945
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3, copyFileSync as copyFileSync2 } from "fs";
|
|
@@ -3903,8 +3956,8 @@ function getOrCreateEncryptionKey(config) {
|
|
|
3903
3956
|
try {
|
|
3904
3957
|
writeFileSync2(keyPath, key.toString("base64"), { mode: 384 });
|
|
3905
3958
|
return key;
|
|
3906
|
-
} catch {
|
|
3907
|
-
|
|
3959
|
+
} catch (e) {
|
|
3960
|
+
throw new Error(`Cannot write encryption key to ${keyPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
3908
3961
|
}
|
|
3909
3962
|
}
|
|
3910
3963
|
try {
|
|
@@ -3949,7 +4002,7 @@ function decryptString(encryptedText, key) {
|
|
|
3949
4002
|
hmac.update(iv);
|
|
3950
4003
|
hmac.update(Buffer.from(encrypted, "base64"));
|
|
3951
4004
|
const computedTag = hmac.digest();
|
|
3952
|
-
if (
|
|
4005
|
+
if (computedTag.length !== authTag.length || !timingSafeEqual(computedTag, authTag)) {
|
|
3953
4006
|
throw new Error("Authentication failed: invalid tag");
|
|
3954
4007
|
}
|
|
3955
4008
|
const decipher = createDecipheriv("aes-256-cbc", encKey, iv);
|
|
@@ -4133,6 +4186,22 @@ function normalizeLspServerArgs(config) {
|
|
|
4133
4186
|
}
|
|
4134
4187
|
}
|
|
4135
4188
|
}
|
|
4189
|
+
function normalizeStaleSecurityConfig(config) {
|
|
4190
|
+
const sec = config.security;
|
|
4191
|
+
if (!sec || sec.enabled !== false)
|
|
4192
|
+
return;
|
|
4193
|
+
const bash = sec.bash;
|
|
4194
|
+
if (!bash || bash.enabled !== false || bash.blockDangerousFlags !== false)
|
|
4195
|
+
return;
|
|
4196
|
+
const LEGACY_OPERATORS = [">", ">>", "2>", "2>>", "`"];
|
|
4197
|
+
if (JSON.stringify(bash.dangerousOperators ?? []) !== JSON.stringify(LEGACY_OPERATORS))
|
|
4198
|
+
return;
|
|
4199
|
+
const defaultCmds = new Set(DEFAULT_SECURITY_CONFIG.bash.blacklist);
|
|
4200
|
+
const blacklist = Array.isArray(bash.blacklist) ? bash.blacklist : [];
|
|
4201
|
+
if (blacklist.some((cmd) => !defaultCmds.has(cmd)))
|
|
4202
|
+
return;
|
|
4203
|
+
config.security = JSON.parse(JSON.stringify(DEFAULT_SECURITY_CONFIG));
|
|
4204
|
+
}
|
|
4136
4205
|
function loadJSON(path) {
|
|
4137
4206
|
try {
|
|
4138
4207
|
if (existsSync4(path)) {
|
|
@@ -4206,6 +4275,7 @@ function loadConfig(options) {
|
|
|
4206
4275
|
config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
|
|
4207
4276
|
}
|
|
4208
4277
|
normalizeLspServerArgs(config);
|
|
4278
|
+
normalizeStaleSecurityConfig(config);
|
|
4209
4279
|
config = applyEnvVars(config);
|
|
4210
4280
|
try {
|
|
4211
4281
|
const encryptor = new ConfigEncryptor;
|
|
@@ -4568,19 +4638,19 @@ class Logger {
|
|
|
4568
4638
|
this.fileLog.cleanupOldLogs(maxDays, maxFiles);
|
|
4569
4639
|
}
|
|
4570
4640
|
logLLMRequest(model, messagesCount, promptPreview, caller) {
|
|
4571
|
-
this.fileLog.logLLMRequest(model, messagesCount, promptPreview, caller);
|
|
4641
|
+
this.fileLog.logLLMRequest(model, messagesCount, sanitizeLogMessage(promptPreview), caller);
|
|
4572
4642
|
}
|
|
4573
4643
|
logLLMResponse(model, responseLength, genTimeMs, error, caller) {
|
|
4574
4644
|
this.fileLog.logLLMResponse(model, responseLength, genTimeMs, error, caller);
|
|
4575
4645
|
}
|
|
4576
4646
|
logToolCall(tool, preview, result) {
|
|
4577
|
-
this.fileLog.logToolCall(tool, preview, result);
|
|
4647
|
+
this.fileLog.logToolCall(tool, sanitizeLogMessage(preview), result !== undefined ? sanitizeLogMessage(result) : undefined);
|
|
4578
4648
|
}
|
|
4579
4649
|
logToolOutput(tool, output, exitCode) {
|
|
4580
|
-
this.fileLog.logToolOutput(tool, output, exitCode);
|
|
4650
|
+
this.fileLog.logToolOutput(tool, sanitizeLogMessage(output), exitCode);
|
|
4581
4651
|
}
|
|
4582
4652
|
logREPL(tag, content) {
|
|
4583
|
-
this.fileLog.logREPL(tag, content);
|
|
4653
|
+
this.fileLog.logREPL(tag, sanitizeLogMessage(content));
|
|
4584
4654
|
}
|
|
4585
4655
|
child(prefix) {
|
|
4586
4656
|
const childLogger = new Logger(this.level, this.prefix ? `${this.prefix}:${prefix}` : prefix);
|
|
@@ -4651,6 +4721,24 @@ class Logger {
|
|
|
4651
4721
|
type,
|
|
4652
4722
|
meta: this.sanitizeMeta(data)
|
|
4653
4723
|
}) + `
|
|
4724
|
+
`, "utf-8");
|
|
4725
|
+
} catch {}
|
|
4726
|
+
}
|
|
4727
|
+
logSilent(level, msg, meta) {
|
|
4728
|
+
const logTarget = this.sessionDir ?? this.logDir;
|
|
4729
|
+
if (!logTarget)
|
|
4730
|
+
return;
|
|
4731
|
+
const ts = new Date().toISOString();
|
|
4732
|
+
const sanitizedMsg = sanitizeLogMessage(msg);
|
|
4733
|
+
const sanitizedMeta = meta ? this.sanitizeMeta(meta) : undefined;
|
|
4734
|
+
try {
|
|
4735
|
+
appendFileSync2(join6(logTarget, "app.jsonl"), JSON.stringify({
|
|
4736
|
+
level,
|
|
4737
|
+
ts,
|
|
4738
|
+
prefix: this.prefix,
|
|
4739
|
+
msg: sanitizedMsg,
|
|
4740
|
+
meta: sanitizedMeta ?? null
|
|
4741
|
+
}) + `
|
|
4654
4742
|
`, "utf-8");
|
|
4655
4743
|
} catch {}
|
|
4656
4744
|
}
|
|
@@ -5244,15 +5332,11 @@ class OpenAICompatProvider {
|
|
|
5244
5332
|
const streamResult = this.doStream(messages, tools, signal, options);
|
|
5245
5333
|
let hasToolCall = false;
|
|
5246
5334
|
let hasText = false;
|
|
5247
|
-
let reasoningAcc = "";
|
|
5248
5335
|
for await (const chunk of streamResult) {
|
|
5249
5336
|
if (chunk.type === "tool_call")
|
|
5250
5337
|
hasToolCall = true;
|
|
5251
5338
|
if (chunk.type === "text")
|
|
5252
5339
|
hasText = true;
|
|
5253
|
-
if (chunk.type === "reasoning" && chunk.content) {
|
|
5254
|
-
reasoningAcc += chunk.content;
|
|
5255
|
-
}
|
|
5256
5340
|
yield chunk;
|
|
5257
5341
|
}
|
|
5258
5342
|
if (!hasToolCall && !hasText) {
|
|
@@ -5297,30 +5381,7 @@ class OpenAICompatProvider {
|
|
|
5297
5381
|
maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
|
|
5298
5382
|
reasoningEffort: options?.reasoningEffort
|
|
5299
5383
|
});
|
|
5300
|
-
const headers =
|
|
5301
|
-
"Content-Type": "application/json"
|
|
5302
|
-
};
|
|
5303
|
-
if (this.config.apiKey && this.config.apiKey !== "not-needed") {
|
|
5304
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
5305
|
-
}
|
|
5306
|
-
const controller = new AbortController;
|
|
5307
|
-
let timedOut = false;
|
|
5308
|
-
const timeoutId = setTimeout(() => {
|
|
5309
|
-
timedOut = true;
|
|
5310
|
-
controller.abort();
|
|
5311
|
-
}, REQUEST_TIMEOUT_MS);
|
|
5312
|
-
const abortSignal = (() => {
|
|
5313
|
-
if (!signal)
|
|
5314
|
-
return controller.signal;
|
|
5315
|
-
try {
|
|
5316
|
-
return AbortSignal.any([controller.signal, signal]);
|
|
5317
|
-
} catch {
|
|
5318
|
-
signal.addEventListener("abort", () => controller.abort(), {
|
|
5319
|
-
once: true
|
|
5320
|
-
});
|
|
5321
|
-
return controller.signal;
|
|
5322
|
-
}
|
|
5323
|
-
})();
|
|
5384
|
+
const { headers, abortSignal, cleanup, isTimeout, flagTimeout, controller } = this.buildRequestSetup(signal);
|
|
5324
5385
|
let response;
|
|
5325
5386
|
try {
|
|
5326
5387
|
response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
|
|
@@ -5337,7 +5398,7 @@ class OpenAICompatProvider {
|
|
|
5337
5398
|
throw wrapped;
|
|
5338
5399
|
}
|
|
5339
5400
|
if (!response.ok) {
|
|
5340
|
-
|
|
5401
|
+
cleanup();
|
|
5341
5402
|
const errorText = await response.text();
|
|
5342
5403
|
const err = new Error(t("error.llm_api", {
|
|
5343
5404
|
status: response.status,
|
|
@@ -5349,7 +5410,7 @@ class OpenAICompatProvider {
|
|
|
5349
5410
|
}
|
|
5350
5411
|
const reader = response.body?.getReader();
|
|
5351
5412
|
if (!reader) {
|
|
5352
|
-
|
|
5413
|
+
cleanup();
|
|
5353
5414
|
throw new Error(t("error.no_response_body"));
|
|
5354
5415
|
}
|
|
5355
5416
|
const decoder = new TextDecoder;
|
|
@@ -5359,18 +5420,18 @@ class OpenAICompatProvider {
|
|
|
5359
5420
|
let sawDone = false;
|
|
5360
5421
|
const readIdle = () => new Promise((resolve, reject) => {
|
|
5361
5422
|
const idleTimer = setTimeout(() => {
|
|
5362
|
-
|
|
5423
|
+
flagTimeout();
|
|
5363
5424
|
controller.abort();
|
|
5364
5425
|
}, idleTimeoutMs);
|
|
5365
5426
|
reader.read().then((result) => {
|
|
5366
5427
|
clearTimeout(idleTimer);
|
|
5367
|
-
if (
|
|
5428
|
+
if (isTimeout())
|
|
5368
5429
|
reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
|
|
5369
5430
|
else
|
|
5370
5431
|
resolve(result);
|
|
5371
5432
|
}, (err) => {
|
|
5372
5433
|
clearTimeout(idleTimer);
|
|
5373
|
-
if (
|
|
5434
|
+
if (isTimeout())
|
|
5374
5435
|
reject(new Error(t("error.llm_stream_idle", { timeout: idleTimeoutMs })));
|
|
5375
5436
|
else
|
|
5376
5437
|
reject(err);
|
|
@@ -5457,20 +5518,12 @@ class OpenAICompatProvider {
|
|
|
5457
5518
|
yield { type: "done", usage };
|
|
5458
5519
|
}
|
|
5459
5520
|
} finally {
|
|
5460
|
-
|
|
5521
|
+
cleanup();
|
|
5461
5522
|
reader.releaseLock();
|
|
5462
5523
|
}
|
|
5463
5524
|
return sawDone;
|
|
5464
5525
|
}
|
|
5465
|
-
|
|
5466
|
-
const body = buildRequestBody({
|
|
5467
|
-
model: this.model,
|
|
5468
|
-
messages,
|
|
5469
|
-
tools,
|
|
5470
|
-
stream: false,
|
|
5471
|
-
maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
|
|
5472
|
-
reasoningEffort: options?.reasoningEffort
|
|
5473
|
-
});
|
|
5526
|
+
buildRequestSetup(signal) {
|
|
5474
5527
|
const headers = {
|
|
5475
5528
|
"Content-Type": "application/json"
|
|
5476
5529
|
};
|
|
@@ -5495,6 +5548,27 @@ class OpenAICompatProvider {
|
|
|
5495
5548
|
return controller.signal;
|
|
5496
5549
|
}
|
|
5497
5550
|
})();
|
|
5551
|
+
return {
|
|
5552
|
+
headers,
|
|
5553
|
+
abortSignal,
|
|
5554
|
+
cleanup: () => clearTimeout(timeoutId),
|
|
5555
|
+
isTimeout: () => timedOut,
|
|
5556
|
+
flagTimeout: () => {
|
|
5557
|
+
timedOut = true;
|
|
5558
|
+
},
|
|
5559
|
+
controller
|
|
5560
|
+
};
|
|
5561
|
+
}
|
|
5562
|
+
async doNonStreaming(messages, tools, signal, options) {
|
|
5563
|
+
const body = buildRequestBody({
|
|
5564
|
+
model: this.model,
|
|
5565
|
+
messages,
|
|
5566
|
+
tools,
|
|
5567
|
+
stream: false,
|
|
5568
|
+
maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
|
|
5569
|
+
reasoningEffort: options?.reasoningEffort
|
|
5570
|
+
});
|
|
5571
|
+
const { headers, abortSignal, cleanup, isTimeout } = this.buildRequestSetup(signal);
|
|
5498
5572
|
try {
|
|
5499
5573
|
const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
|
|
5500
5574
|
method: "POST",
|
|
@@ -5547,12 +5621,12 @@ class OpenAICompatProvider {
|
|
|
5547
5621
|
}
|
|
5548
5622
|
return chunks;
|
|
5549
5623
|
} catch (err) {
|
|
5550
|
-
if (
|
|
5624
|
+
if (isTimeout() && err?.name === "AbortError") {
|
|
5551
5625
|
throw new Error(t("error.llm_timeout", { timeout: REQUEST_TIMEOUT_MS }));
|
|
5552
5626
|
}
|
|
5553
5627
|
throw err instanceof Error ? err : new Error(String(err));
|
|
5554
5628
|
} finally {
|
|
5555
|
-
|
|
5629
|
+
cleanup();
|
|
5556
5630
|
}
|
|
5557
5631
|
}
|
|
5558
5632
|
countTokens(text) {
|
|
@@ -5920,17 +5994,76 @@ var init_executor = __esm(() => {
|
|
|
5920
5994
|
init_i18n();
|
|
5921
5995
|
});
|
|
5922
5996
|
|
|
5997
|
+
// src/tools/path-utils.ts
|
|
5998
|
+
import { resolve, normalize, dirname as dirname2, basename, sep, relative, isAbsolute } from "path";
|
|
5999
|
+
import { existsSync as existsSync7 } from "fs";
|
|
6000
|
+
function isInsideDir(targetPath, dirPath) {
|
|
6001
|
+
const norm = (p) => process.platform === "win32" ? p.toLowerCase() : p;
|
|
6002
|
+
const t2 = norm(resolve(targetPath));
|
|
6003
|
+
const d = norm(resolve(dirPath));
|
|
6004
|
+
if (t2 === d)
|
|
6005
|
+
return true;
|
|
6006
|
+
const rel = relative(d, t2);
|
|
6007
|
+
return rel.length > 0 && !isAbsolute(rel) && !rel.startsWith("..");
|
|
6008
|
+
}
|
|
6009
|
+
function matchesScopeEntry(targetResolved, entryResolved) {
|
|
6010
|
+
if (targetResolved === entryResolved)
|
|
6011
|
+
return true;
|
|
6012
|
+
return isInsideDir(targetResolved, entryResolved);
|
|
6013
|
+
}
|
|
6014
|
+
function safeResolvePath(baseDir, userPath) {
|
|
6015
|
+
const asIs = resolve(normalize(userPath));
|
|
6016
|
+
if (userPath.startsWith("/") || userPath.startsWith("\\")) {
|
|
6017
|
+
if (existsSync7(asIs) || existsSync7(dirname2(asIs)))
|
|
6018
|
+
return asIs;
|
|
6019
|
+
}
|
|
6020
|
+
const norm = normalize(userPath);
|
|
6021
|
+
const stripped = norm.replace(/^[/\\]/, "");
|
|
6022
|
+
const resolved = resolve(baseDir, stripped);
|
|
6023
|
+
if (existsSync7(resolved) || existsSync7(dirname2(resolved)))
|
|
6024
|
+
return resolved;
|
|
6025
|
+
const baseNorm = normalize(baseDir);
|
|
6026
|
+
let cur = baseNorm;
|
|
6027
|
+
while (cur && cur !== dirname2(cur)) {
|
|
6028
|
+
const name = basename(cur);
|
|
6029
|
+
if (!name) {
|
|
6030
|
+
cur = dirname2(cur);
|
|
6031
|
+
continue;
|
|
6032
|
+
}
|
|
6033
|
+
let idx = stripped.toLowerCase().indexOf(name.toLowerCase());
|
|
6034
|
+
while (idx >= 0) {
|
|
6035
|
+
const afterIdx = idx + name.length;
|
|
6036
|
+
const afterChar = stripped[afterIdx];
|
|
6037
|
+
if (afterChar && afterChar !== "\\" && afterChar !== "/") {
|
|
6038
|
+
const fixed = stripped.slice(0, afterIdx) + sep + stripped.slice(afterIdx);
|
|
6039
|
+
const fixedResolved = resolve(baseDir, normalize(fixed));
|
|
6040
|
+
if (existsSync7(fixedResolved) || existsSync7(dirname2(fixedResolved))) {
|
|
6041
|
+
return fixedResolved;
|
|
6042
|
+
}
|
|
6043
|
+
const fromParent = resolve(dirname2(cur), normalize(fixed));
|
|
6044
|
+
if (existsSync7(fromParent) || existsSync7(dirname2(fromParent))) {
|
|
6045
|
+
return fromParent;
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
idx = stripped.toLowerCase().indexOf(name.toLowerCase(), idx + 1);
|
|
6049
|
+
}
|
|
6050
|
+
cur = dirname2(cur);
|
|
6051
|
+
}
|
|
6052
|
+
return resolved;
|
|
6053
|
+
}
|
|
6054
|
+
var init_path_utils = () => {};
|
|
6055
|
+
|
|
5923
6056
|
// src/modules/security/path-validator.ts
|
|
5924
6057
|
var exports_path_validator = {};
|
|
5925
6058
|
__export(exports_path_validator, {
|
|
5926
6059
|
isPathWritable: () => isPathWritable,
|
|
5927
6060
|
isPathInScope: () => isPathInScope
|
|
5928
6061
|
});
|
|
5929
|
-
import { resolve, normalize } from "path";
|
|
6062
|
+
import { resolve as resolve2, normalize as normalize2 } from "path";
|
|
5930
6063
|
function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
5931
|
-
const baseResolved =
|
|
5932
|
-
const targetResolved =
|
|
5933
|
-
if (!targetResolved
|
|
6064
|
+
const baseResolved = resolve2(baseDir);
|
|
6065
|
+
const targetResolved = resolve2(baseDir, normalize2(targetPath));
|
|
6066
|
+
if (!isInsideDir(targetResolved, baseResolved)) {
|
|
5934
6067
|
return {
|
|
5935
6068
|
allowed: false,
|
|
5936
6069
|
reason: "Path is outside the base working directory"
|
|
@@ -5938,8 +6071,8 @@ function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
5938
6071
|
}
|
|
5939
6072
|
if (!securityConfig?.enabled) {
|
|
5940
6073
|
if (scope) {
|
|
5941
|
-
const isReadScope = scope.read_only_files.some((f) => targetResolved
|
|
5942
|
-
const isWriteScope = scope.allowed_files.some((f) => targetResolved
|
|
6074
|
+
const isReadScope = scope.read_only_files.some((f) => matchesScopeEntry(targetResolved, resolve2(baseDir, f)));
|
|
6075
|
+
const isWriteScope = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve2(baseDir, f)));
|
|
5943
6076
|
if (isWriteScope)
|
|
5944
6077
|
return { allowed: true };
|
|
5945
6078
|
if (isReadScope)
|
|
@@ -5979,8 +6112,8 @@ function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
5979
6112
|
if (!scope) {
|
|
5980
6113
|
return { allowed: true };
|
|
5981
6114
|
}
|
|
5982
|
-
const isRead = scope.read_only_files.some((f) => targetResolved
|
|
5983
|
-
const isWrite = scope.allowed_files.some((f) => targetResolved
|
|
6115
|
+
const isRead = scope.read_only_files.some((f) => matchesScopeEntry(targetResolved, resolve2(baseDir, f)));
|
|
6116
|
+
const isWrite = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve2(baseDir, f)));
|
|
5984
6117
|
if (isWrite)
|
|
5985
6118
|
return { allowed: true };
|
|
5986
6119
|
if (isRead)
|
|
@@ -5991,9 +6124,9 @@ function isPathInScope(baseDir, targetPath, scope, securityConfig) {
|
|
|
5991
6124
|
};
|
|
5992
6125
|
}
|
|
5993
6126
|
function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
5994
|
-
const baseResolved =
|
|
5995
|
-
const targetResolved =
|
|
5996
|
-
if (!targetResolved
|
|
6127
|
+
const baseResolved = resolve2(baseDir);
|
|
6128
|
+
const targetResolved = resolve2(baseDir, normalize2(targetPath));
|
|
6129
|
+
if (!isInsideDir(targetResolved, baseResolved)) {
|
|
5997
6130
|
return {
|
|
5998
6131
|
allowed: false,
|
|
5999
6132
|
reason: "Path is outside the base working directory"
|
|
@@ -6001,7 +6134,7 @@ function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
6001
6134
|
}
|
|
6002
6135
|
if (!securityConfig?.enabled) {
|
|
6003
6136
|
if (scope) {
|
|
6004
|
-
const isWriteScope = scope.allowed_files.some((f) => targetResolved
|
|
6137
|
+
const isWriteScope = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve2(baseDir, f)));
|
|
6005
6138
|
if (isWriteScope)
|
|
6006
6139
|
return { allowed: true };
|
|
6007
6140
|
return {
|
|
@@ -6039,7 +6172,7 @@ function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
6039
6172
|
if (!scope) {
|
|
6040
6173
|
return { allowed: true };
|
|
6041
6174
|
}
|
|
6042
|
-
const isWrite = scope.allowed_files.some((f) => targetResolved
|
|
6175
|
+
const isWrite = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve2(baseDir, f)));
|
|
6043
6176
|
if (isWrite)
|
|
6044
6177
|
return { allowed: true };
|
|
6045
6178
|
return {
|
|
@@ -6048,25 +6181,19 @@ function isPathWritable(baseDir, targetPath, scope, securityConfig) {
|
|
|
6048
6181
|
};
|
|
6049
6182
|
}
|
|
6050
6183
|
function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
6051
|
-
const resolvedBaseDir =
|
|
6052
|
-
const resolvedTargetPath =
|
|
6184
|
+
const resolvedBaseDir = resolve2(baseDir);
|
|
6185
|
+
const resolvedTargetPath = resolve2(targetPath);
|
|
6053
6186
|
if (pattern.endsWith("/")) {
|
|
6054
6187
|
const dirPattern = pattern.slice(0, -1);
|
|
6055
|
-
const resolvedDirPattern =
|
|
6056
|
-
|
|
6057
|
-
return true;
|
|
6058
|
-
}
|
|
6059
|
-
if (resolvedTargetPath.startsWith(resolvedDirPattern + "/") || resolvedTargetPath.startsWith(resolvedDirPattern + "\\")) {
|
|
6060
|
-
return true;
|
|
6061
|
-
}
|
|
6062
|
-
return false;
|
|
6188
|
+
const resolvedDirPattern = resolve2(baseDir, dirPattern);
|
|
6189
|
+
return isInsideDir(resolvedTargetPath, resolvedDirPattern);
|
|
6063
6190
|
}
|
|
6064
6191
|
if (pattern.includes("**/")) {
|
|
6065
6192
|
const parts = pattern.split("**/");
|
|
6066
6193
|
const prefix = parts[0];
|
|
6067
6194
|
const suffix = parts[1];
|
|
6068
|
-
const resolvedPrefix =
|
|
6069
|
-
if (!resolvedTargetPath
|
|
6195
|
+
const resolvedPrefix = resolve2(baseDir, prefix);
|
|
6196
|
+
if (!isInsideDir(resolvedTargetPath, resolvedPrefix)) {
|
|
6070
6197
|
return false;
|
|
6071
6198
|
}
|
|
6072
6199
|
const remainingPath = resolvedTargetPath.slice(resolvedPrefix.length);
|
|
@@ -6079,8 +6206,8 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
6079
6206
|
const parts = pattern.split("**");
|
|
6080
6207
|
const prefix = parts[0];
|
|
6081
6208
|
const suffix = parts[1];
|
|
6082
|
-
const resolvedPrefix =
|
|
6083
|
-
if (!resolvedTargetPath
|
|
6209
|
+
const resolvedPrefix = resolve2(baseDir, prefix);
|
|
6210
|
+
if (!isInsideDir(resolvedTargetPath, resolvedPrefix)) {
|
|
6084
6211
|
return false;
|
|
6085
6212
|
}
|
|
6086
6213
|
if (!suffix) {
|
|
@@ -6094,9 +6221,15 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
6094
6221
|
const relativePath = resolvedTargetPath.slice(resolvedBaseDir.length + 1).replace(/\\/g, "/");
|
|
6095
6222
|
return regex.test(relativePath);
|
|
6096
6223
|
}
|
|
6097
|
-
|
|
6224
|
+
const exactTarget = resolve2(baseDir, pattern);
|
|
6225
|
+
if (process.platform === "win32") {
|
|
6226
|
+
return resolvedTargetPath.toLowerCase() === exactTarget.toLowerCase();
|
|
6227
|
+
}
|
|
6228
|
+
return resolvedTargetPath === exactTarget;
|
|
6098
6229
|
}
|
|
6099
|
-
var init_path_validator = () => {
|
|
6230
|
+
var init_path_validator = __esm(() => {
|
|
6231
|
+
init_path_utils();
|
|
6232
|
+
});
|
|
6100
6233
|
|
|
6101
6234
|
// src/modules/security/audit-notifier.ts
|
|
6102
6235
|
var exports_audit_notifier = {};
|
|
@@ -6106,8 +6239,8 @@ __export(exports_audit_notifier, {
|
|
|
6106
6239
|
DEFAULT_AUDIT_NOTIFIER_CONFIG: () => DEFAULT_AUDIT_NOTIFIER_CONFIG,
|
|
6107
6240
|
AuditNotifier: () => AuditNotifier
|
|
6108
6241
|
});
|
|
6109
|
-
import { writeFileSync as writeFileSync4, appendFileSync as appendFileSync3, existsSync as
|
|
6110
|
-
import { join as join7, dirname as
|
|
6242
|
+
import { writeFileSync as writeFileSync4, appendFileSync as appendFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
6243
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
6111
6244
|
import { homedir as homedir2 } from "os";
|
|
6112
6245
|
import { readFileSync as readFileSync4 } from "fs";
|
|
6113
6246
|
|
|
@@ -6134,7 +6267,7 @@ class AuditNotifier {
|
|
|
6134
6267
|
}
|
|
6135
6268
|
ensureLogDirectory() {
|
|
6136
6269
|
if (this.config.filePath) {
|
|
6137
|
-
const dir =
|
|
6270
|
+
const dir = dirname3(this.config.filePath);
|
|
6138
6271
|
mkdirSync5(dir, { recursive: true });
|
|
6139
6272
|
}
|
|
6140
6273
|
}
|
|
@@ -6251,13 +6384,13 @@ class AuditNotifier {
|
|
|
6251
6384
|
} catch (error) {
|
|
6252
6385
|
item.retries++;
|
|
6253
6386
|
const delay = Math.pow(2, item.retries) * 1000;
|
|
6254
|
-
await new Promise((
|
|
6387
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
6255
6388
|
}
|
|
6256
6389
|
}
|
|
6257
6390
|
this.isProcessing = false;
|
|
6258
6391
|
}
|
|
6259
6392
|
readNotifications(limit = 100) {
|
|
6260
|
-
if (!this.config.filePath || !
|
|
6393
|
+
if (!this.config.filePath || !existsSync8(this.config.filePath)) {
|
|
6261
6394
|
return [];
|
|
6262
6395
|
}
|
|
6263
6396
|
try {
|
|
@@ -6331,21 +6464,21 @@ var init_audit_notifier = __esm(() => {
|
|
|
6331
6464
|
});
|
|
6332
6465
|
|
|
6333
6466
|
// src/modules/security/audit-log.ts
|
|
6334
|
-
import { existsSync as
|
|
6335
|
-
import { resolve as
|
|
6467
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, appendFileSync as appendFileSync4 } from "fs";
|
|
6468
|
+
import { resolve as resolve3, join as join8 } from "path";
|
|
6336
6469
|
import { homedir as homedir3 } from "os";
|
|
6337
6470
|
function getAuditDir() {
|
|
6338
6471
|
return _sessionAuditDir ?? _globalAuditDir;
|
|
6339
6472
|
}
|
|
6340
6473
|
function setAuditSessionDir(dir) {
|
|
6341
6474
|
_sessionAuditDir = dir;
|
|
6342
|
-
if (!
|
|
6475
|
+
if (!existsSync9(dir)) {
|
|
6343
6476
|
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6344
6477
|
}
|
|
6345
6478
|
}
|
|
6346
6479
|
function logAudit(entry) {
|
|
6347
6480
|
const dir = getAuditDir();
|
|
6348
|
-
if (!
|
|
6481
|
+
if (!existsSync9(dir)) {
|
|
6349
6482
|
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6350
6483
|
}
|
|
6351
6484
|
try {
|
|
@@ -6410,59 +6543,75 @@ function logSecurityBlock(sessionId, action, reason, details) {
|
|
|
6410
6543
|
var _globalAuditDir, _sessionAuditDir = null;
|
|
6411
6544
|
var init_audit_log = __esm(() => {
|
|
6412
6545
|
init_audit_notifier();
|
|
6413
|
-
_globalAuditDir =
|
|
6546
|
+
_globalAuditDir = resolve3(homedir3(), ".mma", "logs");
|
|
6414
6547
|
});
|
|
6415
6548
|
|
|
6416
|
-
// src/tools/
|
|
6417
|
-
import {
|
|
6418
|
-
import {
|
|
6419
|
-
function
|
|
6420
|
-
const
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
const
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
const
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
if (
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6549
|
+
// src/tools/read-file.ts
|
|
6550
|
+
import { readFileSync as readFileSync5, existsSync as existsSync10, statSync as statSync2, openSync, readSync, closeSync } from "fs";
|
|
6551
|
+
import { extname } from "path";
|
|
6552
|
+
function readLineSlice(path, offset, limit) {
|
|
6553
|
+
const fd = openSync(path, "r");
|
|
6554
|
+
try {
|
|
6555
|
+
const buf = Buffer.alloc(STREAM_CHUNK);
|
|
6556
|
+
let pos = 0;
|
|
6557
|
+
let carry = "";
|
|
6558
|
+
let lineNumber = 1;
|
|
6559
|
+
let total = 0;
|
|
6560
|
+
const collected = [];
|
|
6561
|
+
const endLine = offset + limit;
|
|
6562
|
+
while (true) {
|
|
6563
|
+
const bytes = readSync(fd, buf, 0, STREAM_CHUNK, pos);
|
|
6564
|
+
if (bytes === 0)
|
|
6565
|
+
break;
|
|
6566
|
+
pos += bytes;
|
|
6567
|
+
const chunk = carry + buf.toString("utf-8", 0, bytes);
|
|
6568
|
+
const parts = chunk.split(`
|
|
6569
|
+
`);
|
|
6570
|
+
carry = parts.pop() ?? "";
|
|
6571
|
+
for (const line of parts) {
|
|
6572
|
+
total++;
|
|
6573
|
+
if (lineNumber >= offset && lineNumber < endLine)
|
|
6574
|
+
collected.push(line);
|
|
6575
|
+
lineNumber++;
|
|
6576
|
+
}
|
|
6577
|
+
if (lineNumber >= endLine && collected.length >= limit) {
|
|
6578
|
+
const rest = [];
|
|
6579
|
+
while (true) {
|
|
6580
|
+
const n = readSync(fd, buf, 0, STREAM_CHUNK, pos);
|
|
6581
|
+
if (n === 0)
|
|
6582
|
+
break;
|
|
6583
|
+
pos += n;
|
|
6584
|
+
rest.push(Buffer.from(buf.subarray(0, n)));
|
|
6446
6585
|
}
|
|
6586
|
+
const tail = Buffer.concat([...rest, Buffer.from(carry, "utf-8")]);
|
|
6587
|
+
for (let i = 0;i < tail.length; i++)
|
|
6588
|
+
if (tail[i] === 10)
|
|
6589
|
+
total++;
|
|
6590
|
+
total++;
|
|
6591
|
+
return { selected: collected.join(`
|
|
6592
|
+
`), total };
|
|
6447
6593
|
}
|
|
6448
|
-
idx = stripped.toLowerCase().indexOf(name.toLowerCase(), idx + 1);
|
|
6449
6594
|
}
|
|
6450
|
-
|
|
6595
|
+
if (carry.length > 0 || total === 0) {
|
|
6596
|
+
total++;
|
|
6597
|
+
if (lineNumber >= offset && lineNumber < endLine)
|
|
6598
|
+
collected.push(carry);
|
|
6599
|
+
}
|
|
6600
|
+
return { selected: collected.join(`
|
|
6601
|
+
`), total };
|
|
6602
|
+
} finally {
|
|
6603
|
+
closeSync(fd);
|
|
6451
6604
|
}
|
|
6452
|
-
return resolved;
|
|
6453
6605
|
}
|
|
6454
|
-
var
|
|
6455
|
-
|
|
6456
|
-
// src/tools/read-file.ts
|
|
6457
|
-
import { readFileSync as readFileSync5, existsSync as existsSync10 } from "fs";
|
|
6458
|
-
import { extname } from "path";
|
|
6459
|
-
var DEFAULT_LIMIT = 300, readFileTool;
|
|
6606
|
+
var DEFAULT_LIMIT = 300, LARGE_FILE_BYTES, STREAM_CHUNK, readFileTool;
|
|
6460
6607
|
var init_read_file = __esm(() => {
|
|
6461
6608
|
init_i18n();
|
|
6462
6609
|
init_path_validator();
|
|
6463
6610
|
init_security();
|
|
6464
6611
|
init_audit_log();
|
|
6465
6612
|
init_path_utils();
|
|
6613
|
+
LARGE_FILE_BYTES = 1024 * 1024;
|
|
6614
|
+
STREAM_CHUNK = 256 * 1024;
|
|
6466
6615
|
readFileTool = {
|
|
6467
6616
|
name: "read_file",
|
|
6468
6617
|
icon: "\uD83D\uDC40",
|
|
@@ -6506,15 +6655,25 @@ var init_read_file = __esm(() => {
|
|
|
6506
6655
|
}) : t("file.notfound", { path });
|
|
6507
6656
|
return { success: false, output };
|
|
6508
6657
|
}
|
|
6509
|
-
const content = readFileSync5(resolved, "utf-8");
|
|
6510
|
-
const lines = content.split(`
|
|
6511
|
-
`);
|
|
6512
|
-
const total = lines.length;
|
|
6513
6658
|
const offset = args.offset || 1;
|
|
6514
6659
|
const limit = args.limit || DEFAULT_LIMIT;
|
|
6515
|
-
|
|
6516
|
-
|
|
6660
|
+
let lines;
|
|
6661
|
+
let total;
|
|
6662
|
+
let selected;
|
|
6663
|
+
if (statSync2(resolved).size > LARGE_FILE_BYTES) {
|
|
6664
|
+
const slice = readLineSlice(resolved, offset, limit);
|
|
6665
|
+
total = slice.total;
|
|
6666
|
+
selected = slice.selected;
|
|
6667
|
+
} else {
|
|
6668
|
+
const content = readFileSync5(resolved, "utf-8");
|
|
6669
|
+
lines = content.split(`
|
|
6670
|
+
`);
|
|
6671
|
+
total = lines.length;
|
|
6672
|
+
const end2 = Math.min(offset - 1 + limit, total);
|
|
6673
|
+
selected = lines.slice(offset - 1, end2).join(`
|
|
6517
6674
|
`);
|
|
6675
|
+
}
|
|
6676
|
+
const end = Math.min(offset - 1 + limit, total);
|
|
6518
6677
|
const ext = extname(resolved) || "(no extension)";
|
|
6519
6678
|
const header = t("file.read_header", {
|
|
6520
6679
|
path,
|
|
@@ -6583,6 +6742,16 @@ var init_content_scanner = __esm(() => {
|
|
|
6583
6742
|
});
|
|
6584
6743
|
|
|
6585
6744
|
// src/modules/security/session-isolation.ts
|
|
6745
|
+
var exports_session_isolation = {};
|
|
6746
|
+
__export(exports_session_isolation, {
|
|
6747
|
+
isPathInSessionScope: () => isPathInSessionScope,
|
|
6748
|
+
getSessionTempDir: () => getSessionTempDir,
|
|
6749
|
+
getSessionSecurityConfig: () => getSessionSecurityConfig,
|
|
6750
|
+
createSessionFilePath: () => createSessionFilePath,
|
|
6751
|
+
createSessionContext: () => createSessionContext,
|
|
6752
|
+
cleanupSessionTempDir: () => cleanupSessionTempDir,
|
|
6753
|
+
DEFAULT_SESSION_ISOLATION: () => DEFAULT_SESSION_ISOLATION
|
|
6754
|
+
});
|
|
6586
6755
|
import { join as join9, resolve as resolve5 } from "path";
|
|
6587
6756
|
import { homedir as homedir4 } from "os";
|
|
6588
6757
|
import { mkdirSync as mkdirSync7, existsSync as existsSync11 } from "fs";
|
|
@@ -6618,6 +6787,26 @@ function getSessionSecurityConfig(globalConfig, sessionContext) {
|
|
|
6618
6787
|
contentScan: { ...globalSecurity.contentScan, ...sessionOverrides.contentScan }
|
|
6619
6788
|
};
|
|
6620
6789
|
}
|
|
6790
|
+
function getSessionTempDir(sessionContext) {
|
|
6791
|
+
return sessionContext.tempDir;
|
|
6792
|
+
}
|
|
6793
|
+
function cleanupSessionTempDir(sessionContext) {}
|
|
6794
|
+
function createSessionFilePath(sessionContext, relativePath, useTempDir = false) {
|
|
6795
|
+
const baseDir = useTempDir ? sessionContext.tempDir : sessionContext.workingDir;
|
|
6796
|
+
return resolve5(baseDir, relativePath);
|
|
6797
|
+
}
|
|
6798
|
+
function isPathInSessionScope(sessionContext, path) {
|
|
6799
|
+
const resolvedPath = resolve5(path);
|
|
6800
|
+
const workingDir = resolve5(sessionContext.workingDir);
|
|
6801
|
+
const tempDir = resolve5(sessionContext.tempDir);
|
|
6802
|
+
if (resolvedPath.startsWith(workingDir + "/") || resolvedPath.startsWith(workingDir + "\\")) {
|
|
6803
|
+
return true;
|
|
6804
|
+
}
|
|
6805
|
+
if (resolvedPath.startsWith(tempDir + "/") || resolvedPath.startsWith(tempDir + "\\")) {
|
|
6806
|
+
return true;
|
|
6807
|
+
}
|
|
6808
|
+
return false;
|
|
6809
|
+
}
|
|
6621
6810
|
var DEFAULT_SESSION_ISOLATION;
|
|
6622
6811
|
var init_session_isolation = __esm(() => {
|
|
6623
6812
|
init_security();
|
|
@@ -7010,7 +7199,7 @@ var init_glob_tool = __esm(() => {
|
|
|
7010
7199
|
});
|
|
7011
7200
|
|
|
7012
7201
|
// src/tools/grep-tool.ts
|
|
7013
|
-
import {
|
|
7202
|
+
import { spawn } from "child_process";
|
|
7014
7203
|
import { resolve as resolve7 } from "path";
|
|
7015
7204
|
function truncateLines(output) {
|
|
7016
7205
|
const lines = output.split(`
|
|
@@ -7021,6 +7210,40 @@ function truncateLines(output) {
|
|
|
7021
7210
|
`) + `
|
|
7022
7211
|
... (${lines.length - MAX_PREVIEW_LINES} more lines)`;
|
|
7023
7212
|
}
|
|
7213
|
+
function runSearch(bin, args, cwd, timeoutMs = 30000) {
|
|
7214
|
+
return new Promise((resolvePromise, reject) => {
|
|
7215
|
+
const child = spawn(bin, args, { cwd, windowsHide: true });
|
|
7216
|
+
let out = "";
|
|
7217
|
+
let err = "";
|
|
7218
|
+
let timedOut = false;
|
|
7219
|
+
const timer = setTimeout(() => {
|
|
7220
|
+
timedOut = true;
|
|
7221
|
+
child.kill();
|
|
7222
|
+
}, timeoutMs);
|
|
7223
|
+
child.stdout?.on("data", (d) => {
|
|
7224
|
+
out += d;
|
|
7225
|
+
});
|
|
7226
|
+
child.stderr?.on("data", (d) => {
|
|
7227
|
+
err += d;
|
|
7228
|
+
});
|
|
7229
|
+
child.on("error", (e) => {
|
|
7230
|
+
clearTimeout(timer);
|
|
7231
|
+
reject(e);
|
|
7232
|
+
});
|
|
7233
|
+
child.on("close", (code) => {
|
|
7234
|
+
clearTimeout(timer);
|
|
7235
|
+
if (timedOut) {
|
|
7236
|
+
reject(new Error(`search timed out after ${timeoutMs}ms`));
|
|
7237
|
+
} else if (code === 1) {
|
|
7238
|
+
resolvePromise("");
|
|
7239
|
+
} else if (code === 0) {
|
|
7240
|
+
resolvePromise(out);
|
|
7241
|
+
} else {
|
|
7242
|
+
reject(new Error(err || `exit ${code}`));
|
|
7243
|
+
}
|
|
7244
|
+
});
|
|
7245
|
+
});
|
|
7246
|
+
}
|
|
7024
7247
|
var grepTool;
|
|
7025
7248
|
var init_grep_tool = __esm(() => {
|
|
7026
7249
|
init_i18n();
|
|
@@ -7054,28 +7277,23 @@ var init_grep_tool = __esm(() => {
|
|
|
7054
7277
|
}
|
|
7055
7278
|
logBashCommand(ctx.sessionId, `rg ${rgArgs.join(" ")}`, false, "grep-tool");
|
|
7056
7279
|
try {
|
|
7057
|
-
const output =
|
|
7058
|
-
encoding: "utf-8",
|
|
7059
|
-
maxBuffer: 1024 * 1024,
|
|
7060
|
-
cwd: ctx.baseDir
|
|
7061
|
-
});
|
|
7280
|
+
const output = await runSearch("rg", rgArgs, ctx.baseDir);
|
|
7062
7281
|
return {
|
|
7063
7282
|
success: true,
|
|
7064
7283
|
output: truncateLines(output || t("file.no_matches"))
|
|
7065
7284
|
};
|
|
7066
7285
|
} catch (e) {
|
|
7067
|
-
if (e.
|
|
7286
|
+
if (e.code === "ENOENT") {} else if (/timed out/.test(e.message)) {
|
|
7287
|
+
return { success: false, output: t("error.grep_failed", { message: e.message }) };
|
|
7288
|
+
} else if (e.status === 1) {
|
|
7068
7289
|
return { success: true, output: t("file.no_matches") };
|
|
7290
|
+
}
|
|
7069
7291
|
try {
|
|
7070
7292
|
const grepArgs = ["-rn", pattern, searchPath];
|
|
7071
7293
|
if (args.include) {
|
|
7072
7294
|
grepArgs.push("--include", String(args.include));
|
|
7073
7295
|
}
|
|
7074
|
-
const output =
|
|
7075
|
-
encoding: "utf-8",
|
|
7076
|
-
maxBuffer: 1024 * 1024,
|
|
7077
|
-
cwd: ctx.baseDir
|
|
7078
|
-
});
|
|
7296
|
+
const output = await runSearch("grep", grepArgs, ctx.baseDir);
|
|
7079
7297
|
return {
|
|
7080
7298
|
success: true,
|
|
7081
7299
|
output: truncateLines(output || t("file.no_matches"))
|
|
@@ -7094,7 +7312,7 @@ var init_grep_tool = __esm(() => {
|
|
|
7094
7312
|
});
|
|
7095
7313
|
|
|
7096
7314
|
// src/tools/list-dir.ts
|
|
7097
|
-
import { readdirSync as readdirSync4, statSync as
|
|
7315
|
+
import { readdirSync as readdirSync4, statSync as statSync3, existsSync as existsSync13 } from "fs";
|
|
7098
7316
|
import { resolve as resolve8 } from "path";
|
|
7099
7317
|
var listDirTool;
|
|
7100
7318
|
var init_list_dir = __esm(() => {
|
|
@@ -7132,7 +7350,7 @@ var init_list_dir = __esm(() => {
|
|
|
7132
7350
|
const entries = readdirSync4(resolved);
|
|
7133
7351
|
const lines = entries.map((e) => {
|
|
7134
7352
|
const full = resolve8(resolved, e);
|
|
7135
|
-
return
|
|
7353
|
+
return statSync3(full).isDirectory() ? `${e}/` : e;
|
|
7136
7354
|
});
|
|
7137
7355
|
if (lines.length === 0) {
|
|
7138
7356
|
return { success: true, output: t("file.empty") };
|
|
@@ -7200,7 +7418,7 @@ var init_create_dir = __esm(() => {
|
|
|
7200
7418
|
});
|
|
7201
7419
|
|
|
7202
7420
|
// src/tools/delete-file.ts
|
|
7203
|
-
import { unlinkSync as unlinkSync3, existsSync as existsSync15, statSync as
|
|
7421
|
+
import { unlinkSync as unlinkSync3, existsSync as existsSync15, statSync as statSync4, readFileSync as readFileSync8 } from "fs";
|
|
7204
7422
|
var deleteFileTool;
|
|
7205
7423
|
var init_delete_file = __esm(() => {
|
|
7206
7424
|
init_i18n();
|
|
@@ -7247,7 +7465,7 @@ var init_delete_file = __esm(() => {
|
|
|
7247
7465
|
if (!existsSync15(resolved)) {
|
|
7248
7466
|
return { success: false, output: t("file.notfound", { path }) };
|
|
7249
7467
|
}
|
|
7250
|
-
if (
|
|
7468
|
+
if (statSync4(resolved).isDirectory()) {
|
|
7251
7469
|
return { success: false, output: t("file.is_directory", { path }) };
|
|
7252
7470
|
}
|
|
7253
7471
|
const content = readFileSync8(resolved, "utf-8");
|
|
@@ -7344,7 +7562,7 @@ var init_move_file = __esm(() => {
|
|
|
7344
7562
|
});
|
|
7345
7563
|
|
|
7346
7564
|
// src/tools/file-info.ts
|
|
7347
|
-
import { statSync as
|
|
7565
|
+
import { statSync as statSync5, existsSync as existsSync17 } from "fs";
|
|
7348
7566
|
var fileInfoTool;
|
|
7349
7567
|
var init_file_info = __esm(() => {
|
|
7350
7568
|
init_i18n();
|
|
@@ -7378,7 +7596,7 @@ var init_file_info = __esm(() => {
|
|
|
7378
7596
|
if (!existsSync17(resolved)) {
|
|
7379
7597
|
return { success: false, output: t("file.not_found_short", { path }) };
|
|
7380
7598
|
}
|
|
7381
|
-
const stat =
|
|
7599
|
+
const stat = statSync5(resolved);
|
|
7382
7600
|
return {
|
|
7383
7601
|
success: true,
|
|
7384
7602
|
output: JSON.stringify({
|
|
@@ -7450,6 +7668,16 @@ function isCommandAllowed(command, securityConfig) {
|
|
|
7450
7668
|
reason: `Shell wrapper "${baseForShellCheck}" is blocked — use the bash tool directly`
|
|
7451
7669
|
};
|
|
7452
7670
|
}
|
|
7671
|
+
const INTERPRETER_FLAGS = /(?:^|\s)(?:bash|sh|zsh|dash|python|python3|node|bun|perl|ruby)\s+(?:-c|-e|--command|--eval)\b/;
|
|
7672
|
+
if (INTERPRETER_FLAGS.test(trimmedCommand)) {
|
|
7673
|
+
return { allowed: false, reason: "Interpreter -c/-e invocation is blocked" };
|
|
7674
|
+
}
|
|
7675
|
+
if (/\r?\n/.test(trimmedCommand)) {
|
|
7676
|
+
return { allowed: false, reason: "Multi-line commands are blocked" };
|
|
7677
|
+
}
|
|
7678
|
+
if (/[;&|]/.test(trimmedCommand) && /(^|[;&|\s])(rm|del|rd|dd|chmod|chown|wget|curl|nc|netcat|shutdown|reboot|mkfs|fdisk)\b/.test(trimmedCommand)) {
|
|
7679
|
+
return { allowed: false, reason: "Command chaining to a dangerous command is blocked" };
|
|
7680
|
+
}
|
|
7453
7681
|
if (config.blockDangerousFlags) {
|
|
7454
7682
|
for (const op of config.dangerousOperators || []) {
|
|
7455
7683
|
if (containsOperator(trimmedCommand, op)) {
|
|
@@ -7550,7 +7778,7 @@ var init_command_validator = __esm(() => {
|
|
|
7550
7778
|
});
|
|
7551
7779
|
|
|
7552
7780
|
// src/modules/processes/registry.ts
|
|
7553
|
-
import { spawn, spawnSync } from "child_process";
|
|
7781
|
+
import { spawn as spawn2, spawnSync } from "child_process";
|
|
7554
7782
|
import { platform } from "os";
|
|
7555
7783
|
function getOemDecoder() {
|
|
7556
7784
|
if (oemDecoder !== undefined)
|
|
@@ -7646,7 +7874,7 @@ class ProcessRegistry {
|
|
|
7646
7874
|
this.procs.set(id, entry);
|
|
7647
7875
|
let child;
|
|
7648
7876
|
try {
|
|
7649
|
-
child =
|
|
7877
|
+
child = spawn2(command, {
|
|
7650
7878
|
cwd,
|
|
7651
7879
|
shell: true,
|
|
7652
7880
|
windowsHide: true,
|
|
@@ -8071,7 +8299,7 @@ ${redirected.output}`
|
|
|
8071
8299
|
const workdir = args.workdir ? String(args.workdir) : ctx.baseDir;
|
|
8072
8300
|
const appConfig = ctx.config || {};
|
|
8073
8301
|
const fullSecurityConfig = ctx.sessionContext ? getSessionSecurityConfig(appConfig, ctx.sessionContext) : appConfig.security || DEFAULT_SECURITY_CONFIG;
|
|
8074
|
-
const securityConfig = fullSecurityConfig.bash || DEFAULT_SECURITY_CONFIG.bash;
|
|
8302
|
+
const securityConfig = fullSecurityConfig.enabled === false ? { ...fullSecurityConfig.bash || DEFAULT_SECURITY_CONFIG.bash, enabled: false } : fullSecurityConfig.bash || DEFAULT_SECURITY_CONFIG.bash;
|
|
8075
8303
|
const validation = isCommandAllowed(command, securityConfig);
|
|
8076
8304
|
if (!validation.allowed) {
|
|
8077
8305
|
logSecurityBlock(ctx.sessionId, "bash_command", validation.reason || "Command blocked by security policy", sanitizeCommandForLog(originalCommand));
|
|
@@ -8111,11 +8339,15 @@ Hint: ${cliHint}`;
|
|
|
8111
8339
|
}
|
|
8112
8340
|
const testRun = detectTestResults(output2);
|
|
8113
8341
|
if (testRun && testRun.failed > 0) {
|
|
8114
|
-
output2 =
|
|
8342
|
+
output2 = t("exec.test_runner_fail", {
|
|
8343
|
+
framework: testRun.framework,
|
|
8344
|
+
failed: String(testRun.failed),
|
|
8345
|
+
passed: String(testRun.passed)
|
|
8346
|
+
}) + `
|
|
8115
8347
|
|
|
8116
|
-
|
|
8348
|
+
${output2}`;
|
|
8117
8349
|
} else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
|
|
8118
|
-
output2 =
|
|
8350
|
+
output2 = `${t("exec.test_runner_pass", { framework: testRun.framework, passed: String(testRun.passed) })}
|
|
8119
8351
|
|
|
8120
8352
|
${output2}`;
|
|
8121
8353
|
}
|
|
@@ -8432,15 +8664,16 @@ class SessionLogger {
|
|
|
8432
8664
|
});
|
|
8433
8665
|
}
|
|
8434
8666
|
logUser(content) {
|
|
8667
|
+
const sanitized = sanitizeLogMessage(content);
|
|
8435
8668
|
this.session?.appendMessage({
|
|
8436
8669
|
role: "user",
|
|
8437
|
-
content,
|
|
8670
|
+
content: sanitized,
|
|
8438
8671
|
timestamp: new Date().toISOString()
|
|
8439
8672
|
});
|
|
8440
8673
|
this.session?.appendLog({
|
|
8441
8674
|
ts: new Date().toISOString(),
|
|
8442
8675
|
type: "user",
|
|
8443
|
-
content
|
|
8676
|
+
content: sanitized
|
|
8444
8677
|
});
|
|
8445
8678
|
}
|
|
8446
8679
|
logAssistant(content, reasoning, toolCalls, iteration) {
|
|
@@ -8488,14 +8721,14 @@ class SessionLogger {
|
|
|
8488
8721
|
type: "tool_call",
|
|
8489
8722
|
tool: call.name,
|
|
8490
8723
|
tool_call_id: call.id,
|
|
8491
|
-
args: call.arguments,
|
|
8724
|
+
args: JSON.parse(sanitizeLogMessage(JSON.stringify(call.arguments))),
|
|
8492
8725
|
iteration
|
|
8493
8726
|
});
|
|
8494
8727
|
}
|
|
8495
8728
|
logToolResult(call, result, duration, iteration) {
|
|
8496
8729
|
this.session?.appendMessage({
|
|
8497
8730
|
role: "tool",
|
|
8498
|
-
content: result.output.slice(0, 500),
|
|
8731
|
+
content: sanitizeLogMessage(result.output.slice(0, 500)),
|
|
8499
8732
|
name: call.name,
|
|
8500
8733
|
timestamp: new Date().toISOString()
|
|
8501
8734
|
});
|
|
@@ -8505,7 +8738,7 @@ class SessionLogger {
|
|
|
8505
8738
|
tool: call.name,
|
|
8506
8739
|
tool_call_id: call.id,
|
|
8507
8740
|
success: result.success,
|
|
8508
|
-
content: result.output.slice(0, 1000),
|
|
8741
|
+
content: sanitizeLogMessage(result.output.slice(0, 1000)),
|
|
8509
8742
|
diff: result.diff,
|
|
8510
8743
|
duration,
|
|
8511
8744
|
iteration
|
|
@@ -8610,6 +8843,7 @@ class SessionLogger {
|
|
|
8610
8843
|
}
|
|
8611
8844
|
var init_session_logger = __esm(() => {
|
|
8612
8845
|
init_audit_log();
|
|
8846
|
+
init_data_sanitizer();
|
|
8613
8847
|
});
|
|
8614
8848
|
|
|
8615
8849
|
// node_modules/jsonrepair/lib/esm/utils/JSONRepairError.js
|
|
@@ -9950,26 +10184,26 @@ class StuckDetector {
|
|
|
9950
10184
|
const hints = [];
|
|
9951
10185
|
const desc = this.currentStepDescription.toLowerCase();
|
|
9952
10186
|
if (desc.includes("install") || desc.includes("npm") || desc.includes("pip")) {
|
|
9953
|
-
hints.push("
|
|
10187
|
+
hints.push(t("exec.hint_deps"));
|
|
9954
10188
|
}
|
|
9955
10189
|
if (desc.includes("test") || desc.includes("spec")) {
|
|
9956
|
-
hints.push("
|
|
10190
|
+
hints.push(t("exec.hint_test"));
|
|
9957
10191
|
}
|
|
9958
10192
|
if (desc.includes("build") || desc.includes("compile")) {
|
|
9959
|
-
hints.push("
|
|
10193
|
+
hints.push(t("exec.hint_build"));
|
|
9960
10194
|
}
|
|
9961
10195
|
if (desc.includes("deploy") || desc.includes("publish")) {
|
|
9962
|
-
hints.push("
|
|
10196
|
+
hints.push(t("exec.hint_deploy"));
|
|
9963
10197
|
}
|
|
9964
10198
|
const primary = this.getPrimaryReason();
|
|
9965
10199
|
if (this.hasRepetitiveToolCalls() && primary !== "repetitive") {
|
|
9966
|
-
hints.push("
|
|
10200
|
+
hints.push(t("exec.hint_repetitive"));
|
|
9967
10201
|
}
|
|
9968
10202
|
if (this.hasConsecutiveFailures() && primary !== "consecutive") {
|
|
9969
|
-
hints.push("
|
|
10203
|
+
hints.push(t("exec.hint_consecutive"));
|
|
9970
10204
|
}
|
|
9971
10205
|
if (this.hasReadOnlyLoop() && primary !== "read-only") {
|
|
9972
|
-
hints.push("
|
|
10206
|
+
hints.push(t("exec.hint_read_only"));
|
|
9973
10207
|
}
|
|
9974
10208
|
return hints;
|
|
9975
10209
|
}
|
|
@@ -10264,7 +10498,7 @@ var init_stuck_detector = __esm(() => {
|
|
|
10264
10498
|
|
|
10265
10499
|
// src/modules/artifacts/store.ts
|
|
10266
10500
|
import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
10267
|
-
import { resolve as resolve10, relative, isAbsolute } from "node:path";
|
|
10501
|
+
import { resolve as resolve10, relative as relative2, isAbsolute as isAbsolute2 } from "node:path";
|
|
10268
10502
|
|
|
10269
10503
|
class ArtifactStore {
|
|
10270
10504
|
root;
|
|
@@ -10281,10 +10515,10 @@ class ArtifactStore {
|
|
|
10281
10515
|
return cleaned;
|
|
10282
10516
|
}
|
|
10283
10517
|
static isInside(root, candidate) {
|
|
10284
|
-
const rel =
|
|
10518
|
+
const rel = relative2(root, candidate);
|
|
10285
10519
|
if (rel === "")
|
|
10286
10520
|
return true;
|
|
10287
|
-
return !rel.startsWith("..") && !
|
|
10521
|
+
return !rel.startsWith("..") && !isAbsolute2(rel);
|
|
10288
10522
|
}
|
|
10289
10523
|
save(name, content, ext = "md") {
|
|
10290
10524
|
const safe = ArtifactStore.sanitizeName(name);
|
|
@@ -10383,7 +10617,6 @@ async function executeSubtask(subtask, deps, _sharedContext) {
|
|
|
10383
10617
|
}
|
|
10384
10618
|
const maxAttempts = expertConfig.max_attempts || 3;
|
|
10385
10619
|
let lastError = "";
|
|
10386
|
-
let lastResult = null;
|
|
10387
10620
|
const stuckDetector = new StuckDetector(maxAttempts * 2, maxAttempts);
|
|
10388
10621
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
10389
10622
|
stuckDetector.recordIteration(1);
|
|
@@ -10447,7 +10680,6 @@ ${(subtask.success_criteria || []).map((c) => `- ${c}`).join(`
|
|
|
10447
10680
|
};
|
|
10448
10681
|
}
|
|
10449
10682
|
lastError = result.output;
|
|
10450
|
-
lastResult = result;
|
|
10451
10683
|
stuckDetector.recordToolError("subagent", result.output);
|
|
10452
10684
|
if (attempt < maxAttempts) {
|
|
10453
10685
|
const hints2 = stuckDetector.getActionableHints();
|
|
@@ -10566,21 +10798,36 @@ class MoEExecutor {
|
|
|
10566
10798
|
}
|
|
10567
10799
|
for (let waveIdx = 0;waveIdx < waves.length; waveIdx++) {
|
|
10568
10800
|
const wave = waves[waveIdx];
|
|
10569
|
-
const
|
|
10801
|
+
const maxParallel = this.deps.config?.security?.rateLimits?.maxParallelTasks ?? 5;
|
|
10802
|
+
let active = 0;
|
|
10803
|
+
const waiters = [];
|
|
10804
|
+
const acquire = () => active < maxParallel ? Promise.resolve(active++) : new Promise((resolve11) => waiters.push(() => resolve11(active++)));
|
|
10805
|
+
const release = () => {
|
|
10806
|
+
active--;
|
|
10807
|
+
const next = waiters.shift();
|
|
10808
|
+
if (next)
|
|
10809
|
+
next();
|
|
10810
|
+
};
|
|
10811
|
+
const wavePromises = wave.map(async (subtask) => {
|
|
10812
|
+
await acquire();
|
|
10813
|
+
let result;
|
|
10814
|
+
try {
|
|
10815
|
+
result = await executeSubtask(subtask, this.deps, plan.shared_context);
|
|
10816
|
+
} catch (e) {
|
|
10817
|
+
result = {
|
|
10818
|
+
subtaskId: subtask.id,
|
|
10819
|
+
success: false,
|
|
10820
|
+
summary: `Unhandled error: ${subtask.id}`,
|
|
10821
|
+
result: "",
|
|
10822
|
+
error: e instanceof Error ? e.message : String(e),
|
|
10823
|
+
durationMs: 0
|
|
10824
|
+
};
|
|
10825
|
+
} finally {
|
|
10826
|
+
release();
|
|
10827
|
+
}
|
|
10570
10828
|
results.push(result);
|
|
10571
10829
|
return result;
|
|
10572
|
-
})
|
|
10573
|
-
const errResult = {
|
|
10574
|
-
subtaskId: subtask.id,
|
|
10575
|
-
success: false,
|
|
10576
|
-
summary: `Unhandled error: ${subtask.id}`,
|
|
10577
|
-
result: "",
|
|
10578
|
-
error: e.message,
|
|
10579
|
-
durationMs: 0
|
|
10580
|
-
};
|
|
10581
|
-
results.push(errResult);
|
|
10582
|
-
return errResult;
|
|
10583
|
-
}));
|
|
10830
|
+
});
|
|
10584
10831
|
await Promise.all(wavePromises);
|
|
10585
10832
|
}
|
|
10586
10833
|
const failed = results.filter((r) => !r.success);
|
|
@@ -10589,9 +10836,6 @@ class MoEExecutor {
|
|
|
10589
10836
|
}
|
|
10590
10837
|
return { success: failed.length === 0, results, errors, warnings };
|
|
10591
10838
|
}
|
|
10592
|
-
getChainMaxAttempts() {
|
|
10593
|
-
return 3;
|
|
10594
|
-
}
|
|
10595
10839
|
}
|
|
10596
10840
|
var TRANSIENT_ERROR_PATTERNS;
|
|
10597
10841
|
var init_moe_executor = __esm(() => {
|
|
@@ -11179,7 +11423,7 @@ var init_auditor = __esm(() => {
|
|
|
11179
11423
|
// src/modules/execution/verifier.ts
|
|
11180
11424
|
import { existsSync as existsSync23 } from "fs";
|
|
11181
11425
|
import { resolve as resolve13, extname as extname2, join as join14 } from "path";
|
|
11182
|
-
import { spawn as
|
|
11426
|
+
import { spawn as spawn3 } from "child_process";
|
|
11183
11427
|
|
|
11184
11428
|
class StepVerifier {
|
|
11185
11429
|
baseDir;
|
|
@@ -11341,7 +11585,7 @@ class StepVerifier {
|
|
|
11341
11585
|
}
|
|
11342
11586
|
runAsync(command, cwd, timeoutMs) {
|
|
11343
11587
|
return new Promise((resolve14, reject) => {
|
|
11344
|
-
const child =
|
|
11588
|
+
const child = spawn3(command, {
|
|
11345
11589
|
cwd,
|
|
11346
11590
|
shell: true,
|
|
11347
11591
|
windowsHide: true,
|
|
@@ -11664,6 +11908,9 @@ class Agent {
|
|
|
11664
11908
|
get contextManager() {
|
|
11665
11909
|
return this.deps.contextManager;
|
|
11666
11910
|
}
|
|
11911
|
+
getModule(name) {
|
|
11912
|
+
return this.deps.moduleRegistry?.get(name);
|
|
11913
|
+
}
|
|
11667
11914
|
setScope() {
|
|
11668
11915
|
if (this.deps.scope) {
|
|
11669
11916
|
this.deps.toolExecutor.setScope(this.deps.scope);
|
|
@@ -12397,7 +12644,6 @@ ${warnLine}
|
|
|
12397
12644
|
});
|
|
12398
12645
|
this.deps.llmProvider = newProvider;
|
|
12399
12646
|
this.deps.toolExecutor.updateProvider(newProvider);
|
|
12400
|
-
this.deps.toolExecutor.ctx.llmProvider = newProvider;
|
|
12401
12647
|
const newTokenCounter = new TokenCounter2(config.model);
|
|
12402
12648
|
this.deps.contextManager.resize(config.contextWindow, config.contextBudget, newTokenCounter);
|
|
12403
12649
|
this.deps.config = config;
|
|
@@ -12734,6 +12980,15 @@ class ContextManager {
|
|
|
12734
12980
|
getMessageCount() {
|
|
12735
12981
|
return this.messages.length;
|
|
12736
12982
|
}
|
|
12983
|
+
tokenCache = new WeakMap;
|
|
12984
|
+
estimateMessageTokensCached(m) {
|
|
12985
|
+
let t2 = this.tokenCache.get(m);
|
|
12986
|
+
if (t2 === undefined) {
|
|
12987
|
+
t2 = this.estimateMessageTokens(m);
|
|
12988
|
+
this.tokenCache.set(m, t2);
|
|
12989
|
+
}
|
|
12990
|
+
return t2;
|
|
12991
|
+
}
|
|
12737
12992
|
estimateMessageTokens(m) {
|
|
12738
12993
|
const text = getMessageText(m.content);
|
|
12739
12994
|
if (this.tokenCounter) {
|
|
@@ -12778,7 +13033,7 @@ class ContextManager {
|
|
|
12778
13033
|
needsCompaction() {
|
|
12779
13034
|
if (this.iterationsSinceCompaction >= COMPACTION_INTERVAL)
|
|
12780
13035
|
return true;
|
|
12781
|
-
const totalTokens = this.messages.reduce((sum, m) => sum + this.
|
|
13036
|
+
const totalTokens = this.messages.reduce((sum, m) => sum + this.estimateMessageTokensCached(m), 0);
|
|
12782
13037
|
return totalTokens > this.budget.history * this.compactionThreshold;
|
|
12783
13038
|
}
|
|
12784
13039
|
compact() {
|
|
@@ -12889,7 +13144,7 @@ ${lines.join(`
|
|
|
12889
13144
|
this.sessionMission = "";
|
|
12890
13145
|
}
|
|
12891
13146
|
getEstimatedTokens() {
|
|
12892
|
-
return this.messages.reduce((sum, m) => sum + this.
|
|
13147
|
+
return this.messages.reduce((sum, m) => sum + this.estimateMessageTokensCached(m), 0) + this.toolTokens;
|
|
12893
13148
|
}
|
|
12894
13149
|
setToolTokens(tokens) {
|
|
12895
13150
|
this.toolTokens = tokens;
|
|
@@ -13000,7 +13255,7 @@ var init_confidence = __esm(() => {
|
|
|
13000
13255
|
|
|
13001
13256
|
// src/modules/hallucination/factual.ts
|
|
13002
13257
|
import { existsSync as existsSync24, readdirSync as readdirSync7 } from "fs";
|
|
13003
|
-
import { resolve as resolve14, isAbsolute as
|
|
13258
|
+
import { resolve as resolve14, isAbsolute as isAbsolute3, join as join15 } from "path";
|
|
13004
13259
|
|
|
13005
13260
|
class FactualCheck {
|
|
13006
13261
|
baseDir;
|
|
@@ -13039,7 +13294,7 @@ class FactualCheck {
|
|
|
13039
13294
|
return { status: "pass" };
|
|
13040
13295
|
}
|
|
13041
13296
|
pathExists(fp) {
|
|
13042
|
-
if (
|
|
13297
|
+
if (isAbsolute3(fp))
|
|
13043
13298
|
return existsSync24(fp);
|
|
13044
13299
|
if (this.knownFiles.has(fp))
|
|
13045
13300
|
return true;
|
|
@@ -13705,7 +13960,6 @@ var init_subagent = __esm(() => {
|
|
|
13705
13960
|
const fullTask = context ? `${task}
|
|
13706
13961
|
|
|
13707
13962
|
Context: ${context}` : task;
|
|
13708
|
-
let scopeRestored = false;
|
|
13709
13963
|
try {
|
|
13710
13964
|
const result = await subAgent.run(fullTask);
|
|
13711
13965
|
if (!result.success) {
|
|
@@ -13742,9 +13996,7 @@ ${result.text}
|
|
|
13742
13996
|
Iterations: ${result.iterationCount}`
|
|
13743
13997
|
};
|
|
13744
13998
|
} finally {
|
|
13745
|
-
|
|
13746
|
-
ctx.toolExecutor.setScope(parentScope || { allowed_files: [], read_only_files: [] });
|
|
13747
|
-
}
|
|
13999
|
+
ctx.toolExecutor.setScope(parentScope);
|
|
13748
14000
|
}
|
|
13749
14001
|
} catch (e) {
|
|
13750
14002
|
return { success: false, output: `Sub-agent error: ${e.message}` };
|
|
@@ -13758,20 +14010,22 @@ import { resolve as resolve15, normalize as normalize4 } from "path";
|
|
|
13758
14010
|
function isPathInScope2(baseDir, targetPath, scope) {
|
|
13759
14011
|
const baseResolved = resolve15(baseDir);
|
|
13760
14012
|
const targetResolved = resolve15(baseDir, normalize4(targetPath));
|
|
13761
|
-
if (!targetResolved
|
|
14013
|
+
if (!isInsideDir(targetResolved, baseResolved)) {
|
|
13762
14014
|
return { allowed: false, reason: "Path is outside the base working directory" };
|
|
13763
14015
|
}
|
|
13764
14016
|
if (!scope)
|
|
13765
14017
|
return { allowed: true };
|
|
13766
|
-
const isRead = scope.read_only_files.some((f) => targetResolved
|
|
13767
|
-
const isWrite = scope.allowed_files.some((f) => targetResolved
|
|
14018
|
+
const isRead = scope.read_only_files.some((f) => matchesScopeEntry(targetResolved, resolve15(baseDir, f)));
|
|
14019
|
+
const isWrite = scope.allowed_files.some((f) => matchesScopeEntry(targetResolved, resolve15(baseDir, f)));
|
|
13768
14020
|
if (isWrite)
|
|
13769
14021
|
return { allowed: true };
|
|
13770
14022
|
if (isRead)
|
|
13771
14023
|
return { allowed: true, reason: "Read-only file" };
|
|
13772
14024
|
return { allowed: false, reason: "Path is not within the allowed scope for this sub-agent" };
|
|
13773
14025
|
}
|
|
13774
|
-
var init_scope_check = () => {
|
|
14026
|
+
var init_scope_check = __esm(() => {
|
|
14027
|
+
init_path_utils();
|
|
14028
|
+
});
|
|
13775
14029
|
|
|
13776
14030
|
// src/modules/context/chunk-query.ts
|
|
13777
14031
|
function splitTextIntoChunks(text, chunkChars) {
|
|
@@ -13978,6 +14232,16 @@ ${res.synthesis || res.answers.join(`
|
|
|
13978
14232
|
});
|
|
13979
14233
|
|
|
13980
14234
|
// src/modules/security/network-validator.ts
|
|
14235
|
+
var exports_network_validator = {};
|
|
14236
|
+
__export(exports_network_validator, {
|
|
14237
|
+
sanitizeUrl: () => sanitizeUrl,
|
|
14238
|
+
isUrlAllowed: () => isUrlAllowed,
|
|
14239
|
+
isIpPrivate: () => isIpPrivate
|
|
14240
|
+
});
|
|
14241
|
+
function isIpPrivate(hostname) {
|
|
14242
|
+
const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
14243
|
+
return /^(localhost|127\.0\.0\.1|0\.0\.0\.0|10\.|192\.168\.|169\.254\.|172\.(1[6-9]|2\d|3[01])\.)/.test(h) || h === "::1" || /^(fc|fd)/.test(h) || h.includes(":") && h.startsWith("fe80");
|
|
14244
|
+
}
|
|
13981
14245
|
function isUrlAllowed(url, securityConfig) {
|
|
13982
14246
|
if (!securityConfig?.enabled) {
|
|
13983
14247
|
return { allowed: true };
|
|
@@ -13985,6 +14249,13 @@ function isUrlAllowed(url, securityConfig) {
|
|
|
13985
14249
|
try {
|
|
13986
14250
|
const urlObj = new URL(url);
|
|
13987
14251
|
const domain = urlObj.hostname.toLowerCase();
|
|
14252
|
+
const scheme = urlObj.protocol.toLowerCase();
|
|
14253
|
+
if (scheme !== "http:" && scheme !== "https:") {
|
|
14254
|
+
return { allowed: false, reason: `Scheme "${scheme}" is not allowed` };
|
|
14255
|
+
}
|
|
14256
|
+
if (isIpPrivate(domain)) {
|
|
14257
|
+
return { allowed: false, reason: `Private address "${domain}" is blocked (SSRF)` };
|
|
14258
|
+
}
|
|
13988
14259
|
if (securityConfig.deniedDomains?.length) {
|
|
13989
14260
|
for (const deniedDomain of securityConfig.deniedDomains) {
|
|
13990
14261
|
const denied = deniedDomain.toLowerCase();
|
|
@@ -14177,21 +14448,40 @@ var init_web_fetch = __esm(() => {
|
|
|
14177
14448
|
}
|
|
14178
14449
|
try {
|
|
14179
14450
|
const response = await fetch(url, {
|
|
14180
|
-
signal: AbortSignal.timeout(securityConfig?.requestTimeout || 15000)
|
|
14451
|
+
signal: AbortSignal.timeout(securityConfig?.requestTimeout || 15000),
|
|
14452
|
+
redirect: "manual"
|
|
14181
14453
|
});
|
|
14182
|
-
|
|
14454
|
+
let finalResponse = response;
|
|
14455
|
+
let finalUrl = url;
|
|
14456
|
+
if (response.status >= 300 && response.status < 400) {
|
|
14457
|
+
const loc = response.headers.get("location");
|
|
14458
|
+
if (!loc) {
|
|
14459
|
+
return { success: false, output: t("error.fetch_failed", { message: "Redirect without location" }) };
|
|
14460
|
+
}
|
|
14461
|
+
finalUrl = new URL(loc, url).toString();
|
|
14462
|
+
const recheck = isUrlAllowed(finalUrl, securityConfig);
|
|
14463
|
+
if (!recheck.allowed) {
|
|
14464
|
+
logSecurityBlock(ctx.sessionId, "network_request", recheck.reason || "redirect blocked", sanitizeUrl(finalUrl));
|
|
14465
|
+
return { success: false, output: `[SECURITY BLOCKED] redirect: ${recheck.reason}` };
|
|
14466
|
+
}
|
|
14467
|
+
finalResponse = await fetch(finalUrl, {
|
|
14468
|
+
signal: AbortSignal.timeout(securityConfig?.requestTimeout || 15000),
|
|
14469
|
+
redirect: "follow"
|
|
14470
|
+
});
|
|
14471
|
+
}
|
|
14472
|
+
if (!finalResponse.ok) {
|
|
14183
14473
|
return {
|
|
14184
14474
|
success: false,
|
|
14185
14475
|
output: t("error.http", {
|
|
14186
|
-
status:
|
|
14187
|
-
statusText:
|
|
14476
|
+
status: finalResponse.status,
|
|
14477
|
+
statusText: finalResponse.statusText
|
|
14188
14478
|
})
|
|
14189
14479
|
};
|
|
14190
14480
|
}
|
|
14191
|
-
const contentType =
|
|
14192
|
-
const text = await
|
|
14481
|
+
const contentType = finalResponse.headers.get("content-type") || "";
|
|
14482
|
+
const text = await finalResponse.text();
|
|
14193
14483
|
const cleaned = contentType.includes("html") ? stripHtml(text) : text;
|
|
14194
|
-
logNetworkRequest(ctx.sessionId, sanitizeUrl(
|
|
14484
|
+
logNetworkRequest(ctx.sessionId, sanitizeUrl(finalUrl), true, `Status: ${finalResponse.status}`);
|
|
14195
14485
|
const fullChars = cleaned.length;
|
|
14196
14486
|
const fullLines = cleaned.split(`
|
|
14197
14487
|
`).length;
|
|
@@ -14753,7 +15043,7 @@ ${logs.join(`
|
|
|
14753
15043
|
});
|
|
14754
15044
|
|
|
14755
15045
|
// src/modules/mcp/client.ts
|
|
14756
|
-
import { spawn as
|
|
15046
|
+
import { spawn as spawn4 } from "child_process";
|
|
14757
15047
|
|
|
14758
15048
|
class MCPClient {
|
|
14759
15049
|
serverName;
|
|
@@ -14860,7 +15150,7 @@ class MCPClient {
|
|
|
14860
15150
|
}
|
|
14861
15151
|
connectStdio() {
|
|
14862
15152
|
return new Promise((resolve17, reject) => {
|
|
14863
|
-
const child =
|
|
15153
|
+
const child = spawn4(this.config.command, this.config.args || [], {
|
|
14864
15154
|
env: { ...process.env, ...this.config.env },
|
|
14865
15155
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14866
15156
|
});
|
|
@@ -15657,7 +15947,7 @@ var exports_bridge_client = {};
|
|
|
15657
15947
|
__export(exports_bridge_client, {
|
|
15658
15948
|
BridgeDriver: () => BridgeDriver
|
|
15659
15949
|
});
|
|
15660
|
-
import { spawn as
|
|
15950
|
+
import { spawn as spawn5 } from "child_process";
|
|
15661
15951
|
import { createInterface } from "readline";
|
|
15662
15952
|
import { dirname as dirname10, join as join22 } from "path";
|
|
15663
15953
|
import { fileURLToPath } from "url";
|
|
@@ -15733,7 +16023,7 @@ class BridgeDriver {
|
|
|
15733
16023
|
}
|
|
15734
16024
|
spawnBridge() {
|
|
15735
16025
|
const script = bridgeScriptPath();
|
|
15736
|
-
const proc =
|
|
16026
|
+
const proc = spawn5("node", [script], { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
15737
16027
|
this.proc = proc;
|
|
15738
16028
|
this.lastStderr = [];
|
|
15739
16029
|
proc.stderr.on("data", (chunk) => {
|
|
@@ -16427,6 +16717,9 @@ class BrowserSession {
|
|
|
16427
16717
|
async open(url) {
|
|
16428
16718
|
if (!url)
|
|
16429
16719
|
return { success: false, output: t("browser.url_required") };
|
|
16720
|
+
if (/^(file|chrome|about|data|javascript|view-source):/i.test(url)) {
|
|
16721
|
+
return { success: false, output: `[SECURITY BLOCKED] scheme not allowed: ${url}` };
|
|
16722
|
+
}
|
|
16430
16723
|
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file://")) {
|
|
16431
16724
|
const isLocalhost = /^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?$/i.test(url);
|
|
16432
16725
|
url = isLocalhost ? "http://" + url : "https://" + url;
|
|
@@ -16854,6 +17147,14 @@ var init_attach_image = __esm(() => {
|
|
|
16854
17147
|
const result = await bufferToDataUrl(clipBuf);
|
|
16855
17148
|
dataUrl = result.dataUrl;
|
|
16856
17149
|
} else if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
17150
|
+
const { isUrlAllowed: isUrlAllowed2 } = await Promise.resolve().then(() => (init_network_validator(), exports_network_validator));
|
|
17151
|
+
const { getSessionSecurityConfig: getSessionSecurityConfig2 } = await Promise.resolve().then(() => (init_session_isolation(), exports_session_isolation));
|
|
17152
|
+
const networkConfig = ctx.sessionContext ? getSessionSecurityConfig2(ctx.config, ctx.sessionContext).network : ctx.config?.security?.network;
|
|
17153
|
+
const urlCheck = isUrlAllowed2(source, networkConfig);
|
|
17154
|
+
if (!urlCheck.allowed) {
|
|
17155
|
+
logSecurityBlock(ctx.sessionId, "network_request", urlCheck.reason ?? "attach_image URL blocked", source);
|
|
17156
|
+
return { success: false, output: `[SECURITY BLOCKED] ${urlCheck.reason}` };
|
|
17157
|
+
}
|
|
16857
17158
|
const result = await loadUrlAsDataUrl(source);
|
|
16858
17159
|
dataUrl = result.dataUrl;
|
|
16859
17160
|
} else {
|
|
@@ -17070,6 +17371,7 @@ class ModuleRegistry {
|
|
|
17070
17371
|
modules = new Map;
|
|
17071
17372
|
register(mod) {
|
|
17072
17373
|
this.modules.set(mod.name, mod);
|
|
17374
|
+
return this;
|
|
17073
17375
|
}
|
|
17074
17376
|
get(name) {
|
|
17075
17377
|
return this.modules.get(name);
|
|
@@ -17115,7 +17417,7 @@ class ModuleRegistry {
|
|
|
17115
17417
|
}
|
|
17116
17418
|
|
|
17117
17419
|
// src/modules/plugins/loader.ts
|
|
17118
|
-
import { readdirSync as readdirSync9, existsSync as existsSync31, statSync as
|
|
17420
|
+
import { readdirSync as readdirSync9, existsSync as existsSync31, statSync as statSync6 } from "fs";
|
|
17119
17421
|
import { join as join25, basename as basename3 } from "path";
|
|
17120
17422
|
|
|
17121
17423
|
class PluginLoader {
|
|
@@ -17125,7 +17427,7 @@ class PluginLoader {
|
|
|
17125
17427
|
const entries = readdirSync9(dirPath).sort();
|
|
17126
17428
|
for (const entry of entries) {
|
|
17127
17429
|
const fullPath = join25(dirPath, entry);
|
|
17128
|
-
const stat =
|
|
17430
|
+
const stat = statSync6(fullPath);
|
|
17129
17431
|
if (stat.isFile()) {
|
|
17130
17432
|
if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
|
|
17131
17433
|
continue;
|
|
@@ -17182,7 +17484,7 @@ var init_loader = __esm(() => {
|
|
|
17182
17484
|
});
|
|
17183
17485
|
|
|
17184
17486
|
// src/modules/plugins/builtin/lint-on-write.ts
|
|
17185
|
-
import { spawn as
|
|
17487
|
+
import { spawn as spawn6, execSync } from "child_process";
|
|
17186
17488
|
import { existsSync as existsSync32, readFileSync as readFileSync17 } from "fs";
|
|
17187
17489
|
import { resolve as resolve18, extname as extname4, join as join26 } from "path";
|
|
17188
17490
|
import { platform as platform5 } from "os";
|
|
@@ -17365,7 +17667,7 @@ class LintOnWritePlugin {
|
|
|
17365
17667
|
}
|
|
17366
17668
|
function runAsync(command, cwd, timeoutMs, signal) {
|
|
17367
17669
|
return new Promise((resolve19, reject) => {
|
|
17368
|
-
const child =
|
|
17670
|
+
const child = spawn6(command, {
|
|
17369
17671
|
cwd,
|
|
17370
17672
|
shell: true,
|
|
17371
17673
|
windowsHide: true,
|
|
@@ -17989,7 +18291,11 @@ Last compile error: ${first[1]}`;
|
|
|
17989
18291
|
const step = deps.trackerRef.current?.getCurrentStep();
|
|
17990
18292
|
ctx.contextManager.addMessage({
|
|
17991
18293
|
role: "user",
|
|
17992
|
-
content: `<system-summary
|
|
18294
|
+
content: `<system-summary>${t("exec.stop_directive", {
|
|
18295
|
+
stepId: String(step?.id ?? "?"),
|
|
18296
|
+
description: step?.description ?? "",
|
|
18297
|
+
iterations: String(deps.stuckDetector.getIterationsOnCurrentStep())
|
|
18298
|
+
})}</system-summary>`
|
|
17993
18299
|
});
|
|
17994
18300
|
}
|
|
17995
18301
|
}
|
|
@@ -18669,16 +18975,16 @@ ${progress}${vacuousNote}`,
|
|
|
18669
18975
|
output += ` (${doneCount}/${subs.length})`;
|
|
18670
18976
|
if (doneCount === subs.length) {
|
|
18671
18977
|
output += `
|
|
18672
|
-
|
|
18978
|
+
${t("plan.subtasks_done", { stepId: String(currentStep.id) })}`;
|
|
18673
18979
|
}
|
|
18674
18980
|
return { success: true, output, display };
|
|
18675
18981
|
}
|
|
18676
18982
|
if (action === "list") {
|
|
18677
18983
|
const subs = currentStep.subtasks ?? [];
|
|
18678
|
-
const lines = subs.length ? subs.map((s) => `${s.done ? "[x]" : "[ ]"} ${s.text}`) : ["
|
|
18984
|
+
const lines = subs.length ? subs.map((s) => `${s.done ? "[x]" : "[ ]"} ${s.text}`) : [t("plan.no_subtasks")];
|
|
18679
18985
|
return {
|
|
18680
18986
|
success: true,
|
|
18681
|
-
output:
|
|
18987
|
+
output: `${t("plan.step_label", { stepId: String(currentStep.id), description: currentStep.description })}
|
|
18682
18988
|
${lines.join(`
|
|
18683
18989
|
`)}`
|
|
18684
18990
|
};
|
|
@@ -19373,7 +19679,7 @@ class SessionStore {
|
|
|
19373
19679
|
return this.encryptor?.isEnabled() ?? false;
|
|
19374
19680
|
}
|
|
19375
19681
|
init() {
|
|
19376
|
-
mkdirSync15(this.baseDir, { recursive: true });
|
|
19682
|
+
mkdirSync15(this.baseDir, { recursive: true, mode: 448 });
|
|
19377
19683
|
}
|
|
19378
19684
|
sessionDir(id) {
|
|
19379
19685
|
return join29(this.baseDir, id);
|
|
@@ -19393,12 +19699,12 @@ class SessionStore {
|
|
|
19393
19699
|
saveMeta(id, meta) {
|
|
19394
19700
|
this._metaCache.set(id, meta);
|
|
19395
19701
|
const dir = this.sessionDir(id);
|
|
19396
|
-
mkdirSync15(dir, { recursive: true });
|
|
19702
|
+
mkdirSync15(dir, { recursive: true, mode: 448 });
|
|
19397
19703
|
const content = JSON.stringify(meta, null, 2);
|
|
19398
19704
|
if (this.encryptor) {
|
|
19399
|
-
writeFileSync12(this.metaPath(id), this.encryptor.encryptFileContent(content), "utf-8");
|
|
19705
|
+
writeFileSync12(this.metaPath(id), this.encryptor.encryptFileContent(content), { encoding: "utf-8", mode: 384 });
|
|
19400
19706
|
} else {
|
|
19401
|
-
writeFileSync12(this.metaPath(id), content, "utf-8");
|
|
19707
|
+
writeFileSync12(this.metaPath(id), content, { encoding: "utf-8", mode: 384 });
|
|
19402
19708
|
}
|
|
19403
19709
|
}
|
|
19404
19710
|
loadMeta(id) {
|
|
@@ -19420,14 +19726,14 @@ class SessionStore {
|
|
|
19420
19726
|
}
|
|
19421
19727
|
appendMessage(id, msg) {
|
|
19422
19728
|
const dir = this.sessionDir(id);
|
|
19423
|
-
mkdirSync15(dir, { recursive: true });
|
|
19729
|
+
mkdirSync15(dir, { recursive: true, mode: 448 });
|
|
19424
19730
|
const line = JSON.stringify(msg);
|
|
19425
19731
|
if (this.encryptor?.isEnabled()) {
|
|
19426
19732
|
appendFileSync6(this.historyPath(id), this.encryptor.encryptFileContent(line) + `
|
|
19427
|
-
`, "utf-8");
|
|
19733
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
19428
19734
|
} else {
|
|
19429
19735
|
appendFileSync6(this.historyPath(id), line + `
|
|
19430
|
-
`, "utf-8");
|
|
19736
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
19431
19737
|
}
|
|
19432
19738
|
const meta = this.loadMeta(id);
|
|
19433
19739
|
if (meta) {
|
|
@@ -19467,14 +19773,14 @@ class SessionStore {
|
|
|
19467
19773
|
}
|
|
19468
19774
|
appendSessionLog(id, entry) {
|
|
19469
19775
|
const dir = this.sessionDir(id);
|
|
19470
|
-
mkdirSync15(dir, { recursive: true });
|
|
19776
|
+
mkdirSync15(dir, { recursive: true, mode: 448 });
|
|
19471
19777
|
const line = JSON.stringify(entry);
|
|
19472
19778
|
if (this.encryptor?.isEnabled()) {
|
|
19473
19779
|
appendFileSync6(this.sessionLogPath(id), this.encryptor.encryptFileContent(line) + `
|
|
19474
|
-
`, "utf-8");
|
|
19780
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
19475
19781
|
} else {
|
|
19476
19782
|
appendFileSync6(this.sessionLogPath(id), line + `
|
|
19477
|
-
`, "utf-8");
|
|
19783
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
19478
19784
|
}
|
|
19479
19785
|
}
|
|
19480
19786
|
loadSessionLog(id) {
|
|
@@ -19819,7 +20125,7 @@ class UserProfile {
|
|
|
19819
20125
|
var init_profile = () => {};
|
|
19820
20126
|
|
|
19821
20127
|
// src/modules/skills/loader.ts
|
|
19822
|
-
import { readdirSync as readdirSync13, readFileSync as readFileSync23, existsSync as existsSync38, statSync as
|
|
20128
|
+
import { readdirSync as readdirSync13, readFileSync as readFileSync23, existsSync as existsSync38, statSync as statSync7 } from "fs";
|
|
19823
20129
|
import { join as join31 } from "path";
|
|
19824
20130
|
|
|
19825
20131
|
class SkillsLoader {
|
|
@@ -19834,7 +20140,7 @@ class SkillsLoader {
|
|
|
19834
20140
|
const entries = readdirSync13(dirPath);
|
|
19835
20141
|
for (const entry of entries) {
|
|
19836
20142
|
const fullPath = join31(dirPath, entry);
|
|
19837
|
-
const stat =
|
|
20143
|
+
const stat = statSync7(fullPath);
|
|
19838
20144
|
if (stat.isDirectory()) {
|
|
19839
20145
|
this.scanDir(fullPath, skills);
|
|
19840
20146
|
continue;
|
|
@@ -20077,10 +20383,29 @@ var init_browser2 = __esm(() => {
|
|
|
20077
20383
|
});
|
|
20078
20384
|
|
|
20079
20385
|
// src/modules/lsp/client.ts
|
|
20080
|
-
import { spawn as
|
|
20386
|
+
import { spawn as spawn7, execSync as execSync2 } from "child_process";
|
|
20081
20387
|
import { resolve as resolve20 } from "path";
|
|
20082
20388
|
import { platform as platform8 } from "os";
|
|
20083
20389
|
|
|
20390
|
+
class FileOnlyLogger {
|
|
20391
|
+
logger;
|
|
20392
|
+
constructor(logger) {
|
|
20393
|
+
this.logger = logger;
|
|
20394
|
+
}
|
|
20395
|
+
debug(msg, meta) {
|
|
20396
|
+
this.logger.logSilent("debug", msg, meta);
|
|
20397
|
+
}
|
|
20398
|
+
info(msg, meta) {
|
|
20399
|
+
this.logger.logSilent("info", msg, meta);
|
|
20400
|
+
}
|
|
20401
|
+
warn(msg, meta) {
|
|
20402
|
+
this.logger.logSilent("warn", msg, meta);
|
|
20403
|
+
}
|
|
20404
|
+
error(msg, meta) {
|
|
20405
|
+
this.logger.logSilent("error", msg, meta);
|
|
20406
|
+
}
|
|
20407
|
+
}
|
|
20408
|
+
|
|
20084
20409
|
class LspClient {
|
|
20085
20410
|
logger = null;
|
|
20086
20411
|
setLogger(logger) {
|
|
@@ -20175,10 +20500,26 @@ class LspClient {
|
|
|
20175
20500
|
throw new Error(`${config.command} not found in PATH`);
|
|
20176
20501
|
}
|
|
20177
20502
|
}
|
|
20503
|
+
let effectiveCommand = config.command;
|
|
20504
|
+
let effectiveArgs = config.args ?? [];
|
|
20505
|
+
if (config.command === "npx" && effectiveArgs.length > 0) {
|
|
20506
|
+
const binaryName = this.extractBinaryFromNpxArgs(effectiveArgs);
|
|
20507
|
+
if (binaryName) {
|
|
20508
|
+
const whichCmd = platform8() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
|
|
20509
|
+
try {
|
|
20510
|
+
execSync2(whichCmd, { stdio: "pipe", timeout: 3000 });
|
|
20511
|
+
effectiveCommand = binaryName;
|
|
20512
|
+
effectiveArgs = effectiveArgs.filter((a) => a !== "--yes" && a !== "--package" && a !== binaryName && !this.isPackageVersion(a));
|
|
20513
|
+
this.logger?.debug(`LSP: using globally installed ${binaryName} (skipped npx)`);
|
|
20514
|
+
} catch {
|
|
20515
|
+
this.logger?.debug(`LSP: ${binaryName} not found globally, falling back to npx`);
|
|
20516
|
+
}
|
|
20517
|
+
}
|
|
20518
|
+
}
|
|
20178
20519
|
return new Promise((resolve21, reject) => {
|
|
20179
|
-
const args =
|
|
20520
|
+
const args = effectiveArgs;
|
|
20180
20521
|
const isWin = platform8() === "win32";
|
|
20181
|
-
let spawnCommand = resolveSpawnCommand(
|
|
20522
|
+
let spawnCommand = resolveSpawnCommand(effectiveCommand);
|
|
20182
20523
|
let spawnArgs = args;
|
|
20183
20524
|
const spawnOpts = {
|
|
20184
20525
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -20190,7 +20531,7 @@ class LspClient {
|
|
|
20190
20531
|
spawnArgs = [];
|
|
20191
20532
|
spawnOpts.shell = true;
|
|
20192
20533
|
}
|
|
20193
|
-
const proc =
|
|
20534
|
+
const proc = spawn7(spawnCommand, spawnArgs, spawnOpts);
|
|
20194
20535
|
proc.on("error", reject);
|
|
20195
20536
|
proc.stdout.on("data", (chunk) => {
|
|
20196
20537
|
this.handleData(chunk);
|
|
@@ -20368,6 +20709,22 @@ class LspClient {
|
|
|
20368
20709
|
};
|
|
20369
20710
|
return map[ext ?? ""] ?? "plaintext";
|
|
20370
20711
|
}
|
|
20712
|
+
extractBinaryFromNpxArgs(args) {
|
|
20713
|
+
for (let i = args.length - 1;i >= 0; i--) {
|
|
20714
|
+
const arg = args[i];
|
|
20715
|
+
if (arg === "--stdio")
|
|
20716
|
+
continue;
|
|
20717
|
+
if (arg.startsWith("--"))
|
|
20718
|
+
return null;
|
|
20719
|
+
if (this.isPackageVersion(arg))
|
|
20720
|
+
continue;
|
|
20721
|
+
return arg;
|
|
20722
|
+
}
|
|
20723
|
+
return null;
|
|
20724
|
+
}
|
|
20725
|
+
isPackageVersion(s) {
|
|
20726
|
+
return /^[\w-]+@[\d.+*-]/.test(s);
|
|
20727
|
+
}
|
|
20371
20728
|
}
|
|
20372
20729
|
var DEFAULT_TIMEOUT = 15000;
|
|
20373
20730
|
var init_client2 = __esm(() => {
|
|
@@ -20377,7 +20734,7 @@ var init_client2 = __esm(() => {
|
|
|
20377
20734
|
|
|
20378
20735
|
// src/modules/lsp/check-tool.ts
|
|
20379
20736
|
import { readdir, stat } from "fs/promises";
|
|
20380
|
-
import { resolve as resolve21, relative as
|
|
20737
|
+
import { resolve as resolve21, relative as relative3 } from "path";
|
|
20381
20738
|
async function collectCheckFiles(resolved, config) {
|
|
20382
20739
|
const info = await stat(resolved).catch(() => null);
|
|
20383
20740
|
if (!info)
|
|
@@ -20406,7 +20763,7 @@ async function walkDir(dir, depth, out, config) {
|
|
|
20406
20763
|
}
|
|
20407
20764
|
function formatCheckDiagnostics(items, baseDir) {
|
|
20408
20765
|
return items.slice(0, 40).map(({ file, diag }) => {
|
|
20409
|
-
const rel =
|
|
20766
|
+
const rel = relative3(baseDir, file).replace(/\\/g, "/");
|
|
20410
20767
|
const line = diag.range.start.line + 1;
|
|
20411
20768
|
const col = diag.range.start.character + 1;
|
|
20412
20769
|
const sev = severityLabels[diag.severity] ?? "unknown";
|
|
@@ -20428,7 +20785,7 @@ var init_check_tool = __esm(() => {
|
|
|
20428
20785
|
|
|
20429
20786
|
// src/modules/lsp/module.ts
|
|
20430
20787
|
import { existsSync as existsSync39 } from "fs";
|
|
20431
|
-
import { relative as
|
|
20788
|
+
import { relative as relative4, resolve as resolve22 } from "path";
|
|
20432
20789
|
|
|
20433
20790
|
class LspModule {
|
|
20434
20791
|
name = "lsp";
|
|
@@ -20595,7 +20952,7 @@ ${items}`;
|
|
|
20595
20952
|
const diags = await this.client.checkFile(file, ctx.baseDir, serverConfig, projectRoot);
|
|
20596
20953
|
this.failuresByServer.set(key, 0);
|
|
20597
20954
|
checked++;
|
|
20598
|
-
checkedFiles.push(
|
|
20955
|
+
checkedFiles.push(relative4(ctx.baseDir, file).replace(/\\/g, "/"));
|
|
20599
20956
|
for (const d of diags) {
|
|
20600
20957
|
if (d.severity === 1)
|
|
20601
20958
|
errors.push({ file, diag: d });
|
|
@@ -20676,7 +21033,7 @@ var init_lsp = __esm(() => {
|
|
|
20676
21033
|
// src/modules/lsp/startup-check.ts
|
|
20677
21034
|
import { existsSync as existsSync40 } from "fs";
|
|
20678
21035
|
import { join as join32 } from "path";
|
|
20679
|
-
import { spawn as
|
|
21036
|
+
import { spawn as spawn8 } from "child_process";
|
|
20680
21037
|
async function runStartupHealthCheck(config, baseDir, deps = {}) {
|
|
20681
21038
|
if (!config.enabled)
|
|
20682
21039
|
return null;
|
|
@@ -20738,7 +21095,7 @@ async function runCheck(config, baseDir, deps) {
|
|
|
20738
21095
|
}
|
|
20739
21096
|
async function runTscDefault(projectRoot, timeoutMs) {
|
|
20740
21097
|
return new Promise((resolve23) => {
|
|
20741
|
-
const child =
|
|
21098
|
+
const child = spawn8("npx", ["tsc", "--noEmit", "--skipLibCheck"], {
|
|
20742
21099
|
cwd: projectRoot,
|
|
20743
21100
|
shell: true,
|
|
20744
21101
|
windowsHide: true,
|
|
@@ -20802,8 +21159,8 @@ var init_startup_check = __esm(() => {
|
|
|
20802
21159
|
});
|
|
20803
21160
|
|
|
20804
21161
|
// src/modules/indexer/walker.ts
|
|
20805
|
-
import { readdirSync as readdirSync14, readFileSync as readFileSync24, statSync as
|
|
20806
|
-
import { join as join33, relative as
|
|
21162
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync24, statSync as statSync8, existsSync as existsSync41, watch } from "fs";
|
|
21163
|
+
import { join as join33, relative as relative5, extname as extname5 } from "path";
|
|
20807
21164
|
|
|
20808
21165
|
class Indexer {
|
|
20809
21166
|
baseDir;
|
|
@@ -20842,8 +21199,8 @@ class Indexer {
|
|
|
20842
21199
|
if (count >= this.MAX_FILES)
|
|
20843
21200
|
return;
|
|
20844
21201
|
const fullPath = join33(dir, entry);
|
|
20845
|
-
const relPath =
|
|
20846
|
-
const stat2 =
|
|
21202
|
+
const relPath = relative5(this.baseDir, fullPath);
|
|
21203
|
+
const stat2 = statSync8(fullPath);
|
|
20847
21204
|
if (stat2.isDirectory()) {
|
|
20848
21205
|
if (!IGNORE_DIRS.has(entry)) {
|
|
20849
21206
|
walkDir2(fullPath);
|
|
@@ -21817,7 +22174,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
21817
22174
|
const lines = [
|
|
21818
22175
|
`You are MMA v2, an AI coding agent for small models (${config.model}). Date: ${now}. Workspace: ${baseDir}. ${profileCompressed}.`,
|
|
21819
22176
|
`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.`,
|
|
21820
|
-
`Design: YAGNI (no unneeded code), KISS (simple over clever), DRY (reuse existing utilities)
|
|
22177
|
+
`Design: YAGNI (no unneeded code), KISS (simple over clever), DRY (reuse existing utilities).`,
|
|
22178
|
+
`SCOPE DISCIPLINE: do ONLY what the user explicitly asked — no extra features, files, refactors, "improvements", or fixes beyond the request. Read-only requests ("расскажи", "покажи", "объясни", "check") mean READ-ONLY: inspect and answer, never create/modify/delete anything. If the task is ambiguous (what to create, where, which variant) or the request implies action on something you could not find — STOP and ask the user a short clarifying question in plain text instead of guessing.`,
|
|
22179
|
+
`NOT FOUND ≠ MISSING PROJECT: a "not found" tool result means the PATH was wrong (typo, different location), not that the project does not exist. Before creating or scaffolding ANY project/files: first run list_dir on the working directory to see what is already there; if a user-named path is not found, list its parent directory to locate the real path. NEVER create a new project when the user asked about an existing one — inspect first, create only after confirming the workspace is empty AND the user asked for creation.`
|
|
21821
22180
|
];
|
|
21822
22181
|
if (isWin) {
|
|
21823
22182
|
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}`);
|
|
@@ -22013,7 +22372,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22013
22372
|
pluginManager.register(browserPlugin);
|
|
22014
22373
|
}
|
|
22015
22374
|
}
|
|
22016
|
-
const
|
|
22375
|
+
const lspLogger = new FileOnlyLogger(logger);
|
|
22376
|
+
const lspModule = new LspModule(config.lsp, undefined, lspLogger);
|
|
22017
22377
|
moduleRegistry.register(lspModule);
|
|
22018
22378
|
const lspPlugin = lspModule.getPlugin();
|
|
22019
22379
|
if (lspPlugin) {
|
|
@@ -22021,7 +22381,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22021
22381
|
pluginManager.register(lspPlugin);
|
|
22022
22382
|
}
|
|
22023
22383
|
let startupCheckBlock = null;
|
|
22024
|
-
const startupCheckPromise = !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir, { logger }) : Promise.resolve(null);
|
|
22384
|
+
const startupCheckPromise = !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir, { logger: lspLogger }) : Promise.resolve(null);
|
|
22025
22385
|
const moduleTools = moduleRegistry.collectToolDefinitions();
|
|
22026
22386
|
for (const tool of moduleTools) {
|
|
22027
22387
|
toolRegistry.register(tool);
|
|
@@ -22147,6 +22507,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22147
22507
|
},
|
|
22148
22508
|
sessionManager,
|
|
22149
22509
|
memoryStore,
|
|
22510
|
+
moduleRegistry,
|
|
22150
22511
|
exitOnComplete
|
|
22151
22512
|
};
|
|
22152
22513
|
const agent = new Agent(agentDeps);
|
|
@@ -22879,7 +23240,7 @@ async function runSetup(externalRl) {
|
|
|
22879
23240
|
const contextWindow = parseInt(await ask(rl, t("setup.context_window"), "32768"));
|
|
22880
23241
|
const maxIterations = parseInt(await ask(rl, t("setup.max_iters"), "1000"));
|
|
22881
23242
|
console.log(t("setup.security_header"));
|
|
22882
|
-
console.log(pc2.dim(t("setup.
|
|
23243
|
+
console.log(pc2.dim(t("setup.security_status_on")));
|
|
22883
23244
|
const configureSecurity = await ask(rl, t("setup.security_configure"), "n");
|
|
22884
23245
|
let securityBashBlock = false;
|
|
22885
23246
|
let securityFlagsBlock = false;
|
|
@@ -30240,7 +30601,7 @@ var init_loader3 = __esm(() => {
|
|
|
30240
30601
|
});
|
|
30241
30602
|
|
|
30242
30603
|
// src/modules/certification/fact-checker.ts
|
|
30243
|
-
import { existsSync as existsSync49, readFileSync as readFileSync32, statSync as
|
|
30604
|
+
import { existsSync as existsSync49, readFileSync as readFileSync32, statSync as statSync9 } from "fs";
|
|
30244
30605
|
import { join as join43 } from "path";
|
|
30245
30606
|
function checkSandbox(sandboxDir, checks, exitCode, output) {
|
|
30246
30607
|
const failures = [];
|
|
@@ -30286,14 +30647,14 @@ function runCheck2(sandboxDir, check, exitCode, output) {
|
|
|
30286
30647
|
}
|
|
30287
30648
|
function isFile(p) {
|
|
30288
30649
|
try {
|
|
30289
|
-
return existsSync49(p) &&
|
|
30650
|
+
return existsSync49(p) && statSync9(p).isFile();
|
|
30290
30651
|
} catch {
|
|
30291
30652
|
return false;
|
|
30292
30653
|
}
|
|
30293
30654
|
}
|
|
30294
30655
|
function isDir(p) {
|
|
30295
30656
|
try {
|
|
30296
|
-
return existsSync49(p) &&
|
|
30657
|
+
return existsSync49(p) && statSync9(p).isDirectory();
|
|
30297
30658
|
} catch {
|
|
30298
30659
|
return false;
|
|
30299
30660
|
}
|
|
@@ -30323,7 +30684,7 @@ function describe(check) {
|
|
|
30323
30684
|
var init_fact_checker = () => {};
|
|
30324
30685
|
|
|
30325
30686
|
// src/modules/certification/runner.ts
|
|
30326
|
-
import { spawn as
|
|
30687
|
+
import { spawn as spawn9 } from "child_process";
|
|
30327
30688
|
import { existsSync as existsSync50, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
|
|
30328
30689
|
import { platform as platform10 } from "os";
|
|
30329
30690
|
import { join as join44, resolve as resolve24, dirname as dirname15 } from "path";
|
|
@@ -30434,7 +30795,7 @@ function killTree2(child) {
|
|
|
30434
30795
|
if (!pid)
|
|
30435
30796
|
return;
|
|
30436
30797
|
if (platform10() === "win32") {
|
|
30437
|
-
|
|
30798
|
+
spawn9("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
30438
30799
|
windowsHide: true,
|
|
30439
30800
|
stdio: "ignore"
|
|
30440
30801
|
});
|
|
@@ -30449,7 +30810,7 @@ function killTree2(child) {
|
|
|
30449
30810
|
}
|
|
30450
30811
|
}
|
|
30451
30812
|
var defaultRunner2 = (env3, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
|
|
30452
|
-
const child =
|
|
30813
|
+
const child = spawn9(process.execPath, args, {
|
|
30453
30814
|
cwd,
|
|
30454
30815
|
env: env3,
|
|
30455
30816
|
windowsHide: true,
|
|
@@ -30659,24 +31020,8 @@ __export(exports_repl_commands, {
|
|
|
30659
31020
|
registerAllCommands: () => registerAllCommands,
|
|
30660
31021
|
COMMAND_GROUPS: () => COMMAND_GROUPS
|
|
30661
31022
|
});
|
|
30662
|
-
import { join as join47
|
|
31023
|
+
import { join as join47 } from "path";
|
|
30663
31024
|
import { homedir as homedir17 } from "os";
|
|
30664
|
-
import { existsSync as existsSync53, readFileSync as readFileSync35 } from "fs";
|
|
30665
|
-
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
30666
|
-
function readVersion3() {
|
|
30667
|
-
const here = dirname18(fileURLToPath6(import.meta.url));
|
|
30668
|
-
const candidates = [join47(here, "..", "..", "package.json"), join47(here, "..", "package.json")];
|
|
30669
|
-
for (const p of candidates) {
|
|
30670
|
-
if (existsSync53(p)) {
|
|
30671
|
-
try {
|
|
30672
|
-
const raw = JSON.parse(readFileSync35(p, "utf8"));
|
|
30673
|
-
if (raw.version)
|
|
30674
|
-
return raw.version;
|
|
30675
|
-
} catch {}
|
|
30676
|
-
}
|
|
30677
|
-
}
|
|
30678
|
-
return "0.0.0";
|
|
30679
|
-
}
|
|
30680
31025
|
function registerAllCommands(ctx) {
|
|
30681
31026
|
registerBuiltinCommands(ctx);
|
|
30682
31027
|
registerMmaCommands(ctx);
|
|
@@ -30733,7 +31078,7 @@ function registerMmaCommands(ctx) {
|
|
|
30733
31078
|
}
|
|
30734
31079
|
try {
|
|
30735
31080
|
const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
|
|
30736
|
-
const { existsSync:
|
|
31081
|
+
const { existsSync: existsSync52 } = await import("fs");
|
|
30737
31082
|
const { resolve: resolve25 } = await import("path");
|
|
30738
31083
|
let dataUrl;
|
|
30739
31084
|
let label;
|
|
@@ -30753,7 +31098,7 @@ function registerMmaCommands(ctx) {
|
|
|
30753
31098
|
label = source;
|
|
30754
31099
|
} else {
|
|
30755
31100
|
const absPath = resolve25(process.cwd(), source);
|
|
30756
|
-
if (!
|
|
31101
|
+
if (!existsSync52(absPath)) {
|
|
30757
31102
|
console.log(pc2.red(t("image.not_found", { path: source })));
|
|
30758
31103
|
return;
|
|
30759
31104
|
}
|
|
@@ -30979,10 +31324,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
30979
31324
|
usage: t("repl.reload_usage"),
|
|
30980
31325
|
action: async () => {
|
|
30981
31326
|
console.log(pc2.yellow(t("repl.reloading")));
|
|
30982
|
-
if (ctx.sessionManager && ctx.config.session.autoSave) {
|
|
30983
|
-
const active = ctx.sessionManager.getActiveMeta();
|
|
30984
|
-
if (active) {}
|
|
30985
|
-
}
|
|
31327
|
+
if (ctx.sessionManager && ctx.config.session.autoSave) {}
|
|
30986
31328
|
ctx.agent.shutdown();
|
|
30987
31329
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
|
|
30988
31330
|
const { homedir: homedir18 } = await import("os");
|
|
@@ -31046,7 +31388,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31046
31388
|
usage: t("repl.lsp_usage"),
|
|
31047
31389
|
action: async (args) => {
|
|
31048
31390
|
const subcommand = args[0] || "status";
|
|
31049
|
-
const lspModule = ctx.agent.
|
|
31391
|
+
const lspModule = ctx.agent.getModule("lsp");
|
|
31050
31392
|
if (!lspModule) {
|
|
31051
31393
|
console.log(pc2.yellow(t("repl.lsp_not_available")));
|
|
31052
31394
|
return;
|
|
@@ -31338,11 +31680,12 @@ function registerSkillCommands(ctx) {
|
|
|
31338
31680
|
var version2, COMMAND_GROUPS;
|
|
31339
31681
|
var init_repl_commands = __esm(() => {
|
|
31340
31682
|
init_colors();
|
|
31683
|
+
init_version();
|
|
31341
31684
|
init_table();
|
|
31342
31685
|
init_i18n();
|
|
31343
31686
|
init_setup();
|
|
31344
31687
|
init_config2();
|
|
31345
|
-
version2 =
|
|
31688
|
+
version2 = readMmaVersion();
|
|
31346
31689
|
COMMAND_GROUPS = {
|
|
31347
31690
|
help: "general",
|
|
31348
31691
|
exit: "general",
|
|
@@ -31390,9 +31733,8 @@ init_bootstrap();
|
|
|
31390
31733
|
init_config2();
|
|
31391
31734
|
init_setup();
|
|
31392
31735
|
init_i18n();
|
|
31393
|
-
import { join as join46
|
|
31736
|
+
import { join as join46 } from "path";
|
|
31394
31737
|
import { homedir as homedir16 } from "os";
|
|
31395
|
-
import { existsSync as existsSync52, readFileSync as readFileSync34 } from "fs";
|
|
31396
31738
|
|
|
31397
31739
|
// src/cli/security-commands.ts
|
|
31398
31740
|
init_bootstrap();
|
|
@@ -32028,22 +32370,8 @@ function createPluginCommand(program2) {
|
|
|
32028
32370
|
|
|
32029
32371
|
// src/cli/commands.ts
|
|
32030
32372
|
init_setup();
|
|
32031
|
-
|
|
32032
|
-
|
|
32033
|
-
const here = dirname17(fileURLToPath5(import.meta.url));
|
|
32034
|
-
const candidates = [join46(here, "..", "..", "package.json"), join46(here, "..", "package.json")];
|
|
32035
|
-
for (const p of candidates) {
|
|
32036
|
-
if (existsSync52(p)) {
|
|
32037
|
-
try {
|
|
32038
|
-
const raw = JSON.parse(readFileSync34(p, "utf8"));
|
|
32039
|
-
if (raw.version)
|
|
32040
|
-
return raw.version;
|
|
32041
|
-
} catch {}
|
|
32042
|
-
}
|
|
32043
|
-
}
|
|
32044
|
-
return "0.0.0";
|
|
32045
|
-
}
|
|
32046
|
-
var version = readVersion2();
|
|
32373
|
+
init_version();
|
|
32374
|
+
var version = readMmaVersion();
|
|
32047
32375
|
function createProgram() {
|
|
32048
32376
|
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"));
|
|
32049
32377
|
program2.command("init").description(t("cli.init")).action(async () => {
|
|
@@ -32058,9 +32386,9 @@ function createProgram() {
|
|
|
32058
32386
|
config.maxToolIterations = answers.maxToolIterations;
|
|
32059
32387
|
config.locale = answers.locale;
|
|
32060
32388
|
if (config.security) {
|
|
32061
|
-
config.security.enabled =
|
|
32062
|
-
config.security.bash.enabled =
|
|
32063
|
-
config.security.bash.blockDangerousFlags = answers.securityFlagsBlock;
|
|
32389
|
+
config.security.enabled = true;
|
|
32390
|
+
config.security.bash.enabled = true;
|
|
32391
|
+
config.security.bash.blockDangerousFlags = answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
32064
32392
|
if (answers.securityBashBlock) {
|
|
32065
32393
|
config.security.bash.blacklist = [
|
|
32066
32394
|
...new Set([
|
|
@@ -33025,7 +33353,7 @@ class LineEditor {
|
|
|
33025
33353
|
}
|
|
33026
33354
|
|
|
33027
33355
|
// src/cli/repl.ts
|
|
33028
|
-
import { existsSync as
|
|
33356
|
+
import { existsSync as existsSync53, readFileSync as readFileSync35, writeFileSync as writeFileSync17 } from "fs";
|
|
33029
33357
|
import { join as join49 } from "path";
|
|
33030
33358
|
import { homedir as homedir18 } from "os";
|
|
33031
33359
|
|
|
@@ -33630,6 +33958,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33630
33958
|
}
|
|
33631
33959
|
|
|
33632
33960
|
// src/cli/repl.ts
|
|
33961
|
+
init_client2();
|
|
33633
33962
|
init_config();
|
|
33634
33963
|
init_session_logger();
|
|
33635
33964
|
|
|
@@ -33637,14 +33966,14 @@ init_session_logger();
|
|
|
33637
33966
|
init_colors();
|
|
33638
33967
|
init_js_identifiers();
|
|
33639
33968
|
init_i18n();
|
|
33640
|
-
import { existsSync as
|
|
33969
|
+
import { existsSync as existsSync52, readFileSync as readFileSync34 } from "fs";
|
|
33641
33970
|
import { join as join48 } from "path";
|
|
33642
33971
|
function readActivePlan(baseDir) {
|
|
33643
33972
|
const p = join48(baseDir, ".mma", "plans", "active.json");
|
|
33644
|
-
if (!
|
|
33973
|
+
if (!existsSync52(p))
|
|
33645
33974
|
return null;
|
|
33646
33975
|
try {
|
|
33647
|
-
const raw =
|
|
33976
|
+
const raw = readFileSync34(p, "utf-8");
|
|
33648
33977
|
if (!raw.trim())
|
|
33649
33978
|
return null;
|
|
33650
33979
|
const parsed = JSON.parse(raw);
|
|
@@ -33771,7 +34100,7 @@ class Repl {
|
|
|
33771
34100
|
pendingClipboardImage = null;
|
|
33772
34101
|
exitOnClose = false;
|
|
33773
34102
|
activePlan = null;
|
|
33774
|
-
lastPlanSig =
|
|
34103
|
+
lastPlanSig = null;
|
|
33775
34104
|
rl;
|
|
33776
34105
|
agent;
|
|
33777
34106
|
config;
|
|
@@ -33781,7 +34110,7 @@ class Repl {
|
|
|
33781
34110
|
logger;
|
|
33782
34111
|
slog;
|
|
33783
34112
|
envReport;
|
|
33784
|
-
constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, exitOnClose, envReport) {
|
|
34113
|
+
constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, exitOnClose, envReport, historyPath) {
|
|
33785
34114
|
this.agent = agent;
|
|
33786
34115
|
this.config = config;
|
|
33787
34116
|
this.exitOnClose = exitOnClose === true;
|
|
@@ -33794,7 +34123,7 @@ class Repl {
|
|
|
33794
34123
|
this.configDir = configDir || join49(homedir18(), ".mma");
|
|
33795
34124
|
this.baseDir = baseDir || process.cwd();
|
|
33796
34125
|
this.noAgentsMd = noAgentsMd === true;
|
|
33797
|
-
this.historyPath = join49(homedir18(), ".mma", "repl-history");
|
|
34126
|
+
this.historyPath = historyPath ?? join49(homedir18(), ".mma", "repl-history");
|
|
33798
34127
|
this.loadHistory();
|
|
33799
34128
|
this.rl = process.stdin.isTTY ? new LineEditor({
|
|
33800
34129
|
input: process.stdin,
|
|
@@ -33827,9 +34156,9 @@ class Repl {
|
|
|
33827
34156
|
this.setupListeners();
|
|
33828
34157
|
}
|
|
33829
34158
|
loadHistory() {
|
|
33830
|
-
if (
|
|
34159
|
+
if (existsSync53(this.historyPath)) {
|
|
33831
34160
|
try {
|
|
33832
|
-
const raw =
|
|
34161
|
+
const raw = readFileSync35(this.historyPath, "utf-8");
|
|
33833
34162
|
this.history = raw.split(`
|
|
33834
34163
|
`).filter(Boolean).slice(-this.maxHistory);
|
|
33835
34164
|
} catch {
|
|
@@ -33942,8 +34271,7 @@ class Repl {
|
|
|
33942
34271
|
let forceExitTimer = null;
|
|
33943
34272
|
process.on("SIGINT", () => {
|
|
33944
34273
|
if (this.agentRunning) {
|
|
33945
|
-
console.log(pc2.yellow(
|
|
33946
|
-
[Ctrl+C] Остановка агента... (ещё раз — принудительно)`));
|
|
34274
|
+
console.log(pc2.yellow(t("repl.ctrl_c_interrupt")));
|
|
33947
34275
|
this.agent.shutdown();
|
|
33948
34276
|
this.agentRunning = false;
|
|
33949
34277
|
if (forceExitTimer)
|
|
@@ -34073,6 +34401,10 @@ ${t("image.clipboard_empty")}`));
|
|
|
34073
34401
|
return;
|
|
34074
34402
|
}
|
|
34075
34403
|
this.activePlan = plan;
|
|
34404
|
+
if (this.lastPlanSig === null) {
|
|
34405
|
+
this.lastPlanSig = sig;
|
|
34406
|
+
return;
|
|
34407
|
+
}
|
|
34076
34408
|
if (sig !== this.lastPlanSig) {
|
|
34077
34409
|
this.lastPlanSig = sig;
|
|
34078
34410
|
renderer.planBlock(formatPlanChecklist(plan));
|
|
@@ -34213,7 +34545,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
34213
34545
|
join49(this.baseDir, ".mma", "AGENTS.md"),
|
|
34214
34546
|
join49(this.configDir, "AGENTS.md")
|
|
34215
34547
|
];
|
|
34216
|
-
const foundAgents = agentsMdCandidates.filter((p) =>
|
|
34548
|
+
const foundAgents = agentsMdCandidates.filter((p) => existsSync53(p));
|
|
34217
34549
|
if (foundAgents.length > 0) {
|
|
34218
34550
|
for (const p of foundAgents) {
|
|
34219
34551
|
row(t("repl.agents_label"), pc2.dim(p));
|
|
@@ -34259,7 +34591,8 @@ ${t("image.clipboard_empty")}`));
|
|
|
34259
34591
|
async probeLspBanner() {
|
|
34260
34592
|
const config = this.config.lsp ?? DEFAULT_LSP_CONFIG;
|
|
34261
34593
|
try {
|
|
34262
|
-
const
|
|
34594
|
+
const lspOnlyLogger = this.logger ? new FileOnlyLogger(this.logger) : undefined;
|
|
34595
|
+
const summary = await probeLspServers(config, this.baseDir, { logger: lspOnlyLogger });
|
|
34263
34596
|
this.slog.logSessionStart({
|
|
34264
34597
|
environment: this.envReport,
|
|
34265
34598
|
lspProbe: {
|
|
@@ -34322,10 +34655,9 @@ init_setup();
|
|
|
34322
34655
|
init_config2();
|
|
34323
34656
|
init_i18n();
|
|
34324
34657
|
init_colors();
|
|
34325
|
-
import { existsSync as
|
|
34326
|
-
import { join as join51
|
|
34658
|
+
import { existsSync as existsSync54 } from "fs";
|
|
34659
|
+
import { join as join51 } from "path";
|
|
34327
34660
|
import { homedir as homedir20 } from "os";
|
|
34328
|
-
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
34329
34661
|
|
|
34330
34662
|
// src/modules/updater/index.ts
|
|
34331
34663
|
init_checker();
|
|
@@ -34472,23 +34804,10 @@ function installCrashHandlers() {
|
|
|
34472
34804
|
}
|
|
34473
34805
|
|
|
34474
34806
|
// src/cli/main.ts
|
|
34475
|
-
|
|
34476
|
-
const here = dirname19(fileURLToPath7(import.meta.url));
|
|
34477
|
-
const candidates = [join51(here, "..", "..", "package.json"), join51(here, "..", "package.json")];
|
|
34478
|
-
for (const p of candidates) {
|
|
34479
|
-
if (existsSync56(p)) {
|
|
34480
|
-
try {
|
|
34481
|
-
const raw = JSON.parse(readFileSync38(p, "utf8"));
|
|
34482
|
-
if (raw.version)
|
|
34483
|
-
return raw.version;
|
|
34484
|
-
} catch {}
|
|
34485
|
-
}
|
|
34486
|
-
}
|
|
34487
|
-
return "0.0.0";
|
|
34488
|
-
}
|
|
34807
|
+
init_version();
|
|
34489
34808
|
function startAutoUpdate(config) {
|
|
34490
34809
|
try {
|
|
34491
|
-
const module = new UpdaterModule(config.updater,
|
|
34810
|
+
const module = new UpdaterModule(config.updater, readMmaVersion(), "micro-models-agent", {
|
|
34492
34811
|
info: (m) => process.stderr.write(pc2.dim(m) + `
|
|
34493
34812
|
`),
|
|
34494
34813
|
warn: (m) => process.stderr.write(pc2.yellow(m) + `
|
|
@@ -34564,7 +34883,7 @@ async function main() {
|
|
|
34564
34883
|
process.exit(exitCode);
|
|
34565
34884
|
} else {
|
|
34566
34885
|
const configPath = join51(homedir20(), ".mma", "config.json");
|
|
34567
|
-
if (!
|
|
34886
|
+
if (!existsSync54(configPath)) {
|
|
34568
34887
|
console.log(pc2.yellow(`
|
|
34569
34888
|
` + t("cli.first_run") + `
|
|
34570
34889
|
`));
|
|
@@ -34581,9 +34900,9 @@ async function main() {
|
|
|
34581
34900
|
config2.maxToolIterations = answers.maxToolIterations;
|
|
34582
34901
|
config2.locale = answers.locale;
|
|
34583
34902
|
if (config2.security) {
|
|
34584
|
-
config2.security.enabled =
|
|
34585
|
-
config2.security.bash.enabled =
|
|
34586
|
-
config2.security.bash.blockDangerousFlags = answers.securityFlagsBlock;
|
|
34903
|
+
config2.security.enabled = true;
|
|
34904
|
+
config2.security.bash.enabled = true;
|
|
34905
|
+
config2.security.bash.blockDangerousFlags = answers.securityFlagsBlock || config2.security.bash.blockDangerousFlags;
|
|
34587
34906
|
if (answers.securityBashBlock) {
|
|
34588
34907
|
config2.security.bash.blacklist = [
|
|
34589
34908
|
...new Set([
|