micro-models-agent 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/assets/app.js +22 -4
  3. package/dist/assets/index.html +29 -0
  4. package/dist/assets/js/dom.js +2 -1
  5. package/dist/assets/js/feed.js +38 -26
  6. package/dist/assets/js/format.js +1 -1
  7. package/dist/assets/js/rail.js +114 -0
  8. package/dist/assets/js/settings-controls.js +2 -2
  9. package/dist/assets/js/settings.js +246 -51
  10. package/dist/assets/js/store.js +3 -0
  11. package/dist/assets/js/tabs.js +104 -2
  12. package/dist/assets/styles.css +215 -66
  13. package/dist/cli/cache-line.js +1 -0
  14. package/dist/cli/command-suggest.js +5 -4
  15. package/dist/cli/commands.js +231 -122
  16. package/dist/cli/completer.js +132 -31
  17. package/dist/cli/index.js +1 -0
  18. package/dist/cli/json-payload.js +4 -4
  19. package/dist/cli/main.js +166 -61
  20. package/dist/cli/onboarding-commands.js +126 -0
  21. package/dist/cli/plugin-commands.js +2 -1
  22. package/dist/cli/relaunch.js +16 -13
  23. package/dist/cli/repl-commands-lsp.js +75 -0
  24. package/dist/cli/repl-commands-skills.js +105 -0
  25. package/dist/cli/repl-commands.js +589 -266
  26. package/dist/cli/repl.js +436 -177
  27. package/dist/cli/run-result.js +5 -6
  28. package/dist/cli/security-commands.js +37 -28
  29. package/dist/cli/setup-order.js +4 -55
  30. package/dist/cli/setup-prompt.js +3 -2
  31. package/dist/cli/setup.js +27 -107
  32. package/dist/cli/web-command.js +98 -0
  33. package/dist/config/budget.js +5 -5
  34. package/dist/config/config.js +103 -96
  35. package/dist/config/defaults.js +38 -8
  36. package/dist/config/domains.js +24 -27
  37. package/dist/config/env.js +46 -0
  38. package/dist/config/experts.js +2 -0
  39. package/dist/config/index.js +1 -0
  40. package/dist/config/paths.js +56 -0
  41. package/dist/config/redact.js +44 -0
  42. package/dist/config/registry-extra.js +334 -0
  43. package/dist/config/registry.js +584 -0
  44. package/dist/config/security.js +35 -36
  45. package/dist/config/timeouts.js +74 -0
  46. package/dist/config/utils.js +16 -15
  47. package/dist/core/agent/artifact-progress.js +59 -0
  48. package/dist/core/agent/artifact-resolve.js +79 -0
  49. package/dist/core/agent/audit-gate.js +7 -3
  50. package/dist/core/agent/compaction.js +2 -2
  51. package/dist/core/agent/constants.js +33 -4
  52. package/dist/core/agent/context-log.js +454 -0
  53. package/dist/core/agent/context-renderer.js +5 -0
  54. package/dist/core/agent/critic-gate.js +132 -0
  55. package/dist/core/agent/hallucination-gate.js +148 -38
  56. package/dist/core/agent/loop-state.js +15 -0
  57. package/dist/core/agent/prefix-monitor.js +1 -1
  58. package/dist/core/agent/reasoning-resolver.js +10 -1
  59. package/dist/core/agent/token-tracker.js +7 -4
  60. package/dist/core/agent/tool-batch.js +93 -18
  61. package/dist/core/agent/tool-images.js +18 -0
  62. package/dist/core/agent/tool-output.js +65 -11
  63. package/dist/core/agent/tool-pairing.js +60 -0
  64. package/dist/core/agent-moe.js +25 -24
  65. package/dist/core/agent.js +429 -108
  66. package/dist/core/bootstrap/agent-phase.js +66 -0
  67. package/dist/core/bootstrap/config-phase.js +86 -0
  68. package/dist/core/bootstrap/modules-phase.js +269 -0
  69. package/dist/core/bootstrap/provider-phase.js +183 -0
  70. package/dist/core/bootstrap/session-phase.js +204 -0
  71. package/dist/core/bootstrap/state.js +94 -0
  72. package/dist/core/bootstrap/workspace-phase.js +115 -0
  73. package/dist/core/bootstrap.js +92 -660
  74. package/dist/core/crash-handler.js +17 -7
  75. package/dist/core/environment.js +37 -12
  76. package/dist/core/host-plan.js +43 -0
  77. package/dist/core/host.js +23 -0
  78. package/dist/core/index.js +1 -0
  79. package/dist/core/prompt-builder.js +17 -1
  80. package/dist/core/prompt-overflow.js +42 -43
  81. package/dist/core/session-logger.js +122 -30
  82. package/dist/core/system-prompt.js +117 -0
  83. package/dist/core/version.js +6 -4
  84. package/dist/core/web-host.js +34 -0
  85. package/dist/core/workspace.js +19 -15
  86. package/dist/i18n/en.json +106 -21
  87. package/dist/i18n/index.js +37 -3
  88. package/dist/i18n/ru.json +109 -21
  89. package/dist/index.js +7 -3
  90. package/dist/llm/image-utils.js +55 -64
  91. package/dist/llm/llm-errors.js +9 -0
  92. package/dist/llm/model-loader.js +10 -32
  93. package/dist/llm/models-dev.js +121 -0
  94. package/dist/llm/openai-compat.js +146 -73
  95. package/dist/llm/orchestrator.js +33 -11
  96. package/dist/llm/provider-budget.js +2 -2
  97. package/dist/llm/provider-context.js +233 -0
  98. package/dist/llm/provider.js +1 -1
  99. package/dist/llm/response.js +5 -0
  100. package/dist/llm/token-counter.js +27 -11
  101. package/dist/logger/app-logger.js +48 -22
  102. package/dist/logger/file-log.js +28 -19
  103. package/dist/logger/index.js +1 -0
  104. package/dist/main.js +24285 -22447
  105. package/dist/migration/backup.js +14 -3
  106. package/dist/migration/detect.js +12 -3
  107. package/dist/migration/index.js +1 -0
  108. package/dist/modules/artifacts/store.js +9 -8
  109. package/dist/modules/browser/actions.js +6 -0
  110. package/dist/modules/browser/bridge-client.js +14 -12
  111. package/dist/modules/browser/bridge-path.js +4 -4
  112. package/dist/modules/browser/console-buffer.js +37 -0
  113. package/dist/modules/browser/cookie-store.js +5 -0
  114. package/dist/modules/browser/driver-types.js +1 -0
  115. package/dist/modules/browser/driver.js +19 -11
  116. package/dist/modules/browser/index.js +3 -1
  117. package/dist/modules/browser/module.js +2 -1
  118. package/dist/modules/browser/session.js +10 -30
  119. package/dist/modules/browser/snapshot.js +19 -13
  120. package/dist/modules/browser/types.js +1 -0
  121. package/dist/modules/certification/cli.js +28 -20
  122. package/dist/modules/certification/fact-checker.js +9 -1
  123. package/dist/modules/certification/loader.js +9 -2
  124. package/dist/modules/certification/manifest.js +83 -21
  125. package/dist/modules/certification/runner.js +23 -13
  126. package/dist/modules/certification/scenarios.js +20 -20
  127. package/dist/modules/certification/syntax-scenarios.js +1 -2
  128. package/dist/modules/context/chunk-query.js +11 -9
  129. package/dist/modules/context/fact-extractor.js +37 -39
  130. package/dist/modules/context/index.js +1 -0
  131. package/dist/modules/context/manager.js +106 -80
  132. package/dist/modules/critic/client.js +176 -0
  133. package/dist/modules/critic/evidence.js +81 -0
  134. package/dist/modules/critic/provider.js +45 -0
  135. package/dist/modules/critic/tools.js +78 -0
  136. package/dist/modules/critic/types.js +1 -0
  137. package/dist/modules/execution/audit-runners.js +28 -34
  138. package/dist/modules/execution/auditor.js +53 -49
  139. package/dist/modules/execution/execution-plugin.js +142 -102
  140. package/dist/modules/execution/index.js +1 -1
  141. package/dist/modules/execution/input-from.js +37 -13
  142. package/dist/modules/execution/module.js +368 -191
  143. package/dist/modules/execution/moe-executor.js +53 -26
  144. package/dist/modules/execution/plan-actions.js +320 -202
  145. package/dist/modules/execution/plan-checks.js +35 -0
  146. package/dist/modules/execution/plan-coverage.js +36 -33
  147. package/dist/modules/execution/plan-steps.js +11 -3
  148. package/dist/modules/execution/plan-store.js +83 -17
  149. package/dist/modules/execution/plan-tool.js +89 -35
  150. package/dist/modules/execution/plan-validator.js +30 -10
  151. package/dist/modules/execution/planner.js +22 -13
  152. package/dist/modules/execution/stuck-detector.js +196 -93
  153. package/dist/modules/execution/tracker.js +22 -8
  154. package/dist/modules/execution/verifier.js +69 -48
  155. package/dist/modules/execution/windows-commands.js +12 -12
  156. package/dist/modules/hallucination/artifact-claim.js +93 -0
  157. package/dist/modules/hallucination/confidence.js +13 -11
  158. package/dist/modules/hallucination/consistency.js +10 -2
  159. package/dist/modules/hallucination/detector.js +10 -5
  160. package/dist/modules/hallucination/factual.js +43 -26
  161. package/dist/modules/hallucination/index.js +1 -0
  162. package/dist/modules/hallucination/js-identifiers.js +44 -45
  163. package/dist/modules/hallucination/llm-judge.js +17 -12
  164. package/dist/modules/hallucination/success-claim.js +59 -0
  165. package/dist/modules/indexer/cache.js +52 -4
  166. package/dist/modules/indexer/index.js +1 -0
  167. package/dist/modules/indexer/map-command.js +2 -4
  168. package/dist/modules/indexer/map-select.js +16 -16
  169. package/dist/modules/indexer/module.js +47 -12
  170. package/dist/modules/indexer/project-profile.js +8 -7
  171. package/dist/modules/indexer/symbols.js +15 -15
  172. package/dist/modules/indexer/walker.js +159 -29
  173. package/dist/modules/lsp/check-tool.js +7 -5
  174. package/dist/modules/lsp/client.js +39 -39
  175. package/dist/modules/lsp/command.js +17 -24
  176. package/dist/modules/lsp/config.js +12 -11
  177. package/dist/modules/lsp/index.js +1 -0
  178. package/dist/modules/lsp/module.js +48 -46
  179. package/dist/modules/lsp/probe.js +13 -14
  180. package/dist/modules/lsp/project-root.js +14 -17
  181. package/dist/modules/lsp/startup-check.js +18 -16
  182. package/dist/modules/mcp/client.js +59 -62
  183. package/dist/modules/mcp/index.js +1 -0
  184. package/dist/modules/mcp/module.js +66 -37
  185. package/dist/modules/mcp/registry.js +5 -0
  186. package/dist/modules/memory/index.js +1 -0
  187. package/dist/modules/memory/module.js +12 -8
  188. package/dist/modules/memory/search.js +4 -1
  189. package/dist/modules/memory/store.js +40 -2
  190. package/dist/modules/onboarding/init.js +47 -0
  191. package/dist/modules/onboarding/meet.js +30 -0
  192. package/dist/modules/onboarding/mode.js +30 -0
  193. package/dist/modules/onboarding/prompts.js +43 -0
  194. package/dist/modules/pipelines/engine.js +10 -0
  195. package/dist/modules/pipelines/index.js +1 -0
  196. package/dist/modules/pipelines/parser.js +4 -1
  197. package/dist/modules/pipelines/template.js +4 -0
  198. package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
  199. package/dist/modules/plugins/builtin/notify.js +2 -0
  200. package/dist/modules/plugins/index.js +1 -0
  201. package/dist/modules/plugins/loader.js +18 -3
  202. package/dist/modules/plugins/manager.js +37 -17
  203. package/dist/modules/pricing/index.js +10 -11
  204. package/dist/modules/pricing/prices.js +12 -12
  205. package/dist/modules/processes/index.js +1 -0
  206. package/dist/modules/processes/kill-tree.js +27 -31
  207. package/dist/modules/processes/registry.js +44 -25
  208. package/dist/modules/processes/runner.js +4 -4
  209. package/dist/modules/providers/capabilities.js +28 -0
  210. package/dist/modules/providers/create.js +3 -4
  211. package/dist/modules/providers/factory.js +7 -7
  212. package/dist/modules/providers/fallback.js +12 -12
  213. package/dist/modules/providers/health.js +4 -4
  214. package/dist/modules/providers/manager.js +50 -19
  215. package/dist/modules/providers/naming.js +15 -0
  216. package/dist/modules/providers/preflight.js +71 -0
  217. package/dist/modules/providers/presets.js +39 -8
  218. package/dist/modules/providers/registry.js +7 -2
  219. package/dist/modules/reasoning/policy.js +8 -6
  220. package/dist/modules/reasoning/probe.js +30 -25
  221. package/dist/modules/security/audit-log.js +19 -36
  222. package/dist/modules/security/audit-notifier.js +35 -75
  223. package/dist/modules/security/command-validator.js +99 -51
  224. package/dist/modules/security/content-scanner.js +8 -8
  225. package/dist/modules/security/data-sanitizer.js +9 -16
  226. package/dist/modules/security/encryption.js +32 -52
  227. package/dist/modules/security/index.js +2 -1
  228. package/dist/modules/security/network-validator.js +120 -43
  229. package/dist/modules/security/path-validator.js +61 -46
  230. package/dist/modules/security/protected-paths.js +73 -0
  231. package/dist/modules/security/rate-limiter.js +17 -49
  232. package/dist/modules/security/security-policies.js +25 -57
  233. package/dist/modules/security/session-encryption.js +32 -71
  234. package/dist/modules/security/session-isolation.js +15 -28
  235. package/dist/modules/session/auto-name.js +63 -0
  236. package/dist/modules/session/export.js +98 -0
  237. package/dist/modules/session/index.js +1 -0
  238. package/dist/modules/session/manager.js +79 -28
  239. package/dist/modules/session/module.js +4 -3
  240. package/dist/modules/session/store.js +57 -20
  241. package/dist/modules/setup/answers.js +35 -0
  242. package/dist/modules/setup/apply.js +79 -0
  243. package/dist/modules/setup/discovery.js +72 -0
  244. package/dist/modules/skills/index.js +1 -0
  245. package/dist/modules/skills/loader.js +9 -1
  246. package/dist/modules/skills/module.js +11 -0
  247. package/dist/modules/updater/changelog-reader.js +15 -16
  248. package/dist/modules/updater/checker.js +9 -4
  249. package/dist/modules/updater/dev-detect.js +7 -6
  250. package/dist/modules/updater/index.js +1 -0
  251. package/dist/modules/updater/module.js +17 -15
  252. package/dist/modules/user-profile/compressor.js +2 -0
  253. package/dist/modules/user-profile/index.js +1 -0
  254. package/dist/modules/user-profile/profile.js +10 -1
  255. package/dist/modules/webui/answer-api.js +29 -0
  256. package/dist/modules/webui/assets.js +50 -0
  257. package/dist/modules/webui/body.js +21 -0
  258. package/dist/modules/webui/commands-settings.js +209 -0
  259. package/dist/modules/webui/commands-shared.js +60 -0
  260. package/dist/modules/webui/commands.js +174 -0
  261. package/dist/modules/webui/config-api.js +134 -0
  262. package/dist/modules/webui/file-api.js +202 -0
  263. package/dist/modules/webui/forward.js +159 -0
  264. package/dist/modules/webui/history-api.js +112 -0
  265. package/dist/modules/webui/host-state.js +60 -0
  266. package/dist/modules/webui/i18n-api.js +9 -0
  267. package/dist/modules/webui/limits.js +6 -0
  268. package/dist/modules/webui/log-forward.js +20 -0
  269. package/dist/modules/webui/memory-commands.js +64 -0
  270. package/dist/modules/webui/model-cache.js +18 -0
  271. package/dist/modules/webui/module.js +148 -0
  272. package/dist/modules/webui/persist.js +24 -0
  273. package/dist/modules/webui/prompt-io.js +97 -0
  274. package/dist/modules/webui/providers-api.js +337 -0
  275. package/dist/modules/webui/providers-view.js +56 -0
  276. package/dist/modules/webui/reasoning-view.js +15 -0
  277. package/dist/modules/webui/run-agent.js +81 -0
  278. package/dist/modules/webui/server.js +284 -0
  279. package/dist/modules/webui/sessions-api.js +178 -0
  280. package/dist/modules/webui/settings-api.js +561 -0
  281. package/dist/modules/webui/setup-api.js +201 -0
  282. package/dist/modules/webui/state.js +74 -0
  283. package/dist/modules/webui/util.js +261 -0
  284. package/dist/output/bus.js +7 -2
  285. package/dist/output/channel.js +80 -28
  286. package/dist/output/format.js +2 -1
  287. package/dist/output/index.js +1 -0
  288. package/dist/output/json-sink.js +5 -3
  289. package/dist/output/machine.js +4 -3
  290. package/dist/output/session-sink.js +4 -4
  291. package/dist/tools/approve.js +4 -0
  292. package/dist/tools/attach-image.js +5 -4
  293. package/dist/tools/auto-fixer.js +26 -44
  294. package/dist/tools/auto-tags.js +83 -0
  295. package/dist/tools/bash.js +193 -116
  296. package/dist/tools/browser.js +76 -31
  297. package/dist/tools/chunk-query.js +14 -6
  298. package/dist/tools/create-dir.js +9 -5
  299. package/dist/tools/delete-file.js +9 -5
  300. package/dist/tools/download-file.js +32 -7
  301. package/dist/tools/edit-file.js +52 -14
  302. package/dist/tools/enable-tools.js +6 -6
  303. package/dist/tools/executor.js +132 -27
  304. package/dist/tools/file-info.js +3 -1
  305. package/dist/tools/filter-tools.js +3 -3
  306. package/dist/tools/glob-tool.js +4 -1
  307. package/dist/tools/grep-tool.js +13 -10
  308. package/dist/tools/hidden-tools-block.js +6 -6
  309. package/dist/tools/index.js +16 -8
  310. package/dist/tools/list-dir.js +4 -2
  311. package/dist/tools/load-skill.js +7 -0
  312. package/dist/tools/mcp-call.js +15 -4
  313. package/dist/tools/meet-done.js +14 -0
  314. package/dist/tools/move-file.js +14 -7
  315. package/dist/tools/path-utils.js +29 -29
  316. package/dist/tools/pipeline-run.js +29 -7
  317. package/dist/tools/preview.js +1 -1
  318. package/dist/tools/process-ids.js +31 -0
  319. package/dist/tools/process-kill.js +9 -16
  320. package/dist/tools/process-list.js +1 -0
  321. package/dist/tools/process-log.js +11 -16
  322. package/dist/tools/process-wait.js +63 -0
  323. package/dist/tools/prompt-io.js +4 -3
  324. package/dist/tools/question.js +172 -25
  325. package/dist/tools/read-file.js +55 -34
  326. package/dist/tools/recall.js +14 -8
  327. package/dist/tools/registry.js +6 -5
  328. package/dist/tools/remember.js +8 -4
  329. package/dist/tools/scope-request.js +11 -6
  330. package/dist/tools/search-history.js +5 -2
  331. package/dist/tools/session-info.js +2 -0
  332. package/dist/tools/set-thinking.js +8 -4
  333. package/dist/tools/subagent.js +59 -39
  334. package/dist/tools/syntax-validator.js +292 -90
  335. package/dist/tools/user-input.js +22 -18
  336. package/dist/tools/web-browse.js +9 -3
  337. package/dist/tools/web-fetch.js +12 -23
  338. package/dist/tools/web-search.js +7 -5
  339. package/dist/tools/write-file.js +45 -15
  340. package/dist/ui/action-log.js +13 -0
  341. package/dist/ui/box.js +4 -3
  342. package/dist/ui/colors.js +2 -0
  343. package/dist/ui/diff.js +6 -5
  344. package/dist/ui/index.js +1 -0
  345. package/dist/ui/line-editor.js +230 -122
  346. package/dist/ui/line-math.js +10 -2
  347. package/dist/ui/md-formatter.js +57 -37
  348. package/dist/ui/output.js +8 -3
  349. package/dist/ui/plan-view.js +21 -10
  350. package/dist/ui/renderer.js +393 -67
  351. package/dist/ui/spinner.js +16 -5
  352. package/dist/ui/table.js +15 -17
  353. package/dist/ui/theme.js +143 -0
  354. package/dist/utils/child-run.js +20 -0
  355. package/dist/utils/error.js +1 -1
  356. package/dist/utils/index.js +2 -0
  357. package/dist/utils/retry.js +2 -2
  358. package/dist/utils/truncate.js +1 -1
  359. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ /** Тул перемещения/переименования файла или каталога. */
