@herbertgao/pi-extensions 2026.10.1 → 2026.10.3
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 +14 -17
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
- package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
- package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
- package/node_modules/@dietrichgebert/ponytail/README.md +68 -72
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +7 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
- package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -32
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +17 -16
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +9 -10
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -57
- package/node_modules/{remote-pi → @herbertgao/pi-agency}/LICENSE +1 -1
- package/node_modules/@herbertgao/pi-agency/README.md +40 -0
- package/node_modules/@herbertgao/pi-agency/package.json +53 -0
- package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +1 -1
- package/node_modules/@herbertgao/pi-subagents/package.json +4 -5
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +48 -72
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +15 -6
- package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +4 -1
- package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +9 -8
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +6 -1
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-caffeinate/package.json +3 -3
- package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +13 -1
- package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
- package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
- package/node_modules/@tifan/pi-recap/package.json +1 -1
- package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
- package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
- package/node_modules/pi-antigravity/README.md +23 -2
- package/node_modules/pi-antigravity/package.json +5 -3
- package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
- package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
- package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
- package/node_modules/pi-antigravity/src/image/image.ts +86 -47
- package/node_modules/pi-antigravity/src/image/native.ts +69 -0
- package/node_modules/pi-antigravity/src/index.ts +52 -137
- package/node_modules/pi-antigravity/src/search/search.ts +130 -31
- package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
- package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
- package/node_modules/pi-antigravity/src/types/types.ts +1 -1
- package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
- package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
- package/node_modules/pi-lens/CHANGELOG.md +1159 -0
- package/node_modules/pi-lens/README.md +116 -25
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
- package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
- package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
- package/node_modules/pi-lens/dist/chunk-2FQ62SPM.js +83 -0
- package/node_modules/pi-lens/dist/chunk-2UMYMRI3.js +118 -0
- package/node_modules/pi-lens/dist/chunk-34OCDWOS.js +48 -0
- package/node_modules/pi-lens/dist/chunk-3FERWMCH.js +144 -0
- package/node_modules/pi-lens/dist/chunk-3JL6IFD7.js +97 -0
- package/node_modules/pi-lens/dist/chunk-3JSEWUWI.js +327 -0
- package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
- package/node_modules/pi-lens/dist/chunk-3MRXIJ6I.js +79 -0
- package/node_modules/pi-lens/dist/chunk-3P4PF2BX.js +2250 -0
- package/node_modules/pi-lens/dist/chunk-3PNFWVHT.js +201 -0
- package/node_modules/pi-lens/dist/chunk-3TTPKMEF.js +162 -0
- package/node_modules/pi-lens/dist/chunk-5JHXUETW.js +326 -0
- package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
- package/node_modules/pi-lens/dist/chunk-64LU2OVH.js +341 -0
- package/node_modules/pi-lens/dist/chunk-6B5I6ICN.js +44 -0
- package/node_modules/pi-lens/dist/chunk-6P54TMWL.js +274 -0
- package/node_modules/pi-lens/dist/chunk-6UYA642J.js +145 -0
- package/node_modules/pi-lens/dist/chunk-6XX5HCLK.js +398 -0
- package/node_modules/pi-lens/dist/chunk-7AXO3ZDF.js +30 -0
- package/node_modules/pi-lens/dist/chunk-7BX5DWSA.js +1907 -0
- package/node_modules/pi-lens/dist/chunk-7ELY2CSM.js +902 -0
- package/node_modules/pi-lens/dist/chunk-7M4T5N3V.js +38 -0
- package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
- package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
- package/node_modules/pi-lens/dist/chunk-AFZYMAIV.js +756 -0
- package/node_modules/pi-lens/dist/chunk-ALQRYH6O.js +112 -0
- package/node_modules/pi-lens/dist/chunk-AXKPQ6ZI.js +1610 -0
- package/node_modules/pi-lens/dist/chunk-BHWHXMQ2.js +62 -0
- package/node_modules/pi-lens/dist/chunk-BJF25SUG.js +22 -0
- package/node_modules/pi-lens/dist/chunk-BNKLYOOO.js +18 -0
- package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
- package/node_modules/pi-lens/dist/chunk-CBD2B5CP.js +599 -0
- package/node_modules/pi-lens/dist/chunk-CFYISHUW.js +42 -0
- package/node_modules/pi-lens/dist/chunk-CMU4RMJB.js +30 -0
- package/node_modules/pi-lens/dist/chunk-CULQYFLU.js +14 -0
- package/node_modules/pi-lens/dist/chunk-D65GOCJB.js +289 -0
- package/node_modules/pi-lens/dist/chunk-D6YF5HKX.js +59 -0
- package/node_modules/pi-lens/dist/chunk-DMS52BEP.js +342 -0
- package/node_modules/pi-lens/dist/chunk-ELVWJYRV.js +211 -0
- package/node_modules/pi-lens/dist/chunk-EUT3B22Y.js +27 -0
- package/node_modules/pi-lens/dist/chunk-F5VLZHUW.js +146 -0
- package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
- package/node_modules/pi-lens/dist/chunk-FGALODG3.js +55 -0
- package/node_modules/pi-lens/dist/chunk-G3RLJYA2.js +2358 -0
- package/node_modules/pi-lens/dist/chunk-GAYGGICG.js +32 -0
- package/node_modules/pi-lens/dist/chunk-H2W2AXD3.js +419 -0
- package/node_modules/pi-lens/dist/chunk-H5EV3VMF.js +114 -0
- package/node_modules/pi-lens/dist/chunk-HREZQCT3.js +106 -0
- package/node_modules/pi-lens/dist/chunk-HSIRMK2D.js +517 -0
- package/node_modules/pi-lens/dist/chunk-IMSQR4WO.js +184 -0
- package/node_modules/pi-lens/dist/chunk-IQZXOZ7S.js +4816 -0
- package/node_modules/pi-lens/dist/chunk-IR6XFA5P.js +37 -0
- package/node_modules/pi-lens/dist/chunk-IUKPRZER.js +78 -0
- package/node_modules/pi-lens/dist/chunk-IUR76K6A.js +262 -0
- package/node_modules/pi-lens/dist/chunk-J3FZR3YY.js +98 -0
- package/node_modules/pi-lens/dist/chunk-J6DFZDFA.js +562 -0
- package/node_modules/pi-lens/dist/chunk-J7WJZ6W7.js +50 -0
- package/node_modules/pi-lens/dist/chunk-JONGANYH.js +202 -0
- package/node_modules/pi-lens/dist/chunk-KJCWXUUS.js +425 -0
- package/node_modules/pi-lens/dist/chunk-KJN4IOEJ.js +17 -0
- package/node_modules/pi-lens/dist/chunk-KMGPUKFD.js +25 -0
- package/node_modules/pi-lens/dist/chunk-LD7WFTRX.js +4533 -0
- package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
- package/node_modules/pi-lens/dist/chunk-LNOMA3MP.js +169 -0
- package/node_modules/pi-lens/dist/chunk-MGIWSROG.js +2334 -0
- package/node_modules/pi-lens/dist/chunk-MQA2YBAH.js +308 -0
- package/node_modules/pi-lens/dist/chunk-MTDRLSI7.js +21720 -0
- package/node_modules/pi-lens/dist/chunk-N2IRDBZI.js +2355 -0
- package/node_modules/pi-lens/dist/chunk-N77WFUL7.js +50 -0
- package/node_modules/pi-lens/dist/chunk-N7YNJE5R.js +466 -0
- package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
- package/node_modules/pi-lens/dist/chunk-NFXP47RV.js +999 -0
- package/node_modules/pi-lens/dist/chunk-NJF2X5XF.js +45 -0
- package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
- package/node_modules/pi-lens/dist/chunk-O7HZX7CA.js +309 -0
- package/node_modules/pi-lens/dist/chunk-OC7LYAU5.js +708 -0
- package/node_modules/pi-lens/dist/chunk-OEX24ECI.js +348 -0
- package/node_modules/pi-lens/dist/chunk-OYOB3ZFG.js +78 -0
- package/node_modules/pi-lens/dist/chunk-OYTMZXRF.js +41 -0
- package/node_modules/pi-lens/dist/chunk-OZJINN4O.js +84 -0
- package/node_modules/pi-lens/dist/chunk-PCHDGUKT.js +23 -0
- package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
- package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
- package/node_modules/pi-lens/dist/chunk-RJFED73B.js +79 -0
- package/node_modules/pi-lens/dist/chunk-RTQNRM5X.js +27 -0
- package/node_modules/pi-lens/dist/chunk-S6AMWKPC.js +189 -0
- package/node_modules/pi-lens/dist/chunk-T4QZ6FEH.js +308 -0
- package/node_modules/pi-lens/dist/chunk-T54VJRAD.js +5456 -0
- package/node_modules/pi-lens/dist/chunk-TA5L5KWQ.js +167 -0
- package/node_modules/pi-lens/dist/chunk-TQFBZVCG.js +27 -0
- package/node_modules/pi-lens/dist/chunk-U52SAGRM.js +62 -0
- package/node_modules/pi-lens/dist/chunk-UEAHAMEI.js +5814 -0
- package/node_modules/pi-lens/dist/chunk-UGSMCQ4D.js +327 -0
- package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
- package/node_modules/pi-lens/dist/chunk-USHG4DLH.js +459 -0
- package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
- package/node_modules/pi-lens/dist/chunk-V5MEBST3.js +4139 -0
- package/node_modules/pi-lens/dist/chunk-VGEVL5EQ.js +29 -0
- package/node_modules/pi-lens/dist/chunk-VPWKF3GN.js +87 -0
- package/node_modules/pi-lens/dist/chunk-VSMJK3ZJ.js +1250 -0
- package/node_modules/pi-lens/dist/chunk-VYKKMYVT.js +560 -0
- package/node_modules/pi-lens/dist/chunk-VZHJS6DR.js +442 -0
- package/node_modules/pi-lens/dist/chunk-WATC3IQR.js +213 -0
- package/node_modules/pi-lens/dist/chunk-WPE5FPGR.js +54 -0
- package/node_modules/pi-lens/dist/chunk-WSEWKCXU.js +188 -0
- package/node_modules/pi-lens/dist/chunk-WW5BLB74.js +659 -0
- package/node_modules/pi-lens/dist/chunk-XGUL4SLQ.js +406 -0
- package/node_modules/pi-lens/dist/chunk-XHSCVSOL.js +170 -0
- package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
- package/node_modules/pi-lens/dist/chunk-YLN4K6WD.js +385 -0
- package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
- package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
- package/node_modules/pi-lens/dist/chunk-Z6BB4JLQ.js +478 -0
- package/node_modules/pi-lens/dist/chunk-Z7QQA2KT.js +27 -0
- package/node_modules/pi-lens/dist/chunk-ZETYB3XG.js +2626 -0
- package/node_modules/pi-lens/dist/chunk-ZMOMPX2Z.js +87 -0
- package/node_modules/pi-lens/dist/chunk-ZOEYVXSV.js +149 -0
- package/node_modules/pi-lens/dist/chunk-ZZUFZUQZ.js +12120 -0
- package/node_modules/pi-lens/dist/index.js +39712 -29876
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
- package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
- package/node_modules/pi-lens/dist/mcp/server.js +22688 -1573
- package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
- package/node_modules/pi-lens/dist/probes/installer.js +135 -0
- package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
- package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
- package/node_modules/pi-lens/docs/agent-guide.md +13 -4
- package/node_modules/pi-lens/docs/agent-tools.md +42 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -4
- package/node_modules/pi-lens/docs/configuration.md +28 -2
- package/node_modules/pi-lens/docs/custom-rules.md +25 -2
- package/node_modules/pi-lens/docs/environment-variables.md +86 -8
- package/node_modules/pi-lens/docs/features.md +45 -5
- package/node_modules/pi-lens/docs/globalconfig.md +2 -1
- package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +37 -4
- package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
- package/node_modules/pi-lens/docs/mcp.md +21 -5
- package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +160 -88
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
- package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +736 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +75 -7
- package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +189 -48
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
- package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
- package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
- package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
- package/node_modules/pi-lens/docs/schema/pi-lens-config-v1.json +530 -0
- package/node_modules/pi-lens/docs/servercapabilities.md +56 -49
- package/node_modules/pi-lens/docs/settings.md +1 -0
- package/node_modules/pi-lens/docs/usage.md +5 -1
- package/node_modules/pi-lens/docs/word-index.md +6 -0
- package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
- package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
- package/node_modules/pi-lens/package.json +18 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
- package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
- package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
- package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
- package/node_modules/pi-web-access/CHANGELOG.md +28 -0
- package/node_modules/pi-web-access/README.md +10 -6
- package/node_modules/pi-web-access/ceramic.ts +219 -0
- package/node_modules/pi-web-access/curator-page.ts +3 -1
- package/node_modules/pi-web-access/curator-server.ts +1 -0
- package/node_modules/pi-web-access/dist/index.js +846 -588
- package/node_modules/pi-web-access/dist/mcp-cli.js +725 -516
- package/node_modules/pi-web-access/gemini-search.ts +8 -4
- package/node_modules/pi-web-access/github-extract.ts +34 -2
- package/node_modules/pi-web-access/index.ts +72 -30
- package/node_modules/pi-web-access/openai-search.ts +3 -3
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +6 -9
- package/node_modules/pi-web-access/parallel-mcp.ts +2 -0
- package/node_modules/pi-web-access/query-rewrite.ts +2 -2
- package/node_modules/pi-web-access/storage.ts +49 -0
- package/node_modules/pi-web-access/summary-review.ts +7 -6
- package/node_modules/pi-web-access/utils.ts +21 -6
- package/node_modules/pi-web-access/web-tool-core.ts +1 -0
- package/package.json +19 -19
- package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -212
- package/node_modules/@dietrichgebert/ponytail/README.ja.md +0 -212
- package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -212
- package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +0 -212
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
- package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
- package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
- package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
- package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
- package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
- package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
- package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
- package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
- package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
- package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
- package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
- package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
- package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
- package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
- package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
- package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
- package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
- package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
- package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
- package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
- package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
- package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
- package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
- package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
- package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
- package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
- package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
- package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
- package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
- package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
- package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
- package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
- package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
- package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
- package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
- package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
- package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
- package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
- package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
- package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
- package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
- package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
- package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
- package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
- package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
- package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
- package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
- package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
- package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
- package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
- package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
- package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
- package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
- package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
- package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
- package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
- package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
- package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
- package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
- package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
- package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
- package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
- package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
- package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
- package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
- package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
- package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
- package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
- package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
- package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
- package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
- package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
- package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
- package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
- package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
- package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
- package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
- package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
- package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
- package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
- package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
- package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
- package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
- package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
- package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
- package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
- package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
- package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
- package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
- package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
- package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
- package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
- package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
- package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
- package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
- package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
- package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
- package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
- package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
- package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
- package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
- package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
- package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
- package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
- package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
- package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
- package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
- package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
- package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
- package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
- package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
- package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
- package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
- package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
- package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
- package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
- package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
- package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
- package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
- package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
- package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
- package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
- package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
- package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
- package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
- package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
- package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
- package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
- package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
- package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
- package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
- package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
- package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
- package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
- package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
- package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
- package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
- package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
- package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
- package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
- package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
- package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
- package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
- package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
- package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
- package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
- package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
- package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
- package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
- package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
- package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
- package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
- package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
- package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
- package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
- package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
- package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
- package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
- package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
- package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
- package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
- package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
- package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
- package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
- package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
- package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
- package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
- package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
- package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
- package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
- package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
- package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
- package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
- package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
- package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
- package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
- package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
- package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
- package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
- package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
- package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
- package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
- package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
- package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
- package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
- package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
- package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
- package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
- package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
- package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
- package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
- package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
- package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
- package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
- package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
- package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
- package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
- package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
- package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
- package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
- package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
- package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
- package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
- package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
- package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
- package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
- package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
- package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
- package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
- package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
- package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
- package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
- package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
- package/node_modules/pi-lens/dist/i18n.js +0 -69
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
- package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
- package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
- package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
- package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
- package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
- package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
- package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
- package/node_modules/pi-lens/dist/tools/shared.js +0 -29
- package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
- package/node_modules/pi-web-access/opencode-session-headers.ts +0 -24
- package/node_modules/remote-pi/README.md +0 -675
- package/node_modules/remote-pi/dist/actions/handlers.d.ts +0 -122
- package/node_modules/remote-pi/dist/actions/handlers.js +0 -160
- package/node_modules/remote-pi/dist/actions/handlers.js.map +0 -1
- package/node_modules/remote-pi/dist/actions/registry.d.ts +0 -39
- package/node_modules/remote-pi/dist/actions/registry.js +0 -58
- package/node_modules/remote-pi/dist/actions/registry.js.map +0 -1
- package/node_modules/remote-pi/dist/bin/supervisord.d.ts +0 -2
- package/node_modules/remote-pi/dist/bin/supervisord.js +0 -86
- package/node_modules/remote-pi/dist/bin/supervisord.js.map +0 -1
- package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +0 -58
- package/node_modules/remote-pi/dist/commands/builtin_mirror.js +0 -71
- package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +0 -1
- package/node_modules/remote-pi/dist/commands/list_commands.d.ts +0 -60
- package/node_modules/remote-pi/dist/commands/list_commands.js +0 -73
- package/node_modules/remote-pi/dist/commands/list_commands.js.map +0 -1
- package/node_modules/remote-pi/dist/config.d.ts +0 -62
- package/node_modules/remote-pi/dist/config.js +0 -115
- package/node_modules/remote-pi/dist/config.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/client.d.ts +0 -20
- package/node_modules/remote-pi/dist/daemon/client.js +0 -131
- package/node_modules/remote-pi/dist/daemon/client.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +0 -176
- package/node_modules/remote-pi/dist/daemon/control_protocol.js +0 -63
- package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +0 -45
- package/node_modules/remote-pi/dist/daemon/cron_log.js +0 -72
- package/node_modules/remote-pi/dist/daemon/cron_log.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +0 -80
- package/node_modules/remote-pi/dist/daemon/cron_registry.js +0 -194
- package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/id.d.ts +0 -24
- package/node_modules/remote-pi/dist/daemon/id.js +0 -36
- package/node_modules/remote-pi/dist/daemon/id.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/install.d.ts +0 -170
- package/node_modules/remote-pi/dist/daemon/install.js +0 -547
- package/node_modules/remote-pi/dist/daemon/install.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/registry.d.ts +0 -63
- package/node_modules/remote-pi/dist/daemon/registry.js +0 -153
- package/node_modules/remote-pi/dist/daemon/registry.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +0 -194
- package/node_modules/remote-pi/dist/daemon/rpc_child.js +0 -389
- package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +0 -1
- package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +0 -93
- package/node_modules/remote-pi/dist/daemon/supervisor.js +0 -596
- package/node_modules/remote-pi/dist/daemon/supervisor.js.map +0 -1
- package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +0 -26
- package/node_modules/remote-pi/dist/extension_ui_bridge.js +0 -341
- package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +0 -1
- package/node_modules/remote-pi/dist/index.d.ts +0 -213
- package/node_modules/remote-pi/dist/index.js +0 -4996
- package/node_modules/remote-pi/dist/index.js.map +0 -1
- package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +0 -10
- package/node_modules/remote-pi/dist/mcp/mesh_result.js +0 -42
- package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +0 -1
- package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +0 -16
- package/node_modules/remote-pi/dist/mcp/mesh_server.js +0 -327
- package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/canonical.d.ts +0 -30
- package/node_modules/remote-pi/dist/mesh/canonical.js +0 -61
- package/node_modules/remote-pi/dist/mesh/canonical.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/client.d.ts +0 -18
- package/node_modules/remote-pi/dist/mesh/client.js +0 -125
- package/node_modules/remote-pi/dist/mesh/client.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/encoding.d.ts +0 -38
- package/node_modules/remote-pi/dist/mesh/encoding.js +0 -216
- package/node_modules/remote-pi/dist/mesh/encoding.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +0 -73
- package/node_modules/remote-pi/dist/mesh/self_revoke.js +0 -401
- package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/siblings.d.ts +0 -49
- package/node_modules/remote-pi/dist/mesh/siblings.js +0 -159
- package/node_modules/remote-pi/dist/mesh/siblings.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/types.d.ts +0 -34
- package/node_modules/remote-pi/dist/mesh/types.js +0 -11
- package/node_modules/remote-pi/dist/mesh/types.js.map +0 -1
- package/node_modules/remote-pi/dist/mesh/verify.d.ts +0 -17
- package/node_modules/remote-pi/dist/mesh/verify.js +0 -86
- package/node_modules/remote-pi/dist/mesh/verify.js.map +0 -1
- package/node_modules/remote-pi/dist/pairing/crypto.d.ts +0 -8
- package/node_modules/remote-pi/dist/pairing/crypto.js +0 -22
- package/node_modules/remote-pi/dist/pairing/crypto.js.map +0 -1
- package/node_modules/remote-pi/dist/pairing/handshake.d.ts +0 -28
- package/node_modules/remote-pi/dist/pairing/handshake.js +0 -113
- package/node_modules/remote-pi/dist/pairing/handshake.js.map +0 -1
- package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +0 -16
- package/node_modules/remote-pi/dist/pairing/noise-sha256.js +0 -103
- package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +0 -1
- package/node_modules/remote-pi/dist/pairing/qr.d.ts +0 -59
- package/node_modules/remote-pi/dist/pairing/qr.js +0 -125
- package/node_modules/remote-pi/dist/pairing/qr.js.map +0 -1
- package/node_modules/remote-pi/dist/pairing/storage.d.ts +0 -120
- package/node_modules/remote-pi/dist/pairing/storage.js +0 -604
- package/node_modules/remote-pi/dist/pairing/storage.js.map +0 -1
- package/node_modules/remote-pi/dist/protocol/codec.d.ts +0 -7
- package/node_modules/remote-pi/dist/protocol/codec.js +0 -49
- package/node_modules/remote-pi/dist/protocol/codec.js.map +0 -1
- package/node_modules/remote-pi/dist/protocol/types.d.ts +0 -393
- package/node_modules/remote-pi/dist/protocol/types.js +0 -2
- package/node_modules/remote-pi/dist/protocol/types.js.map +0 -1
- package/node_modules/remote-pi/dist/rooms.d.ts +0 -29
- package/node_modules/remote-pi/dist/rooms.js +0 -57
- package/node_modules/remote-pi/dist/rooms.js.map +0 -1
- package/node_modules/remote-pi/dist/session/address.d.ts +0 -49
- package/node_modules/remote-pi/dist/session/address.js +0 -58
- package/node_modules/remote-pi/dist/session/address.js.map +0 -1
- package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +0 -55
- package/node_modules/remote-pi/dist/session/agent_bridge.js +0 -146
- package/node_modules/remote-pi/dist/session/agent_bridge.js.map +0 -1
- package/node_modules/remote-pi/dist/session/bridge.d.ts +0 -37
- package/node_modules/remote-pi/dist/session/bridge.js +0 -130
- package/node_modules/remote-pi/dist/session/bridge.js.map +0 -1
- package/node_modules/remote-pi/dist/session/broker.d.ts +0 -189
- package/node_modules/remote-pi/dist/session/broker.js +0 -507
- package/node_modules/remote-pi/dist/session/broker.js.map +0 -1
- package/node_modules/remote-pi/dist/session/broker_remote.d.ts +0 -74
- package/node_modules/remote-pi/dist/session/broker_remote.js +0 -583
- package/node_modules/remote-pi/dist/session/broker_remote.js.map +0 -1
- package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +0 -33
- package/node_modules/remote-pi/dist/session/cwd_lock.js +0 -126
- package/node_modules/remote-pi/dist/session/cwd_lock.js.map +0 -1
- package/node_modules/remote-pi/dist/session/envelope.d.ts +0 -33
- package/node_modules/remote-pi/dist/session/envelope.js +0 -117
- package/node_modules/remote-pi/dist/session/envelope.js.map +0 -1
- package/node_modules/remote-pi/dist/session/global_config.d.ts +0 -33
- package/node_modules/remote-pi/dist/session/global_config.js +0 -73
- package/node_modules/remote-pi/dist/session/global_config.js.map +0 -1
- package/node_modules/remote-pi/dist/session/ipc.d.ts +0 -27
- package/node_modules/remote-pi/dist/session/ipc.js +0 -22
- package/node_modules/remote-pi/dist/session/ipc.js.map +0 -1
- package/node_modules/remote-pi/dist/session/leader_election.d.ts +0 -32
- package/node_modules/remote-pi/dist/session/leader_election.js +0 -106
- package/node_modules/remote-pi/dist/session/leader_election.js.map +0 -1
- package/node_modules/remote-pi/dist/session/local_config.d.ts +0 -55
- package/node_modules/remote-pi/dist/session/local_config.js +0 -157
- package/node_modules/remote-pi/dist/session/local_config.js.map +0 -1
- package/node_modules/remote-pi/dist/session/mesh_message.d.ts +0 -20
- package/node_modules/remote-pi/dist/session/mesh_message.js +0 -28
- package/node_modules/remote-pi/dist/session/mesh_message.js.map +0 -1
- package/node_modules/remote-pi/dist/session/mesh_node.d.ts +0 -116
- package/node_modules/remote-pi/dist/session/mesh_node.js +0 -573
- package/node_modules/remote-pi/dist/session/mesh_node.js.map +0 -1
- package/node_modules/remote-pi/dist/session/peer.d.ts +0 -139
- package/node_modules/remote-pi/dist/session/peer.js +0 -451
- package/node_modules/remote-pi/dist/session/peer.js.map +0 -1
- package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +0 -13
- package/node_modules/remote-pi/dist/session/peer_inventory.js +0 -48
- package/node_modules/remote-pi/dist/session/peer_inventory.js.map +0 -1
- package/node_modules/remote-pi/dist/session/peer_limits.d.ts +0 -9
- package/node_modules/remote-pi/dist/session/peer_limits.js +0 -21
- package/node_modules/remote-pi/dist/session/peer_limits.js.map +0 -1
- package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +0 -39
- package/node_modules/remote-pi/dist/session/setup_wizard.js +0 -63
- package/node_modules/remote-pi/dist/session/setup_wizard.js.map +0 -1
- package/node_modules/remote-pi/dist/session/tool_gate.d.ts +0 -5
- package/node_modules/remote-pi/dist/session/tool_gate.js +0 -11
- package/node_modules/remote-pi/dist/session/tool_gate.js.map +0 -1
- package/node_modules/remote-pi/dist/session/tools.d.ts +0 -24
- package/node_modules/remote-pi/dist/session/tools.js +0 -252
- package/node_modules/remote-pi/dist/session/tools.js.map +0 -1
- package/node_modules/remote-pi/dist/session/wizard.d.ts +0 -13
- package/node_modules/remote-pi/dist/session/wizard.js +0 -20
- package/node_modules/remote-pi/dist/session/wizard.js.map +0 -1
- package/node_modules/remote-pi/dist/settings.d.ts +0 -15
- package/node_modules/remote-pi/dist/settings.js +0 -52
- package/node_modules/remote-pi/dist/settings.js.map +0 -1
- package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +0 -37
- package/node_modules/remote-pi/dist/transport/peer_channel.js +0 -98
- package/node_modules/remote-pi/dist/transport/peer_channel.js.map +0 -1
- package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +0 -29
- package/node_modules/remote-pi/dist/transport/pi_forward_client.js +0 -62
- package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +0 -1
- package/node_modules/remote-pi/dist/transport/relay_client.d.ts +0 -91
- package/node_modules/remote-pi/dist/transport/relay_client.js +0 -206
- package/node_modules/remote-pi/dist/transport/relay_client.js.map +0 -1
- package/node_modules/remote-pi/dist/ui/footer.d.ts +0 -32
- package/node_modules/remote-pi/dist/ui/footer.js +0 -34
- package/node_modules/remote-pi/dist/ui/footer.js.map +0 -1
- package/node_modules/remote-pi/docs/daemon.md +0 -289
- package/node_modules/remote-pi/package.json +0 -92
- package/node_modules/remote-pi/service-templates/launchd.plist.template +0 -35
- package/node_modules/remote-pi/service-templates/systemd.service.template +0 -19
- package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +0 -10
- package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +0 -38
- package/node_modules/remote-pi/skills/agent-network/SKILL.md +0 -310
|
@@ -1,1137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Orphaned LSP process reaper (#472), built on the instance registry (#449
|
|
3
|
-
* slice 1).
|
|
4
|
-
*
|
|
5
|
-
* Split into a PURE decision function (`decideOrphanReaping`) and an IMPURE
|
|
6
|
-
* sweep (`sweepOrphans`) so the decision logic is unit-testable with fake
|
|
7
|
-
* pid tables — no real process spawns/kills in tests.
|
|
8
|
-
*
|
|
9
|
-
* Why the registry reaper, not EOF/processId alone (see issue #472): both are
|
|
10
|
-
* best-effort hints a well-behaved server may honor (typescript-language-server
|
|
11
|
-
* does; ast-grep's native exe does not — an upstream LSP-spec violation). The
|
|
12
|
-
* registry reaper works regardless of why a stdin pipe write-end stayed open
|
|
13
|
-
* after the parent died (Windows handle-inheritance capture) — it identifies
|
|
14
|
-
* dead-parent instances directly and kills the full recorded child tree, with
|
|
15
|
-
* a command-line marker fallback for the case where the pid itself was
|
|
16
|
-
* recycled or the mid-tree pid link is broken (e.g. a dead node-wrapper whose
|
|
17
|
-
* native-exe grandchild is still alive under a different, unrecorded pid).
|
|
18
|
-
*
|
|
19
|
-
* #525 root cause: a parent instance was ONLY ever considered dead via
|
|
20
|
-
* `isPidAlive(instance.pid)` — a raw `process.kill(pid, 0)` check. Unlike
|
|
21
|
-
* child pids (which get a command-line/marker identity check via
|
|
22
|
-
* `matchProcess` to guard against a recycled pid), the PARENT pid had no
|
|
23
|
-
* identity verification at all, because `InstanceEntry` never recorded the
|
|
24
|
-
* parent's own command line. Windows recycles pids far more aggressively than
|
|
25
|
-
* POSIX (no zombie/wait-reaping semantics holding a dead pid "reserved"), so
|
|
26
|
-
* over a long enough window (observed: ~13h) a dead parent's pid is very
|
|
27
|
-
* plausibly reassigned to a live, unrelated process — `isPidAlive` then
|
|
28
|
-
* (correctly, per its own conservative contract) reports "alive", and the
|
|
29
|
-
* stale instance is never reaped, no matter how old its heartbeat is.
|
|
30
|
-
*
|
|
31
|
-
* The #525 fix is deliberately ASYMMETRIC by consequence:
|
|
32
|
-
*
|
|
33
|
-
* - **Heartbeat staleness (`STALE_HEARTBEAT_MS`) cleans REGISTRY ENTRIES,
|
|
34
|
-
* never kills.** A stale-heartbeat-but-pid-alive instance goes into
|
|
35
|
-
* `staleInstances` (entry dropped from instances.json) with ZERO process
|
|
36
|
-
* kills and its children still marker-protected. Why: the heartbeat call
|
|
37
|
-
* sites are runtime-turn.ts (per turn end) and quiet-window.ts (per run
|
|
38
|
-
* settle) ONLY — no timer exists. A pi session left OPEN but UNUSED
|
|
39
|
-
* overnight fires neither, so its heartbeat legitimately goes >6h stale
|
|
40
|
-
* while the session and its warm LSP fleet are genuinely alive. Killing on
|
|
41
|
-
* staleness would take that fleet down under the idle session — and
|
|
42
|
-
* `matchProcess` would NOT save it (its children really ARE that
|
|
43
|
-
* instance's LSP servers; identity verification guards against pid reuse,
|
|
44
|
-
* not against misclassifying a live parent). Removing just the entry is
|
|
45
|
-
* safe: the idle session's next turn re-registers via `registerInstance`.
|
|
46
|
-
* - **Process kills require a pid-confirmed-DEAD parent** (`deadInstances`),
|
|
47
|
-
* exactly as before #525. Only then are its children classified for
|
|
48
|
-
* kill/marker-search.
|
|
49
|
-
*
|
|
50
|
-
* This still fixes both observed #525 cases: the 13h-stale test-fixture
|
|
51
|
-
* entry AND the recycled-parent-pid case (a dead parent whose pid now
|
|
52
|
-
* belongs to an unrelated live process — `isPidAlive` lies, but the stale
|
|
53
|
-
* heartbeat gets the ENTRY dropped, which is all the pollution fix needs).
|
|
54
|
-
*/
|
|
55
|
-
/**
|
|
56
|
-
* A pid-ALIVE instance whose heartbeat is older than this gets its registry
|
|
57
|
-
* ENTRY removed — it is NEVER kill-eligible on staleness alone (see the
|
|
58
|
-
* module docstring's asymmetry note: an overnight-idle-but-alive session
|
|
59
|
-
* legitimately exceeds this threshold because heartbeats only fire at turn
|
|
60
|
-
* end / run settle, so staleness must clean records, not kill processes).
|
|
61
|
-
* 6 hours comfortably catches the observed 13h-stale pollution case.
|
|
62
|
-
*/
|
|
63
|
-
export const STALE_HEARTBEAT_MS = 6 * 60 * 60 * 1000;
|
|
64
|
-
import { spawn as nodeSpawn } from "node:child_process";
|
|
65
|
-
import * as fs from "node:fs";
|
|
66
|
-
import * as path from "node:path";
|
|
67
|
-
import { writeFileAtomicAsync } from "./atomic-write.js";
|
|
68
|
-
import { sweepOwnStagingFiles, } from "./atomic-write-staging.js";
|
|
69
|
-
import { acquireQuarantinePidFileLock } from "./bounded-pid-file-lock.js";
|
|
70
|
-
import { unrefChildAndPipes, } from "./child-unref.js";
|
|
71
|
-
import { incrementDegradationCount, recordDegradationOnce, } from "./degradation-ledger.js";
|
|
72
|
-
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
73
|
-
import { resolveBackstopStateDir } from "./instance-reaper-state.js";
|
|
74
|
-
import { isInstanceRegistryEnabled, pruneDeadInstances as pruneDeadRegistryInstances, readInstanceRegistry, } from "./instance-registry.js";
|
|
75
|
-
import { logLatency } from "./latency-logger.js";
|
|
76
|
-
import { queryProcessTable, windowsExe } from "./process-snapshot.js";
|
|
77
|
-
const isWindows = process.platform === "win32";
|
|
78
|
-
/**
|
|
79
|
-
* KILL eligibility: pid-confirmed-dead ONLY. Heartbeat staleness must never
|
|
80
|
-
* make an instance kill-eligible — an overnight-idle-but-alive session
|
|
81
|
-
* legitimately goes >STALE_HEARTBEAT_MS stale (heartbeats fire only at turn
|
|
82
|
-
* end / run settle; no timer exists), and killing its genuine live LSP
|
|
83
|
-
* children would pass `matchProcess` identity verification (they really are
|
|
84
|
-
* that instance's servers — the matcher guards against pid reuse, not
|
|
85
|
-
* against misclassifying a live parent). See the module docstring (#525).
|
|
86
|
-
*/
|
|
87
|
-
function isInstanceKillEligible(instance, isPidAlive) {
|
|
88
|
-
return !isPidAlive(instance.pid);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* REGISTRY-ENTRY staleness: heartbeat older than `STALE_HEARTBEAT_MS` (or
|
|
92
|
-
* unparseable — missing data must never keep a polluted entry alive
|
|
93
|
-
* forever). Drives entry removal ONLY, never kills (#525). This is what
|
|
94
|
-
* cleans the recycled-parent-pid case: `isPidAlive` lies for a long-dead
|
|
95
|
-
* parent whose pid the OS reassigned to an unrelated live process, but the
|
|
96
|
-
* stale heartbeat still gets the ENTRY dropped.
|
|
97
|
-
*/
|
|
98
|
-
function isInstanceEntryStale(instance, now) {
|
|
99
|
-
const heartbeatMs = Date.parse(instance.heartbeatAt);
|
|
100
|
-
if (Number.isNaN(heartbeatMs))
|
|
101
|
-
return true;
|
|
102
|
-
return now - heartbeatMs > STALE_HEARTBEAT_MS;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Markers claimed by any pid-ALIVE instance's children. A marker search
|
|
106
|
-
* kills by command-line match, so a marker that a live session also uses
|
|
107
|
-
* must never be searched — killing it would take down the live session's
|
|
108
|
-
* server. Protection is deliberately keyed on pid-liveness ALONE, regardless
|
|
109
|
-
* of heartbeat staleness (#525): a stale-heartbeat-but-alive instance (e.g.
|
|
110
|
-
* overnight-idle) must keep its children protected — protection stays
|
|
111
|
-
* conservative even where entry cleanup does not.
|
|
112
|
-
* Markers are per-process-unique by construction (sgconfig.ts embeds the
|
|
113
|
-
* pid), so this is defense in depth against non-unique markers ever
|
|
114
|
-
* reappearing (#472: the original shared baseline.sgconfig.yml would have
|
|
115
|
-
* made the fallback kill every live ast-grep on the machine).
|
|
116
|
-
*/
|
|
117
|
-
function collectLiveMarkers(registry, isPidAlive) {
|
|
118
|
-
const liveMarkers = new Set();
|
|
119
|
-
for (const instance of registry) {
|
|
120
|
-
if (!isPidAlive(instance.pid))
|
|
121
|
-
continue;
|
|
122
|
-
for (const child of instance.lspChildren) {
|
|
123
|
-
if (child.marker)
|
|
124
|
-
liveMarkers.add(child.marker);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return liveMarkers;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Classify one dead-parent instance's children into kills / marker-searches,
|
|
131
|
-
* appending onto the shared `out` accumulator. Extracted from
|
|
132
|
-
* `decideOrphanReaping` to keep cognitive complexity in check — no behavior
|
|
133
|
-
* change, just the per-instance inner loop pulled out.
|
|
134
|
-
*/
|
|
135
|
-
function classifyDeadInstanceChildren(instance, isPidAlive, matchProcess, liveMarkers, out) {
|
|
136
|
-
for (const child of instance.lspChildren) {
|
|
137
|
-
const childAlive = isPidAlive(child.pid);
|
|
138
|
-
if (childAlive) {
|
|
139
|
-
const identityOk = matchProcess
|
|
140
|
-
? matchProcess(child.pid, {
|
|
141
|
-
command: child.command,
|
|
142
|
-
marker: child.marker,
|
|
143
|
-
})
|
|
144
|
-
: true;
|
|
145
|
-
if (identityOk) {
|
|
146
|
-
out.childrenToKill.push({
|
|
147
|
-
pid: child.pid,
|
|
148
|
-
serverId: child.serverId,
|
|
149
|
-
command: child.command,
|
|
150
|
-
});
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Child pid is dead, or alive-but-identity-mismatched (recycled pid) —
|
|
155
|
-
// if we have a marker, surface it so the caller can find a live
|
|
156
|
-
// process (e.g. the native exe grandchild) by command-line match.
|
|
157
|
-
// Never surface a marker a live instance also claims (see above).
|
|
158
|
-
if (child.marker && !liveMarkers.has(child.marker)) {
|
|
159
|
-
out.markerSearches.push({
|
|
160
|
-
marker: child.marker,
|
|
161
|
-
serverId: child.serverId,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Pure decision function: given the registry state and injectable liveness /
|
|
168
|
-
* identity predicates, decide what to kill. Performs zero I/O.
|
|
169
|
-
*
|
|
170
|
-
* @param isPidAlive - `process.kill(pid, 0)`-style liveness check. Must be
|
|
171
|
-
* CONSERVATIVE: only pid-confirmed-dead (ESRCH) counts as dead. Any
|
|
172
|
-
* ambiguous result (EPERM, or the caller's fake table saying "unknown")
|
|
173
|
-
* must be treated as alive — never kill on an ambiguous signal-check.
|
|
174
|
-
* @param matchProcess - optional identity verification (e.g. confirm the
|
|
175
|
-
* live pid's command line still matches what we recorded) to guard against
|
|
176
|
-
* a recycled pid coincidentally matching. If omitted, liveness alone is used.
|
|
177
|
-
* @param now - epoch ms "now", for heartbeat-staleness comparison (#525).
|
|
178
|
-
* Injectable for deterministic tests; defaults to `Date.now()`. The two
|
|
179
|
-
* signals are ASYMMETRIC by consequence: pid-confirmed-dead ⇒
|
|
180
|
-
* `deadInstances` (kill-eligible + entry removal); pid-alive but heartbeat
|
|
181
|
-
* older than `STALE_HEARTBEAT_MS` ⇒ `staleInstances` (entry removal ONLY —
|
|
182
|
-
* never kills, never loses marker protection; the parent may be an
|
|
183
|
-
* overnight-idle-but-alive session). See the module docstring.
|
|
184
|
-
*/
|
|
185
|
-
export function decideOrphanReaping(registry, isPidAlive, matchProcess, now = Date.now()) {
|
|
186
|
-
const deadInstances = [];
|
|
187
|
-
const staleInstances = [];
|
|
188
|
-
const childrenToKill = [];
|
|
189
|
-
const markerSearches = [];
|
|
190
|
-
// Marker protection is pid-liveness ONLY — a stale-heartbeat-but-alive
|
|
191
|
-
// instance keeps its children protected (conservative on the kill side).
|
|
192
|
-
const liveMarkers = collectLiveMarkers(registry, isPidAlive);
|
|
193
|
-
for (const instance of registry) {
|
|
194
|
-
if (isInstanceKillEligible(instance, isPidAlive)) {
|
|
195
|
-
// pid-confirmed-dead: entry removal + children classified for kills.
|
|
196
|
-
deadInstances.push(instance);
|
|
197
|
-
classifyDeadInstanceChildren(instance, isPidAlive, matchProcess, liveMarkers, {
|
|
198
|
-
childrenToKill,
|
|
199
|
-
markerSearches,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
else if (isInstanceEntryStale(instance, now)) {
|
|
203
|
-
// pid-alive but stale heartbeat: record cleanup only — NO kills.
|
|
204
|
-
staleInstances.push(instance);
|
|
205
|
-
}
|
|
206
|
-
// else: alive + fresh heartbeat — leave it alone entirely.
|
|
207
|
-
}
|
|
208
|
-
return { deadInstances, staleInstances, childrenToKill, markerSearches };
|
|
209
|
-
}
|
|
210
|
-
// --- Impure liveness / identity / kill helpers ---
|
|
211
|
-
/** `process.kill(pid, 0)` liveness check: ESRCH ⇒ dead, anything else
|
|
212
|
-
* (EPERM, or no error thrown at all) ⇒ conservatively alive. Exported so
|
|
213
|
-
* other registry consumers (clients/lsp-budget.ts, #449 slice 2) reuse this
|
|
214
|
-
* exact liveness check rather than inventing a second one. */
|
|
215
|
-
export function realIsPidAlive(pid) {
|
|
216
|
-
if (!Number.isFinite(pid) || pid <= 0)
|
|
217
|
-
return false;
|
|
218
|
-
try {
|
|
219
|
-
process.kill(pid, 0);
|
|
220
|
-
return true; // no throw — process exists and we can signal it
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
const code = err?.code;
|
|
224
|
-
if (code === "ESRCH")
|
|
225
|
-
return false; // definitively dead
|
|
226
|
-
// EPERM (exists, no permission) or any other/unknown errno: ambiguous —
|
|
227
|
-
// never treat as dead.
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/** Maximum number of directory entries inspected by one staging sweep. */
|
|
232
|
-
export const ATOMIC_STAGE_SWEEP_MAX_ENTRIES = 512;
|
|
233
|
-
/**
|
|
234
|
-
* Remove orphaned generic atomic-write staging files from pi-lens-owned
|
|
235
|
-
* directories. This is deliberately a directory-entry sweep, not a watcher:
|
|
236
|
-
* it runs from session_start, reads at most `maxEntries` entries per directory,
|
|
237
|
-
* and never creates a process-lifetime handle.
|
|
238
|
-
*
|
|
239
|
-
* Safety invariants:
|
|
240
|
-
* - only the three atomic-write `.tmp-<pid>[-<thread>[-<seq>]]` shapes match;
|
|
241
|
-
* - directories and symlinks are never removed;
|
|
242
|
-
* - this process's pid is always protected, and every foreign pid is checked
|
|
243
|
-
* through the reaper's conservative ESRCH-only liveness seam;
|
|
244
|
-
* - every I/O failure is best-effort and cannot fail session_start.
|
|
245
|
-
*/
|
|
246
|
-
export async function sweepAtomicWriteStages(directories, options = {}) {
|
|
247
|
-
const requestedMax = options.maxEntries ?? ATOMIC_STAGE_SWEEP_MAX_ENTRIES;
|
|
248
|
-
const maxEntries = Number.isFinite(requestedMax)
|
|
249
|
-
? Math.max(1, Math.floor(requestedMax))
|
|
250
|
-
: ATOMIC_STAGE_SWEEP_MAX_ENTRIES;
|
|
251
|
-
const isPidAlive = options.isPidAlive ?? realIsPidAlive;
|
|
252
|
-
return sweepOwnStagingFiles(directories, { maxEntries, isPidAlive });
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Interpreter resolution, WQL escaping and the platform listing itself all
|
|
256
|
-
* live in the ONE process-table seam (#2443): `scripts/lib/process-scan.mjs`,
|
|
257
|
-
* reached from clients/ through `clients/process-snapshot.ts`. This file used
|
|
258
|
-
* to carry its own copy of each, which is how the three queries below drifted
|
|
259
|
-
* apart from the two in `clients/resource-sampler.ts` and the two in
|
|
260
|
-
* `scripts/`. `windowsExe` is re-exported by the seam and imported above.
|
|
261
|
-
*/
|
|
262
|
-
/** Search running processes whose command line contains `marker` (Windows,
|
|
263
|
-
* via CIM/WQL). Returns matching pids. Best-effort: any failure ⇒ [], but a
|
|
264
|
-
* failure is RECORDED (#1857 class sweep) — "the search found no orphan" and
|
|
265
|
-
* "the search never ran" produce the same empty array, and only the record
|
|
266
|
-
* tells them apart. */
|
|
267
|
-
async function findPidsByMarkerWindows(marker) {
|
|
268
|
-
if (!isWindows || !marker)
|
|
269
|
-
return [];
|
|
270
|
-
const result = await queryProcessTable({
|
|
271
|
-
fields: ["pid"],
|
|
272
|
-
filter: { column: "CommandLine", op: "like", values: [marker] },
|
|
273
|
-
// The query's own powershell.exe command line embeds the marker, so
|
|
274
|
-
// without this exclusion the search matches itself.
|
|
275
|
-
excludeSelfPid: true,
|
|
276
|
-
}, {
|
|
277
|
-
timeoutMs: BACKSTOP_SCAN_TIMEOUT_MS,
|
|
278
|
-
// #2527 review F2: omitting `onTimeout` falls through to
|
|
279
|
-
// child-unref.ts's default — one bare, unverified `child.kill()`, no
|
|
280
|
-
// tree kill, no identity-carrying record — inside the orphan backstop
|
|
281
|
-
// path, the exact abandonment `terminateScannerChild`'s own doc
|
|
282
|
-
// comment says it exists to prevent. This query shares the backstop's
|
|
283
|
-
// scanner identity and budget (same producer as
|
|
284
|
-
// `enumerateManagedProcesses`), so it gets the same kind.
|
|
285
|
-
onTimeout: (child) => terminateScannerChild(child, {
|
|
286
|
-
kind: "orphan-backstop-scanner-escalated",
|
|
287
|
-
timeoutMs: BACKSTOP_SCAN_TIMEOUT_MS,
|
|
288
|
-
}),
|
|
289
|
-
});
|
|
290
|
-
if (result.status !== "ok") {
|
|
291
|
-
recordDegradationOnce({
|
|
292
|
-
kind: "orphan-backstop-scan-failed",
|
|
293
|
-
subject: "marker-search",
|
|
294
|
-
reason: `marker command-line search ${result.status}`,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
return result.rows.map((row) => row.pid);
|
|
298
|
-
}
|
|
299
|
-
/** Fetch command lines for a set of pids in one query (Windows: CIM; POSIX:
|
|
300
|
-
* `ps`). Returns a pid → command-line map; pids that can't be resolved are
|
|
301
|
-
* simply absent (the caller treats absent as "identity unverifiable — do not
|
|
302
|
-
* kill by pid").
|
|
303
|
-
*
|
|
304
|
-
* Best-effort: any failure ⇒ empty map, RECORDED (#1857 class sweep). An
|
|
305
|
-
* errored query makes every recorded child unverifiable at once, which
|
|
306
|
-
* suppresses the entire registry-driven reap — and reads exactly like a
|
|
307
|
-
* registry whose children have all already exited. Only the record separates
|
|
308
|
-
* the two. */
|
|
309
|
-
async function queryCommandLines(pids) {
|
|
310
|
-
const valid = [...new Set(pids.filter((p) => Number.isFinite(p) && p > 0))];
|
|
311
|
-
const map = new Map();
|
|
312
|
-
if (valid.length === 0)
|
|
313
|
-
return map;
|
|
314
|
-
const result = await queryProcessTable({
|
|
315
|
-
fields: ["pid", "command"],
|
|
316
|
-
filter: { column: "ProcessId", op: "eq", values: valid },
|
|
317
|
-
}, {
|
|
318
|
-
timeoutMs: BACKSTOP_SCAN_TIMEOUT_MS,
|
|
319
|
-
// #2527 review F2: same rationale as findPidsByMarkerWindows above —
|
|
320
|
-
// route through the reaper's own tree-kill-and-verify machinery with
|
|
321
|
-
// the backstop's kind rather than falling through to the unverified
|
|
322
|
-
// default kill with no ledger record.
|
|
323
|
-
onTimeout: (child) => terminateScannerChild(child, {
|
|
324
|
-
kind: "orphan-backstop-scanner-escalated",
|
|
325
|
-
timeoutMs: BACKSTOP_SCAN_TIMEOUT_MS,
|
|
326
|
-
}),
|
|
327
|
-
});
|
|
328
|
-
// POSIX `ps -p` exits nonzero when NONE of the requested pids exist, which
|
|
329
|
-
// is a legitimate clean result for this caller, so that one status is not
|
|
330
|
-
// recorded on that platform. The shared collector calls it an exit failure;
|
|
331
|
-
// this caller's command contract makes it the expected empty table. Windows
|
|
332
|
-
// CIM has no such convention, so an exit failure there is a real failure.
|
|
333
|
-
const psReportedNoSuchPid = !isWindows && result.status === "exit-error";
|
|
334
|
-
if (result.status !== "ok" && !psReportedNoSuchPid) {
|
|
335
|
-
recordDegradationOnce({
|
|
336
|
-
kind: "orphan-backstop-scan-failed",
|
|
337
|
-
subject: "identity-query",
|
|
338
|
-
reason: `command-line identity query ${result.status}; reap suppressed this sweep`,
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
for (const row of result.rows)
|
|
342
|
-
map.set(row.pid, row.command);
|
|
343
|
-
return map;
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Build a `matchProcess` identity predicate from a pid → command-line map
|
|
347
|
-
* (as produced by `queryCommandLines`). PURE — exported for unit testing.
|
|
348
|
-
*
|
|
349
|
-
* Semantics (guarding pid kills against pid recycling):
|
|
350
|
-
* - pid absent from the map ⇒ false: identity is UNVERIFIABLE, so never kill
|
|
351
|
-
* by pid (the marker-search fallback may still catch a real orphan).
|
|
352
|
-
* - marker recorded and present in the command line ⇒ match (strongest
|
|
353
|
-
* signal — markers are per-spawn-unique).
|
|
354
|
-
* - else: the recorded command's basename appears (case-insensitive) in the
|
|
355
|
-
* command line ⇒ match. Empty basename never matches (guard against a
|
|
356
|
-
* recorded empty/odd command matching everything via `includes("")`).
|
|
357
|
-
*/
|
|
358
|
-
export function buildIdentityMatcher(cmdlines) {
|
|
359
|
-
return (pid, expected) => {
|
|
360
|
-
const cmdline = cmdlines.get(pid);
|
|
361
|
-
if (cmdline === undefined)
|
|
362
|
-
return false; // unverifiable ⇒ never kill by pid
|
|
363
|
-
if (expected.marker && cmdline.includes(expected.marker))
|
|
364
|
-
return true;
|
|
365
|
-
const basename = path.basename(expected.command ?? "").toLowerCase();
|
|
366
|
-
if (!basename)
|
|
367
|
-
return false;
|
|
368
|
-
return cmdline.toLowerCase().includes(basename);
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
/** Post-kill liveness poll budget. `taskkill /F /T` returns before the kernel
|
|
372
|
-
* has finished tearing the tree down, so a single immediate check would
|
|
373
|
-
* report false `alive`s. */
|
|
374
|
-
const KILL_VERIFY_ATTEMPTS = 5;
|
|
375
|
-
const KILL_VERIFY_INTERVAL_MS = 100;
|
|
376
|
-
/** `setTimeout` as an awaitable, with the timer unref'd so a settled one-shot
|
|
377
|
-
* `pi --print` never waits on best-effort kill verification. */
|
|
378
|
-
function sleepUnref(ms) {
|
|
379
|
-
return new Promise((resolve) => {
|
|
380
|
-
const timer = setTimeout(resolve, ms);
|
|
381
|
-
timer.unref();
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Force-kill a pid's full process tree, then VERIFY. Windows: `taskkill /F
|
|
386
|
-
* /T`. POSIX: mirror killProcessTree's process-group kill, falling back to a
|
|
387
|
-
* direct signal. The kill itself stays best-effort (all errors swallowed) —
|
|
388
|
-
* what changed in #1857 is that the caller now learns the outcome, so an
|
|
389
|
-
* unverified kill can be recorded with identity instead of being counted as
|
|
390
|
-
* a success.
|
|
391
|
-
*
|
|
392
|
-
* Verification direction is conservative in the safe direction: a recycled
|
|
393
|
-
* pid now belonging to an unrelated live process reports `alive`, so a real
|
|
394
|
-
* reap can be under-reported, but a failed reap is never reported as done.
|
|
395
|
-
*/
|
|
396
|
-
async function killPidTree(pid, options = {}) {
|
|
397
|
-
if (!Number.isFinite(pid) || pid <= 0)
|
|
398
|
-
return "invalid";
|
|
399
|
-
if (isWindows) {
|
|
400
|
-
try {
|
|
401
|
-
const taskkill = windowsExe("taskkill.exe");
|
|
402
|
-
const killer = nodeSpawn(taskkill, ["/F", "/T", "/PID", String(pid)], {
|
|
403
|
-
shell: false,
|
|
404
|
-
windowsHide: true,
|
|
405
|
-
stdio: "ignore",
|
|
406
|
-
});
|
|
407
|
-
unrefChildAndPipes(killer);
|
|
408
|
-
await new Promise((resolve) => {
|
|
409
|
-
killer.once("close", () => resolve());
|
|
410
|
-
killer.once("error", () => resolve());
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
catch {
|
|
414
|
-
// best-effort
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
try {
|
|
419
|
-
process.kill(-pid, "SIGKILL");
|
|
420
|
-
}
|
|
421
|
-
catch {
|
|
422
|
-
try {
|
|
423
|
-
process.kill(pid, "SIGKILL");
|
|
424
|
-
}
|
|
425
|
-
catch {
|
|
426
|
-
// best-effort — process may already be gone
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return await verifyPidGone(pid, options);
|
|
431
|
-
}
|
|
432
|
-
/** Poll `isPidAlive` for a short budget after a kill attempt. */
|
|
433
|
-
async function verifyPidGone(pid, options = {}) {
|
|
434
|
-
const isPidAlive = options.isPidAlive ?? realIsPidAlive;
|
|
435
|
-
const attempts = Math.max(1, options.verifyAttempts ?? KILL_VERIFY_ATTEMPTS);
|
|
436
|
-
const intervalMs = Math.max(0, options.verifyIntervalMs ?? KILL_VERIFY_INTERVAL_MS);
|
|
437
|
-
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
438
|
-
let alive;
|
|
439
|
-
try {
|
|
440
|
-
alive = isPidAlive(pid);
|
|
441
|
-
}
|
|
442
|
-
catch {
|
|
443
|
-
// An unreadable liveness probe is not evidence of death.
|
|
444
|
-
alive = true;
|
|
445
|
-
}
|
|
446
|
-
if (!alive)
|
|
447
|
-
return "gone";
|
|
448
|
-
if (attempt < attempts - 1)
|
|
449
|
-
await sleepUnref(intervalMs);
|
|
450
|
-
}
|
|
451
|
-
return "alive";
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* #658: known pi-lens-managed LSP/scanner binary names (clients/lsp/server.ts
|
|
455
|
-
* spawn candidates), used by the registry-INDEPENDENT backstop sweep below.
|
|
456
|
-
* `opengrep-core` is opengrep's own native subprocess (not spawned directly by
|
|
457
|
-
* pi-lens, but still part of the tree we manage). `yaml-language-server` and
|
|
458
|
-
* `typescript-language-server` are node-launched — their OS image name is
|
|
459
|
-
* `node`/`node.exe`, so matching is done against the full command line
|
|
460
|
-
* (script path), not the process image name, exactly like the existing
|
|
461
|
-
* marker-search's WQL LIKE query below.
|
|
462
|
-
*/
|
|
463
|
-
const MANAGED_BINARIES = [
|
|
464
|
-
{ name: "ast-grep", launcher: "native" },
|
|
465
|
-
{ name: "opengrep-core", launcher: "native" },
|
|
466
|
-
{ name: "opengrep", launcher: "native" },
|
|
467
|
-
{ name: "marksman", launcher: "native" },
|
|
468
|
-
{ name: "zizmor", launcher: "native" },
|
|
469
|
-
{ name: "typos-lsp", launcher: "native" },
|
|
470
|
-
{ name: "yaml-language-server", launcher: "node" },
|
|
471
|
-
{ name: "typescript-language-server", launcher: "node" },
|
|
472
|
-
];
|
|
473
|
-
/** Derived, never hand-written: the command-line substrings every platform
|
|
474
|
-
* matches against. */
|
|
475
|
-
export const MANAGED_BINARY_NAMES = MANAGED_BINARIES.map((binary) => binary.name);
|
|
476
|
-
/**
|
|
477
|
-
* Derived Windows `Win32_Process.Name` values for the enumeration pre-filter
|
|
478
|
-
* (#1857). Equality on `Name` replaces eight leading-wildcard `CommandLine
|
|
479
|
-
* LIKE '%…%'` clauses; the node-launched entries collapse to `node.exe`, so
|
|
480
|
-
* the query returns a SUPERSET that JS then narrows by the same
|
|
481
|
-
* `MANAGED_BINARY_NAMES` substring test POSIX already uses. Identical
|
|
482
|
-
* semantics, one filtering rule.
|
|
483
|
-
*/
|
|
484
|
-
export const MANAGED_IMAGE_NAMES = [
|
|
485
|
-
...new Set(MANAGED_BINARIES.map((binary) => binary.launcher === "node" ? "node.exe" : `${binary.name}.exe`)),
|
|
486
|
-
];
|
|
487
|
-
/**
|
|
488
|
-
* PURE decision function for the #658 registry-independent backstop: given a
|
|
489
|
-
* live OS-process snapshot (already filtered to known managed binary names)
|
|
490
|
-
* and the current registry snapshot, decide which processes are backstop-
|
|
491
|
-
* kill-eligible. Zero I/O — unit-testable with fake data, mirroring
|
|
492
|
-
* `decideOrphanReaping`.
|
|
493
|
-
*
|
|
494
|
-
* A process is eligible ONLY when ALL of:
|
|
495
|
-
* - its pid is NOT already tracked in any instance's `lspChildren[]` (tracked
|
|
496
|
-
* pids stay owned by the registry-driven `decideOrphanReaping` path above —
|
|
497
|
-
* this backstop must never race or duplicate that logic);
|
|
498
|
-
* - its reported parent pid is a verifiable, well-formed pid (finite,
|
|
499
|
-
* positive, and not equal to its own pid) — an unresolvable/malformed
|
|
500
|
-
* parent pid is UNVERIFIABLE, never treated as "confirmed dead" (this is a
|
|
501
|
-
* stricter contract than `realIsPidAlive`'s own conservatism, because here
|
|
502
|
-
* an invalid value means "the OS couldn't tell us", not "confirmed gone");
|
|
503
|
-
* - `isPidAlive(parentPid)` reports the parent as dead.
|
|
504
|
-
*
|
|
505
|
-
* Note the direction of the ambiguity guard: if `parentPid` itself was
|
|
506
|
-
* recycled onto an unrelated live process, `isPidAlive` conservatively
|
|
507
|
-
* reports "alive" and the process is (safely) left alone — a false negative,
|
|
508
|
-
* never a false positive kill. Binary name alone is never sufficient (name
|
|
509
|
-
* matching only decides which processes are candidates for this check at
|
|
510
|
-
* all); a live parent is never overridden "however unfamiliar" the process.
|
|
511
|
-
*/
|
|
512
|
-
export function decideBackstopOrphanReaping(processes, registry, isPidAlive, options = {}) {
|
|
513
|
-
return partitionBackstopCandidates(processes, registry, isPidAlive, options)
|
|
514
|
-
.eligible;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Minimum process age before a name-matching, dead-parent process becomes
|
|
518
|
-
* backstop-kill-eligible (#1857 item 4).
|
|
519
|
-
*
|
|
520
|
-
* The hazard: LSP servers launch through `.cmd` shims with `shell: true`, so
|
|
521
|
-
* the pid registered in `lspChildren[]` is the shim's. Between the real
|
|
522
|
-
* server's spawn and its registration there is a window in which the server
|
|
523
|
-
* is name-matching and untracked. The 2026-08-20 session measured that window
|
|
524
|
-
* at ~890ms (spawn 10:54:00.602, registration 10:54:01.492) with a sweep
|
|
525
|
-
* ending at 10:53:59.996 — a near miss saved only by the parent-alive guard,
|
|
526
|
-
* which is a guard about the PARENT and therefore does not cover a shim whose
|
|
527
|
-
* parent is briefly unresolvable.
|
|
528
|
-
*
|
|
529
|
-
* 60s is roughly 67x the observed window and costs nothing against real
|
|
530
|
-
* orphans, which are minutes-to-hours old by the time any sweep sees them.
|
|
531
|
-
*/
|
|
532
|
-
export const BACKSTOP_SPAWN_GRACE_MS = 60_000;
|
|
533
|
-
/**
|
|
534
|
-
* The single implementation of the backstop decision. Returns the eligible
|
|
535
|
-
* set plus the two age-rejected sets, so `sweepUntrackedOrphans` can record
|
|
536
|
-
* WHY a candidate was spared instead of losing it into an unexplained count.
|
|
537
|
-
*/
|
|
538
|
-
export function partitionBackstopCandidates(processes, registry, isPidAlive, options = {}) {
|
|
539
|
-
const graceMs = Math.max(0, options.graceMs ?? BACKSTOP_SPAWN_GRACE_MS);
|
|
540
|
-
const trackedPids = new Set();
|
|
541
|
-
for (const instance of registry) {
|
|
542
|
-
for (const child of instance.lspChildren)
|
|
543
|
-
trackedPids.add(child.pid);
|
|
544
|
-
}
|
|
545
|
-
const partition = {
|
|
546
|
-
eligible: [],
|
|
547
|
-
tooFresh: [],
|
|
548
|
-
unknownAge: [],
|
|
549
|
-
};
|
|
550
|
-
for (const proc of processes) {
|
|
551
|
-
if (trackedPids.has(proc.pid))
|
|
552
|
-
continue; // owned by the registry-driven reaper
|
|
553
|
-
if (!Number.isFinite(proc.parentPid) || proc.parentPid <= 0)
|
|
554
|
-
continue; // unverifiable
|
|
555
|
-
if (proc.parentPid === proc.pid)
|
|
556
|
-
continue; // malformed data guard
|
|
557
|
-
if (isPidAlive(proc.parentPid))
|
|
558
|
-
continue; // live parent — never kill
|
|
559
|
-
// Spawn-grace guard, applied LAST so the buckets only ever contain
|
|
560
|
-
// processes that passed every other eligibility test.
|
|
561
|
-
if (graceMs > 0) {
|
|
562
|
-
const ageMs = proc.ageMs;
|
|
563
|
-
if (typeof ageMs !== "number" || !Number.isFinite(ageMs)) {
|
|
564
|
-
partition.unknownAge.push(proc);
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
if (ageMs < graceMs) {
|
|
568
|
-
partition.tooFresh.push(proc);
|
|
569
|
-
continue;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
partition.eligible.push(proc);
|
|
573
|
-
}
|
|
574
|
-
return partition;
|
|
575
|
-
}
|
|
576
|
-
/** Enumerate live OS processes whose command line contains one of
|
|
577
|
-
* `MANAGED_BINARY_NAMES` (#658), independent of the instance registry.
|
|
578
|
-
* One projected process-table query through the shared seam, narrowed to
|
|
579
|
-
* managed binaries in JS; returns `{pid, parentPid, command, ageMs}` rows.
|
|
580
|
-
* Best-effort: any failure ⇒ [], with the scan status carried alongside so
|
|
581
|
-
* an empty table is never mistaken for a scan that did not run. */
|
|
582
|
-
async function enumerateManagedProcesses(options = {}) {
|
|
583
|
-
// #1857: `Name = '…'` equality replaces eight leading-wildcard
|
|
584
|
-
// `CommandLine LIKE '%…%'` clauses, and the projection carries an age so
|
|
585
|
-
// the spawn-grace guard has something to reason from. The image-name set is
|
|
586
|
-
// DERIVED from MANAGED_BINARIES (MANAGED_IMAGE_NAMES), so adding a managed
|
|
587
|
-
// binary cannot leave a stale parallel list behind.
|
|
588
|
-
//
|
|
589
|
-
// POSIX `ps` cannot express that filter — the seam reports it as
|
|
590
|
-
// `serverSideFiltered: false` and hands back the whole table — which is why
|
|
591
|
-
// `narrowToManagedBinaries` runs on BOTH platforms rather than only on the
|
|
592
|
-
// POSIX branch. Windows over-collects for its own reason anyway
|
|
593
|
-
// (`node.exe` is a superset image name covering every node process on the
|
|
594
|
-
// machine).
|
|
595
|
-
const scanTimeoutMs = options.timeoutMs ?? BACKSTOP_SCAN_TIMEOUT_MS;
|
|
596
|
-
const result = await queryProcessTable({
|
|
597
|
-
fields: ["pid", "ppid", "ageMs", "command"],
|
|
598
|
-
filter: { column: "Name", op: "eq", values: MANAGED_IMAGE_NAMES },
|
|
599
|
-
}, {
|
|
600
|
-
timeoutMs: scanTimeoutMs,
|
|
601
|
-
onTimeout: (child) => terminateScannerChild(child, {
|
|
602
|
-
verifyAttempts: options.verifyAttempts,
|
|
603
|
-
verifyIntervalMs: options.verifyIntervalMs,
|
|
604
|
-
kind: "orphan-backstop-scanner-escalated",
|
|
605
|
-
timeoutMs: scanTimeoutMs,
|
|
606
|
-
}),
|
|
607
|
-
});
|
|
608
|
-
return narrowToManagedBinaries(result.rows.map((row) => ({
|
|
609
|
-
pid: row.pid,
|
|
610
|
-
parentPid: row.ppid,
|
|
611
|
-
command: row.command,
|
|
612
|
-
ageMs: row.ageMs,
|
|
613
|
-
})), result);
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* The ONE place a raw OS-process snapshot is narrowed to managed binaries.
|
|
617
|
-
* Both platforms over-collect for their own reason — Windows because
|
|
618
|
-
* `node.exe` is a superset image name covering every node process on the
|
|
619
|
-
* machine, POSIX because `ps -e` has no server-side filter — so the narrowing
|
|
620
|
-
* lives at a single call site rather than being repeated per branch, where one
|
|
621
|
-
* copy could rot or be dropped unnoticed.
|
|
622
|
-
*/
|
|
623
|
-
function narrowToManagedBinaries(processes, result) {
|
|
624
|
-
return {
|
|
625
|
-
processes: processes.filter((proc) => matchesManagedBinary(proc.command)),
|
|
626
|
-
status: result.status,
|
|
627
|
-
timeoutKill: result.timeoutKill,
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Terminate a scanner child that blew the scan timeout, using the reaper's
|
|
632
|
-
* OWN tree-kill-and-verify machinery (#1864 review F3).
|
|
633
|
-
*
|
|
634
|
-
* The previous handler sent one bare signal to the direct child and resolved
|
|
635
|
-
* in the same tick. On Windows that signal reaches only the `powershell.exe`
|
|
636
|
-
* we spawned, never a CIM worker beneath it, and nothing checked whether it
|
|
637
|
-
* died. An orphan sweep that leaks its own scanner is the defect the sweep
|
|
638
|
-
* exists to fix, so the scanner now gets exactly what an orphan gets:
|
|
639
|
-
* `taskkill /F /T` or a POSIX group kill, then a verified liveness poll. The
|
|
640
|
-
* escalation is recorded with the scanner's identity, never swallowed.
|
|
641
|
-
*
|
|
642
|
-
* The kill attempt itself is real regardless of what happens next: this
|
|
643
|
-
* handler is invoked the instant the caller's timer elapses, which can race
|
|
644
|
-
* a query child that is genuinely about to close successfully (its own
|
|
645
|
-
* "close" event may still win the overall settle — see
|
|
646
|
-
* `child-unref.ts#spawnCollectStdoutResult`, which resolves on whichever of
|
|
647
|
-
* "close" or the timeout handler's own settle lands first). This function has
|
|
648
|
-
* no way to observe that outcome — it runs strictly BEFORE the caller's
|
|
649
|
-
* promise resolves — so it always records the escalation it actually
|
|
650
|
-
* attempted; callers whose escalation kind can fire on that race classify it
|
|
651
|
-
* as informational rather than a warning (see
|
|
652
|
-
* `degradation-ledger.ts`'s `resource-sampler-scanner-escalated` doc comment).
|
|
653
|
-
*/
|
|
654
|
-
export async function terminateScannerChild(child, options) {
|
|
655
|
-
const pid = child.pid;
|
|
656
|
-
const outcome = typeof pid === "number" && pid > 0
|
|
657
|
-
? await killPidTree(pid, {
|
|
658
|
-
verifyAttempts: options.verifyAttempts,
|
|
659
|
-
verifyIntervalMs: options.verifyIntervalMs,
|
|
660
|
-
})
|
|
661
|
-
: "invalid";
|
|
662
|
-
incrementDegradationCount({
|
|
663
|
-
kind: options.kind,
|
|
664
|
-
subject: `${isWindows ? "win32-cim" : "posix-ps"}#${pid ?? "no-pid"}`,
|
|
665
|
-
reason: `scan exceeded its ${options.timeoutMs}ms timeout; tree kill reported ${outcome}`,
|
|
666
|
-
});
|
|
667
|
-
return outcome;
|
|
668
|
-
}
|
|
669
|
-
/** Hard bound on the enumeration child. The 2026-08-20 incident measured the
|
|
670
|
-
* Windows CIM query at 9344ms under session_start contention; this caps the
|
|
671
|
-
* worst case rather than trusting the median. */
|
|
672
|
-
export const BACKSTOP_SCAN_TIMEOUT_MS = 5_000;
|
|
673
|
-
/** The one command-line match rule, shared by both platforms' parsers. */
|
|
674
|
-
function matchesManagedBinary(command) {
|
|
675
|
-
const lower = command.toLowerCase();
|
|
676
|
-
return MANAGED_BINARY_NAMES.some((name) => lower.includes(name.toLowerCase()));
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Fire-and-forget REGISTRY-INDEPENDENT backstop sweep (#658): finds
|
|
680
|
-
* pi-lens-managed LSP/scanner processes the registry-driven `sweepOrphans`
|
|
681
|
-
* can never see again once their registry trace is lost (a stale-heartbeat
|
|
682
|
-
* entry removal, or a `killPidTree` call that failed silently) — enumerates
|
|
683
|
-
* live OS processes by known binary name, and kills any that are both
|
|
684
|
-
* untracked and have a confirmed-dead parent. A strictly ADDITIVE second
|
|
685
|
-
* layer: `sweepOrphans`'s registry-driven path is untouched and remains the
|
|
686
|
-
* cheap, correct common case.
|
|
687
|
-
*
|
|
688
|
-
* Kill-attempt retry: deliberately NO separate retry-tracking state. A
|
|
689
|
-
* process that survives its kill stays alive, untracked, and dead-parented,
|
|
690
|
-
* so the next sweep re-classifies it as eligible and tries again. #1857 added
|
|
691
|
-
* the missing half of that story: the survivor is now RECORDED, by identity,
|
|
692
|
-
* through `incrementDegradationCount`, instead of being counted as a reap.
|
|
693
|
-
*
|
|
694
|
-
* Cost and cadence (#1857): the sweep is deferred off the session_start
|
|
695
|
-
* critical path by `scheduleUntrackedOrphanSweep`, its enumeration child has
|
|
696
|
-
* a hard `BACKSTOP_SCAN_TIMEOUT_MS` timeout, and a machine-wide wall-clock
|
|
697
|
-
* cooldown stops every session from paying for it. The cooldown is a
|
|
698
|
-
* TIMESTAMP FILE, not a process-lifetime latch: it re-arms by the clock, so a
|
|
699
|
-
* long-lived host whose sessions keep starting keeps reaping, and a session
|
|
700
|
-
* that outlives the cooldown is no worse off than before, when the sweep ran
|
|
701
|
-
* exactly once at its start.
|
|
702
|
-
*
|
|
703
|
-
* Mutual exclusion (#1864 review F1): the cooldown check and the stamp write
|
|
704
|
-
* were a read-check-write, and an atomic rename makes each WRITE atomic
|
|
705
|
-
* without making the PAIR exclusive — two sessions starting together both read
|
|
706
|
-
* "no stamp", both wrote, and both swept. The claim now runs under
|
|
707
|
-
* `acquireQuarantinePidFileLock` (clients/bounded-pid-file-lock.ts:179), the
|
|
708
|
-
* repo's existing async cross-process lock for work that spans awaited I/O.
|
|
709
|
-
* The lock is taken FIRST and there is exactly ONE cooldown read, inside it,
|
|
710
|
-
* so check and claim are a single serialized step rather than a pair with a
|
|
711
|
-
* window between them. A contended sweep skips rather than waits: the loser
|
|
712
|
-
* has nothing useful to do, and blocking would put a best-effort background
|
|
713
|
-
* sweep back on somebody's clock.
|
|
714
|
-
*
|
|
715
|
-
* Never throws — every step is wrapped so a reap failure cannot block or
|
|
716
|
-
* crash the caller (session_start).
|
|
717
|
-
*/
|
|
718
|
-
export async function sweepUntrackedOrphans(options = {}) {
|
|
719
|
-
const startedAt = Date.now();
|
|
720
|
-
if (!isInstanceRegistryEnabled()) {
|
|
721
|
-
return logBackstopOutcome("disabled", startedAt, {}, options.onComplete);
|
|
722
|
-
}
|
|
723
|
-
const cooldownMs = Math.max(0, options.cooldownMs ?? BACKSTOP_COOLDOWN_MS);
|
|
724
|
-
let release = null;
|
|
725
|
-
try {
|
|
726
|
-
// LOCK FIRST, then read the stamp. The ORDER is the fix. A cooldown
|
|
727
|
-
// check outside the lock followed by a stamp write inside it is still a
|
|
728
|
-
// read-check-write: two processes both pass the outside check before
|
|
729
|
-
// either acquires. Keeping a second check inside the lock as well would
|
|
730
|
-
// leave the outside one doing nothing a mutation could detect, so there
|
|
731
|
-
// is exactly ONE check and it lives inside the lock — check and claim
|
|
732
|
-
// are one serialized step. Locking before an eventual skip costs one
|
|
733
|
-
// mkdir, on a timer that already fired 30 seconds after session start.
|
|
734
|
-
release = await acquireBackstopLock();
|
|
735
|
-
if (release === null) {
|
|
736
|
-
// Another process holds the sweep right now. Not an error, and not a
|
|
737
|
-
// cooldown either — a distinct record, because "somebody else is
|
|
738
|
-
// sweeping" and "we swept recently" have different causes.
|
|
739
|
-
return logBackstopOutcome("concurrent", startedAt, {}, options.onComplete);
|
|
740
|
-
}
|
|
741
|
-
const throttled = await isWithinCooldown(Date.now(), cooldownMs, options);
|
|
742
|
-
if (throttled !== undefined) {
|
|
743
|
-
return logBackstopOutcome("cooldown", startedAt, {
|
|
744
|
-
sinceLastMs: throttled,
|
|
745
|
-
}, options.onComplete);
|
|
746
|
-
}
|
|
747
|
-
// Claim the cooldown slot BEFORE the scan, not after: a sweep that dies
|
|
748
|
-
// mid-scan must not re-run at full cost on every retry.
|
|
749
|
-
await writeBackstopStamp(startedAt);
|
|
750
|
-
const [registry, scan] = await Promise.all([
|
|
751
|
-
readInstanceRegistry(),
|
|
752
|
-
enumerateManagedProcesses({
|
|
753
|
-
timeoutMs: options.scanTimeoutMs,
|
|
754
|
-
verifyAttempts: options.verifyAttempts,
|
|
755
|
-
verifyIntervalMs: options.verifyIntervalMs,
|
|
756
|
-
}),
|
|
757
|
-
]);
|
|
758
|
-
if (scan.status !== "ok") {
|
|
759
|
-
// An enumeration that errored or timed out produced an EMPTY result
|
|
760
|
-
// for a reason that is not "nothing to reap" — record it as such.
|
|
761
|
-
recordDegradationOnce({
|
|
762
|
-
kind: "orphan-backstop-scan-failed",
|
|
763
|
-
subject: isWindows ? "win32-cim" : "posix-ps",
|
|
764
|
-
reason: `process enumeration ${scan.status}`,
|
|
765
|
-
});
|
|
766
|
-
return logBackstopOutcome("error", startedAt, {
|
|
767
|
-
scanStatus: scan.status,
|
|
768
|
-
scannerKill: scan.timeoutKill,
|
|
769
|
-
scanned: scan.processes.length,
|
|
770
|
-
}, options.onComplete);
|
|
771
|
-
}
|
|
772
|
-
const partition = partitionBackstopCandidates(scan.processes, registry, realIsPidAlive, { graceMs: options.graceMs });
|
|
773
|
-
for (const proc of partition.unknownAge) {
|
|
774
|
-
// A spared kill must never be silent: without this the guard would
|
|
775
|
-
// look identical to "found nothing".
|
|
776
|
-
incrementDegradationCount({
|
|
777
|
-
kind: "orphan-backstop-age-unknown",
|
|
778
|
-
subject: describeManagedProcess(proc),
|
|
779
|
-
reason: "OS snapshot reported no usable process creation time",
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
const killed = [];
|
|
783
|
-
const unverified = [];
|
|
784
|
-
for (const proc of partition.eligible) {
|
|
785
|
-
const identity = describeManagedProcess(proc);
|
|
786
|
-
const outcome = await killPidTree(proc.pid, {
|
|
787
|
-
verifyAttempts: options.verifyAttempts,
|
|
788
|
-
verifyIntervalMs: options.verifyIntervalMs,
|
|
789
|
-
});
|
|
790
|
-
if (outcome === "gone") {
|
|
791
|
-
killed.push(identity);
|
|
792
|
-
continue;
|
|
793
|
-
}
|
|
794
|
-
unverified.push(identity);
|
|
795
|
-
incrementDegradationCount({
|
|
796
|
-
kind: "orphan-backstop-kill-unverified",
|
|
797
|
-
subject: identity,
|
|
798
|
-
reason: `kill attempted, process still ${outcome === "alive" ? "alive" : "unresolvable"}`,
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
const retryAt = scheduleGraceRetryIfNeeded(partition, options);
|
|
802
|
-
return logBackstopOutcome(classifyKillOutcome(killed.length, unverified.length), startedAt, {
|
|
803
|
-
scanStatus: scan.status,
|
|
804
|
-
scanned: scan.processes.length,
|
|
805
|
-
eligible: partition.eligible.length,
|
|
806
|
-
tooFresh: partition.tooFresh.length,
|
|
807
|
-
unknownAge: partition.unknownAge.length,
|
|
808
|
-
killed: killed.length,
|
|
809
|
-
killUnverified: unverified.length,
|
|
810
|
-
// Identity of the kill, bounded — #1857 item 3. Counts alone can
|
|
811
|
-
// never answer "which process did we kill?".
|
|
812
|
-
killedProcesses: killed.slice(0, BACKSTOP_IDENTITY_LOG_LIMIT),
|
|
813
|
-
unverifiedProcesses: unverified.slice(0, BACKSTOP_IDENTITY_LOG_LIMIT),
|
|
814
|
-
graceRetryInMs: retryAt,
|
|
815
|
-
}, options.onComplete);
|
|
816
|
-
}
|
|
817
|
-
catch (error) {
|
|
818
|
-
// The sweep must never throw out of session_start — but "threw" is now
|
|
819
|
-
// a distinguishable record rather than a silent absence.
|
|
820
|
-
recordDegradationOnce({
|
|
821
|
-
kind: "orphan-backstop-scan-failed",
|
|
822
|
-
subject: "sweep",
|
|
823
|
-
reason: `sweep threw: ${error instanceof Error ? error.message : String(error)}`,
|
|
824
|
-
});
|
|
825
|
-
return logBackstopOutcome("error", startedAt, { threw: true }, options.onComplete);
|
|
826
|
-
}
|
|
827
|
-
finally {
|
|
828
|
-
if (release) {
|
|
829
|
-
await release().catch(() => {
|
|
830
|
-
// A lock we cannot release goes stale on its own timer.
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
/** Map verified/unverified kill counts onto the outcome. The only place that
|
|
836
|
-
* mapping exists. */
|
|
837
|
-
function classifyKillOutcome(killed, unverified) {
|
|
838
|
-
if (unverified === 0)
|
|
839
|
-
return killed > 0 ? "reaped" : "clean";
|
|
840
|
-
return killed > 0 ? "partial" : "unverified";
|
|
841
|
-
}
|
|
842
|
-
/**
|
|
843
|
-
* Minimum wall-clock gap between backstop sweeps, machine-wide. The backstop
|
|
844
|
-
* catches processes that are already orphaned and will stay orphaned; it is
|
|
845
|
-
* not time-critical, so paying its cost once per half hour instead of once
|
|
846
|
-
* per session_start removes essentially all of its aggregate cost.
|
|
847
|
-
*/
|
|
848
|
-
const BACKSTOP_COOLDOWN_MS = 30 * 60 * 1000;
|
|
849
|
-
/** How long after session_start the deferred sweep fires. Chosen to clear the
|
|
850
|
-
* warmup window it was measured starving: `warmup_total` median 3288ms,
|
|
851
|
-
* worst on record 5480ms (#1857). */
|
|
852
|
-
const BACKSTOP_START_DELAY_MS = 30_000;
|
|
853
|
-
/** Cap on process identities embedded in one latency record. */
|
|
854
|
-
const BACKSTOP_IDENTITY_LOG_LIMIT = 10;
|
|
855
|
-
/**
|
|
856
|
-
* Extra margin on top of the spawn grace before the follow-up sweep runs, so
|
|
857
|
-
* a candidate that was 1ms too fresh is comfortably past the grace by the
|
|
858
|
-
* time the retry looks at it again.
|
|
859
|
-
*/
|
|
860
|
-
const BACKSTOP_GRACE_RETRY_MARGIN_MS = 30_000;
|
|
861
|
-
/** How long the sweep lock may be held before another process reclaims it.
|
|
862
|
-
* Well above the scan timeout plus the kill budget, and irrelevant when the
|
|
863
|
-
* holder dies — the lock reclaims immediately on a dead owner pid
|
|
864
|
-
* (clients/bounded-pid-file-lock.ts:90). */
|
|
865
|
-
const BACKSTOP_LOCK_STALE_MS = 120_000;
|
|
866
|
-
/**
|
|
867
|
-
* Schedule the backstop sweep OFF the session_start critical path. The timer
|
|
868
|
-
* is `unref`'d, so a settled one-shot `pi --print` exits without waiting and
|
|
869
|
-
* the sweep simply does not happen for that invocation — which is correct: a
|
|
870
|
-
* process too short-lived to reach the delay is also too short-lived to be
|
|
871
|
-
* the right place to spend seconds of CIM time.
|
|
872
|
-
*/
|
|
873
|
-
export function scheduleUntrackedOrphanSweep(delayMs = BACKSTOP_START_DELAY_MS, options = {}) {
|
|
874
|
-
const timer = setTimeout(() => {
|
|
875
|
-
void sweepUntrackedOrphans(options).catch(() => {
|
|
876
|
-
// sweepUntrackedOrphans never rejects; belt-and-braces.
|
|
877
|
-
});
|
|
878
|
-
}, Math.max(0, delayMs));
|
|
879
|
-
timer.unref();
|
|
880
|
-
return timer;
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Arm ONE follow-up sweep when the spawn grace spared a candidate (#1864
|
|
884
|
-
* review F2).
|
|
885
|
-
*
|
|
886
|
-
* Without it, the grace guard could spare an orphan indefinitely: the sweep
|
|
887
|
-
* runs once per session, a candidate younger than the grace is spared, and a
|
|
888
|
-
* long-lived session never looks again. That is a regression against the old
|
|
889
|
-
* always-at-session-start sweep, which at least re-examined the process every
|
|
890
|
-
* session.
|
|
891
|
-
*
|
|
892
|
-
* The retry passes `force` (the cooldown it would otherwise hit is the stamp
|
|
893
|
-
* this same sweep just wrote) and `allowGraceRetry: false`, so the chain is
|
|
894
|
-
* exactly one deep. It still takes the lock, so it cannot race a concurrent
|
|
895
|
-
* sweep. Residual: if the process exits before the timer fires, the spared
|
|
896
|
-
* candidate waits for the next session's sweep — the retry is unref'd, and a
|
|
897
|
-
* background reap must never hold a settled one-shot open.
|
|
898
|
-
*
|
|
899
|
-
* Returns the delay armed, or undefined when nothing was spared.
|
|
900
|
-
*/
|
|
901
|
-
function scheduleGraceRetryIfNeeded(partition, options) {
|
|
902
|
-
if (options.allowGraceRetry === false)
|
|
903
|
-
return undefined;
|
|
904
|
-
if (partition.tooFresh.length === 0)
|
|
905
|
-
return undefined;
|
|
906
|
-
const graceMs = Math.max(0, options.graceMs ?? BACKSTOP_SPAWN_GRACE_MS);
|
|
907
|
-
const delayMs = Math.max(0, options.graceRetryDelayMs ?? graceMs + BACKSTOP_GRACE_RETRY_MARGIN_MS);
|
|
908
|
-
scheduleUntrackedOrphanSweep(delayMs, {
|
|
909
|
-
...options,
|
|
910
|
-
force: true,
|
|
911
|
-
allowGraceRetry: false,
|
|
912
|
-
});
|
|
913
|
-
options.onGraceRetryScheduled?.(delayMs);
|
|
914
|
-
return delayMs;
|
|
915
|
-
}
|
|
916
|
-
/** Serialize the sweep across processes. Returns null when another process
|
|
917
|
-
* holds it — the caller skips rather than waits (`waitMs: 0`). */
|
|
918
|
-
async function acquireBackstopLock() {
|
|
919
|
-
try {
|
|
920
|
-
return await acquireQuarantinePidFileLock(path.join(resolveBackstopStateDir(getGlobalPiLensDir()), "orphan-backstop.lock"), {
|
|
921
|
-
waitMs: 0,
|
|
922
|
-
retryMs: 50,
|
|
923
|
-
staleMs: BACKSTOP_LOCK_STALE_MS,
|
|
924
|
-
timeoutMessage: "orphan backstop sweep lock contended",
|
|
925
|
-
onContention: "skip-log",
|
|
926
|
-
logContention: () => {
|
|
927
|
-
// The caller emits the `concurrent` record; nothing to add here.
|
|
928
|
-
},
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
catch {
|
|
932
|
-
// A lock we cannot even attempt (unwritable global dir) must not disable
|
|
933
|
-
// the backstop — fall through unlocked, exactly as before this fix.
|
|
934
|
-
return async () => { };
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
/** Milliseconds since the last sweep when the cooldown is in force, else
|
|
938
|
-
* undefined. Centralized so the pre-check and the in-lock re-check cannot
|
|
939
|
-
* drift apart. */
|
|
940
|
-
async function isWithinCooldown(now, cooldownMs, options) {
|
|
941
|
-
if (cooldownMs <= 0 || options.force)
|
|
942
|
-
return undefined;
|
|
943
|
-
const lastSweepAt = await readBackstopStamp();
|
|
944
|
-
if (lastSweepAt === undefined)
|
|
945
|
-
return undefined;
|
|
946
|
-
const sinceLastMs = now - lastSweepAt;
|
|
947
|
-
return sinceLastMs < cooldownMs ? sinceLastMs : undefined;
|
|
948
|
-
}
|
|
949
|
-
/** Short, stable identity for one managed process: which binary, which pid.
|
|
950
|
-
* Full command lines are user paths and can be long, so the record carries
|
|
951
|
-
* the matched managed-binary name plus the pid. */
|
|
952
|
-
function describeManagedProcess(proc) {
|
|
953
|
-
const lower = proc.command.toLowerCase();
|
|
954
|
-
const matched = MANAGED_BINARY_NAMES.find((name) => lower.includes(name.toLowerCase()));
|
|
955
|
-
return `${matched ?? "unknown"}#${proc.pid}`;
|
|
956
|
-
}
|
|
957
|
-
function logBackstopOutcome(outcome, startedAt, metadata, onComplete) {
|
|
958
|
-
try {
|
|
959
|
-
logLatency({
|
|
960
|
-
type: "phase",
|
|
961
|
-
phase: "orphan_backstop_reaped",
|
|
962
|
-
filePath: "",
|
|
963
|
-
durationMs: Date.now() - startedAt,
|
|
964
|
-
metadata: { outcome, ...metadata },
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
catch {
|
|
968
|
-
// best-effort logging only
|
|
969
|
-
}
|
|
970
|
-
try {
|
|
971
|
-
onComplete?.(outcome);
|
|
972
|
-
}
|
|
973
|
-
catch {
|
|
974
|
-
// Test observers must not alter the best-effort sweep outcome.
|
|
975
|
-
}
|
|
976
|
-
return outcome;
|
|
977
|
-
}
|
|
978
|
-
/** Machine-wide cooldown stamp. A file, not a module-level flag: the sweep is
|
|
979
|
-
* machine-scoped (every pi-lens process shares one OS process table), and
|
|
980
|
-
* process-lifetime state cannot express "30 minutes have passed". */
|
|
981
|
-
function backstopStampPath() {
|
|
982
|
-
return path.join(resolveBackstopStateDir(getGlobalPiLensDir()), "orphan-backstop.json");
|
|
983
|
-
}
|
|
984
|
-
async function readBackstopStamp() {
|
|
985
|
-
try {
|
|
986
|
-
const raw = await fs.promises.readFile(backstopStampPath(), "utf8");
|
|
987
|
-
const parsed = JSON.parse(raw);
|
|
988
|
-
const value = parsed?.lastSweepAt;
|
|
989
|
-
return typeof value === "number" && Number.isFinite(value)
|
|
990
|
-
? value
|
|
991
|
-
: undefined;
|
|
992
|
-
}
|
|
993
|
-
catch {
|
|
994
|
-
// Absent or unreadable ⇒ no cooldown in force, so the sweep runs. A
|
|
995
|
-
// corrupt stamp must never be able to disable the backstop permanently.
|
|
996
|
-
return undefined;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
async function writeBackstopStamp(at) {
|
|
1000
|
-
try {
|
|
1001
|
-
await fs.promises.mkdir(resolveBackstopStateDir(getGlobalPiLensDir()), {
|
|
1002
|
-
recursive: true,
|
|
1003
|
-
});
|
|
1004
|
-
await writeFileAtomicAsync(backstopStampPath(), JSON.stringify({ lastSweepAt: at }));
|
|
1005
|
-
}
|
|
1006
|
-
catch {
|
|
1007
|
-
// best-effort — a stamp we cannot persist just means the next session
|
|
1008
|
-
// pays for another sweep, never a crash.
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
/**
|
|
1012
|
-
* Fire-and-forget orphan sweep: reads the registry, decides what's dead via
|
|
1013
|
-
* `decideOrphanReaping`, kills orphaned LSP children (by pid, with a
|
|
1014
|
-
* marker-based command-line search fallback), then drops fully-dead
|
|
1015
|
-
* instances from the registry. Never throws — every step is wrapped so a
|
|
1016
|
-
* reap failure cannot block or crash the caller (session_start).
|
|
1017
|
-
*/
|
|
1018
|
-
export async function sweepOrphans() {
|
|
1019
|
-
if (!isInstanceRegistryEnabled())
|
|
1020
|
-
return;
|
|
1021
|
-
const startedAt = Date.now();
|
|
1022
|
-
try {
|
|
1023
|
-
const registry = await readInstanceRegistry();
|
|
1024
|
-
if (registry.length === 0)
|
|
1025
|
-
return;
|
|
1026
|
-
// Identity verification before any pid kill (recycled-pid guard): fetch
|
|
1027
|
-
// the command lines of every recorded child pid in ONE batched query,
|
|
1028
|
-
// then let the pure decision function verify each live child's identity
|
|
1029
|
-
// against what was recorded at spawn. A pid whose command line can't be
|
|
1030
|
-
// fetched is treated as unverifiable and never killed by pid — the
|
|
1031
|
-
// marker-search fallback may still catch it.
|
|
1032
|
-
const candidatePids = registry.flatMap((instance) => instance.lspChildren.map((child) => child.pid));
|
|
1033
|
-
const cmdlines = await queryCommandLines(candidatePids);
|
|
1034
|
-
const matchProcess = buildIdentityMatcher(cmdlines);
|
|
1035
|
-
const decision = decideOrphanReaping(registry, realIsPidAlive, matchProcess);
|
|
1036
|
-
// #1857 class sweep: the registry-driven path spelled the same
|
|
1037
|
-
// attempt-counted-as-kill defect as the backstop, so it gets the same
|
|
1038
|
-
// verified accounting and the same bounded, identity-carrying record.
|
|
1039
|
-
let killedCount = 0;
|
|
1040
|
-
let unverifiedCount = 0;
|
|
1041
|
-
const killedServerIds = [];
|
|
1042
|
-
const killAndAccount = async (pid, serverId) => {
|
|
1043
|
-
const outcome = await killPidTree(pid);
|
|
1044
|
-
if (outcome === "gone") {
|
|
1045
|
-
killedCount++;
|
|
1046
|
-
killedServerIds.push(serverId);
|
|
1047
|
-
return;
|
|
1048
|
-
}
|
|
1049
|
-
unverifiedCount++;
|
|
1050
|
-
incrementDegradationCount({
|
|
1051
|
-
kind: "orphan-reap-kill-unverified",
|
|
1052
|
-
subject: `${serverId}#${pid}`,
|
|
1053
|
-
reason: `kill attempted, process still ${outcome === "alive" ? "alive" : "unresolvable"}`,
|
|
1054
|
-
});
|
|
1055
|
-
};
|
|
1056
|
-
for (const child of decision.childrenToKill) {
|
|
1057
|
-
await killAndAccount(child.pid, child.serverId);
|
|
1058
|
-
}
|
|
1059
|
-
for (const search of decision.markerSearches) {
|
|
1060
|
-
try {
|
|
1061
|
-
const pids = await findPidsByMarkerWindows(search.marker);
|
|
1062
|
-
for (const pid of pids) {
|
|
1063
|
-
await killAndAccount(pid, search.serverId);
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
catch {
|
|
1067
|
-
// best-effort — a failed marker search just misses that orphan this sweep
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
// Entry removal covers BOTH sets: pid-dead instances AND stale-heartbeat
|
|
1071
|
-
// (pid-alive) instances — the latter is record cleanup only (#525);
|
|
1072
|
-
// nothing belonging to a stale instance was killed above.
|
|
1073
|
-
if (decision.deadInstances.length > 0 ||
|
|
1074
|
-
decision.staleInstances.length > 0) {
|
|
1075
|
-
try {
|
|
1076
|
-
const prunePids = new Set([
|
|
1077
|
-
...decision.deadInstances.map((i) => i.pid),
|
|
1078
|
-
...decision.staleInstances.map((i) => i.pid),
|
|
1079
|
-
]);
|
|
1080
|
-
await pruneDeadInstances(prunePids);
|
|
1081
|
-
}
|
|
1082
|
-
catch {
|
|
1083
|
-
// best-effort — a stale registry entry is re-evaluated next sweep
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
try {
|
|
1087
|
-
logLatency({
|
|
1088
|
-
type: "phase",
|
|
1089
|
-
phase: "orphan_lsp_reaped",
|
|
1090
|
-
filePath: "",
|
|
1091
|
-
durationMs: Date.now() - startedAt,
|
|
1092
|
-
metadata: {
|
|
1093
|
-
deadInstances: decision.deadInstances.length,
|
|
1094
|
-
staleInstances: decision.staleInstances.length,
|
|
1095
|
-
killed: killedCount,
|
|
1096
|
-
killUnverified: unverifiedCount,
|
|
1097
|
-
serverIds: killedServerIds,
|
|
1098
|
-
markerSearches: decision.markerSearches.length,
|
|
1099
|
-
},
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
catch {
|
|
1103
|
-
// best-effort logging only
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
catch {
|
|
1107
|
-
// The sweep must never throw out of session_start.
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
/** Drop dead-parent AND stale-heartbeat (#525, record-cleanup-only)
|
|
1111
|
-
* instances from the registry. Re-reads immediately before
|
|
1112
|
-
* writing (rather than reusing the earlier `readInstanceRegistry()` snapshot)
|
|
1113
|
-
* under the instance-registry lock so the reaper cannot lose a registration
|
|
1114
|
-
* or heartbeat while pruning.
|
|
1115
|
-
*
|
|
1116
|
-
* Exported for the #1217 concurrency regression test; `sweepOrphans` is the
|
|
1117
|
-
* only production caller.
|
|
1118
|
-
*
|
|
1119
|
-
* #1217: this used to hand-roll `${target}.tmp-${process.pid}` + rename
|
|
1120
|
-
* instead of going through `atomic-write.ts`, so it never inherited the
|
|
1121
|
-
* #1205 fix — two concurrent prunes in one process staged into one shared
|
|
1122
|
-
* inode and the first rename published it while the second was still
|
|
1123
|
-
* writing. That is reachable here rather than theoretical: this store is
|
|
1124
|
-
* machine-global and written fire-and-forget from `instance-registry.ts`'s
|
|
1125
|
-
* `prunePids` as well, and `readInstanceRegistry` degrades a parse failure
|
|
1126
|
-
* to empty, so a tear dropped EVERY registered instance rather than one
|
|
1127
|
-
* entry. `writeFileAtomicAsync` also cleans up its staging file on a failed
|
|
1128
|
-
* rename, which the hand-rolled copy did not, and puts this writer back on
|
|
1129
|
-
* the same scheme as the other writer of this same file. */
|
|
1130
|
-
export async function pruneDeadInstances(deadPids) {
|
|
1131
|
-
try {
|
|
1132
|
-
await pruneDeadRegistryInstances(deadPids);
|
|
1133
|
-
}
|
|
1134
|
-
catch {
|
|
1135
|
-
// best-effort
|
|
1136
|
-
}
|
|
1137
|
-
}
|