pi-lens 3.8.73 → 4.0.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 +2614 -12
- package/README.md +50 -21
- package/dist/clients/actionable-warnings.js +73 -18
- package/dist/clients/agent-behavior-client.js +21 -4
- package/dist/clients/ast-grep-client.js +68 -16
- package/dist/clients/ast-grep-types.js +0 -1
- package/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/dist/clients/atomic-write-staging.js +63 -0
- package/dist/clients/atomic-write.js +116 -16
- package/dist/clients/bash-file-access.js +193 -23
- package/dist/clients/biome-client.js +29 -25
- package/dist/clients/bootstrap.js +11 -2
- package/dist/clients/bounded-cache.js +34 -0
- package/dist/clients/bounded-pid-file-lock.js +192 -0
- package/dist/clients/bus-events-logger.js +5 -1
- package/dist/clients/bus-publish.js +10 -3
- package/dist/clients/cache/rule-cache.js +89 -6
- package/dist/clients/cache-manager.js +97 -9
- package/dist/clients/cache-observability.js +498 -0
- package/dist/clients/call-graph.js +460 -108
- package/dist/clients/cascade-logger.js +5 -1
- package/dist/clients/child-unref.js +85 -0
- package/dist/clients/codebase-model.js +52 -23
- package/dist/clients/collateral-test-role.js +40 -0
- package/dist/clients/complexity-client.js +6 -8
- package/dist/clients/console-guard-install.js +14 -0
- package/dist/clients/cooperative-budget.js +42 -0
- package/dist/clients/dead-code-client.js +2 -1
- package/dist/clients/debug-handles.js +206 -0
- package/dist/clients/debug-heap.js +167 -0
- package/dist/clients/degradation-ledger.js +153 -0
- package/dist/clients/dependency-checker.js +466 -63
- package/dist/clients/diagnostic-dispositions.js +83 -13
- package/dist/clients/diagnostics-publish.js +10 -3
- package/dist/clients/dispatch/auxiliary-lsp.js +34 -0
- package/dist/clients/dispatch/dispatcher.js +71 -13
- package/dist/clients/dispatch/fact-store.js +11 -0
- package/dist/clients/dispatch/facts/function-facts.js +37 -7
- package/dist/clients/dispatch/facts/import-facts.js +108 -119
- package/dist/clients/dispatch/facts/tree-sitter-facts.js +12 -12
- package/dist/clients/dispatch/facts/try-catch-facts.js +13 -0
- package/dist/clients/dispatch/indent-detect.js +35 -0
- package/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/dist/clients/dispatch/integration.js +1094 -611
- package/dist/clients/dispatch/lazy.js +14 -0
- package/dist/clients/dispatch/plan.js +11 -1
- package/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/dist/clients/dispatch/rule-policy.js +135 -0
- package/dist/clients/dispatch/rules/async-noise.js +1 -0
- package/dist/clients/dispatch/rules/unsafe-boundary.js +7 -0
- package/dist/clients/dispatch/runners/ast-grep-napi.js +47 -6
- package/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/dist/clients/dispatch/runners/detekt.js +3 -2
- package/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/dist/clients/dispatch/runners/index.js +5 -1
- package/dist/clients/dispatch/runners/lsp.js +14 -6
- package/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -1
- package/dist/clients/dispatch/runners/pyright.js +9 -5
- package/dist/clients/dispatch/runners/ruff.js +4 -8
- package/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/dist/clients/dispatch/runners/taplo.js +2 -1
- package/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/dist/clients/dispatch/runners/tflint.js +37 -5
- package/dist/clients/dispatch/runners/tree-sitter.js +155 -166
- package/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +284 -34
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/dist/clients/disposition-publish.js +10 -3
- package/dist/clients/durable-store.js +97 -0
- package/dist/clients/event-loop-monitor.js +94 -3
- package/dist/clients/extension-log.js +139 -0
- package/dist/clients/extension-mode.js +73 -0
- package/dist/clients/feature-hints.js +53 -7
- package/dist/clients/file-kinds.js +143 -31
- package/dist/clients/file-role.js +18 -3
- package/dist/clients/file-utils.js +56 -34
- package/dist/clients/fix-worklog.js +14 -2
- package/dist/clients/format-events-publish.js +16 -3
- package/dist/clients/format-service.js +24 -8
- package/dist/clients/formatters-lazy.js +8 -0
- package/dist/clients/formatters.js +358 -61
- package/dist/clients/generated-artifacts.js +169 -15
- package/dist/clients/git-guard.js +681 -11
- package/dist/clients/git-tracked-ignore.js +4 -0
- package/dist/clients/go-client.js +2 -1
- package/dist/clients/govulncheck-client.js +8 -0
- package/dist/clients/grammar-source.js +12 -4
- package/dist/clients/gzip-stage-write.js +102 -0
- package/dist/clients/host-ports.js +31 -0
- package/dist/clients/installer/index.js +733 -213
- package/dist/clients/instance-reaper.js +142 -172
- package/dist/clients/jscpd-client.js +31 -54
- package/dist/clients/knip-client.js +78 -36
- package/dist/clients/language-policy.js +14 -2
- package/dist/clients/language-profile.js +72 -77
- package/dist/clients/latency-logger.js +28 -16
- package/dist/clients/lens-config.js +140 -166
- package/dist/clients/lens-engine.js +124 -21
- package/dist/clients/lens-events.js +27 -4
- package/dist/clients/lens-flag-registry.js +293 -0
- package/dist/clients/lens-map.js +96 -26
- package/dist/clients/live-bus-emitter.js +30 -0
- package/dist/clients/log-cleanup.js +8 -18
- package/dist/clients/lsp/cascade-tier.js +23 -3
- package/dist/clients/lsp/client.js +566 -57
- package/dist/clients/lsp/config.js +74 -10
- package/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/dist/clients/lsp/edits.js +1012 -121
- package/dist/clients/lsp/index.js +1840 -803
- package/dist/clients/lsp/language.js +4 -1
- package/dist/clients/lsp/launch.js +40 -32
- package/dist/clients/lsp/path-utils.js +1 -1
- package/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/dist/clients/lsp/server.js +279 -60
- package/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/dist/clients/lsp-document-symbols.js +112 -0
- package/dist/clients/lsp-lazy.js +8 -0
- package/dist/clients/lsp-mutation.js +248 -0
- package/dist/clients/mcp/analyze.js +128 -36
- package/dist/clients/mcp/ipc.js +157 -24
- package/dist/clients/mcp/review.js +36 -3
- package/dist/clients/mcp/session.js +216 -8
- package/dist/clients/memory-sampler.js +115 -0
- package/dist/clients/metrics-client.js +2 -1
- package/dist/clients/metrics-history.js +2 -1
- package/dist/clients/module-report-lsp.js +5 -0
- package/dist/clients/module-report.js +267 -82
- package/dist/clients/ndjson-logger.js +334 -103
- package/dist/clients/opengrep-client.js +1 -1
- package/dist/clients/partial-edit-apply.js +120 -5
- package/dist/clients/path-keyed-map.js +76 -0
- package/dist/clients/path-utils.js +141 -5
- package/dist/clients/performance-report.js +327 -0
- package/dist/clients/pipeline.js +54 -13
- package/dist/clients/print-mode.js +21 -0
- package/dist/clients/project-changes.js +117 -4
- package/dist/clients/project-diagnostics/cache.js +3 -2
- package/dist/clients/project-diagnostics/extractors.js +26 -115
- package/dist/clients/project-diagnostics/fresh-fetch.js +149 -19
- package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +117 -0
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +37 -5
- package/dist/clients/project-diagnostics/scanner.js +268 -128
- package/dist/clients/project-lens-config.js +138 -34
- package/dist/clients/project-report.js +64 -25
- package/dist/clients/project-scale.js +8 -10
- package/dist/clients/project-snapshot-persist-worker.js +6 -0
- package/dist/clients/project-snapshot.js +767 -8
- package/dist/clients/project-trust.js +181 -0
- package/dist/clients/quiet-window.js +27 -5
- package/dist/clients/read-bridge.js +147 -0
- package/dist/clients/read-expansion.js +22 -12
- package/dist/clients/read-guard-logger.js +159 -1
- package/dist/clients/read-guard-tool-lines.js +150 -29
- package/dist/clients/read-guard.js +262 -3
- package/dist/clients/redact/secrets.js +449 -0
- package/dist/clients/resource-sampler.js +144 -39
- package/dist/clients/reverse-deps.js +48 -0
- package/dist/clients/review-graph/builder.js +2570 -326
- package/dist/clients/review-graph/git-identity.js +2 -1
- package/dist/clients/review-graph/import-resolvers.js +21 -10
- package/dist/clients/review-graph/persist-worker.js +10 -0
- package/dist/clients/review-graph/query.js +5 -0
- package/dist/clients/review-graph/shared-extraction-ir.js +90 -0
- package/dist/clients/review-graph/symbol-id.js +46 -0
- package/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/dist/clients/review-graph/workspace-modules.js +51 -3
- package/dist/clients/review-graph-logger.js +53 -0
- package/dist/clients/ruff-client.js +27 -44
- package/dist/clients/runtime-agent-end.js +111 -75
- package/dist/clients/runtime-context.js +36 -12
- package/dist/clients/runtime-coordinator.js +76 -8
- package/dist/clients/runtime-session.js +799 -137
- package/dist/clients/runtime-tool-call.js +117 -62
- package/dist/clients/runtime-tool-result.js +218 -23
- package/dist/clients/runtime-turn.js +284 -40
- package/dist/clients/rust-client.js +2 -1
- package/dist/clients/safe-spawn.js +873 -71
- package/dist/clients/security-scan-client.js +2 -1
- package/dist/clients/session-start-observability.js +15 -0
- package/dist/clients/session-state-store.js +5 -3
- package/dist/clients/sessionstart-logger.js +18 -0
- package/dist/clients/sg-runner.js +284 -159
- package/dist/clients/slow-fs.js +2 -1
- package/dist/clients/smells-rollup.js +224 -0
- package/dist/clients/source-filter.js +231 -43
- package/dist/clients/source-walker.js +66 -10
- package/dist/clients/startup-scan.js +15 -16
- package/dist/clients/startup-timing.js +9 -0
- package/dist/clients/test-runner-client.js +38 -8
- package/dist/clients/todo-scanner.js +30 -3
- package/dist/clients/tool-event.js +35 -0
- package/dist/clients/tool-policy.js +388 -39
- package/dist/clients/tool-render.js +155 -0
- package/dist/clients/tree-sitter-cache.js +153 -140
- package/dist/clients/tree-sitter-client.js +1722 -80
- package/dist/clients/tree-sitter-logger.js +64 -1
- package/dist/clients/tree-sitter-query-loader.js +117 -12
- package/dist/clients/tree-sitter-shared.js +48 -19
- package/dist/clients/tree-sitter-symbol-extractor.js +115 -11
- package/dist/clients/user-notify.js +51 -0
- package/dist/clients/vanished-instance-marker.js +57 -0
- package/dist/clients/warm-attach.js +33 -13
- package/dist/clients/widget-state.js +342 -49
- package/dist/clients/word-index-logger.js +44 -0
- package/dist/clients/word-index.js +612 -68
- package/dist/clients/workspace-topology.js +100 -7
- package/dist/index.js +65923 -47161
- package/dist/mcp/analyze-cli.js +185 -29
- package/dist/mcp/cli.js +110 -0
- package/dist/mcp/server.js +272 -50
- package/dist/tools/ast-grep-search.js +93 -27
- package/dist/tools/lens-diagnostics.js +247 -41
- package/dist/tools/lsp-diagnostics.js +153 -40
- package/dist/tools/lsp-navigation.js +51 -31
- package/dist/tools/module-report.js +9 -0
- package/dist/tools/symbol-search.js +21 -2
- package/docs/agent-guide.md +247 -0
- package/docs/agent-tools.md +21 -10
- package/docs/analysisall.md +192 -0
- package/docs/api-ports-inventory.md +165 -0
- package/docs/ast-grep_rules_catalog.md +5 -2
- package/docs/astplayground.md +3 -4
- package/docs/audit1.md +5 -5
- package/docs/durable-store-audit-1202.md +32 -0
- package/docs/environment-variables.md +197 -11
- package/docs/fable.md +27 -0
- package/docs/features.md +35 -5
- package/docs/globalconfig.md +101 -5
- package/docs/language-coverage.md +6 -5
- package/docs/lsp-capability-matrix.md +2 -2
- package/docs/mcp.md +54 -20
- package/docs/servercapabilities.md +7 -4
- package/docs/settings.md +192 -0
- package/docs/tools.md +18 -1
- package/docs/tree-sitter_rules_catalog.md +11 -31
- package/docs/usage.md +31 -0
- package/package.json +139 -138
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-js-test.yml +1 -0
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-test.yml +2 -0
- package/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-test.yml +4 -0
- package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +22 -4
- package/rules/ast-grep-rules/rules/hardcoded-url.yml +22 -4
- package/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +4 -3
- package/rules/ast-grep-rules/rules/no-case-declarations.yml +4 -3
- package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +10 -0
- package/rules/ast-grep-rules/rules/no-console-except-error.yml +10 -0
- package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +42 -6
- package/rules/ast-grep-rules/rules/no-javascript-url.yml +42 -6
- package/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/rules/ast-grep-rules/rules/strict-equality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-equality.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality.yml +14 -1
- package/rules/tree-sitter-queries/cpp/no-memset-sensitive-data.yml +9 -3
- package/rules/tree-sitter-queries/cpp/no-scoped-lock-without-args.yml +6 -6
- package/rules/tree-sitter-queries/css/calc-spacing.yml +8 -6
- package/rules/tree-sitter-queries/go/go-mutex-copy.yml +21 -18
- package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +3 -3
- package/rules/tree-sitter-queries/java/infinite-loop-java.yml +3 -3
- package/rules/tree-sitter-queries/java/junit-call-super.yml +6 -4
- package/rules/tree-sitter-queries/java/no-double-checked-locking.yml +10 -8
- package/rules/tree-sitter-queries/java/no-field-shadowing.yml +1 -1
- package/rules/tree-sitter-queries/java/prepared-statement-valid-indices.yml +2 -3
- package/rules/tree-sitter-queries/java/resources-closed.yml +2 -2
- package/rules/tree-sitter-queries/java/springboot-default-package.yml +6 -3
- package/rules/tree-sitter-queries/java/switch-fall-through.yml +6 -4
- package/rules/tree-sitter-queries/java/switch-non-case-labels.yml +4 -3
- package/rules/tree-sitter-queries/java/tests-include-assertions.yml +6 -5
- package/rules/tree-sitter-queries/javascript/switch-case-termination-js.yml +5 -8
- package/rules/tree-sitter-queries/javascript/switch-non-case-labels.yml +3 -2
- package/rules/tree-sitter-queries/kotlin/prepared-statement-indices.yml +9 -5
- package/rules/tree-sitter-queries/php/this-in-static-context.yml +5 -5
- package/rules/tree-sitter-queries/python/exit-signature-check.yml +2 -9
- package/rules/tree-sitter-queries/python/in-operator-unsupported.yml +3 -4
- package/rules/tree-sitter-queries/python/no-super-torchscript.yml +4 -8
- package/rules/tree-sitter-queries/python/notimplemented-boolean-context.yml +1 -5
- package/rules/tree-sitter-queries/python/python-empty-except.yml +1 -1
- package/rules/tree-sitter-queries/python/return-in-init.yml +0 -2
- package/rules/tree-sitter-queries/python/yield-return-outside-function.yml +0 -3
- package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +1 -12
- package/rules/tree-sitter-queries/ruby/ruby-eval.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +4 -4
- package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +4 -11
- package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-string-eval.yml +2 -3
- package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +4 -4
- package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +4 -3
- package/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/rules/tree-sitter-queries/typescript/console-statement.yml +11 -0
- package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +4 -9
- package/rules/tree-sitter-queries/typescript/empty-switch-case.yml +13 -9
- package/rules/tree-sitter-queries/typescript/infinite-loop.yml +2 -2
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +10 -1
- package/rules/tree-sitter-queries/typescript/switch-case-termination.yml +5 -8
- package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +7 -10
- package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +4 -2
- package/rules/typos/_typos.toml +17 -0
- package/scripts/analyze-pi-lens-logs.mjs +55 -2
- package/skills/pi-lens-write-tree-sitter-rule/SKILL.md +5 -0
- package/dist/clients/ast-grep-parser.js +0 -86
- package/dist/clients/config-enabled-shape.js +0 -39
- package/dist/clients/lsp/interactive-install.js +0 -367
- package/dist/clients/lsp/lsp-index.js +0 -10
- package/dist/clients/lsp/server-strategies.js +0 -5
- package/dist/clients/project-metadata.js +0 -690
- package/dist/clients/source-groups.js +0 -96
- package/dist/clients/types.js +0 -11
- package/docs/env_variables.md +0 -10
- package/rules/tree-sitter-queries/abap-disabled/delete-where.yml +0 -46
- package/rules/tree-sitter-queries/cpp/no-confused-move-forward.yml +0 -59
- package/rules/tree-sitter-queries/cpp/noexcept-functions.yml +0 -58
- package/rules/tree-sitter-queries/java/mockito-initialized.yml +0 -66
- package/rules/tree-sitter-queries/java/no-wait-notify-on-thread.yml +0 -58
- package/rules/tree-sitter-queries/java/short-circuit-logic.yml +0 -57
- package/rules/tree-sitter-queries/java/spring-session-attributes-setcomplete.yml +0 -75
- package/rules/tree-sitter-queries/plsql-disabled/delete-update-where.yml +0 -56
- package/rules/tree-sitter-queries/plsql-disabled/end-loop-semicolon.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/fetch-bulk-collect-limit.yml +0 -47
- package/rules/tree-sitter-queries/plsql-disabled/forallsave-exceptions.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/nchar-nvarchar2-bytes.yml +0 -54
- package/rules/tree-sitter-queries/plsql-disabled/not-null-initialization.yml +0 -59
- package/rules/tree-sitter-queries/plsql-disabled/raise-application-error-codes.yml +0 -50
- package/rules/tree-sitter-queries/typescript-disabled/ts-path-traversal.yml +0 -71
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { applyConservativeActionableWarningFixes, checkActionableWarningsReportFresh, } from "./actionable-warnings.js";
|
|
4
|
-
import { logLatency } from "./latency-logger.js";
|
|
5
|
-
import { resyncLspFile, runFormatPhase } from "./pipeline.js";
|
|
6
4
|
import { publishFilesTouched } from "./bus-publish.js";
|
|
7
5
|
import { publishAutofixStart, publishFormatStart, } from "./format-events-publish.js";
|
|
6
|
+
import { logLatency } from "./latency-logger.js";
|
|
7
|
+
import { isPathIgnoredByProject } from "./file-utils.js";
|
|
8
|
+
import { newLspMutationCorrelationId } from "./lsp-mutation.js";
|
|
9
|
+
import { getGlobalActionableWarningMaxFixes, } from "./lens-config.js";
|
|
10
|
+
import { resyncLspFile, runFormatPhase } from "./pipeline.js";
|
|
11
|
+
import { getAmbientAbortSignal } from "./safe-spawn.js";
|
|
8
12
|
import { appendProjectChange, } from "./project-changes.js";
|
|
9
13
|
/**
|
|
10
14
|
* A queued file is claimed by any flush once it has sat unclaimed by its own
|
|
@@ -15,6 +19,7 @@ import { appendProjectChange, } from "./project-changes.js";
|
|
|
15
19
|
* extra staleness for guaranteed eventual formatting. (#791)
|
|
16
20
|
*/
|
|
17
21
|
export const DEFERRED_FORMAT_STALE_AFTER_MS = 10 * 60_000;
|
|
22
|
+
const DEFERRED_FORMAT_CONCURRENCY = 3;
|
|
18
23
|
function recordProjectChange(args) {
|
|
19
24
|
const bump = args.runtime.bumpFileSeq;
|
|
20
25
|
if (!bump)
|
|
@@ -137,43 +142,63 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
137
142
|
return !disabled;
|
|
138
143
|
});
|
|
139
144
|
if (formatRecords.length > 0) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
145
|
+
const work = new Array(formatRecords.length);
|
|
146
|
+
const started = new Set();
|
|
147
|
+
let nextIndex = 0;
|
|
148
|
+
const ambientSignal = getAmbientAbortSignal();
|
|
149
|
+
const worker = async () => {
|
|
150
|
+
while (nextIndex < formatRecords.length) {
|
|
151
|
+
const index = nextIndex++;
|
|
152
|
+
if (ambientSignal?.aborted)
|
|
153
|
+
return;
|
|
154
|
+
const record = formatRecords[index];
|
|
155
|
+
const fileStart = Date.now();
|
|
156
|
+
const filePath = path.resolve(record.filePath);
|
|
157
|
+
started.add(index);
|
|
158
|
+
if (!nodeFs.existsSync(filePath)) {
|
|
159
|
+
work[index] = { record, filePath, fileStart, missing: true };
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
work[index] = {
|
|
164
|
+
record,
|
|
165
|
+
filePath,
|
|
166
|
+
fileStart,
|
|
167
|
+
result: await runFormatPhase(filePath, getFormatService, dbg),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
work[index] = {
|
|
172
|
+
record,
|
|
173
|
+
filePath,
|
|
174
|
+
fileStart,
|
|
175
|
+
error: err instanceof Error ? err.message : String(err),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
157
178
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
};
|
|
180
|
+
await Promise.all(Array.from({ length: Math.min(DEFERRED_FORMAT_CONCURRENCY, formatRecords.length) }, () => worker()));
|
|
181
|
+
if (ambientSignal?.aborted) {
|
|
182
|
+
runtime.requeueDeferredFormatFiles(formatRecords.filter((_, index) => !started.has(index)));
|
|
183
|
+
}
|
|
184
|
+
for (const entry of work) {
|
|
185
|
+
if (!entry)
|
|
186
|
+
continue;
|
|
187
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
188
|
+
const { record, filePath, fileStart } = entry;
|
|
189
|
+
if (entry.missing) {
|
|
190
|
+
dbg(`agent_end deferred_format skipped missing file: ${filePath}`);
|
|
191
|
+
summary.skipped.push({ filePath, reason: "missing" });
|
|
167
192
|
continue;
|
|
168
193
|
}
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
errors: [outcome.message],
|
|
173
|
-
});
|
|
194
|
+
if (entry.error) {
|
|
195
|
+
dbg(`agent_end deferred_format failed for ${filePath}: ${entry.error}`);
|
|
196
|
+
summary.failed.push({ filePath, errors: [entry.error] });
|
|
174
197
|
continue;
|
|
175
198
|
}
|
|
176
|
-
const
|
|
199
|
+
const result = entry.result;
|
|
200
|
+
if (!result)
|
|
201
|
+
continue;
|
|
177
202
|
summary.formatted++;
|
|
178
203
|
if (result.formatFailures.length > 0) {
|
|
179
204
|
summary.failed.push({ filePath, errors: result.formatFailures });
|
|
@@ -201,7 +226,7 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
201
226
|
const content = nodeFs.readFileSync(filePath, "utf-8");
|
|
202
227
|
const lineCount = content.split("\n").length;
|
|
203
228
|
const hasImports = /^import\s/m.test(content);
|
|
204
|
-
cacheManager.addModifiedRange(filePath, { start: 1, end: lineCount }, hasImports, bookkeepingCwd);
|
|
229
|
+
cacheManager.addModifiedRange(filePath, { start: 1, end: lineCount }, hasImports, bookkeepingCwd, currentSessionId ?? runtime.telemetrySessionId, "pi");
|
|
205
230
|
}
|
|
206
231
|
catch (err) {
|
|
207
232
|
dbg(`agent_end deferred_format modified-range tracking failed for ${filePath}: ${err}`);
|
|
@@ -250,6 +275,14 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
250
275
|
}
|
|
251
276
|
else {
|
|
252
277
|
const enabledFiles = actionReport.data.files.filter((file) => {
|
|
278
|
+
// #1247: the per-edit dispatch surface gates ignored files before
|
|
279
|
+
// running; the turn-end autofix must apply the same filter or a
|
|
280
|
+
// `.pi-lens.json` `ignore` entry cannot protect a prose file
|
|
281
|
+
// (CHANGELOG.md / AGENTS.md) from being rewritten here.
|
|
282
|
+
if (isPathIgnoredByProject(file.filePath, ctxCwd ?? runtime.projectRoot)) {
|
|
283
|
+
dbg(`agent_end actionable_warnings_autofix: skipped ${file.filePath} (ignored by project)`);
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
253
286
|
const enabled = !!getFlag("lens-actionable-warning-autofix", file.filePath);
|
|
254
287
|
if (!enabled) {
|
|
255
288
|
const source = getFlagSource?.("lens-actionable-warning-autofix", file.filePath);
|
|
@@ -280,48 +313,42 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
280
313
|
if (!freshness.fresh) {
|
|
281
314
|
dbg(`agent_end actionable_warnings_autofix: stale report (${freshness.reason}; reportProjectSeqEnd=${freshness.reportProjectSeqEnd ?? "missing"}; currentProjectSeq=${freshness.currentProjectSeq}${freshness.filePath ? `; file=${freshness.filePath}; reportFileSeq=${freshness.reportFileSeq}; currentFileSeq=${freshness.currentFileSeq}` : ""}), skipping fixes`);
|
|
282
315
|
}
|
|
283
|
-
else {
|
|
316
|
+
else if (eligibleReport.summary.autoFixEligible > 0) {
|
|
284
317
|
// #684: same "genuine work about to happen" gate as
|
|
285
318
|
// publishFormatStart — only fires when the report is fresh AND
|
|
286
319
|
// has at least one autofix-eligible warning, right before
|
|
287
320
|
// applyConservativeActionableWarningFixes actually starts.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
});
|
|
298
|
-
}
|
|
321
|
+
publishAutofixStart({
|
|
322
|
+
cwd: ctxCwd ?? runtime.projectRoot,
|
|
323
|
+
paths: eligibleReport.files
|
|
324
|
+
.filter((file) => file.warnings.some((warning) => !warning.suppressed &&
|
|
325
|
+
warning.actions.some((action) => action.autoFixEligible)))
|
|
326
|
+
.map((file) => file.filePath),
|
|
327
|
+
eligibleCount: eligibleReport.summary.autoFixEligible,
|
|
328
|
+
dbg,
|
|
329
|
+
});
|
|
299
330
|
const fixStart = Date.now();
|
|
331
|
+
const fixCwd = ctxCwd ?? runtime.projectRoot;
|
|
332
|
+
const mutationContext = {
|
|
333
|
+
cwd: fixCwd,
|
|
334
|
+
correlationId: newLspMutationCorrelationId(),
|
|
335
|
+
tool: "lsp-quickfix",
|
|
336
|
+
source: "autofix",
|
|
337
|
+
runtime,
|
|
338
|
+
cacheManager,
|
|
339
|
+
readGuard: getFlag("no-read-guard") ? undefined : runtime.readGuard,
|
|
340
|
+
recordAutofix: getFlag("lens-turn-summary")
|
|
341
|
+
? (filePath) => runtime.turnSummary.recordAutofix(filePath, { tool: "lsp-quickfix" })
|
|
342
|
+
: undefined,
|
|
343
|
+
dbg,
|
|
344
|
+
};
|
|
300
345
|
const fixSummary = await applyConservativeActionableWarningFixes({
|
|
301
|
-
cwd:
|
|
346
|
+
cwd: fixCwd,
|
|
302
347
|
report: eligibleReport,
|
|
348
|
+
maxFixes: getGlobalActionableWarningMaxFixes(),
|
|
303
349
|
dbg,
|
|
350
|
+
mutationContext,
|
|
304
351
|
});
|
|
305
|
-
for (const changedFile of fixSummary.changedFiles) {
|
|
306
|
-
if (!nodeFs.existsSync(changedFile))
|
|
307
|
-
continue;
|
|
308
|
-
recordProjectChange({
|
|
309
|
-
runtime,
|
|
310
|
-
cwd: ctxCwd ?? runtime.projectRoot,
|
|
311
|
-
filePath: changedFile,
|
|
312
|
-
source: "autofix",
|
|
313
|
-
dbg,
|
|
314
|
-
});
|
|
315
|
-
if (!getFlag("no-read-guard"))
|
|
316
|
-
runtime.readGuard.recordWritten(changedFile);
|
|
317
|
-
try {
|
|
318
|
-
const content = nodeFs.readFileSync(changedFile, "utf-8");
|
|
319
|
-
cacheManager.addModifiedRange(changedFile, { start: 1, end: content.split("\n").length }, /^import\s/m.test(content), ctxCwd ?? runtime.projectRoot);
|
|
320
|
-
}
|
|
321
|
-
catch (err) {
|
|
322
|
-
dbg(`agent_end actionable warning changed-file tracking failed for ${changedFile}: ${err}`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
352
|
if (fixSummary.changedFiles.length > 0) {
|
|
326
353
|
publishFilesTouched({
|
|
327
354
|
reason: "autofix",
|
|
@@ -334,13 +361,6 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
334
361
|
kind: "autofix",
|
|
335
362
|
})),
|
|
336
363
|
});
|
|
337
|
-
if (getFlag("lens-turn-summary")) {
|
|
338
|
-
for (const changedFile of fixSummary.changedFiles) {
|
|
339
|
-
runtime.turnSummary.recordAutofix(changedFile, {
|
|
340
|
-
tool: "lsp-quickfix",
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
364
|
}
|
|
345
365
|
logLatency({
|
|
346
366
|
type: "phase",
|
|
@@ -359,6 +379,9 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
359
379
|
notify(`pi-lens applied ${fixSummary.applied} conservative LSP warning quickfix(es)`, "info");
|
|
360
380
|
}
|
|
361
381
|
}
|
|
382
|
+
else {
|
|
383
|
+
dbg("agent_end actionable_warnings_autofix: fresh report, 0 autofix-eligible warnings, skipping");
|
|
384
|
+
}
|
|
362
385
|
}
|
|
363
386
|
}
|
|
364
387
|
logLatency({
|
|
@@ -375,6 +398,19 @@ export async function handleAgentEnd({ ctxCwd, getFlag, getFlagSource, notify, d
|
|
|
375
398
|
skipped: summary.skipped.length,
|
|
376
399
|
},
|
|
377
400
|
});
|
|
401
|
+
logLatency({
|
|
402
|
+
type: "phase",
|
|
403
|
+
toolName: "agent_end",
|
|
404
|
+
filePath: ctxCwd ?? runtime.projectRoot,
|
|
405
|
+
phase: "agent_end_deferred_format_done",
|
|
406
|
+
durationMs: Date.now() - startedAt,
|
|
407
|
+
metadata: {
|
|
408
|
+
formatted: summary.formatted,
|
|
409
|
+
changed: summary.changed.length,
|
|
410
|
+
failed: summary.failed.length,
|
|
411
|
+
skipped: summary.skipped.length,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
378
414
|
dbg(`agent_end deferred_format complete: formatted=${summary.formatted} changed=${summary.changed.length} failed=${summary.failed.length} skipped=${summary.skipped.length}`);
|
|
379
415
|
if (summary.failed.length > 0) {
|
|
380
416
|
notify(`pi-lens deferred format: ${summary.changed.length} changed, ${summary.failed.length} failed`, "warning");
|
|
@@ -1,30 +1,54 @@
|
|
|
1
|
+
// Exported so the Stop-hook bin strips exactly what these bridges prepend.
|
|
2
|
+
export const AUTOMATION_FRAMING = "[pi-lens automated check — not a user request] ";
|
|
3
|
+
function turnEndMessage(content) {
|
|
4
|
+
return {
|
|
5
|
+
role: "user",
|
|
6
|
+
content: `${AUTOMATION_FRAMING}Address 🔴 blockers before continuing; ℹ️ advisories are informational only.\n\n${content}`,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/** Read a turn-end finding without changing its durable delivery state. */
|
|
10
|
+
export function peekTurnEndFindings(cacheManager, cwd) {
|
|
11
|
+
const findings = cacheManager.readCache("turn-end-findings", cwd);
|
|
12
|
+
if (!findings?.data?.content || findings.data.consumed === true)
|
|
13
|
+
return;
|
|
14
|
+
return { messages: [turnEndMessage(findings.data.content)] };
|
|
15
|
+
}
|
|
1
16
|
export function consumeTurnEndFindings(cacheManager, cwd) {
|
|
2
17
|
const findings = cacheManager.readCache("turn-end-findings", cwd);
|
|
18
|
+
if (!findings?.data?.content || findings.data.consumed === true)
|
|
19
|
+
return;
|
|
20
|
+
// A blocker record is also the opt-in commit gate's durable state. Mark the
|
|
21
|
+
// context message consumed without deleting the record; clean/advisory-only
|
|
22
|
+
// records retain the historical consume-and-clear behavior.
|
|
23
|
+
if (findings.data.hasBlockers === true &&
|
|
24
|
+
typeof findings.data.sessionId === "string") {
|
|
25
|
+
cacheManager.writeCache("turn-end-findings", { ...findings.data, consumed: true }, cwd);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
cacheManager.clearCache("turn-end-findings", cwd);
|
|
29
|
+
}
|
|
30
|
+
return { messages: [turnEndMessage(findings.data.content)] };
|
|
31
|
+
}
|
|
32
|
+
/** Read test findings without consuming them; used by acknowledged IPC delivery. */
|
|
33
|
+
export function peekTestFindings(cacheManager, cwd) {
|
|
34
|
+
const findings = cacheManager.readCache("test-runner-findings", cwd);
|
|
3
35
|
if (!findings?.data?.content)
|
|
4
36
|
return;
|
|
5
|
-
cacheManager.writeCache("turn-end-findings", null, cwd);
|
|
6
37
|
return {
|
|
7
38
|
messages: [
|
|
8
39
|
{
|
|
9
40
|
role: "user",
|
|
10
|
-
content:
|
|
41
|
+
content: `${AUTOMATION_FRAMING}Test failures detected last turn — fix before continuing:\n\n${findings.data.content}`,
|
|
11
42
|
},
|
|
12
43
|
],
|
|
13
44
|
};
|
|
14
45
|
}
|
|
15
46
|
export function consumeTestFindings(cacheManager, cwd) {
|
|
16
|
-
const findings = cacheManager
|
|
17
|
-
if (!findings
|
|
47
|
+
const findings = peekTestFindings(cacheManager, cwd);
|
|
48
|
+
if (!findings)
|
|
18
49
|
return;
|
|
19
50
|
cacheManager.writeCache("test-runner-findings", null, cwd);
|
|
20
|
-
return
|
|
21
|
-
messages: [
|
|
22
|
-
{
|
|
23
|
-
role: "user",
|
|
24
|
-
content: `[pi-lens automated check — not a user request] Test failures detected last turn — fix before continuing:\n\n${findings.data.content}`,
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
};
|
|
51
|
+
return findings;
|
|
28
52
|
}
|
|
29
53
|
export function consumeSessionStartGuidance(cacheManager, cwd) {
|
|
30
54
|
const guidance = cacheManager.readCache("session-start-guidance", cwd);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { normalizeMapKey } from "./path-utils.js";
|
|
5
|
+
import { PathKeyedMap } from "./path-keyed-map.js";
|
|
4
6
|
import { ReadGuard } from "./read-guard.js";
|
|
5
7
|
import { RUNTIME_CONFIG } from "./runtime-config.js";
|
|
6
8
|
import { TurnSummaryCollector } from "./turn-summary.js";
|
|
@@ -45,12 +47,13 @@ export class RuntimeCoordinator {
|
|
|
45
47
|
_fileLastProjectSeq = new Map();
|
|
46
48
|
_gitGuardHasBlockers = false;
|
|
47
49
|
_gitGuardSummary = "";
|
|
50
|
+
_gitGuardCacheUnknownReason;
|
|
48
51
|
callGraph = null;
|
|
49
52
|
wordIndex = null;
|
|
50
53
|
_readGuard = null;
|
|
51
54
|
_pendingDeferredFormatFiles = new Map();
|
|
52
55
|
_lspReadWarmState = new Map();
|
|
53
|
-
_pendingInlineBlockers = new
|
|
56
|
+
_pendingInlineBlockers = new PathKeyedMap(normalizeMapKey);
|
|
54
57
|
_actionableWarningsThisTurn = new Map();
|
|
55
58
|
_codeQualityWarningsThisTurn = new Map();
|
|
56
59
|
// #484: opt-in per-RUN summary of diagnostics/autofixes/formats,
|
|
@@ -59,9 +62,9 @@ export class RuntimeCoordinator {
|
|
|
59
62
|
// (cheap, empty Map) but callers gate recording behind the
|
|
60
63
|
// `lens-turn-summary` flag so it's a true no-op when the feature is off.
|
|
61
64
|
_turnSummary = new TurnSummaryCollector();
|
|
62
|
-
resetForSession() {
|
|
65
|
+
resetForSession(startedAt = Date.now()) {
|
|
63
66
|
this._sessionGeneration += 1;
|
|
64
|
-
this._sessionStartedAt =
|
|
67
|
+
this._sessionStartedAt = startedAt;
|
|
65
68
|
this._complexityBaselines.clear();
|
|
66
69
|
this._pipelineCrashCounts.clear();
|
|
67
70
|
this._cachedExports.clear();
|
|
@@ -87,6 +90,7 @@ export class RuntimeCoordinator {
|
|
|
87
90
|
this._fileLastProjectSeq.clear();
|
|
88
91
|
this._gitGuardHasBlockers = false;
|
|
89
92
|
this._gitGuardSummary = "";
|
|
93
|
+
this._gitGuardCacheUnknownReason = undefined;
|
|
90
94
|
this._readGuard = null;
|
|
91
95
|
this._pendingDeferredFormatFiles.clear();
|
|
92
96
|
this._lspReadWarmState.clear();
|
|
@@ -107,8 +111,12 @@ export class RuntimeCoordinator {
|
|
|
107
111
|
this._cascadeSessionStats.coldSnapshotTouches += coldSnapshotTouches;
|
|
108
112
|
}
|
|
109
113
|
updateGitGuardStatus(hasBlockers, output) {
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
// The status is an aggregate over the current per-file map. A clean B
|
|
115
|
+
// result must not erase an unresolved A result; the pipeline records/clears
|
|
116
|
+
// the edited file immediately before this method runs.
|
|
117
|
+
this._gitGuardHasBlockers =
|
|
118
|
+
hasBlockers || this.getInlineBlockersSnapshot().length > 0;
|
|
119
|
+
if (!this._gitGuardHasBlockers) {
|
|
112
120
|
this._gitGuardSummary = "";
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
@@ -116,7 +124,10 @@ export class RuntimeCoordinator {
|
|
|
116
124
|
.split("\n")
|
|
117
125
|
.map((line) => line.trim())
|
|
118
126
|
.find((line) => line.length > 0);
|
|
119
|
-
|
|
127
|
+
const summaries = this.getInlineBlockersSnapshot()
|
|
128
|
+
.map((entry) => entry.summary.trim())
|
|
129
|
+
.filter(Boolean);
|
|
130
|
+
this._gitGuardSummary = (summaries[0] ?? firstLine ?? "Unresolved blockers detected").slice(0, 160);
|
|
120
131
|
}
|
|
121
132
|
get gitGuardHasBlockers() {
|
|
122
133
|
return this._gitGuardHasBlockers;
|
|
@@ -124,6 +135,15 @@ export class RuntimeCoordinator {
|
|
|
124
135
|
get gitGuardSummary() {
|
|
125
136
|
return this._gitGuardSummary;
|
|
126
137
|
}
|
|
138
|
+
markGitGuardCacheUnknown(reason) {
|
|
139
|
+
this._gitGuardCacheUnknownReason = reason;
|
|
140
|
+
}
|
|
141
|
+
clearGitGuardCacheUnknown() {
|
|
142
|
+
this._gitGuardCacheUnknownReason = undefined;
|
|
143
|
+
}
|
|
144
|
+
get gitGuardCacheUnknownReason() {
|
|
145
|
+
return this._gitGuardCacheUnknownReason;
|
|
146
|
+
}
|
|
127
147
|
beginTurn() {
|
|
128
148
|
this._cascadeRuns = [];
|
|
129
149
|
// _pendingCascadeRuns is deliberately NOT cleared here: a cascade compute
|
|
@@ -131,7 +151,9 @@ export class RuntimeCoordinator {
|
|
|
131
151
|
// measured up to ~19s) must surface on the NEXT turn_end, not be dropped —
|
|
132
152
|
// pre-#450 those findings were always awaited, never lost. Session reset
|
|
133
153
|
// still clears it.
|
|
134
|
-
|
|
154
|
+
// Inline blockers are session-scoped per-file state. They are cleared only
|
|
155
|
+
// when that file is re-analyzed clean or the session resets; a new turn must
|
|
156
|
+
// not let a clean unrelated file erase an unresolved blocker.
|
|
135
157
|
this._actionableWarningsThisTurn.clear();
|
|
136
158
|
this._codeQualityWarningsThisTurn.clear();
|
|
137
159
|
// _turnSummary is deliberately NOT cleared here (#484 rework): the
|
|
@@ -365,8 +387,45 @@ export class RuntimeCoordinator {
|
|
|
365
387
|
clearInlineBlockers(filePath) {
|
|
366
388
|
this._pendingInlineBlockers.delete(path.resolve(filePath));
|
|
367
389
|
}
|
|
390
|
+
reconcileInlineBlockers() {
|
|
391
|
+
// Rebuild, never delete-in-place: `PathKeyedMap.delete()` re-normalizes
|
|
392
|
+
// the key, and `normalizeMapKey` realpaths a live file but lowercases
|
|
393
|
+
// the tail of a deleted one — so on Windows a mixed-case filename gets
|
|
394
|
+
// a DIFFERENT delete-time key than its set-time key and the delete
|
|
395
|
+
// misses (#1245, verified live: `MyCase.ts` survived reconcile).
|
|
396
|
+
// Existence-checking the display path and rebuilding survivors avoids
|
|
397
|
+
// the key-mismatch entirely; live survivors re-set to identical keys
|
|
398
|
+
// (both realpath), so only the stale entries are dropped.
|
|
399
|
+
const survivors = [];
|
|
400
|
+
for (const [displayPath, value] of this._pendingInlineBlockers.entries()) {
|
|
401
|
+
if (fs.existsSync(displayPath))
|
|
402
|
+
survivors.push([displayPath, value]);
|
|
403
|
+
}
|
|
404
|
+
if (survivors.length !== this._pendingInlineBlockers.size) {
|
|
405
|
+
this._pendingInlineBlockers.clear();
|
|
406
|
+
for (const [displayPath, value] of survivors) {
|
|
407
|
+
this._pendingInlineBlockers.set(displayPath, value);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Stale-entry reconcile (#1245): a blocker recorded for a file that has
|
|
413
|
+
* since been deleted can never be cleared — `clearInlineBlockers` fires
|
|
414
|
+
* only on a LATER dispatch of the same path, which a deleted file never
|
|
415
|
+
* gets. Every read of the blocker map (turn_end injection, git-guard
|
|
416
|
+
* size/summary, `syncGitGuardRecord`) therefore drops entries whose file no
|
|
417
|
+
* longer exists on disk: a blocker for a deleted file is stale by
|
|
418
|
+
* definition (the agent cannot fix it), so it must not re-surface every
|
|
419
|
+
* turn or gate a commit. The map is tiny (per-turn blockers) and reads are
|
|
420
|
+
* bounded (once per turn_end / tool_result), so the probe cost is
|
|
421
|
+
* negligible.
|
|
422
|
+
*/
|
|
423
|
+
getInlineBlockersSnapshot() {
|
|
424
|
+
this.reconcileInlineBlockers();
|
|
425
|
+
return [...this._pendingInlineBlockers.values()];
|
|
426
|
+
}
|
|
368
427
|
consumeInlineBlockers() {
|
|
369
|
-
const entries =
|
|
428
|
+
const entries = this.getInlineBlockersSnapshot();
|
|
370
429
|
this._pendingInlineBlockers.clear();
|
|
371
430
|
return entries;
|
|
372
431
|
}
|
|
@@ -500,6 +559,15 @@ export class RuntimeCoordinator {
|
|
|
500
559
|
}
|
|
501
560
|
return { claimed, staleClaimed, deferredToOwner };
|
|
502
561
|
}
|
|
562
|
+
/** Return claimed records that were never started by an aborted drain. */
|
|
563
|
+
requeueDeferredFormatFiles(records) {
|
|
564
|
+
for (const record of records) {
|
|
565
|
+
const key = path.resolve(record.filePath);
|
|
566
|
+
if (!this._pendingDeferredFormatFiles.has(key)) {
|
|
567
|
+
this._pendingDeferredFormatFiles.set(key, record);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
503
571
|
shouldWarmLspOnRead(filePath, maxAgeMs = 120_000) {
|
|
504
572
|
const state = this._lspReadWarmState.get(path.resolve(filePath));
|
|
505
573
|
if (!state)
|