@herbertgao/pi-extensions 2026.9.2 → 2026.9.4

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.
Files changed (255) hide show
  1. package/README.md +4 -2
  2. package/THIRD_PARTY_NOTICES.md +49 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +3 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +16 -11
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +20 -6
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +12 -7
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +98 -0
  8. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  9. package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
  10. package/node_modules/@herbertgao/sol-pi/README.md +159 -0
  11. package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
  12. package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
  13. package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
  14. package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
  15. package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
  16. package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
  17. package/node_modules/@herbertgao/sol-pi/package.json +76 -0
  18. package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
  19. package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
  20. package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
  21. package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
  22. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
  23. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
  24. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
  25. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
  26. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
  27. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
  28. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
  29. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
  30. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
  31. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
  32. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
  33. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
  34. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
  35. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
  36. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
  37. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
  38. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
  39. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
  40. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
  41. package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
  42. package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
  43. package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
  44. package/node_modules/@narumitw/pi-btw/dist/index.ts +12 -2
  45. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
  46. package/node_modules/@narumitw/pi-btw/package.json +1 -1
  47. package/node_modules/@narumitw/pi-btw/src/btw.ts +13 -2
  48. package/node_modules/pi-antigravity/LICENSE +21 -0
  49. package/node_modules/pi-antigravity/README.md +194 -0
  50. package/node_modules/pi-antigravity/package.json +67 -0
  51. package/node_modules/pi-antigravity/src/auth/index.ts +14 -0
  52. package/node_modules/pi-antigravity/src/auth/oauth.ts +442 -0
  53. package/node_modules/pi-antigravity/src/client/client.ts +561 -0
  54. package/node_modules/pi-antigravity/src/client/index.ts +1 -0
  55. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +96 -0
  56. package/node_modules/pi-antigravity/src/diagnostics/index.ts +1 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +336 -0
  58. package/node_modules/pi-antigravity/src/image/index.ts +1 -0
  59. package/node_modules/pi-antigravity/src/index.ts +280 -0
  60. package/node_modules/pi-antigravity/src/models/discovery.ts +154 -0
  61. package/node_modules/pi-antigravity/src/models/grouping.ts +424 -0
  62. package/node_modules/pi-antigravity/src/models/index.ts +3 -0
  63. package/node_modules/pi-antigravity/src/models/models.ts +500 -0
  64. package/node_modules/pi-antigravity/src/stream/index.ts +1 -0
  65. package/node_modules/pi-antigravity/src/stream/stream.ts +1460 -0
  66. package/node_modules/pi-antigravity/src/types/enums.ts +42 -0
  67. package/node_modules/pi-antigravity/src/types/index.ts +2 -0
  68. package/node_modules/pi-antigravity/src/types/types.ts +292 -0
  69. package/node_modules/pi-antigravity/src/usage/index.ts +1 -0
  70. package/node_modules/pi-antigravity/src/usage/usage.ts +371 -0
  71. package/node_modules/pi-antigravity/src/utils/http.ts +91 -0
  72. package/node_modules/pi-antigravity/src/utils/index.ts +3 -0
  73. package/node_modules/pi-antigravity/src/utils/security.ts +73 -0
  74. package/node_modules/pi-antigravity/src/utils/util.ts +132 -0
  75. package/node_modules/pi-antigravity/tsconfig.json +21 -0
  76. package/node_modules/pi-lens/CHANGELOG.md +176 -0
  77. package/node_modules/pi-lens/README.md +13 -8
  78. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
  79. package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
  80. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -1
  81. package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
  82. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -0
  83. package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -1
  84. package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
  85. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +2 -0
  86. package/node_modules/pi-lens/dist/clients/config-resolve.js +6 -3
  87. package/node_modules/pi-lens/dist/clients/config-schema.js +17 -0
  88. package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
  89. package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
  90. package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
  91. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -15
  92. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
  93. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -3
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +2 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +2 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +12 -12
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +2 -2
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +2 -2
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +4 -3
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
  143. package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
  144. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
  145. package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
  146. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
  147. package/node_modules/pi-lens/dist/clients/formatters.js +54 -188
  148. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
  149. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
  150. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
  151. package/node_modules/pi-lens/dist/clients/installer/index.js +196 -52
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
  154. package/node_modules/pi-lens/dist/clients/language-profile.js +12 -2
  155. package/node_modules/pi-lens/dist/clients/latency-logger.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/lens-config.js +4 -0
  157. package/node_modules/pi-lens/dist/clients/lsp/config.js +36 -7
  158. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +78 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/index.js +281 -45
  161. package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
  162. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
  163. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
  164. package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
  165. package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
  166. package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
  167. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +8 -3
  168. package/node_modules/pi-lens/dist/clients/pipeline.js +3 -2
  169. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
  170. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
  171. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  172. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +2 -0
  173. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
  174. package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
  175. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +9 -0
  176. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
  177. package/node_modules/pi-lens/dist/clients/runtime-session.js +13 -4
  178. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
  179. package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
  180. package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
  181. package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
  182. package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
  183. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
  184. package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
  185. package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
  186. package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
  187. package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
  188. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
  189. package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
  190. package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
  191. package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
  192. package/node_modules/pi-lens/dist/index.js +21969 -20005
  193. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
  194. package/node_modules/pi-lens/dist/mcp/server.js +135 -159
  195. package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
  196. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
  197. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
  198. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
  199. package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
  200. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
  201. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +353 -85
  202. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +21 -110
  203. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
  204. package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
  205. package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
  206. package/node_modules/pi-lens/dist/tools/render-compact.js +280 -1
  207. package/node_modules/pi-lens/dist/tools/symbol-search.js +2 -2
  208. package/node_modules/pi-lens/docs/agent-guide.md +3 -4
  209. package/node_modules/pi-lens/docs/agent-tools.md +46 -22
  210. package/node_modules/pi-lens/docs/configuration.md +9 -0
  211. package/node_modules/pi-lens/docs/environment-variables.md +0 -5
  212. package/node_modules/pi-lens/docs/features.md +5 -5
  213. package/node_modules/pi-lens/docs/globalconfig.md +11 -1
  214. package/node_modules/pi-lens/docs/mcp.md +13 -1
  215. package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
  216. package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
  217. package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
  218. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +35 -0
  219. package/node_modules/pi-lens/docs/pi-lens-subagent.md +12 -4
  220. package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
  221. package/node_modules/pi-lens/docs/real-harness.md +46 -0
  222. package/node_modules/pi-lens/docs/release-qa-baseline.md +5 -1
  223. package/node_modules/pi-lens/docs/servercapabilities.md +1 -6
  224. package/node_modules/pi-lens/docs/settings.md +37 -2
  225. package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
  226. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  227. package/node_modules/pi-lens/docs/usage.md +13 -3
  228. package/node_modules/pi-lens/package.json +10 -1
  229. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
  230. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
  231. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
  232. package/node_modules/pi-web-access/CHANGELOG.md +29 -0
  233. package/node_modules/pi-web-access/README.md +33 -22
  234. package/node_modules/pi-web-access/abortable.ts +17 -0
  235. package/node_modules/pi-web-access/crawl4ai.ts +204 -0
  236. package/node_modules/pi-web-access/credential-source.ts +1 -0
  237. package/node_modules/pi-web-access/curator-page.ts +20 -2
  238. package/node_modules/pi-web-access/curator-run.ts +44 -0
  239. package/node_modules/pi-web-access/curator-server.ts +3 -1
  240. package/node_modules/pi-web-access/duckduckgo.ts +1 -1
  241. package/node_modules/pi-web-access/extract.ts +57 -12
  242. package/node_modules/pi-web-access/fetch-params.ts +1 -1
  243. package/node_modules/pi-web-access/gemini-search.ts +9 -5
  244. package/node_modules/pi-web-access/index.ts +25 -18
  245. package/node_modules/pi-web-access/openai-search.ts +35 -13
  246. package/node_modules/pi-web-access/package.json +2 -2
  247. package/node_modules/pi-web-access/page-query.ts +5 -2
  248. package/node_modules/pi-web-access/query-rewrite.ts +5 -2
  249. package/node_modules/pi-web-access/serpapi.ts +220 -0
  250. package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
  251. package/node_modules/pi-web-access/storage.ts +10 -0
  252. package/node_modules/pi-web-access/summary-review.ts +34 -21
  253. package/node_modules/pi-web-access/utils.ts +6 -4
  254. package/package.json +11 -5
  255. package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
