@herbertgao/pi-extensions 2026.8.3 → 2026.8.5
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/README.md +45 -4
- package/examples/pi-footer.json +275 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +8 -7
- package/node_modules/@narumitw/pi-btw/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
- package/node_modules/pi-footer/CHANGELOG.md +145 -0
- package/node_modules/pi-footer/LICENSE +21 -0
- package/node_modules/pi-footer/README.md +354 -0
- package/node_modules/pi-footer/package.json +68 -0
- package/node_modules/pi-footer/src/cache.ts +106 -0
- package/node_modules/pi-footer/src/colors.ts +237 -0
- package/node_modules/pi-footer/src/config.ts +202 -0
- package/node_modules/pi-footer/src/event-widgets.ts +41 -0
- package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
- package/node_modules/pi-footer/src/git.ts +135 -0
- package/node_modules/pi-footer/src/index.ts +268 -0
- package/node_modules/pi-footer/src/metrics.ts +141 -0
- package/node_modules/pi-footer/src/presets.ts +383 -0
- package/node_modules/pi-footer/src/render.ts +114 -0
- package/node_modules/pi-footer/src/separators.ts +52 -0
- package/node_modules/pi-footer/src/types.ts +122 -0
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
- package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
- package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
- package/node_modules/pi-footer/src/ui/events.ts +35 -0
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
- package/node_modules/pi-footer/src/ui/fields.ts +252 -0
- package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
- package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
- package/node_modules/pi-footer/src/ui/layout.ts +17 -0
- package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
- package/node_modules/pi-footer/src/ui/model.ts +64 -0
- package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
- package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
- package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
- package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
- package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
- package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
- package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
- package/node_modules/pi-footer/src/ui/screen.ts +243 -0
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
- package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
- package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
- package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
- package/node_modules/pi-footer/src/ui/theme.ts +48 -0
- package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
- package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
- package/node_modules/pi-footer/src/ui.ts +66 -0
- package/node_modules/pi-footer/src/widgets/context.ts +28 -0
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
- package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
- package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
- package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
- package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
- package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
- package/node_modules/pi-footer/src/widgets/options.ts +122 -0
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
- package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/store.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/types.ts +181 -0
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
- package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
- package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
- package/node_modules/pi-lens/CHANGELOG.md +2440 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
- package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
- package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
- package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +49361 -36888
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +211 -44
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/docs/agent-guide.md +2 -2
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +31 -2
- package/node_modules/pi-lens/docs/globalconfig.md +45 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +12 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +18 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
- package/node_modules/pi-mcp-adapter/README.md +34 -3
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +3 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +93 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/package.json +17 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { extractReadPathsFromCommand, extractWrittenPathsFromCommand, } from "./bash-file-access.js";
|
|
4
3
|
import { loadBootstrapClients } from "./bootstrap.js";
|
|
5
4
|
import { detectFileKind } from "./file-kinds.js";
|
|
6
5
|
import { isPathIgnoredByProject } from "./file-utils.js";
|
|
@@ -14,7 +13,7 @@ import { computeTrailingWhitespaceOldTextPatch, findUniqueMatchLineRange, } from
|
|
|
14
13
|
import { applyPartiallyApplicableEdits } from "./partial-edit-apply.js";
|
|
15
14
|
import { isExternalOrVendorFile } from "./path-utils.js";
|
|
16
15
|
import { EXPANSION_BUDGET_MS, EXPANSION_LIMIT_LINES, tryExpandRead, } from "./read-expansion.js";
|
|
17
|
-
import { logReadGuardEvent } from "./read-guard-logger.js";
|
|
16
|
+
import { boundedIndexesForCount, createReadGuardEditBatchSummary, getReadGuardCorrelationId, logReadGuardEvent, } from "./read-guard-logger.js";
|
|
18
17
|
import { countFileLines, getTouchedLinesForGuard, relocateEditRange, tryCorrectIndentationMismatch, tryCorrectIndentationMismatchFromContent, } from "./read-guard-tool-lines.js";
|
|
19
18
|
import { handleToolResult } from "./runtime-tool-result.js";
|
|
20
19
|
import { isToolCallEventType } from "./tool-event.js";
|
|
@@ -142,7 +141,42 @@ function getNewContentFromToolCall(event) {
|
|
|
142
141
|
}
|
|
143
142
|
export async function handleToolCall(deps) {
|
|
144
143
|
const { event, ctx, lensEnabled, getFlag, dbg, runtime, cacheManager, ensureLSPConfigInitialized, updateLspStatus, resetLSPService, getTreeSitterClient = getSharedTreeSitterClient, } = deps;
|
|
144
|
+
const readGuardCorrelationId = getReadGuardCorrelationId(event);
|
|
145
|
+
let filePath;
|
|
146
|
+
const logToolReadGuardEvent = (entry) => logReadGuardEvent({ ...entry, correlationId: readGuardCorrelationId });
|
|
145
147
|
const toolName = event.toolName ?? "";
|
|
148
|
+
const editInputForTelemetry = event.input;
|
|
149
|
+
const requestedEditIndexes = toolName === "write"
|
|
150
|
+
? [0]
|
|
151
|
+
: Array.isArray(editInputForTelemetry?.edits)
|
|
152
|
+
? boundedIndexesForCount(editInputForTelemetry.edits.length)
|
|
153
|
+
: [0];
|
|
154
|
+
const logBlockedEditSummary = (source) => logToolReadGuardEvent({
|
|
155
|
+
event: "edit_batch_summary",
|
|
156
|
+
filePath: filePath ?? "",
|
|
157
|
+
metadata: {
|
|
158
|
+
tool: toolName,
|
|
159
|
+
source,
|
|
160
|
+
editBatchSummary: createReadGuardEditBatchSummary({
|
|
161
|
+
requestedIndexes: requestedEditIndexes,
|
|
162
|
+
requestedTotal: toolName === "write"
|
|
163
|
+
? 1
|
|
164
|
+
: Array.isArray(editInputForTelemetry?.edits)
|
|
165
|
+
? editInputForTelemetry.edits.length
|
|
166
|
+
: 1,
|
|
167
|
+
rejectedReasons: requestedEditIndexes.map((index) => ({
|
|
168
|
+
index,
|
|
169
|
+
code: "preflight_blocked",
|
|
170
|
+
})),
|
|
171
|
+
rejectedTotal: toolName === "write"
|
|
172
|
+
? 1
|
|
173
|
+
: Array.isArray(editInputForTelemetry?.edits)
|
|
174
|
+
? editInputForTelemetry.edits.length
|
|
175
|
+
: 1,
|
|
176
|
+
terminalStatus: "blocked",
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
});
|
|
146
180
|
if (!lensEnabled)
|
|
147
181
|
return;
|
|
148
182
|
if (getFlag("lens-guard") &&
|
|
@@ -156,7 +190,7 @@ export async function handleToolCall(deps) {
|
|
|
156
190
|
}
|
|
157
191
|
}
|
|
158
192
|
const rawFilePath = getToolCallRawFilePath(toolName, event);
|
|
159
|
-
|
|
193
|
+
filePath = resolveToolCallFilePath(rawFilePath, ctx.cwd, runtime.projectRoot);
|
|
160
194
|
if (!getFlag("no-lsp")) {
|
|
161
195
|
try {
|
|
162
196
|
const configCwd = filePath
|
|
@@ -312,7 +346,7 @@ export async function handleToolCall(deps) {
|
|
|
312
346
|
else {
|
|
313
347
|
enclosingSymbol = expansion.enclosingSymbol;
|
|
314
348
|
}
|
|
315
|
-
|
|
349
|
+
logToolReadGuardEvent({
|
|
316
350
|
event: "ts_range_expanded",
|
|
317
351
|
sessionId: runtime.telemetrySessionId,
|
|
318
352
|
filePath,
|
|
@@ -348,7 +382,7 @@ export async function handleToolCall(deps) {
|
|
|
348
382
|
if (toolName === "read" && filePath && !isExternalOrVendor) {
|
|
349
383
|
const totalLines = countFileLines(filePath);
|
|
350
384
|
const deliveredLimit = effectiveReadLimit ?? 1;
|
|
351
|
-
|
|
385
|
+
logToolReadGuardEvent({
|
|
352
386
|
event: "read_pattern",
|
|
353
387
|
sessionId: runtime.telemetrySessionId,
|
|
354
388
|
filePath,
|
|
@@ -379,41 +413,6 @@ export async function handleToolCall(deps) {
|
|
|
379
413
|
timestamp: Date.now(),
|
|
380
414
|
});
|
|
381
415
|
}
|
|
382
|
-
// --- Read-Before-Edit Guard: register file access done via `bash` ---
|
|
383
|
-
// Mirrors how the Read/Write tools are tracked. Only the bash tool —
|
|
384
|
-
// grep/find tools (and their patterns) are not contiguous file access.
|
|
385
|
-
// reads (cat/head/tail/sed -n) → recordRead with the exact range shown
|
|
386
|
-
// writes (>, >>, tee, sed -i, cp/mv dest, touch) → noteCreatedFile, so the
|
|
387
|
-
// agent "owns" the file (recordWritten fires at tool_result), same
|
|
388
|
-
// as the Write tool.
|
|
389
|
-
if (toolName === "bash" && !getFlag("no-read-guard")) {
|
|
390
|
-
const cmd = event.input?.command;
|
|
391
|
-
if (typeof cmd === "string" && cmd) {
|
|
392
|
-
const effectiveCwd = ctx.cwd ?? runtime.projectRoot ?? process.cwd();
|
|
393
|
-
const inScope = (fp) => !isPathIgnoredByProject(fp, runtime.projectRoot, false) &&
|
|
394
|
-
!isExternalOrVendorFile(fp, runtime.projectRoot);
|
|
395
|
-
for (const span of extractReadPathsFromCommand(cmd, effectiveCwd)) {
|
|
396
|
-
if (!inScope(span.filePath))
|
|
397
|
-
continue;
|
|
398
|
-
runtime.readGuard.recordRead({
|
|
399
|
-
filePath: span.filePath,
|
|
400
|
-
requestedOffset: span.offset,
|
|
401
|
-
requestedLimit: span.limit,
|
|
402
|
-
effectiveOffset: span.offset,
|
|
403
|
-
effectiveLimit: span.limit,
|
|
404
|
-
expandedByLsp: false,
|
|
405
|
-
turnIndex: runtime.turnIndex,
|
|
406
|
-
writeIndex: runtime.peekWriteIndex(),
|
|
407
|
-
timestamp: Date.now(),
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
for (const wp of extractWrittenPathsFromCommand(cmd, effectiveCwd)) {
|
|
411
|
-
if (!inScope(wp))
|
|
412
|
-
continue;
|
|
413
|
-
runtime.readGuard.noteCreatedFile(wp, runtime.turnIndex, runtime.peekWriteIndex());
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
416
|
const { complexityClient } = await loadBootstrapClients();
|
|
418
417
|
// Record complexity baseline for historical tracking (booboo/tdi).
|
|
419
418
|
// Not shown inline - just captured for delta analysis.
|
|
@@ -511,7 +510,7 @@ export async function handleToolCall(deps) {
|
|
|
511
510
|
continue;
|
|
512
511
|
entry.apply(escaped);
|
|
513
512
|
entry.value = escaped;
|
|
514
|
-
|
|
513
|
+
logToolReadGuardEvent({
|
|
515
514
|
event: "oldtext_escape_autopatched",
|
|
516
515
|
sessionId: runtime.telemetrySessionId,
|
|
517
516
|
filePath,
|
|
@@ -541,7 +540,7 @@ export async function handleToolCall(deps) {
|
|
|
541
540
|
entry.applyNewText(patch.newText);
|
|
542
541
|
entry.newText = patch.newText;
|
|
543
542
|
}
|
|
544
|
-
|
|
543
|
+
logToolReadGuardEvent({
|
|
545
544
|
event: "oldtext_trailing_ws_autopatched",
|
|
546
545
|
sessionId: runtime.telemetrySessionId,
|
|
547
546
|
filePath,
|
|
@@ -612,7 +611,7 @@ export async function handleToolCall(deps) {
|
|
|
612
611
|
if (correctedNewText !== undefined) {
|
|
613
612
|
entry.applyNewText(correctedNewText);
|
|
614
613
|
}
|
|
615
|
-
|
|
614
|
+
logToolReadGuardEvent({
|
|
616
615
|
event: "oldtext_indent_autopatched",
|
|
617
616
|
sessionId: runtime.telemetrySessionId,
|
|
618
617
|
filePath,
|
|
@@ -651,20 +650,25 @@ export async function handleToolCall(deps) {
|
|
|
651
650
|
const isExistingFile = typeof readGuard?.isNewFile !== "function" ||
|
|
652
651
|
!readGuard.isNewFile(filePath);
|
|
653
652
|
if (readGuard && isExistingFile && !isExternalOrVendor) {
|
|
654
|
-
const { touchedLines, editRanges, preflightError, partiallyApplicable, contentMatchValidated, } = getTouchedLinesForGuard(event, filePath, runtime.telemetrySessionId);
|
|
653
|
+
const { touchedLines, editRanges, preflightError, partiallyApplicable, contentMatchValidated, editBatchSummary, } = getTouchedLinesForGuard(event, filePath, runtime.telemetrySessionId, readGuardCorrelationId);
|
|
655
654
|
if (preflightError) {
|
|
656
655
|
if (partiallyApplicable && partiallyApplicable.length > 0) {
|
|
657
656
|
try {
|
|
658
657
|
const partial = await applyPartiallyApplicableEdits({
|
|
659
658
|
filePath,
|
|
660
659
|
edits: partiallyApplicable,
|
|
660
|
+
summary: editBatchSummary,
|
|
661
|
+
correlationId: readGuardCorrelationId,
|
|
661
662
|
afterWrite: async () => {
|
|
662
663
|
const { biomeClient, ruffClient, metricsClient, agentBehaviorClient, } = await loadBootstrapClients();
|
|
663
664
|
const result = await handleToolResult({
|
|
664
665
|
event: {
|
|
665
666
|
toolName: "write",
|
|
666
667
|
input: { path: filePath },
|
|
667
|
-
details: {
|
|
668
|
+
details: {
|
|
669
|
+
piLensPartialApply: true,
|
|
670
|
+
readGuardCorrelationId,
|
|
671
|
+
},
|
|
668
672
|
content: [],
|
|
669
673
|
provider: event.provider,
|
|
670
674
|
model: event.model,
|
|
@@ -683,20 +687,35 @@ export async function handleToolCall(deps) {
|
|
|
683
687
|
agentBehaviorRecord: (toolName, analyzedPath) => agentBehaviorClient.recordToolCall(toolName, analyzedPath),
|
|
684
688
|
formatBehaviorWarnings: (warnings) => agentBehaviorClient.formatWarnings(warnings),
|
|
685
689
|
});
|
|
690
|
+
if (result?.isError) {
|
|
691
|
+
throw new Error("post-edit pipeline rejected synthetic partial apply");
|
|
692
|
+
}
|
|
686
693
|
return result?.content
|
|
687
694
|
?.map((item) => item.text)
|
|
688
695
|
.filter((text) => !!text)
|
|
689
696
|
.join("\n\n");
|
|
690
697
|
},
|
|
691
698
|
});
|
|
699
|
+
if (partial.postEditStatus === "failed") {
|
|
700
|
+
return {
|
|
701
|
+
block: true,
|
|
702
|
+
reason: `${preflightError}\n\nPartial apply pipeline failed after ${partial.appliedCount} edit${partial.appliedCount === 1 ? "" : "s"} committed.`,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
692
705
|
if (partial.appliedCount > 0) {
|
|
693
|
-
|
|
706
|
+
logToolReadGuardEvent({
|
|
694
707
|
event: "edit_partial_apply",
|
|
695
708
|
sessionId: runtime.telemetrySessionId,
|
|
696
709
|
filePath,
|
|
697
710
|
metadata: {
|
|
698
711
|
appliedCount: partial.appliedCount,
|
|
712
|
+
appliedTotal: partial.appliedTotal,
|
|
699
713
|
appliedIndices: partial.appliedIndices,
|
|
714
|
+
skippedCount: partial.skippedCount ?? 0,
|
|
715
|
+
skippedTotal: partial.skippedTotal ?? 0,
|
|
716
|
+
skippedIndices: partial.skippedIndices ?? "",
|
|
717
|
+
indexesTruncated: partial.indexesTruncated ?? false,
|
|
718
|
+
editBatchSummary: partial.summary,
|
|
700
719
|
routedThroughPostEditPipeline: true,
|
|
701
720
|
},
|
|
702
721
|
});
|
|
@@ -711,9 +730,11 @@ export async function handleToolCall(deps) {
|
|
|
711
730
|
// fall through to full block
|
|
712
731
|
}
|
|
713
732
|
}
|
|
733
|
+
if (!editBatchSummary)
|
|
734
|
+
logBlockedEditSummary("preflight");
|
|
714
735
|
return { block: true, reason: preflightError };
|
|
715
736
|
}
|
|
716
|
-
|
|
737
|
+
logToolReadGuardEvent({
|
|
717
738
|
event: "edit_check_started",
|
|
718
739
|
sessionId: runtime.telemetrySessionId,
|
|
719
740
|
filePath,
|
|
@@ -750,7 +771,7 @@ export async function handleToolCall(deps) {
|
|
|
750
771
|
writeIndex: 0,
|
|
751
772
|
timestamp: Date.now(),
|
|
752
773
|
});
|
|
753
|
-
|
|
774
|
+
logToolReadGuardEvent({
|
|
754
775
|
event: "edit_range_relocated",
|
|
755
776
|
sessionId: runtime.telemetrySessionId,
|
|
756
777
|
filePath,
|
|
@@ -763,10 +784,12 @@ export async function handleToolCall(deps) {
|
|
|
763
784
|
// Relocation applied — let the re-targeted edit proceed.
|
|
764
785
|
}
|
|
765
786
|
else if (verdict.action === "block") {
|
|
787
|
+
logBlockedEditSummary("range_relocated_blocked");
|
|
766
788
|
return { block: true, reason: verdict.reason };
|
|
767
789
|
}
|
|
768
790
|
}
|
|
769
791
|
else if (verdict.action === "block") {
|
|
792
|
+
logBlockedEditSummary("read_guard_blocked");
|
|
770
793
|
return {
|
|
771
794
|
block: true,
|
|
772
795
|
reason: verdict.reason,
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import * as nodeCrypto from "node:crypto";
|
|
2
2
|
import * as nodeFs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { extractGrepSearchReadsFromOutput, extractWrittenPathsFromCommand, } from "./bash-file-access.js";
|
|
4
|
+
import { extractReadPathsFromCommand, extractGrepSearchReadsFromOutput, extractWrittenPathsFromCommand, } from "./bash-file-access.js";
|
|
5
5
|
import { registerSearchReads, } from "./search-read-registration.js";
|
|
6
6
|
import { createFileTime } from "./file-time.js";
|
|
7
7
|
import { publishFormatQueued } from "./format-events-publish.js";
|
|
8
8
|
import { isPathIgnoredByProject } from "./file-utils.js";
|
|
9
9
|
import { getFormatService } from "./format-service.js";
|
|
10
|
-
import { isExternalOrVendorFile } from "./path-utils.js";
|
|
10
|
+
import { isExternalOrVendorFile, normalizeEphemeralMapKey } from "./path-utils.js";
|
|
11
|
+
import { PathKeyedMap } from "./path-keyed-map.js";
|
|
11
12
|
import { resolveLanguageRootForFile } from "./language-profile.js";
|
|
12
13
|
import { logLatency } from "./latency-logger.js";
|
|
14
|
+
import { boundedIndexesForCount, createReadGuardEditBatchSummary, getReadGuardCorrelationId, logReadGuardEvent, } from "./read-guard-logger.js";
|
|
13
15
|
import { runPipeline } from "./pipeline.js";
|
|
14
16
|
import { appendProjectChange, } from "./project-changes.js";
|
|
17
|
+
import { syncGitGuardRecord } from "./git-guard.js";
|
|
15
18
|
import { scheduleWordIndexPersist } from "./word-index.js";
|
|
16
19
|
function parseDiffRanges(diff) {
|
|
17
20
|
const changedLines = [];
|
|
@@ -40,19 +43,23 @@ function parseDiffRanges(diff) {
|
|
|
40
43
|
ranges.push({ start: rangeStart, end: rangeEnd });
|
|
41
44
|
return ranges;
|
|
42
45
|
}
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
// Keyed by (normalized) filePath, then by the raw stateHash — the path portion
|
|
47
|
+
// needs normalizing (divergent Windows spellings must collapse to one entry),
|
|
48
|
+
// the stateHash suffix must NOT be folded into the path key (a real content
|
|
49
|
+
// change for the same file has to stay a distinct entry). A flat
|
|
50
|
+
// `PathKeyedMap<InFlightPipeline>` keyed by a composite `${filePath}:${hash}`
|
|
51
|
+
// string can't express that split cleanly (the normalizer only sees the whole
|
|
52
|
+
// composite string, so it can't fold the path half without also mangling the
|
|
53
|
+
// hash half); nesting keeps each axis normalized/compared with its own rules.
|
|
54
|
+
const inFlightPipelines = new PathKeyedMap(normalizeEphemeralMapKey);
|
|
55
|
+
const lastAnalyzedStateByFile = new PathKeyedMap(normalizeEphemeralMapKey);
|
|
49
56
|
// Called at turn_start — entries from the previous turn can never match the new
|
|
50
57
|
// turnIndex so they're dead weight. Clearing here keeps the map bounded to the
|
|
51
58
|
// files touched in the current turn only (typically < 20).
|
|
52
59
|
export function clearLastAnalyzedStateCache() {
|
|
53
60
|
lastAnalyzedStateByFile.clear();
|
|
54
61
|
}
|
|
55
|
-
const debouncedPipelines = new
|
|
62
|
+
const debouncedPipelines = new PathKeyedMap(normalizeEphemeralMapKey);
|
|
56
63
|
const DEFAULT_DEBOUNCE_MS = 0;
|
|
57
64
|
const MAX_DEBOUNCE_MS = 1000;
|
|
58
65
|
function getDebounceMs() {
|
|
@@ -98,7 +105,13 @@ function scheduleDebounced(filePath, debounceMs, deps) {
|
|
|
98
105
|
const existing = debouncedPipelines.get(filePath);
|
|
99
106
|
if (existing) {
|
|
100
107
|
clearTimeout(existing.timer);
|
|
101
|
-
|
|
108
|
+
const incomingId = deps._telemetryParticipantIds?.[0] ?? getReadGuardCorrelationId(deps.event);
|
|
109
|
+
const priorIds = existing.latestDeps._telemetryParticipantIds ?? [];
|
|
110
|
+
existing.latestDeps = {
|
|
111
|
+
...deps,
|
|
112
|
+
_telemetryParticipantIds: [...priorIds, incomingId].slice(0, 100),
|
|
113
|
+
_telemetryParticipantTotal: (existing.latestDeps._telemetryParticipantTotal ?? priorIds.length) + 1,
|
|
114
|
+
};
|
|
102
115
|
existing.coalescedCount += 1;
|
|
103
116
|
existing.timer = setTimeout(() => {
|
|
104
117
|
debouncedPipelines.delete(filePath);
|
|
@@ -114,6 +127,7 @@ function scheduleDebounced(filePath, debounceMs, deps) {
|
|
|
114
127
|
resolveFn = res;
|
|
115
128
|
rejectFn = rej;
|
|
116
129
|
});
|
|
130
|
+
const initialParticipantIds = deps._telemetryParticipantIds ?? [getReadGuardCorrelationId(deps.event)];
|
|
117
131
|
const entry = {
|
|
118
132
|
timer: setTimeout(() => {
|
|
119
133
|
debouncedPipelines.delete(filePath);
|
|
@@ -122,7 +136,11 @@ function scheduleDebounced(filePath, debounceMs, deps) {
|
|
|
122
136
|
promise,
|
|
123
137
|
resolve: resolveFn,
|
|
124
138
|
reject: rejectFn,
|
|
125
|
-
latestDeps:
|
|
139
|
+
latestDeps: {
|
|
140
|
+
...deps,
|
|
141
|
+
_telemetryParticipantIds: initialParticipantIds.slice(0, 100),
|
|
142
|
+
_telemetryParticipantTotal: deps._telemetryParticipantTotal ?? initialParticipantIds.length,
|
|
143
|
+
},
|
|
126
144
|
scheduledAt: Date.now(),
|
|
127
145
|
coalescedCount: 1,
|
|
128
146
|
};
|
|
@@ -139,6 +157,15 @@ function getFileStateHash(filePath) {
|
|
|
139
157
|
return `unreadable:${code}`;
|
|
140
158
|
}
|
|
141
159
|
}
|
|
160
|
+
function getRequestedEditCount(event) {
|
|
161
|
+
if (event.toolName === "write")
|
|
162
|
+
return 1;
|
|
163
|
+
const edits = event.input?.edits;
|
|
164
|
+
return Array.isArray(edits) && edits.length > 0 ? edits.length : 1;
|
|
165
|
+
}
|
|
166
|
+
function getRequestedEditIndexes(event) {
|
|
167
|
+
return boundedIndexesForCount(getRequestedEditCount(event));
|
|
168
|
+
}
|
|
142
169
|
function sourceForToolName(toolName, details) {
|
|
143
170
|
if (details
|
|
144
171
|
?.piLensPartialApply) {
|
|
@@ -191,7 +218,8 @@ export async function handleToolResult(deps) {
|
|
|
191
218
|
if (event.toolName === "bash" &&
|
|
192
219
|
typeof event.input.command === "string") {
|
|
193
220
|
const command = event.input.command;
|
|
194
|
-
const written = extractWrittenPathsFromCommand(command, workspaceRoot).filter((wp) =>
|
|
221
|
+
const written = extractWrittenPathsFromCommand(command, workspaceRoot).filter((wp) => event.isError !== true &&
|
|
222
|
+
!isExternalOrVendorFile(wp, workspaceRoot) &&
|
|
195
223
|
!isPathIgnoredByProject(wp, workspaceRoot, false));
|
|
196
224
|
for (const wp of written) {
|
|
197
225
|
if (!getFlag("no-read-guard"))
|
|
@@ -202,12 +230,31 @@ export async function handleToolResult(deps) {
|
|
|
202
230
|
_bypassDebounce: true,
|
|
203
231
|
});
|
|
204
232
|
}
|
|
233
|
+
if (event.isError !== true && !getFlag("no-read-guard")) {
|
|
234
|
+
for (const span of extractReadPathsFromCommand(command, workspaceRoot)) {
|
|
235
|
+
if (isExternalOrVendorFile(span.filePath, workspaceRoot))
|
|
236
|
+
continue;
|
|
237
|
+
if (isPathIgnoredByProject(span.filePath, workspaceRoot, false))
|
|
238
|
+
continue;
|
|
239
|
+
deps.readGuard?.recordRead({
|
|
240
|
+
filePath: span.filePath,
|
|
241
|
+
requestedOffset: span.offset,
|
|
242
|
+
requestedLimit: span.limit,
|
|
243
|
+
effectiveOffset: span.offset,
|
|
244
|
+
effectiveLimit: span.limit,
|
|
245
|
+
expandedByLsp: false,
|
|
246
|
+
turnIndex: runtime.turnIndex,
|
|
247
|
+
writeIndex: runtime.peekWriteIndex(),
|
|
248
|
+
timestamp: Date.now(),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
205
252
|
}
|
|
206
253
|
// Search tools reveal specific lines (file:line) the agent then edits — register
|
|
207
254
|
// those shown lines (± context) as reads so the follow-up edit isn't blocked (#169).
|
|
208
255
|
// Our tools attach locations as `details.searchReads`; bash grep is parsed from
|
|
209
256
|
// `grep -n` output. Only shown lines are registered, never the whole file.
|
|
210
|
-
if (deps.readGuard && !getFlag("no-read-guard")) {
|
|
257
|
+
if (deps.readGuard && event.isError !== true && !getFlag("no-read-guard")) {
|
|
211
258
|
const searchReads = [];
|
|
212
259
|
const detailSearchReads = event.details?.searchReads;
|
|
213
260
|
if (Array.isArray(detailSearchReads))
|
|
@@ -240,13 +287,54 @@ export async function handleToolResult(deps) {
|
|
|
240
287
|
dbg(`tool_result: skipped pipeline - file outside project root or in node_modules: ${filePath}`);
|
|
241
288
|
return;
|
|
242
289
|
}
|
|
290
|
+
const readGuardCorrelationId = getReadGuardCorrelationId(event);
|
|
291
|
+
const resultDetails = (event.details ?? {});
|
|
292
|
+
const isPartialApplyResult = resultDetails.piLensPartialApply === true;
|
|
293
|
+
const requestedEditIndexes = getRequestedEditIndexes(event);
|
|
294
|
+
const requestedEditTotal = getRequestedEditCount(event);
|
|
295
|
+
const participantIds = [
|
|
296
|
+
...(deps._telemetryParticipantIds ?? []),
|
|
297
|
+
readGuardCorrelationId,
|
|
298
|
+
].slice(0, 100);
|
|
299
|
+
const participantTotal = (deps._telemetryParticipantTotal ?? 0) +
|
|
300
|
+
(deps._telemetryParticipantIds?.includes(readGuardCorrelationId) ? 0 : 1);
|
|
301
|
+
const hostToolResultFailed = event.isError === true || resultDetails.isError === true;
|
|
302
|
+
if (hostToolResultFailed) {
|
|
303
|
+
logReadGuardEvent({
|
|
304
|
+
event: "edit_batch_summary",
|
|
305
|
+
correlationId: readGuardCorrelationId,
|
|
306
|
+
filePath,
|
|
307
|
+
metadata: {
|
|
308
|
+
tool: event.toolName,
|
|
309
|
+
source: "host_tool_result",
|
|
310
|
+
editBatchSummary: createReadGuardEditBatchSummary({
|
|
311
|
+
requestedIndexes: requestedEditIndexes,
|
|
312
|
+
requestedTotal: requestedEditTotal,
|
|
313
|
+
rejectedReasons: requestedEditIndexes.map((index) => ({
|
|
314
|
+
index,
|
|
315
|
+
code: "write_failed",
|
|
316
|
+
})),
|
|
317
|
+
rejectedTotal: requestedEditTotal,
|
|
318
|
+
participantIds: [readGuardCorrelationId],
|
|
319
|
+
participantTotal: 1,
|
|
320
|
+
commitStatus: "failed",
|
|
321
|
+
terminalStatus: "failed",
|
|
322
|
+
}),
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
return { content: event.content, isError: true };
|
|
326
|
+
}
|
|
243
327
|
// Coalesce sequential edits to the same file into one pipeline run against
|
|
244
328
|
// the final state. Only the debounce-fired call (with _bypassDebounce=true)
|
|
245
329
|
// proceeds to the pipeline body; in-window callers share its promise.
|
|
246
330
|
if (!deps._bypassDebounce) {
|
|
247
331
|
const debounceMs = getDebounceMs();
|
|
248
332
|
if (debounceMs > 0) {
|
|
249
|
-
return scheduleDebounced(filePath, debounceMs,
|
|
333
|
+
return scheduleDebounced(filePath, debounceMs, {
|
|
334
|
+
...deps,
|
|
335
|
+
_telemetryParticipantIds: [readGuardCorrelationId],
|
|
336
|
+
_telemetryParticipantTotal: 1,
|
|
337
|
+
});
|
|
250
338
|
}
|
|
251
339
|
}
|
|
252
340
|
// Refresh the read-guard's FileTime stamp so that the model's own write
|
|
@@ -275,13 +363,18 @@ export async function handleToolResult(deps) {
|
|
|
275
363
|
}
|
|
276
364
|
}
|
|
277
365
|
const initialStateHash = getFileStateHash(filePath);
|
|
278
|
-
const pipelineDedupeKey = `${filePath}:${initialStateHash}`;
|
|
279
366
|
// Deduplicate concurrent calls for the same final file state (pi can fire one
|
|
280
367
|
// tool_result per edit hunk). Do not dedupe by file alone: a distinct later
|
|
281
368
|
// same-turn edit to this file must still be analyzed.
|
|
282
|
-
|
|
369
|
+
const inFlight = inFlightPipelines.get(filePath)?.get(initialStateHash);
|
|
370
|
+
if (inFlight) {
|
|
283
371
|
dbg(`tool_result: skipping duplicate concurrent state for ${filePath}`);
|
|
284
|
-
|
|
372
|
+
const duplicateId = readGuardCorrelationId;
|
|
373
|
+
if (inFlight.participantIds.length < 100) {
|
|
374
|
+
inFlight.participantIds.push(duplicateId);
|
|
375
|
+
}
|
|
376
|
+
inFlight.participantTotal += 1;
|
|
377
|
+
await inFlight.promise;
|
|
285
378
|
return;
|
|
286
379
|
}
|
|
287
380
|
// Deduplicate sequential duplicate events for the same post-write state in the
|
|
@@ -319,6 +412,13 @@ export async function handleToolResult(deps) {
|
|
|
319
412
|
const writeIndex = runtime.nextWriteIndex();
|
|
320
413
|
let modifiedRanges;
|
|
321
414
|
try {
|
|
415
|
+
// #1334 S6: the host DECLARES this payload (`EditToolDetails`, a
|
|
416
|
+
// type-only export), so use it instead of re-declaring `{ diff?: string }`
|
|
417
|
+
// here — the ad-hoc shape hid the sibling `patch`/`firstChangedLine`
|
|
418
|
+
// fields. `Partial<>` keeps the defensive posture: the host types mark
|
|
419
|
+
// `diff` required, but this runs against whatever a live host actually
|
|
420
|
+
// sent, and the `details?.diff` truthiness check below is what the code
|
|
421
|
+
// has always relied on.
|
|
322
422
|
const details = event.details;
|
|
323
423
|
dbg(`tool_result: details.diff=${details?.diff ? "present" : "missing"}, details keys: ${Object.keys(event.details || {}).join(", ")}`);
|
|
324
424
|
if (event.toolName === "edit" && details?.diff) {
|
|
@@ -367,6 +467,7 @@ export async function handleToolResult(deps) {
|
|
|
367
467
|
const pipelinePromise = runPipeline({
|
|
368
468
|
filePath,
|
|
369
469
|
cwd: dispatchCwd,
|
|
470
|
+
projectRoot: turnStateCwd,
|
|
370
471
|
toolName: event.toolName,
|
|
371
472
|
modifiedRanges,
|
|
372
473
|
telemetry: {
|
|
@@ -404,12 +505,57 @@ export async function handleToolResult(deps) {
|
|
|
404
505
|
getFormatService,
|
|
405
506
|
fixedThisTurn: runtime.fixedThisTurn,
|
|
406
507
|
});
|
|
407
|
-
|
|
508
|
+
const pipelineTelemetry = {
|
|
509
|
+
promise: pipelinePromise,
|
|
510
|
+
participantIds: [...new Set(participantIds)].slice(0, 100),
|
|
511
|
+
participantTotal,
|
|
512
|
+
};
|
|
513
|
+
let filePipelines = inFlightPipelines.get(filePath);
|
|
514
|
+
if (!filePipelines) {
|
|
515
|
+
filePipelines = new Map();
|
|
516
|
+
inFlightPipelines.set(filePath, filePipelines);
|
|
517
|
+
}
|
|
518
|
+
filePipelines.set(initialStateHash, pipelineTelemetry);
|
|
408
519
|
try {
|
|
409
520
|
result = await pipelinePromise;
|
|
410
521
|
}
|
|
411
522
|
catch (pipelineErr) {
|
|
523
|
+
if (getFlag("lens-guard")) {
|
|
524
|
+
runtime.markGitGuardCacheUnknown("pipeline_crash");
|
|
525
|
+
}
|
|
412
526
|
dbg(`runPipeline crashed: ${pipelineErr}`);
|
|
527
|
+
logReadGuardEvent({
|
|
528
|
+
event: "edit_post_edit_pipeline_failed",
|
|
529
|
+
correlationId: readGuardCorrelationId,
|
|
530
|
+
filePath,
|
|
531
|
+
metadata: {
|
|
532
|
+
tool: event.toolName,
|
|
533
|
+
commitStatus: "committed",
|
|
534
|
+
reasonCode: "pipeline_failed",
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
logReadGuardEvent({
|
|
538
|
+
event: "edit_batch_summary",
|
|
539
|
+
correlationId: readGuardCorrelationId,
|
|
540
|
+
filePath,
|
|
541
|
+
metadata: {
|
|
542
|
+
tool: event.toolName,
|
|
543
|
+
editBatchSummary: createReadGuardEditBatchSummary({
|
|
544
|
+
requestedIndexes: requestedEditIndexes,
|
|
545
|
+
requestedTotal: requestedEditTotal,
|
|
546
|
+
resolvedIndexes: requestedEditIndexes,
|
|
547
|
+
resolvedTotal: requestedEditTotal,
|
|
548
|
+
appliedIndexes: requestedEditIndexes,
|
|
549
|
+
appliedTotal: requestedEditTotal,
|
|
550
|
+
participantIds: pipelineTelemetry.participantIds,
|
|
551
|
+
participantTotal: pipelineTelemetry.participantTotal,
|
|
552
|
+
commitStatus: "committed",
|
|
553
|
+
postEditStatus: "failed",
|
|
554
|
+
terminalStatus: "failed",
|
|
555
|
+
durationMs: Date.now() - toolResultStart,
|
|
556
|
+
}),
|
|
557
|
+
},
|
|
558
|
+
});
|
|
413
559
|
dbg(`runPipeline crash stack: ${pipelineErr.stack}`);
|
|
414
560
|
if (!getFlag("no-lsp")) {
|
|
415
561
|
resetLSPService({ fast: true, reason: "pipeline_crash" });
|
|
@@ -422,14 +568,57 @@ export async function handleToolResult(deps) {
|
|
|
422
568
|
result: "pipeline_crash",
|
|
423
569
|
});
|
|
424
570
|
const notice = runtime.formatPipelineCrashNotice(filePath, pipelineErr);
|
|
425
|
-
if (!notice)
|
|
426
|
-
return;
|
|
427
571
|
return {
|
|
428
|
-
content:
|
|
572
|
+
content: notice
|
|
573
|
+
? [...event.content, { type: "text", text: notice }]
|
|
574
|
+
: event.content,
|
|
575
|
+
isError: true,
|
|
429
576
|
};
|
|
430
577
|
}
|
|
431
578
|
finally {
|
|
432
|
-
|
|
579
|
+
// Prune the per-file inner map once it's empty so a file touched once
|
|
580
|
+
// this session doesn't leave a permanent empty entry in the outer map.
|
|
581
|
+
filePipelines.delete(initialStateHash);
|
|
582
|
+
if (filePipelines.size === 0) {
|
|
583
|
+
inFlightPipelines.delete(filePath);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (!isPartialApplyResult) {
|
|
587
|
+
const postEditStatus = result.isError ? "failed" : "succeeded";
|
|
588
|
+
if (result.isError) {
|
|
589
|
+
logReadGuardEvent({
|
|
590
|
+
event: "edit_post_edit_pipeline_failed",
|
|
591
|
+
correlationId: readGuardCorrelationId,
|
|
592
|
+
filePath,
|
|
593
|
+
metadata: {
|
|
594
|
+
tool: event.toolName,
|
|
595
|
+
commitStatus: "committed",
|
|
596
|
+
reasonCode: "pipeline_failed",
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
logReadGuardEvent({
|
|
601
|
+
event: "edit_batch_summary",
|
|
602
|
+
correlationId: readGuardCorrelationId,
|
|
603
|
+
filePath,
|
|
604
|
+
metadata: {
|
|
605
|
+
tool: event.toolName,
|
|
606
|
+
editBatchSummary: createReadGuardEditBatchSummary({
|
|
607
|
+
requestedIndexes: requestedEditIndexes,
|
|
608
|
+
requestedTotal: requestedEditTotal,
|
|
609
|
+
resolvedIndexes: requestedEditIndexes,
|
|
610
|
+
resolvedTotal: requestedEditTotal,
|
|
611
|
+
appliedIndexes: requestedEditIndexes,
|
|
612
|
+
appliedTotal: requestedEditTotal,
|
|
613
|
+
participantIds: pipelineTelemetry.participantIds,
|
|
614
|
+
participantTotal: pipelineTelemetry.participantTotal,
|
|
615
|
+
commitStatus: "committed",
|
|
616
|
+
postEditStatus,
|
|
617
|
+
terminalStatus: postEditStatus === "failed" ? "failed" : "success",
|
|
618
|
+
durationMs: Date.now() - toolResultStart,
|
|
619
|
+
}),
|
|
620
|
+
},
|
|
621
|
+
});
|
|
433
622
|
}
|
|
434
623
|
lastAnalyzedStateByFile.set(filePath, {
|
|
435
624
|
turnIndex: runtime.turnIndex,
|
|
@@ -549,6 +738,13 @@ export async function handleToolResult(deps) {
|
|
|
549
738
|
else {
|
|
550
739
|
runtime.clearInlineBlockers(filePath);
|
|
551
740
|
}
|
|
741
|
+
runtime.updateGitGuardStatus(result.hasBlockers, result.output);
|
|
742
|
+
if (getFlag("lens-guard")) {
|
|
743
|
+
syncGitGuardRecord(runtime, cacheManager, turnStateCwd, filePath);
|
|
744
|
+
if (result.isError && !result.hasBlockers) {
|
|
745
|
+
runtime.markGitGuardCacheUnknown("pipeline_error");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
552
748
|
if (result.isError) {
|
|
553
749
|
return {
|
|
554
750
|
content: [...event.content, { type: "text", text: result.output }],
|
|
@@ -556,7 +752,6 @@ export async function handleToolResult(deps) {
|
|
|
556
752
|
};
|
|
557
753
|
}
|
|
558
754
|
let output = result.output;
|
|
559
|
-
runtime.updateGitGuardStatus(result.hasBlockers, result.output);
|
|
560
755
|
if (behaviorWarnings.length > 0 && !result.hasBlockers) {
|
|
561
756
|
output += `\n\n${formatBehaviorWarnings(behaviorWarnings)}`;
|
|
562
757
|
}
|