micro-models-agent 2.11.0 → 2.13.1
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 +39 -0
- package/dist/assets/app.js +22 -2
- package/dist/assets/index.html +39 -9
- package/dist/assets/js/dom.js +15 -0
- package/dist/assets/js/feed.js +129 -10
- package/dist/assets/js/sidebar.js +512 -0
- package/dist/assets/js/tabs.js +30 -5
- package/dist/assets/setup.js +267 -8
- package/dist/assets/styles.css +168 -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/en.json +89 -3
- package/dist/i18n/index.js +37 -3
- package/dist/i18n/ru.json +89 -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 +888 -139
- 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
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/** Git-подобный лог контекста LLM: snapshot'ы и diff между итерациями. */
|
|
2
|
+
/**
|
|
3
|
+
* Запись всего контекста, уходящего в LLM, в git-подобный diff-файл.
|
|
4
|
+
*
|
|
5
|
+
* Формат: snapshot 0 — полный контекст (каждая строка с "+", как новый файл в
|
|
6
|
+
* git); каждый следующий snapshot — построчный diff относительно предыдущего
|
|
7
|
+
* ("-" удалено, "+" добавлено). Секции канонического текста: [SYSTEM],
|
|
8
|
+
* [TOOLS], [HISTORY].
|
|
9
|
+
*
|
|
10
|
+
* Reasoning модели НЕ входит в контекст, отправляемый в LLM, поэтому он пишется
|
|
11
|
+
* отдельной аннотацией [OUTPUT] и в diff не участвует.
|
|
12
|
+
*
|
|
13
|
+
* Секреты редактируются общим sanitizeLogMessage (если redact=true).
|
|
14
|
+
*/
|
|
15
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "fs";
|
|
16
|
+
import { dirname } from "path";
|
|
17
|
+
/**
|
|
18
|
+
* Точечная редакция секретов для context-log.
|
|
19
|
+
*
|
|
20
|
+
* НЕ используем общий sanitizeLogMessage: его generic-паттерн (любая строка
|
|
21
|
+
* 30+ символов) и слишком широкий `pass` вырезают обычный текст контекста —
|
|
22
|
+
* имена скилов, "pass a file", `pass "background: true"` — что делает
|
|
23
|
+
* отладочный лог бесполезным. Здесь только безошибочно опознаваемые секреты.
|
|
24
|
+
*/
|
|
25
|
+
const SECRET_PATTERNS = [
|
|
26
|
+
[/\bsk-[A-Za-z0-9_-]{16,}\b/g, "sk-[REDACTED]"],
|
|
27
|
+
[/\bctx7sk-[A-Za-z0-9_-]{10,}\b/g, "ctx7sk-[REDACTED]"],
|
|
28
|
+
[/\bghp_[A-Za-z0-9]{20,}\b/g, "ghp_[REDACTED]"],
|
|
29
|
+
[/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g, "github_pat_[REDACTED]"],
|
|
30
|
+
[/\bAKIA[0-9A-Z]{16}\b/g, "AKIA[REDACTED]"],
|
|
31
|
+
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, "xox[REDACTED]"],
|
|
32
|
+
[/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/g, "[JWT REDACTED]"],
|
|
33
|
+
[/\b(Bearer)\s+[A-Za-z0-9._~+/-]{20,}=*/gi, "$1 [REDACTED]"],
|
|
34
|
+
[
|
|
35
|
+
/\b(api[_-]?key|access[_-]?token|auth[_-]?token|secret[_-]?key|private[_-]?key)\b(["']?\s*[=:]\s*["']?)([A-Za-z0-9_\-./+]{16,})/gi,
|
|
36
|
+
"$1$2[REDACTED]",
|
|
37
|
+
],
|
|
38
|
+
[/\b(password|passwd)\b(["']?\s*[=:]\s*["']?)([^\s"'&]{8,})/gi, "$1$2[REDACTED]"],
|
|
39
|
+
];
|
|
40
|
+
/** Заменяет только безошибочно опознаваемые секреты, не трогая обычный текст. */
|
|
41
|
+
export function redactContextSecrets(text) {
|
|
42
|
+
let out = text;
|
|
43
|
+
for (const [pattern, replacement] of SECRET_PATTERNS) {
|
|
44
|
+
out = out.replace(pattern, replacement);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** Версия протокола заголовка — растёт при несовместимом изменении формата. */
|
|
49
|
+
export const CONTEXT_LOG_FORMAT = 1;
|
|
50
|
+
const BAR = "═".repeat(78);
|
|
51
|
+
const SEP = "─".repeat(78);
|
|
52
|
+
const CONTEXT_LINES = 3;
|
|
53
|
+
// Ограничение на размер LCS-матрицы. При большем «середине» diff деградирует
|
|
54
|
+
// до полной замены, чтобы не аллоцировать сотни МБ.
|
|
55
|
+
const MAX_LCS_CELLS = 4_000_000;
|
|
56
|
+
function safeJson(value) {
|
|
57
|
+
if (value === undefined)
|
|
58
|
+
return "";
|
|
59
|
+
if (typeof value === "string")
|
|
60
|
+
return value;
|
|
61
|
+
try {
|
|
62
|
+
return JSON.stringify(value);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return String(value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function contentLines(content) {
|
|
69
|
+
if (content == null)
|
|
70
|
+
return [""];
|
|
71
|
+
if (typeof content === "string")
|
|
72
|
+
return content.length > 0 ? content.split("\n") : [""];
|
|
73
|
+
if (Array.isArray(content)) {
|
|
74
|
+
const lines = [];
|
|
75
|
+
for (const part of content) {
|
|
76
|
+
if (part && typeof part === "object") {
|
|
77
|
+
const p = part;
|
|
78
|
+
if (p.type === "text" && typeof p.text === "string") {
|
|
79
|
+
lines.push(...(p.text.length > 0 ? p.text.split("\n") : [""]));
|
|
80
|
+
}
|
|
81
|
+
else if (p.type === "image_url") {
|
|
82
|
+
lines.push("[image]");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
lines.push(safeJson(part));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lines.push(String(part));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return lines.length > 0 ? lines : [""];
|
|
93
|
+
}
|
|
94
|
+
return [String(content)];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Каноническое многострочное представление отправляемого контекста.
|
|
98
|
+
* Порядок фиксирован: system → tools → history. Стабильный формат — залог
|
|
99
|
+
* осмысленного diff между итерациями.
|
|
100
|
+
*/
|
|
101
|
+
export function serializeContextForLog(messages, tools) {
|
|
102
|
+
const out = [];
|
|
103
|
+
const system = [];
|
|
104
|
+
const rest = [];
|
|
105
|
+
for (const m of messages)
|
|
106
|
+
(m.role === "system" ? system : rest).push(m);
|
|
107
|
+
out.push("[SYSTEM]");
|
|
108
|
+
if (system.length === 0) {
|
|
109
|
+
out.push("(none)");
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
for (const m of system)
|
|
113
|
+
out.push(...contentLines(m.content));
|
|
114
|
+
}
|
|
115
|
+
out.push("");
|
|
116
|
+
out.push("[TOOLS]");
|
|
117
|
+
if (tools.length === 0) {
|
|
118
|
+
out.push("(none)");
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
for (const tool of tools) {
|
|
122
|
+
out.push(`${tool.name} | ${tool.description} | ${safeJson(tool.parameters)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
out.push("");
|
|
126
|
+
out.push("[HISTORY]");
|
|
127
|
+
if (rest.length === 0) {
|
|
128
|
+
out.push("(empty)");
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
for (const m of rest) {
|
|
132
|
+
const head = [`role=${m.role}`];
|
|
133
|
+
if (m.tool_call_id)
|
|
134
|
+
head.push(`id=${m.tool_call_id}`);
|
|
135
|
+
if (m.name)
|
|
136
|
+
head.push(`name=${m.name}`);
|
|
137
|
+
if (m.success !== undefined)
|
|
138
|
+
head.push(`success=${m.success}`);
|
|
139
|
+
out.push(head.join(" "));
|
|
140
|
+
if (Array.isArray(m.tool_calls)) {
|
|
141
|
+
for (const tc of m.tool_calls) {
|
|
142
|
+
const name = tc.function?.name ?? "?";
|
|
143
|
+
const args = tc.function?.arguments;
|
|
144
|
+
out.push(` → ${name} ${typeof args === "string" ? args : safeJson(args)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
out.push(...contentLines(m.content));
|
|
148
|
+
out.push("");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out.join("\n");
|
|
152
|
+
}
|
|
153
|
+
function commonPrefixLen(a, b) {
|
|
154
|
+
const max = Math.min(a.length, b.length);
|
|
155
|
+
let i = 0;
|
|
156
|
+
while (i < max && a[i] === b[i])
|
|
157
|
+
i++;
|
|
158
|
+
return i;
|
|
159
|
+
}
|
|
160
|
+
function commonSuffixLen(a, b, prefix) {
|
|
161
|
+
const max = Math.min(a.length - prefix, b.length - prefix);
|
|
162
|
+
let i = 0;
|
|
163
|
+
while (i < max && a[a.length - 1 - i] === b[b.length - 1 - i])
|
|
164
|
+
i++;
|
|
165
|
+
return i;
|
|
166
|
+
}
|
|
167
|
+
/** LCS-дифф для средней части (уже без общего префикса/суффикса). */
|
|
168
|
+
function lcsOps(a, b) {
|
|
169
|
+
const n = a.length;
|
|
170
|
+
const m = b.length;
|
|
171
|
+
if (n === 0 && m === 0)
|
|
172
|
+
return [];
|
|
173
|
+
if (n === 0)
|
|
174
|
+
return b.map((line) => ({ type: "+", line }));
|
|
175
|
+
if (m === 0)
|
|
176
|
+
return a.map((line) => ({ type: "-", line }));
|
|
177
|
+
if (n * m > MAX_LCS_CELLS) {
|
|
178
|
+
const ops = [];
|
|
179
|
+
for (const line of a)
|
|
180
|
+
ops.push({ type: "-", line });
|
|
181
|
+
for (const line of b)
|
|
182
|
+
ops.push({ type: "+", line });
|
|
183
|
+
return ops;
|
|
184
|
+
}
|
|
185
|
+
const width = m + 1;
|
|
186
|
+
const dp = new Uint32Array((n + 1) * width);
|
|
187
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
188
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
189
|
+
dp[i * width + j] =
|
|
190
|
+
a[i] === b[j]
|
|
191
|
+
? dp[(i + 1) * width + (j + 1)] + 1
|
|
192
|
+
: Math.max(dp[(i + 1) * width + j], dp[i * width + (j + 1)]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const ops = [];
|
|
196
|
+
let i = 0;
|
|
197
|
+
let j = 0;
|
|
198
|
+
while (i < n && j < m) {
|
|
199
|
+
if (a[i] === b[j]) {
|
|
200
|
+
ops.push({ type: " ", line: a[i] });
|
|
201
|
+
i++;
|
|
202
|
+
j++;
|
|
203
|
+
}
|
|
204
|
+
else if (dp[(i + 1) * width + j] >= dp[i * width + (j + 1)]) {
|
|
205
|
+
ops.push({ type: "-", line: a[i] });
|
|
206
|
+
i++;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
ops.push({ type: "+", line: b[j] });
|
|
210
|
+
j++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
while (i < n)
|
|
214
|
+
ops.push({ type: "-", line: a[i++] });
|
|
215
|
+
while (j < m)
|
|
216
|
+
ops.push({ type: "+", line: b[j++] });
|
|
217
|
+
return ops;
|
|
218
|
+
}
|
|
219
|
+
function renderHunks(ops, context) {
|
|
220
|
+
const changed = [];
|
|
221
|
+
for (let i = 0; i < ops.length; i++) {
|
|
222
|
+
if (ops[i].type !== " ")
|
|
223
|
+
changed.push(i);
|
|
224
|
+
}
|
|
225
|
+
if (changed.length === 0)
|
|
226
|
+
return [];
|
|
227
|
+
// Номера строк до/после для каждого op.
|
|
228
|
+
const oldNum = new Array(ops.length);
|
|
229
|
+
const newNum = new Array(ops.length);
|
|
230
|
+
let o = 1;
|
|
231
|
+
let nw = 1;
|
|
232
|
+
for (let i = 0; i < ops.length; i++) {
|
|
233
|
+
oldNum[i] = o;
|
|
234
|
+
newNum[i] = nw;
|
|
235
|
+
if (ops[i].type === " ") {
|
|
236
|
+
o++;
|
|
237
|
+
nw++;
|
|
238
|
+
}
|
|
239
|
+
else if (ops[i].type === "-") {
|
|
240
|
+
o++;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
nw++;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const groups = [];
|
|
247
|
+
let start = changed[0];
|
|
248
|
+
let end = changed[0];
|
|
249
|
+
for (let k = 1; k < changed.length; k++) {
|
|
250
|
+
if (changed[k] - end <= context * 2) {
|
|
251
|
+
end = changed[k];
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
groups.push([start, end]);
|
|
255
|
+
start = changed[k];
|
|
256
|
+
end = changed[k];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
groups.push([start, end]);
|
|
260
|
+
const hunks = [];
|
|
261
|
+
for (const [s, e] of groups) {
|
|
262
|
+
const from = Math.max(0, s - context);
|
|
263
|
+
const to = Math.min(ops.length - 1, e + context);
|
|
264
|
+
let oldCount = 0;
|
|
265
|
+
let newCount = 0;
|
|
266
|
+
const body = [];
|
|
267
|
+
for (let i = from; i <= to; i++) {
|
|
268
|
+
const op = ops[i];
|
|
269
|
+
if (op.type !== "+")
|
|
270
|
+
oldCount++;
|
|
271
|
+
if (op.type !== "-")
|
|
272
|
+
newCount++;
|
|
273
|
+
body.push(`${op.type}${op.line}`);
|
|
274
|
+
}
|
|
275
|
+
hunks.push(`@@ -${oldNum[from]},${oldCount} +${newNum[from]},${newCount} @@`);
|
|
276
|
+
hunks.push(...body);
|
|
277
|
+
}
|
|
278
|
+
return hunks;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Плоский unified diff без ANSI и truncation.
|
|
282
|
+
* `prev`/`next` — многострочный текст. Возвращает "" при отсутствии изменений.
|
|
283
|
+
*/
|
|
284
|
+
export function unifiedDiff(prev, next, context = CONTEXT_LINES, fromLabel = "a", toLabel = "b") {
|
|
285
|
+
if (prev === next)
|
|
286
|
+
return "";
|
|
287
|
+
const a = prev.split("\n");
|
|
288
|
+
const b = next.split("\n");
|
|
289
|
+
const prefix = commonPrefixLen(a, b);
|
|
290
|
+
const suffix = commonSuffixLen(a, b, prefix);
|
|
291
|
+
const midA = a.slice(prefix, a.length - suffix);
|
|
292
|
+
const midB = b.slice(prefix, b.length - suffix);
|
|
293
|
+
const midOps = lcsOps(midA, midB);
|
|
294
|
+
const ops = [];
|
|
295
|
+
for (let i = 0; i < prefix; i++)
|
|
296
|
+
ops.push({ type: " ", line: a[i] });
|
|
297
|
+
ops.push(...midOps);
|
|
298
|
+
for (let i = suffix; i > 0; i--)
|
|
299
|
+
ops.push({ type: " ", line: a[a.length - i] });
|
|
300
|
+
const hunks = renderHunks(ops, context);
|
|
301
|
+
if (hunks.length === 0)
|
|
302
|
+
return "";
|
|
303
|
+
return [`--- ${fromLabel}`, `+++ ${toLabel}`, ...hunks].join("\n");
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Пишет git-подобный лог контекста в append-only файл.
|
|
307
|
+
*/
|
|
308
|
+
export class ContextLogger {
|
|
309
|
+
path;
|
|
310
|
+
sessionId;
|
|
311
|
+
model;
|
|
312
|
+
contextWindow;
|
|
313
|
+
runId;
|
|
314
|
+
redact;
|
|
315
|
+
now;
|
|
316
|
+
prev = null;
|
|
317
|
+
snapshotIndex = 0;
|
|
318
|
+
headerWritten = false;
|
|
319
|
+
outputIndex = 0;
|
|
320
|
+
constructor(opts) {
|
|
321
|
+
this.path = opts.path;
|
|
322
|
+
this.sessionId = opts.sessionId;
|
|
323
|
+
this.model = opts.model;
|
|
324
|
+
this.contextWindow = opts.contextWindow;
|
|
325
|
+
this.runId = opts.runId ?? process.pid;
|
|
326
|
+
this.redact = opts.redact !== false;
|
|
327
|
+
this.now = opts.now ?? (() => new Date());
|
|
328
|
+
}
|
|
329
|
+
get filePath() {
|
|
330
|
+
return this.path;
|
|
331
|
+
}
|
|
332
|
+
ensureHeader() {
|
|
333
|
+
if (this.headerWritten)
|
|
334
|
+
return;
|
|
335
|
+
this.headerWritten = true;
|
|
336
|
+
try {
|
|
337
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// best-effort: appendFileSync ниже сообщит об ошибке
|
|
341
|
+
}
|
|
342
|
+
const exists = existsSync(this.path);
|
|
343
|
+
let existing = "";
|
|
344
|
+
if (exists) {
|
|
345
|
+
try {
|
|
346
|
+
existing = readFileSync(this.path, "utf8");
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
existing = "";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// Продолжаем нумерацию snapshot'ов, если файл уже есть.
|
|
353
|
+
const matches = existing.match(/### snapshot (\d+)/g);
|
|
354
|
+
if (matches && matches.length > 0) {
|
|
355
|
+
const last = matches[matches.length - 1].match(/### snapshot (\d+)/);
|
|
356
|
+
if (last)
|
|
357
|
+
this.snapshotIndex = Number(last[1]) + 1;
|
|
358
|
+
}
|
|
359
|
+
if (existing.trim().length > 0) {
|
|
360
|
+
const runHeader = [
|
|
361
|
+
"",
|
|
362
|
+
BAR,
|
|
363
|
+
`===== run ${this.runId} · ${this.now().toISOString()} · session ${this.sessionId} =====`,
|
|
364
|
+
SEP,
|
|
365
|
+
"",
|
|
366
|
+
].join("\n");
|
|
367
|
+
this.append(runHeader);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
const header = [
|
|
371
|
+
BAR,
|
|
372
|
+
"mma context log",
|
|
373
|
+
`session: ${this.sessionId} model: ${this.model} contextWindow: ${this.contextWindow}`,
|
|
374
|
+
`run ${this.runId} · started ${this.now().toISOString()}`,
|
|
375
|
+
"format: snapshot 0 = full context (all lines \"+\"); later snapshots are line diffs",
|
|
376
|
+
" (- removed, + added). [OUTPUT] = model output, NOT part of the LLM context.",
|
|
377
|
+
`secrets: ${this.redact ? "redacted" : "RAW"}`,
|
|
378
|
+
BAR,
|
|
379
|
+
"",
|
|
380
|
+
].join("\n");
|
|
381
|
+
this.append(header);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
append(text) {
|
|
385
|
+
try {
|
|
386
|
+
appendFileSync(this.path, text.endsWith("\n") ? text : `${text}\n`, "utf8");
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
// Логирование контекста не должно ронять агент.
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
sanitize(text) {
|
|
393
|
+
return this.redact ? redactContextSecrets(text) : text;
|
|
394
|
+
}
|
|
395
|
+
/** Записывает снимок отправляемого контекста (diff к предыдущему). */
|
|
396
|
+
record(messages, tools, meta) {
|
|
397
|
+
this.ensureHeader();
|
|
398
|
+
const text = this.sanitize(serializeContextForLog(messages, tools));
|
|
399
|
+
const idx = this.snapshotIndex;
|
|
400
|
+
const header = `### snapshot ${idx} · run ${this.runId} · iteration ${meta.iteration} · ${meta.ts} · ` +
|
|
401
|
+
`${meta.totalTokens}/${meta.contextWindow} tok ` +
|
|
402
|
+
`(system ${meta.systemTokens} · tools ${meta.toolTokens} · history ${meta.historyTokens})`;
|
|
403
|
+
let block;
|
|
404
|
+
if (this.prev === null) {
|
|
405
|
+
const lines = text.split("\n");
|
|
406
|
+
const body = lines.map((l) => `+${l}`).join("\n");
|
|
407
|
+
block = `${header}\n--- /dev/null\n+++ b/context@${idx}\n@@ -0,0 +1,${lines.length} @@\n${body}`;
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
const diff = unifiedDiff(this.prev, text, CONTEXT_LINES, `context@${idx - 1}`, `context@${idx}`);
|
|
411
|
+
block = diff ? `${header}\n${diff}` : `${header}\n(context unchanged)`;
|
|
412
|
+
}
|
|
413
|
+
this.prev = text;
|
|
414
|
+
this.snapshotIndex++;
|
|
415
|
+
this.append(`\n${block}\n`);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Аннотация вывода модели. Reasoning/текст/tool_calls НЕ входят в контекст
|
|
419
|
+
* LLM для следующего запроса — поэтому это не diff, а плоская запись.
|
|
420
|
+
*/
|
|
421
|
+
recordOutput(out) {
|
|
422
|
+
const hasReasoning = !!out.reasoning?.trim();
|
|
423
|
+
const hasText = !!out.text?.trim();
|
|
424
|
+
const hasCalls = !!out.toolCalls && out.toolCalls.length > 0;
|
|
425
|
+
if (!hasReasoning && !hasText && !hasCalls)
|
|
426
|
+
return;
|
|
427
|
+
this.ensureHeader();
|
|
428
|
+
this.outputIndex++;
|
|
429
|
+
const lines = [
|
|
430
|
+
"",
|
|
431
|
+
`[OUTPUT #${this.outputIndex} ← result of iteration ${out.iteration}, NOT part of the LLM context]`,
|
|
432
|
+
];
|
|
433
|
+
if (hasReasoning) {
|
|
434
|
+
lines.push("▍reasoning:");
|
|
435
|
+
for (const line of this.sanitize(out.reasoning).split("\n"))
|
|
436
|
+
lines.push(`▍ ${line}`);
|
|
437
|
+
}
|
|
438
|
+
if (hasText) {
|
|
439
|
+
lines.push("▍assistant text:");
|
|
440
|
+
for (const line of this.sanitize(out.text).split("\n"))
|
|
441
|
+
lines.push(`▍ ${line}`);
|
|
442
|
+
}
|
|
443
|
+
if (hasCalls) {
|
|
444
|
+
lines.push("▍tool_calls:");
|
|
445
|
+
for (const call of out.toolCalls) {
|
|
446
|
+
lines.push(`▍ ${call.name} ${this.sanitize(safeJson(call.arguments))}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
this.append(lines.join("\n"));
|
|
450
|
+
}
|
|
451
|
+
/** Append-only логгер — закрывать нечего. Оставлено для симметрии API. */
|
|
452
|
+
close() {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Рендер UI-индикаторов контекста (stats-бар, уведомление о компакции). */
|
|
1
2
|
import { pc } from "../../ui/colors";
|
|
2
3
|
/**
|
|
3
4
|
* Рендер UI-индикаторов контекста (stats-бар и уведомление о компакции).
|
|
@@ -9,6 +10,10 @@ import { pc } from "../../ui/colors";
|
|
|
9
10
|
*/
|
|
10
11
|
export class ContextRenderer {
|
|
11
12
|
lastCompactionShown = 0;
|
|
13
|
+
/**
|
|
14
|
+
* Рисует индикаторы контекста (stats либо уведомление о компакции).
|
|
15
|
+
* @param ui UI-конфиг; @param contextManager менеджер контекста; @param onMeta sink мета-вывода.
|
|
16
|
+
*/
|
|
12
17
|
render(ui, contextManager, onMeta) {
|
|
13
18
|
if (ui?.showContextStats) {
|
|
14
19
|
this.renderStats(contextManager, onMeta);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/** Вентиль контр-агента (критика): проверка мутаций перед финалом. */
|
|
2
|
+
import { t } from "../../i18n/index";
|
|
3
|
+
const MUTATIONS_BEFORE_CHECK = 3;
|
|
4
|
+
/** Хвост вывода тула в evidence-событии (ограничен — у критика свой бюджет). */
|
|
5
|
+
const MAX_EVENT_OUTPUT_CHARS = 600;
|
|
6
|
+
function issueSignature(issue) {
|
|
7
|
+
return `${issue.severity}:${issue.claim}`.toLowerCase().replace(/\s+/g, " ").slice(0, 120);
|
|
8
|
+
}
|
|
9
|
+
function renderIssues(issues) {
|
|
10
|
+
return issues
|
|
11
|
+
.slice(0, 5)
|
|
12
|
+
.map((i) => `- [${i.severity}] ${i.problem || i.claim}${i.suggestion ? ` → ${i.suggestion}` : ""}`)
|
|
13
|
+
.join("\n");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Вентиль критика: триггерит проверку по мутациям, инжектит вердикт в контекст.
|
|
17
|
+
* Fail-open: сломанный/перебюджетный критик никогда не блокирует агента.
|
|
18
|
+
*/
|
|
19
|
+
export class CriticGate {
|
|
20
|
+
opts;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
this.opts = opts;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Оценивает ход через критика.
|
|
26
|
+
* @param deps состояние цикла и события тулов; @returns "pass" или "retry".
|
|
27
|
+
*/
|
|
28
|
+
async evaluate(deps) {
|
|
29
|
+
if (!this.shouldRun(deps))
|
|
30
|
+
return "pass";
|
|
31
|
+
this.logCheck(deps);
|
|
32
|
+
deps.state.criticCalls++;
|
|
33
|
+
deps.state.mutationsSinceCritic = 0;
|
|
34
|
+
const verdict = await this.verifySafely(deps);
|
|
35
|
+
if (!verdict || verdict.status === "ok")
|
|
36
|
+
return "pass";
|
|
37
|
+
const fresh = this.takeFreshIssues(deps.state, verdict.issues);
|
|
38
|
+
if (fresh.length === 0)
|
|
39
|
+
return "pass";
|
|
40
|
+
this.logVerdict(deps, verdict, fresh.length);
|
|
41
|
+
const canRepair = verdict.status === "critical" &&
|
|
42
|
+
deps.beforeFinish &&
|
|
43
|
+
deps.state.criticRepairCycles < this.opts.maxRepairCycles;
|
|
44
|
+
this.inject(deps, verdict, fresh, canRepair);
|
|
45
|
+
return "retry";
|
|
46
|
+
}
|
|
47
|
+
/** Fail-open: битый/перебюджетный критик не блокирует агента. */
|
|
48
|
+
async verifySafely(deps) {
|
|
49
|
+
try {
|
|
50
|
+
return await this.opts.verify(this.opts.buildPack(deps), this.opts.signal);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
shouldRun(deps) {
|
|
57
|
+
if (!this.opts.enabled)
|
|
58
|
+
return false;
|
|
59
|
+
if (deps.state.criticCalls >= this.opts.maxCalls)
|
|
60
|
+
return false;
|
|
61
|
+
if (!deps.beforeFinish && deps.state.mutationsSinceCritic < MUTATIONS_BEFORE_CHECK) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return deps.toolEvents.length !== 0;
|
|
65
|
+
}
|
|
66
|
+
takeFreshIssues(state, issues) {
|
|
67
|
+
const fresh = [];
|
|
68
|
+
for (const issue of issues) {
|
|
69
|
+
const sig = issueSignature(issue);
|
|
70
|
+
if (state.seenCriticIssues.has(sig))
|
|
71
|
+
continue;
|
|
72
|
+
state.seenCriticIssues.add(sig);
|
|
73
|
+
fresh.push(issue);
|
|
74
|
+
}
|
|
75
|
+
return fresh;
|
|
76
|
+
}
|
|
77
|
+
logCheck(deps) {
|
|
78
|
+
deps.slog.logCritic("critic_check", {
|
|
79
|
+
iteration: deps.iteration,
|
|
80
|
+
beforeFinish: deps.beforeFinish,
|
|
81
|
+
mutations: deps.state.mutationsSinceCritic,
|
|
82
|
+
tools: deps.toolEvents.map((e) => e.tool).slice(0, 8),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
logVerdict(deps, verdict, count) {
|
|
86
|
+
deps.slog.logCritic("critic_verdict", {
|
|
87
|
+
status: verdict.status,
|
|
88
|
+
summary: verdict.summary,
|
|
89
|
+
issueCount: count,
|
|
90
|
+
});
|
|
91
|
+
deps.logger.diagnostic("info", `Critic ${verdict.status}: ${verdict.summary} (${count} issue(s))`);
|
|
92
|
+
}
|
|
93
|
+
inject(deps, verdict, fresh, canRepair) {
|
|
94
|
+
if (!canRepair) {
|
|
95
|
+
deps.contextManager.addMessage({
|
|
96
|
+
role: "user",
|
|
97
|
+
content: `<system-summary>${t("critic.advice", { issues: renderIssues(fresh) })}</system-summary>`,
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
deps.state.criticRepairCycles++;
|
|
102
|
+
deps.slog.logCritic("critic_escalation", {
|
|
103
|
+
summary: verdict.summary,
|
|
104
|
+
cycle: deps.state.criticRepairCycles,
|
|
105
|
+
});
|
|
106
|
+
deps.contextManager.addMessage({
|
|
107
|
+
role: "user",
|
|
108
|
+
content: `<system-summary>${t("critic.repair", {
|
|
109
|
+
summary: verdict.summary,
|
|
110
|
+
issues: renderIssues(fresh),
|
|
111
|
+
})}</system-summary>`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Собирает события тулов хода из активной истории (для evidence-пакета критика).
|
|
117
|
+
* @param contextManager менеджер с getActiveHistory; @returns события role:"tool".
|
|
118
|
+
*/
|
|
119
|
+
export function gatherTurnToolEvents(contextManager) {
|
|
120
|
+
const events = [];
|
|
121
|
+
for (const m of contextManager.getActiveHistory()) {
|
|
122
|
+
if (m.role !== "tool" || !m.name)
|
|
123
|
+
continue;
|
|
124
|
+
// Сообщения role:"tool" уже несут аргументы (tool-batch) и обрезанный
|
|
125
|
+
// вывод — пробрасываем оба, чтобы критик видел ЧТО тронуто и ЧТО вернулось.
|
|
126
|
+
// Без аргументов пакет слеп (changedFiles/commands пусты) и критик
|
|
127
|
+
// вынужден вернуть "ok" за отсутствием улик.
|
|
128
|
+
const output = typeof m.content === "string" ? m.content.slice(-MAX_EVENT_OUTPUT_CHARS) : undefined;
|
|
129
|
+
events.push({ tool: m.name, args: m.arguments, ok: m.success, output });
|
|
130
|
+
}
|
|
131
|
+
return events;
|
|
132
|
+
}
|