@@ -0,0 +1,199 @@
1
+ /** Shared resolver for model-facing tool availability (#2800). */
2
+ import { readFlagConfigValue } from "./lens-flag-registry.js";
3
+ /** The complete model-facing tool population on pi and MCP. */
4
+ export const TOOL_REGISTRY = [
5
+ {
6
+ name: "ast_grep_search",
7
+ piName: "ast_grep_search",
8
+ mcpName: "pilens_ast_grep_search",
9
+ situational: true,
10
+ summary: "AST-aware structural code search across ~40 languages (ast-grep patterns).",
11
+ disableable: true,
12
+ },
13
+ {
14
+ name: "ast_grep_replace",
15
+ piName: "ast_grep_replace",
16
+ mcpName: "pilens_ast_grep_replace",
17
+ situational: true,
18
+ summary: "AST-aware structural code rewrite/refactor (ast-grep patterns).",
19
+ disableable: true,
20
+ },
21
+ {
22
+ name: "ast_grep_outline",
23
+ piName: "ast_grep_outline",
24
+ mcpName: undefined,
25
+ situational: true,
26
+ summary: "Syntax-only file/dir structure (symbols/imports/exports/members) via ast-grep outline — no index/LSP.",
27
+ disableable: true,
28
+ },
29
+ {
30
+ name: "lsp_navigation",
31
+ piName: "lsp_navigation",
32
+ mcpName: "pilens_lsp_navigation",
33
+ situational: true,
34
+ summary: "IDE-style LSP navigation: definition, references, implementation, rename, call hierarchy.",
35
+ disableable: true,
36
+ },
37
+ {
38
+ name: "lens_diagnostics",
39
+ piName: "lens_diagnostics",
40
+ mcpName: "pilens_diagnostics",
41
+ disableable: true,
42
+ },
43
+ {
44
+ name: "lens_diagnostic_mark",
45
+ piName: "lens_diagnostic_mark",
46
+ mcpName: undefined,
47
+ situational: true,
48
+ summary: "Record a disposition for a diagnostic: false-positive / suppress (inline ignore comment) / defer (this session) / flagged (to fix).",
49
+ disableable: true,
50
+ },
51
+ {
52
+ name: "symbol_search",
53
+ piName: "symbol_search",
54
+ mcpName: "pilens_symbol_search",
55
+ disableable: true,
56
+ },
57
+ {
58
+ name: "module_report",
59
+ piName: "module_report",
60
+ mcpName: "pilens_module_report",
61
+ disableable: true,
62
+ },
63
+ {
64
+ name: "project_report",
65
+ piName: "project_report",
66
+ mcpName: "pilens_project_report",
67
+ disableable: true,
68
+ },
69
+ {
70
+ name: "read_symbol",
71
+ piName: "read_symbol",
72
+ mcpName: "pilens_read_symbol",
73
+ disableable: true,
74
+ },
75
+ {
76
+ name: "read_enclosing",
77
+ piName: "read_enclosing",
78
+ mcpName: "pilens_read_enclosing",
79
+ disableable: true,
80
+ },
81
+ {
82
+ name: "effective_config",
83
+ piName: "effective_config",
84
+ mcpName: "pilens_effective_config",
85
+ disableable: true,
86
+ },
87
+ {
88
+ name: "pi_lens_activate_tools",
89
+ piName: "pi_lens_activate_tools",
90
+ mcpName: undefined,
91
+ disableable: false,
92
+ },
93
+ {
94
+ name: "analyze",
95
+ piName: undefined,
96
+ mcpName: "pilens_analyze",
97
+ disableable: true,
98
+ },
99
+ {
100
+ name: "health",
101
+ piName: undefined,
102
+ mcpName: "pilens_health",
103
+ disableable: true,
104
+ },
105
+ {
106
+ name: "latency",
107
+ piName: undefined,
108
+ mcpName: "pilens_latency",
109
+ disableable: true,
110
+ },
111
+ {
112
+ name: "project_scan",
113
+ piName: undefined,
114
+ mcpName: "pilens_project_scan",
115
+ disableable: true,
116
+ },
117
+ {
118
+ name: "rebuild",
119
+ piName: undefined,
120
+ mcpName: "pilens_rebuild",
121
+ disableable: true,
122
+ },
123
+ {
124
+ name: "session_start",
125
+ piName: undefined,
126
+ mcpName: "pilens_session_start",
127
+ disableable: false,
128
+ },
129
+ {
130
+ name: "turn_end",
131
+ piName: undefined,
132
+ mcpName: "pilens_turn_end",
133
+ disableable: false,
134
+ },
135
+ {
136
+ name: "session_end",
137
+ piName: undefined,
138
+ mcpName: "pilens_session_end",
139
+ disableable: false,
140
+ },
141
+ ];
142
+ export const LENS_TOOL_NAMES = TOOL_REGISTRY.map((tool) => tool.name);
143
+ export function toolRegistryEntryForPi(name) {
144
+ return TOOL_REGISTRY.find((tool) => tool.piName === name);
145
+ }
146
+ export function toolRegistryEntryForMcp(name) {
147
+ return TOOL_REGISTRY.find((tool) => tool.mcpName === name);
148
+ }
149
+ /** Resolve one tool. CLI names are comma-separated to support repeatable flags. */
150
+ export function resolveLensToolEnabled(name, globalConfig, projectConfig, cliNoTools) {
151
+ const entry = TOOL_REGISTRY.find((tool) => tool.name === name);
152
+ if (entry?.disableable === false)
153
+ return true;
154
+ const cliNames = Array.isArray(cliNoTools)
155
+ ? cliNoTools
156
+ : typeof cliNoTools === "string"
157
+ ? cliNoTools.split(",")
158
+ : [];
159
+ if (cliNames.some((entry) => entry.trim() === name))
160
+ return false;
161
+ const project = readFlagConfigValue(projectConfig, `tools.${name}.enabled`);
162
+ if (project !== undefined)
163
+ return project;
164
+ const global = readFlagConfigValue(globalConfig, `tools.${name}.enabled`);
165
+ return global ?? true;
166
+ }
167
+ /** Copy and validate the known per-tool leaves from one config document. */
168
+ export function readToolConfig(raw, warnInvalid) {
169
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
170
+ return undefined;
171
+ const section = raw.tools;
172
+ if (!section || typeof section !== "object" || Array.isArray(section))
173
+ return undefined;
174
+ const result = {};
175
+ for (const [name, value] of Object.entries(section)) {
176
+ if (name === "lazy")
177
+ continue;
178
+ const entry = TOOL_REGISTRY.find((tool) => tool.name === name);
179
+ if (!entry) {
180
+ warnInvalid(`unknown key "tools.${name}.enabled" is not a recognized pi-lens tool`, "PILENS_CFG_0009");
181
+ continue;
182
+ }
183
+ if (!entry.disableable) {
184
+ warnInvalid(`key "tools.${name}.enabled" cannot be disabled because it is required for the ${entry.mcpName ? "MCP session" : "pi tool activation"} contract`, "PILENS_CFG_0009");
185
+ continue;
186
+ }
187
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
188
+ warnInvalid(`tools.${name} must be an object with enabled`);
189
+ continue;
190
+ }
191
+ const enabled = value.enabled;
192
+ if (enabled !== undefined && typeof enabled !== "boolean") {
193
+ warnInvalid(`tools.${name}.enabled must be a boolean`);
194
+ continue;
195
+ }
196
+ result[name] = enabled === undefined ? {} : { enabled };
197
+ }
198
+ return Object.keys(result).length > 0 ? result : undefined;
199
+ }
@@ -0,0 +1,299 @@
1
+ import * as os from "node:os";
2
+ import { existsSync, readdirSync } from "node:fs";
3
+ import * as path from "node:path";
4
+ import { logExtension } from "./extension-log.js";
5
+ import { isRealGitMarker, isAtOrAboveHomeDir, isUnderDir, isWindowsPath, nameMatchesMarkerGlob, normalizeEphemeralMapKey, } from "./path-utils.js";
6
+ import { getDegradationLedgerGeneration, recordDegradationOnce, } from "./degradation-ledger.js";
7
+ import { createGenerationMap, createGenerationSource, } from "./generation-guard.js";
8
+ export const FORMATTER_MARKERS = {
9
+ biome: ["biome.json", "biome.jsonc", "package.json", ".gitignore"],
10
+ prettier: [
11
+ ".prettierrc",
12
+ ".prettierrc.json",
13
+ ".prettierrc.yaml",
14
+ ".prettierrc.yml",
15
+ ".prettierrc.js",
16
+ ".prettierrc.cjs",
17
+ ".prettierrc.mjs",
18
+ "prettier.config.js",
19
+ "prettier.config.cjs",
20
+ "prettier.config.mjs",
21
+ ".prettierignore",
22
+ "package.json",
23
+ ".gitignore",
24
+ ],
25
+ ruff: ["pyproject.toml", "ruff.toml", ".ruff.toml", ".gitignore"],
26
+ black: ["pyproject.toml", "black.toml", ".black", ".gitignore"],
27
+ "php-cs-fixer": [".php-cs-fixer.php", ".php-cs-fixer.dist.php", ".gitignore"],
28
+ sqlfluff: [".sqlfluff", "pyproject.toml", "setup.cfg", ".gitignore"],
29
+ oxfmt: [
30
+ "oxfmt.toml",
31
+ ".oxfmtrc.json",
32
+ "vite-plus.json",
33
+ "package.json",
34
+ ".gitignore",
35
+ ],
36
+ rustfmt: ["rustfmt.toml", ".rustfmt.toml", "Cargo.toml", ".gitignore"],
37
+ rubocop: [".rubocop.yml", ".rubocop.yaml", ".gitignore"],
38
+ standardrb: [".standard.yml", ".standard.yaml", ".gitignore"],
39
+ "clang-format": [".clang-format", "_clang-format", ".gitignore"],
40
+ stylua: ["stylua.toml", ".stylua.toml", ".gitignore"],
41
+ ocamlformat: [".ocamlformat", ".gitignore"],
42
+ "google-java-format": [".google-java-format", ".editorconfig", ".gitignore"],
43
+ cljfmt: [".cljfmt.edn", "cljfmt.edn", ".cljfmt", ".gitignore"],
44
+ "cmake-format": [
45
+ ".cmake-format",
46
+ ".cmake-format.yaml",
47
+ ".cmake-format.yml",
48
+ ".cmake-format.json",
49
+ ".cmake-format.py",
50
+ "cmake-format.yaml",
51
+ "cmake-format.yml",
52
+ ".editorconfig",
53
+ ".gitignore",
54
+ ],
55
+ "psscriptanalyzer-format": [
56
+ "PSScriptAnalyzerSettings.psd1",
57
+ "ScriptAnalyzerSettings.psd1",
58
+ ".gitignore",
59
+ ],
60
+ csharpier: [
61
+ ".csharpierrc",
62
+ ".csharpierrc.json",
63
+ ".csharpierrc.yaml",
64
+ ".csharpierrc.yml",
65
+ ".gitignore",
66
+ ],
67
+ ormolu: [".ormolu", ".gitignore"],
68
+ taplo: ["taplo.toml", ".taplo.toml", ".gitignore"],
69
+ terraform: [".terraform.lock.hcl", ".gitignore"],
70
+ swiftformat: [".swiftformat", ".gitignore"],
71
+ fantomas: [".fantomasignore", ".editorconfig", ".gitignore"],
72
+ mix: [".formatter.exs", ".gitignore"],
73
+ shfmt: [".editorconfig", ".gitignore"],
74
+ ktlint: [".editorconfig", ".gitignore"],
75
+ ktfmt: [
76
+ ".editorconfig",
77
+ ".ktfmt",
78
+ ".ktfmt.kts",
79
+ "build.gradle",
80
+ "build.gradle.kts",
81
+ "settings.gradle",
82
+ "settings.gradle.kts",
83
+ ".gitignore",
84
+ ],
85
+ };
86
+ const RUNNER_MARKERS = {
87
+ yamllint: [".yamllint", "yamllint.yaml", "yamllint.yml", "pyproject.toml"],
88
+ ruff: ["pyproject.toml", "ruff.toml", ".ruff.toml"],
89
+ "spellcheck/typos": ["_typos.toml", "typos.toml"],
90
+ biome: ["biome.json", "biome.jsonc", "package.json"],
91
+ oxlint: [".oxlintrc.json", "oxlint.config.js", "package.json"],
92
+ sqlfluff: [".sqlfluff", "pyproject.toml", "setup.cfg"],
93
+ prettier: [".prettierignore", "package.json"],
94
+ // #2894: cargo must run at the package root. This is the marker walk
95
+ // `rust-clippy.ts` used to do for itself with `findNearestContaining`.
96
+ "rust-clippy": ["Cargo.toml"],
97
+ };
98
+ const toolCwdGeneration = createGenerationSource("tool-cwd");
99
+ const logged = createGenerationMap("tool-cwd-resolution-log");
100
+ const markerWalks = new Map();
101
+ const markerWalkGenerations = createGenerationMap("tool-cwd-marker-walks");
102
+ let markerWalkCount = 0;
103
+ /** Pure key seam for ephemeral tool-cwd memo and log identity. */
104
+ export function _toolCwdEphemeralKey(parts) {
105
+ return parts.map(normalizeEphemeralMapKey).join("\0");
106
+ }
107
+ /** Test probe for the per-generation walk memo; not part of runtime behavior. */
108
+ export function _getToolCwdMarkerWalkCount() {
109
+ return markerWalkCount;
110
+ }
111
+ function syncGeneration() {
112
+ if (toolCwdGeneration.current() === getDegradationLedgerGeneration())
113
+ return;
114
+ markerWalks.clear();
115
+ markerWalkGenerations.clear();
116
+ logged.clear();
117
+ while (toolCwdGeneration.current() < getDegradationLedgerGeneration()) {
118
+ toolCwdGeneration.bump();
119
+ }
120
+ }
121
+ function findMarkerRoot(startDir, markers, homeDir) {
122
+ syncGeneration();
123
+ const key = _toolCwdEphemeralKey([
124
+ path.resolve(startDir),
125
+ ...markers,
126
+ path.resolve(homeDir),
127
+ ]);
128
+ const cached = markerWalks.get(key);
129
+ if (cached && markerWalkGenerations.current(key) !== 0) {
130
+ // A negative walk is not stable: a project marker can be created after
131
+ // the first resolution, so do not cache absence across calls. Positive
132
+ // results remain memoized and are still revalidated when their marker is
133
+ // deleted (#2894).
134
+ if (!cached.marker || !cached.root) {
135
+ markerWalks.delete(key);
136
+ markerWalkGenerations.forget(key);
137
+ }
138
+ else {
139
+ // #2777: a marker can disappear during a session; do not reuse a stale root.
140
+ if (existsSync(path.join(cached.root, cached.marker)))
141
+ return cached;
142
+ markerWalks.delete(key);
143
+ markerWalkGenerations.forget(key);
144
+ }
145
+ }
146
+ markerWalkCount++;
147
+ let current = path.resolve(startDir);
148
+ let result = { root: null };
149
+ for (let depth = 0; depth < 64; depth++) {
150
+ if (isAtOrAboveHomeDir(current, homeDir))
151
+ break;
152
+ for (const marker of markers) {
153
+ const slash = marker.replace(/\\/g, "/").lastIndexOf("/");
154
+ const parent = slash >= 0 ? marker.slice(0, slash) : "";
155
+ const basename = slash >= 0 ? marker.slice(slash + 1) : marker;
156
+ const target = parent
157
+ ? path.join(current, ...parent.split("/").filter(Boolean))
158
+ : current;
159
+ let found = false;
160
+ if (basename.includes("*")) {
161
+ try {
162
+ found = readdirSync(target, { withFileTypes: true }).some((entry) => (entry.isFile() || entry.isSymbolicLink()) &&
163
+ nameMatchesMarkerGlob(entry.name, basename));
164
+ }
165
+ catch {
166
+ found = false;
167
+ }
168
+ }
169
+ else {
170
+ found = existsSync(path.join(target, basename));
171
+ }
172
+ if (found) {
173
+ result = { root: current, marker };
174
+ markerWalks.set(key, result);
175
+ markerWalkGenerations.bump(key);
176
+ return result;
177
+ }
178
+ }
179
+ const parentDir = path.dirname(current);
180
+ if (parentDir === current)
181
+ break;
182
+ current = parentDir;
183
+ }
184
+ markerWalks.set(key, result);
185
+ markerWalkGenerations.bump(key);
186
+ return result;
187
+ }
188
+ function markersFor(kind, tool, ctx) {
189
+ if (kind === "lsp")
190
+ return ctx.rootMarkers ?? [];
191
+ // #2871: caller-supplied markers count for runners too. `RUNNER_MARKERS`
192
+ // covers the linter/formatter-shaped runners this module has always known;
193
+ // a caller that owns its own marker table — the test runner, whose
194
+ // `RUNNERS[x].configFiles` IS that table — passes it here rather than
195
+ // registering a second copy of the same data in this file.
196
+ if (kind === "runner")
197
+ return ctx.rootMarkers ?? RUNNER_MARKERS[tool] ?? [];
198
+ return FORMATTER_MARKERS[tool] ?? [".gitignore"];
199
+ }
200
+ function emitResolution(kind, tool, cwd, reason) {
201
+ syncGeneration();
202
+ const key = _toolCwdEphemeralKey([kind, tool, cwd, reason]);
203
+ if (logged.current(key) !== 0)
204
+ return;
205
+ logged.bump(key);
206
+ logExtension({
207
+ subsystem: "tool-cwd",
208
+ level: "debug",
209
+ message: `cwd ${kind} ${tool} cwd=${cwd} reason=${reason}`,
210
+ });
211
+ }
212
+ /**
213
+ * Emit a constant runner advisory once per (tool, resolved root) per session
214
+ * (#2811): the yamllint/stylelint/sqlfluff "no config detected" lines and
215
+ * markdownlint's spawn-timeout cooldown notice logged once PER FILE at error
216
+ * level — 17 lines in one session for one tool. The throttle shares the
217
+ * resolution-log map and its ledger-generation reset, so `session_start`
218
+ * re-arms it like every once-latch in this module. The message rides
219
+ * `ctx.log` at debug level so the dispatch seam stays the one sink for
220
+ * runner lines, with its own filePath/kind metadata.
221
+ */
222
+ export function logRunnerAdvisoryOnce(ctx, tool, root, message) {
223
+ syncGeneration();
224
+ const key = _toolCwdEphemeralKey(["runner-advisory", tool, root]);
225
+ if (logged.current(key) !== 0)
226
+ return;
227
+ logged.bump(key);
228
+ ctx.log(message, "debug");
229
+ }
230
+ /** Resolve every child process cwd/root through one bounded, synchronous seam. */
231
+ export function resolveToolCwd(kind, tool, file, ctx) {
232
+ const dispatchRoot = path.resolve(ctx.cwd ?? process.cwd());
233
+ const absoluteFile = path.resolve(file);
234
+ const fileDir = path.dirname(absoluteFile);
235
+ const homeDir = ctx.homeDir ?? os.homedir();
236
+ const insideDispatch = isUnderDir(absoluteFile, dispatchRoot);
237
+ if (ctx.serverRoot) {
238
+ const rootPath = isWindowsPath(ctx.serverRoot) ? path.win32 : path;
239
+ const serverRoot = rootPath.resolve(ctx.serverRoot);
240
+ if (!ctx.suppressTelemetry)
241
+ emitResolution(kind, tool, serverRoot, "server-root");
242
+ return serverRoot;
243
+ }
244
+ const markers = markersFor(kind, tool, ctx);
245
+ const markerResult = markers.length
246
+ ? findMarkerRoot(fileDir, markers, ctx.allowHomeMarker ? path.parse(homeDir).root : homeDir)
247
+ : { root: null };
248
+ const markerRoot = markerResult.root;
249
+ if (markerRoot &&
250
+ (insideDispatch ? isUnderDir(markerRoot, dispatchRoot) : true)) {
251
+ const finalReason = `marker:${markerResult.marker ?? markers[0]}`;
252
+ if (!ctx.suppressTelemetry)
253
+ emitResolution(kind, tool, markerRoot, finalReason);
254
+ return markerRoot;
255
+ }
256
+ const gitResult = findMarkerRoot(fileDir, [".git"], homeDir);
257
+ const gitRoot = gitResult.root && isRealGitMarker(path.join(gitResult.root, ".git"))
258
+ ? gitResult.root
259
+ : null;
260
+ if (gitRoot && (insideDispatch ? isUnderDir(gitRoot, dispatchRoot) : true)) {
261
+ if (!ctx.suppressTelemetry)
262
+ emitResolution(kind, tool, gitRoot, "git-root");
263
+ return gitRoot;
264
+ }
265
+ if (insideDispatch) {
266
+ if (kind === "formatter") {
267
+ if (!ctx.suppressTelemetry)
268
+ emitResolution(kind, tool, fileDir, "file-dir-fallback");
269
+ if (!ctx.suppressTelemetry && !isUnderDir(fileDir, homeDir)) {
270
+ recordDegradationOnce({
271
+ kind: "tool-cwd-resolution",
272
+ subject: tool,
273
+ reason: `${kind}:home-cap:${absoluteFile}`,
274
+ });
275
+ }
276
+ return fileDir;
277
+ }
278
+ if (!ctx.suppressTelemetry)
279
+ emitResolution(kind, tool, dispatchRoot, "dispatch-root");
280
+ return dispatchRoot;
281
+ }
282
+ const reason = isUnderDir(fileDir, homeDir)
283
+ ? "file-dir-fallback"
284
+ : "home-cap";
285
+ const fallback = isUnderDir(fileDir, homeDir) ? fileDir : homeDir;
286
+ if (!ctx.suppressTelemetry)
287
+ recordDegradationOnce({
288
+ kind: "tool-cwd-resolution",
289
+ subject: tool,
290
+ reason: `${kind}:${reason}:${absoluteFile}`,
291
+ });
292
+ if (!ctx.suppressTelemetry)
293
+ emitResolution(kind, tool, fallback, reason);
294
+ return fallback;
295
+ }
296
+ /** Runner-shaped adapter kept at the same seam for every runner consumer. */
297
+ export function resolveRunnerCwd(ctx, tool) {
298
+ return resolveToolCwd("runner", tool, ctx.filePath, ctx);
299
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The tool-probe spawn seam (#2894).
3
+ *
4
+ * A sibling of `tool-cwd.ts`: that module owns where a child that READS A
5
+ * PROJECT starts, this one owns the fact that a child which reads no project
6
+ * starts nowhere in particular.
7
+ *
8
+ * It is a module of its own rather than another export on `safe-spawn.ts`
9
+ * deliberately. Every availability test in this repo fakes the process
10
+ * boundary with `vi.mock("clients/safe-spawn.js", … { safeSpawnAsync })`, and
11
+ * a same-module helper would call the module's own unmocked binding — the
12
+ * probe would spawn for real while the test believed it had the boundary. One
13
+ * module hop keeps every existing double production-faithful.
14
+ */
15
+ import { safeSpawnAsync } from "./safe-spawn.js";
16
+ /**
17
+ * Run a tool's own presence/version invocation — `<tool> --version`, `cl`,
18
+ * `go version`, `which <pm>`, `Get-Module -ListAvailable …` — and hand back
19
+ * the raw spawn result for the caller's availability policy to classify.
20
+ *
21
+ * ## The contract: a probe never gets a `cwd`
22
+ *
23
+ * A probe asks whether a binary exists and what it calls itself. The answer
24
+ * does not depend on the directory the child starts in, so this seam passes
25
+ * NONE — the child inherits the host's directory and nothing it reports is
26
+ * read out of a project. That is why the sweep in
27
+ * `tests/clients/dispatch/runners/runner-spawn-cwd-sweep.test.ts` admits ONE
28
+ * cwd-less spawn here instead of the 23 it admitted before: every one of those
29
+ * sites re-decided the same thing in its own words, and 23 sentences a
30
+ * reviewer has to re-read one at a time is how an admission table stops being
31
+ * auditable (#2872 round 3 shipped 127 rows carrying two canned sentences,
32
+ * four of them false where they were read).
33
+ *
34
+ * `cwd` is STRIPPED rather than merely left out of {@link ProbeSpawnOptions}:
35
+ * the type stops an object literal from carrying one, and the destructure
36
+ * below removes one that arrived inside an already-typed options object a
37
+ * caller widened or spread. Both halves are guarded — deleting the
38
+ * destructure changes this call's text, which retires the sweep's admission
39
+ * row, and `tests/clients/probe-and-root-spawn-cwd.test.ts` reds on the
40
+ * smuggled cwd reaching the child. A probe that genuinely needs to
41
+ * run somewhere — `mix credo --version` needs a mix project, `cargo clippy
42
+ * --version` needs a package, `eslint --version` resolves a project-local
43
+ * binary — must call `safeSpawnAsync` directly and be admitted by that sweep
44
+ * on its own reason.
45
+ */
46
+ export async function probeToolAsync(command, args, options) {
47
+ // Destructured away rather than overwritten with `undefined`: the key is
48
+ // then genuinely ABSENT from what reaches the spawn, which is both what the
49
+ // contract says and what `exactOptionalPropertyTypes` wants (a present
50
+ // `cwd: undefined` is a strictness spike the `tests/config` ratchet counts).
51
+ const { cwd: _strippedCwd, ...rest } = (options ?? {});
52
+ return safeSpawnAsync(command, [...args], { ...rest });
53
+ }
@@ -1,4 +1,46 @@
1
1
  import { logLatency } from "./latency-logger.js";
2
+ import { BoundedFifoMap } from "./bounded-cache.js";
3
+ import { recordDegradationOnce } from "./degradation-ledger.js";
4
+ // pi re-runs the extension factory for session rebuilds, but imports this
5
+ // module once per process. Keep conversation activation memory here, keyed by
6
+ // pi's session file rather than by a factory closure or process-wide session.
7
+ export const REMEMBERED_LAZY_TOOLS_MAX_SESSIONS = 128;
8
+ const rememberedLazyToolsBySessionFile = new BoundedFifoMap(REMEMBERED_LAZY_TOOLS_MAX_SESSIONS);
9
+ export function rememberLazyTools(sessionFile, names) {
10
+ if (!sessionFile) {
11
+ recordDegradationOnce({
12
+ kind: "tool-set-session-file-unavailable",
13
+ subject: "activation",
14
+ reason: "session-file identity unavailable; activation memory is inert",
15
+ });
16
+ return;
17
+ }
18
+ const remembered = rememberedLazyToolsBySessionFile.get(sessionFile) ?? new Set();
19
+ for (const name of names)
20
+ remembered.add(name);
21
+ rememberedLazyToolsBySessionFile.set(sessionFile, remembered);
22
+ }
23
+ export function getRememberedLazyTools(sessionFile) {
24
+ return sessionFile
25
+ ? (rememberedLazyToolsBySessionFile.get(sessionFile) ?? new Set())
26
+ : new Set();
27
+ }
28
+ export function clearRememberedLazyTools(sessionFile) {
29
+ if (sessionFile)
30
+ rememberedLazyToolsBySessionFile.delete(sessionFile);
31
+ }
32
+ export function inheritRememberedLazyTools(parentSessionFile, childSessionFile) {
33
+ if (!parentSessionFile ||
34
+ !childSessionFile ||
35
+ parentSessionFile === childSessionFile)
36
+ return;
37
+ const remembered = rememberedLazyToolsBySessionFile.get(parentSessionFile);
38
+ if (remembered)
39
+ rememberedLazyToolsBySessionFile.set(childSessionFile, new Set(remembered));
40
+ }
41
+ export function resetRememberedLazyToolsForTests() {
42
+ rememberedLazyToolsBySessionFile.clear();
43
+ }
2
44
  /**
3
45
  * Whether the host will send this model deferred (searchable) tool
4
46
  * definitions rather than the full inline list.
@@ -272,7 +272,15 @@ export class TrivyClient extends SecurityScanClient {
272
272
  const findings = parseTrivyReport(raw);
273
273
  const secrets = parseTrivySecrets(raw);
274
274
  const licenses = parseTrivyLicenses(raw);
275
- return { success: true, findings, secrets, licenses, scannedAt };
275
+ // #2154: the one trivy site that parsed a scan of this root.
276
+ return {
277
+ success: true,
278
+ analyzed: true,
279
+ findings,
280
+ secrets,
281
+ licenses,
282
+ scannedAt,
283
+ };
276
284
  }
277
285
  catch (err) {
278
286
  return {
@@ -0,0 +1,52 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { getProcessSingleton } from "./process-singletons.js";
3
+ const FAMILY = "turn-context";
4
+ const VERSION = 2;
5
+ function state() {
6
+ return getProcessSingleton(FAMILY, VERSION, () => ({
7
+ sessions: new Map(),
8
+ activeSession: new AsyncLocalStorage(),
9
+ }));
10
+ }
11
+ function sessionState(sessionId) {
12
+ const current = state();
13
+ let session = current.sessions.get(sessionId);
14
+ if (!session) {
15
+ session = { turnId: `${sessionId}:0`, turnIndex: 0 };
16
+ current.sessions.set(sessionId, session);
17
+ }
18
+ return session;
19
+ }
20
+ /** Reset the emit identity before a new session can publish rows. */
21
+ export function resetTurnContext(sessionId) {
22
+ const current = state();
23
+ if (sessionId === undefined)
24
+ return;
25
+ current.sessions.set(sessionId, { turnId: `${sessionId}:0`, turnIndex: 0 });
26
+ }
27
+ /** Ensure a stable host session has a counter without changing detached identity. */
28
+ export function setTurnContextSession(sessionId) {
29
+ const stableSessionId = sessionId?.trim();
30
+ if (stableSessionId !== undefined && stableSessionId !== "")
31
+ sessionState(stableSessionId);
32
+ }
33
+ /** Mint the one id shared by every row emitted during this turn. */
34
+ export function beginTurnContext(sessionId) {
35
+ const session = sessionState(sessionId);
36
+ session.turnIndex += 1;
37
+ session.turnId = `${sessionId}:${session.turnIndex}`;
38
+ return session.turnId;
39
+ }
40
+ /** Run a host event with the session identity that owns its writes. */
41
+ export function runWithTurnContext(sessionId, fn) {
42
+ if (sessionId === undefined)
43
+ return fn();
44
+ return state().activeSession.run(sessionId, fn);
45
+ }
46
+ export function getTurnId() {
47
+ const current = state();
48
+ const sessionId = current.activeSession.getStore();
49
+ if (sessionId === undefined)
50
+ return "turn:0";
51
+ return sessionState(sessionId).turnId;
52
+ }