pi-lens 4.2.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1562 -0
- package/README.md +157 -24
- package/config/dependency-cruiser-eager-allowlist.json +6 -2
- package/dist/chunk-22AVIGXS.js +560 -0
- package/dist/chunk-27BBHSZC.js +106 -0
- package/dist/chunk-2BEMIWEW.js +51 -0
- package/dist/chunk-2EECBNC5.js +89 -0
- package/dist/chunk-34U7PLGE.js +23 -0
- package/dist/chunk-3I5QFT3N.js +181 -0
- package/dist/chunk-3MED75HT.js +37 -0
- package/dist/chunk-47D2BO4S.js +87 -0
- package/dist/chunk-4RERDEXR.js +348 -0
- package/dist/chunk-4W2PLC4J.js +27 -0
- package/dist/chunk-52H5B5TC.js +202 -0
- package/dist/chunk-54B42YRX.js +27 -0
- package/dist/chunk-5RXBNSW2.js +266 -0
- package/dist/chunk-5THXD6X5.js +201 -0
- package/dist/chunk-5W3R6LFT.js +24 -0
- package/dist/chunk-5ZEJHV35.js +341 -0
- package/dist/chunk-6P62QVEA.js +118 -0
- package/dist/chunk-6QIUX4EP.js +459 -0
- package/dist/chunk-6VCD4VKZ.js +78 -0
- package/dist/chunk-7F72PXY2.js +326 -0
- package/dist/chunk-7FBF7NPR.js +998 -0
- package/dist/chunk-7U2IDNXN.js +37 -0
- package/dist/chunk-ABBOA7UD.js +157 -0
- package/dist/chunk-AHDW3GPE.js +50 -0
- package/dist/chunk-AJ3QXTRQ.js +756 -0
- package/dist/chunk-AJVLWNFN.js +1610 -0
- package/dist/chunk-AJXODZY6.js +112 -0
- package/dist/chunk-ARFCXG7S.js +84 -0
- package/dist/chunk-AS44CFQ4.js +708 -0
- package/dist/chunk-AWE5TMPO.js +38 -0
- package/dist/chunk-B3I2UUT4.js +4692 -0
- package/dist/chunk-BRJKOBZP.js +331 -0
- package/dist/chunk-BW5KFIQC.js +59 -0
- package/dist/chunk-C2AUYXQH.js +149 -0
- package/dist/chunk-CGGAIP7J.js +29 -0
- package/dist/chunk-D6QS3U6M.js +30 -0
- package/dist/chunk-DFVMIKMQ.js +349 -0
- package/dist/chunk-DIULIWKU.js +169 -0
- package/dist/chunk-DQ2NZ7C4.js +2317 -0
- package/dist/chunk-E2WFBRHM.js +213 -0
- package/dist/chunk-E4MAEDPM.js +50 -0
- package/dist/chunk-EOKRQ3SA.js +30 -0
- package/dist/chunk-F3AU2TMM.js +2626 -0
- package/dist/chunk-F3P7VQ7H.js +170 -0
- package/dist/chunk-FC4RP4I2.js +12 -0
- package/dist/chunk-FJNKANOS.js +32 -0
- package/dist/chunk-GUOGVTCQ.js +146 -0
- package/dist/chunk-GVXA6CCE.js +18 -0
- package/dist/chunk-HHYAELLJ.js +2334 -0
- package/dist/chunk-HMFXILPV.js +83 -0
- package/dist/chunk-HVUZXSWK.js +421 -0
- package/dist/chunk-HWBZIGMJ.js +62 -0
- package/dist/chunk-I3R7GKDR.js +17 -0
- package/dist/chunk-I7SPGOWI.js +55 -0
- package/dist/chunk-IIXOAGHR.js +38 -0
- package/dist/chunk-IVP5KAB7.js +2250 -0
- package/dist/chunk-IXEBJSUE.js +22 -0
- package/dist/chunk-JOLITGCC.js +27 -0
- package/dist/chunk-K6VYLB2U.js +145 -0
- package/dist/chunk-LJDODBBZ.js +78 -0
- package/dist/chunk-LNAKEO63.js +78 -0
- package/dist/chunk-LSMJ6KBE.js +4139 -0
- package/dist/chunk-LW6XKDZQ.js +5565 -0
- package/dist/chunk-N3YQJI6O.js +899 -0
- package/dist/chunk-NABLC7W3.js +148 -0
- package/dist/chunk-NKOUFPQF.js +659 -0
- package/dist/chunk-NL5GLMFI.js +211 -0
- package/dist/chunk-NMABWBZN.js +327 -0
- package/dist/chunk-NTHH2J74.js +27 -0
- package/dist/chunk-NTUJEGDZ.js +59 -0
- package/dist/chunk-NXL4FFQQ.js +47 -0
- package/dist/chunk-NXPS2L6U.js +466 -0
- package/dist/chunk-O5WILGMI.js +42 -0
- package/dist/chunk-O6TQT6RI.js +48 -0
- package/dist/chunk-OAXNDZJ7.js +323 -0
- package/dist/chunk-ODEEOXPH.js +44 -0
- package/dist/chunk-OQGFCZJ4.js +4533 -0
- package/dist/chunk-ORTMY4Q4.js +282 -0
- package/dist/chunk-OS32RNR2.js +442 -0
- package/dist/chunk-PAK6IDME.js +189 -0
- package/dist/chunk-PWFHBTSF.js +406 -0
- package/dist/chunk-Q5U6FMDI.js +2352 -0
- package/dist/chunk-QOZXJ4NA.js +3364 -0
- package/dist/chunk-QYF5S6P6.js +45 -0
- package/dist/chunk-RIISMHKN.js +36 -0
- package/dist/chunk-RPD4ZSB7.js +25 -0
- package/dist/chunk-RPD5K5NC.js +79 -0
- package/dist/chunk-RUOG5D2C.js +262 -0
- package/dist/chunk-T4ERT73Y.js +599 -0
- package/dist/chunk-T6D4Q4AQ.js +54 -0
- package/dist/chunk-TGPXLY2J.js +391 -0
- package/dist/chunk-TLGFBM3F.js +12116 -0
- package/dist/chunk-TO6WL3GM.js +88 -0
- package/dist/chunk-TT22N74J.js +517 -0
- package/dist/chunk-TVN5N6GY.js +21216 -0
- package/dist/chunk-U3VGR4VU.js +308 -0
- package/dist/chunk-UK3CMEAL.js +1250 -0
- package/dist/chunk-UOPVJQ4N.js +103 -0
- package/dist/chunk-UXVNL3D7.js +23 -0
- package/dist/chunk-UY752QSN.js +45 -0
- package/dist/chunk-V5PXFSZK.js +188 -0
- package/dist/chunk-V7R425XG.js +398 -0
- package/dist/chunk-VCSRSYLF.js +81 -0
- package/dist/chunk-VD4JQCQC.js +162 -0
- package/dist/chunk-VDBPVIKD.js +62 -0
- package/dist/chunk-VHQLPIRJ.js +114 -0
- package/dist/chunk-VN2AXLEX.js +5414 -0
- package/dist/chunk-VNMT7C64.js +478 -0
- package/dist/chunk-WONERJTP.js +309 -0
- package/dist/chunk-XECLWQZN.js +85 -0
- package/dist/chunk-XIMYZ3JA.js +27 -0
- package/dist/chunk-XK4CIILI.js +144 -0
- package/dist/chunk-Y4T27COV.js +562 -0
- package/dist/chunk-YGVYKNSN.js +1860 -0
- package/dist/chunk-YLF3PPT5.js +308 -0
- package/dist/chunk-YSNFMJKN.js +14 -0
- package/dist/chunk-Z3CCVDUB.js +76 -0
- package/dist/chunk-Z54ZSOBZ.js +61 -0
- package/dist/chunk-ZAWW3AF6.js +97 -0
- package/dist/chunk-ZMOL6RQC.js +167 -0
- package/dist/index.js +19039 -8956
- package/dist/mcp/analyze-cli.js +186 -228
- package/dist/mcp/cli.js +137 -98
- package/dist/mcp/server.js +22592 -1564
- package/dist/mcp/worker.js +97 -38
- package/dist/probes/installer.js +135 -0
- package/dist/workers/project-snapshot-persist-worker.js +22 -0
- package/dist/workers/review-graph-persist-worker.js +15 -0
- package/docs/adr/0001-stale-advisory-live-arm.md +29 -0
- package/docs/adr/0002-workspace-hash-rendezvous.md +30 -0
- package/docs/adr/0003-git-guard-latch-writer.md +203 -0
- package/docs/adr/0004-disposition-policy-seam.md +38 -0
- package/docs/adr/0005-tool-availability-enforcement-seam.md +29 -0
- package/docs/adr/0006-derived-state-benchmark-first.md +31 -0
- package/docs/adr/0007-end-to-end-witness-per-seam-slice.md +38 -0
- package/docs/adr/0008-turn-end-lane-interface.md +165 -0
- package/docs/adr/0009-reported-path-attribution.md +114 -0
- package/docs/adr/README.md +26 -0
- package/docs/agent-guide.md +13 -4
- package/docs/agent-tools.md +60 -1
- package/docs/ast-grep_rules_catalog.md +6 -4
- package/docs/configuration.md +75 -2
- package/docs/engineering-principles.md +202 -0
- package/docs/environment-variables.md +113 -6
- package/docs/features.md +10 -6
- package/docs/globalconfig.md +7 -1
- package/docs/io-bridge-v2-migration.md +94 -0
- package/docs/language-coverage.md +17 -0
- package/docs/lsp-capability-matrix.md +131 -32
- package/docs/lsp-idle-eviction.md +124 -0
- package/docs/mcp.md +21 -5
- package/docs/merge-queue-rollout.md +77 -0
- package/docs/pi-lens-fixer.md +161 -39
- package/docs/pi-lens-investigator.md +77 -20
- package/docs/pi-lens-merge-policy.md +694 -0
- package/docs/pi-lens-monitor.md +73 -7
- package/docs/pi-lens-retro.md +6 -4
- package/docs/pi-lens-reviewer.md +190 -34
- package/docs/pi-lens-subagent.md +247 -37
- package/docs/pi-lens-warden.md +18 -7
- package/docs/public-api-stability.md +56 -0
- package/docs/release-qa-baseline.md +86 -17
- package/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
- package/docs/servercapabilities.md +66 -52
- package/docs/settings.md +13 -3
- package/docs/subagent-compat.md +1 -1
- package/docs/usage.md +5 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-bash.wasm.json +3 -3
- package/package.json +23 -9
- package/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
- package/rules/ast-grep-rules/rule-tests/no-assert-tuple-test.yml +13 -0
- package/rules/ast-grep-rules/rule-tests/no-boolean-in-except-test.yml +26 -0
- package/rules/ast-grep-rules/rule-tests/no-http-headers-bracket-access-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/unchecked-numeric-parse-python-test.yml +18 -0
- package/rules/ast-grep-rules/rule-tests/unchecked-throwing-call-python-test.yml +4 -3
- package/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
- package/rules/ast-grep-rules/rules/go-no-fmt-println.yml +2 -2
- package/rules/ast-grep-rules/rules/no-assert-tuple.yml +23 -13
- package/rules/ast-grep-rules/rules/no-boolean-in-except.yml +7 -3
- package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +2 -2
- package/rules/ast-grep-rules/rules/no-console-except-error.yml +2 -2
- package/rules/ast-grep-rules/rules/no-http-headers-bracket-access.yml +12 -7
- package/rules/ast-grep-rules/rules/unchecked-numeric-parse-python.yml +44 -0
- package/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +5 -3
- package/rules/tree-sitter-queries/typescript/console-statement.yml +2 -2
- package/scripts/analyze-pi-lens-logs.mjs +1275 -62
- package/scripts/download-grammars.js +5 -0
- package/scripts/grammars.lock.json +6 -1
- package/scripts/install-selftest.mjs +38 -35
- package/scripts/lib/host-provided-deps.mjs +13 -4
- package/scripts/lib/web-tree-sitter-dir.mjs +141 -0
- package/dist/clients/actionable-warnings-logger.js +0 -35
- package/dist/clients/actionable-warnings.js +0 -1594
- package/dist/clients/advisory-provenance.js +0 -457
- package/dist/clients/agent-behavior-client.js +0 -147
- package/dist/clients/agent-nudge.js +0 -325
- package/dist/clients/analysed-root.js +0 -1
- package/dist/clients/ast-grep-client.js +0 -731
- package/dist/clients/ast-grep-rule-manager.js +0 -146
- package/dist/clients/ast-grep-tool-logger.js +0 -118
- package/dist/clients/ast-grep-types.js +0 -10
- package/dist/clients/ast-grep-yaml-synth.js +0 -154
- package/dist/clients/atomic-write-staging.js +0 -75
- package/dist/clients/atomic-write.js +0 -189
- package/dist/clients/availability-probe-flight.js +0 -12
- package/dist/clients/bash-file-access.js +0 -1056
- package/dist/clients/biome-client.js +0 -357
- package/dist/clients/blocker-freshness.js +0 -837
- package/dist/clients/blocker-past-eof.js +0 -129
- package/dist/clients/bootstrap.js +0 -569
- package/dist/clients/bounded-cache.js +0 -190
- package/dist/clients/bounded-pid-file-lock.js +0 -202
- package/dist/clients/bounded-telemetry.js +0 -295
- package/dist/clients/build-identity.js +0 -95
- package/dist/clients/bundled-resource-health.js +0 -113
- package/dist/clients/bus-events-logger.js +0 -105
- package/dist/clients/bus-publish.js +0 -171
- package/dist/clients/cache/rule-cache.js +0 -190
- package/dist/clients/cache-manager.js +0 -465
- package/dist/clients/cache-observability.js +0 -1313
- package/dist/clients/call-graph.js +0 -852
- package/dist/clients/cargo-manifest.js +0 -422
- package/dist/clients/cascade-budget.js +0 -196
- package/dist/clients/cascade-format.js +0 -315
- package/dist/clients/cascade-logger.js +0 -31
- package/dist/clients/cascade-types.js +0 -9
- package/dist/clients/child-unref.js +0 -115
- package/dist/clients/code-quality-warnings.js +0 -286
- package/dist/clients/codebase-model.js +0 -173
- package/dist/clients/collateral-test-role.js +0 -40
- package/dist/clients/complexity-client.js +0 -462
- package/dist/clients/config-core/deny.js +0 -221
- package/dist/clients/config-core/index.js +0 -50
- package/dist/clients/config-core/merge.js +0 -357
- package/dist/clients/config-core/normalize.js +0 -340
- package/dist/clients/config-core/process-spec.js +0 -248
- package/dist/clients/config-core/provenance.js +0 -164
- package/dist/clients/config-core/records.js +0 -218
- package/dist/clients/config-core/resolve.js +0 -125
- package/dist/clients/config-core/safe-object.js +0 -78
- package/dist/clients/config-core/schema.js +0 -165
- package/dist/clients/config-diagnostic-codes.js +0 -283
- package/dist/clients/config-locations.js +0 -160
- package/dist/clients/config-resolve.js +0 -792
- package/dist/clients/config-schema.js +0 -251
- package/dist/clients/config-warn.js +0 -407
- package/dist/clients/console-guard-install.js +0 -26
- package/dist/clients/cooperative-budget.js +0 -42
- package/dist/clients/dead-code-client.js +0 -510
- package/dist/clients/dead-code-logger.js +0 -30
- package/dist/clients/deadline-utils.js +0 -280
- package/dist/clients/debug-handles.js +0 -211
- package/dist/clients/debug-heap.js +0 -174
- package/dist/clients/deferred-lsp-work.js +0 -106
- package/dist/clients/degradation-ledger.js +0 -396
- package/dist/clients/demoted-finding-render.js +0 -169
- package/dist/clients/dependency-checker.js +0 -862
- package/dist/clients/deps/ast-grep-napi.js +0 -22
- package/dist/clients/deps/js-yaml.js +0 -9
- package/dist/clients/deps/minimatch.js +0 -4
- package/dist/clients/deps/pi-tui.js +0 -14
- package/dist/clients/deps/typebox.js +0 -14
- package/dist/clients/deps/vscode-jsonrpc.js +0 -1
- package/dist/clients/deps/web-tree-sitter.js +0 -24
- package/dist/clients/diagnostic-dispositions.js +0 -594
- package/dist/clients/diagnostic-line-freshness.js +0 -305
- package/dist/clients/diagnostic-logger.js +0 -73
- package/dist/clients/diagnostic-tracker.js +0 -105
- package/dist/clients/diagnostics-publish.js +0 -230
- package/dist/clients/dispatch/ast-grep-catalog.js +0 -65
- package/dist/clients/dispatch/auxiliary-lsp.js +0 -343
- package/dist/clients/dispatch/collect-later-tier.js +0 -35
- package/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
- package/dist/clients/dispatch/dispatcher.js +0 -1269
- package/dist/clients/dispatch/fact-provider-types.js +0 -1
- package/dist/clients/dispatch/fact-rule-runner.js +0 -17
- package/dist/clients/dispatch/fact-runner.js +0 -30
- package/dist/clients/dispatch/fact-scheduler.js +0 -68
- package/dist/clients/dispatch/fact-store.js +0 -377
- package/dist/clients/dispatch/facts/comment-facts.js +0 -22
- package/dist/clients/dispatch/facts/file-content.js +0 -19
- package/dist/clients/dispatch/facts/function-facts.js +0 -348
- package/dist/clients/dispatch/facts/import-facts.js +0 -204
- package/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
- package/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
- package/dist/clients/dispatch/finding-policy.js +0 -222
- package/dist/clients/dispatch/indent-detect.js +0 -411
- package/dist/clients/dispatch/inline-suppressions.js +0 -70
- package/dist/clients/dispatch/integration.js +0 -2377
- package/dist/clients/dispatch/lazy.js +0 -11
- package/dist/clients/dispatch/pending-runner-findings.js +0 -87
- package/dist/clients/dispatch/plan.js +0 -278
- package/dist/clients/dispatch/priorities.js +0 -20
- package/dist/clients/dispatch/rule-id-normalize.js +0 -50
- package/dist/clients/dispatch/rule-ignores.js +0 -62
- package/dist/clients/dispatch/rule-policy.js +0 -135
- package/dist/clients/dispatch/rules/async-noise.js +0 -40
- package/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
- package/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
- package/dist/clients/dispatch/rules/error-obscuring.js +0 -32
- package/dist/clients/dispatch/rules/error-swallowing.js +0 -31
- package/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
- package/dist/clients/dispatch/rules/high-complexity.js +0 -72
- package/dist/clients/dispatch/rules/high-fan-out.js +0 -74
- package/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
- package/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
- package/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
- package/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
- package/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
- package/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
- package/dist/clients/dispatch/runner-context.js +0 -52
- package/dist/clients/dispatch/runners/actionlint.js +0 -113
- package/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
- package/dist/clients/dispatch/runners/biome-check.js +0 -283
- package/dist/clients/dispatch/runners/cpp-check.js +0 -236
- package/dist/clients/dispatch/runners/credo.js +0 -80
- package/dist/clients/dispatch/runners/cue-vet.js +0 -398
- package/dist/clients/dispatch/runners/dart-analyze.js +0 -202
- package/dist/clients/dispatch/runners/detekt.js +0 -169
- package/dist/clients/dispatch/runners/dotnet-build.js +0 -159
- package/dist/clients/dispatch/runners/elixir-check.js +0 -210
- package/dist/clients/dispatch/runners/eslint.js +0 -154
- package/dist/clients/dispatch/runners/fact-rules.js +0 -29
- package/dist/clients/dispatch/runners/fish-indent.js +0 -69
- package/dist/clients/dispatch/runners/gleam-check.js +0 -87
- package/dist/clients/dispatch/runners/go-vet.js +0 -90
- package/dist/clients/dispatch/runners/golangci-lint.js +0 -120
- package/dist/clients/dispatch/runners/hadolint.js +0 -79
- package/dist/clients/dispatch/runners/helm-lint.js +0 -171
- package/dist/clients/dispatch/runners/helm-render.js +0 -940
- package/dist/clients/dispatch/runners/htmlhint.js +0 -108
- package/dist/clients/dispatch/runners/index.js +0 -108
- package/dist/clients/dispatch/runners/javac.js +0 -89
- package/dist/clients/dispatch/runners/ktlint.js +0 -136
- package/dist/clients/dispatch/runners/lsp.js +0 -378
- package/dist/clients/dispatch/runners/markdownlint.js +0 -160
- package/dist/clients/dispatch/runners/mypy.js +0 -101
- package/dist/clients/dispatch/runners/oxlint.js +0 -398
- package/dist/clients/dispatch/runners/php-lint.js +0 -62
- package/dist/clients/dispatch/runners/phpstan.js +0 -120
- package/dist/clients/dispatch/runners/prisma-validate.js +0 -62
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
- package/dist/clients/dispatch/runners/pyright.js +0 -169
- package/dist/clients/dispatch/runners/rubocop.js +0 -90
- package/dist/clients/dispatch/runners/ruff.js +0 -89
- package/dist/clients/dispatch/runners/rust-clippy.js +0 -251
- package/dist/clients/dispatch/runners/shellcheck.js +0 -169
- package/dist/clients/dispatch/runners/shfmt.js +0 -102
- package/dist/clients/dispatch/runners/spellcheck.js +0 -121
- package/dist/clients/dispatch/runners/spotbugs.js +0 -259
- package/dist/clients/dispatch/runners/sqlfluff.js +0 -174
- package/dist/clients/dispatch/runners/stylelint.js +0 -158
- package/dist/clients/dispatch/runners/swiftlint.js +0 -154
- package/dist/clients/dispatch/runners/taplo.js +0 -168
- package/dist/clients/dispatch/runners/terragrunt.js +0 -168
- package/dist/clients/dispatch/runners/tflint.js +0 -110
- package/dist/clients/dispatch/runners/tree-sitter.js +0 -620
- package/dist/clients/dispatch/runners/trivy-config.js +0 -247
- package/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
- package/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
- package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -85
- package/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
- package/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
- package/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
- package/dist/clients/dispatch/runners/utils.js +0 -15
- package/dist/clients/dispatch/runners/vale.js +0 -116
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
- package/dist/clients/dispatch/runners/yamllint.js +0 -85
- package/dist/clients/dispatch/runners/zig-check.js +0 -91
- package/dist/clients/dispatch/suppress-writer.js +0 -69
- package/dist/clients/dispatch/tool-profile.js +0 -30
- package/dist/clients/dispatch/types.js +0 -21
- package/dist/clients/dispatch/utils/format-utils.js +0 -45
- package/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
- package/dist/clients/disposition-logger.js +0 -32
- package/dist/clients/disposition-publish.js +0 -124
- package/dist/clients/durable-store.js +0 -97
- package/dist/clients/effective-config.js +0 -413
- package/dist/clients/env-utils.js +0 -83
- package/dist/clients/error-class.js +0 -23
- package/dist/clients/eval-timestamp.js +0 -17
- package/dist/clients/event-loop-hold.js +0 -274
- package/dist/clients/event-loop-monitor.js +0 -250
- package/dist/clients/extension-log.js +0 -504
- package/dist/clients/extension-mode.js +0 -73
- package/dist/clients/feature-hints.js +0 -103
- package/dist/clients/file-kinds.js +0 -412
- package/dist/clients/file-role.js +0 -134
- package/dist/clients/file-time.js +0 -160
- package/dist/clients/file-utils.js +0 -1032
- package/dist/clients/finding-delivery-gate.js +0 -463
- package/dist/clients/finding-identity.js +0 -88
- package/dist/clients/fix-worklog.js +0 -103
- package/dist/clients/format-events-publish.js +0 -343
- package/dist/clients/format-service.js +0 -222
- package/dist/clients/formatters-lazy.js +0 -9
- package/dist/clients/formatters.js +0 -1994
- package/dist/clients/freshness-cadence.js +0 -17
- package/dist/clients/freshness.js +0 -38
- package/dist/clients/generated-artifacts.js +0 -394
- package/dist/clients/generation-guard.js +0 -215
- package/dist/clients/git-guard.js +0 -923
- package/dist/clients/git-tracked-ignore.js +0 -243
- package/dist/clients/gitleaks-client.js +0 -509
- package/dist/clients/go-client.js +0 -104
- package/dist/clients/govulncheck-client.js +0 -605
- package/dist/clients/gradle-ktfmt-style.js +0 -252
- package/dist/clients/grammar-source.js +0 -465
- package/dist/clients/gzip-stage-write.js +0 -135
- package/dist/clients/hashline-anchor.js +0 -424
- package/dist/clients/hook-budgets.js +0 -76
- package/dist/clients/host-edit-normalize.js +0 -140
- package/dist/clients/host-ports.js +0 -39
- package/dist/clients/indent-retarget.js +0 -154
- package/dist/clients/install-diagnostics.js +0 -143
- package/dist/clients/installer/index.js +0 -5167
- package/dist/clients/installer/managed-tool-refresh-session.js +0 -58
- package/dist/clients/installer/managed-tool-refresh.js +0 -783
- package/dist/clients/instance-reaper-state.js +0 -7
- package/dist/clients/instance-reaper.js +0 -1137
- package/dist/clients/instance-registry-lock.js +0 -145
- package/dist/clients/instance-registry.js +0 -773
- package/dist/clients/jscpd-client.js +0 -370
- package/dist/clients/json-cache-read.js +0 -65
- package/dist/clients/knip-client.js +0 -788
- package/dist/clients/language-policy.js +0 -252
- package/dist/clients/language-profile.js +0 -361
- package/dist/clients/language-registry.js +0 -603
- package/dist/clients/latency-logger.js +0 -719
- package/dist/clients/lazy-import.js +0 -21
- package/dist/clients/ledger-bounds.js +0 -54
- package/dist/clients/lens-config.js +0 -346
- package/dist/clients/lens-engine.js +0 -358
- package/dist/clients/lens-events.js +0 -125
- package/dist/clients/lens-flag-registry.js +0 -467
- package/dist/clients/lens-map.js +0 -1156
- package/dist/clients/live-bus-emitter.js +0 -74
- package/dist/clients/log-cleanup.js +0 -222
- package/dist/clients/lsp/aggregation.js +0 -178
- package/dist/clients/lsp/cascade-tier.js +0 -421
- package/dist/clients/lsp/client.js +0 -4465
- package/dist/clients/lsp/config.js +0 -689
- package/dist/clients/lsp/diagnostic-binding.js +0 -225
- package/dist/clients/lsp/document-drift.js +0 -467
- package/dist/clients/lsp/edits.js +0 -1219
- package/dist/clients/lsp/index.js +0 -8305
- package/dist/clients/lsp/inferred-project.js +0 -216
- package/dist/clients/lsp/jvm-runtime.js +0 -298
- package/dist/clients/lsp/language.js +0 -63
- package/dist/clients/lsp/launch.js +0 -725
- package/dist/clients/lsp/lombok.js +0 -158
- package/dist/clients/lsp/path-utils.js +0 -5
- package/dist/clients/lsp/pending-aux-coverage.js +0 -251
- package/dist/clients/lsp/position-encoding.js +0 -61
- package/dist/clients/lsp/ruby-drive-dirs.js +0 -83
- package/dist/clients/lsp/server.js +0 -3209
- package/dist/clients/lsp/session-roots.js +0 -192
- package/dist/clients/lsp/spawn-history.js +0 -17
- package/dist/clients/lsp/sync-kind.js +0 -42
- package/dist/clients/lsp/tsserver-sync.js +0 -332
- package/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
- package/dist/clients/lsp/wait-policy/classification.js +0 -85
- package/dist/clients/lsp/wait-policy/index.js +0 -10
- package/dist/clients/lsp/wait-policy/strategies.js +0 -303
- package/dist/clients/lsp/watch-queue.js +0 -61
- package/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
- package/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
- package/dist/clients/lsp/workspace-sweep-hold.js +0 -184
- package/dist/clients/lsp-budget.js +0 -186
- package/dist/clients/lsp-document-symbols.js +0 -110
- package/dist/clients/lsp-lazy.js +0 -9
- package/dist/clients/lsp-mutation.js +0 -396
- package/dist/clients/lsp-status.js +0 -59
- package/dist/clients/map-with-concurrency.js +0 -40
- package/dist/clients/mcp/analyze.js +0 -420
- package/dist/clients/mcp/host-shim.js +0 -31
- package/dist/clients/mcp/ipc.js +0 -335
- package/dist/clients/mcp/review.js +0 -151
- package/dist/clients/mcp/session.js +0 -412
- package/dist/clients/memory-sampler.js +0 -208
- package/dist/clients/message-end-attribution.js +0 -30
- package/dist/clients/metrics-client.js +0 -107
- package/dist/clients/metrics-history.js +0 -327
- package/dist/clients/middle-man-analysis.js +0 -263
- package/dist/clients/model-provider.js +0 -53
- package/dist/clients/module-report-lsp.js +0 -189
- package/dist/clients/module-report.js +0 -2150
- package/dist/clients/mutating-tool.js +0 -651
- package/dist/clients/mutation-attribution.js +0 -368
- package/dist/clients/mutation-bridge.js +0 -240
- package/dist/clients/ndjson-logger.js +0 -734
- package/dist/clients/observed-mutation-sources.js +0 -101
- package/dist/clients/observed-mutation.js +0 -1253
- package/dist/clients/oldtext-autopatch.js +0 -128
- package/dist/clients/opaque-mutation-scan.js +0 -496
- package/dist/clients/opengrep-client.js +0 -328
- package/dist/clients/opengrep-config.js +0 -76
- package/dist/clients/package-manager.js +0 -607
- package/dist/clients/package-root.js +0 -38
- package/dist/clients/partial-edit-apply.js +0 -439
- package/dist/clients/path-attribution-telemetry.js +0 -43
- package/dist/clients/path-keyed-map.js +0 -108
- package/dist/clients/path-utils.js +0 -1244
- package/dist/clients/performance-report.js +0 -331
- package/dist/clients/persist-debounce.js +0 -70
- package/dist/clients/persistent-reverify.js +0 -271
- package/dist/clients/php-cs-fixer-config.js +0 -119
- package/dist/clients/pipeline.js +0 -1425
- package/dist/clients/post-autofix-notice.js +0 -37
- package/dist/clients/print-mode.js +0 -21
- package/dist/clients/probe-home-state.js +0 -228
- package/dist/clients/process-bridge.js +0 -66
- package/dist/clients/process-singletons.js +0 -210
- package/dist/clients/process-snapshot.js +0 -68
- package/dist/clients/project-changes.js +0 -181
- package/dist/clients/project-conventions.js +0 -171
- package/dist/clients/project-diagnostics/cache.js +0 -137
- package/dist/clients/project-diagnostics/extractors.js +0 -109
- package/dist/clients/project-diagnostics/fresh-fetch.js +0 -650
- package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
- package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
- package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
- package/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
- package/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
- package/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
- package/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
- package/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
- package/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
- package/dist/clients/project-diagnostics/scanner.js +0 -520
- package/dist/clients/project-diagnostics/types.js +0 -1
- package/dist/clients/project-lens-config.js +0 -799
- package/dist/clients/project-report.js +0 -701
- package/dist/clients/project-scale.js +0 -237
- package/dist/clients/project-scan-policy.js +0 -11
- package/dist/clients/project-snapshot-fingerprint.js +0 -48
- package/dist/clients/project-snapshot-persist-worker.js +0 -20
- package/dist/clients/project-snapshot.js +0 -1653
- package/dist/clients/project-trust.js +0 -181
- package/dist/clients/python-environment.js +0 -217
- package/dist/clients/python-provenance.js +0 -639
- package/dist/clients/quiet-window-config.js +0 -47
- package/dist/clients/quiet-window.js +0 -236
- package/dist/clients/read-bridge.js +0 -139
- package/dist/clients/read-expansion.js +0 -266
- package/dist/clients/read-guard-logger.js +0 -251
- package/dist/clients/read-guard-tool-lines.js +0 -1306
- package/dist/clients/read-guard.js +0 -1561
- package/dist/clients/recent-touches.js +0 -240
- package/dist/clients/redact/secrets.js +0 -449
- package/dist/clients/resource-sampler.js +0 -628
- package/dist/clients/reverse-deps.js +0 -270
- package/dist/clients/review-graph/builder.js +0 -5118
- package/dist/clients/review-graph/format.js +0 -33
- package/dist/clients/review-graph/git-identity.js +0 -157
- package/dist/clients/review-graph/import-resolvers.js +0 -481
- package/dist/clients/review-graph/persist-worker.js +0 -10
- package/dist/clients/review-graph/query.js +0 -244
- package/dist/clients/review-graph/revision-drift.js +0 -21
- package/dist/clients/review-graph/service.js +0 -96
- package/dist/clients/review-graph/shared-extraction-ir.js +0 -82
- package/dist/clients/review-graph/symbol-id.js +0 -103
- package/dist/clients/review-graph/tsconfig-paths.js +0 -299
- package/dist/clients/review-graph/types.js +0 -1
- package/dist/clients/review-graph/workspace-modules.js +0 -449
- package/dist/clients/review-graph-logger.js +0 -81
- package/dist/clients/ruff-client.js +0 -152
- package/dist/clients/rules-scanner.js +0 -131
- package/dist/clients/run-duration.js +0 -55
- package/dist/clients/runtime-agent-end.js +0 -746
- package/dist/clients/runtime-config.js +0 -77
- package/dist/clients/runtime-context.js +0 -211
- package/dist/clients/runtime-coordinator.js +0 -1290
- package/dist/clients/runtime-session.js +0 -2485
- package/dist/clients/runtime-tool-call.js +0 -1284
- package/dist/clients/runtime-tool-result.js +0 -2034
- package/dist/clients/runtime-turn.js +0 -3600
- package/dist/clients/rust-client.js +0 -97
- package/dist/clients/safe-spawn.js +0 -2166
- package/dist/clients/sanitize.js +0 -286
- package/dist/clients/scan-utils.js +0 -25
- package/dist/clients/scratch-tree-policy.js +0 -165
- package/dist/clients/search-read-registration.js +0 -94
- package/dist/clients/secret-findings.js +0 -114
- package/dist/clients/security-scan-client.js +0 -352
- package/dist/clients/session-event-guard.js +0 -297
- package/dist/clients/session-lifecycle.js +0 -484
- package/dist/clients/session-start-observability.js +0 -94
- package/dist/clients/session-state-store.js +0 -101
- package/dist/clients/session-summary.js +0 -15
- package/dist/clients/sessionstart-logger.js +0 -27
- package/dist/clients/sg-runner.js +0 -876
- package/dist/clients/sgconfig.js +0 -347
- package/dist/clients/shared-checkout-guard.js +0 -338
- package/dist/clients/single-flight.js +0 -158
- package/dist/clients/situational-tool-telemetry.js +0 -108
- package/dist/clients/skills-resolver.js +0 -141
- package/dist/clients/slow-fs.js +0 -138
- package/dist/clients/smells-rollup.js +0 -231
- package/dist/clients/source-filter.js +0 -596
- package/dist/clients/source-walker.js +0 -211
- package/dist/clients/spawn-output-cap.js +0 -37
- package/dist/clients/spawn-timeout-cooldown.js +0 -110
- package/dist/clients/stale-marker.js +0 -17
- package/dist/clients/startup-scan.js +0 -400
- package/dist/clients/startup-timing.js +0 -130
- package/dist/clients/string-utils.js +0 -33
- package/dist/clients/subagent-mode.js +0 -111
- package/dist/clients/symbol-containment.js +0 -24
- package/dist/clients/symbol-types.js +0 -5
- package/dist/clients/test-runner-client.js +0 -2507
- package/dist/clients/test-runner-delivery.js +0 -287
- package/dist/clients/todo-scanner.js +0 -229
- package/dist/clients/tool-agreement.js +0 -423
- package/dist/clients/tool-config.js +0 -249
- package/dist/clients/tool-cwd.js +0 -270
- package/dist/clients/tool-definition.js +0 -47
- package/dist/clients/tool-event.js +0 -105
- package/dist/clients/tool-policy.js +0 -2755
- package/dist/clients/tool-probe.js +0 -53
- package/dist/clients/tool-render.js +0 -156
- package/dist/clients/tool-set-policy.js +0 -118
- package/dist/clients/tree-sitter-cache.js +0 -364
- package/dist/clients/tree-sitter-client.js +0 -4134
- package/dist/clients/tree-sitter-logger.js +0 -100
- package/dist/clients/tree-sitter-navigator.js +0 -269
- package/dist/clients/tree-sitter-query-loader.js +0 -569
- package/dist/clients/tree-sitter-shared.js +0 -158
- package/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
- package/dist/clients/trivy-client.js +0 -467
- package/dist/clients/tui-fit.js +0 -50
- package/dist/clients/turn-context.js +0 -52
- package/dist/clients/turn-summary-render.js +0 -72
- package/dist/clients/turn-summary.js +0 -132
- package/dist/clients/typos-config.js +0 -29
- package/dist/clients/user-notify.js +0 -51
- package/dist/clients/vanished-instance-marker.js +0 -57
- package/dist/clients/warm-attach.js +0 -246
- package/dist/clients/widget-state.js +0 -1677
- package/dist/clients/word-index-logger.js +0 -49
- package/dist/clients/word-index-store.js +0 -459
- package/dist/clients/word-index.js +0 -2080
- package/dist/clients/workspace-topology.js +0 -389
- package/dist/clients/write-ordering-guard.js +0 -65
- package/dist/clients/zizmor-config.js +0 -327
- package/dist/i18n.js +0 -69
- package/dist/mcp/build-staleness.js +0 -124
- package/dist/scripts/lib/process-scan.mjs +0 -583
- package/dist/scripts/lib/skills-predicate.mjs +0 -129
- package/dist/tools/activate-tools.js +0 -87
- package/dist/tools/ast-grep-outline.js +0 -137
- package/dist/tools/ast-grep-replace.js +0 -244
- package/dist/tools/ast-grep-search.js +0 -762
- package/dist/tools/effective-config.js +0 -90
- package/dist/tools/lens-diagnostic-mark.js +0 -375
- package/dist/tools/lens-diagnostics.js +0 -2544
- package/dist/tools/lsp-diagnostics.js +0 -1634
- package/dist/tools/lsp-navigation.js +0 -1367
- package/dist/tools/lsp-structured-output.js +0 -294
- package/dist/tools/module-report.js +0 -382
- package/dist/tools/project-report.js +0 -86
- package/dist/tools/render-compact.js +0 -374
- package/dist/tools/scan-progress.js +0 -53
- package/dist/tools/shared.js +0 -29
- package/dist/tools/symbol-search.js +0 -155
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { logLatency } from "./latency-logger.js";
|
|
5
|
-
import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
|
|
6
|
-
import { resolveRunnerPath } from "./dispatch/runner-context.js";
|
|
7
|
-
import { freshnessFromMtime } from "./freshness.js";
|
|
8
|
-
export const MAX_ADVISORY_AFFECTED_FILES = 256;
|
|
9
|
-
export function advisoryPathKey(filePath, cwd) {
|
|
10
|
-
return normalizeMapKey(path.resolve(cwd, filePath));
|
|
11
|
-
}
|
|
12
|
-
/** Shared with git guard: there is one SHA-256 implementation for advisories. */
|
|
13
|
-
export function advisoryFileHash(filePath) {
|
|
14
|
-
try {
|
|
15
|
-
return createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
const code = error.code ?? "unknown";
|
|
19
|
-
return code === "ENOENT" ? "missing" : `unreadable:${code}`;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function snapshotOne(filePath, cwd, role) {
|
|
23
|
-
const resolved = path.resolve(cwd, filePath);
|
|
24
|
-
try {
|
|
25
|
-
const stat = fs.statSync(resolved);
|
|
26
|
-
return {
|
|
27
|
-
path: resolved,
|
|
28
|
-
role,
|
|
29
|
-
mtimeMs: stat.mtimeMs,
|
|
30
|
-
size: stat.size,
|
|
31
|
-
sha256: advisoryFileHash(resolved),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
const code = error.code ?? "unknown";
|
|
36
|
-
return {
|
|
37
|
-
path: resolved,
|
|
38
|
-
role,
|
|
39
|
-
mtimeMs: -1,
|
|
40
|
-
size: -1,
|
|
41
|
-
sha256: code === "ENOENT" ? "missing" : `unreadable:${code}`,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export function snapshotAdvisoryProvenance(args) {
|
|
46
|
-
const seen = new Set();
|
|
47
|
-
const files = [];
|
|
48
|
-
for (const file of args.files) {
|
|
49
|
-
const key = advisoryPathKey(file.path, args.cwd);
|
|
50
|
-
if (seen.has(key))
|
|
51
|
-
continue;
|
|
52
|
-
seen.add(key);
|
|
53
|
-
files.push(snapshotOne(file.path, args.cwd, file.role));
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
revision: {
|
|
57
|
-
sessionId: args.runtime.telemetrySessionId,
|
|
58
|
-
projectSeq: args.runtime.projectSeq,
|
|
59
|
-
turnIndex: args.runtime.turnIndex,
|
|
60
|
-
generation: args.generation,
|
|
61
|
-
capturedAt: args.capturedAt ?? Date.now(),
|
|
62
|
-
},
|
|
63
|
-
files,
|
|
64
|
-
...(args.truncated ? { truncated: true } : {}),
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
function isFiniteNumber(value) {
|
|
68
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
69
|
-
}
|
|
70
|
-
function isCapturedHash(value) {
|
|
71
|
-
return (typeof value === "string" &&
|
|
72
|
-
(/^[a-f0-9]{64}$/.test(value) ||
|
|
73
|
-
value === "missing" ||
|
|
74
|
-
value.startsWith("unreadable:")));
|
|
75
|
-
}
|
|
76
|
-
function isWellFormed(value) {
|
|
77
|
-
if (!value || typeof value !== "object")
|
|
78
|
-
return false;
|
|
79
|
-
const record = value;
|
|
80
|
-
const revision = record.revision;
|
|
81
|
-
return (!!revision &&
|
|
82
|
-
typeof revision.sessionId === "string" &&
|
|
83
|
-
isFiniteNumber(revision.projectSeq) &&
|
|
84
|
-
isFiniteNumber(revision.turnIndex) &&
|
|
85
|
-
isFiniteNumber(revision.generation) &&
|
|
86
|
-
isFiniteNumber(revision.capturedAt) &&
|
|
87
|
-
Array.isArray(record.files) &&
|
|
88
|
-
record.files.length > 0 &&
|
|
89
|
-
record.files.every((file) => !!file &&
|
|
90
|
-
typeof file.path === "string" &&
|
|
91
|
-
(file.role === "source" ||
|
|
92
|
-
file.role === "test" ||
|
|
93
|
-
file.role === "affected") &&
|
|
94
|
-
isFiniteNumber(file.mtimeMs) &&
|
|
95
|
-
isFiniteNumber(file.size) &&
|
|
96
|
-
isCapturedHash(file.sha256)));
|
|
97
|
-
}
|
|
98
|
-
export function validateAdvisoryProvenance(record, cwd, runtime) {
|
|
99
|
-
if (!isWellFormed(record.provenance)) {
|
|
100
|
-
return {
|
|
101
|
-
status: "unknown",
|
|
102
|
-
reasons: ["malformed-or-legacy-provenance"],
|
|
103
|
-
allFilesDeleted: false,
|
|
104
|
-
changedPathCount: 0,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
const provenance = record.provenance;
|
|
108
|
-
const reasons = [];
|
|
109
|
-
let unknown = provenance.truncated === true;
|
|
110
|
-
if (unknown)
|
|
111
|
-
reasons.push("truncated-provenance");
|
|
112
|
-
if (runtime) {
|
|
113
|
-
if (provenance.revision.sessionId !== runtime.telemetrySessionId)
|
|
114
|
-
reasons.push("session-mismatch");
|
|
115
|
-
}
|
|
116
|
-
let deletedFiles = 0;
|
|
117
|
-
const changedPaths = new Set();
|
|
118
|
-
for (const captured of provenance.files) {
|
|
119
|
-
const resolved = path.resolve(cwd, captured.path);
|
|
120
|
-
const reasonsBefore = reasons.length;
|
|
121
|
-
let stat;
|
|
122
|
-
try {
|
|
123
|
-
stat = fs.statSync(resolved);
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
const code = error.code ?? "unknown";
|
|
127
|
-
if (code === "ENOENT") {
|
|
128
|
-
if (captured.sha256 !== "missing") {
|
|
129
|
-
deletedFiles += 1;
|
|
130
|
-
reasons.push(`missing:${advisoryPathKey(resolved, cwd)}`);
|
|
131
|
-
changedPaths.add(advisoryPathKey(resolved, cwd));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
unknown = true;
|
|
136
|
-
reasons.push(`unreadable:${advisoryPathKey(resolved, cwd)}:${code}`);
|
|
137
|
-
changedPaths.add(advisoryPathKey(resolved, cwd));
|
|
138
|
-
}
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
if (captured.sha256.startsWith("unreadable:")) {
|
|
142
|
-
unknown = true;
|
|
143
|
-
reasons.push(`capture-unreadable:${advisoryPathKey(resolved, cwd)}`);
|
|
144
|
-
changedPaths.add(advisoryPathKey(resolved, cwd));
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (captured.sha256 === "missing") {
|
|
148
|
-
reasons.push(`created:${advisoryPathKey(resolved, cwd)}`);
|
|
149
|
-
changedPaths.add(advisoryPathKey(resolved, cwd));
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
if (stat.mtimeMs !== captured.mtimeMs || stat.size !== captured.size) {
|
|
153
|
-
reasons.push(`metadata-changed:${advisoryPathKey(resolved, cwd)}`);
|
|
154
|
-
}
|
|
155
|
-
const currentHash = advisoryFileHash(resolved);
|
|
156
|
-
if (currentHash.startsWith("unreadable:")) {
|
|
157
|
-
unknown = true;
|
|
158
|
-
reasons.push(`${currentHash}:${advisoryPathKey(resolved, cwd)}`);
|
|
159
|
-
}
|
|
160
|
-
else if (currentHash !== captured.sha256) {
|
|
161
|
-
reasons.push(`content-changed:${advisoryPathKey(resolved, cwd)}`);
|
|
162
|
-
}
|
|
163
|
-
if (reasons.length > reasonsBefore)
|
|
164
|
-
changedPaths.add(advisoryPathKey(resolved, cwd));
|
|
165
|
-
}
|
|
166
|
-
const allFilesDeleted = deletedFiles === provenance.files.length;
|
|
167
|
-
if (unknown)
|
|
168
|
-
return {
|
|
169
|
-
status: "unknown",
|
|
170
|
-
reasons,
|
|
171
|
-
allFilesDeleted,
|
|
172
|
-
changedPathCount: changedPaths.size,
|
|
173
|
-
};
|
|
174
|
-
return reasons.length > 0
|
|
175
|
-
? {
|
|
176
|
-
status: "superseded",
|
|
177
|
-
reasons,
|
|
178
|
-
allFilesDeleted,
|
|
179
|
-
changedPathCount: changedPaths.size,
|
|
180
|
-
}
|
|
181
|
-
: { status: "current", reasons: [], allFilesDeleted, changedPathCount: 0 };
|
|
182
|
-
}
|
|
183
|
-
// ── Finding-cited path existence (#1461 slice 1) ──────────────────────────────
|
|
184
|
-
//
|
|
185
|
-
// `validateAdvisoryProvenance` above answers "did the files the agent EDITED
|
|
186
|
-
// change since capture?". A cached scanner finding asks a different question:
|
|
187
|
-
// "does the file this finding NAMES still exist?". #1460's live case is the gap
|
|
188
|
-
// between them — a gitleaks blocker for a directory deleted eleven minutes
|
|
189
|
-
// earlier shipped as `current` seven consecutive times, because the edited
|
|
190
|
-
// files were all intact and the cited path was never in the envelope.
|
|
191
|
-
//
|
|
192
|
-
// CONTRACT (the five remaining #1461 slices reuse this verbatim):
|
|
193
|
-
// - Input is any finding shape; the caller supplies `citedPath`, so nothing
|
|
194
|
-
// here knows about gitleaks, trivy, govulncheck, vulture, or delta mode.
|
|
195
|
-
// - A finding whose cited path is absent (ENOENT/ENOTDIR) is DROPPED, not
|
|
196
|
-
// demoted. There is no remediation for a file that is gone — the agent
|
|
197
|
-
// cannot rotate a credential in it or delete a line from it. Content drift
|
|
198
|
-
// on a SURVIVING file stays `validateAdvisoryProvenance`'s job, and that
|
|
199
|
-
// one demotes.
|
|
200
|
-
// - Fail open, never closed: a finding with no cited path, an unreadable
|
|
201
|
-
// path (EACCES/EPERM/EBUSY), or a path past the stat budget is DELIVERED.
|
|
202
|
-
// Unreadable is not absent; a missed drop is noise, a wrong drop is a lost
|
|
203
|
-
// secret.
|
|
204
|
-
// - Bounded cost: findings are deduped by their RAW cited string first —
|
|
205
|
-
// zero filesystem work — before anything pays for `resolveRunnerPath`'s
|
|
206
|
-
// ancestor walk or `normalizeMapKey`'s realpath. A first cut that deduped
|
|
207
|
-
// by the resolved/canonical key instead still ran that expensive step
|
|
208
|
-
// once per FINDING, not once per unique path, because the dedup lookup
|
|
209
|
-
// came after the cost it was meant to avoid: #1461's live 126-finding
|
|
210
|
-
// record measured 72.7ms in that shape, all of it before the first
|
|
211
|
-
// `statSync`. Deduping the raw string first drops the same record to a
|
|
212
|
-
// ~1.6ms median (this repo, Windows, 10-sample bench) — the remaining
|
|
213
|
-
// cost is one canonicalization + one stat per distinct cited string,
|
|
214
|
-
// capped at `MAX_FINDING_PATH_STATS`.
|
|
215
|
-
// - Path identity uses the guard normalizer (`resolveRunnerPath` →
|
|
216
|
-
// `normalizeMapKey`) for the canonical-key dedup and the shape-aware
|
|
217
|
-
// `toProjectRelativePath` for display, so Windows spellings of one path
|
|
218
|
-
// collapse to one stat and one log entry (defect shapes 1 and 2). This
|
|
219
|
-
// helper has no zero-I/O contract of its own to protect — it stats.
|
|
220
|
-
// Slice 3 (delta mode) must find and name its OWN seam before reusing
|
|
221
|
-
// this shape; `formatDeltaMode` (tools/lens-diagnostics.ts) reads only
|
|
222
|
-
// actionable-warnings, code-quality-warnings, and the delta report, none
|
|
223
|
-
// of which any #1461 slice writes yet, so nothing here currently touches
|
|
224
|
-
// it.
|
|
225
|
-
/** Stat budget: one per unique cited path, sharing the envelope's own cap. */
|
|
226
|
-
const MAX_FINDING_PATH_STATS = MAX_ADVISORY_AFFECTED_FILES;
|
|
227
|
-
/** How many dead paths a single drop record names before it stops. */
|
|
228
|
-
const MAX_LOGGED_DEAD_PATHS = 3;
|
|
229
|
-
/**
|
|
230
|
-
* One stat, one verdict. With `scannedAtMs` supplied, a surviving file whose
|
|
231
|
-
* mtime is newer than the scan reports `stale`.
|
|
232
|
-
*
|
|
233
|
-
* The boundary is `mtime > scannedAt + MTIME_DRIFT_TOLERANCE_MS`: a file
|
|
234
|
-
* scanned AT `scannedAt`, or written within the tolerance window of the scan
|
|
235
|
-
* timestamp being captured, reads live; genuinely past it reads stale.
|
|
236
|
-
*
|
|
237
|
-
* #1708: a bare +1ms tolerance — the convention `reconcileProjectDiagnosticsSnapshot`
|
|
238
|
-
* (clients/project-diagnostics/cache.ts:98) still uses, and the one this gate
|
|
239
|
-
* originally cited-but-withheld ("over-demoting costs a line number,
|
|
240
|
-
* under-demoting replays a false coordinate — the cheaper error wins the
|
|
241
|
-
* tie") — was not enough: on Windows a file's mtime can LEAD the immediately
|
|
242
|
-
* following `Date.now()` read by up to ~11.4ms, the same host skew
|
|
243
|
-
* `MTIME_DRIFT_TOLERANCE_MS` (`blocker-freshness.ts`) was raised to 50ms for
|
|
244
|
-
* (#1491/#1498). At +1ms this gate still demoted a real STOP-blocker (a
|
|
245
|
-
* trivy secret, #1628) to ACTION NEEDED, flaking
|
|
246
|
-
* tests/clients/runtime-turn-secrets-disposition.test.ts. Reusing the shared
|
|
247
|
-
* constant — rather than a second hand-tuned number — keeps one source of
|
|
248
|
-
* truth for the measured skew.
|
|
249
|
-
*/
|
|
250
|
-
export function findingPathFreshness(resolvedPath, scannedAtMs) {
|
|
251
|
-
try {
|
|
252
|
-
const stat = fs.statSync(resolvedPath);
|
|
253
|
-
// No scan timestamp recorded: the pre-kernel behavior treated the
|
|
254
|
-
// path as live (no reference to be stale against) - preserved.
|
|
255
|
-
if (scannedAtMs === undefined)
|
|
256
|
-
return "live";
|
|
257
|
-
const verdict = freshnessFromMtime({
|
|
258
|
-
mtimeMs: stat.mtimeMs,
|
|
259
|
-
referenceMs: scannedAtMs,
|
|
260
|
-
});
|
|
261
|
-
return verdict.verdict === "stale" ? "stale" : "live";
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
const code = error.code ?? "unknown";
|
|
265
|
-
// ENOTDIR: an ancestor component is no longer a directory — the cited
|
|
266
|
-
// path cannot exist either, same as ENOENT.
|
|
267
|
-
return code === "ENOENT" || code === "ENOTDIR" ? "missing" : "unknown";
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
/** Existence-only probe. Retained for callers that have no scan timestamp. */
|
|
271
|
-
export function findingPathExistence(resolvedPath) {
|
|
272
|
-
return findingPathFreshness(resolvedPath);
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Parse a scan timestamp into epoch ms, or `undefined` when it cannot be
|
|
276
|
-
* trusted. `undefined` disables the staleness arm — never demote on a bad
|
|
277
|
-
* clock.
|
|
278
|
-
*/
|
|
279
|
-
export function parseScannedAtMs(scannedAt) {
|
|
280
|
-
if (scannedAt === undefined || scannedAt === null || scannedAt === "") {
|
|
281
|
-
return undefined;
|
|
282
|
-
}
|
|
283
|
-
const ms = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
|
|
284
|
-
return Number.isFinite(ms) ? ms : undefined;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Partition findings into `{live, stale, dropped}` by what the path each one
|
|
288
|
-
* names looks like now. Pure apart from the `fs.statSync` probe, which is
|
|
289
|
-
* injectable so the partition rules are unit-testable without a filesystem.
|
|
290
|
-
*
|
|
291
|
-
* The stat memo lives in local `Map`s built fresh on every call — per delivery,
|
|
292
|
-
* never per process. A module-level cache here would be the process-lifetime
|
|
293
|
-
* latch shape: it would pin the first verdict for a path and re-serve it for
|
|
294
|
-
* every later turn, which is the very defect this gate exists to close.
|
|
295
|
-
*/
|
|
296
|
-
export function partitionFindingsByCitedPath(args) {
|
|
297
|
-
const limit = args.maxUniquePaths ?? MAX_FINDING_PATH_STATS;
|
|
298
|
-
const probe = args.existence ?? findingPathFreshness;
|
|
299
|
-
const scannedAtMs = parseScannedAtMs(args.scannedAt);
|
|
300
|
-
const onMissing = args.onMissing ?? "drop";
|
|
301
|
-
// Two dedup layers, cheapest first. `rawVerdicts` collapses findings that
|
|
302
|
-
// cite the IDENTICAL string with zero filesystem work — the dominant case
|
|
303
|
-
// (#1460's live record: 126 findings over a handful of distinct `file`
|
|
304
|
-
// strings). Only a raw string not seen before pays for
|
|
305
|
-
// `resolveRunnerPath`/`advisoryPathKey`, which folds FS-confirmed spelling
|
|
306
|
-
// variants (case, separators, ancestor walk-up) into `verdicts`, the
|
|
307
|
-
// canonical-key map that `statCount` reports. Keying the expensive work by
|
|
308
|
-
// the RESOLVED path instead of the raw one (the pre-#1461-HIGH-2 shape)
|
|
309
|
-
// still called it once per finding, since the dedup lookup came after the
|
|
310
|
-
// cost it was meant to dedupe.
|
|
311
|
-
const rawVerdicts = new Map();
|
|
312
|
-
const verdicts = new Map();
|
|
313
|
-
const live = [];
|
|
314
|
-
const stale = [];
|
|
315
|
-
const dropped = [];
|
|
316
|
-
const deadPaths = [];
|
|
317
|
-
const stalePaths = [];
|
|
318
|
-
let truncated = false;
|
|
319
|
-
for (const finding of args.findings) {
|
|
320
|
-
const cited = args.citedPath(finding);
|
|
321
|
-
if (!cited) {
|
|
322
|
-
live.push(finding);
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
let verdict = rawVerdicts.get(cited);
|
|
326
|
-
if (verdict === undefined) {
|
|
327
|
-
// Ancestor-tolerant, same as `toRunnerDisplayPath`'s resolution in
|
|
328
|
-
// runtime-turn.ts — a bare `path.resolve` here would decide the drop
|
|
329
|
-
// against a different root than the one used to render the survivor,
|
|
330
|
-
// dropping findings the display path would have shown correctly
|
|
331
|
-
// (#1461 HIGH-1). `resolveRunnerPath` already runs its result through
|
|
332
|
-
// `normalizeMapKey`, so the resolved path IS the canonical key — a
|
|
333
|
-
// second `advisoryPathKey` pass would re-pay the same realpath cost.
|
|
334
|
-
const resolved = resolveRunnerPath(args.cwd, cited);
|
|
335
|
-
const key = resolved;
|
|
336
|
-
verdict = verdicts.get(key);
|
|
337
|
-
if (verdict === undefined) {
|
|
338
|
-
if (verdicts.size >= limit) {
|
|
339
|
-
// Budget spent on paths we have not seen before — deliver rather
|
|
340
|
-
// than guess. Already-probed paths keep their cached verdict.
|
|
341
|
-
truncated = true;
|
|
342
|
-
verdict = "live";
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
verdict = probe(resolved, scannedAtMs);
|
|
346
|
-
// A store that treats deletion as evidence-loss rather than
|
|
347
|
-
// remediation-loss folds `missing` into the demote arm here, so
|
|
348
|
-
// every downstream list and record sees one consistent verdict.
|
|
349
|
-
if (verdict === "missing" && onMissing === "demote") {
|
|
350
|
-
verdict = "stale";
|
|
351
|
-
}
|
|
352
|
-
verdicts.set(key, verdict);
|
|
353
|
-
if (verdict === "missing")
|
|
354
|
-
deadPaths.push(resolved);
|
|
355
|
-
else if (verdict === "stale")
|
|
356
|
-
stalePaths.push(resolved);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
rawVerdicts.set(cited, verdict);
|
|
360
|
-
}
|
|
361
|
-
if (verdict === "missing")
|
|
362
|
-
dropped.push(finding);
|
|
363
|
-
else if (verdict === "stale")
|
|
364
|
-
stale.push(finding);
|
|
365
|
-
else
|
|
366
|
-
live.push(finding);
|
|
367
|
-
}
|
|
368
|
-
return {
|
|
369
|
-
live,
|
|
370
|
-
stale,
|
|
371
|
-
dropped,
|
|
372
|
-
deadPaths,
|
|
373
|
-
stalePaths,
|
|
374
|
-
statCount: verdicts.size,
|
|
375
|
-
truncated,
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Delivery-seam wrapper: partition, then emit one bounded `finding_dead_path_drop`
|
|
380
|
-
* record when anything was dropped, and return only what is safe to deliver.
|
|
381
|
-
*
|
|
382
|
-
* The record is the #1432 Gap 1 principle applied here — an eviction that logs
|
|
383
|
-
* nothing is only confirmable by the absence of complaints. One record per
|
|
384
|
-
* store per delivery, with a capped path sample; never one per finding.
|
|
385
|
-
*/
|
|
386
|
-
export function dropFindingsForMissingPaths(args) {
|
|
387
|
-
// No `scannedAt` — existence-only, so `partition.stale` is always empty.
|
|
388
|
-
const partition = partitionFindingsByCitedPath(args);
|
|
389
|
-
emitDeadPathDropRecord(args.store, args.cwd, partition);
|
|
390
|
-
return partition.live;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Delivery-seam wrapper with the #1622 freshness verdict. Emits at most one
|
|
394
|
-
* `finding_dead_path_drop` and one `finding_stale_line_demote` record per store
|
|
395
|
-
* per delivery, each with a capped path sample; never one per finding.
|
|
396
|
-
*
|
|
397
|
-
* Callers that have no scan timestamp keep using `dropFindingsForMissingPaths`.
|
|
398
|
-
*/
|
|
399
|
-
export function gateFindingsByPathFreshness(args) {
|
|
400
|
-
const partition = partitionFindingsByCitedPath(args);
|
|
401
|
-
emitDeadPathDropRecord(args.store, args.cwd, partition);
|
|
402
|
-
emitStaleLineDemoteRecord(args.store, args.cwd, args.scannedAt, partition);
|
|
403
|
-
return { live: partition.live, stale: partition.stale };
|
|
404
|
-
}
|
|
405
|
-
function emitDeadPathDropRecord(store, cwd, partition) {
|
|
406
|
-
if (partition.dropped.length === 0)
|
|
407
|
-
return;
|
|
408
|
-
logLatency({
|
|
409
|
-
type: "phase",
|
|
410
|
-
phase: "finding_dead_path_drop",
|
|
411
|
-
filePath: cwd,
|
|
412
|
-
durationMs: 0,
|
|
413
|
-
metadata: {
|
|
414
|
-
store,
|
|
415
|
-
droppedDeadPaths: partition.dropped.length,
|
|
416
|
-
deadPathCount: partition.deadPaths.length,
|
|
417
|
-
deliveredCount: partition.live.length,
|
|
418
|
-
statCount: partition.statCount,
|
|
419
|
-
samplePaths: partition.deadPaths
|
|
420
|
-
.slice(0, MAX_LOGGED_DEAD_PATHS)
|
|
421
|
-
.map((deadPath) => toProjectRelativePath(deadPath, cwd)),
|
|
422
|
-
...(partition.truncated ? { truncated: true } : {}),
|
|
423
|
-
},
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* #1622 criterion 5: the demote path gets the same record shape as the drop
|
|
428
|
-
* path. Before this, a stale-line replay window was reconstructible only by
|
|
429
|
-
* proxy — the blocker payload is written nowhere.
|
|
430
|
-
*/
|
|
431
|
-
function emitStaleLineDemoteRecord(store, cwd, scannedAt, partition) {
|
|
432
|
-
if (partition.stale.length === 0)
|
|
433
|
-
return;
|
|
434
|
-
logLatency({
|
|
435
|
-
type: "phase",
|
|
436
|
-
phase: "finding_stale_line_demote",
|
|
437
|
-
filePath: cwd,
|
|
438
|
-
durationMs: 0,
|
|
439
|
-
metadata: {
|
|
440
|
-
store,
|
|
441
|
-
demotedStalePaths: partition.stale.length,
|
|
442
|
-
stalePathCount: partition.stalePaths.length,
|
|
443
|
-
deliveredCount: partition.live.length,
|
|
444
|
-
statCount: partition.statCount,
|
|
445
|
-
samplePaths: partition.stalePaths
|
|
446
|
-
.slice(0, MAX_LOGGED_DEAD_PATHS)
|
|
447
|
-
.map((stalePath) => toProjectRelativePath(stalePath, cwd)),
|
|
448
|
-
...(scannedAt === undefined ? {} : { scannedAt: String(scannedAt) }),
|
|
449
|
-
...(partition.truncated ? { truncated: true } : {}),
|
|
450
|
-
},
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
export function provenanceStamp(provenance) {
|
|
454
|
-
if (!isWellFormed(provenance))
|
|
455
|
-
return "session unknown / turn unknown / generation unknown";
|
|
456
|
-
return `session ${provenance.revision.sessionId} / turn ${provenance.revision.turnIndex} / generation ${provenance.revision.generation}`;
|
|
457
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentBehaviorClient for pi-lens
|
|
3
|
-
*
|
|
4
|
-
* Tracks tool call sequences and flags anti-patterns in real-time:
|
|
5
|
-
* - Blind writes: editing or writing without reading first (a same-session
|
|
6
|
-
* write/edit of the same path counts as file knowledge)
|
|
7
|
-
* - Thrashing: repeated identical tool calls with no progress
|
|
8
|
-
*
|
|
9
|
-
* No external dependencies — purely tracks tool call history.
|
|
10
|
-
*/
|
|
11
|
-
import { isMutatingToolName } from "./mutating-tool.js";
|
|
12
|
-
import { normalizeMapKey } from "./path-utils.js";
|
|
13
|
-
// --- Constants ---
|
|
14
|
-
// #2423: mutating-tool membership comes from the seam
|
|
15
|
-
// (`clients/mutating-tool.ts`), the single source of truth for the built-in
|
|
16
|
-
// table. The hand-maintained set this replaced also carried a dead
|
|
17
|
-
// `"multiedit"` entry that no host emits.
|
|
18
|
-
//
|
|
19
|
-
// Known gap, stated rather than hidden: this client is handed a tool NAME and a
|
|
20
|
-
// path, never the event, so it cannot see a third-party mutating tool the way
|
|
21
|
-
// the shape adapters can. Blind-write and thrash detection covers pi's
|
|
22
|
-
// built-ins only.
|
|
23
|
-
const READ_OPS = new Set(["read", "bash", "grep", "glob", "find", "rg"]);
|
|
24
|
-
const BLIND_WRITE_WINDOW = 5; // Check last N tool calls for a read
|
|
25
|
-
const THRASH_THRESHOLD = 3; // Flag after N consecutive identical tool+file pairs
|
|
26
|
-
const THRASH_TIMEOUT_MS = 30_000; // Reset thrash counter if gap > 30s
|
|
27
|
-
// --- Client ---
|
|
28
|
-
export class AgentBehaviorClient {
|
|
29
|
-
toolHistory = [];
|
|
30
|
-
consecutiveCount = 0;
|
|
31
|
-
lastToolName = null;
|
|
32
|
-
lastToolFilePath = null;
|
|
33
|
-
lastToolTimestamp = 0;
|
|
34
|
-
// Per-file tracking
|
|
35
|
-
fileEditCount = new Map();
|
|
36
|
-
static FILE_EDIT_IDLE_MS = 30 * 60_000;
|
|
37
|
-
/**
|
|
38
|
-
* Record a tool call and return any warnings triggered.
|
|
39
|
-
* Called from tool_result handler.
|
|
40
|
-
*/
|
|
41
|
-
recordToolCall(toolName, filePath) {
|
|
42
|
-
const warnings = [];
|
|
43
|
-
const now = Date.now();
|
|
44
|
-
// Track consecutive identical tool+file pairs (thrashing).
|
|
45
|
-
// Editing different files in sequence is normal agent behaviour — only flag
|
|
46
|
-
// when the same tool is called on the same file N times without making
|
|
47
|
-
// progress on anything else.
|
|
48
|
-
const normalizedPath = filePath ? normalizeMapKey(filePath) : null;
|
|
49
|
-
if (toolName === this.lastToolName &&
|
|
50
|
-
normalizedPath === this.lastToolFilePath &&
|
|
51
|
-
now - this.lastToolTimestamp < THRASH_TIMEOUT_MS) {
|
|
52
|
-
this.consecutiveCount++;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
this.consecutiveCount = 1;
|
|
56
|
-
}
|
|
57
|
-
this.lastToolName = toolName;
|
|
58
|
-
this.lastToolFilePath = normalizedPath;
|
|
59
|
-
this.lastToolTimestamp = now;
|
|
60
|
-
// Check for thrashing
|
|
61
|
-
if (this.consecutiveCount === THRASH_THRESHOLD) {
|
|
62
|
-
const fileLabel = filePath ? ` on \`${filePath}\`` : "";
|
|
63
|
-
warnings.push({
|
|
64
|
-
type: "thrashing",
|
|
65
|
-
message: `🔴 THRASHING — ${THRASH_THRESHOLD} consecutive \`${toolName}\`${fileLabel} calls with no progress. Consider fixing the root cause instead of re-running.`,
|
|
66
|
-
severity: "error",
|
|
67
|
-
details: {
|
|
68
|
-
toolName,
|
|
69
|
-
filePath,
|
|
70
|
-
callCount: this.consecutiveCount,
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
// Check for blind writes
|
|
75
|
-
if (isMutatingToolName(toolName)) {
|
|
76
|
-
const recentWindow = this.toolHistory.slice(-BLIND_WRITE_WINDOW);
|
|
77
|
-
// A read in the window proves file knowledge; so does a same-session
|
|
78
|
-
// write/edit of THIS path — the agent authored the content, so the
|
|
79
|
-
// write-then-edit loop on a self-authored file is not a blind write.
|
|
80
|
-
const hasRecentRead = recentWindow.some((r) => READ_OPS.has(r.toolName) ||
|
|
81
|
-
(isMutatingToolName(r.toolName) &&
|
|
82
|
-
r.filePath !== undefined &&
|
|
83
|
-
normalizedPath !== null &&
|
|
84
|
-
normalizeMapKey(r.filePath) === normalizedPath));
|
|
85
|
-
if (!hasRecentRead && recentWindow.length > 0) {
|
|
86
|
-
// Count how many writes in the window without reads
|
|
87
|
-
const writesWithoutRead = recentWindow.filter((r) => isMutatingToolName(r.toolName)).length;
|
|
88
|
-
if (writesWithoutRead >= 2) {
|
|
89
|
-
warnings.push({
|
|
90
|
-
type: "blind-write",
|
|
91
|
-
message: `⚠ BLIND WRITE — editing \`${filePath ?? "file"}\` without reading in the last ${BLIND_WRITE_WINDOW} tool calls. Read the file first to avoid assumptions.`,
|
|
92
|
-
severity: "warning",
|
|
93
|
-
details: {
|
|
94
|
-
filePath,
|
|
95
|
-
windowSize: recentWindow.length,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Track edits per file
|
|
101
|
-
if (filePath) {
|
|
102
|
-
const key = normalizeMapKey(filePath);
|
|
103
|
-
this.fileEditCount.set(key, {
|
|
104
|
-
count: (this.fileEditCount.get(key)?.count ?? 0) + 1,
|
|
105
|
-
lastUsedAt: now,
|
|
106
|
-
});
|
|
107
|
-
for (const [pathKey, entry] of this.fileEditCount) {
|
|
108
|
-
if (now - entry.lastUsedAt > AgentBehaviorClient.FILE_EDIT_IDLE_MS) {
|
|
109
|
-
this.fileEditCount.delete(pathKey);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// Add to history (keep last 50 entries)
|
|
115
|
-
this.toolHistory.push({ toolName, filePath, timestamp: now });
|
|
116
|
-
if (this.toolHistory.length > 50) {
|
|
117
|
-
this.toolHistory = this.toolHistory.slice(-50);
|
|
118
|
-
}
|
|
119
|
-
return warnings;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Format warnings for LLM consumption.
|
|
123
|
-
*/
|
|
124
|
-
formatWarnings(warnings) {
|
|
125
|
-
if (warnings.length === 0)
|
|
126
|
-
return "";
|
|
127
|
-
return warnings.map((w) => w.message).join("\n");
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Get edit count for a file in this session.
|
|
131
|
-
*/
|
|
132
|
-
getEditCount(filePath) {
|
|
133
|
-
return this.fileEditCount.get(normalizeMapKey(filePath))?.count ?? 0;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Reset state (e.g., on session start).
|
|
137
|
-
*/
|
|
138
|
-
reset() {
|
|
139
|
-
this.toolHistory = [];
|
|
140
|
-
this.consecutiveCount = 0;
|
|
141
|
-
this.lastToolName = null;
|
|
142
|
-
this.lastToolTimestamp = 0;
|
|
143
|
-
this.fileEditCount.clear();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
/** Process-wide behaviour history, independent of analyzer bootstrap residency. */
|
|
147
|
-
export const agentBehaviorClient = new AgentBehaviorClient();
|