@ashx-j/lunr 0.2.26 → 0.2.28

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 (697) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/LICENSE +22 -0
  3. package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
  4. package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
  5. package/dist/builtin-extensions/ashxj-thinking.js +1 -1
  6. package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
  7. package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
  8. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  9. package/dist/builtin-extensions/ashxj-tui.js +6 -10
  10. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  11. package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
  12. package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
  13. package/dist/builtin-extensions/lunr-cron.js +94 -108
  14. package/dist/builtin-extensions/lunr-cron.js.map +1 -1
  15. package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
  16. package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
  17. package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
  18. package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.d.ts.map +1 -1
  19. package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js +5 -2
  20. package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js.map +1 -1
  21. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
  22. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
  23. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
  24. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
  25. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
  26. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
  27. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
  28. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
  29. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
  30. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  31. package/dist/builtin-extensions/pi-intercom/index.js +2 -2
  32. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  33. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
  34. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
  35. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
  36. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
  37. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
  38. package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
  39. package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
  40. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
  41. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
  42. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
  43. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
  44. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
  45. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
  46. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
  47. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
  48. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
  49. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
  50. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
  51. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
  52. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
  53. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
  54. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
  55. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
  56. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
  57. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
  58. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
  59. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
  60. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
  61. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
  62. package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
  63. package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
  64. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
  65. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
  67. package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
  68. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
  69. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
  70. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
  71. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
  73. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
  74. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  75. package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
  76. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  77. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
  78. package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
  79. package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
  80. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
  81. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
  82. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
  83. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
  84. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
  85. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
  86. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
  87. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
  88. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
  89. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
  90. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
  91. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
  92. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
  93. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
  94. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
  95. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
  96. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
  97. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
  98. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
  99. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
  100. package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
  101. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
  102. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
  103. package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
  104. package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
  105. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
  106. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
  107. package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
  108. package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
  109. package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
  110. package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
  111. package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
  112. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
  113. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
  114. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
  115. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
  116. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
  117. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
  118. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
  119. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  120. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
  121. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  122. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts +4 -1
  123. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
  124. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +2 -0
  125. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
  126. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +6 -1
  127. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  128. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +17 -31
  129. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  130. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
  131. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
  132. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
  133. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +5 -1
  134. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  135. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +78 -42
  136. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  137. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  138. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
  139. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  140. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts +0 -3
  141. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  142. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +134 -28
  143. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  144. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  145. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +26 -5
  146. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  147. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
  148. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  149. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
  150. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  151. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
  152. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  153. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
  154. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  155. package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts +7 -1
  156. package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts.map +1 -1
  157. package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js +70 -38
  158. package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js.map +1 -1
  159. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +9 -0
  160. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  161. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  162. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
  163. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
  164. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
  165. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
  166. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  167. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
  168. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  169. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  170. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +13 -62
  171. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  172. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
  173. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  174. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +18 -9
  175. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  176. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
  177. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
  178. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
  179. package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
  180. package/dist/builtin-extensions/pi-tps.js +28 -17
  181. package/dist/builtin-extensions/pi-tps.js.map +1 -1
  182. package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
  183. package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
  184. package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
  185. package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
  186. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
  187. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
  188. package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
  189. package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
  190. package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
  191. package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
  192. package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
  193. package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
  194. package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
  195. package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
  196. package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
  197. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
  198. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
  199. package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
  200. package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
  201. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  202. package/dist/builtin-extensions/pi-web-access/index.js +48 -73
  203. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  204. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
  205. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
  206. package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
  207. package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
  208. package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
  209. package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
  210. package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
  211. package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
  212. package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
  213. package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
  214. package/dist/builtin-extensions/simple-pi-memory.js +1 -1
  215. package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
  216. package/dist/catalog/models.json +1 -1
  217. package/dist/catalog/providers/google-vertex.json +1 -1
  218. package/dist/catalog/providers/google.json +1 -1
  219. package/dist/catalog/publication.json +3 -3
  220. package/dist/cli/install-cli.d.ts.map +1 -1
  221. package/dist/cli/install-cli.js +38 -30
  222. package/dist/cli/install-cli.js.map +1 -1
  223. package/dist/core/agent-session-runtime.d.ts +13 -0
  224. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  225. package/dist/core/agent-session-runtime.js +75 -11
  226. package/dist/core/agent-session-runtime.js.map +1 -1
  227. package/dist/core/agent-session-services.d.ts +1 -0
  228. package/dist/core/agent-session-services.d.ts.map +1 -1
  229. package/dist/core/agent-session-services.js +1 -0
  230. package/dist/core/agent-session-services.js.map +1 -1
  231. package/dist/core/agent-session.d.ts +46 -21
  232. package/dist/core/agent-session.d.ts.map +1 -1
  233. package/dist/core/agent-session.js +344 -126
  234. package/dist/core/agent-session.js.map +1 -1
  235. package/dist/core/browser/runtime.d.ts +2 -0
  236. package/dist/core/browser/runtime.d.ts.map +1 -1
  237. package/dist/core/browser/runtime.js +80 -45
  238. package/dist/core/browser/runtime.js.map +1 -1
  239. package/dist/core/browser/setup.d.ts.map +1 -1
  240. package/dist/core/browser/setup.js +12 -1
  241. package/dist/core/browser/setup.js.map +1 -1
  242. package/dist/core/cache-stats.d.ts +1 -1
  243. package/dist/core/cache-stats.d.ts.map +1 -1
  244. package/dist/core/cache-stats.js +1 -1
  245. package/dist/core/cache-stats.js.map +1 -1
  246. package/dist/core/compaction/branch-summarization.d.ts +2 -1
  247. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  248. package/dist/core/compaction/branch-summarization.js +1 -0
  249. package/dist/core/compaction/branch-summarization.js.map +1 -1
  250. package/dist/core/compaction/compaction.d.ts +5 -11
  251. package/dist/core/compaction/compaction.d.ts.map +1 -1
  252. package/dist/core/compaction/compaction.js +38 -92
  253. package/dist/core/compaction/compaction.js.map +1 -1
  254. package/dist/core/computer-use/adapter.d.ts.map +1 -1
  255. package/dist/core/computer-use/adapter.js +7 -2
  256. package/dist/core/computer-use/adapter.js.map +1 -1
  257. package/dist/core/computer-use/image.d.ts.map +1 -1
  258. package/dist/core/computer-use/image.js +17 -5
  259. package/dist/core/computer-use/image.js.map +1 -1
  260. package/dist/core/computer-use/schemas.d.ts.map +1 -1
  261. package/dist/core/computer-use/schemas.js +23 -6
  262. package/dist/core/computer-use/schemas.js.map +1 -1
  263. package/dist/core/computer-use/workflow.d.ts.map +1 -1
  264. package/dist/core/computer-use/workflow.js +459 -119
  265. package/dist/core/computer-use/workflow.js.map +1 -1
  266. package/dist/core/context-breakdown.d.ts +2 -2
  267. package/dist/core/context-breakdown.d.ts.map +1 -1
  268. package/dist/core/context-breakdown.js +22 -20
  269. package/dist/core/context-breakdown.js.map +1 -1
  270. package/dist/core/cron/jobs.d.ts +26 -8
  271. package/dist/core/cron/jobs.d.ts.map +1 -1
  272. package/dist/core/cron/jobs.js +357 -232
  273. package/dist/core/cron/jobs.js.map +1 -1
  274. package/dist/core/cron/origin-context.d.ts +1 -0
  275. package/dist/core/cron/origin-context.d.ts.map +1 -1
  276. package/dist/core/cron/origin-context.js.map +1 -1
  277. package/dist/core/cron/scheduler.d.ts +22 -17
  278. package/dist/core/cron/scheduler.d.ts.map +1 -1
  279. package/dist/core/cron/scheduler.js +153 -73
  280. package/dist/core/cron/scheduler.js.map +1 -1
  281. package/dist/core/extensions/index.d.ts +1 -1
  282. package/dist/core/extensions/index.d.ts.map +1 -1
  283. package/dist/core/extensions/index.js.map +1 -1
  284. package/dist/core/extensions/runner.d.ts +1 -0
  285. package/dist/core/extensions/runner.d.ts.map +1 -1
  286. package/dist/core/extensions/runner.js +8 -0
  287. package/dist/core/extensions/runner.js.map +1 -1
  288. package/dist/core/extensions/types.d.ts +11 -1
  289. package/dist/core/extensions/types.d.ts.map +1 -1
  290. package/dist/core/extensions/types.js.map +1 -1
  291. package/dist/core/footer-data-provider.d.ts +4 -0
  292. package/dist/core/footer-data-provider.d.ts.map +1 -1
  293. package/dist/core/footer-data-provider.js +22 -1
  294. package/dist/core/footer-data-provider.js.map +1 -1
  295. package/dist/core/large-subagent-launch.d.ts +1 -1
  296. package/dist/core/large-subagent-launch.d.ts.map +1 -1
  297. package/dist/core/large-subagent-launch.js +7 -7
  298. package/dist/core/large-subagent-launch.js.map +1 -1
  299. package/dist/core/model-registry.d.ts +2 -0
  300. package/dist/core/model-registry.d.ts.map +1 -1
  301. package/dist/core/model-registry.js +5 -0
  302. package/dist/core/model-registry.js.map +1 -1
  303. package/dist/core/model-runtime.d.ts +4 -1
  304. package/dist/core/model-runtime.d.ts.map +1 -1
  305. package/dist/core/model-runtime.js +40 -4
  306. package/dist/core/model-runtime.js.map +1 -1
  307. package/dist/core/package-manager.d.ts.map +1 -1
  308. package/dist/core/package-manager.js.map +1 -1
  309. package/dist/core/permissions.d.ts +3 -3
  310. package/dist/core/permissions.d.ts.map +1 -1
  311. package/dist/core/permissions.js +11 -35
  312. package/dist/core/permissions.js.map +1 -1
  313. package/dist/core/plan-mode.d.ts +5 -13
  314. package/dist/core/plan-mode.d.ts.map +1 -1
  315. package/dist/core/plan-mode.js +126 -116
  316. package/dist/core/plan-mode.js.map +1 -1
  317. package/dist/core/project-trust.d.ts.map +1 -1
  318. package/dist/core/project-trust.js +1 -1
  319. package/dist/core/project-trust.js.map +1 -1
  320. package/dist/core/prompt-resource-bridge.d.ts +12 -0
  321. package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
  322. package/dist/core/prompt-resource-bridge.js +9 -0
  323. package/dist/core/prompt-resource-bridge.js.map +1 -0
  324. package/dist/core/protected-targets.d.ts +6 -0
  325. package/dist/core/protected-targets.d.ts.map +1 -0
  326. package/dist/core/protected-targets.js +45 -0
  327. package/dist/core/protected-targets.js.map +1 -0
  328. package/dist/core/resource-loader.d.ts.map +1 -1
  329. package/dist/core/resource-loader.js +7 -0
  330. package/dist/core/resource-loader.js.map +1 -1
  331. package/dist/core/rollback.d.ts +19 -3
  332. package/dist/core/rollback.d.ts.map +1 -1
  333. package/dist/core/rollback.js +172 -56
  334. package/dist/core/rollback.js.map +1 -1
  335. package/dist/core/runtime-credentials.d.ts +3 -0
  336. package/dist/core/runtime-credentials.d.ts.map +1 -1
  337. package/dist/core/runtime-credentials.js +14 -1
  338. package/dist/core/runtime-credentials.js.map +1 -1
  339. package/dist/core/sdk.d.ts +3 -0
  340. package/dist/core/sdk.d.ts.map +1 -1
  341. package/dist/core/sdk.js +1 -0
  342. package/dist/core/sdk.js.map +1 -1
  343. package/dist/core/session-manager.d.ts +11 -1
  344. package/dist/core/session-manager.d.ts.map +1 -1
  345. package/dist/core/session-manager.js +111 -33
  346. package/dist/core/session-manager.js.map +1 -1
  347. package/dist/core/session-ownership.d.ts.map +1 -1
  348. package/dist/core/session-ownership.js +8 -3
  349. package/dist/core/session-ownership.js.map +1 -1
  350. package/dist/core/session-retention.d.ts.map +1 -1
  351. package/dist/core/session-retention.js +18 -7
  352. package/dist/core/session-retention.js.map +1 -1
  353. package/dist/core/session-startup-settings.d.ts +10 -0
  354. package/dist/core/session-startup-settings.d.ts.map +1 -0
  355. package/dist/core/session-startup-settings.js +70 -0
  356. package/dist/core/session-startup-settings.js.map +1 -0
  357. package/dist/core/subscriptions.d.ts +16 -7
  358. package/dist/core/subscriptions.d.ts.map +1 -1
  359. package/dist/core/subscriptions.js +96 -55
  360. package/dist/core/subscriptions.js.map +1 -1
  361. package/dist/core/trust-manager.d.ts +2 -1
  362. package/dist/core/trust-manager.d.ts.map +1 -1
  363. package/dist/core/trust-manager.js +6 -1
  364. package/dist/core/trust-manager.js.map +1 -1
  365. package/dist/core/update-check.d.ts.map +1 -1
  366. package/dist/core/update-check.js.map +1 -1
  367. package/dist/core/usage-accounting.d.ts +47 -0
  368. package/dist/core/usage-accounting.d.ts.map +1 -0
  369. package/dist/core/usage-accounting.js +205 -0
  370. package/dist/core/usage-accounting.js.map +1 -0
  371. package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
  372. package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
  373. package/dist/core/usage-adapters/kimi-coding.js +2 -2
  374. package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
  375. package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
  376. package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
  377. package/dist/core/usage-adapters/openai-codex.js +3 -3
  378. package/dist/core/usage-adapters/openai-codex.js.map +1 -1
  379. package/dist/core/usage-adapters/xai.d.ts +2 -2
  380. package/dist/core/usage-adapters/xai.d.ts.map +1 -1
  381. package/dist/core/usage-adapters/xai.js +3 -3
  382. package/dist/core/usage-adapters/xai.js.map +1 -1
  383. package/dist/core/usage-adapters/zai.d.ts +2 -2
  384. package/dist/core/usage-adapters/zai.d.ts.map +1 -1
  385. package/dist/core/usage-adapters/zai.js +2 -2
  386. package/dist/core/usage-adapters/zai.js.map +1 -1
  387. package/dist/core/usage-history.d.ts +1 -18
  388. package/dist/core/usage-history.d.ts.map +1 -1
  389. package/dist/core/usage-history.js +46 -114
  390. package/dist/core/usage-history.js.map +1 -1
  391. package/dist/core/usage-service.d.ts +9 -2
  392. package/dist/core/usage-service.d.ts.map +1 -1
  393. package/dist/core/usage-service.js +95 -50
  394. package/dist/core/usage-service.js.map +1 -1
  395. package/dist/gateway/agent-bridge.d.ts +9 -1
  396. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  397. package/dist/gateway/agent-bridge.js +91 -37
  398. package/dist/gateway/agent-bridge.js.map +1 -1
  399. package/dist/gateway/buttons.d.ts +8 -1
  400. package/dist/gateway/buttons.d.ts.map +1 -1
  401. package/dist/gateway/buttons.js +76 -18
  402. package/dist/gateway/buttons.js.map +1 -1
  403. package/dist/gateway/commands.d.ts +1 -0
  404. package/dist/gateway/commands.d.ts.map +1 -1
  405. package/dist/gateway/commands.js +10 -4
  406. package/dist/gateway/commands.js.map +1 -1
  407. package/dist/gateway/cron.d.ts +12 -11
  408. package/dist/gateway/cron.d.ts.map +1 -1
  409. package/dist/gateway/cron.js +65 -56
  410. package/dist/gateway/cron.js.map +1 -1
  411. package/dist/gateway/index.d.ts.map +1 -1
  412. package/dist/gateway/index.js +2 -1
  413. package/dist/gateway/index.js.map +1 -1
  414. package/dist/gateway/mobile-commands.d.ts +1 -0
  415. package/dist/gateway/mobile-commands.d.ts.map +1 -1
  416. package/dist/gateway/mobile-commands.js +69 -15
  417. package/dist/gateway/mobile-commands.js.map +1 -1
  418. package/dist/gateway/presenter.d.ts +12 -2
  419. package/dist/gateway/presenter.d.ts.map +1 -1
  420. package/dist/gateway/presenter.js +73 -38
  421. package/dist/gateway/presenter.js.map +1 -1
  422. package/dist/gateway/router.d.ts +2 -2
  423. package/dist/gateway/router.d.ts.map +1 -1
  424. package/dist/gateway/router.js +17 -2
  425. package/dist/gateway/router.js.map +1 -1
  426. package/dist/gateway/service.d.ts +2 -0
  427. package/dist/gateway/service.d.ts.map +1 -1
  428. package/dist/gateway/service.js +153 -0
  429. package/dist/gateway/service.js.map +1 -1
  430. package/dist/main.d.ts.map +1 -1
  431. package/dist/main.js +86 -33
  432. package/dist/main.js.map +1 -1
  433. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  434. package/dist/modes/interactive/components/assistant-message.js +6 -4
  435. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  436. package/dist/modes/interactive/components/context-view.d.ts.map +1 -1
  437. package/dist/modes/interactive/components/context-view.js +2 -2
  438. package/dist/modes/interactive/components/context-view.js.map +1 -1
  439. package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
  440. package/dist/modes/interactive/components/copyable-text.js +3 -2
  441. package/dist/modes/interactive/components/copyable-text.js.map +1 -1
  442. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  443. package/dist/modes/interactive/components/extension-editor.js +1 -1
  444. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  445. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  446. package/dist/modes/interactive/components/footer.js +8 -12
  447. package/dist/modes/interactive/components/footer.js.map +1 -1
  448. package/dist/modes/interactive/components/usage-view.d.ts +5 -0
  449. package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/usage-view.js +21 -6
  451. package/dist/modes/interactive/components/usage-view.js.map +1 -1
  452. package/dist/modes/interactive/interactive-mode.d.ts +3 -7
  453. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  454. package/dist/modes/interactive/interactive-mode.js +151 -131
  455. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  456. package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
  457. package/dist/modes/interactive/smooth-streaming.js.map +1 -1
  458. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  459. package/dist/modes/rpc/rpc-mode.js +2 -1
  460. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  461. package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
  462. package/dist/node-runtime/agent-session-services-RTUGP3XR.js +54 -0
  463. package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
  464. package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-N4EUD6SB.js} +8 -7
  465. package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
  466. package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
  467. package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
  468. package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
  469. package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
  470. package/dist/node-runtime/chunk-4QRUZ64T.js +1203 -0
  471. package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
  472. package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
  473. package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
  474. package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
  475. package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
  476. package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
  477. package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-BMRXLQWQ.js} +464 -160
  478. package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
  479. package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-DFMPRAQQ.js} +162 -82
  480. package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
  481. package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-E5DQGNJI.js} +455 -278
  482. package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
  483. package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
  484. package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
  485. package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
  486. package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
  487. package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
  488. package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
  489. package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
  490. package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
  491. package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
  492. package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
  493. package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
  494. package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
  495. package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
  496. package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-ICKGAKWX.js} +23 -59
  497. package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
  498. package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
  499. package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
  500. package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JEK35V2M.js} +45 -31
  501. package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
  502. package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
  503. package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
  504. package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
  505. package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
  506. package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-PWHTSQC4.js} +83 -21
  507. package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
  508. package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-QT3F6UEV.js} +233 -169
  509. package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
  510. package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
  511. package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
  512. package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
  513. package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-RY3FJ6WI.js} +29 -27
  514. package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
  515. package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-STMX7W5I.js} +3 -3
  516. package/dist/node-runtime/chunk-SWWHZ2RI.js +158 -0
  517. package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
  518. package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-U3YVZ6UC.js} +18 -14
  519. package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
  520. package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
  521. package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
  522. package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
  523. package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
  524. package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
  525. package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
  526. package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
  527. package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
  528. package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
  529. package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
  530. package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
  531. package/dist/node-runtime/cli-runtime.js +4 -4
  532. package/dist/node-runtime/{command-FSBU2EVZ.js → command-YRT7I2BW.js} +5 -5
  533. package/dist/node-runtime/{commands-NAAL7KNT.js → commands-2ETOWWRA.js} +16 -16
  534. package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
  535. package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
  536. package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
  537. package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
  538. package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
  539. package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-DBQRRMAY.js} +4 -4
  540. package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
  541. package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
  542. package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
  543. package/dist/node-runtime/{gateway-DQM333YV.js → gateway-V6K2CDWN.js} +51 -42
  544. package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
  545. package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
  546. package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
  547. package/dist/node-runtime/{goal-NJEE5CTU.js → goal-J7TYMOBK.js} +14 -8
  548. package/dist/node-runtime/index.js +51 -48
  549. package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
  550. package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
  551. package/dist/node-runtime/interactive-mode-ASZ7APYP.js +75 -0
  552. package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
  553. package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
  554. package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
  555. package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
  556. package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-WD3YH4CR.js} +93 -80
  557. package/dist/node-runtime/main.js +43 -41
  558. package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-ICUXFW5V.js} +12 -12
  559. package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
  560. package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
  561. package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
  562. package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
  563. package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
  564. package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
  565. package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
  566. package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
  567. package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
  568. package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-QPU3OXE4.js} +114 -167
  569. package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
  570. package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
  571. package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
  572. package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
  573. package/dist/node-runtime/rpc-entry.js +43 -41
  574. package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
  575. package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
  576. package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
  577. package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
  578. package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
  579. package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LYH53ESV.js} +4 -4
  580. package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-ZJ3N5Q74.js} +3 -3
  581. package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
  582. package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
  583. package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
  584. package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
  585. package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
  586. package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-UI6SHG3X.js} +412 -288
  587. package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
  588. package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
  589. package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
  590. package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
  591. package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
  592. package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
  593. package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
  594. package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
  595. package/dist/utils/clipboard-image.d.ts.map +1 -1
  596. package/dist/utils/clipboard-image.js.map +1 -1
  597. package/dist/utils/exif-orientation.d.ts.map +1 -1
  598. package/dist/utils/exif-orientation.js +25 -10
  599. package/dist/utils/exif-orientation.js.map +1 -1
  600. package/dist/utils/image-resize-core.d.ts.map +1 -1
  601. package/dist/utils/image-resize-core.js +2 -0
  602. package/dist/utils/image-resize-core.js.map +1 -1
  603. package/dist/utils/paths.d.ts +4 -0
  604. package/dist/utils/paths.d.ts.map +1 -1
  605. package/dist/utils/paths.js +33 -2
  606. package/dist/utils/paths.js.map +1 -1
  607. package/dist/utils/sanitize-terminal-text.d.ts +7 -0
  608. package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
  609. package/dist/utils/sanitize-terminal-text.js +67 -0
  610. package/dist/utils/sanitize-terminal-text.js.map +1 -0
  611. package/docs/browser.md +1 -1
  612. package/docs/custom-provider.md +3 -3
  613. package/docs/extensions.md +8 -4
  614. package/docs/features.md +25 -7
  615. package/docs/keybindings.md +1 -1
  616. package/docs/models.md +5 -5
  617. package/docs/packages.md +1 -1
  618. package/docs/providers.md +2 -2
  619. package/docs/rpc.md +7 -7
  620. package/docs/security.md +10 -5
  621. package/docs/sessions.md +2 -2
  622. package/docs/skills.md +1 -1
  623. package/docs/terminal-setup.md +7 -7
  624. package/docs/themes.md +1 -1
  625. package/docs/usage.md +1 -1
  626. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  627. package/examples/extensions/bash-spawn-hook.ts +1 -1
  628. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  629. package/examples/extensions/claude-rules.ts +1 -1
  630. package/examples/extensions/commands.ts +1 -1
  631. package/examples/extensions/custom-compaction.ts +1 -1
  632. package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
  633. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  634. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  635. package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
  636. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  637. package/examples/extensions/doom-overlay/index.ts +1 -1
  638. package/examples/extensions/git-merge-and-resolve.ts +2 -2
  639. package/examples/extensions/gondolin/index.ts +1 -1
  640. package/examples/extensions/gondolin/package-lock.json +2 -2
  641. package/examples/extensions/gondolin/package.json +1 -1
  642. package/examples/extensions/hidden-thinking-label.ts +1 -1
  643. package/examples/extensions/inline-bash.ts +2 -2
  644. package/examples/extensions/input-transform-streaming.ts +2 -2
  645. package/examples/extensions/input-transform.ts +2 -2
  646. package/examples/extensions/interactive-shell.ts +1 -1
  647. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  648. package/examples/extensions/mac-system-theme.ts +1 -1
  649. package/examples/extensions/minimal-mode.ts +1 -1
  650. package/examples/extensions/modal-editor.ts +1 -1
  651. package/examples/extensions/model-status.ts +1 -1
  652. package/examples/extensions/notify.ts +3 -3
  653. package/examples/extensions/overlay-qa-tests.ts +1 -1
  654. package/examples/extensions/overlay-test.ts +1 -1
  655. package/examples/extensions/pirate.ts +1 -1
  656. package/examples/extensions/preset.ts +3 -3
  657. package/examples/extensions/project-trust.ts +4 -4
  658. package/examples/extensions/prompt-customizer.ts +1 -1
  659. package/examples/extensions/rainbow-editor.ts +1 -1
  660. package/examples/extensions/rpc-demo.ts +1 -1
  661. package/examples/extensions/sandbox/index.ts +7 -7
  662. package/examples/extensions/sandbox/package-lock.json +2 -2
  663. package/examples/extensions/sandbox/package.json +1 -1
  664. package/examples/extensions/shutdown-command.ts +5 -5
  665. package/examples/extensions/ssh.ts +2 -2
  666. package/examples/extensions/subagent/README.md +3 -3
  667. package/examples/extensions/subagent/index.ts +2 -2
  668. package/examples/extensions/titlebar-spinner.ts +1 -1
  669. package/examples/extensions/tool-override.ts +1 -1
  670. package/examples/extensions/tools.ts +1 -1
  671. package/examples/extensions/with-deps/package-lock.json +2 -2
  672. package/examples/extensions/with-deps/package.json +1 -1
  673. package/examples/extensions/working-indicator.ts +1 -1
  674. package/examples/extensions/working-message-test.ts +1 -1
  675. package/examples/sdk/01-minimal.ts +1 -1
  676. package/examples/sdk/03-custom-prompt.ts +1 -1
  677. package/examples/sdk/04-skills.ts +1 -1
  678. package/examples/sdk/06-extensions.ts +2 -2
  679. package/examples/sdk/08-prompt-templates.ts +1 -1
  680. package/install-lock/package-lock.json +24 -24
  681. package/install-lock/package.json +2 -2
  682. package/npm-shrinkwrap.json +60 -24
  683. package/package.json +9 -9
  684. package/dist/modes/interactive/components/exit-card.d.ts +0 -22
  685. package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
  686. package/dist/modes/interactive/components/exit-card.js +0 -69
  687. package/dist/modes/interactive/components/exit-card.js.map +0 -1
  688. package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
  689. package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
  690. package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
  691. package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
  692. package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
  693. package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
  694. package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
  695. package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
  696. package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
  697. package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