1
2
  import { renameSync, existsSync, mkdirSync } from "fs";
2
3
  import { resolve, normalize, dirname } from "path";
3
4
  import { t } from "../i18n/index";
@@ -6,6 +7,8 @@ import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
6
7
  import { getSessionSecurityConfig } from "../modules/security/session-isolation";
7
8
  import { generateMoveDiff } from "../ui/diff";
8
9
  import { safeResolvePath } from "./path-utils";
10
+ import { protectedPathBlock } from "../modules/security/protected-paths";
11
+ /** Перемещает/переименовывает; обе стороны проверяются по скоупу. */
9
12
  export const moveFileTool = {
10
13
  name: "move_file",
11
14
  icon: "📦",
@@ -25,11 +28,10 @@ export const moveFileTool = {
25
28
  const baseDir = resolve(ctx.baseDir);
26
29
  const fromResolved = safeResolvePath(baseDir, normalize(fromPath));
27
30
  const toResolved = safeResolvePath(baseDir, normalize(toPath));
28
- // Get session-specific security config
29
31
  const securityConfig = ctx.sessionContext
30
32
  ? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
31
33
  : ctx.config.security;
32
- // Check file operations limit
34
+ // Лимит файловых операций.
33
35
  const maxFileOps = securityConfig?.maxFileOperations ?? 100;
34
36
  const currentCount = ctx.fileOperationsCount ?? 0;
35
37
  if (currentCount >= maxFileOps) {
@@ -39,8 +41,8 @@ export const moveFileTool = {
39
41
  output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
40
42
  };
41
43
  }
42
- // Check source path permissions — against the RESOLVED path (the same
43
- // one renameSync will act on), never the raw argument.
44
+ // Права источника — по РЕЗОЛВЛЕННОМУ пути (по нему же работает renameSync),
45
+ // никогда по сырому аргументу.
44
46
  const fromCheck = isPathWritable(ctx.baseDir, fromResolved, ctx.scope, ctx.config.security?.paths);
45
47
  if (!fromCheck.allowed) {
46
48
  logSecurityBlock(ctx.sessionId, "file_write", fromCheck.reason || "Source path not allowed", fromPath);
@@ -51,7 +53,7 @@ export const moveFileTool = {
51
53
  }),
52
54
  };
53
55
  }
