micro-models-agent 2.11.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.
- package/CHANGELOG.md +10 -0
- package/dist/assets/js/tabs.js +22 -3
- package/dist/cli/cache-line.js +1 -0
- package/dist/cli/command-suggest.js +5 -4
- package/dist/cli/commands.js +231 -122
- package/dist/cli/completer.js +132 -31
- package/dist/cli/index.js +1 -0
- package/dist/cli/json-payload.js +4 -4
- package/dist/cli/main.js +166 -61
- package/dist/cli/onboarding-commands.js +126 -0
- package/dist/cli/plugin-commands.js +2 -1
- package/dist/cli/relaunch.js +16 -13
- package/dist/cli/repl-commands-lsp.js +75 -0
- package/dist/cli/repl-commands-skills.js +105 -0
- package/dist/cli/repl-commands.js +589 -266
- package/dist/cli/repl.js +436 -177
- package/dist/cli/run-result.js +5 -6
- package/dist/cli/security-commands.js +37 -28
- package/dist/cli/setup-order.js +4 -55
- package/dist/cli/setup-prompt.js +3 -2
- package/dist/cli/setup.js +27 -107
- package/dist/cli/web-command.js +98 -0
- package/dist/config/budget.js +5 -5
- package/dist/config/config.js +103 -96
- package/dist/config/defaults.js +38 -8
- package/dist/config/domains.js +24 -27
- package/dist/config/env.js +46 -0
- package/dist/config/experts.js +2 -0
- package/dist/config/index.js +1 -0
- package/dist/config/paths.js +56 -0
- package/dist/config/redact.js +44 -0
- package/dist/config/registry-extra.js +334 -0
- package/dist/config/registry.js +584 -0
- package/dist/config/security.js +35 -36
- package/dist/config/timeouts.js +74 -0
- package/dist/config/utils.js +16 -15
- package/dist/core/agent/artifact-progress.js +59 -0
- package/dist/core/agent/artifact-resolve.js +79 -0
- package/dist/core/agent/audit-gate.js +7 -3
- package/dist/core/agent/compaction.js +2 -2
- package/dist/core/agent/constants.js +33 -4
- package/dist/core/agent/context-log.js +454 -0
- package/dist/core/agent/context-renderer.js +5 -0
- package/dist/core/agent/critic-gate.js +132 -0
- package/dist/core/agent/hallucination-gate.js +148 -38
- package/dist/core/agent/loop-state.js +15 -0
- package/dist/core/agent/prefix-monitor.js +1 -1
- package/dist/core/agent/reasoning-resolver.js +10 -1
- package/dist/core/agent/token-tracker.js +7 -4
- package/dist/core/agent/tool-batch.js +93 -18
- package/dist/core/agent/tool-images.js +18 -0
- package/dist/core/agent/tool-output.js +65 -11
- package/dist/core/agent/tool-pairing.js +60 -0
- package/dist/core/agent-moe.js +25 -24
- package/dist/core/agent.js +429 -108
- package/dist/core/bootstrap/agent-phase.js +66 -0
- package/dist/core/bootstrap/config-phase.js +86 -0
- package/dist/core/bootstrap/modules-phase.js +269 -0
- package/dist/core/bootstrap/provider-phase.js +183 -0
- package/dist/core/bootstrap/session-phase.js +204 -0
- package/dist/core/bootstrap/state.js +94 -0
- package/dist/core/bootstrap/workspace-phase.js +115 -0
- package/dist/core/bootstrap.js +92 -660
- package/dist/core/crash-handler.js +17 -7
- package/dist/core/environment.js +37 -12
- package/dist/core/host-plan.js +43 -0
- package/dist/core/host.js +23 -0
- package/dist/core/index.js +1 -0
- package/dist/core/prompt-builder.js +17 -1
- package/dist/core/prompt-overflow.js +42 -43
- package/dist/core/session-logger.js +122 -30
- package/dist/core/system-prompt.js +117 -0
- package/dist/core/version.js +6 -4
- package/dist/core/web-host.js +34 -0
- package/dist/core/workspace.js +19 -15
- package/dist/i18n/index.js +37 -3
- package/dist/index.js +7 -3
- package/dist/llm/image-utils.js +55 -64
- package/dist/llm/llm-errors.js +9 -0
- package/dist/llm/model-loader.js +10 -32
- package/dist/llm/models-dev.js +121 -0
- package/dist/llm/openai-compat.js +146 -73
- package/dist/llm/orchestrator.js +33 -11
- package/dist/llm/provider-budget.js +2 -2
- package/dist/llm/provider-context.js +233 -0
- package/dist/llm/provider.js +1 -1
- package/dist/llm/response.js +5 -0
- package/dist/llm/token-counter.js +27 -11
- package/dist/logger/app-logger.js +48 -22
- package/dist/logger/file-log.js +28 -19
- package/dist/logger/index.js +1 -0
- package/dist/main.js +273 -47
- package/dist/migration/backup.js +14 -3
- package/dist/migration/detect.js +12 -3
- package/dist/migration/index.js +1 -0
- package/dist/modules/artifacts/store.js +9 -8
- package/dist/modules/browser/actions.js +6 -0
- package/dist/modules/browser/bridge-client.js +14 -12
- package/dist/modules/browser/bridge-path.js +4 -4
- package/dist/modules/browser/console-buffer.js +37 -0
- package/dist/modules/browser/cookie-store.js +5 -0
- package/dist/modules/browser/driver-types.js +1 -0
- package/dist/modules/browser/driver.js +19 -11
- package/dist/modules/browser/index.js +3 -1
- package/dist/modules/browser/module.js +2 -1
- package/dist/modules/browser/session.js +10 -30
- package/dist/modules/browser/snapshot.js +19 -13
- package/dist/modules/browser/types.js +1 -0
- package/dist/modules/certification/cli.js +28 -20
- package/dist/modules/certification/fact-checker.js +9 -1
- package/dist/modules/certification/loader.js +9 -2
- package/dist/modules/certification/manifest.js +83 -21
- package/dist/modules/certification/runner.js +23 -13
- package/dist/modules/certification/scenarios.js +20 -20
- package/dist/modules/certification/syntax-scenarios.js +1 -2
- package/dist/modules/context/chunk-query.js +11 -9
- package/dist/modules/context/fact-extractor.js +37 -39
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +106 -80
- package/dist/modules/critic/client.js +176 -0
- package/dist/modules/critic/evidence.js +81 -0
- package/dist/modules/critic/provider.js +45 -0
- package/dist/modules/critic/tools.js +78 -0
- package/dist/modules/critic/types.js +1 -0
- package/dist/modules/execution/audit-runners.js +28 -34
- package/dist/modules/execution/auditor.js +53 -49
- package/dist/modules/execution/execution-plugin.js +142 -102
- package/dist/modules/execution/index.js +1 -1
- package/dist/modules/execution/input-from.js +37 -13
- package/dist/modules/execution/module.js +368 -191
- package/dist/modules/execution/moe-executor.js +53 -26
- package/dist/modules/execution/plan-actions.js +320 -202
- package/dist/modules/execution/plan-checks.js +35 -0
- package/dist/modules/execution/plan-coverage.js +36 -33
- package/dist/modules/execution/plan-steps.js +11 -3
- package/dist/modules/execution/plan-store.js +83 -17
- package/dist/modules/execution/plan-tool.js +89 -35
- package/dist/modules/execution/plan-validator.js +30 -10
- package/dist/modules/execution/planner.js +22 -13
- package/dist/modules/execution/stuck-detector.js +196 -93
- package/dist/modules/execution/tracker.js +22 -8
- package/dist/modules/execution/verifier.js +69 -48
- package/dist/modules/execution/windows-commands.js +12 -12
- package/dist/modules/hallucination/artifact-claim.js +93 -0
- package/dist/modules/hallucination/confidence.js +13 -11
- package/dist/modules/hallucination/consistency.js +10 -2
- package/dist/modules/hallucination/detector.js +10 -5
- package/dist/modules/hallucination/factual.js +43 -26
- package/dist/modules/hallucination/index.js +1 -0
- package/dist/modules/hallucination/js-identifiers.js +44 -45
- package/dist/modules/hallucination/llm-judge.js +17 -12
- package/dist/modules/hallucination/success-claim.js +59 -0
- package/dist/modules/indexer/cache.js +52 -4
- package/dist/modules/indexer/index.js +1 -0
- package/dist/modules/indexer/map-command.js +2 -4
- package/dist/modules/indexer/map-select.js +16 -16
- package/dist/modules/indexer/module.js +47 -12
- package/dist/modules/indexer/project-profile.js +8 -7
- package/dist/modules/indexer/symbols.js +15 -15
- package/dist/modules/indexer/walker.js +159 -29
- package/dist/modules/lsp/check-tool.js +7 -5
- package/dist/modules/lsp/client.js +39 -39
- package/dist/modules/lsp/command.js +17 -24
- package/dist/modules/lsp/config.js +12 -11
- package/dist/modules/lsp/index.js +1 -0
- package/dist/modules/lsp/module.js +48 -46
- package/dist/modules/lsp/probe.js +13 -14
- package/dist/modules/lsp/project-root.js +14 -17
- package/dist/modules/lsp/startup-check.js +18 -16
- package/dist/modules/mcp/client.js +59 -62
- package/dist/modules/mcp/index.js +1 -0
- package/dist/modules/mcp/module.js +66 -37
- package/dist/modules/mcp/registry.js +5 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +12 -8
- package/dist/modules/memory/search.js +4 -1
- package/dist/modules/memory/store.js +40 -2
- package/dist/modules/onboarding/init.js +47 -0
- package/dist/modules/onboarding/meet.js +30 -0
- package/dist/modules/onboarding/mode.js +30 -0
- package/dist/modules/onboarding/prompts.js +43 -0
- package/dist/modules/pipelines/engine.js +10 -0
- package/dist/modules/pipelines/index.js +1 -0
- package/dist/modules/pipelines/parser.js +4 -1
- package/dist/modules/pipelines/template.js +4 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
- package/dist/modules/plugins/builtin/notify.js +2 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +18 -3
- package/dist/modules/plugins/manager.js +37 -17
- package/dist/modules/pricing/index.js +10 -11
- package/dist/modules/pricing/prices.js +12 -12
- package/dist/modules/processes/index.js +1 -0
- package/dist/modules/processes/kill-tree.js +27 -31
- package/dist/modules/processes/registry.js +44 -25
- package/dist/modules/processes/runner.js +4 -4
- package/dist/modules/providers/capabilities.js +28 -0
- package/dist/modules/providers/create.js +3 -4
- package/dist/modules/providers/factory.js +7 -7
- package/dist/modules/providers/fallback.js +12 -12
- package/dist/modules/providers/health.js +4 -4
- package/dist/modules/providers/manager.js +50 -19
- package/dist/modules/providers/naming.js +15 -0
- package/dist/modules/providers/preflight.js +71 -0
- package/dist/modules/providers/presets.js +39 -8
- package/dist/modules/providers/registry.js +7 -2
- package/dist/modules/reasoning/policy.js +8 -6
- package/dist/modules/reasoning/probe.js +30 -25
- package/dist/modules/security/audit-log.js +19 -36
- package/dist/modules/security/audit-notifier.js +35 -75
- package/dist/modules/security/command-validator.js +99 -51
- package/dist/modules/security/content-scanner.js +8 -8
- package/dist/modules/security/data-sanitizer.js +9 -16
- package/dist/modules/security/encryption.js +32 -52
- package/dist/modules/security/index.js +2 -1
- package/dist/modules/security/network-validator.js +120 -43
- package/dist/modules/security/path-validator.js +61 -46
- package/dist/modules/security/protected-paths.js +73 -0
- package/dist/modules/security/rate-limiter.js +17 -49
- package/dist/modules/security/security-policies.js +25 -57
- package/dist/modules/security/session-encryption.js +32 -71
- package/dist/modules/security/session-isolation.js +15 -28
- package/dist/modules/session/auto-name.js +63 -0
- package/dist/modules/session/export.js +98 -0
- package/dist/modules/session/index.js +1 -0
- package/dist/modules/session/manager.js +79 -28
- package/dist/modules/session/module.js +4 -3
- package/dist/modules/session/store.js +57 -20
- package/dist/modules/setup/answers.js +35 -0
- package/dist/modules/setup/apply.js +79 -0
- package/dist/modules/setup/discovery.js +72 -0
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/loader.js +9 -1
- package/dist/modules/skills/module.js +11 -0
- package/dist/modules/updater/changelog-reader.js +15 -16
- package/dist/modules/updater/checker.js +9 -4
- package/dist/modules/updater/dev-detect.js +7 -6
- package/dist/modules/updater/index.js +1 -0
- package/dist/modules/updater/module.js +17 -15
- package/dist/modules/user-profile/compressor.js +2 -0
- package/dist/modules/user-profile/index.js +1 -0
- package/dist/modules/user-profile/profile.js +10 -1
- package/dist/modules/webui/answer-api.js +29 -0
- package/dist/modules/webui/assets.js +50 -0
- package/dist/modules/webui/body.js +21 -0
- package/dist/modules/webui/commands-settings.js +209 -0
- package/dist/modules/webui/commands-shared.js +60 -0
- package/dist/modules/webui/commands.js +174 -0
- package/dist/modules/webui/config-api.js +134 -0
- package/dist/modules/webui/file-api.js +202 -0
- package/dist/modules/webui/forward.js +159 -0
- package/dist/modules/webui/history-api.js +112 -0
- package/dist/modules/webui/host-state.js +60 -0
- package/dist/modules/webui/i18n-api.js +9 -0
- package/dist/modules/webui/limits.js +6 -0
- package/dist/modules/webui/log-forward.js +20 -0
- package/dist/modules/webui/memory-commands.js +64 -0
- package/dist/modules/webui/model-cache.js +18 -0
- package/dist/modules/webui/module.js +148 -0
- package/dist/modules/webui/persist.js +24 -0
- package/dist/modules/webui/prompt-io.js +97 -0
- package/dist/modules/webui/providers-api.js +337 -0
- package/dist/modules/webui/providers-view.js +56 -0
- package/dist/modules/webui/reasoning-view.js +15 -0
- package/dist/modules/webui/run-agent.js +81 -0
- package/dist/modules/webui/server.js +284 -0
- package/dist/modules/webui/sessions-api.js +178 -0
- package/dist/modules/webui/settings-api.js +561 -0
- package/dist/modules/webui/setup-api.js +201 -0
- package/dist/modules/webui/state.js +74 -0
- package/dist/modules/webui/util.js +261 -0
- package/dist/output/bus.js +7 -2
- package/dist/output/channel.js +80 -28
- package/dist/output/format.js +2 -1
- package/dist/output/index.js +1 -0
- package/dist/output/json-sink.js +5 -3
- package/dist/output/machine.js +4 -3
- package/dist/output/session-sink.js +4 -4
- package/dist/tools/approve.js +4 -0
- package/dist/tools/attach-image.js +5 -4
- package/dist/tools/auto-fixer.js +26 -44
- package/dist/tools/auto-tags.js +83 -0
- package/dist/tools/bash.js +193 -116
- package/dist/tools/browser.js +76 -31
- package/dist/tools/chunk-query.js +14 -6
- package/dist/tools/create-dir.js +9 -5
- package/dist/tools/delete-file.js +9 -5
- package/dist/tools/download-file.js +32 -7
- package/dist/tools/edit-file.js +52 -14
- package/dist/tools/enable-tools.js +6 -6
- package/dist/tools/executor.js +132 -27
- package/dist/tools/file-info.js +3 -1
- package/dist/tools/filter-tools.js +3 -3
- package/dist/tools/glob-tool.js +4 -1
- package/dist/tools/grep-tool.js +13 -10
- package/dist/tools/hidden-tools-block.js +6 -6
- package/dist/tools/index.js +16 -8
- package/dist/tools/list-dir.js +4 -2
- package/dist/tools/load-skill.js +7 -0
- package/dist/tools/mcp-call.js +15 -4
- package/dist/tools/meet-done.js +14 -0
- package/dist/tools/move-file.js +14 -7
- package/dist/tools/path-utils.js +29 -29
- package/dist/tools/pipeline-run.js +29 -7
- package/dist/tools/preview.js +1 -1
- package/dist/tools/process-ids.js +31 -0
- package/dist/tools/process-kill.js +9 -16
- package/dist/tools/process-list.js +1 -0
- package/dist/tools/process-log.js +11 -16
- package/dist/tools/process-wait.js +63 -0
- package/dist/tools/prompt-io.js +4 -3
- package/dist/tools/question.js +172 -25
- package/dist/tools/read-file.js +55 -34
- package/dist/tools/recall.js +14 -8
- package/dist/tools/registry.js +6 -5
- package/dist/tools/remember.js +8 -4
- package/dist/tools/scope-request.js +11 -6
- package/dist/tools/search-history.js +5 -2
- package/dist/tools/session-info.js +2 -0
- package/dist/tools/set-thinking.js +8 -4
- package/dist/tools/subagent.js +59 -39
- package/dist/tools/syntax-validator.js +292 -90
- package/dist/tools/user-input.js +22 -18
- package/dist/tools/web-browse.js +9 -3
- package/dist/tools/web-fetch.js +12 -23
- package/dist/tools/web-search.js +7 -5
- package/dist/tools/write-file.js +45 -15
- package/dist/ui/action-log.js +13 -0
- package/dist/ui/box.js +4 -3
- package/dist/ui/colors.js +2 -0
- package/dist/ui/diff.js +6 -5
- package/dist/ui/index.js +1 -0
- package/dist/ui/line-editor.js +230 -122
- package/dist/ui/line-math.js +10 -2
- package/dist/ui/md-formatter.js +57 -37
- package/dist/ui/output.js +8 -3
- package/dist/ui/plan-view.js +21 -10
- package/dist/ui/renderer.js +393 -67
- package/dist/ui/spinner.js +16 -5
- package/dist/ui/table.js +15 -17
- package/dist/ui/theme.js +143 -0
- package/dist/utils/child-run.js +20 -0
- package/dist/utils/error.js +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/retry.js +2 -2
- package/dist/utils/truncate.js +1 -1
- package/package.json +1 -1
package/dist/tools/browser.js
CHANGED
|
@@ -1,36 +1,72 @@
|
|
|
1
1
|
import { BrowserSession } from "../modules/browser/session";
|
|
2
2
|
import { DEFAULT_BROWSER_CONFIG } from "../modules/browser/types";
|
|
3
|
+
import { bufferToDataUrl } from "../llm/image-utils";
|
|
3
4
|
import { t } from "../i18n/index";
|
|
5
|
+
import { defaultOutputBus } from "../output";
|
|
6
|
+
import { errMsg } from "../utils";
|
|
4
7
|
import { join } from "path";
|
|
8
|
+
import { mkdirSync } from "fs";
|
|
9
|
+
import { projectMmaDir } from "../config/paths";
|
|
5
10
|
let session = null;
|
|
11
|
+
/**
|
|
12
|
+
* Тестовый seam: подмена фабрики сессии без `vi.mock` (в Bun он глобален на
|
|
13
|
+
* процесс). Используется только в тестах сериализации действий.
|
|
14
|
+
*/
|
|
15
|
+
let sessionFactory;
|
|
16
|
+
/** @internal */
|
|
17
|
+
export function setBrowserSessionFactoryForTest(factory) {
|
|
18
|
+
sessionFactory = factory;
|
|
19
|
+
session = null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Сериализация действий. В MoE параллельные волны делят module-level сессию:
|
|
23
|
+
* одновременный `execute()` перетирает состояние страницы, а `close()` одного
|
|
24
|
+
* субагента убивает сессию остальных. Promise-chain гарантирует, что в браузере
|
|
25
|
+
* одновременно выполняется ровно одно действие.
|
|
26
|
+
*/
|
|
27
|
+
let actionChain = Promise.resolve();
|
|
28
|
+
/** Сериализует действия: одновременно в браузере ровно одно. */
|
|
29
|
+
function serialize(fn) {
|
|
30
|
+
const run = actionChain.then(fn, fn);
|
|
31
|
+
actionChain = run.then(() => undefined, () => undefined);
|
|
32
|
+
return run;
|
|
33
|
+
}
|
|
34
|
+
/** Ленивая module-level сессия (cookie-dir в project .mma). */
|
|
6
35
|
function getSession(ctx) {
|
|
7
36
|
if (!session) {
|
|
8
|
-
const cookieDir = join(ctx.baseDir, "
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
const cookieDir = join(projectMmaDir(ctx.baseDir), "browser");
|
|
38
|
+
mkdirSync(cookieDir, { recursive: true });
|
|
39
|
+
session = sessionFactory
|
|
40
|
+
? sessionFactory(ctx)
|
|
41
|
+
: new BrowserSession({
|
|
42
|
+
...DEFAULT_BROWSER_CONFIG,
|
|
43
|
+
headless: ctx.config.browser?.headless ?? true,
|
|
44
|
+
maxElements: ctx.config.browser?.maxElements ?? 30,
|
|
45
|
+
maxContentChars: ctx.config.browser?.maxContentChars ?? 2500,
|
|
46
|
+
maxConsoleEntries: ctx.config.browser?.maxConsoleEntries ?? 40,
|
|
47
|
+
maxConsoleLineChars: ctx.config.browser?.maxConsoleLineChars ?? 400,
|
|
48
|
+
navigationTimeout: ctx.config.browser?.navigationTimeout ?? 15000,
|
|
49
|
+
viewportWidth: ctx.config.browser?.viewportWidth ?? 1280,
|
|
50
|
+
viewportHeight: ctx.config.browser?.viewportHeight ?? 720,
|
|
51
|
+
cookieDir,
|
|
52
|
+
});
|
|
21
53
|
}
|
|
22
54
|
return session;
|
|
23
55
|
}
|
|
56
|
+
/** Скриншот для текстовой модели: пометка + текстовый снапшот. */
|
|
24
57
|
export function formatScreenshotForTextModel(snapshot) {
|
|
25
58
|
return `${t("tool.screenshot_unavailable")}\n\n${snapshot}`;
|
|
26
59
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Превращает PNG-буфер скриншота в image-part для доставки модели. Размер
|
|
62
|
+
* ограничивается `bufferToDataUrl` (ресайз ≤800px, JPEG) — иначе base64
|
|
63
|
+
* раздул бы контекст малыша (32K).
|
|
64
|
+
*/
|
|
65
|
+
export async function screenshotToImagePart(screenshot) {
|
|
66
|
+
const { dataUrl } = await bufferToDataUrl(screenshot);
|
|
67
|
+
return { type: "image_url", image_url: { url: dataUrl } };
|
|
33
68
|
}
|
|
69
|
+
/** Фабрика browser-тула (сессия ленивая, module-level). */
|
|
34
70
|
export function createBrowserTool() {
|
|
35
71
|
return {
|
|
36
72
|
name: "browser",
|
|
@@ -93,23 +129,32 @@ export function createBrowserTool() {
|
|
|
93
129
|
return { success: false, output: t("tool.action_required") };
|
|
94
130
|
}
|
|
95
131
|
const s = getSession(ctx);
|
|
96
|
-
const result = await s.execute(action, args);
|
|
97
|
-
const supportsVision = ctx.config.model.includes("vision") ||
|
|
98
|
-
ctx.config.model.includes("gpt-4o") ||
|
|
99
|
-
ctx.config.model.includes("claude");
|
|
132
|
+
const result = await serialize(() => s.execute(action, args));
|
|
100
133
|
if (result.screenshot) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
134
|
+
// vision берётся из резолвера возможностей активной модели
|
|
135
|
+
// (пресет + native-probe + конфиг), а не из эвристики по имени.
|
|
136
|
+
// Без vision отдаём текстовый snapshot — base64 в контекст не льём.
|
|
137
|
+
if (ctx.capabilities?.vision) {
|
|
138
|
+
return {
|
|
139
|
+
success: result.success,
|
|
140
|
+
output: result.output,
|
|
141
|
+
images: [await screenshotToImagePart(result.screenshot)],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return { success: result.success, output: formatScreenshotForTextModel(result.output) };
|
|
105
145
|
}
|
|
106
146
|
return { success: result.success, output: result.output };
|
|
107
147
|
},
|
|
108
148
|
};
|
|
109
149
|
}
|
|
150
|
+
/** Закрывает сессию после текущего действия (не посреди навигации). */
|
|
110
151
|
export function closeBrowserSession() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
152
|
+
const s = session;
|
|
153
|
+
session = null;
|
|
154
|
+
if (!s)
|
|
155
|
+
return;
|
|
156
|
+
// Дожидаемся текущего действия, чтобы не закрыть браузер во время навигации.
|
|
157
|
+
actionChain = actionChain.then(() => s.close().catch((err) => {
|
|
158
|
+
defaultOutputBus.log("warn", "browser", `Failed to close browser session: ${errMsg(err)}`);
|
|
159
|
+
}));
|
|
115
160
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
/** Тул ответа по большому тексту: чанкинг + параллельные запросы + синтез. */
|
|
1
2
|
import { readFileSync } from "node:fs";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { t } from "../i18n/index";
|
|
4
5
|
import { isPathInScope } from "../modules/security/path-validator";
|
|
5
6
|
import { DEFAULT_SECURITY_CONFIG } from "../config/security";
|
|
6
7
|
import { runChunkQuery } from "../modules/context/chunk-query";
|
|
8
|
+
/** Отвечает на вопрос по тексту вне контекстного окна (чанк-параллелизм). */
|
|
7
9
|
export const chunkQueryTool = {
|
|
8
10
|
name: "chunk_query",
|
|
9
11
|
icon: "🧩",
|
|
10
12
|
description: "Answer a query over a large text (file or inline) by splitting it into chunks and querying the model per chunk in parallel, then optionally synthesizing a final answer. Use when the input is too large for the context window.",
|
|
11
13
|
tags: ["research"],
|
|
12
|
-
/**
|
|
14
|
+
/** Несколько LLM-вызовов (чанк-запросы + синтез) — нужен больший бюджет. */
|
|
13
15
|
timeoutMs: 300000,
|
|
14
16
|
parameters: {
|
|
15
17
|
type: "object",
|
|
@@ -64,22 +66,28 @@ export const chunkQueryTool = {
|
|
|
64
66
|
return { success: false, output: t("tool.chunk_query_no_input") };
|
|
65
67
|
let content = text || "";
|
|
66
68
|
if (inputPath) {
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
+
// Та же политика, что у read_file: глобальные security-пути (.env/ключи)
|
|
70
|
+
// + скоуп субагента.
|
|
69
71
|
const resolvedInput = resolve(ctx.baseDir, inputPath);
|
|
70
72
|
const check = isPathInScope(ctx.baseDir, resolvedInput, ctx.scope, ctx.config?.security?.paths || DEFAULT_SECURITY_CONFIG.paths);
|
|
71
73
|
if (!check.allowed) {
|
|
72
|
-
return {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
output: t("tool.chunk_query_path_not_allowed", { reason: check.reason || "Path not allowed" }),
|
|
77
|
+
};
|
|
73
78
|
}
|
|
74
79
|
try {
|
|
75
80
|
content = readFileSync(resolvedInput, "utf8");
|
|
76
81
|
}
|
|
77
82
|
catch (e) {
|
|
78
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
output: t("tool.chunk_query_read_failed", { path: inputPath, error: e.message }),
|
|
86
|
+
};
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
if (!ctx.llmProvider) {
|
|
82
|
-
return { success: false, output: "
|
|
90
|
+
return { success: false, output: t("tool.chunk_query_no_provider") };
|
|
83
91
|
}
|
|
84
92
|
const maxParallel = typeof args.max_parallel === "number"
|
|
85
93
|
? args.max_parallel
|
package/dist/tools/create-dir.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/** Тул создания каталога (рекурсивно, с security-проверками). */
|
|
1
2
|
import { mkdirSync, existsSync } from "fs";
|
|
2
3
|
import { t } from "../i18n/index";
|
|
3
4
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
4
5
|
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
5
6
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
6
7
|
import { safeResolvePath } from "./path-utils";
|
|
8
|
+
import { protectedPathBlock } from "../modules/security/protected-paths";
|
|
9
|
+
/** Создаёт каталог со всеми промежуточными. */
|
|
7
10
|
export const createDirTool = {
|
|
8
11
|
name: "create_dir",
|
|
9
12
|
icon: "📁",
|
|
@@ -19,11 +22,10 @@ export const createDirTool = {
|
|
|
19
22
|
handler: async (ctx, args) => {
|
|
20
23
|
const path = String(args.path);
|
|
21
24
|
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
22
|
-
// Get session-specific security config
|
|
23
25
|
const securityConfig = ctx.sessionContext
|
|
24
26
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
25
27
|
: ctx.config.security;
|
|
26
|
-
//
|
|
28
|
+
// Лимит файловых операций.
|
|
27
29
|
const maxFileOps = securityConfig?.maxFileOperations ?? 100;
|
|
28
30
|
const currentCount = ctx.fileOperationsCount ?? 0;
|
|
29
31
|
if (currentCount >= maxFileOps) {
|
|
@@ -33,7 +35,7 @@ export const createDirTool = {
|
|
|
33
35
|
output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
|
-
//
|
|
38
|
+
// Проверка прав на путь.
|
|
37
39
|
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
38
40
|
if (!scopeCheck.allowed) {
|
|
39
41
|
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
@@ -44,12 +46,14 @@ export const createDirTool = {
|
|
|
44
46
|
}),
|
|
45
47
|
};
|
|
46
48
|
}
|
|
49
|
+
// Защищённые пути (.git/, .env-каталоги, …) — спросить юзера.
|
|
50
|
+
const protectedBlock = protectedPathBlock(ctx, resolved, path);
|
|
51
|
+
if (protectedBlock)
|
|
52
|
+
return protectedBlock;
|
|
47
53
|
if (!existsSync(resolved)) {
|
|
48
54
|
mkdirSync(resolved, { recursive: true });
|
|
49
55
|
}
|
|
50
|
-
// Increment file operations counter
|
|
51
56
|
ctx.fileOperationsCount = currentCount + 1;
|
|
52
|
-
// Log directory creation
|
|
53
57
|
logFileWrite(ctx.sessionId, path, true, "Directory created");
|
|
54
58
|
return { success: true, output: t("file.created", { path }) };
|
|
55
59
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Тул удаления файла (с diff удалённого, security-проверки). */
|
|
1
2
|
import { unlinkSync, existsSync, statSync, readFileSync } from "fs";
|
|
2
3
|
import { t } from "../i18n/index";
|
|
3
4
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
@@ -5,6 +6,8 @@ import { logFileDelete, logSecurityBlock } from "../modules/security/audit-log";
|
|
|
5
6
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
6
7
|
import { generateDeleteDiff } from "../ui/diff";
|
|
7
8
|
import { safeResolvePath } from "./path-utils";
|
|
9
|
+
import { protectedPathBlock } from "../modules/security/protected-paths";
|
|
10
|
+
/** Удаляет файл; каталоги отклоняет. Возвращает diff удалённого. */
|
|
8
11
|
export const deleteFileTool = {
|
|
9
12
|
name: "delete_file",
|
|
10
13
|
icon: "🗑️",
|
|
@@ -20,11 +23,10 @@ export const deleteFileTool = {
|
|
|
20
23
|
handler: async (ctx, args) => {
|
|
21
24
|
const path = String(args.path);
|
|
22
25
|
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
23
|
-
// Get session-specific security config
|
|
24
26
|
const securityConfig = ctx.sessionContext
|
|
25
27
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
26
28
|
: ctx.config.security;
|
|
27
|
-
//
|
|
29
|
+
// Лимит файловых операций.
|
|
28
30
|
const maxFileOps = securityConfig?.maxFileOperations ?? 100;
|
|
29
31
|
const currentCount = ctx.fileOperationsCount ?? 0;
|
|
30
32
|
if (currentCount >= maxFileOps) {
|
|
@@ -34,7 +36,7 @@ export const deleteFileTool = {
|
|
|
34
36
|
output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
|
-
//
|
|
39
|
+
// Проверка прав на путь.
|
|
38
40
|
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
39
41
|
if (!scopeCheck.allowed) {
|
|
40
42
|
logSecurityBlock(ctx.sessionId, "file_delete", scopeCheck.reason || "Path not allowed", path);
|
|
@@ -45,6 +47,10 @@ export const deleteFileTool = {
|
|
|
45
47
|
}),
|
|
46
48
|
};
|
|
47
49
|
}
|
|
50
|
+
// Защищённые пути (.git/, .env, ключи) — спросить юзера.
|
|
51
|
+
const protectedBlock = protectedPathBlock(ctx, resolved, path, "file_delete");
|
|
52
|
+
if (protectedBlock)
|
|
53
|
+
return protectedBlock;
|
|
48
54
|
if (!existsSync(resolved)) {
|
|
49
55
|
return { success: false, output: t("file.notfound", { path }) };
|
|
50
56
|
}
|
|
@@ -54,9 +60,7 @@ export const deleteFileTool = {
|
|
|
54
60
|
const content = readFileSync(resolved, "utf-8");
|
|
55
61
|
unlinkSync(resolved);
|
|
56
62
|
const diff = generateDeleteDiff(content);
|
|
57
|
-
// Increment file operations counter
|
|
58
63
|
ctx.fileOperationsCount = currentCount + 1;
|
|
59
|
-
// Log successful file deletion
|
|
60
64
|
logFileDelete(ctx.sessionId, path, true);
|
|
61
65
|
return { success: true, output: t("file.deleted", { path }), diff };
|
|
62
66
|
},
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
/** Тул скачивания URL в файл (байты, SSRF-защита, cap размера). */
|
|
1
2
|
import { writeFileSync, mkdirSync } from "fs";
|
|
2
|
-
import { dirname } from "path";
|
|
3
|
+
import { dirname, resolve } from "path";
|
|
3
4
|
import { t } from "../i18n/index";
|
|
4
|
-
import { isUrlAllowed, sanitizeUrl } from "../modules/security/network-validator";
|
|
5
|
+
import { BlockedRedirectError, fetchValidated, isUrlAllowed, sanitizeUrl, } from "../modules/security/network-validator";
|
|
5
6
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
7
|
+
import { protectedPathBlock } from "../modules/security/protected-paths";
|
|
6
8
|
import { logNetworkRequest, logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
7
9
|
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
8
10
|
import { DEFAULT_SECURITY_CONFIG } from "../config/security";
|
|
9
11
|
import { safeResolvePath } from "./path-utils";
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
+
/** Дефолтный cap скачивания (байты). Чанковые ответы без content-length
|
|
13
|
+
* проверяются после чтения тела. */
|
|
12
14
|
export const MAX_DOWNLOAD_BYTES = 100 * 1024 * 1024;
|
|
15
|
+
/** Качает бинарник в файл; возвращает путь/размер/тип. Параметры: url, path, max_bytes. */
|
|
13
16
|
export const downloadFileTool = {
|
|
14
17
|
name: "download_file",
|
|
15
18
|
icon: "⬇️",
|
|
@@ -36,7 +39,7 @@ export const downloadFileTool = {
|
|
|
36
39
|
if (!url || !path) {
|
|
37
40
|
return { success: false, output: t("tool.invalid_params") };
|
|
38
41
|
}
|
|
39
|
-
//
|
|
42
|
+
// Сеть: те же правила, что у web_fetch/web_browse.
|
|
40
43
|
const securityConfig = ctx.sessionContext
|
|
41
44
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext).network
|
|
42
45
|
: ctx.config.security?.network || DEFAULT_SECURITY_CONFIG.network;
|
|
@@ -48,7 +51,7 @@ export const downloadFileTool = {
|
|
|
48
51
|
output: `[SECURITY BLOCKED] URL is not allowed: ${validation.reason}`,
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
|
-
//
|
|
54
|
+
// Путь: резолв от baseDir + проверка записи.
|
|
52
55
|
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
53
56
|
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
54
57
|
if (!scopeCheck.allowed) {
|
|
@@ -60,9 +63,27 @@ export const downloadFileTool = {
|
|
|
60
63
|
}),
|
|
61
64
|
};
|
|
62
65
|
}
|
|
66
|
+
// Тот же guard записи, что у write/edit: онбординг-allowlist и always-on
|
|
67
|
+
// проверка защищённых путей (иначе скачиванием перезаписали бы .env/ключи,
|
|
68
|
+
// которые остальные тулы записи отклоняют).
|
|
69
|
+
if (ctx.writePathAllowlist) {
|
|
70
|
+
const ok = ctx.writePathAllowlist.some((p) => resolve(p).toLowerCase() === resolve(resolved).toLowerCase());
|
|
71
|
+
if (!ok) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
output: t("file.path_not_allowed", {
|
|
75
|
+
path: `${path} — ${t("onboarding.write_restricted")}`,
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const protectedBlock = protectedPathBlock(ctx, resolved, path);
|
|
81
|
+
if (protectedBlock)
|
|
82
|
+
return protectedBlock;
|
|
63
83
|
const maxBytes = Number(args.max_bytes) > 0 ? Number(args.max_bytes) : MAX_DOWNLOAD_BYTES;
|
|
64
84
|
try {
|
|
65
|
-
|
|
85
|
+
// Редиректы перевадидируются (SSRF) внутри хелпера.
|
|
86
|
+
const { response } = await fetchValidated(url, securityConfig, {
|
|
66
87
|
signal: AbortSignal.timeout(securityConfig?.requestTimeout || 15000),
|
|
67
88
|
});
|
|
68
89
|
if (!response.ok) {
|
|
@@ -107,6 +128,10 @@ export const downloadFileTool = {
|
|
|
107
128
|
};
|
|
108
129
|
}
|
|
109
130
|
catch (e) {
|
|
131
|
+
if (e instanceof BlockedRedirectError) {
|
|
132
|
+
logSecurityBlock(ctx.sessionId, "network_request", e.reason, sanitizeUrl(url));
|
|
133
|
+
return { success: false, output: `[SECURITY BLOCKED] redirect: ${e.reason}` };
|
|
134
|
+
}
|
|
110
135
|
logNetworkRequest(ctx.sessionId, sanitizeUrl(url), false, `Error: ${e.message}`);
|
|
111
136
|
return {
|
|
112
137
|
success: false,
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/** Тул точечной правки: exact-match замена, скан, валидация, diff. */
|
|
1
2
|
import { readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { resolve } from "path";
|
|
2
4
|
import { t } from "../i18n/index";
|
|
3
5
|
import { isPathWritable } from "../modules/security/path-validator";
|
|
4
6
|
import { scanContent } from "../modules/security/content-scanner";
|
|
@@ -7,6 +9,9 @@ import { getSessionSecurityConfig } from "../modules/security/session-isolation"
|
|
|
7
9
|
import { generateDiff } from "../ui/diff";
|
|
8
10
|
import { safeResolvePath } from "./path-utils";
|
|
9
11
|
import { preValidateSyntax, detectImportConflicts } from "./syntax-validator";
|
|
12
|
+
import { errMsg } from "../utils";
|
|
13
|
+
import { protectedPathBlock } from "../modules/security/protected-paths";
|
|
14
|
+
/** Заменяет точный фрагмент в существующем файле (не regex). */
|
|
10
15
|
export const editFileTool = {
|
|
11
16
|
name: "edit_file",
|
|
12
17
|
icon: "✏️",
|
|
@@ -24,11 +29,23 @@ export const editFileTool = {
|
|
|
24
29
|
handler: async (ctx, args) => {
|
|
25
30
|
const path = String(args.path);
|
|
26
31
|
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
27
|
-
|
|
32
|
+
if (ctx.writePathAllowlist) {
|
|
33
|
+
const ok = ctx.writePathAllowlist.some((p) => resolve(p).toLowerCase() === resolve(resolved).toLowerCase());
|
|
34
|
+
if (!ok) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
output: t("file.path_not_allowed", {
|
|
38
|
+
path: `${path} — ${t("onboarding.write_restricted")}`,
|
|
39
|
+
reason: t("onboarding.write_restricted"),
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Сессионный security-конфиг.
|
|
28
45
|
const securityConfig = ctx.sessionContext
|
|
29
46
|
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
30
47
|
: ctx.config.security;
|
|
31
|
-
//
|
|
48
|
+
// Лимит файловых операций.
|
|
32
49
|
const maxFileOps = securityConfig?.maxFileOperations ?? 100;
|
|
33
50
|
const currentCount = ctx.fileOperationsCount ?? 0;
|
|
34
51
|
if (currentCount >= maxFileOps) {
|
|
@@ -38,7 +55,7 @@ export const editFileTool = {
|
|
|
38
55
|
output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
|
|
39
56
|
};
|
|
40
57
|
}
|
|
41
|
-
//
|
|
58
|
+
// Проверка прав на путь.
|
|
42
59
|
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
43
60
|
if (!scopeCheck.allowed) {
|
|
44
61
|
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
@@ -49,19 +66,43 @@ export const editFileTool = {
|
|
|
49
66
|
}),
|
|
50
67
|
};
|
|
51
68
|
}
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
69
|
+
// Защищённые пути (.git/, .env, ключи) — всегда спросить юзера.
|
|
70
|
+
const protectedBlock = protectedPathBlock(ctx, resolved, path);
|
|
71
|
+
if (protectedBlock)
|
|
72
|
+
return protectedBlock;
|
|
73
|
+
// Строковые аргументы обязательны: String(null) дало бы "null" и молча
|
|
74
|
+
// испортило файл. Явно отклоняем не-строки с понятной ошибкой.
|
|
75
|
+
if (typeof args.old !== "string" || typeof args.new !== "string") {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
output: t("file.edit_args_invalid", {
|
|
79
|
+
oldType: typeof args.old,
|
|
80
|
+
newType: typeof args.new,
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const oldStr = args.old;
|
|
85
|
+
const newStr = args.new;
|
|
86
|
+
let content;
|
|
87
|
+
try {
|
|
88
|
+
content = readFileSync(resolved, "utf-8");
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
output: t("file.read_failed", { path, error: errMsg(err) }),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
55
96
|
if (!content.includes(oldStr)) {
|
|
56
97
|
return {
|
|
57
98
|
success: false,
|
|
58
99
|
output: t("file.string_not_found", { str: oldStr, path }),
|
|
59
100
|
};
|
|
60
101
|
}
|
|
61
|
-
//
|
|
62
|
-
//
|
|
102
|
+
// Замена через функцию — иначе newStr трактуется как шаблон и `$&`, `$1`
|
|
103
|
+
// молча испортят содержимое файла.
|
|
63
104
|
const updated = content.replace(oldStr, () => newStr);
|
|
64
|
-
//
|
|
105
|
+
// Скан нового контента — только при включённой безопасности.
|
|
65
106
|
if (securityConfig?.enabled && securityConfig?.contentScan?.enabled) {
|
|
66
107
|
const scanResult = scanContent(updated, path, securityConfig.contentScan);
|
|
67
108
|
if (!scanResult.allowed) {
|
|
@@ -72,7 +113,7 @@ export const editFileTool = {
|
|
|
72
113
|
};
|
|
73
114
|
}
|
|
74
115
|
}
|
|
75
|
-
//
|
|
116
|
+
// Превалидация синтаксиса ДО записи — иначе петля "файл записан" + syntax error.
|
|
76
117
|
const validation = await preValidateSyntax(resolved, updated, ctx.baseDir);
|
|
77
118
|
if (!validation.valid) {
|
|
78
119
|
return {
|
|
@@ -80,15 +121,12 @@ export const editFileTool = {
|
|
|
80
121
|
output: t("file.syntax_error", { path, error: validation.error || "Unknown syntax error" }),
|
|
81
122
|
};
|
|
82
123
|
}
|
|
83
|
-
//
|
|
124
|
+
// Конфликты import/export — только warning.
|
|
84
125
|
const conflicts = detectImportConflicts(updated);
|
|
85
126
|
writeFileSync(resolved, updated, "utf-8");
|
|
86
127
|
const diff = generateDiff(content, updated);
|
|
87
|
-
// Increment file operations counter
|
|
88
128
|
ctx.fileOperationsCount = currentCount + 1;
|
|
89
|
-
// Log successful file edit
|
|
90
129
|
logFileWrite(ctx.sessionId, path, true, "File edited");
|
|
91
|
-
// Add conflict warning to output if any
|
|
92
130
|
let output = t("file.replaced_in", { path });
|
|
93
131
|
if (conflicts.length > 0) {
|
|
94
132
|
output += `\n[WARNING] Import/export conflicts: ${conflicts.join(", ")}`;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
/** On-demand тул: включает скрытые категории тулов на сессию. */
|
|
1
2
|
import { t } from "../i18n/index";
|
|
2
3
|
/**
|
|
3
|
-
* On-demand
|
|
4
|
-
* (+ `alwaysOn`
|
|
5
|
-
* process)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* loop re-reads each iteration to recompute the LLM-visible tool set.
|
|
4
|
+
* On-demand активация тулов. Главный цикл показывает только
|
|
5
|
+
* `config.tools.defaultTags` (+ структурные `alwaysOn`); спецтулы (web,
|
|
6
|
+
* browser, memory, process) скрыты из схемы ради экономии токенов. Модель
|
|
7
|
+
* включает их этим тулом; хендлер дописывает теги в общий мутабельный
|
|
8
|
+
* `ctx.activeToolTags`, который цикл перечитывает каждую итерацию.
|
|
9
9
|
*/
|
|
10
10
|
export const enableToolsTool = {
|
|
11
11
|
name: "enable_tools",
|