micro-models-agent 2.16.1 → 2.17.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 +28 -0
- package/dist/assets/app.js +1 -314
- package/dist/assets/hljs.js +5 -1244
- package/dist/assets/index.html +11 -0
- package/dist/assets/js/api.js +1 -28
- package/dist/assets/js/core/menus.js +1 -0
- package/dist/assets/js/core/store/feed.js +1 -0
- package/dist/assets/js/core/store/index.js +1 -0
- package/dist/assets/js/core/store/session.js +1 -0
- package/dist/assets/js/core/store/settings.js +1 -0
- package/dist/assets/js/core/store/tabs.js +1 -0
- package/dist/assets/js/dialog.js +1 -105
- package/dist/assets/js/dom.js +1 -178
- package/dist/assets/js/feed/core.js +1 -0
- package/dist/assets/js/feed/history.js +1 -0
- package/dist/assets/js/feed/plan.js +1 -0
- package/dist/assets/js/feed/question.js +3 -0
- package/dist/assets/js/feed/sidebar.js +1 -0
- package/dist/assets/js/feed/stream.js +1 -0
- package/dist/assets/js/feed/turn.js +5 -0
- package/dist/assets/js/feed.js +1 -1250
- package/dist/assets/js/format.js +2 -122
- package/dist/assets/js/i18n.js +1 -46
- package/dist/assets/js/rail.js +1 -114
- package/dist/assets/js/session/cmdk.js +1 -0
- package/dist/assets/js/session/composer.js +1 -0
- package/dist/assets/js/session/sessions-menu.js +1 -0
- package/dist/assets/js/session/tabs.js +1 -0
- package/dist/assets/js/settings/controls.js +1 -0
- package/dist/assets/js/settings/editors.js +1 -0
- package/dist/assets/js/settings/form.js +1 -0
- package/dist/assets/js/settings/modal.js +1 -0
- package/dist/assets/js/settings/panels.js +1 -0
- package/dist/assets/js/settings/providers.js +1 -0
- package/dist/assets/js/settings/search.js +1 -0
- package/dist/assets/js/settings/subdialog.js +1 -0
- package/dist/assets/js/settings/write.js +1 -0
- package/dist/assets/js/settings.js +1 -1914
- package/dist/assets/js/sidebar/core.js +1 -0
- package/dist/assets/js/sidebar/lsp.js +2 -0
- package/dist/assets/js/sidebar/map.js +1 -0
- package/dist/assets/js/sidebar/state.js +1 -0
- package/dist/assets/js/sidebar/trace.js +5 -0
- package/dist/assets/js/sidebar.js +1 -705
- package/dist/assets/js/store.js +1 -82
- package/dist/assets/js/subdialog.js +1 -0
- package/dist/assets/md.js +1 -291
- package/dist/assets/setup.js +3 -816
- package/dist/assets/smd.js +29 -1665
- package/dist/assets/styles.css +1 -1598
- package/dist/certification/certifications.json +1 -494
- package/dist/i18n/en.json +1 -1851
- package/dist/i18n/ru.json +1 -1854
- package/dist/main.js +815 -55797
- package/dist/modules/browser/bridge-server.mjs +2 -252
- package/package.json +10 -5
- package/dist/assets/js/settings-controls.js +0 -452
- package/dist/assets/js/tabs.js +0 -867
- package/dist/cli/cache-line.js +0 -31
- package/dist/cli/command-suggest.js +0 -39
- package/dist/cli/commands.js +0 -781
- package/dist/cli/completer.js +0 -268
- package/dist/cli/index.js +0 -3
- package/dist/cli/json-payload.js +0 -32
- package/dist/cli/main.js +0 -346
- package/dist/cli/onboarding-commands.js +0 -126
- package/dist/cli/plugin-commands.js +0 -38
- package/dist/cli/relaunch.js +0 -40
- package/dist/cli/repl-commands-lsp.js +0 -75
- package/dist/cli/repl-commands-skills.js +0 -105
- package/dist/cli/repl-commands.js +0 -1218
- package/dist/cli/repl.js +0 -1238
- package/dist/cli/run-result.js +0 -38
- package/dist/cli/security-commands.js +0 -177
- package/dist/cli/setup-order.js +0 -6
- package/dist/cli/setup-prompt.js +0 -50
- package/dist/cli/setup.js +0 -161
- package/dist/cli/web-command.js +0 -98
- package/dist/config/budget.js +0 -48
- package/dist/config/config.js +0 -345
- package/dist/config/defaults.js +0 -197
- package/dist/config/domains.js +0 -135
- package/dist/config/env.js +0 -46
- package/dist/config/experts.js +0 -17
- package/dist/config/index.js +0 -5
- package/dist/config/paths.js +0 -56
- package/dist/config/redact.js +0 -44
- package/dist/config/registry-extra.js +0 -334
- package/dist/config/registry.js +0 -584
- package/dist/config/security.js +0 -212
- package/dist/config/timeouts.js +0 -74
- package/dist/config/types.js +0 -1
- package/dist/config/utils.js +0 -57
- package/dist/core/agent/artifact-progress.js +0 -59
- package/dist/core/agent/artifact-resolve.js +0 -79
- package/dist/core/agent/audit-gate.js +0 -53
- package/dist/core/agent/compaction.js +0 -89
- package/dist/core/agent/constants.js +0 -90
- package/dist/core/agent/context-log.js +0 -454
- package/dist/core/agent/context-renderer.js +0 -45
- package/dist/core/agent/critic-gate.js +0 -132
- package/dist/core/agent/hallucination-gate.js +0 -197
- package/dist/core/agent/loop-state.js +0 -68
- package/dist/core/agent/prefix-monitor.js +0 -101
- package/dist/core/agent/reasoning-resolver.js +0 -65
- package/dist/core/agent/token-tracker.js +0 -99
- package/dist/core/agent/tool-batch.js +0 -312
- package/dist/core/agent/tool-images.js +0 -18
- package/dist/core/agent/tool-output.js +0 -116
- package/dist/core/agent/tool-pairing.js +0 -60
- package/dist/core/agent-moe.js +0 -248
- package/dist/core/agent.js +0 -1299
- package/dist/core/bootstrap/agent-phase.js +0 -66
- package/dist/core/bootstrap/config-phase.js +0 -86
- package/dist/core/bootstrap/modules-phase.js +0 -269
- package/dist/core/bootstrap/provider-phase.js +0 -183
- package/dist/core/bootstrap/session-phase.js +0 -204
- package/dist/core/bootstrap/state.js +0 -94
- package/dist/core/bootstrap/workspace-phase.js +0 -115
- package/dist/core/bootstrap.js +0 -112
- package/dist/core/crash-handler.js +0 -62
- package/dist/core/environment.js +0 -224
- package/dist/core/host-plan.js +0 -43
- package/dist/core/host.js +0 -23
- package/dist/core/index.js +0 -3
- package/dist/core/prompt-builder.js +0 -95
- package/dist/core/prompt-overflow.js +0 -306
- package/dist/core/session-logger.js +0 -375
- package/dist/core/system-prompt.js +0 -117
- package/dist/core/types.js +0 -1
- package/dist/core/version.js +0 -28
- package/dist/core/web-host.js +0 -34
- package/dist/core/workspace.js +0 -80
- package/dist/i18n/index.js +0 -80
- package/dist/index.js +0 -30
- package/dist/llm/cache-usage.js +0 -76
- package/dist/llm/image-utils.js +0 -138
- package/dist/llm/index.js +0 -4
- package/dist/llm/llm-errors.js +0 -50
- package/dist/llm/model-loader.js +0 -86
- package/dist/llm/models-dev.js +0 -121
- package/dist/llm/openai-compat.js +0 -756
- package/dist/llm/orchestrator.js +0 -294
- package/dist/llm/provider-budget.js +0 -68
- package/dist/llm/provider-context.js +0 -233
- package/dist/llm/provider.js +0 -9
- package/dist/llm/response.js +0 -44
- package/dist/llm/stream-state.js +0 -26
- package/dist/llm/token-counter.js +0 -83
- package/dist/llm/types.js +0 -1
- package/dist/logger/app-logger.js +0 -212
- package/dist/logger/file-log.js +0 -160
- package/dist/logger/index.js +0 -2
- package/dist/migration/backup.js +0 -56
- package/dist/migration/detect.js +0 -61
- package/dist/migration/index.js +0 -3
- package/dist/modules/artifacts/store.js +0 -62
- package/dist/modules/browser/actions.js +0 -79
- package/dist/modules/browser/bridge-client.js +0 -203
- package/dist/modules/browser/bridge-path.js +0 -10
- package/dist/modules/browser/console-buffer.js +0 -37
- package/dist/modules/browser/cookie-store.js +0 -29
- package/dist/modules/browser/driver-types.js +0 -1
- package/dist/modules/browser/driver.js +0 -186
- package/dist/modules/browser/index.js +0 -9
- package/dist/modules/browser/module.js +0 -30
- package/dist/modules/browser/session.js +0 -322
- package/dist/modules/browser/snapshot.js +0 -154
- package/dist/modules/browser/types.js +0 -13
- package/dist/modules/certification/cli.js +0 -264
- package/dist/modules/certification/fact-checker.js +0 -90
- package/dist/modules/certification/loader.js +0 -127
- package/dist/modules/certification/manifest.js +0 -231
- package/dist/modules/certification/runner.js +0 -233
- package/dist/modules/certification/scenarios.js +0 -586
- package/dist/modules/certification/syntax-scenarios.js +0 -50
- package/dist/modules/certification/types.js +0 -1
- package/dist/modules/context/chunk-query.js +0 -122
- package/dist/modules/context/fact-extractor.js +0 -170
- package/dist/modules/context/history.js +0 -15
- package/dist/modules/context/index.js +0 -2
- package/dist/modules/context/manager.js +0 -482
- package/dist/modules/critic/client.js +0 -176
- package/dist/modules/critic/evidence.js +0 -81
- package/dist/modules/critic/provider.js +0 -45
- package/dist/modules/critic/tools.js +0 -78
- package/dist/modules/critic/types.js +0 -1
- package/dist/modules/execution/audit-runners.js +0 -206
- package/dist/modules/execution/auditor.js +0 -222
- package/dist/modules/execution/execution-plugin.js +0 -478
- package/dist/modules/execution/index.js +0 -8
- package/dist/modules/execution/input-from.js +0 -70
- package/dist/modules/execution/module.js +0 -891
- package/dist/modules/execution/moe-executor.js +0 -443
- package/dist/modules/execution/plan-actions.js +0 -642
- package/dist/modules/execution/plan-checks.js +0 -35
- package/dist/modules/execution/plan-coverage.js +0 -71
- package/dist/modules/execution/plan-persister.js +0 -46
- package/dist/modules/execution/plan-steps.js +0 -31
- package/dist/modules/execution/plan-store.js +0 -274
- package/dist/modules/execution/plan-tool.js +0 -249
- package/dist/modules/execution/plan-validator.js +0 -197
- package/dist/modules/execution/planner.js +0 -103
- package/dist/modules/execution/stuck-detector.js +0 -834
- package/dist/modules/execution/tracker.js +0 -92
- package/dist/modules/execution/transient-error.js +0 -30
- package/dist/modules/execution/types.js +0 -1
- package/dist/modules/execution/verifier.js +0 -343
- package/dist/modules/execution/windows-commands.js +0 -52
- package/dist/modules/hallucination/artifact-claim.js +0 -93
- package/dist/modules/hallucination/confidence.js +0 -81
- package/dist/modules/hallucination/consistency.js +0 -37
- package/dist/modules/hallucination/detector.js +0 -57
- package/dist/modules/hallucination/factual.js +0 -205
- package/dist/modules/hallucination/index.js +0 -6
- package/dist/modules/hallucination/js-identifiers.js +0 -261
- package/dist/modules/hallucination/llm-judge.js +0 -116
- package/dist/modules/hallucination/success-claim.js +0 -59
- package/dist/modules/index.js +0 -5
- package/dist/modules/indexer/cache.js +0 -88
- package/dist/modules/indexer/index.js +0 -4
- package/dist/modules/indexer/map-command.js +0 -33
- package/dist/modules/indexer/map-select.js +0 -87
- package/dist/modules/indexer/module.js +0 -293
- package/dist/modules/indexer/project-profile.js +0 -184
- package/dist/modules/indexer/symbols.js +0 -189
- package/dist/modules/indexer/walker.js +0 -285
- package/dist/modules/lsp/check-tool.js +0 -61
- package/dist/modules/lsp/client.js +0 -406
- package/dist/modules/lsp/command.js +0 -53
- package/dist/modules/lsp/config.js +0 -189
- package/dist/modules/lsp/index.js +0 -4
- package/dist/modules/lsp/module.js +0 -295
- package/dist/modules/lsp/probe.js +0 -86
- package/dist/modules/lsp/project-root.js +0 -69
- package/dist/modules/lsp/startup-check.js +0 -154
- package/dist/modules/lsp/types.js +0 -1
- package/dist/modules/mcp/client.js +0 -445
- package/dist/modules/mcp/index.js +0 -4
- package/dist/modules/mcp/module.js +0 -295
- package/dist/modules/mcp/registry.js +0 -20
- package/dist/modules/memory/index.js +0 -2
- package/dist/modules/memory/module.js +0 -101
- package/dist/modules/memory/search.js +0 -45
- package/dist/modules/memory/store.js +0 -107
- package/dist/modules/onboarding/init.js +0 -47
- package/dist/modules/onboarding/meet.js +0 -30
- package/dist/modules/onboarding/mode.js +0 -30
- package/dist/modules/onboarding/prompts.js +0 -43
- package/dist/modules/pipelines/engine.js +0 -70
- package/dist/modules/pipelines/index.js +0 -4
- package/dist/modules/pipelines/parser.js +0 -59
- package/dist/modules/pipelines/template.js +0 -18
- package/dist/modules/plugins/builtin/lint-on-write.js +0 -377
- package/dist/modules/plugins/builtin/notify.js +0 -11
- package/dist/modules/plugins/index.js +0 -2
- package/dist/modules/plugins/loader.js +0 -85
- package/dist/modules/plugins/manager.js +0 -244
- package/dist/modules/plugins/types.js +0 -1
- package/dist/modules/pricing/index.js +0 -70
- package/dist/modules/pricing/prices.js +0 -147
- package/dist/modules/processes/detect.js +0 -34
- package/dist/modules/processes/index.js +0 -4
- package/dist/modules/processes/kill-tree.js +0 -52
- package/dist/modules/processes/registry.js +0 -294
- package/dist/modules/processes/runner.js +0 -23
- package/dist/modules/providers/cache.js +0 -23
- package/dist/modules/providers/capabilities.js +0 -28
- package/dist/modules/providers/create.js +0 -21
- package/dist/modules/providers/factory.js +0 -28
- package/dist/modules/providers/fallback.js +0 -81
- package/dist/modules/providers/health.js +0 -47
- package/dist/modules/providers/index.js +0 -6
- package/dist/modules/providers/manager.js +0 -207
- package/dist/modules/providers/naming.js +0 -15
- package/dist/modules/providers/preflight.js +0 -71
- package/dist/modules/providers/presets.js +0 -232
- package/dist/modules/providers/registry.js +0 -27
- package/dist/modules/providers/types.js +0 -1
- package/dist/modules/reasoning/policy.js +0 -42
- package/dist/modules/reasoning/probe.js +0 -116
- package/dist/modules/registry.js +0 -48
- package/dist/modules/security/audit-log.js +0 -119
- package/dist/modules/security/audit-notifier.js +0 -267
- package/dist/modules/security/command-validator.js +0 -272
- package/dist/modules/security/content-scanner.js +0 -53
- package/dist/modules/security/data-sanitizer.js +0 -82
- package/dist/modules/security/encryption.js +0 -216
- package/dist/modules/security/index.js +0 -15
- package/dist/modules/security/network-validator.js +0 -236
- package/dist/modules/security/path-validator.js +0 -261
- package/dist/modules/security/protected-paths.js +0 -73
- package/dist/modules/security/rate-limiter.js +0 -98
- package/dist/modules/security/security-policies.js +0 -499
- package/dist/modules/security/session-encryption.js +0 -182
- package/dist/modules/security/session-isolation.js +0 -75
- package/dist/modules/session/auto-name.js +0 -63
- package/dist/modules/session/export.js +0 -98
- package/dist/modules/session/index.js +0 -4
- package/dist/modules/session/manager.js +0 -234
- package/dist/modules/session/module.js +0 -33
- package/dist/modules/session/store.js +0 -295
- package/dist/modules/session/types.js +0 -1
- package/dist/modules/setup/answers.js +0 -35
- package/dist/modules/setup/apply.js +0 -79
- package/dist/modules/setup/discovery.js +0 -72
- package/dist/modules/skills/index.js +0 -3
- package/dist/modules/skills/loader.js +0 -86
- package/dist/modules/skills/matcher.js +0 -27
- package/dist/modules/skills/module.js +0 -141
- package/dist/modules/types.js +0 -1
- package/dist/modules/updater/changelog-reader.js +0 -93
- package/dist/modules/updater/checker.js +0 -101
- package/dist/modules/updater/dev-detect.js +0 -18
- package/dist/modules/updater/index.js +0 -4
- package/dist/modules/updater/module.js +0 -129
- package/dist/modules/user-profile/compressor.js +0 -18
- package/dist/modules/user-profile/index.js +0 -2
- package/dist/modules/user-profile/profile.js +0 -77
- package/dist/modules/webui/answer-api.js +0 -29
- package/dist/modules/webui/assets.js +0 -50
- package/dist/modules/webui/body.js +0 -21
- package/dist/modules/webui/commands-settings.js +0 -209
- package/dist/modules/webui/commands-shared.js +0 -60
- package/dist/modules/webui/commands.js +0 -174
- package/dist/modules/webui/config-api.js +0 -134
- package/dist/modules/webui/file-api.js +0 -202
- package/dist/modules/webui/forward.js +0 -159
- package/dist/modules/webui/history-api.js +0 -112
- package/dist/modules/webui/host-state.js +0 -60
- package/dist/modules/webui/i18n-api.js +0 -9
- package/dist/modules/webui/limits.js +0 -6
- package/dist/modules/webui/log-forward.js +0 -20
- package/dist/modules/webui/memory-commands.js +0 -64
- package/dist/modules/webui/model-cache.js +0 -18
- package/dist/modules/webui/module.js +0 -148
- package/dist/modules/webui/persist.js +0 -24
- package/dist/modules/webui/prompt-io.js +0 -97
- package/dist/modules/webui/providers-api.js +0 -337
- package/dist/modules/webui/providers-view.js +0 -56
- package/dist/modules/webui/reasoning-view.js +0 -15
- package/dist/modules/webui/run-agent.js +0 -81
- package/dist/modules/webui/server.js +0 -284
- package/dist/modules/webui/sessions-api.js +0 -178
- package/dist/modules/webui/settings-api.js +0 -561
- package/dist/modules/webui/setup-api.js +0 -201
- package/dist/modules/webui/state.js +0 -74
- package/dist/modules/webui/util.js +0 -261
- package/dist/output/bus.js +0 -37
- package/dist/output/channel.js +0 -285
- package/dist/output/format.js +0 -15
- package/dist/output/index.js +0 -8
- package/dist/output/json-sink.js +0 -24
- package/dist/output/machine.js +0 -9
- package/dist/output/session-sink.js +0 -27
- package/dist/output/types.js +0 -1
- package/dist/tools/approve.js +0 -41
- package/dist/tools/attach-image.js +0 -102
- package/dist/tools/auto-fixer.js +0 -180
- package/dist/tools/auto-tags.js +0 -83
- package/dist/tools/bash.js +0 -649
- package/dist/tools/browser.js +0 -160
- package/dist/tools/chunk-query.js +0 -112
- package/dist/tools/create-dir.js +0 -60
- package/dist/tools/delete-file.js +0 -67
- package/dist/tools/download-file.js +0 -142
- package/dist/tools/edit-file.js +0 -136
- package/dist/tools/enable-tools.js +0 -59
- package/dist/tools/executor.js +0 -304
- package/dist/tools/file-info.js +0 -49
- package/dist/tools/filter-tools.js +0 -17
- package/dist/tools/glob-tool.js +0 -37
- package/dist/tools/grep-tool.js +0 -142
- package/dist/tools/hidden-tools-block.js +0 -37
- package/dist/tools/index.js +0 -88
- package/dist/tools/list-dir.js +0 -53
- package/dist/tools/load-skill.js +0 -51
- package/dist/tools/mcp-call.js +0 -80
- package/dist/tools/meet-done.js +0 -14
- package/dist/tools/move-file.js +0 -94
- package/dist/tools/path-utils.js +0 -108
- package/dist/tools/pipeline-run.js +0 -167
- package/dist/tools/preview.js +0 -2
- package/dist/tools/process-ids.js +0 -31
- package/dist/tools/process-kill.js +0 -33
- package/dist/tools/process-list.js +0 -38
- package/dist/tools/process-log.js +0 -49
- package/dist/tools/process-wait.js +0 -63
- package/dist/tools/prompt-io.js +0 -29
- package/dist/tools/question.js +0 -288
- package/dist/tools/read-file.js +0 -200
- package/dist/tools/recall.js +0 -124
- package/dist/tools/registry.js +0 -48
- package/dist/tools/remember.js +0 -72
- package/dist/tools/scope-check.js +0 -32
- package/dist/tools/scope-request.js +0 -96
- package/dist/tools/search-history.js +0 -88
- package/dist/tools/session-info.js +0 -46
- package/dist/tools/set-thinking.js +0 -75
- package/dist/tools/subagent.js +0 -257
- package/dist/tools/syntax-validator.js +0 -379
- package/dist/tools/types.js +0 -1
- package/dist/tools/user-input.js +0 -134
- package/dist/tools/web-browse.js +0 -93
- package/dist/tools/web-fetch.js +0 -108
- package/dist/tools/web-search.js +0 -107
- package/dist/tools/write-file.js +0 -128
- package/dist/ui/action-log.js +0 -13
- package/dist/ui/box.js +0 -78
- package/dist/ui/colors.js +0 -6
- package/dist/ui/diff.js +0 -189
- package/dist/ui/index.js +0 -7
- package/dist/ui/line-editor.js +0 -1083
- package/dist/ui/line-math.js +0 -98
- package/dist/ui/md-formatter.js +0 -322
- package/dist/ui/output.js +0 -19
- package/dist/ui/plan-view.js +0 -109
- package/dist/ui/renderer.js +0 -589
- package/dist/ui/spinner.js +0 -91
- package/dist/ui/table.js +0 -142
- package/dist/ui/theme.js +0 -143
- package/dist/utils/child-run.js +0 -20
- package/dist/utils/error.js +0 -4
- package/dist/utils/index.js +0 -6
- package/dist/utils/retry.js +0 -17
- package/dist/utils/sleep.js +0 -23
- package/dist/utils/truncate.js +0 -9
|
@@ -1,834 +0,0 @@
|
|
|
1
|
-
// Детектор застревания: повторы, фейлы, read-only петли, bash-флейлинг, хинты.
|
|
2
|
-
import { t } from "../../i18n/index";
|
|
3
|
-
import { isTransientError } from "./transient-error";
|
|
4
|
-
const BLOCKING_PATTERNS = [
|
|
5
|
-
/EACCES/i,
|
|
6
|
-
/permission denied/i,
|
|
7
|
-
/EPERM/i,
|
|
8
|
-
/ENOENT.*node_modules/i,
|
|
9
|
-
/ENOTDIR/i,
|
|
10
|
-
/invalid.*token/i,
|
|
11
|
-
/unauthorized/i,
|
|
12
|
-
/401/i,
|
|
13
|
-
/403/i,
|
|
14
|
-
];
|
|
15
|
-
/** Классифицирует вывод ошибки: транзиентная / блокирующая / системная. */
|
|
16
|
-
export function classifyError(output) {
|
|
17
|
-
if (isTransientError(output))
|
|
18
|
-
return "transient";
|
|
19
|
-
for (const p of BLOCKING_PATTERNS) {
|
|
20
|
-
if (p.test(output))
|
|
21
|
-
return "blocking";
|
|
22
|
-
}
|
|
23
|
-
return "systematic";
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Тулы только чтения: состояние наблюдают, не меняют. Длинная серия только их
|
|
27
|
-
* без write/exec между — модель читает/разведывает, ничего не производя:
|
|
28
|
-
* read-only петля, которую детектор обязан подсветить (было: 50+ read_file
|
|
29
|
-
* итераций с нулём записей после ложноположительного реджекта готового
|
|
30
|
-
* аудита задачи).
|
|
31
|
-
*/
|
|
32
|
-
const READ_ONLY_TOOLS = new Set([
|
|
33
|
-
"read_file",
|
|
34
|
-
"glob",
|
|
35
|
-
"grep",
|
|
36
|
-
"list_dir",
|
|
37
|
-
"file_info",
|
|
38
|
-
"browser",
|
|
39
|
-
"web_search",
|
|
40
|
-
"web_fetch",
|
|
41
|
-
"web_browse",
|
|
42
|
-
"search_history",
|
|
43
|
-
"recall",
|
|
44
|
-
"project_map",
|
|
45
|
-
"chunk_query",
|
|
46
|
-
"process_list",
|
|
47
|
-
"process_log",
|
|
48
|
-
"plan",
|
|
49
|
-
"plan_step",
|
|
50
|
-
"todo",
|
|
51
|
-
"verify",
|
|
52
|
-
"load_skill",
|
|
53
|
-
]);
|
|
54
|
-
/** Подряд read-only вызовов, образующих петлю. */
|
|
55
|
-
const READ_ONLY_LOOP_THRESHOLD = 10;
|
|
56
|
-
/**
|
|
57
|
-
* Bash-флейлинг: ≥BASH_FLAIL_THRESHOLD фейлов в последних BASH_FLAIL_WINDOW
|
|
58
|
-
* bash-попытках, независимо от перемежающихся успехов. Per-word hard-stop
|
|
59
|
-
* (bash.ts) обходится ротацией команд (wc → cat → Get-Content → …); окно
|
|
60
|
-
* ловит саму ротацию (было в ses_mt4x3zbo: 8 фейловых shell-уловок за ~12
|
|
61
|
-
* итераций при подсчёте/склейке файлов на Windows).
|
|
62
|
-
*/
|
|
63
|
-
const BASH_FLAIL_WINDOW = 6;
|
|
64
|
-
const BASH_FLAIL_THRESHOLD = 4;
|
|
65
|
-
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
66
|
-
/** Пути (абсолютные/относительные) + опционально :line:col. */
|
|
67
|
-
const ERROR_LOC_RE = /(?:[A-Za-z]:[\\/])?(?:[\w.@+-]+[\\/])+[\w.@+-]+\.[a-zA-Z0-9]{1,6}(?::\d+(?::\d+)?)?/g;
|
|
68
|
-
const SIGNATURE_MAX_CHARS = 120;
|
|
69
|
-
const SEARCHABLE_PATTERNS = [
|
|
70
|
-
/error TS\d+/i,
|
|
71
|
-
/ERR_[A-Z_]+/,
|
|
72
|
-
/ENOENT|EACCES|EPERM|ECONNREFUSED|ETIMEDOUT|ECONNRESET|ENOTFOUND|EADDRINUSE/i,
|
|
73
|
-
/Cannot find module|Module not found|ERR_MODULE_NOT_FOUND/i,
|
|
74
|
-
/TypeError|ReferenceError|SyntaxError|RangeError/i,
|
|
75
|
-
/is not a function|is not defined|is not a constructor/i,
|
|
76
|
-
/Cannot read properties of/i,
|
|
77
|
-
/failed to (compile|build|resolve|parse)/i,
|
|
78
|
-
/fatal error|panic/i,
|
|
79
|
-
];
|
|
80
|
-
const RUNTIME_WORDS = [
|
|
81
|
-
"bun",
|
|
82
|
-
"node",
|
|
83
|
-
"deno",
|
|
84
|
-
"npm",
|
|
85
|
-
"yarn",
|
|
86
|
-
"pnpm",
|
|
87
|
-
"pip",
|
|
88
|
-
"pip3",
|
|
89
|
-
"python",
|
|
90
|
-
"python3",
|
|
91
|
-
"tsc",
|
|
92
|
-
"tsx",
|
|
93
|
-
"npx",
|
|
94
|
-
"go",
|
|
95
|
-
"cargo",
|
|
96
|
-
"gradle",
|
|
97
|
-
"maven",
|
|
98
|
-
"make",
|
|
99
|
-
];
|
|
100
|
-
const FRAMEWORK_WORDS = [
|
|
101
|
-
"react",
|
|
102
|
-
"vue",
|
|
103
|
-
"angular",
|
|
104
|
-
"svelte",
|
|
105
|
-
"vite",
|
|
106
|
-
"next",
|
|
107
|
-
"nest",
|
|
108
|
-
"express",
|
|
109
|
-
"fastify",
|
|
110
|
-
"flask",
|
|
111
|
-
"django",
|
|
112
|
-
"spring",
|
|
113
|
-
"webpack",
|
|
114
|
-
"tailwind",
|
|
115
|
-
];
|
|
116
|
-
/**
|
|
117
|
-
* Стабильная идентичность ошибки: тул + нормализованные первые error-строки
|
|
118
|
-
* (ANSI вычищен, пути/локации заменены, пробелы склеены, кап 120 символов).
|
|
119
|
-
* Два прогона с одной первопричиной дают одну сигнатуру — она и триггерит
|
|
120
|
-
* автопоиск в вебе.
|
|
121
|
-
*/
|
|
122
|
-
export function normalizeErrorSignature(toolName, output) {
|
|
123
|
-
if (!output)
|
|
124
|
-
return null;
|
|
125
|
-
const cleaned = output
|
|
126
|
-
.replace(ANSI_RE, "")
|
|
127
|
-
.replace(ERROR_LOC_RE, " <path>")
|
|
128
|
-
.split("\n")
|
|
129
|
-
.map((l) => l.trim())
|
|
130
|
-
.filter(Boolean)
|
|
131
|
-
.join(" ")
|
|
132
|
-
.replace(/\s+/g, " ")
|
|
133
|
-
.trim();
|
|
134
|
-
if (!cleaned)
|
|
135
|
-
return null;
|
|
136
|
-
return `${toolName}::${cleaned.slice(0, SIGNATURE_MAX_CHARS)}`;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Гейт автопоиска в вебе: искать стоит только реальный содержательный текст
|
|
140
|
-
* ошибки. "Command failed with exit code 1" без деталей — шум, поиск по нему
|
|
141
|
-
* лишь жжёт запрос.
|
|
142
|
-
*/
|
|
143
|
-
export function isSearchableError(output) {
|
|
144
|
-
if (!output?.trim())
|
|
145
|
-
return false;
|
|
146
|
-
const text = output.trim();
|
|
147
|
-
return SEARCHABLE_PATTERNS.some((p) => p.test(text));
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Строит поисковый запрос по падающей ошибке: до 3 чищенных error-строк (пути
|
|
151
|
-
* вырезаны) с префиксом рантайма/фреймворка из step/bash-контекста и суффиксом
|
|
152
|
-
* "how to fix". На пустом тексте — фолбэк `tool step`. Кап maxQueryChars.
|
|
153
|
-
*/
|
|
154
|
-
export function buildSearchQuery(stepDescription, lastBashCommand, lastErrorOutput, toolName, maxQueryChars) {
|
|
155
|
-
const errorText = (lastErrorOutput || "")
|
|
156
|
-
.replace(ANSI_RE, "")
|
|
157
|
-
.replace(ERROR_LOC_RE, " <path>")
|
|
158
|
-
.split("\n")
|
|
159
|
-
.map((l) => l.trim())
|
|
160
|
-
.filter(Boolean)
|
|
161
|
-
.slice(0, 3)
|
|
162
|
-
.join(" ")
|
|
163
|
-
.replace(/\s+/g, " ")
|
|
164
|
-
.trim();
|
|
165
|
-
const context = `${stepDescription} ${lastBashCommand}`.toLowerCase();
|
|
166
|
-
const runtime = RUNTIME_WORDS.find((w) => context.includes(w)) || "";
|
|
167
|
-
const framework = FRAMEWORK_WORDS.find((w) => context.includes(w)) || "";
|
|
168
|
-
let query;
|
|
169
|
-
if (errorText) {
|
|
170
|
-
query =
|
|
171
|
-
runtime && !errorText.toLowerCase().includes(runtime) ? `${runtime} ${errorText}` : errorText;
|
|
172
|
-
if (framework && !query.toLowerCase().includes(framework))
|
|
173
|
-
query = `${query} ${framework}`;
|
|
174
|
-
query = `${query} how to fix`;
|
|
175
|
-
}
|
|
176
|
-
else if (runtime || framework) {
|
|
177
|
-
query = `${[runtime, framework, "error", "how to fix"].filter(Boolean).join(" ")}`;
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
query = `${toolName} ${stepDescription} how to fix`;
|
|
181
|
-
}
|
|
182
|
-
return query.slice(0, maxQueryChars);
|
|
183
|
-
}
|
|
184
|
-
/** Детектор застревания: счётчики итераций, ошибок, повторов, петель, эскалаций. */
|
|
185
|
-
export class StuckDetector {
|
|
186
|
-
threshold;
|
|
187
|
-
errorThreshold;
|
|
188
|
-
iterationsOnCurrentStep = 0;
|
|
189
|
-
currentStepId = null;
|
|
190
|
-
toolErrors = new Map();
|
|
191
|
-
currentStepDescription = "";
|
|
192
|
-
recentToolCalls = [];
|
|
193
|
-
maxRecentCalls = 10;
|
|
194
|
-
repetitionThreshold = 3;
|
|
195
|
-
consecutiveFailures = 0;
|
|
196
|
-
lastFailedTool = "";
|
|
197
|
-
lastErrorOutput = "";
|
|
198
|
-
escalationCount = 0;
|
|
199
|
-
escalationThreshold = 3;
|
|
200
|
-
fileRewriteCount = new Map();
|
|
201
|
-
fileRewriteThreshold = 3;
|
|
202
|
-
lastBashCommand = "";
|
|
203
|
-
lastBashOutput = "";
|
|
204
|
-
emptyBashRunCount = 0;
|
|
205
|
-
readOnlyStreak = 0;
|
|
206
|
-
bashRecent = [];
|
|
207
|
-
errorSignatureCounts = new Map();
|
|
208
|
-
searchedErrorSignatures = new Set();
|
|
209
|
-
webSearchThreshold;
|
|
210
|
-
constructor(threshold = 6, errorThreshold = 3, webSearchThreshold = 5) {
|
|
211
|
-
this.threshold = threshold;
|
|
212
|
-
this.errorThreshold = errorThreshold;
|
|
213
|
-
this.webSearchThreshold = webSearchThreshold;
|
|
214
|
-
}
|
|
215
|
-
recordIteration(stepId) {
|
|
216
|
-
if (stepId === this.currentStepId) {
|
|
217
|
-
this.iterationsOnCurrentStep++;
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
this.resetStepState(stepId);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/** Отмечает вызов тула (для детекции повторов; write/exec сбрасывают read-only стрик). */
|
|
224
|
-
recordToolCall(name, args) {
|
|
225
|
-
const argsKey = JSON.stringify(args);
|
|
226
|
-
this.recentToolCalls.push({ name, argsKey });
|
|
227
|
-
if (this.recentToolCalls.length > this.maxRecentCalls) {
|
|
228
|
-
this.recentToolCalls.shift();
|
|
229
|
-
}
|
|
230
|
-
// Трекаем подряд read-only стрик. Пишущий/исполняющий тул сбрасывает;
|
|
231
|
-
// plan/todo-бухгалтерия считается read-only (не реальный прогресс).
|
|
232
|
-
if (READ_ONLY_TOOLS.has(name)) {
|
|
233
|
-
this.readOnlyStreak++;
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
this.readOnlyStreak = 0;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* True после длинной серии read-only вызовов без записей между. Модель
|
|
241
|
-
* исследует/петляет, ничего не производя — инжектим recovery-хинт, чтобы
|
|
242
|
-
* перестала перечитывать и реально правила файлы.
|
|
243
|
-
*/
|
|
244
|
-
hasReadOnlyLoop() {
|
|
245
|
-
return this.readOnlyStreak >= READ_ONLY_LOOP_THRESHOLD;
|
|
246
|
-
}
|
|
247
|
-
/** Отмечает ошибку тула (счётчики + сигнатура для автопоиска). */
|
|
248
|
-
recordToolError(toolName, output) {
|
|
249
|
-
this.toolErrors.set(toolName, (this.toolErrors.get(toolName) || 0) + 1);
|
|
250
|
-
this.consecutiveFailures++;
|
|
251
|
-
this.lastFailedTool = toolName;
|
|
252
|
-
if (output)
|
|
253
|
-
this.lastErrorOutput = output;
|
|
254
|
-
const sig = normalizeErrorSignature(toolName, output);
|
|
255
|
-
if (sig) {
|
|
256
|
-
this.errorSignatureCounts.set(sig, (this.errorSignatureCounts.get(sig) || 0) + 1);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/** Последний текст ошибки. */
|
|
260
|
-
getLastErrorOutput() {
|
|
261
|
-
return this.lastErrorOutput;
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Записывает КАЖДЫЙ вызов bash (успех или нет): детекция рассуждает о
|
|
265
|
-
* повторных прогонах с пустым выводом, проблемах entry-point и ошибках
|
|
266
|
-
* платформы (PowerShell-командлеты в cmd.exe, bash-heredoc'и, …).
|
|
267
|
-
*/
|
|
268
|
-
recordBashOutput(command, output) {
|
|
269
|
-
const trimmed = output.trim();
|
|
270
|
-
if (command === this.lastBashCommand && !trimmed) {
|
|
271
|
-
this.emptyBashRunCount++;
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
this.emptyBashRunCount = 0;
|
|
275
|
-
}
|
|
276
|
-
this.lastBashCommand = command;
|
|
277
|
-
this.lastBashOutput = output;
|
|
278
|
-
}
|
|
279
|
-
/** Исход одной bash-попытки (успех/фейл) — для детекции флейлинга. */
|
|
280
|
-
recordBashAttempt(success) {
|
|
281
|
-
this.bashRecent.push(success);
|
|
282
|
-
if (this.bashRecent.length > BASH_FLAIL_WINDOW)
|
|
283
|
-
this.bashRecent.shift();
|
|
284
|
-
}
|
|
285
|
-
/** True, если свежие bash-попытки в массе фейлятся — модель флейлит. */
|
|
286
|
-
hasBashFlailing() {
|
|
287
|
-
if (this.bashRecent.length < BASH_FLAIL_THRESHOLD)
|
|
288
|
-
return false;
|
|
289
|
-
let failures = 0;
|
|
290
|
-
for (const ok of this.bashRecent)
|
|
291
|
-
if (!ok)
|
|
292
|
-
failures++;
|
|
293
|
-
return failures >= BASH_FLAIL_THRESHOLD;
|
|
294
|
-
}
|
|
295
|
-
/** Последняя bash-команда. */
|
|
296
|
-
getLastBashCommand() {
|
|
297
|
-
return this.lastBashCommand;
|
|
298
|
-
}
|
|
299
|
-
/** Вывод последней bash-команды. */
|
|
300
|
-
getLastBashOutput() {
|
|
301
|
-
return this.lastBashOutput;
|
|
302
|
-
}
|
|
303
|
-
/** Последний упавший тул. */
|
|
304
|
-
getLastFailedTool() {
|
|
305
|
-
return this.lastFailedTool;
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Первая сигнатура ошибки, чей счётчик повторов достиг порога веб-поиска и
|
|
309
|
-
* которая ЕЩЁ не искалась (или размаркирована для ретрая). Именно она
|
|
310
|
-
* триггерит автопоиск — не итерации и не per-tool счётчик ошибок.
|
|
311
|
-
*/
|
|
312
|
-
getRepeatedErrorSignature() {
|
|
313
|
-
for (const [sig, count] of this.errorSignatureCounts) {
|
|
314
|
-
if (count >= this.webSearchThreshold && !this.searchedErrorSignatures.has(sig)) {
|
|
315
|
-
return sig;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
/** Счётчик повторов сигнатуры ошибки. */
|
|
321
|
-
getErrorSignatureCount(sig) {
|
|
322
|
-
return this.errorSignatureCounts.get(sig) || 0;
|
|
323
|
-
}
|
|
324
|
-
/** Помечает сигнатуру как уже искавшуюся. */
|
|
325
|
-
markErrorSearched(sig) {
|
|
326
|
-
this.searchedErrorSignatures.add(sig);
|
|
327
|
-
}
|
|
328
|
-
/** Снимает метку поиска (разрешает один ретрай). */
|
|
329
|
-
unmarkErrorSearched(sig) {
|
|
330
|
-
this.searchedErrorSignatures.delete(sig);
|
|
331
|
-
}
|
|
332
|
-
/** True, если сигнатура уже искалась. */
|
|
333
|
-
hasErrorSearched(sig) {
|
|
334
|
-
return this.searchedErrorSignatures.has(sig);
|
|
335
|
-
}
|
|
336
|
-
/** Итераций на текущем шаге. */
|
|
337
|
-
getIterationsOnCurrentStep() {
|
|
338
|
-
return this.iterationsOnCurrentStep;
|
|
339
|
-
}
|
|
340
|
-
/** Отмечает успешный тул: сброс фейлов, затухание per-tool счётчиков и итераций. */
|
|
341
|
-
recordToolSuccess() {
|
|
342
|
-
this.consecutiveFailures = 0;
|
|
343
|
-
this.lastFailedTool = "";
|
|
344
|
-
// NOTE: escalationCount осознанно НЕ сбрасываем. Один тривиальный успех
|
|
345
|
-
// (напр. один read_file) раньше его чистил, и shouldEscalate() на флейлящей
|
|
346
|
-
// сессии почти не достигался — в ущерб анти-гейминг свойству, которое
|
|
347
|
-
// resetStepState() явно хранит.
|
|
348
|
-
// Затухание per-tool счётчиков на продуктивных вызовах: рано упавший тул не
|
|
349
|
-
// держит варнинг "Tool X failed N times" вечно (ses_mt4fn58c — bash упал 3x,
|
|
350
|
-
// варнинг перелоггировался после 30 успешных записей). Свежие фейлы счёт
|
|
351
|
-
// набирают заново.
|
|
352
|
-
for (const [tool, count] of this.toolErrors) {
|
|
353
|
-
if (count <= 1)
|
|
354
|
-
this.toolErrors.delete(tool);
|
|
355
|
-
else
|
|
356
|
-
this.toolErrors.set(tool, count - 1);
|
|
357
|
-
}
|
|
358
|
-
// Затухание счётчика итераций на продуктивных вызовах: чтение новых файлов,
|
|
359
|
-
// записи, правки — всё прогресс. Защита от ложных stuck-варнингов при
|
|
360
|
-
// легитимной разведке файлов (напр. чтение 12 файлов в analytic-шаге).
|
|
361
|
-
if (this.iterationsOnCurrentStep > 0) {
|
|
362
|
-
this.iterationsOnCurrentStep = Math.max(0, this.iterationsOnCurrentStep - 1);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
/** Ставит текущий шаг (сброс per-step состояния при смене). */
|
|
366
|
-
setCurrentStep(stepId, description) {
|
|
367
|
-
if (stepId !== this.currentStepId) {
|
|
368
|
-
this.resetStepState(stepId);
|
|
369
|
-
}
|
|
370
|
-
this.currentStepDescription = description;
|
|
371
|
-
}
|
|
372
|
-
/** True, если итераций на шаге больше порога. */
|
|
373
|
-
isStuck() {
|
|
374
|
-
return this.iterationsOnCurrentStep > this.threshold;
|
|
375
|
-
}
|
|
376
|
-
/** True, если тул (или любой) ошибался ≥ порога. */
|
|
377
|
-
hasRepetitiveErrors(toolName) {
|
|
378
|
-
if (toolName) {
|
|
379
|
-
return (this.toolErrors.get(toolName) || 0) >= this.errorThreshold;
|
|
380
|
-
}
|
|
381
|
-
return Array.from(this.toolErrors.values()).some((c) => c >= this.errorThreshold);
|
|
382
|
-
}
|
|
383
|
-
/** True при повторяющихся вызовах: подряд идентичных или перемежающихся (A,B,A,B,A). */
|
|
384
|
-
hasRepetitiveToolCalls() {
|
|
385
|
-
if (this.recentToolCalls.length < this.repetitionThreshold)
|
|
386
|
-
return false;
|
|
387
|
-
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
388
|
-
// Проверка подряд идентичных (исходная логика)
|
|
389
|
-
let consecutiveCount = 1;
|
|
390
|
-
for (let i = this.recentToolCalls.length - 2; i >= 0; i--) {
|
|
391
|
-
if (this.recentToolCalls[i].name === last.name &&
|
|
392
|
-
this.recentToolCalls[i].argsKey === last.argsKey) {
|
|
393
|
-
consecutiveCount++;
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (consecutiveCount >= this.repetitionThreshold)
|
|
400
|
-
return true;
|
|
401
|
-
// Плюс перемежающиеся паттерны (A, B, A, B, A): считаем каждую связку
|
|
402
|
-
// тул+args в окне и флагуем при ≥ порога
|
|
403
|
-
const counts = new Map();
|
|
404
|
-
for (const call of this.recentToolCalls) {
|
|
405
|
-
const key = `${call.name}::${call.argsKey}`;
|
|
406
|
-
counts.set(key, (counts.get(key) || 0) + 1);
|
|
407
|
-
}
|
|
408
|
-
for (const count of counts.values()) {
|
|
409
|
-
if (count >= this.repetitionThreshold)
|
|
410
|
-
return true;
|
|
411
|
-
}
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
/** True при ≥ порога подряд фейлов. */
|
|
415
|
-
hasConsecutiveFailures() {
|
|
416
|
-
return this.consecutiveFailures >= this.errorThreshold;
|
|
417
|
-
}
|
|
418
|
-
/** Число подряд фейлов. */
|
|
419
|
-
getConsecutiveFailuresCount() {
|
|
420
|
-
return this.consecutiveFailures;
|
|
421
|
-
}
|
|
422
|
-
/** Отмечает эскалацию (счётчик для shouldEscalate). */
|
|
423
|
-
recordEscalation() {
|
|
424
|
-
this.escalationCount++;
|
|
425
|
-
}
|
|
426
|
-
/** True, если эскалаций набралось на порог. */
|
|
427
|
-
shouldEscalate() {
|
|
428
|
-
return this.escalationCount >= this.escalationThreshold;
|
|
429
|
-
}
|
|
430
|
-
/** Сбрасывает счётчик эскалаций. */
|
|
431
|
-
resetEscalation() {
|
|
432
|
-
this.escalationCount = 0;
|
|
433
|
-
}
|
|
434
|
-
/** Число эскалаций. */
|
|
435
|
-
getEscalationCount() {
|
|
436
|
-
return this.escalationCount;
|
|
437
|
-
}
|
|
438
|
-
/** Хинты по описанию шага + состояниям, ещё не покрытым recovery-сообщением. */
|
|
439
|
-
getHints() {
|
|
440
|
-
const hints = [];
|
|
441
|
-
const desc = this.currentStepDescription.toLowerCase();
|
|
442
|
-
if (desc.includes("install") || desc.includes("npm") || desc.includes("pip")) {
|
|
443
|
-
hints.push(t("exec.hint_deps"));
|
|
444
|
-
}
|
|
445
|
-
if (desc.includes("test") || desc.includes("spec")) {
|
|
446
|
-
hints.push(t("exec.hint_test"));
|
|
447
|
-
}
|
|
448
|
-
if (desc.includes("build") || desc.includes("compile")) {
|
|
449
|
-
hints.push(t("exec.hint_build"));
|
|
450
|
-
}
|
|
451
|
-
if (desc.includes("deploy") || desc.includes("publish")) {
|
|
452
|
-
hints.push(t("exec.hint_deploy"));
|
|
453
|
-
}
|
|
454
|
-
// State-based буллеты. Recovery-сообщение (getRecoveryMessage) уже репортит
|
|
455
|
-
// ПЕРВИЧНУЮ причину (приоритет: stuck > tool-errors > consecutive >
|
|
456
|
-
// repetitive > read-only). Дублировать то же состояние хинтом — задвоить
|
|
457
|
-
// гайданс в одном cooldown-блоке. Буллет добавляем, только если recovery
|
|
458
|
-
// это состояние НЕ покрывает.
|
|
459
|
-
const primary = this.getPrimaryReason();
|
|
460
|
-
if (this.hasRepetitiveToolCalls() && primary !== "repetitive") {
|
|
461
|
-
hints.push(t("exec.hint_repetitive"));
|
|
462
|
-
}
|
|
463
|
-
if (this.hasConsecutiveFailures() && primary !== "consecutive") {
|
|
464
|
-
hints.push(t("exec.hint_consecutive"));
|
|
465
|
-
}
|
|
466
|
-
if (this.hasReadOnlyLoop() && primary !== "read-only") {
|
|
467
|
-
hints.push(t("exec.hint_read_only"));
|
|
468
|
-
}
|
|
469
|
-
return hints;
|
|
470
|
-
}
|
|
471
|
-
/** Actionable-хинты по последней ошибке: рантаймы, синтаксис, сеть, шелл-несоответствия. */
|
|
472
|
-
getActionableHints() {
|
|
473
|
-
const hints = [];
|
|
474
|
-
const error = this.lastErrorOutput;
|
|
475
|
-
// Bash-хинты от фейла тула не зависят — срабатывают, даже когда команда
|
|
476
|
-
// "успешна" с пустым выводом (потерянный entry point).
|
|
477
|
-
const isScriptRun = /(^|[\s&])(bun|node|tsx|ts-node|deno|python|python3)\S*\s+[^|&]+\s+[^\s]+$/.test(this.lastBashCommand);
|
|
478
|
-
if (isScriptRun && !this.lastBashOutput.trim() && this.emptyBashRunCount >= 2) {
|
|
479
|
-
hints.push(`The command "${this.lastBashCommand}" ran ${this.emptyBashRunCount} times with EMPTY output. The program likely has no entry point — read the file with read_file and check that it actually calls its main function with process.argv / CLI arguments and prints results (console.log). Then run it again.`);
|
|
480
|
-
}
|
|
481
|
-
if (!error)
|
|
482
|
-
return hints;
|
|
483
|
-
// Несовместимость рантайма — тул/модуль крашится на этой версии Node
|
|
484
|
-
if (/Cannot read properties of undefined|is not a function|is not a constructor/i.test(error)) {
|
|
485
|
-
if (/node_modules/.test(error)) {
|
|
486
|
-
hints.push("A dependency is incompatible with your Node.js version. Check if there is an alternative package or use a different runtime.");
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// Синтаксические ошибки TypeScript — модель сгенерила невалидный код
|
|
490
|
-
if (/TS1005|TS1003|TS1109|TS1128|TS1434/i.test(error)) {
|
|
491
|
-
hints.push("TypeScript syntax error. Read the error message carefully — it tells you the exact line and column. Fix the syntax before retrying.");
|
|
492
|
-
}
|
|
493
|
-
// Ошибки типов TypeScript — модель не понимает типы
|
|
494
|
-
if (/TS2322|TS2345|TS2769|TS7006|TS7016/i.test(error)) {
|
|
495
|
-
hints.push("TypeScript type mismatch. Check the type signature of the function/API you are using. If a package lacks type declarations, install @types/<package> or use skipLibCheck.");
|
|
496
|
-
}
|
|
497
|
-
// Модуль не найден — нет зависимости или неверный импорт
|
|
498
|
-
if (/Cannot find module|Module not found|ERR_MODULE_NOT_FOUND/i.test(error)) {
|
|
499
|
-
hints.push("Module not found. Run npm install or check if the import path is correct.");
|
|
500
|
-
}
|
|
501
|
-
// Ошибки прав доступа
|
|
502
|
-
if (/EACCES|EPERM|permission denied/i.test(error)) {
|
|
503
|
-
hints.push("Permission denied. Check file permissions or run with appropriate privileges.");
|
|
504
|
-
}
|
|
505
|
-
// Сетевые ошибки
|
|
506
|
-
if (/ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch failed/i.test(error)) {
|
|
507
|
-
hints.push("Network error. Check if the server is running and accessible.");
|
|
508
|
-
}
|
|
509
|
-
// Команда не найдена
|
|
510
|
-
if (/is not recognized|command not found|not found in path/i.test(error)) {
|
|
511
|
-
hints.push("Command not found. Check if the tool is installed and in PATH.");
|
|
512
|
-
}
|
|
513
|
-
// Петля перезаписи: один файл безуспешно переписывается
|
|
514
|
-
if (this.hasExcessiveRewrites()) {
|
|
515
|
-
const file = this.getExcessiveRewriteFile();
|
|
516
|
-
hints.push(`File ${file} has been rewritten ${this.getFileRewriteCount(file)} times without success. Stop rewriting and try a fundamentally different approach.`);
|
|
517
|
-
}
|
|
518
|
-
// Повторные идентичные bash-прогоны с пустым выводом — вероятно, потерянный
|
|
519
|
-
// entry point (программа не вызывает main).
|
|
520
|
-
// PowerShell-командлеты внутри cmd.exe (шелл-несоответствие Windows).
|
|
521
|
-
if (/Write-Host|Get-Content|Select-String|Out-File|Set-Content/i.test(error)) {
|
|
522
|
-
hints.push("That looks like a PowerShell cmdlet — the shell here is cmd.exe. Use echo/type for output or the read_file/write_file tools instead.");
|
|
523
|
-
}
|
|
524
|
-
// Модель выдумала несуществующий Bun API.
|
|
525
|
-
if (/Bun\.\w+ is not a function|Bun\.\w+ is not defined|Bun\.\w+ is not a constructor/i.test(error)) {
|
|
526
|
-
hints.push("That Bun API does not exist. Verify the API name in the Bun docs — common ones are Bun.file, Bun.write, Bun.spawn, Bun.serve. For file checks use fs.existsSync from node:fs.");
|
|
527
|
-
}
|
|
528
|
-
// Модель продолжает писать файл, в выводе которого type/syntax ошибка —
|
|
529
|
-
// читай реальную ошибку и чини её, не переписывай вслепую.
|
|
530
|
-
if (/error TS\d+|typecheck failed|syntax check failed/i.test(error)) {
|
|
531
|
-
hints.push("The file still has a type/syntax error (see the error line in the output). Read the file with read_file around the reported line, fix the actual error, then re-run — rewriting the whole file blindly usually makes it worse.");
|
|
532
|
-
}
|
|
533
|
-
// Bash-heredoc в cmd.exe.
|
|
534
|
-
if (/unexpected.*<<|Непредвиденное появление/i.test(error)) {
|
|
535
|
-
hints.push("Heredoc (<< EOF) is a bash feature — this shell is cmd.exe and does not support it. Write the file with write_file instead.");
|
|
536
|
-
}
|
|
537
|
-
return hints;
|
|
538
|
-
}
|
|
539
|
-
/** Альтернативный тул при рантайм-краше (ts-node→tsx, jest→vitest, …). Null, если нечего предложить. */
|
|
540
|
-
getToolAlternative() {
|
|
541
|
-
const tool = this.lastFailedTool;
|
|
542
|
-
const error = this.lastErrorOutput;
|
|
543
|
-
if (!tool)
|
|
544
|
-
return null;
|
|
545
|
-
// Упавший тул с рантайм-крашем — предлагаем альтернативы
|
|
546
|
-
if (/Cannot read properties of undefined|is not a function|TypeError|ReferenceError/i.test(error)) {
|
|
547
|
-
// Карта упавших тулов на частые альтернативы
|
|
548
|
-
const alternatives = {
|
|
549
|
-
"ts-node": "tsx",
|
|
550
|
-
jest: "vitest",
|
|
551
|
-
mocha: "vitest",
|
|
552
|
-
webpack: "vite",
|
|
553
|
-
rollup: "vite",
|
|
554
|
-
parcel: "vite",
|
|
555
|
-
babel: "tsc",
|
|
556
|
-
eslint: "biome",
|
|
557
|
-
prettier: "biome",
|
|
558
|
-
};
|
|
559
|
-
for (const [failed, alt] of Object.entries(alternatives)) {
|
|
560
|
-
if (tool.includes(failed) || error.includes(failed)) {
|
|
561
|
-
return alt;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
return null;
|
|
566
|
-
}
|
|
567
|
-
/** Отмечает перезапись файла (счётчик для детекции петли). */
|
|
568
|
-
recordFileRewrite(filePath) {
|
|
569
|
-
const count = (this.fileRewriteCount.get(filePath) || 0) + 1;
|
|
570
|
-
this.fileRewriteCount.set(filePath, count);
|
|
571
|
-
}
|
|
572
|
-
/** Число перезаписей файла. */
|
|
573
|
-
getFileRewriteCount(filePath) {
|
|
574
|
-
return this.fileRewriteCount.get(filePath) || 0;
|
|
575
|
-
}
|
|
576
|
-
/** True, если какой-то файл переписывается ≥ порога. */
|
|
577
|
-
hasExcessiveRewrites() {
|
|
578
|
-
return Array.from(this.fileRewriteCount.values()).some((c) => c >= this.fileRewriteThreshold);
|
|
579
|
-
}
|
|
580
|
-
/** Файл с избыточными перезаписями, или null. */
|
|
581
|
-
getExcessiveRewriteFile() {
|
|
582
|
-
for (const [file, count] of this.fileRewriteCount) {
|
|
583
|
-
if (count >= this.fileRewriteThreshold)
|
|
584
|
-
return file;
|
|
585
|
-
}
|
|
586
|
-
return null;
|
|
587
|
-
}
|
|
588
|
-
/** Сообщение о повторяющемся туле (+хинт про process_wait для process_log). */
|
|
589
|
-
getRepetitiveToolMessage() {
|
|
590
|
-
if (!this.hasRepetitiveToolCalls())
|
|
591
|
-
return "";
|
|
592
|
-
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
593
|
-
const base = t("exec.repetitive_tool", {
|
|
594
|
-
tool: last.name,
|
|
595
|
-
count: this.repetitionThreshold,
|
|
596
|
-
});
|
|
597
|
-
// Опрос фонового процесса по одному process_log за раз жжёт LLM-вызов на
|
|
598
|
-
// опрос — указываем на одиночное блокирующее ожидание.
|
|
599
|
-
if (last.name === "process_log") {
|
|
600
|
-
return `${base} ${t("exec.repetitive_process_log_hint")}`;
|
|
601
|
-
}
|
|
602
|
-
return base;
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Стабильная идентичность варнинга для анти-спам дедупа: тот же ключ каждую
|
|
606
|
-
* итерацию — спам, НОВЫЙ ключ (или длинная пауза) заслуживает re-warn.
|
|
607
|
-
*/
|
|
608
|
-
getWarningKey() {
|
|
609
|
-
if (this.isStuck())
|
|
610
|
-
return "stuck";
|
|
611
|
-
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
612
|
-
if (errorTool)
|
|
613
|
-
return `tool-errors:${errorTool[0]}`;
|
|
614
|
-
if (this.hasBashFlailing())
|
|
615
|
-
return "bash-flailing";
|
|
616
|
-
if (this.hasConsecutiveFailures())
|
|
617
|
-
return "consecutive";
|
|
618
|
-
if (this.hasRepetitiveToolCalls())
|
|
619
|
-
return "repetitive";
|
|
620
|
-
if (this.hasReadOnlyLoop())
|
|
621
|
-
return "read-only";
|
|
622
|
-
return "";
|
|
623
|
-
}
|
|
624
|
-
/** Причина застревания для лога (пусто если здоров). */
|
|
625
|
-
getStuckReason() {
|
|
626
|
-
if (this.isStuck()) {
|
|
627
|
-
return t("exec.stuck", {
|
|
628
|
-
stepId: String(this.currentStepId ?? "?"),
|
|
629
|
-
description: this.currentStepDescription,
|
|
630
|
-
iterations: this.iterationsOnCurrentStep,
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
634
|
-
if (errorTool) {
|
|
635
|
-
return t("exec.tool_errors", { tool: errorTool[0], count: errorTool[1] });
|
|
636
|
-
}
|
|
637
|
-
if (this.hasBashFlailing()) {
|
|
638
|
-
const fails = this.bashRecent.filter((ok) => !ok).length;
|
|
639
|
-
return t("exec.bash_flailing", {
|
|
640
|
-
fails: String(fails),
|
|
641
|
-
window: String(this.bashRecent.length),
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
if (this.hasConsecutiveFailures()) {
|
|
645
|
-
return t("exec.consecutive_failures", { count: String(this.consecutiveFailures) });
|
|
646
|
-
}
|
|
647
|
-
// Повторные идентичные вызовы — петля, даже если ничего не фейлится (напр.
|
|
648
|
-
// цикл опроса process_log): без этой ветки оставались невидимы —
|
|
649
|
-
// getWarningKey репортил "repetitive", но причина не логгировалась.
|
|
650
|
-
if (this.hasRepetitiveToolCalls()) {
|
|
651
|
-
return this.getRepetitiveToolMessage();
|
|
652
|
-
}
|
|
653
|
-
if (this.hasReadOnlyLoop()) {
|
|
654
|
-
return t("exec.read_only_loop", {
|
|
655
|
-
count: String(this.readOnlyStreak),
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
return "";
|
|
659
|
-
}
|
|
660
|
-
/** Recovery-сообщение по первичной причине (пусто если здоров). */
|
|
661
|
-
getRecoveryMessage() {
|
|
662
|
-
const reason = this.getPrimaryReason();
|
|
663
|
-
if (reason === "stuck") {
|
|
664
|
-
return t("exec.stuck_recovery", {
|
|
665
|
-
iterations: this.iterationsOnCurrentStep,
|
|
666
|
-
stepId: String(this.currentStepId ?? "?"),
|
|
667
|
-
description: this.currentStepDescription,
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
// Per-tool recovery (конкретнее — напр. "bash упал 3 раза")
|
|
671
|
-
if (reason === "tool-errors") {
|
|
672
|
-
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
673
|
-
return t("exec.tool_errors_recovery", {
|
|
674
|
-
tool: errorTool[0],
|
|
675
|
-
count: errorTool[1],
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
// Подряд фейлы разных тулов (общее — напр. "5 consecutive failures")
|
|
679
|
-
if (reason === "consecutive") {
|
|
680
|
-
return t("exec.consecutive_failures_recovery", {
|
|
681
|
-
count: this.consecutiveFailures,
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
if (reason === "bash-flailing") {
|
|
685
|
-
const fails = this.bashRecent.filter((ok) => !ok).length;
|
|
686
|
-
return t("exec.bash_flailing_recovery", {
|
|
687
|
-
fails: String(fails),
|
|
688
|
-
window: String(this.bashRecent.length),
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
|
-
if (reason === "repetitive") {
|
|
692
|
-
return this.getRepetitiveToolMessage();
|
|
693
|
-
}
|
|
694
|
-
if (reason === "read-only") {
|
|
695
|
-
return t("exec.read_only_loop_recovery", {
|
|
696
|
-
count: String(this.readOnlyStreak),
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
return "";
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Классифицирует текущее состояние фейла в стабильный {@link FailureType}
|
|
703
|
-
* для диагностики/логов. Null если здоров. `iteration` даёт вызывающий
|
|
704
|
-
* (детектор глобальный счётчик цикла не ведёт).
|
|
705
|
-
*/
|
|
706
|
-
classifyFailure(iteration = 0) {
|
|
707
|
-
const primary = this.getPrimaryReason();
|
|
708
|
-
if (!primary)
|
|
709
|
-
return null;
|
|
710
|
-
switch (primary) {
|
|
711
|
-
case "stuck":
|
|
712
|
-
return { type: "timeout", iteration };
|
|
713
|
-
case "tool-errors": {
|
|
714
|
-
const entry = Array.from(this.toolErrors.entries()).find(([, c]) => c >= this.errorThreshold);
|
|
715
|
-
return { type: "error_loop", tool: entry?.[0], count: entry?.[1], iteration };
|
|
716
|
-
}
|
|
717
|
-
case "consecutive":
|
|
718
|
-
return { type: "error_loop", count: this.consecutiveFailures, iteration };
|
|
719
|
-
case "repetitive": {
|
|
720
|
-
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
721
|
-
return { type: "repeat", tool: last?.name, count: this.repetitionThreshold, iteration };
|
|
722
|
-
}
|
|
723
|
-
case "bash-flailing": {
|
|
724
|
-
const fails = this.bashRecent.filter((ok) => !ok).length;
|
|
725
|
-
return { type: "bash_flailing", count: fails, iteration };
|
|
726
|
-
}
|
|
727
|
-
case "read-only":
|
|
728
|
-
return { type: "read_only", count: this.readOnlyStreak, iteration };
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
/** Компактный маркер `[STUCK: type=..., tool=..., count=..., iteration=N]`. */
|
|
732
|
-
formatFailureDiagnostic(c) {
|
|
733
|
-
const parts = [`type=${c.type}`];
|
|
734
|
-
if (c.tool)
|
|
735
|
-
parts.push(`tool=${c.tool}`);
|
|
736
|
-
if (typeof c.count === "number")
|
|
737
|
-
parts.push(`count=${c.count}`);
|
|
738
|
-
if (c.iteration > 0)
|
|
739
|
-
parts.push(`iteration=${c.iteration}`);
|
|
740
|
-
return `[STUCK: ${parts.join(", ")}]`;
|
|
741
|
-
}
|
|
742
|
-
/** Диагностический маркер текущего состояния, или "" если здоров. */
|
|
743
|
-
getStuckDiagnostic(iteration = 0) {
|
|
744
|
-
const c = this.classifyFailure(iteration);
|
|
745
|
-
return c ? this.formatFailureDiagnostic(c) : "";
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* Единый источник правды приоритета recovery-причины. Общий для
|
|
749
|
-
* getRecoveryMessage() и getHints(), чтобы не расходились: bullet состояния,
|
|
750
|
-
* которое getRecoveryMessage уже репортит, хинтом НЕ дублируется
|
|
751
|
-
* (приоритет: stuck > tool-errors > consecutive > repetitive > read-only).
|
|
752
|
-
*/
|
|
753
|
-
getPrimaryReason() {
|
|
754
|
-
if (this.isStuck())
|
|
755
|
-
return "stuck";
|
|
756
|
-
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
757
|
-
if (errorTool)
|
|
758
|
-
return "tool-errors";
|
|
759
|
-
if (this.hasBashFlailing())
|
|
760
|
-
return "bash-flailing";
|
|
761
|
-
if (this.hasConsecutiveFailures())
|
|
762
|
-
return "consecutive";
|
|
763
|
-
if (this.hasRepetitiveToolCalls())
|
|
764
|
-
return "repetitive";
|
|
765
|
-
if (this.hasReadOnlyLoop())
|
|
766
|
-
return "read-only";
|
|
767
|
-
return null;
|
|
768
|
-
}
|
|
769
|
-
/** Полный сброс всего состояния детектора. */
|
|
770
|
-
reset() {
|
|
771
|
-
this.currentStepId = null;
|
|
772
|
-
this.iterationsOnCurrentStep = 0;
|
|
773
|
-
this.toolErrors.clear();
|
|
774
|
-
this.consecutiveFailures = 0;
|
|
775
|
-
this.lastFailedTool = "";
|
|
776
|
-
this.lastErrorOutput = "";
|
|
777
|
-
this.recentToolCalls = [];
|
|
778
|
-
this.fileRewriteCount.clear();
|
|
779
|
-
this.escalationCount = 0;
|
|
780
|
-
this.lastBashCommand = "";
|
|
781
|
-
this.lastBashOutput = "";
|
|
782
|
-
this.emptyBashRunCount = 0;
|
|
783
|
-
this.bashRecent = [];
|
|
784
|
-
this.readOnlyStreak = 0;
|
|
785
|
-
this.errorSignatureCounts.clear();
|
|
786
|
-
this.searchedErrorSignatures.clear();
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* Сбрасывает только step-прогресс, храня историю вызовов и read-only стрик.
|
|
790
|
-
* Нужно, когда план готов, но цикл продолжается (напр. audit-gate реджектнул
|
|
791
|
-
* готовую задачу): step-stuck варнинги глушатся, а петля чтения-без-записи
|
|
792
|
-
* всё равно детектится.
|
|
793
|
-
*/
|
|
794
|
-
resetStepProgress() {
|
|
795
|
-
this.currentStepId = null;
|
|
796
|
-
this.iterationsOnCurrentStep = 0;
|
|
797
|
-
this.toolErrors.clear();
|
|
798
|
-
this.consecutiveFailures = 0;
|
|
799
|
-
this.lastFailedTool = "";
|
|
800
|
-
this.lastErrorOutput = "";
|
|
801
|
-
this.fileRewriteCount.clear();
|
|
802
|
-
this.escalationCount = 0;
|
|
803
|
-
this.lastBashCommand = "";
|
|
804
|
-
this.lastBashOutput = "";
|
|
805
|
-
this.emptyBashRunCount = 0;
|
|
806
|
-
this.bashRecent = [];
|
|
807
|
-
this.errorSignatureCounts.clear();
|
|
808
|
-
this.searchedErrorSignatures.clear();
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Сбрасывает всё per-step состояние при переходе на новый шаг. Не даёт
|
|
812
|
-
* ошибкам и повторам одного шага протекать в следующий (ложные
|
|
813
|
-
* stuck/recovery на первой итерации нового шага).
|
|
814
|
-
*/
|
|
815
|
-
resetStepState(stepId) {
|
|
816
|
-
this.currentStepId = stepId;
|
|
817
|
-
this.iterationsOnCurrentStep = 1;
|
|
818
|
-
this.toolErrors.clear();
|
|
819
|
-
this.consecutiveFailures = 0;
|
|
820
|
-
this.lastFailedTool = "";
|
|
821
|
-
this.lastErrorOutput = "";
|
|
822
|
-
this.recentToolCalls = [];
|
|
823
|
-
this.fileRewriteCount.clear();
|
|
824
|
-
this.lastBashCommand = "";
|
|
825
|
-
this.lastBashOutput = "";
|
|
826
|
-
this.emptyBashRunCount = 0;
|
|
827
|
-
this.bashRecent = [];
|
|
828
|
-
this.readOnlyStreak = 0;
|
|
829
|
-
this.errorSignatureCounts.clear();
|
|
830
|
-
this.searchedErrorSignatures.clear();
|
|
831
|
-
// escalationCount храним — иначе агент "геймит" детекцию, маркируя шаг
|
|
832
|
-
// done и переходя к следующему
|
|
833
|
-
}
|
|
834
|
-
}
|