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.
- package/CHANGELOG.md +33 -0
- package/dist/assets/app.js +22 -4
- package/dist/assets/index.html +29 -0
- package/dist/assets/js/dom.js +2 -1
- package/dist/assets/js/feed.js +38 -26
- package/dist/assets/js/format.js +1 -1
- package/dist/assets/js/rail.js +114 -0
- package/dist/assets/js/settings-controls.js +2 -2
- package/dist/assets/js/settings.js +246 -51
- package/dist/assets/js/store.js +3 -0
- package/dist/assets/js/tabs.js +104 -2
- package/dist/assets/styles.css +215 -66
- 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/en.json +106 -21
- package/dist/i18n/index.js +37 -3
- package/dist/i18n/ru.json +109 -21
- 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 +24285 -22447
- 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
|
@@ -4,30 +4,31 @@ import { resolve, extname, join } from "path";
|
|
|
4
4
|
import { platform } from "os";
|
|
5
5
|
import { findProjectRoot, resolveTscCommand } from "../../lsp/project-root";
|
|
6
6
|
import { autoFixErrors, parseTscOutput } from "../../../tools/auto-fixer";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
7
|
+
import { LINT_TYPE_CHECK_DEBOUNCE_MS as TYPE_CHECK_DEBOUNCE_MS } from "../../../config/timeouts";
|
|
8
|
+
import { settleChildClose } from "../../../utils/child-run";
|
|
9
|
+
// Проекты, чей lint-скрипт ссылается на неустановленный бинарник
|
|
10
|
+
// (ключ baseDir::script). Сам прогон дорог, а маркер провала — чистый шум,
|
|
11
|
+
// поэтому пропускаем до конца процесса.
|
|
11
12
|
const missingLintBinaries = new Set();
|
|
12
13
|
function lintCacheKey(baseDir, lintScript) {
|
|
13
14
|
return `${baseDir}::${lintScript}`;
|
|
14
15
|
}
|
|
15
|
-
//
|
|
16
|
-
// "command not found" (exit 127); Windows cmd.exe
|
|
17
|
-
// (en) / "не является внутренней или внешней командой" (ru)
|
|
16
|
+
// Шелл-уровень "command not found" по локалям/рантаймам: POSIX `sh` пишет
|
|
17
|
+
// "command not found" (exit 127); Windows cmd.exe — "is not recognized"
|
|
18
|
+
// (en) / "не является внутренней или внешней командой" (ru), exit 1/9009.
|
|
18
19
|
const MISSING_BINARY_RE = /command not found|not recognized|не является внутренней|не распознан|не є внутрішньою|".+?" не является/i;
|
|
19
20
|
function isMissingBinaryError(err, combinedOutput) {
|
|
20
21
|
// 127 = POSIX sh "command not found"; 9009 = Windows cmd.exe "is not
|
|
21
|
-
// recognized" —
|
|
22
|
+
// recognized" — оба авторитетны независимо от кодировки вывода.
|
|
22
23
|
if (err.status === 127 || err.status === 9009)
|
|
23
24
|
return true;
|
|
24
25
|
if (err.code === "ENOENT")
|
|
25
26
|
return true;
|
|
26
27
|
return MISSING_BINARY_RE.test(combinedOutput);
|
|
27
28
|
}
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
29
|
+
// Пофайловые синтаксис-проверки кешируются по (путь, хеш содержимого): перезапись
|
|
30
|
+
// файла с тем же содержимым скипает (дорогую) проверку. Смена контента →
|
|
31
|
+
// промах кеша → свежая проверка.
|
|
31
32
|
const syntaxCache = new Map();
|
|
32
33
|
function contentHash(content) {
|
|
33
34
|
let h = 5381;
|
|
@@ -38,10 +39,10 @@ function contentHash(content) {
|
|
|
38
39
|
}
|
|
39
40
|
let _winDecoder;
|
|
40
41
|
/**
|
|
41
|
-
* OEM
|
|
42
|
-
*
|
|
43
|
-
* chcp.com
|
|
44
|
-
* (
|
|
42
|
+
* OEM-декодер активной консольной кодовой страницы (CP866 на русской Windows).
|
|
43
|
+
* Зеркалит `getOemDecoder()` из modules/processes/registry.ts: транзиентный
|
|
44
|
+
* провал chcp.com под нагрузкой НЕ кешируется — мемоизируется только успешный
|
|
45
|
+
* проб (или жёсткий фолбэк ibm866).
|
|
45
46
|
*/
|
|
46
47
|
function getWinDecoder() {
|
|
47
48
|
if (_winDecoder !== undefined)
|
|
@@ -59,26 +60,26 @@ function getWinDecoder() {
|
|
|
59
60
|
}).toString("latin1");
|
|
60
61
|
const m = cpOut.match(/(\d+)/);
|
|
61
62
|
if (m)
|
|
62
|
-
decoder = new TextDecoder(
|
|
63
|
+
decoder = new TextDecoder(`ibm${m[1]}`);
|
|
63
64
|
}
|
|
64
65
|
catch {
|
|
65
|
-
/* chcp
|
|
66
|
+
/* chcp упал (нагрузка/ENOENT) — повторим при следующем вызове */
|
|
66
67
|
}
|
|
67
68
|
if (!decoder) {
|
|
68
69
|
try {
|
|
69
70
|
decoder = new TextDecoder("ibm866");
|
|
70
71
|
}
|
|
71
72
|
catch {
|
|
72
|
-
return new TextDecoder("utf-8"); //
|
|
73
|
+
return new TextDecoder("utf-8"); // неизвестная кодировка — не кешируем
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
_winDecoder = decoder;
|
|
76
77
|
return _winDecoder;
|
|
77
78
|
}
|
|
78
79
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
80
|
+
* Декодирует вывод дочернего процесса в UTF-8. Современные тулы (bun, node,
|
|
81
|
+
* npm, tsc) пишут UTF-8 в пайп; встроенные команды/ошибки cmd.exe — OEM-страницу.
|
|
82
|
+
* Сначала строгий UTF-8; на невалидных байтах — откат на OEM.
|
|
82
83
|
*/
|
|
83
84
|
function decodeOutput(chunks) {
|
|
84
85
|
const raw = Buffer.concat(chunks);
|
|
@@ -86,7 +87,7 @@ function decodeOutput(chunks) {
|
|
|
86
87
|
return new TextDecoder("utf-8", { fatal: true }).decode(raw);
|
|
87
88
|
}
|
|
88
89
|
catch {
|
|
89
|
-
/*
|
|
90
|
+
/* невалидный UTF-8 → вывод в OEM-кодировке */
|
|
90
91
|
}
|
|
91
92
|
try {
|
|
92
93
|
return getWinDecoder().decode(raw);
|
|
@@ -95,12 +96,15 @@ function decodeOutput(chunks) {
|
|
|
95
96
|
return raw.toString("latin1");
|
|
96
97
|
}
|
|
97
98
|
}
|
|
99
|
+
/** Встроенный плагин: проверяет каждую запись/правку файла (синтаксис, lint, типы). */
|
|
98
100
|
export class LintOnWritePlugin {
|
|
99
101
|
name = "lint-on-write";
|
|
100
102
|
priority = 10;
|
|
101
103
|
isBuiltin = true;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
// Дебаунснутый проектный typecheck по корню проекта. Один общий слот
|
|
105
|
+
// протекал tsc-ошибками одного проекта в записи другого (монорепо).
|
|
106
|
+
_checks = new Map();
|
|
107
|
+
/** После записи/правки: синтаксис файла, затем проектные lint и typecheck. */
|
|
104
108
|
async onAfterTool(ctx, call, result) {
|
|
105
109
|
if (call.name !== "write_file" && call.name !== "edit_file") {
|
|
106
110
|
return;
|
|
@@ -132,9 +136,9 @@ export class LintOnWritePlugin {
|
|
|
132
136
|
}
|
|
133
137
|
async checkSyntax(filePath, ext, baseDir, signal) {
|
|
134
138
|
if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
|
|
135
|
-
//
|
|
136
|
-
// (~3-
|
|
137
|
-
//
|
|
139
|
+
// Проверка только синтаксиса парсером bun (~200мс) вместо `npx tsc`
|
|
140
|
+
// (~3-5с на запись). Полные ошибки типов всё равно отдаёт дебаунснутый
|
|
141
|
+
// проектный typecheck ниже, когда есть tsconfig.
|
|
138
142
|
let content = "";
|
|
139
143
|
try {
|
|
140
144
|
content = readFileSync(filePath, "utf-8");
|
|
@@ -148,8 +152,8 @@ export class LintOnWritePlugin {
|
|
|
148
152
|
return cached.error;
|
|
149
153
|
}
|
|
150
154
|
try {
|
|
151
|
-
await runAsync(
|
|
152
|
-
//
|
|
155
|
+
await runAsync({ file: "bun", args: ["build", "--no-bundle", "--target=bun", filePath] }, baseDir, 10000, signal);
|
|
156
|
+
// Прерванная проверка (interrupt) файл не валидировала — не кешируем.
|
|
153
157
|
if (signal?.aborted)
|
|
154
158
|
return null;
|
|
155
159
|
syntaxCache.set(filePath, { hash, error: null });
|
|
@@ -169,7 +173,7 @@ export class LintOnWritePlugin {
|
|
|
169
173
|
}
|
|
170
174
|
if (ext === ".js" || ext === ".jsx" || ext === ".cjs" || ext === ".mjs") {
|
|
171
175
|
try {
|
|
172
|
-
await runAsync(
|
|
176
|
+
await runAsync({ file: "node", args: ["--check", filePath] }, baseDir, 5000, signal);
|
|
173
177
|
return null;
|
|
174
178
|
}
|
|
175
179
|
catch (err) {
|
|
@@ -192,10 +196,10 @@ export class LintOnWritePlugin {
|
|
|
192
196
|
if (!lintScript) {
|
|
193
197
|
return;
|
|
194
198
|
}
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
+
// Объявленный, но неустановленный бинарник линтера ("eslint" не стоит) —
|
|
200
|
+
// пробел проектного сетапа, а не проблема кода: дописывание его к каждой
|
|
201
|
+
// записи засоряло контекст ~25×/сессию (ses_mt4fn58c). Детектим раз
|
|
202
|
+
// и дальше скипаем молча до конца процесса.
|
|
199
203
|
if (missingLintBinaries.has(lintCacheKey(ctx.baseDir, lintScript))) {
|
|
200
204
|
ctx.logger.debug(`Lint skipped (linter for "${lintScript}" not installed)`);
|
|
201
205
|
return;
|
|
@@ -224,10 +228,10 @@ export class LintOnWritePlugin {
|
|
|
224
228
|
}
|
|
225
229
|
}
|
|
226
230
|
async runProjectTypeCheck(filePath, baseDir, result, signal) {
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
+
// Корень проекта резолвим от РЕДАКТИРОВАННОГО ФАЙЛА, а не от baseDir агента.
|
|
232
|
+
// Во вложенной раскладке (`baseDir/proj/tsconfig.json`) старый поиск от
|
|
233
|
+
// baseDir молча скипал typecheck на каждой записи — вся сессия шла
|
|
234
|
+
// вообще без типового фидбэка (наблюдено в ses_mst2r0r5).
|
|
231
235
|
const projectRoot = findProjectRoot(filePath, baseDir, ["tsconfig.json", "package.json"]);
|
|
232
236
|
const tsconfigPath = join(projectRoot, "tsconfig.json");
|
|
233
237
|
if (!existsSync(tsconfigPath)) {
|
|
@@ -239,28 +243,29 @@ export class LintOnWritePlugin {
|
|
|
239
243
|
return;
|
|
240
244
|
}
|
|
241
245
|
const now = Date.now();
|
|
242
|
-
|
|
243
|
-
|
|
246
|
+
const prev = this._checks.get(projectRoot);
|
|
247
|
+
if (prev && now - prev.at < TYPE_CHECK_DEBOUNCE_MS) {
|
|
248
|
+
const error = await prev.promise;
|
|
244
249
|
if (error) {
|
|
245
250
|
result.output += `\n\n[Project typecheck failed]: ${error}`;
|
|
246
251
|
}
|
|
247
252
|
return;
|
|
248
253
|
}
|
|
249
|
-
this.
|
|
250
|
-
this.
|
|
251
|
-
const error = await
|
|
254
|
+
const promise = this.runTscCheck(tsc, projectRoot, signal);
|
|
255
|
+
this._checks.set(projectRoot, { at: now, promise });
|
|
256
|
+
const error = await promise;
|
|
252
257
|
if (error) {
|
|
253
|
-
//
|
|
258
|
+
// Пробуем автофиксить чинимые ошибки
|
|
254
259
|
const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck 2>&1`, projectRoot, 30000, signal).catch(() => ({ stdout: "", stderr: error }));
|
|
255
260
|
const output = stderr || stdout;
|
|
256
261
|
const errors = parseTscOutput(output);
|
|
257
262
|
if (errors.length > 0) {
|
|
258
263
|
const fixResult = autoFixErrors(filePath, readFileSync(filePath, "utf-8"), errors);
|
|
259
264
|
if (fixResult.fixed) {
|
|
260
|
-
//
|
|
265
|
+
// Пишем починенное содержимое
|
|
261
266
|
writeFileSync(filePath, fixResult.newContent, "utf-8");
|
|
262
267
|
result.output += `\n\n[Auto-fixed]: ${fixResult.applied.join("; ")}`;
|
|
263
|
-
//
|
|
268
|
+
// Если остались ошибки — показываем их
|
|
264
269
|
if (fixResult.remaining.length > 0) {
|
|
265
270
|
const remainingMsg = fixResult.remaining
|
|
266
271
|
.map((e) => `TS${e.code}: ${e.message}`)
|
|
@@ -279,7 +284,7 @@ export class LintOnWritePlugin {
|
|
|
279
284
|
}
|
|
280
285
|
async runTscCheck(tsc, baseDir, signal) {
|
|
281
286
|
try {
|
|
282
|
-
|
|
287
|
+
await runAsync(`${tsc} --noEmit --skipLibCheck`, baseDir, 30000, signal);
|
|
283
288
|
return null;
|
|
284
289
|
}
|
|
285
290
|
catch (err) {
|
|
@@ -299,17 +304,29 @@ export class LintOnWritePlugin {
|
|
|
299
304
|
}
|
|
300
305
|
}
|
|
301
306
|
/**
|
|
302
|
-
*
|
|
303
|
-
*
|
|
307
|
+
* Гоняет команду асинхронно (не стопая event loop) и резолвится на выходе.
|
|
308
|
+
* Реджектит с захваченными stderr/stdout при ненулевом коде или таймауте.
|
|
309
|
+
*
|
|
310
|
+
* Простая строка идёт через шелл (проектные скрипты). Спецификация
|
|
311
|
+
* `{file,args}` спавнится БЕЗ шелла — обязательно для всего, собранного из
|
|
312
|
+
* модельного пути (`bun build … <file>`, `node --check <file>`): в имени могут
|
|
313
|
+
* быть `$()`, бектики или `%VAR%`, а shell:true их исполнил бы (инъекция).
|
|
304
314
|
*/
|
|
305
315
|
function runAsync(command, cwd, timeoutMs, signal) {
|
|
306
316
|
return new Promise((resolve, reject) => {
|
|
307
|
-
const child =
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
317
|
+
const child = typeof command === "string"
|
|
318
|
+
? spawn(command, {
|
|
319
|
+
cwd,
|
|
320
|
+
shell: true,
|
|
321
|
+
windowsHide: true,
|
|
322
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
323
|
+
})
|
|
324
|
+
: spawn(command.file, command.args, {
|
|
325
|
+
cwd,
|
|
326
|
+
shell: false,
|
|
327
|
+
windowsHide: true,
|
|
328
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
329
|
+
});
|
|
313
330
|
const utf8Chunks = [];
|
|
314
331
|
const oemChunks = [];
|
|
315
332
|
child.stdout?.on("data", (d) => {
|
|
@@ -322,10 +339,10 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
322
339
|
child.kill();
|
|
323
340
|
reject(new Error(`Command timed out after ${timeoutMs}ms`));
|
|
324
341
|
}, timeoutMs);
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
342
|
+
// Прерывание (Esc/Ctrl+C): убиваем ребёнка, чтобы петля агента сразу
|
|
343
|
+
// вернулась в промпт, а не высиживала длинный tsc/lint. Хендлер close
|
|
344
|
+
// при аборте резолвит пусто — гард `signal.aborted` вызывающего результат
|
|
345
|
+
// всё равно отбрасывает.
|
|
329
346
|
const onAbort = () => child.kill();
|
|
330
347
|
if (signal?.aborted) {
|
|
331
348
|
child.kill();
|
|
@@ -348,17 +365,13 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
348
365
|
signal?.removeEventListener("abort", onAbort);
|
|
349
366
|
const stdout = decodeOutput(utf8Chunks);
|
|
350
367
|
const stderr = decodeOutput(oemChunks);
|
|
351
|
-
if (signal?.aborted
|
|
368
|
+
if (signal?.aborted) {
|
|
352
369
|
resolve({ stdout, stderr });
|
|
370
|
+
return;
|
|
353
371
|
}
|
|
354
|
-
|
|
355
|
-
const err = new Error(`Command failed with exit code ${code}`);
|
|
356
|
-
err.stdout = stdout;
|
|
357
|
-
err.stderr = stderr;
|
|
358
|
-
err.status = code;
|
|
359
|
-
reject(err);
|
|
360
|
-
}
|
|
372
|
+
settleChildClose(code, stdout, stderr, `Command failed with exit code ${code}`, resolve, reject);
|
|
361
373
|
});
|
|
362
374
|
});
|
|
363
375
|
}
|
|
376
|
+
/** Готовый инстанс для регистрации. */
|
|
364
377
|
export const plugin = new LintOnWritePlugin();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Встроенный плагин: пишет в лог завершение сессии. */
|
|
1
2
|
export class NotifyPlugin {
|
|
2
3
|
name = "notify";
|
|
3
4
|
priority = 5;
|
|
@@ -6,4 +7,5 @@ export class NotifyPlugin {
|
|
|
6
7
|
ctx.logger.info("Session ended");
|
|
7
8
|
}
|
|
8
9
|
}
|
|
10
|
+
/** Готовый инстанс для регистрации. */
|
|
9
11
|
export const plugin = new NotifyPlugin();
|
|
@@ -1,20 +1,35 @@
|
|
|
1
|
+
// Загрузчик плагинов из каталогов: файлы и папки, гейт по minMmaVersion.
|
|
1
2
|
import { readdirSync, existsSync, statSync } from "fs";
|
|
2
3
|
import { join, basename } from "path";
|
|
3
4
|
import { t } from "../../i18n/index";
|
|
4
5
|
import { semverGt } from "../updater/checker";
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
+
* Кандидаты точки входа для плагинов-папок. Порядок:
|
|
7
8
|
* `plugin.ts` > `plugin.js` > `index.ts` > `index.js`.
|
|
8
9
|
*/
|
|
9
10
|
const FOLDER_ENTRY_NAMES = ["plugin.ts", "plugin.js", "index.ts", "index.js"];
|
|
11
|
+
/** Грузит плагины из каталога и регистрирует в менеджере (битые — скип с варном). */
|
|
10
12
|
export class PluginLoader {
|
|
13
|
+
/**
|
|
14
|
+
* Сканирует каталог (файлы .ts/.js и папки с точкой входа).
|
|
15
|
+
* Нет каталога — no-op.
|
|
16
|
+
*/
|
|
11
17
|
loadFromDir(dirPath, pluginManager, logger, options) {
|
|
12
18
|
if (!existsSync(dirPath))
|
|
13
19
|
return;
|
|
14
20
|
const entries = readdirSync(dirPath).sort();
|
|
15
21
|
for (const entry of entries) {
|
|
16
22
|
const fullPath = join(dirPath, entry);
|
|
17
|
-
|
|
23
|
+
// Битый симлинк / отказ в доступе не должен ронять весь скан плагинов
|
|
24
|
+
// (а с ним и bootstrap) — пропускаем с предупреждением.
|
|
25
|
+
let stat;
|
|
26
|
+
try {
|
|
27
|
+
stat = statSync(fullPath);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
logger?.warn(`Skipping unreadable plugin entry "${fullPath}": ${String(e)}`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
18
33
|
if (stat.isFile()) {
|
|
19
34
|
if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
|
|
20
35
|
continue;
|
|
@@ -40,7 +55,7 @@ export class PluginLoader {
|
|
|
40
55
|
try {
|
|
41
56
|
const mod = require(fullPath);
|
|
42
57
|
const plugin = mod.default || mod.plugin;
|
|
43
|
-
if (plugin
|
|
58
|
+
if (plugin?.name) {
|
|
44
59
|
if (plugin.minMmaVersion &&
|
|
45
60
|
options?.mmaVersion &&
|
|
46
61
|
semverGt(plugin.minMmaVersion, options.mmaVersion)) {
|
|
@@ -2,16 +2,16 @@ import { semverGt } from "../updater/checker";
|
|
|
2
2
|
import { Logger } from "../../logger/app-logger";
|
|
3
3
|
const logger = new Logger("warn", "plugins");
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Гоняет (возможно асинхронный) хук плагина с изоляцией ошибок. Синхронные
|
|
6
|
+
* броски и асинхронные реджекты логгируются и глотаются — один битый плагин
|
|
7
|
+
* не должен ронять агента и остальные плагины.
|
|
8
8
|
*/
|
|
9
9
|
function safeInvoke(plugin, hookName, fn) {
|
|
10
10
|
try {
|
|
11
11
|
const out = fn();
|
|
12
12
|
if (out instanceof Promise) {
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// Промис реджектится УЖЕ ПОСЛЕ выхода из этого try/catch — вешаем catch
|
|
14
|
+
// сразу, иначе изоляцию ломает необработанный unhandledRejection.
|
|
15
15
|
out.catch((e) => {
|
|
16
16
|
logger.warn(`plugin "${plugin.name}" ${hookName} rejected`, { error: String(e) });
|
|
17
17
|
});
|
|
@@ -24,17 +24,18 @@ function safeInvoke(plugin, hookName, fn) {
|
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
/** Менеджер плагинов: реестр, сортировка по приоритету, диспатч хуков. */
|
|
27
28
|
export class PluginManager {
|
|
28
29
|
plugins = [];
|
|
29
30
|
hostBridge = null;
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* Регистрирует host-мост (ставит REPL один раз). Подмешивается в каждый
|
|
33
|
+
* хук-контекст как `ctx.hostBridge`, если вызывающий не дал свой.
|
|
33
34
|
*/
|
|
34
35
|
setHostBridge(bridge) {
|
|
35
36
|
this.hostBridge = bridge;
|
|
36
37
|
}
|
|
37
|
-
/**
|
|
38
|
+
/** Подмешивает host-мост в хук-контекст (значение вызывающего побеждает). */
|
|
38
39
|
withHost(ctx) {
|
|
39
40
|
if (!ctx || typeof ctx !== "object")
|
|
40
41
|
return ctx;
|
|
@@ -44,9 +45,9 @@ export class PluginManager {
|
|
|
44
45
|
return { ...ctx, hostBridge: this.hostBridge };
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* Регистрирует плагин. Одноимённые дедупятся — побеждает старшая `version`
|
|
49
|
+
* (безверсейные считаются "0.0.0"). Проигравший отбрасывается; `skipped_older`
|
|
50
|
+
* даёт вызывающему предупредить о выкинутой копии.
|
|
50
51
|
*/
|
|
51
52
|
register(plugin, source) {
|
|
52
53
|
if (source)
|
|
@@ -68,27 +69,36 @@ export class PluginManager {
|
|
|
68
69
|
sort() {
|
|
69
70
|
this.plugins.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
70
71
|
}
|
|
72
|
+
/** Все зарегистрированные плагины (копия, по приоритету). */
|
|
71
73
|
getAllPlugins() {
|
|
72
74
|
return [...this.plugins];
|
|
73
75
|
}
|
|
74
|
-
/**
|
|
76
|
+
/** Детальный список (имя, версия, источник) для показа в REPL/CLI. */
|
|
75
77
|
getPluginInfos() {
|
|
76
78
|
return [...this.plugins].map((plugin) => ({ plugin, source: plugin.source }));
|
|
77
79
|
}
|
|
78
|
-
/**
|
|
80
|
+
/** Ищет зарегистрированный плагин по имени; null, если нет. */
|
|
79
81
|
getPlugin(name) {
|
|
80
82
|
return this.plugins.find((p) => p.name === name) ?? null;
|
|
81
83
|
}
|
|
84
|
+
/** Гоняет хук сборки промпта по всем плагинам; собирает вклады. */
|
|
82
85
|
runOnBuildPrompt() {
|
|
83
86
|
return this.plugins.map((p) => safeInvoke(p, "onBuildPrompt", () => p.onBuildPrompt?.() ?? null) ?? null);
|
|
84
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Хук перед тулом. False (любой плагин против) — не выполнять.
|
|
90
|
+
* Возвращает false при блокировке, true при разрешении.
|
|
91
|
+
*/
|
|
85
92
|
async runOnBeforeTool(rawCtx, call) {
|
|
86
93
|
const ctx = this.withHost(rawCtx);
|
|
87
94
|
for (const plugin of this.plugins) {
|
|
88
95
|
if (plugin.onBeforeTool) {
|
|
89
96
|
try {
|
|
90
97
|
const result = await plugin.onBeforeTool(ctx, call);
|
|
91
|
-
|
|
98
|
+
// Контракт плагина разрешает блокировать `false` ИЛИ строкой-причиной
|
|
99
|
+
// (экзекутор тулов понимает оба). Проигнорировать строку здесь —
|
|
100
|
+
// молча не заблокировать, если путь когда-нибудь задействуют.
|
|
101
|
+
if (result === false || typeof result === "string")
|
|
92
102
|
return false;
|
|
93
103
|
}
|
|
94
104
|
catch (e) {
|
|
@@ -98,6 +108,7 @@ export class PluginManager {
|
|
|
98
108
|
}
|
|
99
109
|
return true;
|
|
100
110
|
}
|
|
111
|
+
/** Хук после тула: отдаёт вызов и результат всем плагинам. */
|
|
101
112
|
async runOnAfterTool(rawCtx, call, result) {
|
|
102
113
|
const ctx = this.withHost(rawCtx);
|
|
103
114
|
for (const plugin of this.plugins) {
|
|
@@ -111,6 +122,7 @@ export class PluginManager {
|
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
}
|
|
125
|
+
/** Хук ошибки агента: рассылает ошибку всем плагинам. */
|
|
114
126
|
async runOnError(rawCtx, error) {
|
|
115
127
|
const ctx = this.withHost(rawCtx);
|
|
116
128
|
for (const plugin of this.plugins) {
|
|
@@ -124,6 +136,7 @@ export class PluginManager {
|
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
138
|
}
|
|
139
|
+
/** Хук старта сессии. */
|
|
127
140
|
runOnSessionStart(rawCtx) {
|
|
128
141
|
const ctx = this.withHost(rawCtx);
|
|
129
142
|
for (const plugin of this.plugins) {
|
|
@@ -132,6 +145,7 @@ export class PluginManager {
|
|
|
132
145
|
}
|
|
133
146
|
}
|
|
134
147
|
}
|
|
148
|
+
/** Хук перед обдумыванием (THINK). */
|
|
135
149
|
runOnBeforeThink(rawCtx) {
|
|
136
150
|
const ctx = this.withHost(rawCtx);
|
|
137
151
|
for (const plugin of this.plugins) {
|
|
@@ -140,6 +154,7 @@ export class PluginManager {
|
|
|
140
154
|
}
|
|
141
155
|
}
|
|
142
156
|
}
|
|
157
|
+
/** Хук после ответа LLM. */
|
|
143
158
|
runOnAfterThink(rawCtx, response) {
|
|
144
159
|
const ctx = this.withHost(rawCtx);
|
|
145
160
|
for (const plugin of this.plugins) {
|
|
@@ -148,6 +163,7 @@ export class PluginManager {
|
|
|
148
163
|
}
|
|
149
164
|
}
|
|
150
165
|
}
|
|
166
|
+
/** Хук конца сессии. */
|
|
151
167
|
runOnSessionEnd(rawCtx) {
|
|
152
168
|
const ctx = this.withHost(rawCtx);
|
|
153
169
|
for (const plugin of this.plugins) {
|
|
@@ -156,6 +172,7 @@ export class PluginManager {
|
|
|
156
172
|
}
|
|
157
173
|
}
|
|
158
174
|
}
|
|
175
|
+
/** Хук вызова тула (факт вызова, без результата). */
|
|
159
176
|
runOnToolCall(rawCtx) {
|
|
160
177
|
const ctx = this.withHost(rawCtx);
|
|
161
178
|
for (const plugin of this.plugins) {
|
|
@@ -164,6 +181,7 @@ export class PluginManager {
|
|
|
164
181
|
}
|
|
165
182
|
}
|
|
166
183
|
}
|
|
184
|
+
/** Хук смены фазы агента. */
|
|
167
185
|
runOnPhase(rawCtx, phase) {
|
|
168
186
|
const ctx = this.withHost(rawCtx);
|
|
169
187
|
for (const plugin of this.plugins) {
|
|
@@ -172,6 +190,7 @@ export class PluginManager {
|
|
|
172
190
|
}
|
|
173
191
|
}
|
|
174
192
|
}
|
|
193
|
+
/** Хук старта тул-вызова. */
|
|
175
194
|
runOnToolStart(rawCtx, call) {
|
|
176
195
|
const ctx = this.withHost(rawCtx);
|
|
177
196
|
for (const plugin of this.plugins) {
|
|
@@ -180,6 +199,7 @@ export class PluginManager {
|
|
|
180
199
|
}
|
|
181
200
|
}
|
|
182
201
|
}
|
|
202
|
+
/** Хук конца тул-вызова (вызов, результат, длительность). */
|
|
183
203
|
runOnToolEnd(rawCtx, call, result, durationMs) {
|
|
184
204
|
const ctx = this.withHost(rawCtx);
|
|
185
205
|
for (const plugin of this.plugins) {
|
|
@@ -188,7 +208,7 @@ export class PluginManager {
|
|
|
188
208
|
}
|
|
189
209
|
}
|
|
190
210
|
}
|
|
191
|
-
/**
|
|
211
|
+
/** Прогоняет текстовый чанк через трансформы плагинов; возвращает (возможно заменённый) чанк. */
|
|
192
212
|
runOnText(rawCtx, chunk) {
|
|
193
213
|
const ctx = this.withHost(rawCtx);
|
|
194
214
|
for (const plugin of this.plugins) {
|
|
@@ -200,7 +220,7 @@ export class PluginManager {
|
|
|
200
220
|
}
|
|
201
221
|
return chunk;
|
|
202
222
|
}
|
|
203
|
-
/**
|
|
223
|
+
/** Прогоняет мета-чанк через трансформы плагинов; возвращает (возможно заменённый) чанк. */
|
|
204
224
|
runOnMeta(rawCtx, chunk) {
|
|
205
225
|
const ctx = this.withHost(rawCtx);
|
|
206
226
|
for (const plugin of this.plugins) {
|
|
@@ -212,7 +232,7 @@ export class PluginManager {
|
|
|
212
232
|
}
|
|
213
233
|
return chunk;
|
|
214
234
|
}
|
|
215
|
-
/**
|
|
235
|
+
/** Рассылает сводку завершённого прогона всем плагинам (раз за Agent.run()). */
|
|
216
236
|
runOnTurnEnd(rawCtx, summary) {
|
|
217
237
|
const ctx = this.withHost(rawCtx);
|
|
218
238
|
for (const plugin of this.plugins) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { calculateCostDetailed, formatCost } from "./prices";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* attribution. When the model has no known price, `record` is a no-op.
|
|
3
|
+
* Копит стоимость LLM-вызовов сессии с атрибуцией обслужившему провайдеру.
|
|
4
|
+
* После каждого вызова — `record()` с usage (оценочным или API) и именем
|
|
5
|
+
* провайдера; `total` даёт running-итог, `totalFor(provider)` / `breakdown()` —
|
|
6
|
+
* разбивку. Без известной цены модели `record` — no-op.
|
|
8
7
|
*/
|
|
9
8
|
export class CostTracker {
|
|
10
9
|
config;
|
|
@@ -17,7 +16,7 @@ export class CostTracker {
|
|
|
17
16
|
this.model = model;
|
|
18
17
|
this.config = config;
|
|
19
18
|
}
|
|
20
|
-
/**
|
|
19
|
+
/** Сменяет модель (при /model switch). Дальнейшие записи — по новой цене. */
|
|
21
20
|
setModel(model) {
|
|
22
21
|
this.model = model;
|
|
23
22
|
}
|
|
@@ -42,7 +41,7 @@ export class CostTracker {
|
|
|
42
41
|
entry.known = true;
|
|
43
42
|
entry.total += result.cost;
|
|
44
43
|
}
|
|
45
|
-
/**
|
|
44
|
+
/** Итоговая стоимость в USD; undefined без известной цены модели. */
|
|
46
45
|
get total() {
|
|
47
46
|
return this._known ? this._total : undefined;
|
|
48
47
|
}
|
|
@@ -50,12 +49,12 @@ export class CostTracker {
|
|
|
50
49
|
get saved() {
|
|
51
50
|
return this._known ? this._saved : undefined;
|
|
52
51
|
}
|
|
53
|
-
/**
|
|
52
|
+
/** Итог по одному провайдеру; undefined, если по нему ничего не оценено. */
|
|
54
53
|
totalFor(provider) {
|
|
55
54
|
const entry = this.byProvider.get(provider);
|
|
56
|
-
return entry
|
|
55
|
+
return entry?.known ? entry.total : undefined;
|
|
57
56
|
}
|
|
58
|
-
/**
|
|
57
|
+
/** Итоги по провайдерам по убыванию стоимости (только оценённые). */
|
|
59
58
|
breakdown() {
|
|
60
59
|
const entries = [];
|
|
61
60
|
for (const [provider, entry] of this.byProvider) {
|
|
@@ -64,7 +63,7 @@ export class CostTracker {
|
|
|
64
63
|
}
|
|
65
64
|
return entries.sort((a, b) => b.cost - a.cost);
|
|
66
65
|
}
|
|
67
|
-
/**
|
|
66
|
+
/** Читаемый итог; undefined, когда цен нет. */
|
|
68
67
|
get formatted() {
|
|
69
68
|
return this._known ? formatCost(this._total) : undefined;
|
|
70
69
|
}
|