@@ -1 +1 @@
1
- {"version":3,"file":"session-ownership.js","sourceRoot":"","sources":["../../src/core/session-ownership.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\trealpathSync,\n\trenameSync,\n\trmdirSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nexport interface SessionOwner {\n\tversion: 1;\n\tfile: string;\n\tgeneration: string;\n\tpid: number;\n\thost: string;\n\tinstance: string;\n\tsessionId?: string;\n}\nconst instance = randomUUID();\n\nexport class SessionOwnershipError extends Error {\n\treadonly file: string;\n\tconstructor(\n\t\tfile: string,\n\t\tmessage = \"Session is owned by another runtime. Request a transfer or close its owner first.\",\n\t) {\n\t\tsuper(`${message} ${file}`);\n\t\tthis.name = \"SessionOwnershipError\";\n\t\tthis.file = file;\n\t}\n}\n\nexport function canonicalSessionPath(file: string): string {\n\tconst absolute = resolve(file);\n\tlet canonical: string;\n\tif (existsSync(absolute)) canonical = realpathSync.native(absolute);\n\telse {\n\t\tconst parent = dirname(absolute);\n\t\tcanonical = parent === absolute ? absolute : join(canonicalSessionPath(parent), basename(absolute));\n\t}\n\treturn process.platform === \"win32\" ? canonical.toLowerCase() : canonical;\n}\n\nexport function sessionOwnershipDirectory(file: string): string {\n\treturn `${canonicalSessionPath(file)}.owner`;\n}\n\nexport function readSessionOwner(file: string): SessionOwner | undefined {\n\tconst directory = sessionOwnershipDirectory(file);\n\tif (!existsSync(directory)) return undefined;\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(join(directory, \"owner.json\"), \"utf8\"));\n\t\tif (\n\t\t\tvalue.version === 1 &&\n\t\t\tvalue.file === canonicalSessionPath(file) &&\n\t\t\ttypeof value.generation === \"string\" &&\n\t\t\tNumber.isSafeInteger(value.pid) &&\n\t\t\tvalue.pid > 0 &&\n\t\t\ttypeof value.host === \"string\" &&\n\t\t\ttypeof value.instance === \"string\"\n\t\t)\n\t\t\treturn value;\n\t} catch {}\n\tthrow new SessionOwnershipError(file, \"Session owner is incomplete or unknown; automatic takeover is unsafe.\");\n}\n\nexport function isSessionOwnerDead(owner: SessionOwner): boolean {\n\tif (owner.host !== hostname()) return false;\n\ttry {\n\t\tprocess.kill(owner.pid, 0);\n\t\treturn false;\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"ESRCH\";\n\t}\n}\n\nexport function writeSessionJson(file: string, value: unknown): void {\n\tconst temp = `${file}.${randomUUID()}.tmp`;\n\ttry {\n\t\twriteFileSync(temp, JSON.stringify(value), { flag: \"wx\", mode: 0o600 });\n\t\trenameSync(temp, file);\n\t} finally {\n\t\tif (existsSync(temp)) unlinkSync(temp);\n\t}\n}\n\nexport class SessionOwnership {\n\treadonly owner: SessionOwner;\n\tprivate released = false;\n\tprivate readonly directory: string;\n\tconstructor(file: string) {\n\t\tfile = canonicalSessionPath(file);\n\t\tif (existsSync(file) && statSync(file).nlink > 1)\n\t\t\tthrow new SessionOwnershipError(file, \"Hard-linked sessions cannot be owned safely. Use a separate copy.\");\n\t\tthis.directory = sessionOwnershipDirectory(file);\n\t\tthis.owner = { version: 1, file, generation: randomUUID(), pid: process.pid, host: hostname(), instance };\n\t\tmkdirSync(dirname(file), { recursive: true });\n\t\ttry {\n\t\t\tmkdirSync(this.directory);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\t\tconst old = readSessionOwner(file);\n\t\t\tif (!old || !isSessionOwnerDead(old)) throw new SessionOwnershipError(file);\n\t\t\t// Serialize dead-owner cleanup. Never infer death from elapsed time or a missing heartbeat.\n\t\t\tconst recovery = `${this.directory}.recovery`;\n\t\t\ttry {\n\t\t\t\twriteFileSync(recovery, JSON.stringify(this.owner), { flag: \"wx\", mode: 0o600 });\n\t\t\t} catch {\n\t\t\t\tthrow new SessionOwnershipError(\n\t\t\t\t\tfile,\n\t\t\t\t\t\"Session recovery is already in progress or requires manual inspection.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst current = readSessionOwner(file);\n\t\t\t\tif (current?.generation !== old.generation || !isSessionOwnerDead(current))\n\t\t\t\t\tthrow new SessionOwnershipError(file);\n\t\t\t\tunlinkSync(join(this.directory, \"owner.json\"));\n\t\t\t\trmdirSync(this.directory);\n\t\t\t\ttry {\n\t\t\t\t\tmkdirSync(this.directory);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new SessionOwnershipError(file);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tunlinkSync(recovery);\n\t\t\t}\n\t\t}\n\t\twriteSessionJson(join(this.directory, \"owner.json\"), this.owner);\n\t}\n\tbindSession(sessionId: string): void {\n\t\tthis.assert();\n\t\tthis.owner.sessionId = sessionId;\n\t\twriteSessionJson(join(this.directory, \"owner.json\"), this.owner);\n\t}\n\n\tassert(): void {\n\t\tif (existsSync(this.owner.file) && statSync(this.owner.file).nlink > 1)\n\t\t\tthrow new SessionOwnershipError(this.owner.file, \"Session acquired a hard-link alias; writes are blocked.\");\n\t\tif (this.released || readSessionOwner(this.owner.file)?.generation !== this.owner.generation) {\n\t\t\tthrow new SessionOwnershipError(\n\t\t\t\tthis.owner.file,\n\t\t\t\t\"This runtime no longer owns the session. Reclaim or reopen it before continuing.\",\n\t\t\t);\n\t\t}\n\t}\n\trelease(): void {\n\t\tif (this.released) return;\n\t\tthis.assert();\n\t\tthis.released = true;\n\t\tconst releasedDirectory = `${this.directory}.released-${this.owner.generation}`;\n\t\trenameSync(this.directory, releasedDirectory);\n\t\tunlinkSync(join(releasedDirectory, \"owner.json\"));\n\t\trmdirSync(releasedDirectory);\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-ownership.js","sourceRoot":"","sources":["../../src/core/session-ownership.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\trealpathSync,\n\trenameSync,\n\trmdirSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nexport interface SessionOwner {\n\tversion: 1;\n\tfile: string;\n\tgeneration: string;\n\tpid: number;\n\thost: string;\n\tinstance: string;\n\tsessionId?: string;\n}\nconst instance = randomUUID();\n\nexport class SessionOwnershipError extends Error {\n\treadonly file: string;\n\tconstructor(\n\t\tfile: string,\n\t\tmessage = \"Session is owned by another runtime. Request a transfer or close its owner first.\",\n\t) {\n\t\tsuper(`${message} ${file}`);\n\t\tthis.name = \"SessionOwnershipError\";\n\t\tthis.file = file;\n\t}\n}\n\nexport function canonicalSessionPath(file: string): string {\n\tconst absolute = resolve(file);\n\tlet canonical: string;\n\tif (existsSync(absolute)) canonical = realpathSync.native(absolute);\n\telse {\n\t\tconst parent = dirname(absolute);\n\t\tcanonical = parent === absolute ? absolute : join(canonicalSessionPath(parent), basename(absolute));\n\t}\n\treturn process.platform === \"win32\" ? canonical.toLowerCase() : canonical;\n}\n\nexport function sessionOwnershipDirectory(file: string): string {\n\treturn `${canonicalSessionPath(file)}.owner`;\n}\n\nexport function readSessionOwner(file: string): SessionOwner | undefined {\n\tconst directory = sessionOwnershipDirectory(file);\n\tif (!existsSync(directory)) return undefined;\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(join(directory, \"owner.json\"), \"utf8\"));\n\t\tif (\n\t\t\tvalue.version === 1 &&\n\t\t\tvalue.file === canonicalSessionPath(file) &&\n\t\t\ttypeof value.generation === \"string\" &&\n\t\t\tNumber.isSafeInteger(value.pid) &&\n\t\t\tvalue.pid > 0 &&\n\t\t\ttypeof value.host === \"string\" &&\n\t\t\ttypeof value.instance === \"string\"\n\t\t)\n\t\t\treturn value;\n\t} catch {}\n\tthrow new SessionOwnershipError(file, \"Session owner is incomplete or unknown; automatic takeover is unsafe.\");\n}\n\nexport function isSessionOwnerDead(owner: SessionOwner): boolean {\n\tif (owner.host !== hostname()) return false;\n\ttry {\n\t\tprocess.kill(owner.pid, 0);\n\t\treturn false;\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"ESRCH\";\n\t}\n}\n\nexport function writeSessionJson(file: string, value: unknown): void {\n\tconst temp = `${file}.${randomUUID()}.tmp`;\n\ttry {\n\t\twriteFileSync(temp, JSON.stringify(value), { flag: \"wx\", mode: 0o600 });\n\t\trenameSync(temp, file);\n\t} finally {\n\t\tif (existsSync(temp)) unlinkSync(temp);\n\t}\n}\n\nexport class SessionOwnership {\n\treadonly owner: SessionOwner;\n\tprivate released = false;\n\tprivate readonly directory: string;\n\tconstructor(file: string) {\n\t\tfile = canonicalSessionPath(file);\n\t\tif (existsSync(file) && statSync(file).nlink > 1)\n\t\t\tthrow new SessionOwnershipError(file, \"Hard-linked sessions cannot be owned safely. Use a separate copy.\");\n\t\tthis.directory = sessionOwnershipDirectory(file);\n\t\tthis.owner = { version: 1, file, generation: randomUUID(), pid: process.pid, host: hostname(), instance };\n\t\tmkdirSync(dirname(file), { recursive: true });\n\t\ttry {\n\t\t\tmkdirSync(this.directory);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\t\tconst old = readSessionOwner(file);\n\t\t\tif (!old || !isSessionOwnerDead(old)) throw new SessionOwnershipError(file);\n\t\t\t// Serialize dead-owner cleanup. Never infer death from elapsed time or a missing heartbeat.\n\t\t\tconst recovery = `${this.directory}.recovery`;\n\t\t\ttry {\n\t\t\t\twriteFileSync(recovery, JSON.stringify(this.owner), { flag: \"wx\", mode: 0o600 });\n\t\t\t} catch {\n\t\t\t\tthrow new SessionOwnershipError(\n\t\t\t\t\tfile,\n\t\t\t\t\t\"Session recovery is already in progress or requires manual inspection.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst current = readSessionOwner(file);\n\t\t\t\tif (current?.generation !== old.generation || !isSessionOwnerDead(current))\n\t\t\t\t\tthrow new SessionOwnershipError(file);\n\t\t\t\tunlinkSync(join(this.directory, \"owner.json\"));\n\t\t\t\trmdirSync(this.directory);\n\t\t\t\ttry {\n\t\t\t\t\tmkdirSync(this.directory);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new SessionOwnershipError(file);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tunlinkSync(recovery);\n\t\t\t}\n\t\t}\n\t\twriteSessionJson(join(this.directory, \"owner.json\"), this.owner);\n\t}\n\tbindSession(sessionId: string): void {\n\t\tthis.assert();\n\t\tthis.owner.sessionId = sessionId;\n\t\twriteSessionJson(join(this.directory, \"owner.json\"), this.owner);\n\t}\n\n\tassert(): void {\n\t\tif (existsSync(this.owner.file) && statSync(this.owner.file).nlink > 1)\n\t\t\tthrow new SessionOwnershipError(this.owner.file, \"Session acquired a hard-link alias; writes are blocked.\");\n\t\tif (this.released || readSessionOwner(this.owner.file)?.generation !== this.owner.generation) {\n\t\t\tthrow new SessionOwnershipError(\n\t\t\t\tthis.owner.file,\n\t\t\t\t\"This runtime no longer owns the session. Reclaim or reopen it before continuing.\",\n\t\t\t);\n\t\t}\n\t}\n\trelease(): void {\n\t\tif (this.released) return;\n\t\tthis.assert();\n\t\tconst releasedDirectory = `${this.directory}.released-${this.owner.generation}`;\n\t\trenameSync(this.directory, releasedDirectory);\n\t\tthis.released = true;\n\t\ttry {\n\t\t\tunlinkSync(join(releasedDirectory, \"owner.json\"));\n\t\t\trmdirSync(releasedDirectory);\n\t\t} catch {\n\t\t\t// Ownership is already relinquished. Cleanup must not reject the handoff.\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-retention.d.ts","sourceRoot":"","sources":["../../src/core/session-retention.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,uBAAuB;IACvC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAsB;IACtC,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;CAClB;AA8CD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,uBAA4B,GACnC,OAAO,CAAC,sBAAsB,CAAC,CAwBjC","sourcesContent":["// lunr: session retention pruning (Phase 5 of lunr-ux plan).\n// Scans the sessions root (~/.lunr/agent/sessions/, layout: <root>/--<cwd>--/*.jsonl,\n// or a flat custom --session-dir) and deletes .jsonl session files older than the\n// configured retention window. The active session file is always excluded.\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir, stat, unlink } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { SessionOwnership } from \"./session-ownership.ts\";\n\nexport interface PruneOldSessionsOptions {\n\t/** Absolute path of the active session file; never deleted. */\n\texcludeFile?: string;\n\t/** Reference timestamp in ms (defaults to Date.now()); injectable for tests. */\n\tnow?: number;\n}\n\nexport interface PruneOldSessionsResult {\n\t/** Paths of session files that were deleted. */\n\tdeleted: string[];\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nfunction sameFile(a: string, b: string): boolean {\n\tconst ra = resolve(a);\n\tconst rb = resolve(b);\n\t// Windows paths are case-insensitive.\n\treturn process.platform === \"win32\" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;\n}\n\nasync function pruneDir(\n\tdir: string,\n\tcutoffMs: number,\n\texcludeFile: string | undefined,\n\tdeleted: string[],\n): Promise<void> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn; // Missing or unreadable directory: nothing to prune.\n\t}\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !entry.name.endsWith(\".jsonl\")) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\tif (excludeFile && sameFile(filePath, excludeFile)) continue;\n\t\ttry {\n\t\t\tconst info = await stat(filePath);\n\t\t\tif (info.mtimeMs < cutoffMs) {\n\t\t\t\tconst ownership = new SessionOwnership(filePath);\n\t\t\t\ttry {\n\t\t\t\t\tif ((await stat(filePath)).mtimeMs < cutoffMs) {\n\t\t\t\t\t\tawait unlink(filePath);\n\t\t\t\t\t\tdeleted.push(filePath);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\townership.release();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Per-file errors (stat/unlink races, permissions) must never break startup.\n\t\t}\n\t}\n}\n\n/**\n * Delete .jsonl session files older than `retentionDays` under `sessionsRoot`.\n * Covers both layouts: project subdirectories (<root>/--<cwd>--/*.jsonl) and flat\n * custom session dirs (<root>/*.jsonl). `retentionDays <= 0` keeps everything.\n */\nexport async function pruneOldSessions(\n\tsessionsRoot: string,\n\tretentionDays: number,\n\toptions: PruneOldSessionsOptions = {},\n): Promise<PruneOldSessionsResult> {\n\tconst deleted: string[] = [];\n\tif (!Number.isFinite(retentionDays) || retentionDays <= 0) {\n\t\treturn { deleted };\n\t}\n\tconst now = options.now ?? Date.now();\n\tconst cutoffMs = now - retentionDays * DAY_MS;\n\n\t// Flat layout (custom --session-dir).\n\tawait pruneDir(sessionsRoot, cutoffMs, options.excludeFile, deleted);\n\n\t// Project layout (<root>/<dir>/*.jsonl).\n\tlet subdirs: Dirent[];\n\ttry {\n\t\tsubdirs = await readdir(sessionsRoot, { withFileTypes: true });\n\t} catch {\n\t\treturn { deleted };\n\t}\n\tfor (const entry of subdirs) {\n\t\tif (entry.isDirectory()) {\n\t\t\tawait pruneDir(join(sessionsRoot, entry.name), cutoffMs, options.excludeFile, deleted);\n\t\t}\n\t}\n\treturn { deleted };\n}\n"]}
1
+ {"version":3,"file":"session-retention.d.ts","sourceRoot":"","sources":["../../src/core/session-retention.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,uBAAuB;IACvC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAsB;IACtC,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;CAClB;AAsDD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,uBAA4B,GACnC,OAAO,CAAC,sBAAsB,CAAC,CAwBjC","sourcesContent":["// lunr: session retention pruning (Phase 5 of lunr-ux plan).\n// Scans the sessions root (~/.lunr/agent/sessions/, layout: <root>/--<cwd>--/*.jsonl,\n// or a flat custom --session-dir) and deletes .jsonl session files older than the\n// configured retention window. The active session file is always excluded.\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir, realpath, stat, unlink } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { SessionOwnership } from \"./session-ownership.ts\";\n\nexport interface PruneOldSessionsOptions {\n\t/** Absolute path of the active session file; never deleted. */\n\texcludeFile?: string;\n\t/** Reference timestamp in ms (defaults to Date.now()); injectable for tests. */\n\tnow?: number;\n}\n\nexport interface PruneOldSessionsResult {\n\t/** Paths of session files that were deleted. */\n\tdeleted: string[];\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nasync function sameFile(a: string, b: string): Promise<boolean> {\n\tconst normalize = (path: string) => (process.platform === \"win32\" ? resolve(path).toLowerCase() : resolve(path));\n\tconst ra = normalize(a);\n\tconst rb = normalize(b);\n\t// Windows paths are case-insensitive.\n\tif (ra === rb) return true;\n\tif (basename(ra) !== basename(rb)) return false;\n\ttry {\n\t\tconst [canonicalA, canonicalB] = await Promise.all([realpath(a), realpath(b)]);\n\t\treturn normalize(canonicalA) === normalize(canonicalB);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function pruneDir(\n\tdir: string,\n\tcutoffMs: number,\n\texcludeFile: string | undefined,\n\tdeleted: string[],\n): Promise<void> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn; // Missing or unreadable directory: nothing to prune.\n\t}\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !entry.name.endsWith(\".jsonl\")) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\tif (excludeFile && (await sameFile(filePath, excludeFile))) continue;\n\t\ttry {\n\t\t\tconst info = await stat(filePath);\n\t\t\tif (info.mtimeMs < cutoffMs) {\n\t\t\t\tconst ownership = new SessionOwnership(filePath);\n\t\t\t\ttry {\n\t\t\t\t\tif ((await stat(filePath)).mtimeMs < cutoffMs) {\n\t\t\t\t\t\tawait unlink(filePath);\n\t\t\t\t\t\tdeleted.push(filePath);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\townership.release();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Per-file errors (stat/unlink races, permissions) must never break startup.\n\t\t}\n\t}\n}\n\n/**\n * Delete .jsonl session files older than `retentionDays` under `sessionsRoot`.\n * Covers both layouts: project subdirectories (<root>/--<cwd>--/*.jsonl) and flat\n * custom session dirs (<root>/*.jsonl). `retentionDays <= 0` keeps everything.\n */\nexport async function pruneOldSessions(\n\tsessionsRoot: string,\n\tretentionDays: number,\n\toptions: PruneOldSessionsOptions = {},\n): Promise<PruneOldSessionsResult> {\n\tconst deleted: string[] = [];\n\tif (!Number.isFinite(retentionDays) || retentionDays <= 0) {\n\t\treturn { deleted };\n\t}\n\tconst now = options.now ?? Date.now();\n\tconst cutoffMs = now - retentionDays * DAY_MS;\n\n\t// Flat layout (custom --session-dir).\n\tawait pruneDir(sessionsRoot, cutoffMs, options.excludeFile, deleted);\n\n\t// Project layout (<root>/<dir>/*.jsonl).\n\tlet subdirs: Dirent[];\n\ttry {\n\t\tsubdirs = await readdir(sessionsRoot, { withFileTypes: true });\n\t} catch {\n\t\treturn { deleted };\n\t}\n\tfor (const entry of subdirs) {\n\t\tif (entry.isDirectory()) {\n\t\t\tawait pruneDir(join(sessionsRoot, entry.name), cutoffMs, options.excludeFile, deleted);\n\t\t}\n\t}\n\treturn { deleted };\n}\n"]}
@@ -2,15 +2,26 @@
2
2
  // Scans the sessions root (~/.lunr/agent/sessions/, layout: <root>/--<cwd>--/*.jsonl,
3
3
  // or a flat custom --session-dir) and deletes .jsonl session files older than the
4
4
  // configured retention window. The active session file is always excluded.
5
- import { readdir, stat, unlink } from "node:fs/promises";
6
- import { join, resolve } from "node:path";
5
+ import { readdir, realpath, stat, unlink } from "node:fs/promises";
6
+ import { basename, join, resolve } from "node:path";
7
7
  import { SessionOwnership } from "./session-ownership.js";
8
8
  const DAY_MS = 24 * 60 * 60 * 1000;
9
- function sameFile(a, b) {
10
- const ra = resolve(a);
11
- const rb = resolve(b);
9
+ async function sameFile(a, b) {
10
+ const normalize = (path) => (process.platform === "win32" ? resolve(path).toLowerCase() : resolve(path));
11
+ const ra = normalize(a);
12
+ const rb = normalize(b);
12
13
  // Windows paths are case-insensitive.
13
- return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
14
+ if (ra === rb)
15
+ return true;
16
+ if (basename(ra) !== basename(rb))
17
+ return false;
18
+ try {
19
+ const [canonicalA, canonicalB] = await Promise.all([realpath(a), realpath(b)]);
20
+ return normalize(canonicalA) === normalize(canonicalB);
21
+ }
22
+ catch {
23
+ return false;
24
+ }
14
25
  }
15
26
  async function pruneDir(dir, cutoffMs, excludeFile, deleted) {
16
27
  let entries;
@@ -24,7 +35,7 @@ async function pruneDir(dir, cutoffMs, excludeFile, deleted) {
24
35
  if (!entry.isFile() || !entry.name.endsWith(".jsonl"))
25
36
  continue;
26
37
  const filePath = join(dir, entry.name);
27
- if (excludeFile && sameFile(filePath, excludeFile))
38
+ if (excludeFile && (await sameFile(filePath, excludeFile)))
28
39
  continue;
29
40
  try {
30
41
  const info = await stat(filePath);
@@ -1 +1 @@
1
- {"version":3,"file":"session-retention.js","sourceRoot":"","sources":["../../src/core/session-retention.ts"],"names":[],"mappings":"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","sourcesContent":["// lunr: session retention pruning (Phase 5 of lunr-ux plan).\n// Scans the sessions root (~/.lunr/agent/sessions/, layout: <root>/--<cwd>--/*.jsonl,\n// or a flat custom --session-dir) and deletes .jsonl session files older than the\n// configured retention window. The active session file is always excluded.\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir, stat, unlink } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { SessionOwnership } from \"./session-ownership.ts\";\n\nexport interface PruneOldSessionsOptions {\n\t/** Absolute path of the active session file; never deleted. */\n\texcludeFile?: string;\n\t/** Reference timestamp in ms (defaults to Date.now()); injectable for tests. */\n\tnow?: number;\n}\n\nexport interface PruneOldSessionsResult {\n\t/** Paths of session files that were deleted. */\n\tdeleted: string[];\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nfunction sameFile(a: string, b: string): boolean {\n\tconst ra = resolve(a);\n\tconst rb = resolve(b);\n\t// Windows paths are case-insensitive.\n\treturn process.platform === \"win32\" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;\n}\n\nasync function pruneDir(\n\tdir: string,\n\tcutoffMs: number,\n\texcludeFile: string | undefined,\n\tdeleted: string[],\n): Promise<void> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn; // Missing or unreadable directory: nothing to prune.\n\t}\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !entry.name.endsWith(\".jsonl\")) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\tif (excludeFile && sameFile(filePath, excludeFile)) continue;\n\t\ttry {\n\t\t\tconst info = await stat(filePath);\n\t\t\tif (info.mtimeMs < cutoffMs) {\n\t\t\t\tconst ownership = new SessionOwnership(filePath);\n\t\t\t\ttry {\n\t\t\t\t\tif ((await stat(filePath)).mtimeMs < cutoffMs) {\n\t\t\t\t\t\tawait unlink(filePath);\n\t\t\t\t\t\tdeleted.push(filePath);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\townership.release();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Per-file errors (stat/unlink races, permissions) must never break startup.\n\t\t}\n\t}\n}\n\n/**\n * Delete .jsonl session files older than `retentionDays` under `sessionsRoot`.\n * Covers both layouts: project subdirectories (<root>/--<cwd>--/*.jsonl) and flat\n * custom session dirs (<root>/*.jsonl). `retentionDays <= 0` keeps everything.\n */\nexport async function pruneOldSessions(\n\tsessionsRoot: string,\n\tretentionDays: number,\n\toptions: PruneOldSessionsOptions = {},\n): Promise<PruneOldSessionsResult> {\n\tconst deleted: string[] = [];\n\tif (!Number.isFinite(retentionDays) || retentionDays <= 0) {\n\t\treturn { deleted };\n\t}\n\tconst now = options.now ?? Date.now();\n\tconst cutoffMs = now - retentionDays * DAY_MS;\n\n\t// Flat layout (custom --session-dir).\n\tawait pruneDir(sessionsRoot, cutoffMs, options.excludeFile, deleted);\n\n\t// Project layout (<root>/<dir>/*.jsonl).\n\tlet subdirs: Dirent[];\n\ttry {\n\t\tsubdirs = await readdir(sessionsRoot, { withFileTypes: true });\n\t} catch {\n\t\treturn { deleted };\n\t}\n\tfor (const entry of subdirs) {\n\t\tif (entry.isDirectory()) {\n\t\t\tawait pruneDir(join(sessionsRoot, entry.name), cutoffMs, options.excludeFile, deleted);\n\t\t}\n\t}\n\treturn { deleted };\n}\n"]}
1
+ {"version":3,"file":"session-retention.js","sourceRoot":"","sources":["../../src/core/session-retention.ts"],"names":[],"mappings":"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","sourcesContent":["// lunr: session retention pruning (Phase 5 of lunr-ux plan).\n// Scans the sessions root (~/.lunr/agent/sessions/, layout: <root>/--<cwd>--/*.jsonl,\n// or a flat custom --session-dir) and deletes .jsonl session files older than the\n// configured retention window. The active session file is always excluded.\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir, realpath, stat, unlink } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { SessionOwnership } from \"./session-ownership.ts\";\n\nexport interface PruneOldSessionsOptions {\n\t/** Absolute path of the active session file; never deleted. */\n\texcludeFile?: string;\n\t/** Reference timestamp in ms (defaults to Date.now()); injectable for tests. */\n\tnow?: number;\n}\n\nexport interface PruneOldSessionsResult {\n\t/** Paths of session files that were deleted. */\n\tdeleted: string[];\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nasync function sameFile(a: string, b: string): Promise<boolean> {\n\tconst normalize = (path: string) => (process.platform === \"win32\" ? resolve(path).toLowerCase() : resolve(path));\n\tconst ra = normalize(a);\n\tconst rb = normalize(b);\n\t// Windows paths are case-insensitive.\n\tif (ra === rb) return true;\n\tif (basename(ra) !== basename(rb)) return false;\n\ttry {\n\t\tconst [canonicalA, canonicalB] = await Promise.all([realpath(a), realpath(b)]);\n\t\treturn normalize(canonicalA) === normalize(canonicalB);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function pruneDir(\n\tdir: string,\n\tcutoffMs: number,\n\texcludeFile: string | undefined,\n\tdeleted: string[],\n): Promise<void> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn; // Missing or unreadable directory: nothing to prune.\n\t}\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !entry.name.endsWith(\".jsonl\")) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\tif (excludeFile && (await sameFile(filePath, excludeFile))) continue;\n\t\ttry {\n\t\t\tconst info = await stat(filePath);\n\t\t\tif (info.mtimeMs < cutoffMs) {\n\t\t\t\tconst ownership = new SessionOwnership(filePath);\n\t\t\t\ttry {\n\t\t\t\t\tif ((await stat(filePath)).mtimeMs < cutoffMs) {\n\t\t\t\t\t\tawait unlink(filePath);\n\t\t\t\t\t\tdeleted.push(filePath);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\townership.release();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Per-file errors (stat/unlink races, permissions) must never break startup.\n\t\t}\n\t}\n}\n\n/**\n * Delete .jsonl session files older than `retentionDays` under `sessionsRoot`.\n * Covers both layouts: project subdirectories (<root>/--<cwd>--/*.jsonl) and flat\n * custom session dirs (<root>/*.jsonl). `retentionDays <= 0` keeps everything.\n */\nexport async function pruneOldSessions(\n\tsessionsRoot: string,\n\tretentionDays: number,\n\toptions: PruneOldSessionsOptions = {},\n): Promise<PruneOldSessionsResult> {\n\tconst deleted: string[] = [];\n\tif (!Number.isFinite(retentionDays) || retentionDays <= 0) {\n\t\treturn { deleted };\n\t}\n\tconst now = options.now ?? Date.now();\n\tconst cutoffMs = now - retentionDays * DAY_MS;\n\n\t// Flat layout (custom --session-dir).\n\tawait pruneDir(sessionsRoot, cutoffMs, options.excludeFile, deleted);\n\n\t// Project layout (<root>/<dir>/*.jsonl).\n\tlet subdirs: Dirent[];\n\ttry {\n\t\tsubdirs = await readdir(sessionsRoot, { withFileTypes: true });\n\t} catch {\n\t\treturn { deleted };\n\t}\n\tfor (const entry of subdirs) {\n\t\tif (entry.isDirectory()) {\n\t\t\tawait pruneDir(join(sessionsRoot, entry.name), cutoffMs, options.excludeFile, deleted);\n\t\t}\n\t}\n\treturn { deleted };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { SessionManager } from "./session-manager.ts";
2
+ import type { SettingsManager } from "./settings-manager.ts";
3
+ /** Global maintenance keeps its own policy; approved project policy applies only to its configured tree. */
4
+ export declare function getSessionRetentionTargets(globalSettings: SettingsManager, runtimeSettings: SettingsManager, explicitSessionDir?: string): {
5
+ directory: string;
6
+ days: number;
7
+ }[];
8
+ /** Replace the provisional manager only after a project directory is approved. */
9
+ export declare function selectApprovedStartupSession(manager: SessionManager, directory: string | undefined, select?: () => Promise<SessionManager>): Promise<SessionManager>;
10
+ //# sourceMappingURL=session-startup-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-startup-settings.d.ts","sourceRoot":"","sources":["../../src/core/session-startup-settings.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAgC7D,4GAA4G;AAC5G,wBAAgB,0BAA0B,CACzC,cAAc,EAAE,eAAe,EAC/B,eAAe,EAAE,eAAe,EAChC,kBAAkB,CAAC,EAAE,MAAM,GACzB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAmBvC;AAED,kFAAkF;AAClF,wBAAsB,4BAA4B,CACjD,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,GACpC,OAAO,CAAC,cAAc,CAAC,CASzB","sourcesContent":["import { lstatSync, realpathSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, join, relative, sep } from \"node:path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { SessionManager } from \"./session-manager.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nfunction canonicalDirectory(directory: string, allowMissing = false): string | undefined {\n\ttry {\n\t\treturn realpathSync(directory);\n\t} catch {\n\t\tif (!allowMissing) return undefined;\n\t\ttry {\n\t\t\t// A dangling link exists and must not be treated as a missing directory.\n\t\t\tlstatSync(directory);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof Error) || !(\"code\" in error) || error.code !== \"ENOENT\") return undefined;\n\t\t}\n\t\ttry {\n\t\t\t// The standard sessions root may not exist when all storage is custom.\n\t\t\treturn join(realpathSync(dirname(directory)), basename(directory));\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nfunction containsDirectory(root: string, directory: string): boolean {\n\tif (process.platform === \"win32\") {\n\t\troot = root.toLowerCase();\n\t\tdirectory = directory.toLowerCase();\n\t}\n\tconst path = relative(root, directory);\n\treturn path === \"\" || (!isAbsolute(path) && path !== \"..\" && !path.startsWith(`..${sep}`));\n}\n\n/** Global maintenance keeps its own policy; approved project policy applies only to its configured tree. */\nexport function getSessionRetentionTargets(\n\tglobalSettings: SettingsManager,\n\truntimeSettings: SettingsManager,\n\texplicitSessionDir?: string,\n): { directory: string; days: number }[] {\n\tconst globalDirectory = canonicalDirectory(getSessionsDir(), true);\n\tif (!globalDirectory) return [];\n\tconst globalDays = globalSettings.getSessionRetentionDays();\n\tconst targets = [{ directory: globalDirectory, days: globalDays }];\n\tconst configuredDirectory = explicitSessionDir ?? runtimeSettings.getSessionDir();\n\tconst directory = configuredDirectory ? canonicalDirectory(configuredDirectory) : undefined;\n\tif (directory && relative(globalDirectory, directory) !== \"\") {\n\t\tconst projectDirectory = runtimeSettings.getProjectSettings().sessionDir;\n\t\tconst overlapsGlobal =\n\t\t\tcontainsDirectory(globalDirectory, directory) || containsDirectory(directory, globalDirectory);\n\t\tconst usesProjectDirectory =\n\t\t\truntimeSettings.isProjectTrusted() &&\n\t\t\tprojectDirectory !== undefined &&\n\t\t\t!overlapsGlobal &&\n\t\t\tdirectory === canonicalDirectory(projectDirectory);\n\t\ttargets.push({ directory, days: usesProjectDirectory ? runtimeSettings.getSessionRetentionDays() : globalDays });\n\t}\n\treturn targets.filter(({ days }) => days > 0);\n}\n\n/** Replace the provisional manager only after a project directory is approved. */\nexport async function selectApprovedStartupSession(\n\tmanager: SessionManager,\n\tdirectory: string | undefined,\n\tselect?: () => Promise<SessionManager>,\n): Promise<SessionManager> {\n\tconst cwd = manager.getCwd();\n\tconst sessionFile = manager.getSessionFile();\n\tconst id = manager.getSessionId();\n\tmanager.dispose();\n\tif (select) return select();\n\treturn sessionFile\n\t\t? SessionManager.open(sessionFile, directory, cwd)\n\t\t: SessionManager.create(cwd, directory, { id });\n}\n"]}
@@ -0,0 +1,70 @@
1
+ import { lstatSync, realpathSync } from "node:fs";
2
+ import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
3
+ import { getSessionsDir } from "../config.js";
4
+ import { SessionManager } from "./session-manager.js";
5
+ function canonicalDirectory(directory, allowMissing = false) {
6
+ try {
7
+ return realpathSync(directory);
8
+ }
9
+ catch {
10
+ if (!allowMissing)
11
+ return undefined;
12
+ try {
13
+ // A dangling link exists and must not be treated as a missing directory.
14
+ lstatSync(directory);
15
+ return undefined;
16
+ }
17
+ catch (error) {
18
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT")
19
+ return undefined;
20
+ }
21
+ try {
22
+ // The standard sessions root may not exist when all storage is custom.
23
+ return join(realpathSync(dirname(directory)), basename(directory));
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
29
+ }
30
+ function containsDirectory(root, directory) {
31
+ if (process.platform === "win32") {
32
+ root = root.toLowerCase();
33
+ directory = directory.toLowerCase();
34
+ }
35
+ const path = relative(root, directory);
36
+ return path === "" || (!isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`));
37
+ }
38
+ /** Global maintenance keeps its own policy; approved project policy applies only to its configured tree. */
39
+ export function getSessionRetentionTargets(globalSettings, runtimeSettings, explicitSessionDir) {
40
+ const globalDirectory = canonicalDirectory(getSessionsDir(), true);
41
+ if (!globalDirectory)
42
+ return [];
43
+ const globalDays = globalSettings.getSessionRetentionDays();
44
+ const targets = [{ directory: globalDirectory, days: globalDays }];
45
+ const configuredDirectory = explicitSessionDir ?? runtimeSettings.getSessionDir();
46
+ const directory = configuredDirectory ? canonicalDirectory(configuredDirectory) : undefined;
47
+ if (directory && relative(globalDirectory, directory) !== "") {
48
+ const projectDirectory = runtimeSettings.getProjectSettings().sessionDir;
49
+ const overlapsGlobal = containsDirectory(globalDirectory, directory) || containsDirectory(directory, globalDirectory);
50
+ const usesProjectDirectory = runtimeSettings.isProjectTrusted() &&
51
+ projectDirectory !== undefined &&
52
+ !overlapsGlobal &&
53
+ directory === canonicalDirectory(projectDirectory);
54
+ targets.push({ directory, days: usesProjectDirectory ? runtimeSettings.getSessionRetentionDays() : globalDays });
55
+ }
56
+ return targets.filter(({ days }) => days > 0);
57
+ }
58
+ /** Replace the provisional manager only after a project directory is approved. */
59
+ export async function selectApprovedStartupSession(manager, directory, select) {
60
+ const cwd = manager.getCwd();
61
+ const sessionFile = manager.getSessionFile();
62
+ const id = manager.getSessionId();
63
+ manager.dispose();
64
+ if (select)
65
+ return select();
66
+ return sessionFile
67
+ ? SessionManager.open(sessionFile, directory, cwd)
68
+ : SessionManager.create(cwd, directory, { id });
69
+ }
70
+ //# sourceMappingURL=session-startup-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-startup-settings.js","sourceRoot":"","sources":["../../src/core/session-startup-settings.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstatSync, realpathSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, join, relative, sep } from \"node:path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { SessionManager } from \"./session-manager.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\nfunction canonicalDirectory(directory: string, allowMissing = false): string | undefined {\n\ttry {\n\t\treturn realpathSync(directory);\n\t} catch {\n\t\tif (!allowMissing) return undefined;\n\t\ttry {\n\t\t\t// A dangling link exists and must not be treated as a missing directory.\n\t\t\tlstatSync(directory);\n\t\t\treturn undefined;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof Error) || !(\"code\" in error) || error.code !== \"ENOENT\") return undefined;\n\t\t}\n\t\ttry {\n\t\t\t// The standard sessions root may not exist when all storage is custom.\n\t\t\treturn join(realpathSync(dirname(directory)), basename(directory));\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nfunction containsDirectory(root: string, directory: string): boolean {\n\tif (process.platform === \"win32\") {\n\t\troot = root.toLowerCase();\n\t\tdirectory = directory.toLowerCase();\n\t}\n\tconst path = relative(root, directory);\n\treturn path === \"\" || (!isAbsolute(path) && path !== \"..\" && !path.startsWith(`..${sep}`));\n}\n\n/** Global maintenance keeps its own policy; approved project policy applies only to its configured tree. */\nexport function getSessionRetentionTargets(\n\tglobalSettings: SettingsManager,\n\truntimeSettings: SettingsManager,\n\texplicitSessionDir?: string,\n): { directory: string; days: number }[] {\n\tconst globalDirectory = canonicalDirectory(getSessionsDir(), true);\n\tif (!globalDirectory) return [];\n\tconst globalDays = globalSettings.getSessionRetentionDays();\n\tconst targets = [{ directory: globalDirectory, days: globalDays }];\n\tconst configuredDirectory = explicitSessionDir ?? runtimeSettings.getSessionDir();\n\tconst directory = configuredDirectory ? canonicalDirectory(configuredDirectory) : undefined;\n\tif (directory && relative(globalDirectory, directory) !== \"\") {\n\t\tconst projectDirectory = runtimeSettings.getProjectSettings().sessionDir;\n\t\tconst overlapsGlobal =\n\t\t\tcontainsDirectory(globalDirectory, directory) || containsDirectory(directory, globalDirectory);\n\t\tconst usesProjectDirectory =\n\t\t\truntimeSettings.isProjectTrusted() &&\n\t\t\tprojectDirectory !== undefined &&\n\t\t\t!overlapsGlobal &&\n\t\t\tdirectory === canonicalDirectory(projectDirectory);\n\t\ttargets.push({ directory, days: usesProjectDirectory ? runtimeSettings.getSessionRetentionDays() : globalDays });\n\t}\n\treturn targets.filter(({ days }) => days > 0);\n}\n\n/** Replace the provisional manager only after a project directory is approved. */\nexport async function selectApprovedStartupSession(\n\tmanager: SessionManager,\n\tdirectory: string | undefined,\n\tselect?: () => Promise<SessionManager>,\n): Promise<SessionManager> {\n\tconst cwd = manager.getCwd();\n\tconst sessionFile = manager.getSessionFile();\n\tconst id = manager.getSessionId();\n\tmanager.dispose();\n\tif (select) return select();\n\treturn sessionFile\n\t\t? SessionManager.open(sessionFile, directory, cwd)\n\t\t: SessionManager.create(cwd, directory, { id });\n}\n"]}
@@ -25,9 +25,14 @@ interface ProviderPool {
25
25
  keys: SubEntry[];
26
26
  }
27
27
  type SubscriptionData = Record<string, ProviderPool>;
28
+ export interface RequestSubscriptionKey {
29
+ providerId: string;
30
+ fingerprint: string;
31
+ }
32
+ /** Request identities are non-secret digests kept outside messages and storage. */
33
+ export declare function subscriptionKeyFingerprint(providerId: string, key: string): string;
28
34
  export declare class SubscriptionManager {
29
35
  private data;
30
- private loaded;
31
36
  private storage;
32
37
  private authStorage;
33
38
  private queue;
@@ -37,9 +42,13 @@ export declare class SubscriptionManager {
37
42
  static fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager;
38
43
  static inMemory(authStorage: CredentialStore, data?: SubscriptionData): SubscriptionManager;
39
44
  private parseStorageData;
40
- private ensureLoaded;
45
+ /**
46
+ * Lock order is subscriptions then auth. Keep selection and its auth mirror
47
+ * under the subscription lock so later selectors cannot overtake a mirror.
48
+ * These files are not an atomic commit: a failed mirror aborts the pool write;
49
+ * a later pool write failure can leave auth ahead and a repeated selection repairs it.
50
+ */
41
51
  private enqueue;
42
- private persist;
43
52
  private readRawCredential;
44
53
  private ensurePool;
45
54
  private nextId;
@@ -51,12 +60,12 @@ export declare class SubscriptionManager {
51
60
  renameKey(providerId: string, id: string, name: string): Promise<void>;
52
61
  setActive(providerId: string, id: string): Promise<void>;
53
62
  /**
54
- * Mark the active key exhausted and rotate to the next non-exhausted key in
63
+ * Mark the request's key exhausted and rotate to the next non-exhausted key in
55
64
  * round-robin order. A parseable reset time is persisted as exhaustedUntil;
56
- * otherwise the exhaustion is process-local only. Returns the new active key,
57
- * or null when no non-exhausted alternative exists (current key stays active).
65
+ * otherwise the exhaustion is process-local only. A later selection is preserved.
66
+ * Returns the usable active key for retry, or null when no alternative exists.
58
67
  */
59
- rotateOnFailure(providerId: string, errorMessage: string, now?: number): Promise<SubEntry | null>;
68
+ rotateOnFailure(providerId: string, errorMessage: string, requestFingerprint: string | undefined, now?: number): Promise<SubEntry | null>;
60
69
  /** Manual reactivate: clears persisted and process-local exhaustion for a key. */
61
70
  clearExhaustion(providerId: string, id: string): Promise<void>;
62
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAAE,KAAK,kBAAkB,EAAsD,MAAM,mBAAmB,CAAC;AAGhH,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,YAAY;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;CACjB;AAED,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAErD,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAkB;IAErC,OAAO,CAAC,KAAK,CAAuC;IAGpD,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,eAGN;IAED,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAK9E;IAED,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,kBAAkB,GAAG,mBAAmB,CAEjG;IAED,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,GAAE,gBAAqB,GAAG,mBAAmB,CAI9F;IAED,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,OAAO;YAQD,iBAAiB;YAajB,UAAU;IAiBxB,OAAO,CAAC,MAAM;YASA,YAAY;IAIpB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAKlD;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAKjE;IAEK,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAY9E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8B7D;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7D;IAED;;;;;OAKG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA0ClH;IAED,kFAAkF;IAC5E,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAenE;CACD","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate loaded = false;\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider ids of keys exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as SubscriptionData;\n\t}\n\n\tprivate ensureLoaded(): void {\n\t\tif (this.loaded) return;\n\t\tthis.loaded = true;\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t} catch {\n\t\t\t// Preserve the empty snapshot; a malformed file is never overwritten by a load.\n\t\t}\n\t}\n\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(fn);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\tprivate persist(): Promise<void> {\n\t\treturn this.storage.withLockAsync(async () => ({\n\t\t\tresult: undefined,\n\t\t\tnext: JSON.stringify(this.data, null, 2),\n\t\t}));\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tthis.ensureLoaded();\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\tawait this.persist();\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tawait this.authStorage.modify(providerId, async () => ({ type: \"api_key\", key }));\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.ensureLoaded();\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.persist();\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the active key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. Returns the new active key,\n\t * or null when no non-exhausted alternative exists (current key stays active).\n\t */\n\tasync rotateOnFailure(providerId: string, errorMessage: string, now: number = Date.now()): Promise<SubEntry | null> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tif (!current) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tentry.id === current.id ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(current.id);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(entry.id) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the (now-exhausted) current key active, but still\n\t\t\t// persist its exhaustion state when a reset time was recorded.\n\t\t\tif (keys !== pool.keys) {\n\t\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t\tawait this.persist();\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.memoryExhausted.get(providerId)?.delete(id);\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\tawait this.persist();\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/core/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAAE,KAAK,kBAAkB,EAAsD,MAAM,mBAAmB,CAAC;AAIhH,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,YAAY;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;CACjB;AAED,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAErD,MAAM,WAAW,sBAAsB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAkB;IAErC,OAAO,CAAC,KAAK,CAAuC;IAGpD,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,eAGN;IAED,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAK9E;IAED,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,kBAAkB,GAAG,mBAAmB,CAEjG;IAED,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,GAAE,gBAAqB,GAAG,mBAAmB,CAI9F;IAED,OAAO,CAAC,gBAAgB;IAmCxB;;;;;OAKG;IACH,OAAO,CAAC,OAAO;YAeD,iBAAiB;YAajB,UAAU;IAexB,OAAO,CAAC,MAAM;YASA,YAAY;IAapB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAKlD;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAKjE;IAEK,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU9E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC7D;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO3E;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7D;IAED;;;;;OAKG;IACG,eAAe,CACpB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,GAAG,GAAE,MAAmB,GACtB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4C1B;IAED,kFAAkF;IAC5E,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcnE;CACD","sourcesContent":["/**\n * lunr: multi-subscription API-key pools per provider.\n *\n * The pool lives in subscriptions.json next to auth.json; auth.json always\n * holds the ACTIVE credential, so rotation is just mirroring the chosen pool\n * key into auth.json via AuthStorage.modify. Persistence reuses the\n * AuthStorage backend classes (proper-lockfile guarded read-modify-write,\n * mode 0600) pointed at the subscriptions.json path.\n *\n * Raw keys are never logged or embedded in error messages — errors refer to\n * provider and key ids only.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Credential, CredentialStore } from \"@ashx-j/lunr-ai\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend, InMemoryAuthStorageBackend } from \"./auth-storage.ts\";\nimport { parseResetTimeMs } from \"./usage-limit.ts\";\nimport { clearPlanUsageCache } from \"./usage-service.ts\";\n\nexport interface SubEntry {\n\tid: string;\n\tname: string;\n\tkey: string;\n\taddedAt: number;\n\texhaustedUntil?: number;\n\tlastError?: string;\n}\n\ninterface ProviderPool {\n\tactive: string;\n\tkeys: SubEntry[];\n}\n\ntype SubscriptionData = Record<string, ProviderPool>;\n\nexport interface RequestSubscriptionKey {\n\tproviderId: string;\n\tfingerprint: string;\n}\n\n/** Request identities are non-secret digests kept outside messages and storage. */\nexport function subscriptionKeyFingerprint(providerId: string, key: string): string {\n\treturn createHash(\"sha256\").update(providerId).update(\"\\0\").update(key).digest(\"hex\");\n}\n\nexport class SubscriptionManager {\n\tprivate data: SubscriptionData = {};\n\tprivate storage: AuthStorageBackend;\n\tprivate authStorage: CredentialStore;\n\t// In-process write queue so mutations serialize even before the file lock.\n\tprivate queue: Promise<unknown> = Promise.resolve();\n\t// Per-provider fingerprints exhausted without a parseable reset time.\n\t// Process-local on purpose: infinite exhaustion is never persisted.\n\tprivate memoryExhausted = new Map<string, Set<string>>();\n\n\tprivate constructor(authStorage: CredentialStore, storage: AuthStorageBackend) {\n\t\tthis.authStorage = authStorage;\n\t\tthis.storage = storage;\n\t}\n\n\tstatic create(authStorage: CredentialStore, path?: string): SubscriptionManager {\n\t\treturn new SubscriptionManager(\n\t\t\tauthStorage,\n\t\t\tnew FileAuthStorageBackend(path ?? join(getAgentDir(), \"subscriptions.json\")),\n\t\t);\n\t}\n\n\tstatic fromStorage(authStorage: CredentialStore, storage: AuthStorageBackend): SubscriptionManager {\n\t\treturn new SubscriptionManager(authStorage, storage);\n\t}\n\n\tstatic inMemory(authStorage: CredentialStore, data: SubscriptionData = {}): SubscriptionManager {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn SubscriptionManager.fromStorage(authStorage, storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): SubscriptionData {\n\t\tif (content === undefined) return {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(content);\n\t\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error();\n\t\t\tfor (const value of Object.values(parsed as Record<string, unknown>)) {\n\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\tconst pool = value as Record<string, unknown>;\n\t\t\t\tif (typeof pool.active !== \"string\" || !Array.isArray(pool.keys)) throw new Error();\n\t\t\t\tconst ids = new Set<string>();\n\t\t\t\tfor (const value of pool.keys as unknown[]) {\n\t\t\t\t\tif (!value || typeof value !== \"object\") throw new Error();\n\t\t\t\t\tconst entry = value as Record<string, unknown>;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof entry.id !== \"string\" ||\n\t\t\t\t\t\tids.has(entry.id) ||\n\t\t\t\t\t\ttypeof entry.name !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.key !== \"string\" ||\n\t\t\t\t\t\ttypeof entry.addedAt !== \"number\" ||\n\t\t\t\t\t\t!Number.isFinite(entry.addedAt) ||\n\t\t\t\t\t\t(entry.exhaustedUntil !== undefined &&\n\t\t\t\t\t\t\t(typeof entry.exhaustedUntil !== \"number\" || !Number.isFinite(entry.exhaustedUntil))) ||\n\t\t\t\t\t\t(entry.lastError !== undefined && typeof entry.lastError !== \"string\")\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error();\n\t\t\t\t\tids.add(entry.id);\n\t\t\t\t}\n\t\t\t\tif (!ids.has(pool.active)) throw new Error();\n\t\t\t}\n\t\t\treturn parsed as SubscriptionData;\n\t\t} catch {\n\t\t\tthrow new Error(\"Subscription storage is malformed. Repair it before changing subscriptions.\");\n\t\t}\n\t}\n\n\t/**\n\t * Lock order is subscriptions then auth. Keep selection and its auth mirror\n\t * under the subscription lock so later selectors cannot overtake a mirror.\n\t * These files are not an atomic commit: a failed mirror aborts the pool write;\n\t * a later pool write failure can leave auth ahead and a repeated selection repairs it.\n\t */\n\tprivate enqueue<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst next = this.queue.then(() =>\n\t\t\tthis.storage.withLockAsync(async (current) => {\n\t\t\t\tthis.data = this.parseStorageData(current);\n\t\t\t\tconst before = JSON.stringify(this.data, null, 2);\n\t\t\t\tconst result = await fn();\n\t\t\t\tconst after = JSON.stringify(this.data, null, 2);\n\t\t\t\treturn { result, next: before === after ? undefined : after };\n\t\t\t}),\n\t\t);\n\t\tthis.queue = next.catch(() => {});\n\t\treturn next;\n\t}\n\n\t/** Raw stored credential read via the modify path (returning undefined leaves it unchanged). */\n\tprivate async readRawCredential(providerId: string): Promise<Credential | undefined> {\n\t\tlet current: Credential | undefined;\n\t\tawait this.authStorage.modify(providerId, async (credential) => {\n\t\t\tcurrent = credential;\n\t\t\treturn undefined;\n\t\t});\n\t\treturn current;\n\t}\n\n\t/**\n\t * Pool for a provider, lazy-importing an existing stored api_key credential\n\t * as \"Sub 1\" on first access. OAuth credentials are not imported.\n\t */\n\tprivate async ensurePool(providerId: string): Promise<ProviderPool | undefined> {\n\t\tconst existing = this.data[providerId];\n\t\tif (existing) return existing;\n\n\t\tconst credential = await this.readRawCredential(providerId);\n\t\tif (credential?.type !== \"api_key\" || credential.key === undefined) return undefined;\n\n\t\tconst pool: ProviderPool = {\n\t\t\tactive: \"1\",\n\t\t\tkeys: [{ id: \"1\", name: \"Sub 1\", key: credential.key, addedAt: Date.now() }],\n\t\t};\n\t\tthis.data = { ...this.data, [providerId]: pool };\n\t\treturn pool;\n\t}\n\n\tprivate nextId(pool: ProviderPool): string {\n\t\tlet max = 0;\n\t\tfor (const entry of pool.keys) {\n\t\t\tconst numeric = Number.parseInt(entry.id, 10);\n\t\t\tif (Number.isFinite(numeric) && numeric > max) max = numeric;\n\t\t}\n\t\treturn String(max + 1);\n\t}\n\n\tprivate async mirrorActive(providerId: string, key: string): Promise<void> {\n\t\tclearPlanUsageCache();\n\t\ttry {\n\t\t\tawait this.authStorage.modify(providerId, async (current) => ({\n\t\t\t\ttype: \"api_key\",\n\t\t\t\tkey,\n\t\t\t\t...(current?.type === \"api_key\" && current.env ? { env: current.env } : {}),\n\t\t\t}));\n\t\t} finally {\n\t\t\tclearPlanUsageCache();\n\t\t}\n\t}\n\n\tasync list(providerId: string): Promise<SubEntry[]> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool ? [...pool.keys] : [];\n\t\t});\n\t}\n\n\tasync getActive(providerId: string): Promise<SubEntry | undefined> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\treturn pool?.keys.find((entry) => entry.id === pool.active);\n\t\t});\n\t}\n\n\tasync addKey(providerId: string, key: string, name?: string): Promise<SubEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst id = pool ? this.nextId(pool) : \"1\";\n\t\t\tconst entry: SubEntry = { id, name: name ?? `Sub ${id}`, key, addedAt: Date.now() };\n\t\t\tconst keys = [...(pool?.keys ?? []), entry];\n\t\t\tthis.data = { ...this.data, [providerId]: { active: id, keys } };\n\t\t\tawait this.mirrorActive(providerId, key);\n\t\t\treturn entry;\n\t\t});\n\t}\n\n\tasync removeKey(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool || !pool.keys.some((entry) => entry.id === id)) return;\n\n\t\t\tconst keys = pool.keys.filter((entry) => entry.id !== id);\n\t\t\tconst removed = pool.keys.find((entry) => entry.id === id)!;\n\t\t\tif (!keys.some((entry) => entry.key === removed.key)) {\n\t\t\t\tthis.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, removed.key));\n\t\t\t}\n\n\t\t\tif (keys.length === 0) {\n\t\t\t\tconst data = { ...this.data };\n\t\t\t\tdelete data[providerId];\n\t\t\t\tthis.data = data;\n\t\t\t\tclearPlanUsageCache();\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authStorage.delete(providerId);\n\t\t\t\t} finally {\n\t\t\t\t\tclearPlanUsageCache();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet active = pool.active;\n\t\t\tif (active === id) {\n\t\t\t\tactive = keys[0]?.id ?? active;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t\t\tconst promoted = keys.find((entry) => entry.id === active);\n\t\t\t\tif (promoted) await this.mirrorActive(providerId, promoted.key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.data = { ...this.data, [providerId]: { active, keys } };\n\t\t});\n\t}\n\n\tasync renameKey(providerId: string, id: string, name: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst keys = pool.keys.map((entry) => (entry.id === id ? { ...entry, name } : entry));\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n\n\tasync setActive(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tconst entry = pool?.keys.find((candidate) => candidate.id === id);\n\t\t\tif (!pool || !entry) return;\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, active: id } };\n\t\t\tawait this.mirrorActive(providerId, entry.key);\n\t\t});\n\t}\n\n\t/**\n\t * Mark the request's key exhausted and rotate to the next non-exhausted key in\n\t * round-robin order. A parseable reset time is persisted as exhaustedUntil;\n\t * otherwise the exhaustion is process-local only. A later selection is preserved.\n\t * Returns the usable active key for retry, or null when no alternative exists.\n\t */\n\tasync rotateOnFailure(\n\t\tproviderId: string,\n\t\terrorMessage: string,\n\t\trequestFingerprint: string | undefined,\n\t\tnow: number = Date.now(),\n\t): Promise<SubEntry | null> {\n\t\tif (!requestFingerprint) return null;\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = this.data[providerId];\n\t\t\tif (!pool || pool.keys.length < 2) return null;\n\t\t\tconst currentIndex = pool.keys.findIndex((entry) => entry.id === pool.active);\n\t\t\tif (currentIndex === -1) return null;\n\t\t\tconst current = pool.keys[currentIndex];\n\t\t\tconst matchesRequest = (entry: SubEntry): boolean =>\n\t\t\t\tsubscriptionKeyFingerprint(providerId, entry.key) === requestFingerprint;\n\t\t\tif (!current || !pool.keys.some(matchesRequest)) return null;\n\n\t\t\tconst resetAt = parseResetTimeMs(errorMessage);\n\t\t\tlet keys = pool.keys;\n\t\t\tif (resetAt !== undefined && resetAt > now) {\n\t\t\t\tkeys = pool.keys.map((entry) =>\n\t\t\t\t\tmatchesRequest(entry) ? { ...entry, exhaustedUntil: resetAt, lastError: errorMessage } : entry,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst set = this.memoryExhausted.get(providerId) ?? new Set<string>();\n\t\t\t\tset.add(requestFingerprint);\n\t\t\t\tthis.memoryExhausted.set(providerId, set);\n\t\t\t}\n\n\t\t\tconst isExhausted = (entry: SubEntry): boolean =>\n\t\t\t\tthis.memoryExhausted.get(providerId)?.has(subscriptionKeyFingerprint(providerId, entry.key)) === true ||\n\t\t\t\t(entry.exhaustedUntil !== undefined && entry.exhaustedUntil > now);\n\n\t\t\tif (keys !== pool.keys) this.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t\t// Another owner already selected a different credential. Retry it without\n\t\t\t// replacing its selection or mirroring a stale failure over its auth.\n\t\t\tif (!matchesRequest(current)) return isExhausted(current) ? null : current;\n\n\t\t\tfor (let offset = 1; offset < keys.length; offset++) {\n\t\t\t\tconst candidate = keys[(currentIndex + offset) % keys.length];\n\t\t\t\tif (!candidate || isExhausted(candidate)) continue;\n\t\t\t\tthis.data = { ...this.data, [providerId]: { active: candidate.id, keys } };\n\t\t\t\tawait this.mirrorActive(providerId, candidate.key);\n\t\t\t\treturn candidate;\n\t\t\t}\n\n\t\t\t// No alternative: keep the now-exhausted current key active.\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t/** Manual reactivate: clears persisted and process-local exhaustion for a key. */\n\tasync clearExhaustion(providerId: string, id: string): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst pool = await this.ensurePool(providerId);\n\t\t\tif (!pool) return;\n\t\t\tconst target = pool.keys.find((entry) => entry.id === id);\n\t\t\tif (target) this.memoryExhausted.get(providerId)?.delete(subscriptionKeyFingerprint(providerId, target.key));\n\t\t\tif (!target || (target.exhaustedUntil === undefined && target.lastError === undefined)) return;\n\t\t\tconst keys = pool.keys.map((entry) => {\n\t\t\t\tif (entry.id !== id) return entry;\n\t\t\t\tconst { exhaustedUntil: _exhaustedUntil, lastError: _lastError, ...rest } = entry;\n\t\t\t\treturn rest;\n\t\t\t});\n\t\t\tthis.data = { ...this.data, [providerId]: { ...pool, keys } };\n\t\t});\n\t}\n}\n"]}