@herbertgao/pi-extensions 2026.8.11 → 2026.8.13

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 (476) hide show
  1. package/README.md +23 -10
  2. package/{node_modules/@herbertgao/pi-mermaid-open/LICENSE → THIRD_PARTY_NOTICES.md} +19 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +15 -0
  4. package/node_modules/@czottmann/pi-automode/README.md +5 -3
  5. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +2 -0
  6. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-002-global-config-in-extension-data-directory.md +60 -0
  7. package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +1 -0
  8. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +4 -4
  9. package/node_modules/@czottmann/pi-automode/docs/configuration.md +20 -4
  10. package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +1 -1
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/bash.ts +692 -0
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +181 -3
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +4 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +143 -36
  15. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +103 -148
  16. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +32 -6
  17. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +344 -22
  18. package/node_modules/@czottmann/pi-automode/extensions/auto-mode.ts +1 -0
  19. package/node_modules/@czottmann/pi-automode/package.json +4 -1
  20. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -1
  21. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -1
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +4 -0
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +21 -1
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +1 -1
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +19 -19
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +4 -2
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +2 -1
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +10 -2
  29. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  30. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +10 -0
  31. package/node_modules/@herbertgao/pi-subagents/README.md +21 -15
  32. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  33. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +11 -8
  34. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +184 -106
  35. package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +31 -11
  36. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +9 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/index.ts +156 -31
  38. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +17 -0
  39. package/node_modules/@herbertgao/pi-subagents/src/model-resolver.ts +14 -0
  40. package/node_modules/@herbertgao/pi-subagents/src/schedule.ts +8 -0
  41. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +32 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/types.ts +17 -2
  43. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +23 -2
  44. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +259 -24
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  46. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +376 -0
  47. package/node_modules/@luxusai/pi-hindsight/README.md +92 -0
  48. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +56 -0
  49. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +169 -0
  50. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +107 -0
  51. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +89 -0
  52. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +189 -0
  53. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  54. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  55. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +54 -0
  56. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +75 -0
  57. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +300 -0
  58. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +158 -0
  59. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +164 -0
  60. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +139 -0
  61. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +74 -0
  62. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +261 -0
  63. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +151 -0
  64. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +18 -0
  65. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +46 -0
  66. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +383 -0
  67. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +240 -0
  68. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +176 -0
  69. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +178 -0
  70. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +41 -0
  71. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +386 -0
  72. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +39 -0
  73. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +34 -0
  74. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +140 -0
  75. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +61 -0
  76. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +925 -0
  77. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +247 -0
  78. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +547 -0
  79. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +480 -0
  80. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +182 -0
  81. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +86 -0
  82. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +1010 -0
  83. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +175 -0
  84. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +425 -0
  85. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +210 -0
  86. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +817 -0
  87. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +25 -0
  88. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +319 -0
  89. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +196 -0
  90. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +189 -0
  91. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +65 -0
  92. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +260 -0
  93. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +244 -0
  94. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +59 -0
  95. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +82 -0
  96. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +45 -0
  97. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +331 -0
  98. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +354 -0
  99. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +66 -0
  100. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +132 -0
  101. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +228 -0
  102. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +74 -0
  103. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +16 -0
  104. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +648 -0
  105. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +105 -0
  106. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +68 -0
  107. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +71 -0
  108. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +13 -0
  109. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +162 -0
  110. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +131 -0
  111. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +104 -0
  112. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +37 -0
  113. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +893 -0
  114. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +35 -0
  115. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +177 -0
  116. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +7 -0
  117. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +21 -0
  118. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +112 -0
  119. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +111 -0
  120. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +214 -0
  121. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +70 -0
  122. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +392 -0
  123. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +45 -0
  124. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +9 -0
  125. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +860 -0
  126. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +105 -0
  127. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +189 -0
  128. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +299 -0
  129. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +201 -0
  130. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +47 -0
  131. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +239 -0
  132. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +50 -0
  133. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +259 -0
  134. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +77 -0
  135. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +534 -0
  136. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +319 -0
  137. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +325 -0
  138. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +42 -0
  139. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +244 -0
  140. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +56 -0
  141. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +50 -0
  142. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +167 -0
  143. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +207 -0
  144. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +120 -0
  145. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +8 -0
  146. package/node_modules/@luxusai/pi-hindsight/package.json +118 -0
  147. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +75 -0
  148. package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -46
  149. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  150. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  151. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +136 -22
  152. package/node_modules/{@herbertgao → @tifan}/pi-copy-response/README.md +2 -4
  153. package/node_modules/{@herbertgao → @tifan}/pi-copy-response/package.json +5 -15
  154. package/node_modules/{@herbertgao → @tifan}/pi-handoff/README.md +4 -6
  155. package/node_modules/@tifan/pi-handoff/package.json +43 -0
  156. package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/index.ts +1 -1
  157. package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/README.md +2 -4
  158. package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/package.json +5 -15
  159. package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/README.md +5 -7
  160. package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/package.json +5 -19
  161. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/README.md +4 -6
  162. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/package.json +6 -16
  163. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/src/index.ts +1 -6
  164. package/node_modules/{@herbertgao → @tifan}/pi-recap/README.md +4 -6
  165. package/node_modules/@tifan/pi-recap/package.json +42 -0
  166. package/node_modules/{@herbertgao → @tifan}/pi-recap/src/models.ts +1 -6
  167. package/node_modules/{@herbertgao → @tifan}/pi-rename/README.md +13 -8
  168. package/node_modules/@tifan/pi-rename/package.json +47 -0
  169. package/node_modules/@tifan/pi-rename/src/herdr-label.ts +4 -0
  170. package/node_modules/{@herbertgao → @tifan}/pi-rename/src/index.ts +4 -16
  171. package/node_modules/{@herbertgao → @tifan}/pi-rename/src/models.ts +1 -6
  172. package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/README.md +2 -4
  173. package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/package.json +5 -15
  174. package/node_modules/pi-lens/CHANGELOG.md +96 -1
  175. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +14 -5
  177. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +48 -18
  178. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -3
  179. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +16 -4
  180. package/node_modules/pi-lens/dist/clients/bash-file-access.js +3 -1
  181. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -3
  182. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +21 -8
  183. package/node_modules/pi-lens/dist/clients/bounded-cache.js +24 -8
  184. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +4 -3
  185. package/node_modules/pi-lens/dist/clients/cache-manager.js +6 -7
  186. package/node_modules/pi-lens/dist/clients/cache-observability.js +461 -66
  187. package/node_modules/pi-lens/dist/clients/call-graph.js +238 -63
  188. package/node_modules/pi-lens/dist/clients/codebase-model.js +13 -5
  189. package/node_modules/pi-lens/dist/clients/complexity-client.js +1 -3
  190. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -8
  191. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +1 -2
  192. package/node_modules/pi-lens/dist/clients/deadline-utils.js +3 -1
  193. package/node_modules/pi-lens/dist/clients/debug-heap.js +8 -1
  194. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +1 -1
  195. package/node_modules/pi-lens/dist/clients/dependency-checker.js +11 -7
  196. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +39 -2
  197. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +5 -1
  198. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +2 -3
  199. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +13 -1
  200. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +3 -1
  201. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +2 -1
  202. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -4
  204. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +3 -1
  205. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +6 -2
  206. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +31 -7
  207. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +5 -2
  208. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +10 -14
  209. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +5 -1
  210. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +1 -1
  211. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +3 -1
  212. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +25 -6
  213. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +1 -1
  214. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +1 -1
  215. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +3 -1
  216. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +13 -8
  217. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +4 -1
  218. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +25 -10
  219. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +1 -1
  220. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +1 -1
  221. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +1 -1
  222. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +28 -4
  223. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +6 -8
  224. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +212 -11
  225. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +1 -1
  226. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +8 -4
  227. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -6
  228. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +23 -14
  229. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -1
  230. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +7 -9
  231. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +7 -7
  233. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +6 -8
  234. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +13 -8
  235. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +13 -8
  236. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +17 -3
  237. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +7 -1
  238. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +24 -1
  239. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +35 -0
  240. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +1 -3
  241. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +6 -8
  242. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +1 -1
  243. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +2 -1
  244. package/node_modules/pi-lens/dist/clients/dispatch/types.js +6 -1
  245. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +6 -2
  246. package/node_modules/pi-lens/dist/clients/extension-log.js +5 -3
  247. package/node_modules/pi-lens/dist/clients/feature-hints.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/file-kinds.js +34 -129
  249. package/node_modules/pi-lens/dist/clients/file-utils.js +17 -1
  250. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +57 -2
  251. package/node_modules/pi-lens/dist/clients/formatters.js +144 -27
  252. package/node_modules/pi-lens/dist/clients/freshness.js +38 -0
  253. package/node_modules/pi-lens/dist/clients/git-guard.js +43 -13
  254. package/node_modules/pi-lens/dist/clients/go-client.js +1 -3
  255. package/node_modules/pi-lens/dist/clients/grammar-source.js +1 -1
  256. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +30 -4
  257. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +1 -3
  258. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +4 -2
  259. package/node_modules/pi-lens/dist/clients/installer/index.js +86 -77
  260. package/node_modules/pi-lens/dist/clients/instance-reaper.js +5 -2
  261. package/node_modules/pi-lens/dist/clients/instance-registry.js +4 -2
  262. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  263. package/node_modules/pi-lens/dist/clients/knip-client.js +29 -15
  264. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -1
  265. package/node_modules/pi-lens/dist/clients/latency-logger.js +13 -2
  266. package/node_modules/pi-lens/dist/clients/lens-config.js +1 -1
  267. package/node_modules/pi-lens/dist/clients/lens-map.js +8 -3
  268. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +1 -1
  269. package/node_modules/pi-lens/dist/clients/lsp/config.js +4 -6
  270. package/node_modules/pi-lens/dist/clients/lsp/edits.js +119 -32
  271. package/node_modules/pi-lens/dist/clients/lsp/index.js +94 -35
  272. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +2 -1
  273. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +4 -1
  274. package/node_modules/pi-lens/dist/clients/lsp/launch.js +11 -3
  275. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +152 -0
  276. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -7
  277. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +10 -2
  278. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +9 -4
  279. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -3
  280. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +10 -20
  281. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +1 -1
  282. package/node_modules/pi-lens/dist/clients/memory-sampler.js +73 -7
  283. package/node_modules/pi-lens/dist/clients/metrics-client.js +1 -3
  284. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +8 -3
  285. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +7 -1
  286. package/node_modules/pi-lens/dist/clients/module-report.js +14 -17
  287. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +3 -5
  288. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +220 -0
  289. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -1
  290. package/node_modules/pi-lens/dist/clients/pipeline.js +57 -9
  291. package/node_modules/pi-lens/dist/clients/project-conventions.js +2 -8
  292. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +10 -7
  293. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +2 -1
  294. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +3 -1
  295. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +13 -9
  296. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +3 -1
  297. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -2
  298. package/node_modules/pi-lens/dist/clients/project-lens-config.js +12 -5
  299. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +48 -0
  300. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +15 -1
  301. package/node_modules/pi-lens/dist/clients/project-snapshot.js +472 -66
  302. package/node_modules/pi-lens/dist/clients/read-expansion.js +12 -2
  303. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +5 -4
  304. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +5 -2
  305. package/node_modules/pi-lens/dist/clients/read-guard.js +1 -1
  306. package/node_modules/pi-lens/dist/clients/resource-sampler.js +1 -1
  307. package/node_modules/pi-lens/dist/clients/reverse-deps.js +6 -5
  308. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +42 -17
  309. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +8 -2
  310. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +10 -1
  311. package/node_modules/pi-lens/dist/clients/review-graph/query.js +2 -1
  312. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +4 -2
  313. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +2 -2
  314. package/node_modules/pi-lens/dist/clients/ruff-client.js +6 -5
  315. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +63 -31
  316. package/node_modules/pi-lens/dist/clients/runtime-context.js +12 -4
  317. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +83 -4
  318. package/node_modules/pi-lens/dist/clients/runtime-session.js +14 -0
  319. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +59 -3
  320. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +148 -34
  321. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -6
  322. package/node_modules/pi-lens/dist/clients/rust-client.js +1 -3
  323. package/node_modules/pi-lens/dist/clients/safe-spawn.js +150 -11
  324. package/node_modules/pi-lens/dist/clients/security-scan-client.js +1 -3
  325. package/node_modules/pi-lens/dist/clients/session-summary.js +3 -1
  326. package/node_modules/pi-lens/dist/clients/sg-runner.js +1 -3
  327. package/node_modules/pi-lens/dist/clients/source-filter.js +10 -6
  328. package/node_modules/pi-lens/dist/clients/source-walker.js +1 -1
  329. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +96 -0
  330. package/node_modules/pi-lens/dist/clients/startup-scan.js +4 -1
  331. package/node_modules/pi-lens/dist/clients/tool-definition.js +7 -0
  332. package/node_modules/pi-lens/dist/clients/tool-policy.js +26 -11
  333. package/node_modules/pi-lens/dist/clients/tool-render.js +2 -1
  334. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +10 -4
  335. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  336. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +12 -5
  337. package/node_modules/pi-lens/dist/clients/warm-attach.js +12 -5
  338. package/node_modules/pi-lens/dist/clients/widget-state.js +27 -11
  339. package/node_modules/pi-lens/dist/clients/word-index.js +3 -1
  340. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -2
  341. package/node_modules/pi-lens/dist/clients/zizmor-config.js +9 -1
  342. package/node_modules/pi-lens/dist/i18n.js +13 -5
  343. package/node_modules/pi-lens/dist/index.js +10797 -8627
  344. package/node_modules/pi-lens/dist/mcp/server.js +3 -1
  345. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +68 -12
  346. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +21 -7
  347. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +164 -35
  348. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +24 -7
  349. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  350. package/node_modules/pi-lens/dist/tools/module-report.js +1 -1
  351. package/node_modules/pi-lens/dist/tools/project-report.js +1 -1
  352. package/node_modules/pi-lens/dist/tools/symbol-search.js +5 -2
  353. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +1 -1
  354. package/node_modules/pi-lens/docs/servercapabilities.md +1 -1
  355. package/node_modules/pi-lens/package.json +160 -156
  356. package/node_modules/pi-lens/rules/typos/_typos.toml +2 -2
  357. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +31 -13
  358. package/node_modules/pi-lens/scripts/download-grammars.js +9 -3
  359. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +30 -9
  360. package/node_modules/pi-mcp-adapter/CHANGELOG.md +47 -0
  361. package/node_modules/pi-mcp-adapter/README.md +42 -13
  362. package/node_modules/pi-mcp-adapter/commands.ts +4 -2
  363. package/node_modules/pi-mcp-adapter/config.ts +45 -5
  364. package/node_modules/pi-mcp-adapter/direct-tools.ts +103 -92
  365. package/node_modules/pi-mcp-adapter/dist/agent-dir.d.ts +10 -0
  366. package/node_modules/pi-mcp-adapter/dist/agent-dir.js +63 -0
  367. package/node_modules/pi-mcp-adapter/dist/agent-dir.js.map +1 -0
  368. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.d.ts +8 -0
  369. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js +356 -0
  370. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js.map +1 -0
  371. package/node_modules/pi-mcp-adapter/dist/config.d.ts +123 -0
  372. package/node_modules/pi-mcp-adapter/dist/config.js +1087 -0
  373. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -0
  374. package/node_modules/pi-mcp-adapter/dist/metadata-cache.d.ts +19 -0
  375. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +319 -0
  376. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -0
  377. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.d.ts +2 -0
  378. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js +156 -0
  379. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js.map +1 -0
  380. package/node_modules/pi-mcp-adapter/dist/resource-tools.d.ts +1 -0
  381. package/node_modules/pi-mcp-adapter/dist/resource-tools.js +15 -0
  382. package/node_modules/pi-mcp-adapter/dist/resource-tools.js.map +1 -0
  383. package/node_modules/pi-mcp-adapter/dist/types.d.ts +564 -0
  384. package/node_modules/pi-mcp-adapter/dist/types.js +287 -0
  385. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -0
  386. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.d.ts +6 -0
  387. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js +37 -0
  388. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js.map +1 -0
  389. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.d.ts +104 -0
  390. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js +59 -0
  391. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js.map +1 -0
  392. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.d.ts +4 -0
  393. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js +27 -0
  394. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js.map +1 -0
  395. package/node_modules/pi-mcp-adapter/dist/utils.d.ts +32 -0
  396. package/node_modules/pi-mcp-adapter/dist/utils.js +338 -0
  397. package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -0
  398. package/node_modules/pi-mcp-adapter/failure-backoff.ts +23 -0
  399. package/node_modules/pi-mcp-adapter/index.ts +204 -82
  400. package/node_modules/pi-mcp-adapter/init.ts +41 -30
  401. package/node_modules/pi-mcp-adapter/lifecycle.ts +16 -8
  402. package/node_modules/pi-mcp-adapter/mcp-code.ts +2 -0
  403. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +66 -16
  404. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +95 -7
  405. package/node_modules/pi-mcp-adapter/mcp-probe.ts +18 -4
  406. package/node_modules/pi-mcp-adapter/mcp-references.ts +368 -0
  407. package/node_modules/pi-mcp-adapter/mcp-status.ts +8 -19
  408. package/node_modules/pi-mcp-adapter/metadata-cache.ts +20 -12
  409. package/node_modules/pi-mcp-adapter/namespace-tools.ts +276 -0
  410. package/node_modules/pi-mcp-adapter/package.json +24 -6
  411. package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -10
  412. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -0
  413. package/node_modules/pi-mcp-adapter/server-manager.ts +63 -12
  414. package/node_modules/pi-mcp-adapter/state.ts +1 -3
  415. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +16 -2
  416. package/node_modules/pi-mcp-adapter/types.ts +48 -1
  417. package/node_modules/pi-mcp-adapter/ui-server.ts +3 -19
  418. package/node_modules/pi-mcp-adapter/ui-session.ts +2 -1
  419. package/node_modules/pi-mcp-adapter/utils.ts +21 -19
  420. package/node_modules/pi-web-access/CHANGELOG.md +26 -0
  421. package/node_modules/pi-web-access/README.md +71 -14
  422. package/node_modules/pi-web-access/chrome-cookies.ts +115 -35
  423. package/node_modules/pi-web-access/credential-source.ts +6 -2
  424. package/node_modules/pi-web-access/curator-page.ts +5 -3
  425. package/node_modules/pi-web-access/curator-server.ts +2 -1
  426. package/node_modules/pi-web-access/extract.ts +89 -9
  427. package/node_modules/pi-web-access/fetch-params.ts +13 -0
  428. package/node_modules/pi-web-access/gemini-adc.ts +298 -0
  429. package/node_modules/pi-web-access/gemini-api.ts +59 -15
  430. package/node_modules/pi-web-access/gemini-search.ts +58 -18
  431. package/node_modules/pi-web-access/gemini-url-context.ts +3 -2
  432. package/node_modules/pi-web-access/gemini-web-config.ts +57 -7
  433. package/node_modules/pi-web-access/gemini-web.ts +9 -3
  434. package/node_modules/pi-web-access/github-api.ts +5 -4
  435. package/node_modules/pi-web-access/github-issue-pr.ts +700 -0
  436. package/node_modules/pi-web-access/index.ts +403 -297
  437. package/node_modules/pi-web-access/kimi-search.ts +234 -0
  438. package/node_modules/pi-web-access/openai-search.ts +118 -21
  439. package/node_modules/pi-web-access/package.json +3 -2
  440. package/node_modules/pi-web-access/ssrf-protection.ts +4 -1
  441. package/node_modules/pi-web-access/utils.ts +307 -1
  442. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  443. package/package.json +46 -37
  444. package/node_modules/@herbertgao/pi-copy-response/CHANGELOG.md +0 -49
  445. package/node_modules/@herbertgao/pi-copy-response/LICENSE +0 -22
  446. package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +0 -83
  447. package/node_modules/@herbertgao/pi-handoff/LICENSE +0 -22
  448. package/node_modules/@herbertgao/pi-handoff/package.json +0 -60
  449. package/node_modules/@herbertgao/pi-inline-skills/CHANGELOG.md +0 -57
  450. package/node_modules/@herbertgao/pi-inline-skills/LICENSE +0 -22
  451. package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +0 -39
  452. package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +0 -63
  453. package/node_modules/@herbertgao/pi-preferred-thinking/LICENSE +0 -22
  454. package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +0 -101
  455. package/node_modules/@herbertgao/pi-recap/LICENSE +0 -22
  456. package/node_modules/@herbertgao/pi-recap/package.json +0 -55
  457. package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +0 -49
  458. package/node_modules/@herbertgao/pi-rename/LICENSE +0 -22
  459. package/node_modules/@herbertgao/pi-rename/package.json +0 -63
  460. package/node_modules/@herbertgao/pi-titlebar-spinner/CHANGELOG.md +0 -25
  461. package/node_modules/@herbertgao/pi-titlebar-spinner/LICENSE +0 -22
  462. /package/node_modules/{@herbertgao → @tifan}/pi-copy-response/src/index.ts +0 -0
  463. /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/complete-text.ts +0 -0
  464. /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/session-query.ts +0 -0
  465. /package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/src/index.ts +0 -0
  466. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +0 -0
  467. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/viewer.mjs +0 -0
  468. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/src/index.ts +0 -0
  469. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/index.ts +0 -0
  470. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/model-picker.ts +0 -0
  471. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/sanitize.ts +0 -0
  472. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/tui.ts +0 -0
  473. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/model-picker.ts +0 -0
  474. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/naming.ts +0 -0
  475. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/sanitize.ts +0 -0
  476. /package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/src/index.ts +0 -0