54
- // Check destination path permissions — resolved, same as above.
56
+ // Права приёмника — тоже по резолвленному.
55
57
  const toCheck = isPathWritable(ctx.baseDir, toResolved, ctx.scope, ctx.config.security?.paths);
56
58
  if (!toCheck.allowed) {
57
59
  logSecurityBlock(ctx.sessionId, "file_write", toCheck.reason || "Destination path not allowed", toPath);
@@ -62,6 +64,13 @@ export const moveFileTool = {
62
64
  }),
63
65
  };
64
66
  }
67
+ // Защищённые пути с любой стороны (.git/, .env, ключи) — спросить юзера.
68
+ const fromProtected = protectedPathBlock(ctx, fromResolved, fromPath);
69
+ if (fromProtected)
70
+ return fromProtected;
71
+ const toProtected = protectedPathBlock(ctx, toResolved, toPath);
72
+ if (toProtected)
73
+ return toProtected;
65
74
  if (!existsSync(fromResolved)) {
66
75
  return {
67
76
  success: false,
@@ -74,9 +83,7 @@ export const moveFileTool = {
74
83
  }
75
84
  renameSync(fromResolved, toResolved);
76
85
  const diff = generateMoveDiff(fromPath, toPath);
77
- // Increment file operations counter
78
86
  ctx.fileOperationsCount = currentCount + 1;
79
- // Log file move
80
87
  logFileWrite(ctx.sessionId, `${fromPath} -> ${toPath}`, true, "File moved");
81
88
  return {
82
89
  success: true,
@@ -1,20 +1,20 @@
1
+ /** Утилиты путей: нормализация, jail-проверки, резолв от baseDir. */
1
2
  import { resolve, normalize, dirname, basename, sep, relative, isAbsolute } from "path";
2
3
  import { existsSync } from "fs";
3
4
  /**
4
- * Normalize backslashes to forward slashes for display and comparison.
5
- * On POSIX this is a no-op; on Windows it converts `src\foo\bar.ts` → `src/foo/bar.ts`.
5
+ * Бэкслэши → прямые слэши для показа и сравнения.
6
+ * На POSIX no-op; на Windows `src\foo\bar.ts` → `src/foo/bar.ts`.
6
7
  */
7
8
  export function toForwardSlash(p) {
8
9
  return p.replace(/\\/g, "/");
9
10
  }
10
11
  /**
11
- * True when targetPath equals dirPath or lies inside it.
12
+ * True, если targetPath равен dirPath или внутри него.
12
13
  *
13
- * Cross-platform notes:
14
- * - Uses path.relative() instead of raw startsWith, so a sibling directory
15
- * sharing the string prefix ("src" vs "src-other") can never pass.
16
- * - On Windows the filesystem is case-insensitive: both sides are lowercased
17
- * before comparison so "E:\Proj" and "e:\proj" resolve to the same dir.
14
+ * Кроссплатформенно:
15
+ * - через path.relative(), а не raw startsWith: сосед с общим префиксом
16
+ * ("src" vs "src-other") не пройдёт;
17
+ * - на Windows ФС регистронезависима: обе стороны lowercased до сравнения.
18
18
  */
19
19
  export function isInsideDir(targetPath, dirPath) {
20
20
  const norm = (p) => (process.platform === "win32" ? p.toLowerCase() : p);
@@ -23,12 +23,13 @@ export function isInsideDir(targetPath, dirPath) {
23
23
  if (t === d)
24
24
  return true;
25
25
  const rel = relative(d, t);
26
- return rel.length > 0 && !isAbsolute(rel) && !rel.startsWith("..");
26
+ // Побег наверх — только реальный `..`/`../…`. Файл буквально `..foo`
27
+ // даёт rel === "..foo" и легально внутри.
28
+ return rel.length > 0 && !isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
27
29
  }
28
30
  /**
29
- * True when targetPath matches a scope entry: either the entry file itself or
30
- * anything inside the entry directory. Separator- and case-safe (see
31
- * isInsideDir).
31
+ * True, если targetPath — сам scope-entry или внутри него. Проверка
32
+ * сепараторов и регистра — как в isInsideDir.
32
33
  */
33
34
  export function matchesScopeEntry(targetResolved, entryResolved) {
34
35
  if (targetResolved === entryResolved)
@@ -36,33 +37,32 @@ export function matchesScopeEntry(targetResolved, entryResolved) {
36
37
  return isInsideDir(targetResolved, entryResolved);
37
38
  }
38
39
  /**
39
- * Resolve a user-provided path against baseDir.
40
+ * Резолв пользовательского пути от baseDir.
40
41
  *
41
- * Handles common model mistakes:
42
- * 1. Leading slash: "/testing/cat3" → resolve relative to baseDir
43
- * 2. Missing separator: "E:\agent_test" when baseDir is "E:\agent\_test"
44
- * → inserts the missing backslash
42
+ * Чинит типовые ошибки модели:
43
+ * 1. Ведущий слэш: "/testing/cat3" → относительно baseDir.
44
+ * 2. Пропущенный сепаратор: "E:\agent_test" при baseDir "E:\agent\_test"
45
+ * → вставка недостающего бэкслэша.
45
46
  *
46
- * For new files the parent directory must exist; we check that instead of
47
- * the full path so write_file still works.
47
+ * Для новых файлов обязан существовать родитель — проверяется он, а не
48
+ * полный путь, иначе write_file не сработает.
48
49
  */
49
50
  export function safeResolvePath(baseDir, userPath) {
50
- // Absolute path that actually exists — honor it as-is
51
+ // Существующий абсолютный путь — как есть.
51
52
  const asIs = resolve(normalize(userPath));
52
53
  if (userPath.startsWith("/") || userPath.startsWith("\\")) {
53
54
  if (existsSync(asIs) || existsSync(dirname(asIs)))
54
55
  return asIs;
55
56
  }
56
57
  const norm = normalize(userPath);
57
- // Absolute paths are used verbatim — do NOT strip a leading slash and
58
- // re-resolve against baseDir (that would turn /var/folders/... into
59
- // baseDir/var/folders/... on POSIX). The leading-slash heuristic below
60
- // only applies as a fallback when the absolute path does not resolve.
58
+ // Абсолютные пути — дословно, БЕЗ сдирания ведущего слэша и перерезолва
59
+ // от baseDir (иначе /var/folders/... станет baseDir/var/folders/... на POSIX).
60
+ // Эвристика ведущего слэша ниже — только fallback, когда путь не на диске.
61
61
  if (isAbsolute(norm)) {
62
62
  if (existsSync(norm) || existsSync(dirname(norm)))
63
63
  return norm;
64
- // Absolute but not on disk yet (e.g. a new file whose dirs don't exist).
65
- // Model mistake recovery: a leading slash may mean "relative to baseDir".
64
+ // Абсолютный, но ещё не на диске (новый файл без существующих dirs).
65
+ // Восстановление ошибки модели: ведущий слэш мог значить "от baseDir".
66
66
  const stripped = norm.replace(/^[/\\]/, "");
67
67
  const relativeCandidate = resolve(baseDir, stripped);
68
68
  if (existsSync(relativeCandidate) || existsSync(dirname(relativeCandidate))) {
@@ -72,11 +72,11 @@ export function safeResolvePath(baseDir, userPath) {
72
72
  }
73
73
  const stripped = norm.replace(/^[/\\]/, "");
74
74
  const resolved = resolve(baseDir, stripped);
75
- // Fast path: if it exists (or parent does for new files), return it
75
+ // Быстрый путь: существует сам (или родитель для новых файлов).
76
76
  if (existsSync(resolved) || existsSync(dirname(resolved)))
77
77
  return resolved;
78
78
  const baseNorm = normalize(baseDir);
79
- // Walk ancestors and look for missing separators
79
+ // Подъём по предкам в поиске пропущенных сепараторов.
80
80
  let cur = baseNorm;
81
81
  while (cur && cur !== dirname(cur)) {
82
82
  const name = basename(cur);
@@ -94,7 +94,7 @@ export function safeResolvePath(baseDir, userPath) {
94
94
  if (existsSync(fixedResolved) || existsSync(dirname(fixedResolved))) {
95
95
  return fixedResolved;
96
96
  }
97
- // Try from ancestor's parent
97
+ // Проба от родителя предка.
98
98
  const fromParent = resolve(dirname(cur), normalize(fixed));
99
99
  if (existsSync(fromParent) || existsSync(dirname(fromParent))) {
100
100
  return fromParent;
@@ -1,3 +1,4 @@
1
+ /** Тул прогона именованного YAML-пайплайна (DAG субагентов). */
1
2
  import { t } from "../i18n/index";
2
3
  import { PipelineEngine } from "../modules/pipelines/engine";
3
4
  import { PipelineParser } from "../modules/pipelines/parser";
@@ -8,9 +9,10 @@ import { PluginManager } from "../modules/plugins/manager";
8
9
  import { HallucinationDetector } from "../modules/hallucination/detector";
9
10
  import { logSecurityBlock } from "../modules/security/audit-log";
10
11
  import { getSessionSecurityConfig } from "../modules/security/session-isolation";
11
- const engine = new PipelineEngine();
12
+ /** Максимум параллельных шагов / попыток шага. */
12
13
  const MAX_CONCURRENT = 3;
13
14
  const MAX_ATTEMPTS = 3;
15
+ /** Прогоняет шаг пайплайна субагентом с ретраями (параметры/выходы — в промпт). */
14
16
  async function runStep(ctx, step, params, outputs) {
15
17
  const prompt = TemplateEngine.render(step.prompt, params, outputs);
16
18
  let lastError = "";
@@ -51,6 +53,7 @@ async function runStep(ctx, step, params, outputs) {
51
53
  }
52
54
  return { ok: false, output: "", error: lastError };
53
55
  }
56
+ /** Запускает DAG субагентов в порядке зависимостей. Параметры: name, yaml, params. */
54
57
  export const pipelineRunTool = {
55
58
  name: "pipeline_run",
56
59
  icon: "▶️",
@@ -75,14 +78,18 @@ export const pipelineRunTool = {
75
78
  if (!ctx.llmProvider || !ctx.toolExecutor) {
76
79
  return {
77
80
  success: false,
78
- output: "Pipeline cannot run: missing llmProvider or toolExecutor in context",
81
+ output: t("tool.pipeline_cannot_run"),
79
82
  };
80
83
  }
81
84
  const securityConfig = ctx.sessionContext
82
85
  ? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
83
86
  : ctx.config.security;
84
87
  const maxDepth = securityConfig?.maxRecursionDepth ?? 3;
85
- const currentDepth = ctx.recursionDepth ?? 0;
88
+ // Глубина — на SHARED-контексте исполнителя (вложенные субагенты/пайплайны
89
+ // читают её там); `ctx.recursionDepth` снапшотится раз и не инкрементится,
90
+ // поэтому старый гард не срабатывал. Зеркало subagent.ts.
91
+ const executor = ctx.toolExecutor;
92
+ const currentDepth = executor?.getRecursionDepth?.() ?? ctx.recursionDepth ?? 0;
86
93
  if (currentDepth >= maxDepth) {
87
94
  logSecurityBlock(ctx.sessionId, "bash_command", `Maximum recursion depth (${maxDepth}) exceeded`, name);
88
95
  return {
@@ -90,9 +97,12 @@ export const pipelineRunTool = {
90
97
  output: `[SECURITY BLOCKED] Maximum pipeline recursion depth (${maxDepth}) exceeded`,
91
98
  };
92
99
  }
100
+ // Свежий движок на вызов: module-level синглтон сбрасывался конкурентными
101
+ // вызовами (MoE-волны делят исполнитель) и портил состояние шагов.
102
+ const engine = new PipelineEngine();
103
+ executor?.setRecursionDepth?.(currentDepth + 1);
93
104
  try {
94
105
  const pipeline = PipelineParser.parse(yaml);
95
- engine.reset();
96
106
  const order = engine.resolveDependencies(pipeline.steps);
97
107
  const completed = new Set();
98
108
  const outputs = {};
@@ -130,16 +140,28 @@ export const pipelineRunTool = {
130
140
  if (failed.size > 0) {
131
141
  return {
132
142
  success: false,
133
- output: `Pipeline "${pipeline.name}" failed. Executed ${completed.size}/${pipeline.steps.length} steps.\n${logs.join("\n")}`,
143
+ output: t("tool.pipeline_failed", {
144
+ name: pipeline.name,
145
+ done: String(completed.size),
146
+ total: String(pipeline.steps.length),
147
+ logs: logs.join("\n"),
148
+ }),
134
149
  };
135
150
  }
136
151
  return {
137
152
  success: true,
138
- output: `Pipeline "${pipeline.name}" completed successfully (${completed.size} steps).\n${logs.join("\n")}`,
153
+ output: t("tool.pipeline_completed", {
154
+ name: pipeline.name,
155
+ steps: String(completed.size),
156
+ logs: logs.join("\n"),
157
+ }),
139
158
  };
140
159
  }
141
160
  catch (e) {
142
- return { success: false, output: `Pipeline error: ${e.message}` };
161
+ return { success: false, output: t("tool.pipeline_error", { error: e.message }) };
162
+ }
163
+ finally {
164
+ executor?.setRecursionDepth?.(currentDepth);
143
165
  }
144
166
  },
145
167
  };
@@ -1,2 +1,2 @@
1
- /** Maximum number of lines shown in tool output previews. */
1
+ /** Максимум строк в превью вывода тулов. */
2
2
  export const MAX_PREVIEW_LINES = 15;
@@ -0,0 +1,31 @@
1
+ /** Общие хелперы process_*-тулов: хинт без id, валидация + lookup. */
2
+ import { processRegistry } from "../modules/processes";
3
+ import { t } from "../i18n/index";
4
+ /**
5
+ * Общий ответ "id не дан" для process_*-тулов: хинт использования плюс
6
+ * известные сейчас id (или пометка пустого реестра). Одна копия, чтобы
7
+ * process-kill/log/wait не расходились.
8
+ */
9
+ export function missingProcessIdHint() {
10
+ const ids = processRegistry.list().map((p) => p.id);
11
+ const listPart = ids.length
12
+ ? `\n${t("proc.list_header")}: ${ids.join(", ")}`
13
+ : `\n${t("proc.missing_id_list")}`;
14
+ return `${t("proc.missing_id")}${listPart}`;
15
+ }
16
+ /**
17
+ * Общая валидация id + lookup в реестре для process_*-тулов. Возвращает запись
18
+ * либо `{ ok: false, output }` для прямого возврата (нет id — хинт,
19
+ * неизвестный id — not-found).
20
+ */
21
+ export function lookupProcess(args) {
22
+ if (args.id === undefined || String(args.id).trim() === "") {
23
+ return { ok: false, output: missingProcessIdHint() };
24
+ }
25
+ const id = String(args.id);
26
+ const entry = processRegistry.get(id);
27
+ if (!entry) {
28
+ return { ok: false, output: t("proc.not_found", { id }) };
29
+ }
30
+ return { ok: true, id, entry };
31
+ }
@@ -1,5 +1,7 @@
1
1
  import { processRegistry } from "../modules/processes";
2
2
  import { t } from "../i18n/index";
3
+ import { lookupProcess } from "./process-ids";
4
+ /** Останавливает фоновый процесс по id из bash/process_list. */
3
5
  export const processKillTool = {
4
6
  name: "process_kill",
5
7
  icon: "⛔",
@@ -12,22 +14,13 @@ export const processKillTool = {
12
14
  },
13
15
  required: ["id"],
14
16
  },
15
- handler: async (ctx, args) => {
16
- if (args.id === undefined || String(args.id).trim() === "") {
17
- // ses_mt4fn58c: `process_kill -p=5173` produced "Process not found:
18
- // undefined" — no hint about the correct format. Answer with usage.
19
- const known = processRegistry.list();
20
- const ids = known.map((p) => p.id);
21
- const listPart = ids.length
22
- ? `\n${t("proc.list_header")}: ${ids.join(", ")}`
23
- : `\n${t("proc.missing_id_list")}`;
24
- return { success: false, output: `${t("proc.missing_id")}${listPart}` };
25
- }
26
- const id = String(args.id);
27
- const entry = processRegistry.get(id);
28
- if (!entry) {
29
- return { success: false, output: t("proc.not_found", { id }) };
30
- }
17
+ handler: async (_ctx, args) => {
18
+ // Без id отвечаем использованием: голое "not found: undefined" не
19
+ // подсказывает верный формат.
20
+ const found = lookupProcess(args);
21
+ if (!found.ok)
22
+ return { success: false, output: found.output };
23
+ const { id, entry } = found;
31
24
  const killed = processRegistry.kill(id);
32
25
  if (!killed) {
33
26
  return { success: false, output: t("proc.kill_failed", { id }) };
@@ -1,5 +1,6 @@
1
1
  import { processRegistry } from "../modules/processes";
2
2
  import { t } from "../i18n/index";
3
+ /** Показывает фоновые процессы сессии с хинтом управления. */
3
4
  export const processListTool = {
4
5
  name: "process_list",
5
6
  icon: "📊",
@@ -1,11 +1,15 @@
1
1
  import { processRegistry } from "../modules/processes";
2
2
  import { t } from "../i18n/index";
3
3
  import { MAX_PREVIEW_LINES } from "./preview";
4
+ import { lookupProcess } from "./process-ids";
4
5
  const DEFAULT_TAIL = MAX_PREVIEW_LINES;
6
+ /** Потолок из описания тула; тул вне усечения по бюджету. */
7
+ const MAX_TAIL = 300;
8
+ /** Показывает буферизированный вывод фонового процесса (последние N строк). */
5
9
  export const processLogTool = {
6
10
  name: "process_log",
7
11
  icon: "📜",
8
- description: `Show the buffered output of a background process started via bash. Shows the last ${DEFAULT_TAIL} lines by default. Use after starting a dev server to verify it came up without errors, and while it runs to check its state.`,
12
+ description: `Show the buffered output of a background process started via bash. Shows the last ${DEFAULT_TAIL} lines by default. Use after starting a dev server to verify it came up without errors, and while it runs to check its state. For a finite task that should complete, prefer process_wait over polling this tool.`,
9
13
  tags: ["shell"],
10
14
  boundedOutput: true,
11
15
  parameters: {
@@ -19,26 +23,17 @@ export const processLogTool = {
19
23
  },
20
24
  required: ["id"],
21
25
  },
22
- handler: async (ctx, args) => {
23
- if (args.id === undefined || String(args.id).trim() === "") {
24
- const known = processRegistry.list();
25
- const ids = known.map((p) => p.id);
26
- const listPart = ids.length
27
- ? `\n${t("proc.list_header")}: ${ids.join(", ")}`
28
- : `\n${t("proc.missing_id_list")}`;
29
- return { success: false, output: `${t("proc.missing_id")}${listPart}` };
30
- }
31
- const id = String(args.id);
32
- const entry = processRegistry.get(id);
33
- if (!entry) {
34
- return { success: false, output: t("proc.not_found", { id }) };
35
- }
26
+ handler: async (_ctx, args) => {
27
+ const found = lookupProcess(args);
28
+ if (!found.ok)
29
+ return { success: false, output: found.output };
30
+ const { id, entry } = found;
36
31
  const status = entry.status === "running"
37
32
  ? t("proc.status_running")
38
33
  : entry.status === "exited"
39
34
  ? t("proc.status_exited")
40
35
  : t("proc.status_killed");
41
- const tail = typeof args.tail === "number" ? args.tail : DEFAULT_TAIL;
36
+ const tail = Math.min(MAX_TAIL, Math.max(1, typeof args.tail === "number" ? Math.floor(args.tail) : DEFAULT_TAIL));
42
37
  const log = processRegistry.getLog(id, tail);
43
38
  if (!log) {
44
39
  return {
@@ -0,0 +1,63 @@
1
+ import { processRegistry } from "../modules/processes";
2
+ import { t } from "../i18n/index";
3
+ import { lookupProcess } from "./process-ids";
4
+ import { PROCESS_WAIT_DEFAULT_MS as DEFAULT_WAIT_MS, PROCESS_WAIT_MAX_MS as MAX_WAIT_MS, } from "../config/timeouts";
5
+ /**
6
+ * Дефолтное ожидание: хватает на типичный тест/билд, но в пределах таймаута
7
+ * исполнителя. `timeoutMs` тула даёт запас под явное долгое ожидание
8
+ * (кламп MAX_WAIT_MS).
9
+ */
10
+ const DEFAULT_TAIL = 20;
11
+ const MAX_TAIL = 100;
12
+ /** Ждёт выхода процесса до timeoutMs; отчитывается кодом и хвостом вывода. */
13
+ export const processWaitTool = {
14
+ name: "process_wait",
15
+ icon: "⏳",
16
+ description: "Wait for a background process started via bash to finish, instead of polling process_log or sleeping (ping/timeout/sleep commands). Blocks up to timeoutMs, then reports whether the process exited (with its exit code and recent output) or is still running. For dev servers/watchers that never exit, use process_log to inspect and process_kill to stop them.",
17
+ tags: ["shell"],
18
+ boundedOutput: true,
19
+ timeoutMs: 120000,
20
+ parameters: {
21
+ type: "object",
22
+ properties: {
23
+ id: { type: "string", description: "Process id from bash output or process_list" },
24
+ timeoutMs: {
25
+ type: "number",
26
+ description: `How long to wait in milliseconds (default: ${DEFAULT_WAIT_MS}, max ${MAX_WAIT_MS})`,
27
+ },
28
+ tail: {
29
+ type: "number",
30
+ description: `Number of trailing output lines to include (default: ${DEFAULT_TAIL}, max ${MAX_TAIL})`,
31
+ },
32
+ },
33
+ required: ["id"],
34
+ },
35
+ handler: async (_ctx, args) => {
36
+ const found = lookupProcess(args);
37
+ if (!found.ok)
38
+ return { success: false, output: found.output };
39
+ const { id } = found;
40
+ const waitMs = Math.min(MAX_WAIT_MS, Math.max(1000, typeof args.timeoutMs === "number" ? Math.floor(args.timeoutMs) : DEFAULT_WAIT_MS));
41
+ const started = Date.now();
42
+ await processRegistry.waitForExit(id, waitMs);
43
+ const elapsed = Date.now() - started;
44
+ const entry = processRegistry.get(id);
45
+ if (!entry) {
46
+ return { success: false, output: t("proc.not_found", { id }) };
47
+ }
48
+ const tail = Math.min(MAX_TAIL, Math.max(1, typeof args.tail === "number" ? Math.floor(args.tail) : DEFAULT_TAIL));
49
+ const log = processRegistry.getLog(id, tail);
50
+ const logPart = log ? `\n${log}` : `\n${t("proc.log_empty")}`;
51
+ if (entry.status !== "running") {
52
+ const status = entry.status === "exited" ? t("proc.status_exited") : t("proc.status_killed");
53
+ return {
54
+ success: true,
55
+ output: `${t("proc.wait_exited", { id, ms: elapsed, status, code: entry.exitCode ?? "?" })}${logPart}`,
56
+ };
57
+ }
58
+ return {
59
+ success: true,
60
+ output: `${t("proc.wait_running", { id, ms: elapsed })}\n${t("proc.wait_hint", { id })}${logPart}`,
61
+ };
62
+ },
63
+ };
@@ -1,6 +1,7 @@
1
+ /** Терминал-независимая поверхность промпта для интерактивных тулов. */
1
2
  import { formatMenu, choicePrompt, parseSelection, CUSTOM_INDEX, } from "./user-input";
2
3
  import { t } from "../i18n/index";
3
- /** Build a PromptIO from injectable ask/print primitives (testable, terminal-agnostic). */
4
+ /** Собирает PromptIO из инжектируемых ask/print (тестируемо, без терминала). */
4
5
  export function createPromptIO(deps) {
5
6
  return {
6
7
  async askText(question) {
@@ -9,8 +10,8 @@ export function createPromptIO(deps) {
9
10
  async askChoice(question, options, opts = {}) {
10
11
  const multiple = opts.multiple ?? false;
11
12
  const entryCount = options.length + (opts.allowCustom ? 1 : 0);
12
- // No selectable entries (and no custom entry) — parseSelection can never
13
- // succeed, so return without printing a dead menu or looping forever.
13
+ // Выбирать нечего (и кастома нет) — parseSelection не сойдётся никогда,
14
+ // поэтому выходим без печати мёртвого меню и вечного цикла.
14
15
  if (entryCount === 0)
15
16
  return [];
16
17
  const customEntry = options.length;