@@ -8,7 +8,7 @@ function safeInlineJSON(data: unknown): string {
8
8
  }
9
9
 
10
10
  function buildProviderButtons(
11
- available: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean },
11
+ available: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; kimi: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean },
12
12
  selected: string,
13
13
  hasInitialQueries: boolean,
14
14
  ): string {
@@ -34,6 +34,7 @@ function buildProviderButtons(
34
34
  { value: "duckduckgo", label: "DuckDuckGo", available: available.duckduckgo },
35
35
  { value: "perplexity", label: "Perplexity", available: available.perplexity },
36
36
  { value: "gemini", label: "Gemini", available: available.gemini },
37
+ { value: "kimi", label: "Kimi", available: available.kimi },
37
38
  { value: "anysearch", label: "AnySearch", available: available.anysearch },
38
39
  { value: "xai", label: "xAI", available: available.xai },
39
40
  { value: "brightdata", label: "Bright Data", available: available.brightdata },
@@ -58,7 +59,7 @@ export function generateCuratorPage(
58
59
  queries: string[],
59
60
  sessionToken: string,
60
61
  timeout: number,
61
- availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean },
62
+ availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; kimi: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean },
62
63
  defaultProvider: string,
63
64
  searchProvider: string,
64
65
  summaryModels: Array<{ value: string; label: string }>,
@@ -1459,7 +1460,7 @@ const SCRIPT = `(function() {
1459
1460
  var token = DATA.sessionToken;
1460
1461
  var timeoutSec = DATA.timeout;
1461
1462
  var queries = Array.isArray(DATA.queries) ? DATA.queries : [];
1462
- var providers = ["all", "openai", "exa", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "bocha", "ollama", "searxng", "duckduckgo", "perplexity", "gemini", "anysearch", "xai", "brightdata", "serpbase", "serper", "valyu"];
1463
+ var providers = ["all", "openai", "exa", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "bocha", "ollama", "searxng", "duckduckgo", "perplexity", "gemini", "kimi", "anysearch", "xai", "brightdata", "serpbase", "serper", "valyu"];
1463
1464
  var availProviders = DATA.availableProviders && typeof DATA.availableProviders === "object" ? DATA.availableProviders : {};
1464
1465
  var workflow = "summary-review";
1465
1466
  var initialDefaultProvider = typeof DATA.defaultProvider === "string" ? DATA.defaultProvider : "exa";
@@ -1682,6 +1683,7 @@ const SCRIPT = `(function() {
1682
1683
  if (provider === "perplexity") return "Perplexity";
1683
1684
  if (provider === "exa") return "Exa";
1684
1685
  if (provider === "gemini") return "Gemini";
1686
+ if (provider === "kimi") return "Kimi";
1685
1687
  if (provider === "anysearch") return "AnySearch";
1686
1688
  if (provider === "xai") return "xAI";
1687
1689
  if (provider === "brightdata") return "Bright Data";
@@ -18,7 +18,7 @@ export interface CuratorServerOptions {
18
18
  queries: string[];
19
19
  sessionToken: string;
20
20
  timeout: number;
21
- availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean };
21
+ availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; kimi: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean };
22
22
  defaultProvider: string;
23
23
  searchProvider: string;
24
24
  summaryModels: Array<{ value: string; label: string }>;
@@ -292,6 +292,7 @@ export function startCuratorServer(
292
292
  if (provider === "perplexity") return availableProviders.perplexity;
293
293
  if (provider === "exa") return availableProviders.exa;
294
294
  if (provider === "gemini") return availableProviders.gemini;
295
+ if (provider === "kimi") return availableProviders.kimi;
295
296
  if (provider === "anysearch") return availableProviders.anysearch;
296
297
  if (provider === "xai") return availableProviders.xai;
297
298
  if (provider === "brightdata") return availableProviders.brightdata;
@@ -8,6 +8,7 @@ import { activityMonitor } from "./activity.ts";
8
8
  import { extractRSCContent } from "./rsc-extract.ts";
9
9
  import { extractPDFToMarkdown, isPDF, loadPDFConfig } from "./pdf-extract.ts";
10
10
  import { extractGitHub } from "./github-extract.ts";
11
+ import { extractGitHubIssuePr } from "./github-issue-pr.ts";
11
12
  import { isYouTubeURL, isYouTubeEnabled, extractYouTube, extractYouTubeFrame, extractYouTubeFrames, getYouTubeStreamInfo } from "./youtube-extract.ts";
12
13
  import { CredentialResolutionError } from "./credential-source.ts";
13
14
  import { extractWithUrlContext, extractWithGeminiWeb } from "./gemini-url-context.ts";
@@ -23,7 +24,7 @@ import { extractWithBrightDataUnlocker, isBrightDataUnlockerAvailable } from "./
23
24
  import { isVideoFile, extractVideo, extractVideoFrame, getLocalVideoDuration } from "./video-extract.ts";
24
25
  import { appendDeclaredWebLinks, discoverDeclaredWebLinks, type DeclaredWebLink } from "./declared-web-links.ts";
25
26
  import { fetchRemoteUrl, loadFetchContentDomainPolicy, loadSsrfConfig, validateRemoteUrl, type DomainPolicy, type Lookup, type SsrfConfig } from "./ssrf-protection.ts";
26
- import { formatSeconds, getWebSearchConfigPath } from "./utils.ts";
27
+ import { formatSeconds, getWebSearchConfigPath, type ProxiedRequestInit } from "./utils.ts";
27
28
  import { isImageEnabled } from "./feature-config.ts";
28
29
  import { assertAuthFetchUrl, authFetchRedirectGuard, type AuthFetchProfile } from "./auth-fetch.ts";
29
30
  import { getBrowserCookiesForHosts, getLastBrowserCookieDiagnostic } from "./chrome-cookies.ts";
@@ -42,6 +43,13 @@ type FetchRouting = { providers: FetchProvider[]; allowRemoteHostedProviders: bo
42
43
  const DEFAULT_FETCH_PROVIDER_ORDER: FetchProvider[] = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"];
43
44
  const REMOTE_HOSTED_FETCH_PROVIDERS = new Set<FetchProvider>(["jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "parallel-mcp", "brightdata", "gemini"]);
44
45
 
46
+ async function extractWithDefuddle(text: string, url: string): Promise<{ title: string; content: string } | null> {
47
+ const { Defuddle } = await import("defuddle/node");
48
+ const { document } = parseHTML(text);
49
+ const result = await Defuddle(document as unknown as Document, url, { markdown: true, useAsync: false });
50
+ return typeof result.content === "string" ? { title: result.title, content: result.content } : null;
51
+ }
52
+
45
53
  export { loadSsrfConfig } from "./ssrf-protection.ts";
46
54
 
47
55
  export function loadSsrfAllowRanges(): string[] {
@@ -183,6 +191,32 @@ function loadFetchRouting(): FetchRouting {
183
191
  return { providers, allowRemoteHostedProviders: allowRemoteHostedProvidersValue === true };
184
192
  }
185
193
 
194
+ /** Names of the search/fetch tools the caller has actually registered, so
195
+ * failure guidance never points at tools that do not exist in the session. */
196
+ export interface RegisteredToolNames {
197
+ webSearch?: string;
198
+ fetchContent?: string;
199
+ }
200
+
201
+ /** Guidance for definitive origin 404/410 responses: no extraction provider
202
+ * can retrieve a page the origin says is gone, so point at the registered
203
+ * search/fetch tools (when the caller knows them) instead of provider config. */
204
+ function notFoundGuidance(result: ExtractedContent, toolNames?: RegisteredToolNames): string {
205
+ const lines = [
206
+ result.error ?? `HTTP ${result.status}`,
207
+ "",
208
+ `The origin server says this page does not exist (HTTP ${result.status}), so extraction providers cannot retrieve it.`,
209
+ ];
210
+ if (toolNames?.webSearch && toolNames.fetchContent) {
211
+ lines.push(`The page may have moved or been renamed. Use ${toolNames.webSearch} to find the current URL, then retry ${toolNames.fetchContent} with it.`);
212
+ } else if (toolNames?.webSearch) {
213
+ lines.push(`The page may have moved or been renamed. Use ${toolNames.webSearch} to find the current URL.`);
214
+ } else {
215
+ lines.push("The page may have moved or been renamed. Find the current URL, then retry the fetch with it.");
216
+ }
217
+ return lines.join("\n");
218
+ }
219
+
186
220
  function abortedResult(url: string): ExtractedContent {
187
221
  return { url, title: "", content: "", error: "Aborted" };
188
222
  }
@@ -227,6 +261,9 @@ export interface ExtractOptions {
227
261
  mode?: "readable" | "raw" | "answer";
228
262
  answerModel?: string;
229
263
  authFetchProfile?: AuthFetchProfile;
264
+ toolNames?: RegisteredToolNames;
265
+ /** Optional http(s) proxy URL; routed through the curl-backed transport. */
266
+ proxy?: string;
230
267
  /** Custom DNS resolver used for SSRF validation. Primarily a test seam. */
231
268
  lookup?: Lookup;
232
269
  }
@@ -585,6 +622,18 @@ export async function extractContent(
585
622
  return { url, title: "", content: "", error: errorMessage(err) };
586
623
  }
587
624
 
625
+ try {
626
+ const ghIssuePrResult = await extractGitHubIssuePr(url, signal, options);
627
+ if (ghIssuePrResult) return ghIssuePrResult;
628
+ if (signal?.aborted) return abortedResult(url);
629
+ } catch (err) {
630
+ const message = errorMessage(err);
631
+ if (isAbortError(err)) return abortedResult(url);
632
+ if (isConfigParseError(err)) {
633
+ return { url, title: "", content: "", error: message };
634
+ }
635
+ }
636
+
588
637
  try {
589
638
  const ghResult = await extractGitHub(url, signal, options?.forceClone);
590
639
  if (ghResult) return ghResult;
@@ -856,6 +905,14 @@ export async function extractContent(
856
905
  const finalHttpResult = httpResult as ExtractedContent | null;
857
906
  if (finalHttpResult && declaredLinks.length > 0) return { ...finalHttpResult, error: null };
858
907
 
908
+ // A definitive 404/410 from the origin means no extraction provider can
909
+ // retrieve the page, so the provider-configuration checklist below would
910
+ // send users down the wrong path. Point at search instead.
911
+ if (finalHttpResult?.status === 404 || finalHttpResult?.status === 410) {
912
+ return { ...finalHttpResult, error: notFoundGuidance(finalHttpResult, options?.toolNames) };
913
+ }
914
+
915
+ const searchToolName = options?.toolNames?.webSearch;
859
916
  const guidance = [
860
917
  finalHttpResult?.error ?? "No fetch_content provider returned content",
861
918
  ...(firecrawlError ? [`Firecrawl fallback failed: ${firecrawlError}`] : []),
@@ -879,19 +936,17 @@ export async function extractContent(
879
936
  ` • Set brightdataApiKey and brightdataUnlockerZone in ${WEB_SEARCH_CONFIG_PATH} or BRIGHTDATA_API_KEY and BRIGHTDATA_UNLOCKER_ZONE`,
880
937
  ` • Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,
881
938
  " • Sign into gemini.google.com in Chrome",
882
- " • Use web_search to find content about this topic",
939
+ ...(searchToolName ? [` • Use ${searchToolName} to find content about this topic`] : []),
883
940
  ].join("\n");
884
941
  return { ...(finalHttpResult ?? { url, title: "", content: "", error: null }), error: guidance };
885
942
  }
886
943
 
887
944
  function isLikelyJSRendered(html: string): boolean {
888
- // Extract body content
889
945
  const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
890
946
  if (!bodyMatch) return false;
891
947
 
892
948
  const bodyHtml = bodyMatch[1];
893
949
 
894
- // Strip tags to get text content
895
950
  const textContent = bodyHtml
896
951
  .replace(/<script[\s\S]*?<\/script>/gi, "")
897
952
  .replace(/<style[\s\S]*?<\/style>/gi, "")
@@ -899,10 +954,8 @@ function isLikelyJSRendered(html: string): boolean {
899
954
  .replace(/\s+/g, " ")
900
955
  .trim();
901
956
 
902
- // Count scripts
903
957
  const scriptCount = (html.match(/<script/gi) || []).length;
904
958
 
905
- // Heuristic: little text content but many scripts suggests JS rendering
906
959
  return textContent.length < 500 && scriptCount > 3;
907
960
  }
908
961
 
@@ -999,8 +1052,10 @@ async function extractViaHttp(
999
1052
  const ssrf = loadSsrfConfig();
1000
1053
  const domainPolicy = loadFetchContentDomainPolicy();
1001
1054
  const authProfile = options?.authFetchProfile;
1002
- const requestInit = {
1055
+ const trustEnvProxy = options?.proxy === undefined && ssrf.trustEnvProxy;
1056
+ const requestInit: ProxiedRequestInit = {
1003
1057
  signal: controller.signal,
1058
+ __proxy: options?.proxy,
1004
1059
  headers: {
1005
1060
  "User-Agent": "OpenAI File Downloader, XaiImageApiFetch/1.0",
1006
1061
  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
@@ -1014,13 +1069,13 @@ async function extractViaHttp(
1014
1069
  },
1015
1070
  };
1016
1071
  const response = authProfile
1017
- ? await fetchAuthenticatedRemoteUrl(url, requestInit, { ssrf, domainPolicy, ...(options?.lookup ? { lookup: options.lookup } : {}) }, authProfile)
1072
+ ? await fetchAuthenticatedRemoteUrl(url, requestInit, { ssrf: { ...ssrf, trustEnvProxy }, domainPolicy, ...(options?.lookup ? { lookup: options.lookup } : {}) }, authProfile)
1018
1073
  : await fetchRemoteUrl(
1019
1074
  url,
1020
1075
  requestInit,
1021
1076
  {
1022
1077
  allowRanges: ssrf.allowRanges,
1023
- trustEnvProxy: ssrf.trustEnvProxy,
1078
+ trustEnvProxy,
1024
1079
  domainPolicy,
1025
1080
  ...(options?.lookup ? { lookup: options.lookup } : {}),
1026
1081
  },
@@ -1170,6 +1225,19 @@ async function extractViaHttp(
1170
1225
  declaredLinks,
1171
1226
  };
1172
1227
  }
1228
+ controller.signal.throwIfAborted();
1229
+ const defuddleResult = await extractWithDefuddle(text, response.url || url);
1230
+ controller.signal.throwIfAborted();
1231
+ if (defuddleResult && defuddleResult.content.length >= MIN_USEFUL_CONTENT) {
1232
+ activityMonitor.logComplete(activityId, response.status);
1233
+ return {
1234
+ url,
1235
+ title: documentTitle || defuddleResult.title,
1236
+ content: appendDeclaredWebLinks(defuddleResult.content, declaredLinks),
1237
+ error: null,
1238
+ declaredLinks,
1239
+ };
1240
+ }
1173
1241
 
1174
1242
  activityMonitor.logComplete(activityId, response.status);
1175
1243
  const jsRendered = isLikelyJSRendered(text);
@@ -1203,6 +1271,18 @@ async function extractViaHttp(
1203
1271
  declaredLinks,
1204
1272
  };
1205
1273
  }
1274
+ controller.signal.throwIfAborted();
1275
+ const defuddleResult = await extractWithDefuddle(text, response.url || url);
1276
+ controller.signal.throwIfAborted();
1277
+ if (defuddleResult && defuddleResult.content.length >= MIN_USEFUL_CONTENT) {
1278
+ return {
1279
+ url,
1280
+ title: article.title || documentTitle || defuddleResult.title,
1281
+ content: appendDeclaredWebLinks(defuddleResult.content, declaredLinks),
1282
+ error: null,
1283
+ declaredLinks,
1284
+ };
1285
+ }
1206
1286
  return {
1207
1287
  url,
1208
1288
  title: article.title || documentTitle,
@@ -1,3 +1,5 @@
1
+ import { normalizeProxyUrl } from "./utils.ts";
2
+
1
3
  export interface FetchContentParams {
2
4
  url?: unknown;
3
5
  urls?: unknown;
@@ -9,6 +11,7 @@ export interface FetchContentParams {
9
11
  mode?: unknown;
10
12
  answerModel?: unknown;
11
13
  auth?: unknown;
14
+ proxy?: unknown;
12
15
  }
13
16
 
14
17
  export interface NormalizedFetchContentParams {
@@ -22,6 +25,7 @@ export interface NormalizedFetchContentParams {
22
25
  mode?: "readable" | "raw" | "answer";
23
26
  answerModel?: string;
24
27
  auth?: true | string;
28
+ proxy?: string;
25
29
  };
26
30
  }
27
31
 
@@ -39,6 +43,7 @@ export function normalizeFetchContentParams(params: FetchContentParams): Normali
39
43
  const mode = normalizeMode(params.mode);
40
44
  const answerModel = normalizeOptionalString(params.answerModel);
41
45
  const auth = normalizeAuth(params.auth);
46
+ const proxy = normalizeProxy(params.proxy);
42
47
 
43
48
  return {
44
49
  urlList,
@@ -51,6 +56,7 @@ export function normalizeFetchContentParams(params: FetchContentParams): Normali
51
56
  ...(mode !== undefined ? { mode } : {}),
52
57
  ...(answerModel !== undefined ? { answerModel } : {}),
53
58
  ...(auth !== undefined ? { auth } : {}),
59
+ ...(proxy !== undefined ? { proxy } : {}),
54
60
  },
55
61
  };
56
62
  }
@@ -88,6 +94,13 @@ function normalizeAuth(value: unknown): true | string | undefined {
88
94
  throw new Error("auth must be a profile name, true, or false");
89
95
  }
90
96
 
97
+ function normalizeProxy(value: unknown): string | undefined {
98
+ if (value === undefined || value === false) return undefined;
99
+ if (value === null) throw new Error("proxy must be an http(s) proxy URL string");
100
+ const normalized = normalizeProxyUrl(value, "proxy");
101
+ return normalized ?? "";
102
+ }
103
+
91
104
  function normalizeOptionalFrameCount(value: unknown): number | undefined {
92
105
  if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 12) return undefined;
93
106
  return value;
@@ -0,0 +1,298 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { createSign } from "node:crypto";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { getWebSearchConfigPath } from "./utils.ts";
6
+ import { redactCredential, CredentialResolutionError } from "./credential-source.ts";
7
+
8
+ const CONFIG_PATH = getWebSearchConfigPath();
9
+ const DEFAULT_ADC_PATH = join(homedir(), ".config", "gcloud", "application_default_credentials.json");
10
+ const TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
11
+ const VERTEX_HOST = "https://aiplatform.googleapis.com";
12
+ const VERTEX_API_VERSION = "v1";
13
+ const REFRESH_SKEW_MS = 60_000;
14
+
15
+ interface GeminiAdcConfig {
16
+ geminiAuth?: unknown;
17
+ geminiProject?: unknown;
18
+ geminiLocation?: unknown;
19
+ geminiApiKey?: unknown;
20
+ geminiBaseUrl?: unknown;
21
+ }
22
+
23
+ let cachedConfig: GeminiAdcConfig | null = null;
24
+
25
+ function loadConfig(): GeminiAdcConfig {
26
+ if (cachedConfig) return cachedConfig;
27
+ if (!existsSync(CONFIG_PATH)) {
28
+ cachedConfig = {};
29
+ return cachedConfig;
30
+ }
31
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
32
+ try {
33
+ const parsed = JSON.parse(raw);
34
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
35
+ throw new Error("config root must be an object");
36
+ }
37
+ cachedConfig = parsed as GeminiAdcConfig;
38
+ } catch (err) {
39
+ const message = err instanceof Error ? err.message : String(err);
40
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
41
+ }
42
+ return cachedConfig;
43
+ }
44
+
45
+ function normalizeIdentifier(value: unknown, envName: string): string | null {
46
+ if (typeof value === "string") {
47
+ const trimmed = value.trim();
48
+ if (trimmed) return trimmed;
49
+ }
50
+ const fromEnv = process.env[envName]?.trim();
51
+ return fromEnv ? fromEnv : null;
52
+ }
53
+
54
+ export function isAdcAuthSelected(): boolean {
55
+ return (loadConfig().geminiAuth ?? "").toString().trim().toLowerCase() === "adc";
56
+ }
57
+
58
+ export function getAdcProject(): string | null {
59
+ return (
60
+ normalizeIdentifier(loadConfig().geminiProject, "GOOGLE_CLOUD_PROJECT") ??
61
+ normalizeIdentifier(null, "GCLOUD_PROJECT")
62
+ );
63
+ }
64
+
65
+ export function getAdcLocation(): string | null {
66
+ return normalizeIdentifier(loadConfig().geminiLocation, "GOOGLE_CLOUD_LOCATION");
67
+ }
68
+
69
+ function getAdcPath(): string {
70
+ return process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim() || DEFAULT_ADC_PATH;
71
+ }
72
+
73
+ export function getVertexApiBase(project: string, location: string): string {
74
+ return `${VERTEX_HOST}/${VERTEX_API_VERSION}/projects/${encodeURIComponent(project)}/locations/${encodeURIComponent(location)}/publishers/google`;
75
+ }
76
+
77
+ export function isVertexHost(origin: string): boolean {
78
+ return origin === VERTEX_HOST;
79
+ }
80
+
81
+ interface AdcFile {
82
+ type?: string;
83
+ client_id?: string;
84
+ client_secret?: string;
85
+ refresh_token?: string;
86
+ client_email?: string;
87
+ private_key?: string;
88
+ private_key_id?: string;
89
+ token_uri?: string;
90
+ universe_domain?: string;
91
+ }
92
+
93
+ function objectRecord(value: unknown): Record<string, unknown> | null {
94
+ return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : null;
95
+ }
96
+
97
+ function stringField(record: Record<string, unknown>, name: string): string | undefined {
98
+ const value = record[name];
99
+ return typeof value === "string" ? value : undefined;
100
+ }
101
+
102
+ function requiredString(record: Record<string, unknown>, name: string, context: string): string {
103
+ const value = stringField(record, name)?.trim();
104
+ if (!value) throw new Error(`${context} is missing ${name}`);
105
+ return value;
106
+ }
107
+
108
+ function parseAdcFile(raw: string): AdcFile {
109
+ const parsed = objectRecord(JSON.parse(raw));
110
+ if (!parsed) throw new Error("credential root must be an object");
111
+ const type = stringField(parsed, "type") ?? "authorized_user";
112
+ if (type === "authorized_user") {
113
+ return {
114
+ type,
115
+ client_id: requiredString(parsed, "client_id", "Gemini ADC authorized_user file"),
116
+ client_secret: requiredString(parsed, "client_secret", "Gemini ADC authorized_user file"),
117
+ refresh_token: requiredString(parsed, "refresh_token", "Gemini ADC authorized_user file"),
118
+ universe_domain: stringField(parsed, "universe_domain"),
119
+ };
120
+ }
121
+ if (type === "service_account") {
122
+ return {
123
+ type,
124
+ client_email: requiredString(parsed, "client_email", "Gemini ADC service_account file"),
125
+ private_key: requiredString(parsed, "private_key", "Gemini ADC service_account file"),
126
+ private_key_id: stringField(parsed, "private_key_id"),
127
+ token_uri: stringField(parsed, "token_uri"),
128
+ universe_domain: stringField(parsed, "universe_domain"),
129
+ };
130
+ }
131
+ return { type };
132
+ }
133
+
134
+ function parseTokenExchangeResponse(text: string, context: string): CachedToken {
135
+ let raw: unknown;
136
+ try {
137
+ raw = JSON.parse(text);
138
+ } catch (err) {
139
+ const message = err instanceof Error ? err.message : String(err);
140
+ throw new Error(`${context} returned invalid JSON: ${message}`);
141
+ }
142
+ const parsed = objectRecord(raw);
143
+ if (!parsed) throw new Error(`${context} returned a non-object response`);
144
+ const token = stringField(parsed, "access_token")?.trim();
145
+ if (!token) throw new Error(`${context} returned no access_token`);
146
+ const expiresIn = parsed.expires_in ?? 3600;
147
+ if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn)) {
148
+ throw new Error(`${context} returned invalid expires_in`);
149
+ }
150
+ return { token, expiresAt: Date.now() + Math.max((expiresIn * 1000) - REFRESH_SKEW_MS, 60_000) };
151
+ }
152
+
153
+ async function loadAdcFile(): Promise<AdcFile> {
154
+ const path = getAdcPath();
155
+ if (!path || !existsSync(path)) {
156
+ throw new Error(`Google Application Default Credentials file not found at ${path}`);
157
+ }
158
+ const raw = readFileSync(path, "utf-8");
159
+ try {
160
+ return parseAdcFile(raw);
161
+ } catch (err) {
162
+ const message = err instanceof Error ? err.message : String(err);
163
+ throw new Error(`Failed to parse Google Application Default Credentials file at ${path}: ${message}`);
164
+ }
165
+ }
166
+
167
+ interface CachedToken {
168
+ token: string;
169
+ expiresAt: number;
170
+ }
171
+
172
+ let cachedToken: CachedToken | null = null;
173
+
174
+ export function clearAdcTokenCache(): void {
175
+ cachedToken = null;
176
+ }
177
+
178
+ /**
179
+ * Classifies a failed OAuth token exchange. Only explicit credential/authorization
180
+ * rejections (400 invalid_client/invalid_grant/invalid_scope, 401, 403 denied)
181
+ * mean the configured credentials themselves were refused — a hard credential
182
+ * problem callers should surface rather than silently fall back. Everything else
183
+ * (429 rate limit, 404, 5xx, network failures, timeouts) is transient, so it is
184
+ * left as a plain Error and existing per-provider fallbacks keep working.
185
+ */
186
+ const OAUTH_CREDENTIAL_REJECTION_STATUSES = new Set([400, 401, 403]);
187
+
188
+ function throwOnTokenExchangeFailure(res: Response, detail: string, status: number): never {
189
+ if (OAUTH_CREDENTIAL_REJECTION_STATUSES.has(status)) {
190
+ throw new CredentialResolutionError("Gemini ADC", "oauth-credential-rejected");
191
+ }
192
+ throw new Error(`Gemini ADC token exchange failed (${status}): ${detail.slice(0, 300)}`);
193
+ }
194
+
195
+ async function exchangeRefreshToken(cfg: AdcFile, signal?: AbortSignal): Promise<CachedToken> {
196
+ const body = new URLSearchParams({
197
+ client_id: cfg.client_id ?? "",
198
+ client_secret: cfg.client_secret ?? "",
199
+ refresh_token: cfg.refresh_token ?? "",
200
+ grant_type: "refresh_token",
201
+ });
202
+ const res = await fetch(TOKEN_ENDPOINT, {
203
+ method: "POST",
204
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
205
+ body,
206
+ signal,
207
+ });
208
+ const text = await res.text();
209
+ if (!res.ok) {
210
+ throwOnTokenExchangeFailure(res, text, res.status);
211
+ }
212
+ return parseTokenExchangeResponse(text, "Gemini ADC token exchange");
213
+ }
214
+
215
+ async function exchangeServiceAccountJwt(cfg: AdcFile, signal?: AbortSignal): Promise<CachedToken> {
216
+ if (!cfg.client_email || !cfg.private_key) {
217
+ throw new Error("Gemini ADC service_account file is missing client_email or private_key");
218
+ }
219
+ const now = Math.floor(Date.now() / 1000);
220
+ const header = { alg: "RS256", typ: "JWT" };
221
+ const claims = {
222
+ iss: cfg.client_email,
223
+ scope: "https://www.googleapis.com/auth/cloud-platform",
224
+ aud: TOKEN_ENDPOINT,
225
+ iat: now,
226
+ exp: now + 3600,
227
+ };
228
+ const encodePart = (obj: object): string =>
229
+ Buffer.from(JSON.stringify(obj)).toString("base64url");
230
+ const assertion = `${encodePart(header)}.${encodePart(claims)}`;
231
+ const signer = createSign("RSA-SHA256");
232
+ signer.update(assertion);
233
+ signer.end();
234
+ const signature = signer.sign(cfg.private_key, "base64url");
235
+ const jwt = `${assertion}.${signature}`;
236
+
237
+ const body = new URLSearchParams({
238
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
239
+ assertion: jwt,
240
+ });
241
+ const res = await fetch(cfg.token_uri || TOKEN_ENDPOINT, {
242
+ method: "POST",
243
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
244
+ body,
245
+ signal,
246
+ });
247
+ const text = await res.text();
248
+ if (!res.ok) {
249
+ throwOnTokenExchangeFailure(res, text, res.status);
250
+ }
251
+ return parseTokenExchangeResponse(text, "Gemini ADC service account token exchange");
252
+ }
253
+
254
+ export async function getAdcAccessToken(signal?: AbortSignal): Promise<string> {
255
+ if (cachedToken && cachedToken.expiresAt > Date.now()) {
256
+ return cachedToken.token;
257
+ }
258
+ const cfg = await loadAdcFile();
259
+ const type = cfg.type ?? "authorized_user";
260
+ if (type === "authorized_user") {
261
+ cachedToken = await exchangeRefreshToken(cfg, signal);
262
+ } else if (type === "service_account") {
263
+ cachedToken = await exchangeServiceAccountJwt(cfg, signal);
264
+ } else {
265
+ throw new Error(`Gemini ADC unsupported credential type "${type}" (expected authorized_user or service_account)`);
266
+ }
267
+ return cachedToken.token;
268
+ }
269
+
270
+ export function isGeminiAdcAvailable(): boolean {
271
+ if (!isAdcAuthSelected()) return false;
272
+ // An explicitly configured Gemini API key takes precedence over ADC, so
273
+ // existing key-based setups and their unit tests keep working unchanged.
274
+ if (hasGeminiApiKeySource()) return false;
275
+ // An explicit base URL (gateway/relay/proxy) also wins over ADC: if the
276
+ // user deliberately routed Gemini somewhere, honor that routing.
277
+ if (hasExplicitApiBase()) return false;
278
+ if (!getAdcProject() || !getAdcLocation()) return false;
279
+ return existsSync(getAdcPath());
280
+ }
281
+
282
+ function hasGeminiApiKeySource(): boolean {
283
+ const configured = loadConfig().geminiApiKey;
284
+ if (typeof configured === "string" && configured.trim()) return true;
285
+ const fromEnv = process.env.GEMINI_API_KEY?.trim();
286
+ return !!fromEnv;
287
+ }
288
+
289
+ function hasExplicitApiBase(): boolean {
290
+ const fromEnv = process.env.GOOGLE_GEMINI_BASE_URL?.trim();
291
+ if (fromEnv) return true;
292
+ const configured = loadConfig().geminiBaseUrl;
293
+ return typeof configured === "string" && configured.trim().length > 0;
294
+ }
295
+
296
+ export function redactAdcToken(text: string): string {
297
+ return cachedToken ? redactCredential(text, cachedToken.token) : text;
298
+ }