@ashx-j/lunr 0.1.0 → 0.1.1

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 (848) hide show
  1. package/dist/builtin-extensions/ashxj-spinners.d.ts +1 -1
  2. package/dist/builtin-extensions/ashxj-spinners.d.ts.map +1 -1
  3. package/dist/builtin-extensions/ashxj-spinners.js.map +1 -1
  4. package/dist/builtin-extensions/ashxj-thinking.d.ts +5 -5
  5. package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
  6. package/dist/builtin-extensions/ashxj-thinking.js +3 -3
  7. package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
  8. package/dist/builtin-extensions/ashxj-tui.d.ts +2 -2
  9. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  10. package/dist/builtin-extensions/ashxj-tui.js +2 -2
  11. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  12. package/dist/builtin-extensions/lunr-cron.d.ts +1 -1
  13. package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
  14. package/dist/builtin-extensions/lunr-cron.js.map +1 -1
  15. package/dist/builtin-extensions/lunr-local-providers/index.d.ts +1 -1
  16. package/dist/builtin-extensions/lunr-local-providers/index.d.ts.map +1 -1
  17. package/dist/builtin-extensions/lunr-local-providers/index.js.map +1 -1
  18. package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts +1 -1
  19. package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
  20. package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
  21. package/dist/builtin-extensions/lunr-plan-tools.d.ts +1 -1
  22. package/dist/builtin-extensions/lunr-plan-tools.d.ts.map +1 -1
  23. package/dist/builtin-extensions/lunr-plan-tools.js +1 -1
  24. package/dist/builtin-extensions/lunr-plan-tools.js.map +1 -1
  25. package/dist/builtin-extensions/lunr-skill-creator/index.d.ts +1 -1
  26. package/dist/builtin-extensions/lunr-skill-creator/index.d.ts.map +1 -1
  27. package/dist/builtin-extensions/lunr-skill-creator/index.js.map +1 -1
  28. package/dist/builtin-extensions/lunr-todos.d.ts +1 -1
  29. package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
  30. package/dist/builtin-extensions/lunr-todos.js +1 -1
  31. package/dist/builtin-extensions/lunr-todos.js.map +1 -1
  32. package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.d.ts.map +1 -1
  33. package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js.map +1 -1
  34. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts +1 -1
  35. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
  36. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
  37. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts +2 -2
  38. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
  39. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
  40. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
  41. package/dist/builtin-extensions/pi-intercom/index.d.ts +1 -1
  42. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  43. package/dist/builtin-extensions/pi-intercom/index.js +2 -2
  44. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  45. package/dist/builtin-extensions/pi-intercom/ui/compose.d.ts +2 -2
  46. package/dist/builtin-extensions/pi-intercom/ui/compose.d.ts.map +1 -1
  47. package/dist/builtin-extensions/pi-intercom/ui/compose.js +1 -1
  48. package/dist/builtin-extensions/pi-intercom/ui/compose.js.map +1 -1
  49. package/dist/builtin-extensions/pi-intercom/ui/inline-message.d.ts +2 -2
  50. package/dist/builtin-extensions/pi-intercom/ui/inline-message.d.ts.map +1 -1
  51. package/dist/builtin-extensions/pi-intercom/ui/inline-message.js +1 -1
  52. package/dist/builtin-extensions/pi-intercom/ui/inline-message.js.map +1 -1
  53. package/dist/builtin-extensions/pi-intercom/ui/session-list.d.ts +2 -2
  54. package/dist/builtin-extensions/pi-intercom/ui/session-list.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-intercom/ui/session-list.js +1 -1
  56. package/dist/builtin-extensions/pi-intercom/ui/session-list.js.map +1 -1
  57. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
  58. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
  60. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts +1 -1
  61. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
  62. package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -1
  63. package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
  64. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-actions.d.ts +1 -1
  65. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-actions.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-actions.js +1 -1
  67. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-actions.js.map +1 -1
  68. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-overview.d.ts +1 -1
  69. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-overview.d.ts.map +1 -1
  70. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-overview.js +1 -1
  71. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-overview.js.map +1 -1
  72. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +1 -1
  73. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
  74. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
  75. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-search.d.ts +1 -1
  76. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-search.d.ts.map +1 -1
  77. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-search.js.map +1 -1
  78. package/dist/builtin-extensions/pi-lsp-extension/src/tools/completions.d.ts +1 -1
  79. package/dist/builtin-extensions/pi-lsp-extension/src/tools/completions.d.ts.map +1 -1
  80. package/dist/builtin-extensions/pi-lsp-extension/src/tools/completions.js +1 -1
  81. package/dist/builtin-extensions/pi-lsp-extension/src/tools/completions.js.map +1 -1
  82. package/dist/builtin-extensions/pi-lsp-extension/src/tools/definition.d.ts +1 -1
  83. package/dist/builtin-extensions/pi-lsp-extension/src/tools/definition.d.ts.map +1 -1
  84. package/dist/builtin-extensions/pi-lsp-extension/src/tools/definition.js +1 -1
  85. package/dist/builtin-extensions/pi-lsp-extension/src/tools/definition.js.map +1 -1
  86. package/dist/builtin-extensions/pi-lsp-extension/src/tools/diagnostics.d.ts +1 -1
  87. package/dist/builtin-extensions/pi-lsp-extension/src/tools/diagnostics.d.ts.map +1 -1
  88. package/dist/builtin-extensions/pi-lsp-extension/src/tools/diagnostics.js +1 -1
  89. package/dist/builtin-extensions/pi-lsp-extension/src/tools/diagnostics.js.map +1 -1
  90. package/dist/builtin-extensions/pi-lsp-extension/src/tools/hover.d.ts +1 -1
  91. package/dist/builtin-extensions/pi-lsp-extension/src/tools/hover.d.ts.map +1 -1
  92. package/dist/builtin-extensions/pi-lsp-extension/src/tools/hover.js +1 -1
  93. package/dist/builtin-extensions/pi-lsp-extension/src/tools/hover.js.map +1 -1
  94. package/dist/builtin-extensions/pi-lsp-extension/src/tools/references.d.ts +1 -1
  95. package/dist/builtin-extensions/pi-lsp-extension/src/tools/references.d.ts.map +1 -1
  96. package/dist/builtin-extensions/pi-lsp-extension/src/tools/references.js +1 -1
  97. package/dist/builtin-extensions/pi-lsp-extension/src/tools/references.js.map +1 -1
  98. package/dist/builtin-extensions/pi-lsp-extension/src/tools/rename.d.ts +1 -1
  99. package/dist/builtin-extensions/pi-lsp-extension/src/tools/rename.d.ts.map +1 -1
  100. package/dist/builtin-extensions/pi-lsp-extension/src/tools/rename.js +1 -1
  101. package/dist/builtin-extensions/pi-lsp-extension/src/tools/rename.js.map +1 -1
  102. package/dist/builtin-extensions/pi-lsp-extension/src/tools/symbols.d.ts +1 -1
  103. package/dist/builtin-extensions/pi-lsp-extension/src/tools/symbols.d.ts.map +1 -1
  104. package/dist/builtin-extensions/pi-lsp-extension/src/tools/symbols.js +1 -1
  105. package/dist/builtin-extensions/pi-lsp-extension/src/tools/symbols.js.map +1 -1
  106. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +1 -1
  107. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
  108. package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
  109. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts +1 -1
  110. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts.map +1 -1
  111. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js.map +1 -1
  112. package/dist/builtin-extensions/pi-mcp-adapter/elicitation-handler.d.ts +1 -1
  113. package/dist/builtin-extensions/pi-mcp-adapter/elicitation-handler.d.ts.map +1 -1
  114. package/dist/builtin-extensions/pi-mcp-adapter/elicitation-handler.js.map +1 -1
  115. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts +1 -1
  116. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  117. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  118. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts +1 -1
  119. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
  120. package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
  121. package/dist/builtin-extensions/pi-mcp-adapter/mcp-panel.d.ts.map +1 -1
  122. package/dist/builtin-extensions/pi-mcp-adapter/mcp-panel.js +1 -1
  123. package/dist/builtin-extensions/pi-mcp-adapter/mcp-panel.js.map +1 -1
  124. package/dist/builtin-extensions/pi-mcp-adapter/mcp-setup-panel.d.ts.map +1 -1
  125. package/dist/builtin-extensions/pi-mcp-adapter/mcp-setup-panel.js +1 -1
  126. package/dist/builtin-extensions/pi-mcp-adapter/mcp-setup-panel.js.map +1 -1
  127. package/dist/builtin-extensions/pi-mcp-adapter/panel-keys.d.ts.map +1 -1
  128. package/dist/builtin-extensions/pi-mcp-adapter/panel-keys.js +1 -1
  129. package/dist/builtin-extensions/pi-mcp-adapter/panel-keys.js.map +1 -1
  130. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts +1 -1
  131. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
  132. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
  133. package/dist/builtin-extensions/pi-mcp-adapter/sampling-handler.d.ts +2 -2
  134. package/dist/builtin-extensions/pi-mcp-adapter/sampling-handler.d.ts.map +1 -1
  135. package/dist/builtin-extensions/pi-mcp-adapter/sampling-handler.js +1 -1
  136. package/dist/builtin-extensions/pi-mcp-adapter/sampling-handler.js.map +1 -1
  137. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +1 -1
  138. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
  139. package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
  140. package/dist/builtin-extensions/pi-mcp-adapter/tool-result-renderer.d.ts +2 -2
  141. package/dist/builtin-extensions/pi-mcp-adapter/tool-result-renderer.d.ts.map +1 -1
  142. package/dist/builtin-extensions/pi-mcp-adapter/tool-result-renderer.js +1 -1
  143. package/dist/builtin-extensions/pi-mcp-adapter/tool-result-renderer.js.map +1 -1
  144. package/dist/builtin-extensions/pi-mcp-adapter/types.d.ts +1 -1
  145. package/dist/builtin-extensions/pi-mcp-adapter/types.d.ts.map +1 -1
  146. package/dist/builtin-extensions/pi-mcp-adapter/types.js.map +1 -1
  147. package/dist/builtin-extensions/pi-mcp-adapter/utils.d.ts +1 -1
  148. package/dist/builtin-extensions/pi-mcp-adapter/utils.d.ts.map +1 -1
  149. package/dist/builtin-extensions/pi-mcp-adapter/utils.js.map +1 -1
  150. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
  151. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
  152. package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
  153. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +1 -1
  154. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
  155. package/dist/builtin-extensions/pi-ollama-cloud/models.generated.d.ts +1 -1
  156. package/dist/builtin-extensions/pi-ollama-cloud/models.generated.d.ts.map +1 -1
  157. package/dist/builtin-extensions/pi-ollama-cloud/models.generated.js.map +1 -1
  158. package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
  159. package/dist/builtin-extensions/pi-ollama-cloud/thinking-levels.d.ts +1 -1
  160. package/dist/builtin-extensions/pi-ollama-cloud/thinking-levels.d.ts.map +1 -1
  161. package/dist/builtin-extensions/pi-ollama-cloud/thinking-levels.js.map +1 -1
  162. package/dist/builtin-extensions/pi-prompt-template-model/deterministic-renderer.d.ts +2 -2
  163. package/dist/builtin-extensions/pi-prompt-template-model/deterministic-renderer.d.ts.map +1 -1
  164. package/dist/builtin-extensions/pi-prompt-template-model/deterministic-renderer.js +1 -1
  165. package/dist/builtin-extensions/pi-prompt-template-model/deterministic-renderer.js.map +1 -1
  166. package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts +1 -1
  167. package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
  168. package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
  169. package/dist/builtin-extensions/pi-prompt-template-model/loop-utils.d.ts +1 -1
  170. package/dist/builtin-extensions/pi-prompt-template-model/loop-utils.d.ts.map +1 -1
  171. package/dist/builtin-extensions/pi-prompt-template-model/loop-utils.js.map +1 -1
  172. package/dist/builtin-extensions/pi-prompt-template-model/model-selection.d.ts +1 -1
  173. package/dist/builtin-extensions/pi-prompt-template-model/model-selection.d.ts.map +1 -1
  174. package/dist/builtin-extensions/pi-prompt-template-model/model-selection.js.map +1 -1
  175. package/dist/builtin-extensions/pi-prompt-template-model/notifications.d.ts +1 -1
  176. package/dist/builtin-extensions/pi-prompt-template-model/notifications.d.ts.map +1 -1
  177. package/dist/builtin-extensions/pi-prompt-template-model/notifications.js.map +1 -1
  178. package/dist/builtin-extensions/pi-prompt-template-model/prompt-execution.d.ts +1 -1
  179. package/dist/builtin-extensions/pi-prompt-template-model/prompt-execution.d.ts.map +1 -1
  180. package/dist/builtin-extensions/pi-prompt-template-model/prompt-execution.js.map +1 -1
  181. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +1 -1
  182. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
  183. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +1 -1
  184. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
  185. package/dist/builtin-extensions/pi-prompt-template-model/skill-loaded-renderer.d.ts +2 -2
  186. package/dist/builtin-extensions/pi-prompt-template-model/skill-loaded-renderer.d.ts.map +1 -1
  187. package/dist/builtin-extensions/pi-prompt-template-model/skill-loaded-renderer.js +1 -1
  188. package/dist/builtin-extensions/pi-prompt-template-model/skill-loaded-renderer.js.map +1 -1
  189. package/dist/builtin-extensions/pi-prompt-template-model/subagent-renderer.d.ts +2 -2
  190. package/dist/builtin-extensions/pi-prompt-template-model/subagent-renderer.d.ts.map +1 -1
  191. package/dist/builtin-extensions/pi-prompt-template-model/subagent-renderer.js +1 -1
  192. package/dist/builtin-extensions/pi-prompt-template-model/subagent-renderer.js.map +1 -1
  193. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.d.ts +3 -3
  194. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.d.ts.map +1 -1
  195. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js +1 -1
  196. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js.map +1 -1
  197. package/dist/builtin-extensions/pi-prompt-template-model/subagent-widget.d.ts +2 -2
  198. package/dist/builtin-extensions/pi-prompt-template-model/subagent-widget.d.ts.map +1 -1
  199. package/dist/builtin-extensions/pi-prompt-template-model/subagent-widget.js +1 -1
  200. package/dist/builtin-extensions/pi-prompt-template-model/subagent-widget.js.map +1 -1
  201. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts +1 -1
  202. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
  203. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +1 -1
  204. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
  205. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.d.ts +2 -2
  206. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.d.ts.map +1 -1
  207. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.js.map +1 -1
  208. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.d.ts +1 -1
  209. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.d.ts.map +1 -1
  210. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js.map +1 -1
  211. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts +1 -1
  212. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts.map +1 -1
  213. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js.map +1 -1
  214. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +1 -1
  215. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  216. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +2 -2
  217. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  218. package/dist/builtin-extensions/pi-subagents/src/extension/rpc.d.ts +2 -2
  219. package/dist/builtin-extensions/pi-subagents/src/extension/rpc.d.ts.map +1 -1
  220. package/dist/builtin-extensions/pi-subagents/src/extension/rpc.js.map +1 -1
  221. package/dist/builtin-extensions/pi-subagents/src/extension/steering-notices.d.ts +1 -1
  222. package/dist/builtin-extensions/pi-subagents/src/extension/steering-notices.d.ts.map +1 -1
  223. package/dist/builtin-extensions/pi-subagents/src/extension/steering-notices.js.map +1 -1
  224. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +1 -1
  225. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  226. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
  227. package/dist/builtin-extensions/pi-subagents/src/profiles/profiles.d.ts +1 -1
  228. package/dist/builtin-extensions/pi-subagents/src/profiles/profiles.d.ts.map +1 -1
  229. package/dist/builtin-extensions/pi-subagents/src/profiles/profiles.js.map +1 -1
  230. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +1 -1
  231. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  232. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  233. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts +1 -1
  234. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
  235. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
  236. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.d.ts +1 -1
  237. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.d.ts.map +1 -1
  238. package/dist/builtin-extensions/pi-subagents/src/runs/background/auto-drain.js.map +1 -1
  239. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.d.ts +1 -1
  240. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.d.ts.map +1 -1
  241. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js.map +1 -1
  242. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -1
  243. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  244. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  245. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.d.ts +1 -1
  246. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.d.ts.map +1 -1
  247. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js.map +1 -1
  248. package/dist/builtin-extensions/pi-subagents/src/runs/background/scheduled-runs.d.ts +2 -2
  249. package/dist/builtin-extensions/pi-subagents/src/runs/background/scheduled-runs.d.ts.map +1 -1
  250. package/dist/builtin-extensions/pi-subagents/src/runs/background/scheduled-runs.js.map +1 -1
  251. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  252. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  253. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts +1 -1
  254. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts.map +1 -1
  255. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js.map +1 -1
  256. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts +1 -1
  257. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
  258. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
  259. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/async-steering-action.d.ts +1 -1
  260. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/async-steering-action.d.ts.map +1 -1
  261. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/async-steering-action.js.map +1 -1
  262. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.d.ts +2 -2
  263. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.d.ts.map +1 -1
  264. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js +1 -1
  265. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js.map +1 -1
  266. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts +2 -2
  267. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  268. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  269. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  270. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  271. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +2 -2
  272. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  273. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  274. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts +1 -1
  275. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts.map +1 -1
  276. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js.map +1 -1
  277. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-spawn.d.ts +1 -1
  278. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-spawn.d.ts.map +1 -1
  279. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-spawn.js +1 -1
  280. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-spawn.js.map +1 -1
  281. package/dist/builtin-extensions/pi-subagents/src/runs/shared/single-output.d.ts +1 -1
  282. package/dist/builtin-extensions/pi-subagents/src/runs/shared/single-output.d.ts.map +1 -1
  283. package/dist/builtin-extensions/pi-subagents/src/runs/shared/single-output.js.map +1 -1
  284. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts +1 -1
  285. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
  286. package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
  287. package/dist/builtin-extensions/pi-subagents/src/shared/child-transcript.d.ts +1 -1
  288. package/dist/builtin-extensions/pi-subagents/src/shared/child-transcript.d.ts.map +1 -1
  289. package/dist/builtin-extensions/pi-subagents/src/shared/child-transcript.js.map +1 -1
  290. package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.d.ts.map +1 -1
  291. package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js +1 -1
  292. package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js.map +1 -1
  293. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +3 -3
  294. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  295. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  296. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
  297. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
  298. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +1 -1
  299. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
  300. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +1 -1
  301. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  302. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  303. package/dist/builtin-extensions/pi-subagents/src/slash/slash-bridge.d.ts +2 -2
  304. package/dist/builtin-extensions/pi-subagents/src/slash/slash-bridge.d.ts.map +1 -1
  305. package/dist/builtin-extensions/pi-subagents/src/slash/slash-bridge.js.map +1 -1
  306. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
  307. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  308. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +2 -2
  309. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  310. package/dist/builtin-extensions/pi-subagents/src/slash/slash-live-state.d.ts +1 -1
  311. package/dist/builtin-extensions/pi-subagents/src/slash/slash-live-state.d.ts.map +1 -1
  312. package/dist/builtin-extensions/pi-subagents/src/slash/slash-live-state.js.map +1 -1
  313. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts +2 -2
  314. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts.map +1 -1
  315. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js +1 -1
  316. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js.map +1 -1
  317. package/dist/builtin-extensions/pi-subagents/src/tui/render-helpers.d.ts +1 -1
  318. package/dist/builtin-extensions/pi-subagents/src/tui/render-helpers.d.ts.map +1 -1
  319. package/dist/builtin-extensions/pi-subagents/src/tui/render-helpers.js +1 -1
  320. package/dist/builtin-extensions/pi-subagents/src/tui/render-helpers.js.map +1 -1
  321. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +3 -3
  322. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  323. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +2 -2
  324. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  325. package/dist/builtin-extensions/pi-subagents/src/watchdog/model-selection.d.ts +1 -1
  326. package/dist/builtin-extensions/pi-subagents/src/watchdog/model-selection.d.ts.map +1 -1
  327. package/dist/builtin-extensions/pi-subagents/src/watchdog/model-selection.js.map +1 -1
  328. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-child.d.ts +1 -1
  329. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-child.d.ts.map +1 -1
  330. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-child.js.map +1 -1
  331. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.d.ts +1 -1
  332. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.d.ts.map +1 -1
  333. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js +1 -1
  334. package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js.map +1 -1
  335. package/dist/builtin-extensions/pi-subagents/src/watchdog/render.d.ts +1 -1
  336. package/dist/builtin-extensions/pi-subagents/src/watchdog/render.d.ts.map +1 -1
  337. package/dist/builtin-extensions/pi-subagents/src/watchdog/render.js +1 -1
  338. package/dist/builtin-extensions/pi-subagents/src/watchdog/render.js.map +1 -1
  339. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts +3 -3
  340. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
  341. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
  342. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
  343. package/dist/builtin-extensions/pi-subagents/src/watchdog/runtime.d.ts +1 -1
  344. package/dist/builtin-extensions/pi-subagents/src/watchdog/runtime.d.ts.map +1 -1
  345. package/dist/builtin-extensions/pi-subagents/src/watchdog/runtime.js.map +1 -1
  346. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts +2 -2
  347. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts.map +1 -1
  348. package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js.map +1 -1
  349. package/dist/builtin-extensions/pi-tps.d.ts +1 -1
  350. package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
  351. package/dist/builtin-extensions/pi-tps.js.map +1 -1
  352. package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts +1 -1
  353. package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts.map +1 -1
  354. package/dist/builtin-extensions/pi-web-access/gemini-search.js.map +1 -1
  355. package/dist/builtin-extensions/pi-web-access/index.d.ts +1 -1
  356. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  357. package/dist/builtin-extensions/pi-web-access/index.js +2 -2
  358. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  359. package/dist/builtin-extensions/pi-web-access/openai-search.d.ts +1 -1
  360. package/dist/builtin-extensions/pi-web-access/openai-search.d.ts.map +1 -1
  361. package/dist/builtin-extensions/pi-web-access/openai-search.js +1 -1
  362. package/dist/builtin-extensions/pi-web-access/openai-search.js.map +1 -1
  363. package/dist/builtin-extensions/pi-web-access/storage.d.ts +1 -1
  364. package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
  365. package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
  366. package/dist/builtin-extensions/pi-web-access/summary-review.d.ts +1 -1
  367. package/dist/builtin-extensions/pi-web-access/summary-review.d.ts.map +1 -1
  368. package/dist/builtin-extensions/pi-web-access/summary-review.js +1 -1
  369. package/dist/builtin-extensions/pi-web-access/summary-review.js.map +1 -1
  370. package/dist/bun/cli.d.ts.map +1 -1
  371. package/dist/bun/cli.js +1 -1
  372. package/dist/bun/cli.js.map +1 -1
  373. package/dist/bun/register-bedrock.d.ts.map +1 -1
  374. package/dist/bun/register-bedrock.js +2 -2
  375. package/dist/bun/register-bedrock.js.map +1 -1
  376. package/dist/cli/args.d.ts +1 -1
  377. package/dist/cli/args.d.ts.map +1 -1
  378. package/dist/cli/args.js.map +1 -1
  379. package/dist/cli/config-selector.d.ts.map +1 -1
  380. package/dist/cli/config-selector.js +1 -1
  381. package/dist/cli/config-selector.js.map +1 -1
  382. package/dist/cli/file-processor.d.ts +1 -1
  383. package/dist/cli/file-processor.d.ts.map +1 -1
  384. package/dist/cli/file-processor.js.map +1 -1
  385. package/dist/cli/initial-message.d.ts +1 -1
  386. package/dist/cli/initial-message.d.ts.map +1 -1
  387. package/dist/cli/initial-message.js.map +1 -1
  388. package/dist/cli/list-models.d.ts.map +1 -1
  389. package/dist/cli/list-models.js +1 -1
  390. package/dist/cli/list-models.js.map +1 -1
  391. package/dist/cli/session-picker.d.ts.map +1 -1
  392. package/dist/cli/session-picker.js +1 -1
  393. package/dist/cli/session-picker.js.map +1 -1
  394. package/dist/cli/startup-ui.d.ts +1 -1
  395. package/dist/cli/startup-ui.d.ts.map +1 -1
  396. package/dist/cli/startup-ui.js +2 -2
  397. package/dist/cli/startup-ui.js.map +1 -1
  398. package/dist/config.d.ts.map +1 -1
  399. package/dist/config.js +1 -1
  400. package/dist/config.js.map +1 -1
  401. package/dist/core/agent-session-services.d.ts +2 -2
  402. package/dist/core/agent-session-services.d.ts.map +1 -1
  403. package/dist/core/agent-session-services.js.map +1 -1
  404. package/dist/core/agent-session.d.ts +2 -2
  405. package/dist/core/agent-session.d.ts.map +1 -1
  406. package/dist/core/agent-session.js +1 -1
  407. package/dist/core/agent-session.js.map +1 -1
  408. package/dist/core/auth-storage.d.ts +1 -1
  409. package/dist/core/auth-storage.d.ts.map +1 -1
  410. package/dist/core/auth-storage.js.map +1 -1
  411. package/dist/core/cache-stats.d.ts +1 -1
  412. package/dist/core/cache-stats.d.ts.map +1 -1
  413. package/dist/core/cache-stats.js.map +1 -1
  414. package/dist/core/catalog-merge.d.ts +1 -1
  415. package/dist/core/catalog-merge.d.ts.map +1 -1
  416. package/dist/core/catalog-merge.js.map +1 -1
  417. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  418. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  419. package/dist/core/compaction/branch-summarization.js +1 -1
  420. package/dist/core/compaction/branch-summarization.js.map +1 -1
  421. package/dist/core/compaction/compaction.d.ts +2 -2
  422. package/dist/core/compaction/compaction.d.ts.map +1 -1
  423. package/dist/core/compaction/compaction.js +1 -1
  424. package/dist/core/compaction/compaction.js.map +1 -1
  425. package/dist/core/compaction/utils.d.ts +2 -2
  426. package/dist/core/compaction/utils.d.ts.map +1 -1
  427. package/dist/core/compaction/utils.js.map +1 -1
  428. package/dist/core/context-breakdown.d.ts +1 -1
  429. package/dist/core/context-breakdown.d.ts.map +1 -1
  430. package/dist/core/context-breakdown.js.map +1 -1
  431. package/dist/core/defaults.d.ts +1 -1
  432. package/dist/core/defaults.d.ts.map +1 -1
  433. package/dist/core/defaults.js.map +1 -1
  434. package/dist/core/export-html/index.d.ts +1 -1
  435. package/dist/core/export-html/index.d.ts.map +1 -1
  436. package/dist/core/export-html/index.js.map +1 -1
  437. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  438. package/dist/core/export-html/tool-renderer.js.map +1 -1
  439. package/dist/core/extensions/loader.d.ts.map +1 -1
  440. package/dist/core/extensions/loader.js +25 -25
  441. package/dist/core/extensions/loader.js.map +1 -1
  442. package/dist/core/extensions/runner.d.ts +3 -3
  443. package/dist/core/extensions/runner.d.ts.map +1 -1
  444. package/dist/core/extensions/runner.js.map +1 -1
  445. package/dist/core/extensions/types.d.ts +5 -5
  446. package/dist/core/extensions/types.d.ts.map +1 -1
  447. package/dist/core/extensions/types.js.map +1 -1
  448. package/dist/core/extensions/wrapper.d.ts +1 -1
  449. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  450. package/dist/core/extensions/wrapper.js.map +1 -1
  451. package/dist/core/keybindings.d.ts +2 -2
  452. package/dist/core/keybindings.d.ts.map +1 -1
  453. package/dist/core/keybindings.js +1 -1
  454. package/dist/core/keybindings.js.map +1 -1
  455. package/dist/core/live-catalog.d.ts +1 -1
  456. package/dist/core/live-catalog.d.ts.map +1 -1
  457. package/dist/core/live-catalog.js.map +1 -1
  458. package/dist/core/messages.d.ts +3 -3
  459. package/dist/core/messages.d.ts.map +1 -1
  460. package/dist/core/messages.js.map +1 -1
  461. package/dist/core/model-registry.d.ts +1 -1
  462. package/dist/core/model-registry.d.ts.map +1 -1
  463. package/dist/core/model-registry.js.map +1 -1
  464. package/dist/core/model-resolver.d.ts +2 -2
  465. package/dist/core/model-resolver.d.ts.map +1 -1
  466. package/dist/core/model-resolver.js +1 -1
  467. package/dist/core/model-resolver.js.map +1 -1
  468. package/dist/core/model-runtime.d.ts +1 -1
  469. package/dist/core/model-runtime.d.ts.map +1 -1
  470. package/dist/core/model-runtime.js +2 -2
  471. package/dist/core/model-runtime.js.map +1 -1
  472. package/dist/core/models-store.d.ts +1 -1
  473. package/dist/core/models-store.d.ts.map +1 -1
  474. package/dist/core/models-store.js.map +1 -1
  475. package/dist/core/official-catalog.d.ts +1 -1
  476. package/dist/core/official-catalog.d.ts.map +1 -1
  477. package/dist/core/official-catalog.js.map +1 -1
  478. package/dist/core/provider-attribution.d.ts +1 -1
  479. package/dist/core/provider-attribution.d.ts.map +1 -1
  480. package/dist/core/provider-attribution.js.map +1 -1
  481. package/dist/core/provider-composer.d.ts +1 -1
  482. package/dist/core/provider-composer.d.ts.map +1 -1
  483. package/dist/core/provider-composer.js +2 -2
  484. package/dist/core/provider-composer.js.map +1 -1
  485. package/dist/core/runtime-credentials.d.ts +1 -1
  486. package/dist/core/runtime-credentials.d.ts.map +1 -1
  487. package/dist/core/runtime-credentials.js.map +1 -1
  488. package/dist/core/sdk.d.ts +3 -3
  489. package/dist/core/sdk.d.ts.map +1 -1
  490. package/dist/core/sdk.js +3 -3
  491. package/dist/core/sdk.js.map +1 -1
  492. package/dist/core/session-manager.d.ts +2 -2
  493. package/dist/core/session-manager.d.ts.map +1 -1
  494. package/dist/core/session-manager.js +1 -1
  495. package/dist/core/session-manager.js.map +1 -1
  496. package/dist/core/settings-manager.d.ts +2 -2
  497. package/dist/core/settings-manager.d.ts.map +1 -1
  498. package/dist/core/settings-manager.js.map +1 -1
  499. package/dist/core/subscriptions.d.ts +1 -1
  500. package/dist/core/subscriptions.d.ts.map +1 -1
  501. package/dist/core/subscriptions.js.map +1 -1
  502. package/dist/core/tools/bash.d.ts +1 -1
  503. package/dist/core/tools/bash.d.ts.map +1 -1
  504. package/dist/core/tools/bash.js +1 -1
  505. package/dist/core/tools/bash.js.map +1 -1
  506. package/dist/core/tools/edit.d.ts +2 -2
  507. package/dist/core/tools/edit.d.ts.map +1 -1
  508. package/dist/core/tools/edit.js +1 -1
  509. package/dist/core/tools/edit.js.map +1 -1
  510. package/dist/core/tools/find.d.ts +1 -1
  511. package/dist/core/tools/find.d.ts.map +1 -1
  512. package/dist/core/tools/find.js +1 -1
  513. package/dist/core/tools/find.js.map +1 -1
  514. package/dist/core/tools/grep.d.ts +1 -1
  515. package/dist/core/tools/grep.d.ts.map +1 -1
  516. package/dist/core/tools/grep.js +1 -1
  517. package/dist/core/tools/grep.js.map +1 -1
  518. package/dist/core/tools/index.d.ts +1 -1
  519. package/dist/core/tools/index.d.ts.map +1 -1
  520. package/dist/core/tools/index.js.map +1 -1
  521. package/dist/core/tools/ls.d.ts +1 -1
  522. package/dist/core/tools/ls.d.ts.map +1 -1
  523. package/dist/core/tools/ls.js +1 -1
  524. package/dist/core/tools/ls.js.map +1 -1
  525. package/dist/core/tools/read.d.ts +1 -1
  526. package/dist/core/tools/read.d.ts.map +1 -1
  527. package/dist/core/tools/read.js +1 -1
  528. package/dist/core/tools/read.js.map +1 -1
  529. package/dist/core/tools/render-utils.d.ts +1 -1
  530. package/dist/core/tools/render-utils.d.ts.map +1 -1
  531. package/dist/core/tools/render-utils.js +1 -1
  532. package/dist/core/tools/render-utils.js.map +1 -1
  533. package/dist/core/tools/tool-definition-wrapper.d.ts +1 -1
  534. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  535. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  536. package/dist/core/tools/write.d.ts +1 -1
  537. package/dist/core/tools/write.d.ts.map +1 -1
  538. package/dist/core/tools/write.js +1 -1
  539. package/dist/core/tools/write.js.map +1 -1
  540. package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
  541. package/dist/core/usage-adapters/openai-codex.js.map +1 -1
  542. package/dist/core/usage-history.d.ts.map +1 -1
  543. package/dist/core/usage-history.js.map +1 -1
  544. package/dist/core/user-models.d.ts +1 -1
  545. package/dist/core/user-models.d.ts.map +1 -1
  546. package/dist/core/user-models.js.map +1 -1
  547. package/dist/gateway/agent-bridge.d.ts +3 -3
  548. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  549. package/dist/gateway/agent-bridge.js.map +1 -1
  550. package/dist/gateway/commands.d.ts.map +1 -1
  551. package/dist/gateway/commands.js.map +1 -1
  552. package/dist/gateway/cron.d.ts.map +1 -1
  553. package/dist/gateway/cron.js.map +1 -1
  554. package/dist/main.d.ts.map +1 -1
  555. package/dist/main.js +1 -1
  556. package/dist/main.js.map +1 -1
  557. package/dist/modes/interactive/components/armin.d.ts +1 -1
  558. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  559. package/dist/modes/interactive/components/armin.js.map +1 -1
  560. package/dist/modes/interactive/components/assistant-message.d.ts +2 -2
  561. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  562. package/dist/modes/interactive/components/assistant-message.js +1 -1
  563. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  564. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  565. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  566. package/dist/modes/interactive/components/bash-execution.js +1 -1
  567. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  568. package/dist/modes/interactive/components/boot-screen.d.ts +1 -1
  569. package/dist/modes/interactive/components/boot-screen.d.ts.map +1 -1
  570. package/dist/modes/interactive/components/boot-screen.js +1 -1
  571. package/dist/modes/interactive/components/boot-screen.js.map +1 -1
  572. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  573. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  574. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  575. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  576. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  577. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  578. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  579. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  580. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  581. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  582. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  583. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  584. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  585. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  586. package/dist/modes/interactive/components/config-selector.js +1 -1
  587. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  588. package/dist/modes/interactive/components/countdown-timer.d.ts +1 -1
  589. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  590. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  591. package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
  592. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  593. package/dist/modes/interactive/components/custom-editor.js +1 -1
  594. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  595. package/dist/modes/interactive/components/custom-entry.d.ts +1 -1
  596. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  597. package/dist/modes/interactive/components/custom-entry.js +1 -1
  598. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  599. package/dist/modes/interactive/components/custom-message.d.ts +1 -1
  600. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  601. package/dist/modes/interactive/components/custom-message.js +1 -1
  602. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  603. package/dist/modes/interactive/components/daxnuts.d.ts +1 -1
  604. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  605. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  606. package/dist/modes/interactive/components/dynamic-border.d.ts +1 -1
  607. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  608. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  609. package/dist/modes/interactive/components/earendil-announcement.d.ts +1 -1
  610. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  611. package/dist/modes/interactive/components/earendil-announcement.js +1 -1
  612. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  613. package/dist/modes/interactive/components/extension-editor.d.ts +1 -1
  614. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  615. package/dist/modes/interactive/components/extension-editor.js +1 -1
  616. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  617. package/dist/modes/interactive/components/extension-input.d.ts +1 -1
  618. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  619. package/dist/modes/interactive/components/extension-input.js +1 -1
  620. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  621. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  622. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  623. package/dist/modes/interactive/components/extension-selector.js +1 -1
  624. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  625. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  626. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  627. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  628. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  629. package/dist/modes/interactive/components/footer.d.ts +1 -1
  630. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  631. package/dist/modes/interactive/components/footer.js +1 -1
  632. package/dist/modes/interactive/components/footer.js.map +1 -1
  633. package/dist/modes/interactive/components/injected-prompt-message.d.ts +1 -1
  634. package/dist/modes/interactive/components/injected-prompt-message.d.ts.map +1 -1
  635. package/dist/modes/interactive/components/injected-prompt-message.js +1 -1
  636. package/dist/modes/interactive/components/injected-prompt-message.js.map +1 -1
  637. package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -1
  638. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  639. package/dist/modes/interactive/components/keybinding-hints.js +1 -1
  640. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  641. package/dist/modes/interactive/components/login-dialog.d.ts +2 -2
  642. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  643. package/dist/modes/interactive/components/login-dialog.js +1 -1
  644. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  645. package/dist/modes/interactive/components/model-selector.d.ts +2 -2
  646. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  647. package/dist/modes/interactive/components/model-selector.js +2 -2
  648. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  649. package/dist/modes/interactive/components/oauth-selector.d.ts +2 -2
  650. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  651. package/dist/modes/interactive/components/oauth-selector.js +1 -1
  652. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  653. package/dist/modes/interactive/components/processes-selector.d.ts +1 -1
  654. package/dist/modes/interactive/components/processes-selector.d.ts.map +1 -1
  655. package/dist/modes/interactive/components/processes-selector.js +1 -1
  656. package/dist/modes/interactive/components/processes-selector.js.map +1 -1
  657. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  658. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  659. package/dist/modes/interactive/components/scoped-models-selector.js +1 -1
  660. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  661. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  662. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  663. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  664. package/dist/modes/interactive/components/session-selector.d.ts +1 -1
  665. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  666. package/dist/modes/interactive/components/session-selector.js +1 -1
  667. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  668. package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
  669. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  670. package/dist/modes/interactive/components/settings-selector.js +1 -1
  671. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  672. package/dist/modes/interactive/components/show-images-selector.d.ts +1 -1
  673. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  674. package/dist/modes/interactive/components/show-images-selector.js +1 -1
  675. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  676. package/dist/modes/interactive/components/skill-invocation-message.d.ts +1 -1
  677. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  678. package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
  679. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  680. package/dist/modes/interactive/components/status-indicator.d.ts +1 -1
  681. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  682. package/dist/modes/interactive/components/status-indicator.js +1 -1
  683. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  684. package/dist/modes/interactive/components/theme-selector.d.ts +1 -1
  685. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  686. package/dist/modes/interactive/components/theme-selector.js +1 -1
  687. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  688. package/dist/modes/interactive/components/thinking-selector.d.ts +2 -2
  689. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  690. package/dist/modes/interactive/components/thinking-selector.js +1 -1
  691. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  692. package/dist/modes/interactive/components/thinking-summary.d.ts +1 -1
  693. package/dist/modes/interactive/components/thinking-summary.d.ts.map +1 -1
  694. package/dist/modes/interactive/components/thinking-summary.js.map +1 -1
  695. package/dist/modes/interactive/components/thinking-tail.d.ts +1 -1
  696. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  697. package/dist/modes/interactive/components/thinking-tail.js +1 -1
  698. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  699. package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
  700. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  701. package/dist/modes/interactive/components/tool-execution.js +1 -1
  702. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  703. package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
  704. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  705. package/dist/modes/interactive/components/tree-selector.js +1 -1
  706. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  707. package/dist/modes/interactive/components/trust-selector.d.ts +1 -1
  708. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  709. package/dist/modes/interactive/components/trust-selector.js +1 -1
  710. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  711. package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
  712. package/dist/modes/interactive/components/usage-view.js +1 -1
  713. package/dist/modes/interactive/components/usage-view.js.map +1 -1
  714. package/dist/modes/interactive/components/user-message-selector.d.ts +1 -1
  715. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  716. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  717. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  718. package/dist/modes/interactive/components/user-message.d.ts +1 -1
  719. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  720. package/dist/modes/interactive/components/user-message.js +1 -1
  721. package/dist/modes/interactive/components/user-message.js.map +1 -1
  722. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  723. package/dist/modes/interactive/components/visual-truncate.js +1 -1
  724. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  725. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  726. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  727. package/dist/modes/interactive/interactive-mode.js +1 -1
  728. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  729. package/dist/modes/interactive/smooth-streaming.d.ts +1 -1
  730. package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
  731. package/dist/modes/interactive/smooth-streaming.js.map +1 -1
  732. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -1
  733. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  734. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  735. package/dist/modes/interactive/theme/theme.d.ts +2 -2
  736. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  737. package/dist/modes/interactive/theme/theme.js +2 -2
  738. package/dist/modes/interactive/theme/theme.js.map +1 -1
  739. package/dist/modes/print-mode.d.ts +1 -1
  740. package/dist/modes/print-mode.d.ts.map +1 -1
  741. package/dist/modes/print-mode.js.map +1 -1
  742. package/dist/modes/rpc/rpc-client.d.ts +2 -2
  743. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  744. package/dist/modes/rpc/rpc-client.js.map +1 -1
  745. package/dist/modes/rpc/rpc-types.d.ts +2 -2
  746. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  747. package/dist/modes/rpc/rpc-types.js.map +1 -1
  748. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  749. package/examples/extensions/bash-spawn-hook.ts +2 -2
  750. package/examples/extensions/bookmark.ts +1 -1
  751. package/examples/extensions/border-status-editor.ts +3 -3
  752. package/examples/extensions/built-in-tool-renderer.ts +3 -3
  753. package/examples/extensions/claude-rules.ts +1 -1
  754. package/examples/extensions/commands.ts +1 -1
  755. package/examples/extensions/confirm-destructive.ts +1 -1
  756. package/examples/extensions/custom-compaction.ts +3 -3
  757. package/examples/extensions/custom-footer.ts +3 -3
  758. package/examples/extensions/custom-header.ts +2 -2
  759. package/examples/extensions/custom-provider-anthropic/index.ts +2 -2
  760. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  761. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  762. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  763. package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
  764. package/examples/extensions/dirty-repo-guard.ts +1 -1
  765. package/examples/extensions/doom-overlay/doom-component.ts +2 -2
  766. package/examples/extensions/doom-overlay/doom-keys.ts +1 -1
  767. package/examples/extensions/doom-overlay/index.ts +1 -1
  768. package/examples/extensions/dynamic-resources/index.ts +1 -1
  769. package/examples/extensions/dynamic-tools.ts +1 -1
  770. package/examples/extensions/entry-renderer.ts +2 -2
  771. package/examples/extensions/event-bus.ts +1 -1
  772. package/examples/extensions/file-trigger.ts +1 -1
  773. package/examples/extensions/git-checkpoint.ts +1 -1
  774. package/examples/extensions/git-merge-and-resolve.ts +1 -1
  775. package/examples/extensions/github-issue-autocomplete.ts +2 -2
  776. package/examples/extensions/gondolin/index.ts +2 -2
  777. package/examples/extensions/gondolin/package.json +1 -1
  778. package/examples/extensions/handoff.ts +4 -4
  779. package/examples/extensions/hello.ts +2 -2
  780. package/examples/extensions/hidden-thinking-label.ts +1 -1
  781. package/examples/extensions/inline-bash.ts +1 -1
  782. package/examples/extensions/input-transform-streaming.ts +1 -1
  783. package/examples/extensions/input-transform.ts +1 -1
  784. package/examples/extensions/interactive-shell.ts +1 -1
  785. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  786. package/examples/extensions/mac-system-theme.ts +1 -1
  787. package/examples/extensions/message-renderer.ts +2 -2
  788. package/examples/extensions/minimal-mode.ts +3 -3
  789. package/examples/extensions/modal-editor.ts +2 -2
  790. package/examples/extensions/model-status.ts +1 -1
  791. package/examples/extensions/notify.ts +1 -1
  792. package/examples/extensions/overlay-qa-tests.ts +3 -3
  793. package/examples/extensions/overlay-test.ts +2 -2
  794. package/examples/extensions/permission-gate.ts +1 -1
  795. package/examples/extensions/pirate.ts +1 -1
  796. package/examples/extensions/plan-mode/index.ts +4 -4
  797. package/examples/extensions/preset.ts +4 -4
  798. package/examples/extensions/project-trust.ts +1 -1
  799. package/examples/extensions/prompt-customizer.ts +1 -1
  800. package/examples/extensions/protected-paths.ts +1 -1
  801. package/examples/extensions/provider-payload.ts +1 -1
  802. package/examples/extensions/qna.ts +3 -3
  803. package/examples/extensions/question.ts +2 -2
  804. package/examples/extensions/questionnaire.ts +2 -2
  805. package/examples/extensions/rainbow-editor.ts +1 -1
  806. package/examples/extensions/reload-runtime.ts +1 -1
  807. package/examples/extensions/rpc-demo.ts +1 -1
  808. package/examples/extensions/sandbox/index.ts +2 -2
  809. package/examples/extensions/send-user-message.ts +1 -1
  810. package/examples/extensions/session-name.ts +1 -1
  811. package/examples/extensions/shutdown-command.ts +1 -1
  812. package/examples/extensions/snake.ts +2 -2
  813. package/examples/extensions/space-invaders.ts +2 -2
  814. package/examples/extensions/ssh.ts +2 -2
  815. package/examples/extensions/status-line.ts +1 -1
  816. package/examples/extensions/structured-output.ts +2 -2
  817. package/examples/extensions/subagent/agents.ts +1 -1
  818. package/examples/extensions/subagent/index.ts +5 -5
  819. package/examples/extensions/summarize.ts +4 -4
  820. package/examples/extensions/system-prompt-header.ts +1 -1
  821. package/examples/extensions/tic-tac-toe.ts +3 -3
  822. package/examples/extensions/timed-confirm.ts +1 -1
  823. package/examples/extensions/titlebar-spinner.ts +1 -1
  824. package/examples/extensions/todo.ts +3 -3
  825. package/examples/extensions/tool-override.ts +2 -2
  826. package/examples/extensions/tools.ts +3 -3
  827. package/examples/extensions/trigger-compact.ts +1 -1
  828. package/examples/extensions/truncated-tool.ts +3 -3
  829. package/examples/extensions/widget-placement.ts +1 -1
  830. package/examples/extensions/with-deps/index.ts +1 -1
  831. package/examples/extensions/with-deps/package.json +1 -1
  832. package/examples/extensions/working-indicator.ts +1 -1
  833. package/examples/extensions/working-message-test.ts +1 -1
  834. package/examples/rpc-extension-ui.ts +1 -1
  835. package/examples/sdk/01-minimal.ts +1 -1
  836. package/examples/sdk/02-custom-model.ts +2 -2
  837. package/examples/sdk/03-custom-prompt.ts +1 -1
  838. package/examples/sdk/04-skills.ts +1 -1
  839. package/examples/sdk/05-tools.ts +1 -1
  840. package/examples/sdk/06-extensions.ts +2 -2
  841. package/examples/sdk/07-context-files.ts +1 -1
  842. package/examples/sdk/08-prompt-templates.ts +1 -1
  843. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  844. package/examples/sdk/10-settings.ts +1 -1
  845. package/examples/sdk/11-sessions.ts +1 -1
  846. package/examples/sdk/12-full-control.ts +2 -2
  847. package/examples/sdk/13-session-runtime.ts +1 -1
  848. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"openai-codex.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/openai-codex.ts"],"names":[],"mappings":"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","sourcesContent":["// Vendored from narumiruna-pi-codex-usage (deleted in Phase 1 of the lunr-ux plan):\n// the pi-auth HTTP query path of `query.ts` plus `normalize.ts` and the backend\n// types from `types.ts`, consolidated into one core module. The codex app-server\n// fallback was dropped; extension-API imports (ExtensionContext, modelRegistry)\n// were rewired to core's ModelRuntime. No runtime dependency on the deleted\n// extension or on extension-repos remains.\n\nimport type { Api, AuthResult, Model } from \"@earendil-works/pi-ai\";\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow } from \"../usage-service.ts\";\nimport { asNumber, asObject, asString, fetchWithTimeout, toEpochMs } from \"./shared.ts\";\n\nconst CODEX_PROVIDER_ID = \"openai-codex\";\nconst CODEX_USAGE_URL = \"https://chatgpt.com/backend-api/wham/usage\";\nconst MAX_ERROR_BODY_CHARS = 600;\n\n// ---------------------------------------------------------------------------\n// Backend payload types (vendored from types.ts)\n// ---------------------------------------------------------------------------\n\ntype RateLimitStatusPayload = {\n\tplan_type?: unknown;\n\trate_limit?: unknown;\n\tadditional_rate_limits?: unknown;\n\tcredits?: unknown;\n\trate_limit_reset_credits?: unknown;\n};\n\ntype BackendRateLimitDetails = {\n\tprimary_window?: unknown;\n\tsecondary_window?: unknown;\n};\n\ntype BackendWindowSnapshot = {\n\tused_percent?: unknown;\n\tlimit_window_seconds?: unknown;\n\treset_at?: unknown;\n};\n\ntype BackendAdditionalRateLimit = {\n\tlimit_name?: unknown;\n\tmetered_feature?: unknown;\n\trate_limit?: unknown;\n};\n\ntype BackendCreditsSnapshot = {\n\thas_credits?: unknown;\n\tunlimited?: unknown;\n\tbalance?: unknown;\n};\n\ntype NormalizedWindow = {\n\tusedPercent: number;\n\twindowMinutes?: number;\n\tresetsAt?: number;\n};\n\ntype NormalizedSnapshot = {\n\tlimitId: string;\n\tlimitName?: string;\n\tprimary?: NormalizedWindow;\n\tsecondary?: NormalizedWindow;\n};\n\ntype CodexUsageReport = {\n\tplanType?: string;\n\tsnapshots: NormalizedSnapshot[];\n};\n\n// ---------------------------------------------------------------------------\n// Payload normalization (vendored from normalize.ts, backend path only)\n// ---------------------------------------------------------------------------\n\nfunction normalizeBackendPayload(payload: RateLimitStatusPayload): CodexUsageReport {\n\tconst snapshots: NormalizedSnapshot[] = [];\n\tconst planType = asString(payload.plan_type);\n\tconst primary = normalizeBackendSnapshot(\"codex\", undefined, payload.rate_limit, payload.credits);\n\tif (primary) snapshots.push(primary);\n\n\tconst additional = Array.isArray(payload.additional_rate_limits) ? payload.additional_rate_limits : [];\n\tfor (const item of additional) {\n\t\tconst additionalLimit = asObject(item) as BackendAdditionalRateLimit | undefined;\n\t\tif (!additionalLimit) continue;\n\t\tconst limitId = asString(additionalLimit.metered_feature) ?? asString(additionalLimit.limit_name);\n\t\tif (!limitId) continue;\n\t\ttry {\n\t\t\tconst snapshot = normalizeBackendSnapshot(\n\t\t\t\tlimitId,\n\t\t\t\tasString(additionalLimit.limit_name),\n\t\t\t\tadditionalLimit.rate_limit,\n\t\t\t\tundefined,\n\t\t\t);\n\t\t\tif (snapshot) snapshots.push(snapshot);\n\t\t} catch {\n\t\t\t// Optional additional buckets must not hide otherwise usable primary usage.\n\t\t}\n\t}\n\n\tif (snapshots.length === 0) {\n\t\tthrow new Error(\"Codex usage endpoint returned no displayable usage data.\");\n\t}\n\treturn { planType, snapshots };\n}\n\nfunction normalizeBackendSnapshot(\n\tlimitId: string,\n\tlimitName: string | undefined,\n\trateLimit: unknown,\n\tcredits: unknown,\n): NormalizedSnapshot | undefined {\n\tif (rateLimit === null || rateLimit === undefined) {\n\t\treturn normalizeBackendCredits(credits) ? { limitId, limitName } : undefined;\n\t}\n\tconst details = asObject(rateLimit) as BackendRateLimitDetails | undefined;\n\tif (!details) throw new Error(\"rate limit was not an object.\");\n\tconst primary = normalizeBackendWindow(details.primary_window);\n\tconst secondary = normalizeBackendWindow(details.secondary_window);\n\tif (!primary && !secondary && !normalizeBackendCredits(credits)) return undefined;\n\treturn { limitId, limitName, primary, secondary };\n}\n\nfunction normalizeBackendWindow(value: unknown): NormalizedWindow | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tconst window = asObject(value) as BackendWindowSnapshot | undefined;\n\tif (!window) throw new Error(\"rate-limit window was not an object.\");\n\tconst usedPercent = asNumber(window.used_percent);\n\tif (usedPercent === undefined) return undefined;\n\tconst limitSeconds = asNumber(window.limit_window_seconds);\n\treturn {\n\t\tusedPercent,\n\t\twindowMinutes: limitSeconds && limitSeconds > 0 ? Math.ceil(limitSeconds / 60) : undefined,\n\t\tresetsAt: toEpochMs(window.reset_at),\n\t};\n}\n\nfunction normalizeBackendCredits(value: unknown): boolean {\n\tconst credits = asObject(value) as BackendCreditsSnapshot | undefined;\n\tif (!credits) return false;\n\treturn credits.has_credits !== undefined && credits.unlimited !== undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Auth resolution via core ModelRuntime (rewired from ctx.modelRegistry)\n// ---------------------------------------------------------------------------\n\nasync function resolveCodexHeaders(runtime: ModelRuntime): Promise<Record<string, string> | undefined> {\n\tconst candidates: Model<Api>[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (model: Model<Api> | undefined) => {\n\t\tif (!model || model.provider !== CODEX_PROVIDER_ID) return;\n\t\tconst key = `${model.provider}/${model.id}`;\n\t\tif (seen.has(key)) return;\n\t\tseen.add(key);\n\t\tcandidates.push(model);\n\t};\n\tfor (const model of runtime.getAvailableSnapshot()) add(model);\n\tfor (const model of runtime.getModels()) add(model);\n\n\tfor (const model of candidates) {\n\t\tlet resolution: AuthResult | undefined;\n\t\ttry {\n\t\t\tresolution = await runtime.getAuth(model);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!resolution) continue;\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [name, value] of Object.entries(resolution.auth.headers ?? {})) {\n\t\t\tif (value !== null && value !== undefined) headers[name] = String(value);\n\t\t}\n\t\tif (!hasHeader(headers, \"Authorization\") && resolution.auth.apiKey) {\n\t\t\theaders.Authorization = `Bearer ${resolution.auth.apiKey}`;\n\t\t}\n\t\tif (!hasHeader(headers, \"User-Agent\")) {\n\t\t\theaders[\"User-Agent\"] = \"lunr-usage-service\";\n\t\t}\n\t\tif (hasHeader(headers, \"Authorization\")) return headers;\n\t}\n\treturn undefined;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n\treturn Object.keys(headers).some((key) => key.toLowerCase() === name.toLowerCase());\n}\n\nfunction redactErrorBody(body: string): string {\n\tconst redacted = body\n\t\t.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, \"Bearer <redacted>\")\n\t\t.replace(/\"access_token\"\\s*:\\s*\"[^\"]+\"/gi, '\"access_token\":\"<redacted>\"')\n\t\t.trim();\n\treturn redacted.length > MAX_ERROR_BODY_CHARS ? `${redacted.slice(0, MAX_ERROR_BODY_CHARS - 1)}…` : redacted;\n}\n\n// ---------------------------------------------------------------------------\n// PlanUsage mapping\n// ---------------------------------------------------------------------------\n\nfunction windowLabel(window: NormalizedWindow, fallback: string | undefined): string {\n\tconst minutes = window.windowMinutes;\n\tif (minutes === undefined) return fallback ?? \"Limit\";\n\tif (minutes >= 7 * 24 * 60) return \"Weekly\";\n\tif (minutes >= 24 * 60) return `${Math.round(minutes / (24 * 60))}d`;\n\tif (minutes >= 60) return `${Math.round(minutes / 60)}h`;\n\treturn `${minutes}m`;\n}\n\nfunction toPlanUsage(report: CodexUsageReport): PlanUsage | undefined {\n\tconst snapshot = report.snapshots[0];\n\tif (!snapshot) return undefined;\n\t// Longer window (weekly) first, shorter (5h) second — matches /usage layout.\n\tconst windows: PlanUsageWindow[] = [];\n\tfor (const window of [snapshot.secondary, snapshot.primary]) {\n\t\tif (!window) continue;\n\t\twindows.push({\n\t\t\tlabel: windowLabel(window, snapshot.limitName),\n\t\t\tusedPercent: window.usedPercent,\n\t\t\tresetsAt: window.resetsAt,\n\t\t});\n\t}\n\tif (windows.length === 0) return undefined;\n\treturn { provider: CODEX_PROVIDER_ID, planLabel: report.planType, windows };\n}\n\nexport async function fetchCodexPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tconst headers = await resolveCodexHeaders(runtime);\n\tif (!headers) return undefined;\n\n\tconst response = await fetchWithTimeout(CODEX_USAGE_URL, { headers });\n\tconst text = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`Codex usage endpoint returned ${response.status}: ${redactErrorBody(text)}`);\n\t}\n\tconst payload = asObject(JSON.parse(text)) as RateLimitStatusPayload | undefined;\n\tif (!payload) return undefined;\n\treturn toPlanUsage(normalizeBackendPayload(payload));\n}\n"]}
1
+ {"version":3,"file":"openai-codex.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/openai-codex.ts"],"names":[],"mappings":"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","sourcesContent":["// Vendored from narumiruna-pi-codex-usage (deleted in Phase 1 of the lunr-ux plan):\n// the pi-auth HTTP query path of `query.ts` plus `normalize.ts` and the backend\n// types from `types.ts`, consolidated into one core module. The codex app-server\n// fallback was dropped; extension-API imports (ExtensionContext, modelRegistry)\n// were rewired to core's ModelRuntime. No runtime dependency on the deleted\n// extension or on extension-repos remains.\n\nimport type { Api, AuthResult, Model } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow } from \"../usage-service.ts\";\nimport { asNumber, asObject, asString, fetchWithTimeout, toEpochMs } from \"./shared.ts\";\n\nconst CODEX_PROVIDER_ID = \"openai-codex\";\nconst CODEX_USAGE_URL = \"https://chatgpt.com/backend-api/wham/usage\";\nconst MAX_ERROR_BODY_CHARS = 600;\n\n// ---------------------------------------------------------------------------\n// Backend payload types (vendored from types.ts)\n// ---------------------------------------------------------------------------\n\ntype RateLimitStatusPayload = {\n\tplan_type?: unknown;\n\trate_limit?: unknown;\n\tadditional_rate_limits?: unknown;\n\tcredits?: unknown;\n\trate_limit_reset_credits?: unknown;\n};\n\ntype BackendRateLimitDetails = {\n\tprimary_window?: unknown;\n\tsecondary_window?: unknown;\n};\n\ntype BackendWindowSnapshot = {\n\tused_percent?: unknown;\n\tlimit_window_seconds?: unknown;\n\treset_at?: unknown;\n};\n\ntype BackendAdditionalRateLimit = {\n\tlimit_name?: unknown;\n\tmetered_feature?: unknown;\n\trate_limit?: unknown;\n};\n\ntype BackendCreditsSnapshot = {\n\thas_credits?: unknown;\n\tunlimited?: unknown;\n\tbalance?: unknown;\n};\n\ntype NormalizedWindow = {\n\tusedPercent: number;\n\twindowMinutes?: number;\n\tresetsAt?: number;\n};\n\ntype NormalizedSnapshot = {\n\tlimitId: string;\n\tlimitName?: string;\n\tprimary?: NormalizedWindow;\n\tsecondary?: NormalizedWindow;\n};\n\ntype CodexUsageReport = {\n\tplanType?: string;\n\tsnapshots: NormalizedSnapshot[];\n};\n\n// ---------------------------------------------------------------------------\n// Payload normalization (vendored from normalize.ts, backend path only)\n// ---------------------------------------------------------------------------\n\nfunction normalizeBackendPayload(payload: RateLimitStatusPayload): CodexUsageReport {\n\tconst snapshots: NormalizedSnapshot[] = [];\n\tconst planType = asString(payload.plan_type);\n\tconst primary = normalizeBackendSnapshot(\"codex\", undefined, payload.rate_limit, payload.credits);\n\tif (primary) snapshots.push(primary);\n\n\tconst additional = Array.isArray(payload.additional_rate_limits) ? payload.additional_rate_limits : [];\n\tfor (const item of additional) {\n\t\tconst additionalLimit = asObject(item) as BackendAdditionalRateLimit | undefined;\n\t\tif (!additionalLimit) continue;\n\t\tconst limitId = asString(additionalLimit.metered_feature) ?? asString(additionalLimit.limit_name);\n\t\tif (!limitId) continue;\n\t\ttry {\n\t\t\tconst snapshot = normalizeBackendSnapshot(\n\t\t\t\tlimitId,\n\t\t\t\tasString(additionalLimit.limit_name),\n\t\t\t\tadditionalLimit.rate_limit,\n\t\t\t\tundefined,\n\t\t\t);\n\t\t\tif (snapshot) snapshots.push(snapshot);\n\t\t} catch {\n\t\t\t// Optional additional buckets must not hide otherwise usable primary usage.\n\t\t}\n\t}\n\n\tif (snapshots.length === 0) {\n\t\tthrow new Error(\"Codex usage endpoint returned no displayable usage data.\");\n\t}\n\treturn { planType, snapshots };\n}\n\nfunction normalizeBackendSnapshot(\n\tlimitId: string,\n\tlimitName: string | undefined,\n\trateLimit: unknown,\n\tcredits: unknown,\n): NormalizedSnapshot | undefined {\n\tif (rateLimit === null || rateLimit === undefined) {\n\t\treturn normalizeBackendCredits(credits) ? { limitId, limitName } : undefined;\n\t}\n\tconst details = asObject(rateLimit) as BackendRateLimitDetails | undefined;\n\tif (!details) throw new Error(\"rate limit was not an object.\");\n\tconst primary = normalizeBackendWindow(details.primary_window);\n\tconst secondary = normalizeBackendWindow(details.secondary_window);\n\tif (!primary && !secondary && !normalizeBackendCredits(credits)) return undefined;\n\treturn { limitId, limitName, primary, secondary };\n}\n\nfunction normalizeBackendWindow(value: unknown): NormalizedWindow | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tconst window = asObject(value) as BackendWindowSnapshot | undefined;\n\tif (!window) throw new Error(\"rate-limit window was not an object.\");\n\tconst usedPercent = asNumber(window.used_percent);\n\tif (usedPercent === undefined) return undefined;\n\tconst limitSeconds = asNumber(window.limit_window_seconds);\n\treturn {\n\t\tusedPercent,\n\t\twindowMinutes: limitSeconds && limitSeconds > 0 ? Math.ceil(limitSeconds / 60) : undefined,\n\t\tresetsAt: toEpochMs(window.reset_at),\n\t};\n}\n\nfunction normalizeBackendCredits(value: unknown): boolean {\n\tconst credits = asObject(value) as BackendCreditsSnapshot | undefined;\n\tif (!credits) return false;\n\treturn credits.has_credits !== undefined && credits.unlimited !== undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Auth resolution via core ModelRuntime (rewired from ctx.modelRegistry)\n// ---------------------------------------------------------------------------\n\nasync function resolveCodexHeaders(runtime: ModelRuntime): Promise<Record<string, string> | undefined> {\n\tconst candidates: Model<Api>[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (model: Model<Api> | undefined) => {\n\t\tif (!model || model.provider !== CODEX_PROVIDER_ID) return;\n\t\tconst key = `${model.provider}/${model.id}`;\n\t\tif (seen.has(key)) return;\n\t\tseen.add(key);\n\t\tcandidates.push(model);\n\t};\n\tfor (const model of runtime.getAvailableSnapshot()) add(model);\n\tfor (const model of runtime.getModels()) add(model);\n\n\tfor (const model of candidates) {\n\t\tlet resolution: AuthResult | undefined;\n\t\ttry {\n\t\t\tresolution = await runtime.getAuth(model);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!resolution) continue;\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [name, value] of Object.entries(resolution.auth.headers ?? {})) {\n\t\t\tif (value !== null && value !== undefined) headers[name] = String(value);\n\t\t}\n\t\tif (!hasHeader(headers, \"Authorization\") && resolution.auth.apiKey) {\n\t\t\theaders.Authorization = `Bearer ${resolution.auth.apiKey}`;\n\t\t}\n\t\tif (!hasHeader(headers, \"User-Agent\")) {\n\t\t\theaders[\"User-Agent\"] = \"lunr-usage-service\";\n\t\t}\n\t\tif (hasHeader(headers, \"Authorization\")) return headers;\n\t}\n\treturn undefined;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n\treturn Object.keys(headers).some((key) => key.toLowerCase() === name.toLowerCase());\n}\n\nfunction redactErrorBody(body: string): string {\n\tconst redacted = body\n\t\t.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, \"Bearer <redacted>\")\n\t\t.replace(/\"access_token\"\\s*:\\s*\"[^\"]+\"/gi, '\"access_token\":\"<redacted>\"')\n\t\t.trim();\n\treturn redacted.length > MAX_ERROR_BODY_CHARS ? `${redacted.slice(0, MAX_ERROR_BODY_CHARS - 1)}…` : redacted;\n}\n\n// ---------------------------------------------------------------------------\n// PlanUsage mapping\n// ---------------------------------------------------------------------------\n\nfunction windowLabel(window: NormalizedWindow, fallback: string | undefined): string {\n\tconst minutes = window.windowMinutes;\n\tif (minutes === undefined) return fallback ?? \"Limit\";\n\tif (minutes >= 7 * 24 * 60) return \"Weekly\";\n\tif (minutes >= 24 * 60) return `${Math.round(minutes / (24 * 60))}d`;\n\tif (minutes >= 60) return `${Math.round(minutes / 60)}h`;\n\treturn `${minutes}m`;\n}\n\nfunction toPlanUsage(report: CodexUsageReport): PlanUsage | undefined {\n\tconst snapshot = report.snapshots[0];\n\tif (!snapshot) return undefined;\n\t// Longer window (weekly) first, shorter (5h) second — matches /usage layout.\n\tconst windows: PlanUsageWindow[] = [];\n\tfor (const window of [snapshot.secondary, snapshot.primary]) {\n\t\tif (!window) continue;\n\t\twindows.push({\n\t\t\tlabel: windowLabel(window, snapshot.limitName),\n\t\t\tusedPercent: window.usedPercent,\n\t\t\tresetsAt: window.resetsAt,\n\t\t});\n\t}\n\tif (windows.length === 0) return undefined;\n\treturn { provider: CODEX_PROVIDER_ID, planLabel: report.planType, windows };\n}\n\nexport async function fetchCodexPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tconst headers = await resolveCodexHeaders(runtime);\n\tif (!headers) return undefined;\n\n\tconst response = await fetchWithTimeout(CODEX_USAGE_URL, { headers });\n\tconst text = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`Codex usage endpoint returned ${response.status}: ${redactErrorBody(text)}`);\n\t}\n\tconst payload = asObject(JSON.parse(text)) as RateLimitStatusPayload | undefined;\n\tif (!payload) return undefined;\n\treturn toPlanUsage(normalizeBackendPayload(payload));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"usage-history.d.ts","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IAClC,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,UAAU,EAAE,sBAAsB,CAAC;IACnC,6EAA6E;IAC7E,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gEAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAaD,sDAAsD;AACtD,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAoID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CA2EpG","sourcesContent":["/**\n * lunr: 30-day token usage history for the /usage command.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - REAL token totals per provider/model from assistant-message `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai/compat\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport {\n\ttype FileEntry,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\tsessionEntryToContextMessages,\n} from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\tperModel: Map<string, UsageHistoryModelRow>;\n\tperDay: Map<string, number>;\n\tcategories: UsageHistoryCategories;\n\thasUsage: boolean;\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Milliseconds for day bucketing: entry timestamp, then message timestamp, then file mtime. */\nfunction entryTimestampMs(entry: SessionEntry, message: AssistantMessage, fallbackMs: number): number {\n\tconst entryMs = Date.parse(entry.timestamp ?? \"\");\n\tif (!Number.isNaN(entryMs)) return entryMs;\n\tif (typeof message.timestamp === \"number\" && message.timestamp > 0) return message.timestamp;\n\treturn fallbackMs;\n}\n\n/** Parse one session file into its aggregate. Returns null when the file is unusable. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\tlet entries: FileEntry[];\n\ttry {\n\t\tentries = loadEntriesFromFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (entries.length === 0) return null;\n\n\tconst aggregate: FileAggregate = {\n\t\tmtimeMs,\n\t\tsize,\n\t\tperModel: new Map(),\n\t\tperDay: new Map(),\n\t\tcategories: emptyCategories(),\n\t\thasUsage: false,\n\t};\n\n\tconst sessionEntries: SessionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session\") continue; // header\n\t\tsessionEntries.push(entry);\n\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst message = entry.message as AssistantMessage;\n\t\tconst usage = message.usage;\n\t\tif (!usage || typeof usage.input !== \"number\" || typeof usage.output !== \"number\") continue;\n\n\t\tconst provider = typeof message.provider === \"string\" && message.provider ? message.provider : \"unknown\";\n\t\tconst modelId = message.responseModel ?? message.model;\n\t\tconst key = `${provider}/${typeof modelId === \"string\" && modelId ? modelId : \"unknown\"}`;\n\t\tconst input = usage.input;\n\t\tconst output = usage.output;\n\t\tconst cacheRead = typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0;\n\t\tconst cacheWrite = typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0;\n\n\t\tlet row = aggregate.perModel.get(key);\n\t\tif (!row) {\n\t\t\trow = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\taggregate.perModel.set(key, row);\n\t\t}\n\t\trow.input += input;\n\t\trow.output += output;\n\t\trow.cacheRead += cacheRead;\n\t\trow.cacheWrite += cacheWrite;\n\t\trow.total += input + output + cacheRead + cacheWrite;\n\t\taggregate.hasUsage = true;\n\n\t\tconst day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);\n\t\taggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);\n\t}\n\n\t// Category estimate over the file's messages (system prompt/tools are not\n\t// stored in session files, so message categories carry the breakdown).\n\ttry {\n\t\tconst messages = sessionEntries.flatMap(sessionEntryToContextMessages);\n\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\taggregate.categories = {\n\t\t\tuser: breakdown.user,\n\t\t\tassistantText: breakdown.assistantText,\n\t\t\tthinking: breakdown.thinking,\n\t\t\ttoolCalls: breakdown.toolCalls,\n\t\t\ttoolResults: breakdown.toolResults,\n\t\t\tsummaries: breakdown.summaries,\n\t\t\ttotal:\n\t\t\t\tbreakdown.user +\n\t\t\t\tbreakdown.assistantText +\n\t\t\t\tbreakdown.thinking +\n\t\t\t\tbreakdown.toolCalls +\n\t\t\t\tbreakdown.toolResults +\n\t\t\t\tbreakdown.summaries,\n\t\t};\n\t} catch {\n\t\t// Category estimation is best-effort; per-model/day totals above still stand.\n\t}\n\n\treturn aggregate;\n}\n\n/**\n * Aggregate token usage across all session files modified since `sinceMs`.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tif (aggregate.hasUsage) result.sessionsWithUsage++;\n\t\t\tfor (const row of aggregate.perModel.values()) {\n\t\t\t\tlet target = result.perModel.find((r) => r.model === row.model);\n\t\t\t\tif (!target) {\n\t\t\t\t\ttarget = { model: row.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(target);\n\t\t\t\t}\n\t\t\t\ttarget.input += row.input;\n\t\t\t\ttarget.output += row.output;\n\t\t\t\ttarget.cacheRead += row.cacheRead;\n\t\t\t\ttarget.cacheWrite += row.cacheWrite;\n\t\t\t\ttarget.total += row.total;\n\t\t\t}\n\t\t\tfor (const [day, total] of aggregate.perDay) {\n\t\t\t\tconst existing = result.perDay.find((r) => r.day === day);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.total += total;\n\t\t\t\t} else {\n\t\t\t\t\tresult.perDay.push({ day, total });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of [\n\t\t\t\t\"user\",\n\t\t\t\t\"assistantText\",\n\t\t\t\t\"thinking\",\n\t\t\t\t\"toolCalls\",\n\t\t\t\t\"toolResults\",\n\t\t\t\t\"summaries\",\n\t\t\t\t\"total\",\n\t\t\t] as const) {\n\t\t\t\tresult.categories[key] += aggregate.categories[key];\n\t\t\t}\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
1
+ {"version":3,"file":"usage-history.d.ts","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IAClC,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,UAAU,EAAE,sBAAsB,CAAC;IACnC,6EAA6E;IAC7E,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gEAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAaD,sDAAsD;AACtD,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAoID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CA2EpG","sourcesContent":["/**\n * lunr: 30-day token usage history for the /usage command.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - REAL token totals per provider/model from assistant-message `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport {\n\ttype FileEntry,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\tsessionEntryToContextMessages,\n} from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\tperModel: Map<string, UsageHistoryModelRow>;\n\tperDay: Map<string, number>;\n\tcategories: UsageHistoryCategories;\n\thasUsage: boolean;\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Milliseconds for day bucketing: entry timestamp, then message timestamp, then file mtime. */\nfunction entryTimestampMs(entry: SessionEntry, message: AssistantMessage, fallbackMs: number): number {\n\tconst entryMs = Date.parse(entry.timestamp ?? \"\");\n\tif (!Number.isNaN(entryMs)) return entryMs;\n\tif (typeof message.timestamp === \"number\" && message.timestamp > 0) return message.timestamp;\n\treturn fallbackMs;\n}\n\n/** Parse one session file into its aggregate. Returns null when the file is unusable. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\tlet entries: FileEntry[];\n\ttry {\n\t\tentries = loadEntriesFromFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (entries.length === 0) return null;\n\n\tconst aggregate: FileAggregate = {\n\t\tmtimeMs,\n\t\tsize,\n\t\tperModel: new Map(),\n\t\tperDay: new Map(),\n\t\tcategories: emptyCategories(),\n\t\thasUsage: false,\n\t};\n\n\tconst sessionEntries: SessionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session\") continue; // header\n\t\tsessionEntries.push(entry);\n\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst message = entry.message as AssistantMessage;\n\t\tconst usage = message.usage;\n\t\tif (!usage || typeof usage.input !== \"number\" || typeof usage.output !== \"number\") continue;\n\n\t\tconst provider = typeof message.provider === \"string\" && message.provider ? message.provider : \"unknown\";\n\t\tconst modelId = message.responseModel ?? message.model;\n\t\tconst key = `${provider}/${typeof modelId === \"string\" && modelId ? modelId : \"unknown\"}`;\n\t\tconst input = usage.input;\n\t\tconst output = usage.output;\n\t\tconst cacheRead = typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0;\n\t\tconst cacheWrite = typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0;\n\n\t\tlet row = aggregate.perModel.get(key);\n\t\tif (!row) {\n\t\t\trow = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\taggregate.perModel.set(key, row);\n\t\t}\n\t\trow.input += input;\n\t\trow.output += output;\n\t\trow.cacheRead += cacheRead;\n\t\trow.cacheWrite += cacheWrite;\n\t\trow.total += input + output + cacheRead + cacheWrite;\n\t\taggregate.hasUsage = true;\n\n\t\tconst day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);\n\t\taggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);\n\t}\n\n\t// Category estimate over the file's messages (system prompt/tools are not\n\t// stored in session files, so message categories carry the breakdown).\n\ttry {\n\t\tconst messages = sessionEntries.flatMap(sessionEntryToContextMessages);\n\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\taggregate.categories = {\n\t\t\tuser: breakdown.user,\n\t\t\tassistantText: breakdown.assistantText,\n\t\t\tthinking: breakdown.thinking,\n\t\t\ttoolCalls: breakdown.toolCalls,\n\t\t\ttoolResults: breakdown.toolResults,\n\t\t\tsummaries: breakdown.summaries,\n\t\t\ttotal:\n\t\t\t\tbreakdown.user +\n\t\t\t\tbreakdown.assistantText +\n\t\t\t\tbreakdown.thinking +\n\t\t\t\tbreakdown.toolCalls +\n\t\t\t\tbreakdown.toolResults +\n\t\t\t\tbreakdown.summaries,\n\t\t};\n\t} catch {\n\t\t// Category estimation is best-effort; per-model/day totals above still stand.\n\t}\n\n\treturn aggregate;\n}\n\n/**\n * Aggregate token usage across all session files modified since `sinceMs`.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tif (aggregate.hasUsage) result.sessionsWithUsage++;\n\t\t\tfor (const row of aggregate.perModel.values()) {\n\t\t\t\tlet target = result.perModel.find((r) => r.model === row.model);\n\t\t\t\tif (!target) {\n\t\t\t\t\ttarget = { model: row.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(target);\n\t\t\t\t}\n\t\t\t\ttarget.input += row.input;\n\t\t\t\ttarget.output += row.output;\n\t\t\t\ttarget.cacheRead += row.cacheRead;\n\t\t\t\ttarget.cacheWrite += row.cacheWrite;\n\t\t\t\ttarget.total += row.total;\n\t\t\t}\n\t\t\tfor (const [day, total] of aggregate.perDay) {\n\t\t\t\tconst existing = result.perDay.find((r) => r.day === day);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.total += total;\n\t\t\t\t} else {\n\t\t\t\t\tresult.perDay.push({ day, total });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of [\n\t\t\t\t\"user\",\n\t\t\t\t\"assistantText\",\n\t\t\t\t\"thinking\",\n\t\t\t\t\"toolCalls\",\n\t\t\t\t\"toolResults\",\n\t\t\t\t\"summaries\",\n\t\t\t\t\"total\",\n\t\t\t] as const) {\n\t\t\t\tresult.categories[key] += aggregate.categories[key];\n\t\t\t}\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"usage-history.js","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: 30-day token usage history for the /usage command.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - REAL token totals per provider/model from assistant-message `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai/compat\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport {\n\ttype FileEntry,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\tsessionEntryToContextMessages,\n} from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\tperModel: Map<string, UsageHistoryModelRow>;\n\tperDay: Map<string, number>;\n\tcategories: UsageHistoryCategories;\n\thasUsage: boolean;\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Milliseconds for day bucketing: entry timestamp, then message timestamp, then file mtime. */\nfunction entryTimestampMs(entry: SessionEntry, message: AssistantMessage, fallbackMs: number): number {\n\tconst entryMs = Date.parse(entry.timestamp ?? \"\");\n\tif (!Number.isNaN(entryMs)) return entryMs;\n\tif (typeof message.timestamp === \"number\" && message.timestamp > 0) return message.timestamp;\n\treturn fallbackMs;\n}\n\n/** Parse one session file into its aggregate. Returns null when the file is unusable. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\tlet entries: FileEntry[];\n\ttry {\n\t\tentries = loadEntriesFromFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (entries.length === 0) return null;\n\n\tconst aggregate: FileAggregate = {\n\t\tmtimeMs,\n\t\tsize,\n\t\tperModel: new Map(),\n\t\tperDay: new Map(),\n\t\tcategories: emptyCategories(),\n\t\thasUsage: false,\n\t};\n\n\tconst sessionEntries: SessionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session\") continue; // header\n\t\tsessionEntries.push(entry);\n\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst message = entry.message as AssistantMessage;\n\t\tconst usage = message.usage;\n\t\tif (!usage || typeof usage.input !== \"number\" || typeof usage.output !== \"number\") continue;\n\n\t\tconst provider = typeof message.provider === \"string\" && message.provider ? message.provider : \"unknown\";\n\t\tconst modelId = message.responseModel ?? message.model;\n\t\tconst key = `${provider}/${typeof modelId === \"string\" && modelId ? modelId : \"unknown\"}`;\n\t\tconst input = usage.input;\n\t\tconst output = usage.output;\n\t\tconst cacheRead = typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0;\n\t\tconst cacheWrite = typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0;\n\n\t\tlet row = aggregate.perModel.get(key);\n\t\tif (!row) {\n\t\t\trow = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\taggregate.perModel.set(key, row);\n\t\t}\n\t\trow.input += input;\n\t\trow.output += output;\n\t\trow.cacheRead += cacheRead;\n\t\trow.cacheWrite += cacheWrite;\n\t\trow.total += input + output + cacheRead + cacheWrite;\n\t\taggregate.hasUsage = true;\n\n\t\tconst day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);\n\t\taggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);\n\t}\n\n\t// Category estimate over the file's messages (system prompt/tools are not\n\t// stored in session files, so message categories carry the breakdown).\n\ttry {\n\t\tconst messages = sessionEntries.flatMap(sessionEntryToContextMessages);\n\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\taggregate.categories = {\n\t\t\tuser: breakdown.user,\n\t\t\tassistantText: breakdown.assistantText,\n\t\t\tthinking: breakdown.thinking,\n\t\t\ttoolCalls: breakdown.toolCalls,\n\t\t\ttoolResults: breakdown.toolResults,\n\t\t\tsummaries: breakdown.summaries,\n\t\t\ttotal:\n\t\t\t\tbreakdown.user +\n\t\t\t\tbreakdown.assistantText +\n\t\t\t\tbreakdown.thinking +\n\t\t\t\tbreakdown.toolCalls +\n\t\t\t\tbreakdown.toolResults +\n\t\t\t\tbreakdown.summaries,\n\t\t};\n\t} catch {\n\t\t// Category estimation is best-effort; per-model/day totals above still stand.\n\t}\n\n\treturn aggregate;\n}\n\n/**\n * Aggregate token usage across all session files modified since `sinceMs`.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tif (aggregate.hasUsage) result.sessionsWithUsage++;\n\t\t\tfor (const row of aggregate.perModel.values()) {\n\t\t\t\tlet target = result.perModel.find((r) => r.model === row.model);\n\t\t\t\tif (!target) {\n\t\t\t\t\ttarget = { model: row.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(target);\n\t\t\t\t}\n\t\t\t\ttarget.input += row.input;\n\t\t\t\ttarget.output += row.output;\n\t\t\t\ttarget.cacheRead += row.cacheRead;\n\t\t\t\ttarget.cacheWrite += row.cacheWrite;\n\t\t\t\ttarget.total += row.total;\n\t\t\t}\n\t\t\tfor (const [day, total] of aggregate.perDay) {\n\t\t\t\tconst existing = result.perDay.find((r) => r.day === day);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.total += total;\n\t\t\t\t} else {\n\t\t\t\t\tresult.perDay.push({ day, total });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of [\n\t\t\t\t\"user\",\n\t\t\t\t\"assistantText\",\n\t\t\t\t\"thinking\",\n\t\t\t\t\"toolCalls\",\n\t\t\t\t\"toolResults\",\n\t\t\t\t\"summaries\",\n\t\t\t\t\"total\",\n\t\t\t] as const) {\n\t\t\t\tresult.categories[key] += aggregate.categories[key];\n\t\t\t}\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
1
+ {"version":3,"file":"usage-history.js","sourceRoot":"","sources":["../../src/core/usage-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAe,WAAW,EAAc,QAAQ,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAEN,mBAAmB,EAEnB,6BAA6B,GAC7B,MAAM,sBAAsB,CAAC;AAkD9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;AAEnD,sDAAsD;AACtD,MAAM,UAAU,sBAAsB,GAAS;IAC9C,SAAS,CAAC,KAAK,EAAE,CAAC;AAAA,CAClB;AAED,SAAS,eAAe,GAA2B;IAClD,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAAA,CACxG;AAED,SAAS,YAAY,GAAiB;IACrC,OAAO;QACN,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,EAAE;QACV,UAAU,EAAE,eAAe,EAAE;QAC7B,oBAAoB,EAAE,KAAK;QAC3B,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,iBAAiB,EAAE,CAAC;KACpB,CAAC;AAAA,CACF;AAED,6FAA6F;AAC7F,SAAS,qBAAqB,CAAC,WAAmB,EAAY;IAC7D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,UAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,UAAU,GAAG,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAChC,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC7C,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;oBACxC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;wBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC7D,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,oCAAkC;YACnC,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,gGAAgG;AAChG,SAAS,gBAAgB,CAAC,KAAmB,EAAE,OAAyB,EAAE,UAAkB,EAAU;IACrG,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,CAAC,SAAS,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,SAAS,CAAC;IAC7F,OAAO,UAAU,CAAC;AAAA,CAClB;AAED,yFAAyF;AACzF,SAAS,gBAAgB,CAAC,QAAgB,EAAE,OAAe,EAAE,IAAY,EAAwB;IAChG,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,SAAS,GAAkB;QAChC,OAAO;QACP,IAAI;QACJ,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,UAAU,EAAE,eAAe,EAAE;QAC7B,QAAQ,EAAE,KAAK;KACf,CAAC;IAEF,MAAM,cAAc,GAAmB,EAAE,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS,CAAC,SAAS;QACjD,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE3B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,OAA2B,CAAC;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,SAAS;QAE5F,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACzG,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,KAAK,CAAC;QACvD,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAC1F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE/E,IAAI,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,EAAE,CAAC;YACV,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YACjF,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;QACD,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC;QACnB,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC;QACrB,GAAG,CAAC,SAAS,IAAI,SAAS,CAAC;QAC3B,GAAG,CAAC,UAAU,IAAI,UAAU,CAAC;QAC7B,GAAG,CAAC,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;QACrD,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;QAE1B,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3F,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;IACvG,CAAC;IAED,0EAA0E;IAC1E,uEAAuE;IACvE,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,uBAAuB,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC;QACvG,SAAS,CAAC,UAAU,GAAG;YACtB,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,aAAa,EAAE,SAAS,CAAC,aAAa;YACtC,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,KAAK,EACJ,SAAS,CAAC,IAAI;gBACd,SAAS,CAAC,aAAa;gBACvB,SAAS,CAAC,QAAQ;gBAClB,SAAS,CAAC,SAAS;gBACnB,SAAS,CAAC,WAAW;gBACrB,SAAS,CAAC,SAAS;SACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,8EAA8E;IAC/E,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAkD,EAAgB;IACrG,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,cAAc,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/B,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACnB,IAAI,KAAY,CAAC;YACjB,IAAI,CAAC;gBACJ,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACR,SAAS,CAAC,oCAAoC;YAC/C,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO;gBAAE,SAAS;YAC9C,MAAM,CAAC,YAAY,EAAE,CAAC;YAEtB,IAAI,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,SAAS,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;gBACxF,SAAS,GAAG,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;gBAC/E,MAAM,CAAC,WAAW,EAAE,CAAC;gBACrB,IAAI,SAAS,EAAE,CAAC;oBACf,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACP,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;YACD,IAAI,CAAC,SAAS;gBAAE,SAAS;YAEzB,IAAI,SAAS,CAAC,QAAQ;gBAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACnD,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC/C,IAAI,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;gBAChE,IAAI,CAAC,MAAM,EAAE,CAAC;oBACb,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;oBAC1F,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC9B,CAAC;gBACD,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;gBAC1B,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC;gBAC5B,MAAM,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,CAAC;gBAClC,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC;gBACpC,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;YAC3B,CAAC;YACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;gBAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;gBAC1D,IAAI,QAAQ,EAAE,CAAC;oBACd,QAAQ,CAAC,KAAK,IAAI,KAAK,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpC,CAAC;YACF,CAAC;YACD,KAAK,MAAM,GAAG,IAAI;gBACjB,MAAM;gBACN,eAAe;gBACf,UAAU;gBACV,WAAW;gBACX,aAAa;gBACb,WAAW;gBACX,OAAO;aACE,EAAE,CAAC;gBACZ,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACrD,CAAC;QACF,CAAC;QAED,wCAAwC;QACxC,KAAK,MAAM,UAAU,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,YAAY,EAAE,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd","sourcesContent":["/**\n * lunr: 30-day token usage history for the /usage command.\n *\n * Scans the on-disk session files (`.jsonl` under `getSessionsDir()`, both the\n * flat and the per-project-subdirectory layouts) and aggregates:\n * - REAL token totals per provider/model from assistant-message `usage`\n * metadata (same fields the live session rows use),\n * - per-day totals (YYYY-MM-DD buckets from the entry timestamp),\n * - an ESTIMATED category breakdown (chars/4) via the same accounting as\n * `computeContextBreakdown` — session files don't store the system prompt or\n * tool definitions, so `includesSystemPrompt` is always false and the\n * message categories carry the whole breakdown.\n *\n * Results are cached per file keyed on mtime+size; unchanged files are not\n * re-parsed. Never throws — corrupt files are skipped, total failure returns\n * an empty aggregate.\n */\n\nimport { type Dirent, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { computeContextBreakdown } from \"./context-breakdown.ts\";\nimport {\n\ttype FileEntry,\n\tloadEntriesFromFile,\n\ttype SessionEntry,\n\tsessionEntryToContextMessages,\n} from \"./session-manager.ts\";\n\nexport interface UsageHistoryModelRow {\n\tmodel: string;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotal: number;\n}\n\nexport interface UsageHistoryDayRow {\n\t/** YYYY-MM-DD (UTC). */\n\tday: string;\n\ttotal: number;\n}\n\nexport interface UsageHistoryCategories {\n\tuser: number;\n\tassistantText: number;\n\tthinking: number;\n\ttoolCalls: number;\n\ttoolResults: number;\n\tsummaries: number;\n\ttotal: number;\n}\n\nexport interface UsageHistory {\n\tperModel: UsageHistoryModelRow[];\n\tperDay: UsageHistoryDayRow[];\n\tcategories: UsageHistoryCategories;\n\t/** False: session files don't store the system prompt / tool definitions. */\n\tincludesSystemPrompt: boolean;\n\t/** Session files inside the scan window (mtime ≥ sinceMs). */\n\tfilesScanned: number;\n\t/** Files actually parsed this call (cache misses). Also serves as a test hook. */\n\tfilesParsed: number;\n\t/** Scanned files containing at least one assistant message with usage. */\n\tsessionsWithUsage: number;\n}\n\ninterface FileAggregate {\n\tmtimeMs: number;\n\tsize: number;\n\tperModel: Map<string, UsageHistoryModelRow>;\n\tperDay: Map<string, number>;\n\tcategories: UsageHistoryCategories;\n\thasUsage: boolean;\n}\n\nconst fileCache = new Map<string, FileAggregate>();\n\n/** Test hook: drop all cached per-file aggregates. */\nexport function resetUsageHistoryCache(): void {\n\tfileCache.clear();\n}\n\nfunction emptyCategories(): UsageHistoryCategories {\n\treturn { user: 0, assistantText: 0, thinking: 0, toolCalls: 0, toolResults: 0, summaries: 0, total: 0 };\n}\n\nfunction emptyHistory(): UsageHistory {\n\treturn {\n\t\tperModel: [],\n\t\tperDay: [],\n\t\tcategories: emptyCategories(),\n\t\tincludesSystemPrompt: false,\n\t\tfilesScanned: 0,\n\t\tfilesParsed: 0,\n\t\tsessionsWithUsage: 0,\n\t};\n}\n\n/** Enumerate `.jsonl` session files in both layouts: flat and per-project subdirectories. */\nfunction enumerateSessionFiles(sessionsDir: string): string[] {\n\tconst files: string[] = [];\n\tlet dirEntries: Dirent[];\n\ttry {\n\t\tdirEntries = readdirSync(sessionsDir, { withFileTypes: true });\n\t} catch {\n\t\treturn files;\n\t}\n\tfor (const entry of dirEntries) {\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n\t\t\tfiles.push(join(sessionsDir, entry.name));\n\t\t} else if (entry.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tconst subDir = join(sessionsDir, entry.name);\n\t\t\t\tfor (const name of readdirSync(subDir)) {\n\t\t\t\t\tif (name.endsWith(\".jsonl\")) files.push(join(subDir, name));\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable subdirectory — skip.\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\n/** Milliseconds for day bucketing: entry timestamp, then message timestamp, then file mtime. */\nfunction entryTimestampMs(entry: SessionEntry, message: AssistantMessage, fallbackMs: number): number {\n\tconst entryMs = Date.parse(entry.timestamp ?? \"\");\n\tif (!Number.isNaN(entryMs)) return entryMs;\n\tif (typeof message.timestamp === \"number\" && message.timestamp > 0) return message.timestamp;\n\treturn fallbackMs;\n}\n\n/** Parse one session file into its aggregate. Returns null when the file is unusable. */\nfunction parseSessionFile(filePath: string, mtimeMs: number, size: number): FileAggregate | null {\n\tlet entries: FileEntry[];\n\ttry {\n\t\tentries = loadEntriesFromFile(filePath);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (entries.length === 0) return null;\n\n\tconst aggregate: FileAggregate = {\n\t\tmtimeMs,\n\t\tsize,\n\t\tperModel: new Map(),\n\t\tperDay: new Map(),\n\t\tcategories: emptyCategories(),\n\t\thasUsage: false,\n\t};\n\n\tconst sessionEntries: SessionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session\") continue; // header\n\t\tsessionEntries.push(entry);\n\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst message = entry.message as AssistantMessage;\n\t\tconst usage = message.usage;\n\t\tif (!usage || typeof usage.input !== \"number\" || typeof usage.output !== \"number\") continue;\n\n\t\tconst provider = typeof message.provider === \"string\" && message.provider ? message.provider : \"unknown\";\n\t\tconst modelId = message.responseModel ?? message.model;\n\t\tconst key = `${provider}/${typeof modelId === \"string\" && modelId ? modelId : \"unknown\"}`;\n\t\tconst input = usage.input;\n\t\tconst output = usage.output;\n\t\tconst cacheRead = typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0;\n\t\tconst cacheWrite = typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0;\n\n\t\tlet row = aggregate.perModel.get(key);\n\t\tif (!row) {\n\t\t\trow = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\taggregate.perModel.set(key, row);\n\t\t}\n\t\trow.input += input;\n\t\trow.output += output;\n\t\trow.cacheRead += cacheRead;\n\t\trow.cacheWrite += cacheWrite;\n\t\trow.total += input + output + cacheRead + cacheWrite;\n\t\taggregate.hasUsage = true;\n\n\t\tconst day = new Date(entryTimestampMs(entry, message, mtimeMs)).toISOString().slice(0, 10);\n\t\taggregate.perDay.set(day, (aggregate.perDay.get(day) ?? 0) + input + output + cacheRead + cacheWrite);\n\t}\n\n\t// Category estimate over the file's messages (system prompt/tools are not\n\t// stored in session files, so message categories carry the breakdown).\n\ttry {\n\t\tconst messages = sessionEntries.flatMap(sessionEntryToContextMessages);\n\t\tconst breakdown = computeContextBreakdown({ systemPrompt: \"\", tools: [], messages, contextWindow: 0 });\n\t\taggregate.categories = {\n\t\t\tuser: breakdown.user,\n\t\t\tassistantText: breakdown.assistantText,\n\t\t\tthinking: breakdown.thinking,\n\t\t\ttoolCalls: breakdown.toolCalls,\n\t\t\ttoolResults: breakdown.toolResults,\n\t\t\tsummaries: breakdown.summaries,\n\t\t\ttotal:\n\t\t\t\tbreakdown.user +\n\t\t\t\tbreakdown.assistantText +\n\t\t\t\tbreakdown.thinking +\n\t\t\t\tbreakdown.toolCalls +\n\t\t\t\tbreakdown.toolResults +\n\t\t\t\tbreakdown.summaries,\n\t\t};\n\t} catch {\n\t\t// Category estimation is best-effort; per-model/day totals above still stand.\n\t}\n\n\treturn aggregate;\n}\n\n/**\n * Aggregate token usage across all session files modified since `sinceMs`.\n * Never throws; on total failure returns an empty aggregate.\n */\nexport function collectUsageHistory(options: { sinceMs: number; sessionsDir?: string }): UsageHistory {\n\tconst result = emptyHistory();\n\ttry {\n\t\tconst sessionsDir = options.sessionsDir ?? getSessionsDir();\n\t\tconst files = enumerateSessionFiles(sessionsDir);\n\t\tconst seen = new Set<string>();\n\n\t\tfor (const filePath of files) {\n\t\t\tseen.add(filePath);\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = statSync(filePath);\n\t\t\t} catch {\n\t\t\t\tcontinue; // vanished between readdir and stat\n\t\t\t}\n\t\t\tif (stats.mtimeMs < options.sinceMs) continue;\n\t\t\tresult.filesScanned++;\n\n\t\t\tlet aggregate = fileCache.get(filePath);\n\t\t\tif (!aggregate || aggregate.mtimeMs !== stats.mtimeMs || aggregate.size !== stats.size) {\n\t\t\t\taggregate = parseSessionFile(filePath, stats.mtimeMs, stats.size) ?? undefined;\n\t\t\t\tresult.filesParsed++;\n\t\t\t\tif (aggregate) {\n\t\t\t\t\tfileCache.set(filePath, aggregate);\n\t\t\t\t} else {\n\t\t\t\t\tfileCache.delete(filePath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!aggregate) continue;\n\n\t\t\tif (aggregate.hasUsage) result.sessionsWithUsage++;\n\t\t\tfor (const row of aggregate.perModel.values()) {\n\t\t\t\tlet target = result.perModel.find((r) => r.model === row.model);\n\t\t\t\tif (!target) {\n\t\t\t\t\ttarget = { model: row.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\t\t\t\t\tresult.perModel.push(target);\n\t\t\t\t}\n\t\t\t\ttarget.input += row.input;\n\t\t\t\ttarget.output += row.output;\n\t\t\t\ttarget.cacheRead += row.cacheRead;\n\t\t\t\ttarget.cacheWrite += row.cacheWrite;\n\t\t\t\ttarget.total += row.total;\n\t\t\t}\n\t\t\tfor (const [day, total] of aggregate.perDay) {\n\t\t\t\tconst existing = result.perDay.find((r) => r.day === day);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.total += total;\n\t\t\t\t} else {\n\t\t\t\t\tresult.perDay.push({ day, total });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of [\n\t\t\t\t\"user\",\n\t\t\t\t\"assistantText\",\n\t\t\t\t\"thinking\",\n\t\t\t\t\"toolCalls\",\n\t\t\t\t\"toolResults\",\n\t\t\t\t\"summaries\",\n\t\t\t\t\"total\",\n\t\t\t] as const) {\n\t\t\t\tresult.categories[key] += aggregate.categories[key];\n\t\t\t}\n\t\t}\n\n\t\t// Drop cache entries for deleted files.\n\t\tfor (const cachedPath of fileCache.keys()) {\n\t\t\tif (!seen.has(cachedPath)) fileCache.delete(cachedPath);\n\t\t}\n\n\t\tresult.perModel.sort((a, b) => b.total - a.total);\n\t\tresult.perDay.sort((a, b) => a.day.localeCompare(b.day));\n\t} catch {\n\t\treturn emptyHistory();\n\t}\n\treturn result;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { Api, Model } from "@earendil-works/pi-ai";
1
+ import type { Api, Model } from "@ashx-j/lunr-ai";
2
2
  export declare const USER_MODELS_FILENAME = "user-models.json";
3
3
  export interface UserModelEntry {
4
4
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"user-models.d.ts","sourceRoot":"","sources":["../../src/core/user-models.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAIxD,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9B,gBAAgB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEvE;AA+CD,wBAAgB,eAAe,IAAI,cAAc,CAEhD;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAc3D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGxE;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC5B;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,CASrD;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,CAQrD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,cAAc,CAI1G;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAelE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,cAAc,CAgBlE;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,IAAI,CAAiB;IAE7B,YAAY,IAAI,EAAE,MAAM,GAAG,SAAS,EAGnC;IAED,IAAI,IAAI,cAAc,EAAE,CAEvB;IAED,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAElB;IAED,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAE1C;IAED,aAAa,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,cAAc,EAAE,CAO9D;IAED,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CAOlD;IAED,MAAM,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAI5C;IAED,OAAO,CAAC,OAAO;CAQf","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { modelKey } from \"./official-catalog.ts\";\n\nexport const USER_MODELS_FILENAME = \"user-models.json\";\n\nexport interface UserModelEntry {\n\tid: string;\n\tname: string;\n\tapi: Api;\n\tprovider: string;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: Model<Api>[\"cost\"];\n\tcontextWindow: number;\n\tmaxTokens: number;\n\tcompat?: Model<Api>[\"compat\"];\n\tthinkingLevelMap?: Model<Api>[\"thinkingLevelMap\"];\n\tsource: \"user\";\n}\n\nexport interface UserModelsFile {\n\tversion: number;\n\tmodels: UserModelEntry[];\n}\n\nexport function userModelsPath(agentDir: string = getAgentDir()): string {\n\treturn join(agentDir, USER_MODELS_FILENAME);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\treturn undefined;\n}\n\nfunction parseUserEntry(value: unknown): UserModelEntry | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = typeof value.id === \"string\" ? value.id.trim() : \"\";\n\tconst provider = typeof value.provider === \"string\" ? value.provider.trim() : \"\";\n\tif (!id || !provider) return undefined;\n\tconst parsedInput: (\"text\" | \"image\")[] = Array.isArray(value.input)\n\t\t? value.input.filter((entry): entry is \"text\" | \"image\" => entry === \"text\" || entry === \"image\")\n\t\t: [\"text\"];\n\tconst input: (\"text\" | \"image\")[] = parsedInput.length > 0 ? parsedInput : [\"text\"];\n\tconst cost = isRecord(value.cost)\n\t\t? {\n\t\t\t\tinput: asFiniteNumber(value.cost.input) ?? 0,\n\t\t\t\toutput: asFiniteNumber(value.cost.output) ?? 0,\n\t\t\t\tcacheRead: asFiniteNumber(value.cost.cacheRead) ?? 0,\n\t\t\t\tcacheWrite: asFiniteNumber(value.cost.cacheWrite) ?? 0,\n\t\t\t}\n\t\t: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\treturn {\n\t\tid,\n\t\tname: typeof value.name === \"string\" && value.name.trim() ? value.name : id,\n\t\tapi: typeof value.api === \"string\" && value.api.trim() ? value.api : \"openai-completions\",\n\t\tprovider,\n\t\tbaseUrl: typeof value.baseUrl === \"string\" ? value.baseUrl : \"\",\n\t\treasoning: value.reasoning === true,\n\t\tinput,\n\t\tcost,\n\t\tcontextWindow: asFiniteNumber(value.contextWindow) ?? 128000,\n\t\tmaxTokens: asFiniteNumber(value.maxTokens) ?? 8192,\n\t\tcompat: isRecord(value.compat) ? (value.compat as Model<Api>[\"compat\"]) : undefined,\n\t\tthinkingLevelMap: isRecord(value.thinkingLevelMap)\n\t\t\t? (value.thinkingLevelMap as Model<Api>[\"thinkingLevelMap\"])\n\t\t\t: undefined,\n\t\tsource: \"user\",\n\t};\n}\n\nexport function emptyUserModels(): UserModelsFile {\n\treturn { version: 1, models: [] };\n}\n\n/** Read user-models.json. Missing or unreadable files yield an empty document (never throws). */\nexport function readUserModels(path: string): UserModelsFile {\n\ttry {\n\t\tif (!existsSync(path)) return emptyUserModels();\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\tif (!isRecord(parsed) || !Array.isArray(parsed.models)) return emptyUserModels();\n\t\tconst models: UserModelEntry[] = [];\n\t\tfor (const entry of parsed.models) {\n\t\t\tconst model = parseUserEntry(entry);\n\t\t\tif (model) models.push(model);\n\t\t}\n\t\treturn { version: asFiniteNumber(parsed.version) ?? 1, models };\n\t} catch {\n\t\treturn emptyUserModels();\n\t}\n}\n\nexport function writeUserModels(file: UserModelsFile, path: string): void {\n\tmkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n\twriteFileSync(path, `${JSON.stringify({ version: file.version ?? 1, models: file.models }, null, 2)}\\n`, \"utf-8\");\n}\n\nexport function evictUserModelsOnOfficial(\n\tuser: UserModelsFile,\n\tofficialKeys: Iterable<string>,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst hits = new Set(officialKeys);\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (hits.has(modelKey(model.provider, model.id))) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function evictUserModelsByProvider(\n\tuser: UserModelsFile,\n\tproviderId: string,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (model.provider === providerId) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function upsertUserModels(existing: UserModelsFile, rows: readonly UserModelEntry[]): UserModelsFile {\n\tconst byKey = new Map(existing.models.map((model) => [modelKey(model.provider, model.id), model]));\n\tfor (const row of rows) byKey.set(modelKey(row.provider, row.id), { ...row, source: \"user\" });\n\treturn { version: existing.version ?? 1, models: [...byKey.values()] };\n}\n\nexport function userEntryToModel(entry: UserModelEntry): Model<Api> {\n\treturn {\n\t\tid: entry.id,\n\t\tname: entry.name,\n\t\tapi: entry.api,\n\t\tprovider: entry.provider,\n\t\tbaseUrl: entry.baseUrl,\n\t\treasoning: entry.reasoning,\n\t\tinput: entry.input,\n\t\tcost: entry.cost,\n\t\tcontextWindow: entry.contextWindow,\n\t\tmaxTokens: entry.maxTokens,\n\t\tcompat: entry.compat,\n\t\tthinkingLevelMap: entry.thinkingLevelMap,\n\t};\n}\n\nexport function modelToUserEntry(model: Model<Api>): UserModelEntry {\n\treturn {\n\t\tid: model.id,\n\t\tname: model.name,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tbaseUrl: model.baseUrl,\n\t\treasoning: model.reasoning,\n\t\tinput: model.input,\n\t\tcost: model.cost,\n\t\tcontextWindow: model.contextWindow,\n\t\tmaxTokens: model.maxTokens,\n\t\tcompat: model.compat,\n\t\tthinkingLevelMap: model.thinkingLevelMap,\n\t\tsource: \"user\",\n\t};\n}\n\nexport class UserModelsStore {\n\tprivate readonly path: string | undefined;\n\tprivate file: UserModelsFile;\n\n\tconstructor(path: string | undefined) {\n\t\tthis.path = path;\n\t\tthis.file = path ? readUserModels(path) : emptyUserModels();\n\t}\n\n\tlist(): UserModelEntry[] {\n\t\treturn this.file.models;\n\t}\n\n\tkeys(): Set<string> {\n\t\treturn new Set(this.file.models.map((model) => modelKey(model.provider, model.id)));\n\t}\n\n\tmodelsFor(providerId: string): Model<Api>[] {\n\t\treturn this.file.models.filter((model) => model.provider === providerId).map(userEntryToModel);\n\t}\n\n\tevictOfficial(officialKeys: Iterable<string>): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsOnOfficial(this.file, officialKeys);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tevictProvider(providerId: string): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsByProvider(this.file, providerId);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tupsert(rows: readonly UserModelEntry[]): void {\n\t\tif (rows.length === 0) return;\n\t\tthis.file = upsertUserModels(this.file, rows);\n\t\tthis.persist();\n\t}\n\n\tprivate persist(): void {\n\t\tif (!this.path) return;\n\t\ttry {\n\t\t\twriteUserModels(this.file, this.path);\n\t\t} catch {\n\t\t\t// Persist is best-effort in test / in-memory runtimes.\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"user-models.d.ts","sourceRoot":"","sources":["../../src/core/user-models.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAIxD,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9B,gBAAgB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEvE;AA+CD,wBAAgB,eAAe,IAAI,cAAc,CAEhD;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAc3D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGxE;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,cAAc,EACpB,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC5B;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,CASrD;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,CAQrD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,cAAc,CAI1G;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAelE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,cAAc,CAgBlE;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,IAAI,CAAiB;IAE7B,YAAY,IAAI,EAAE,MAAM,GAAG,SAAS,EAGnC;IAED,IAAI,IAAI,cAAc,EAAE,CAEvB;IAED,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAElB;IAED,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAE1C;IAED,aAAa,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,cAAc,EAAE,CAO9D;IAED,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CAOlD;IAED,MAAM,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAI5C;IAED,OAAO,CAAC,OAAO;CAQf","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { Api, Model } from \"@ashx-j/lunr-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { modelKey } from \"./official-catalog.ts\";\n\nexport const USER_MODELS_FILENAME = \"user-models.json\";\n\nexport interface UserModelEntry {\n\tid: string;\n\tname: string;\n\tapi: Api;\n\tprovider: string;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: Model<Api>[\"cost\"];\n\tcontextWindow: number;\n\tmaxTokens: number;\n\tcompat?: Model<Api>[\"compat\"];\n\tthinkingLevelMap?: Model<Api>[\"thinkingLevelMap\"];\n\tsource: \"user\";\n}\n\nexport interface UserModelsFile {\n\tversion: number;\n\tmodels: UserModelEntry[];\n}\n\nexport function userModelsPath(agentDir: string = getAgentDir()): string {\n\treturn join(agentDir, USER_MODELS_FILENAME);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\treturn undefined;\n}\n\nfunction parseUserEntry(value: unknown): UserModelEntry | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = typeof value.id === \"string\" ? value.id.trim() : \"\";\n\tconst provider = typeof value.provider === \"string\" ? value.provider.trim() : \"\";\n\tif (!id || !provider) return undefined;\n\tconst parsedInput: (\"text\" | \"image\")[] = Array.isArray(value.input)\n\t\t? value.input.filter((entry): entry is \"text\" | \"image\" => entry === \"text\" || entry === \"image\")\n\t\t: [\"text\"];\n\tconst input: (\"text\" | \"image\")[] = parsedInput.length > 0 ? parsedInput : [\"text\"];\n\tconst cost = isRecord(value.cost)\n\t\t? {\n\t\t\t\tinput: asFiniteNumber(value.cost.input) ?? 0,\n\t\t\t\toutput: asFiniteNumber(value.cost.output) ?? 0,\n\t\t\t\tcacheRead: asFiniteNumber(value.cost.cacheRead) ?? 0,\n\t\t\t\tcacheWrite: asFiniteNumber(value.cost.cacheWrite) ?? 0,\n\t\t\t}\n\t\t: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\treturn {\n\t\tid,\n\t\tname: typeof value.name === \"string\" && value.name.trim() ? value.name : id,\n\t\tapi: typeof value.api === \"string\" && value.api.trim() ? value.api : \"openai-completions\",\n\t\tprovider,\n\t\tbaseUrl: typeof value.baseUrl === \"string\" ? value.baseUrl : \"\",\n\t\treasoning: value.reasoning === true,\n\t\tinput,\n\t\tcost,\n\t\tcontextWindow: asFiniteNumber(value.contextWindow) ?? 128000,\n\t\tmaxTokens: asFiniteNumber(value.maxTokens) ?? 8192,\n\t\tcompat: isRecord(value.compat) ? (value.compat as Model<Api>[\"compat\"]) : undefined,\n\t\tthinkingLevelMap: isRecord(value.thinkingLevelMap)\n\t\t\t? (value.thinkingLevelMap as Model<Api>[\"thinkingLevelMap\"])\n\t\t\t: undefined,\n\t\tsource: \"user\",\n\t};\n}\n\nexport function emptyUserModels(): UserModelsFile {\n\treturn { version: 1, models: [] };\n}\n\n/** Read user-models.json. Missing or unreadable files yield an empty document (never throws). */\nexport function readUserModels(path: string): UserModelsFile {\n\ttry {\n\t\tif (!existsSync(path)) return emptyUserModels();\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\tif (!isRecord(parsed) || !Array.isArray(parsed.models)) return emptyUserModels();\n\t\tconst models: UserModelEntry[] = [];\n\t\tfor (const entry of parsed.models) {\n\t\t\tconst model = parseUserEntry(entry);\n\t\t\tif (model) models.push(model);\n\t\t}\n\t\treturn { version: asFiniteNumber(parsed.version) ?? 1, models };\n\t} catch {\n\t\treturn emptyUserModels();\n\t}\n}\n\nexport function writeUserModels(file: UserModelsFile, path: string): void {\n\tmkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n\twriteFileSync(path, `${JSON.stringify({ version: file.version ?? 1, models: file.models }, null, 2)}\\n`, \"utf-8\");\n}\n\nexport function evictUserModelsOnOfficial(\n\tuser: UserModelsFile,\n\tofficialKeys: Iterable<string>,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst hits = new Set(officialKeys);\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (hits.has(modelKey(model.provider, model.id))) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function evictUserModelsByProvider(\n\tuser: UserModelsFile,\n\tproviderId: string,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (model.provider === providerId) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function upsertUserModels(existing: UserModelsFile, rows: readonly UserModelEntry[]): UserModelsFile {\n\tconst byKey = new Map(existing.models.map((model) => [modelKey(model.provider, model.id), model]));\n\tfor (const row of rows) byKey.set(modelKey(row.provider, row.id), { ...row, source: \"user\" });\n\treturn { version: existing.version ?? 1, models: [...byKey.values()] };\n}\n\nexport function userEntryToModel(entry: UserModelEntry): Model<Api> {\n\treturn {\n\t\tid: entry.id,\n\t\tname: entry.name,\n\t\tapi: entry.api,\n\t\tprovider: entry.provider,\n\t\tbaseUrl: entry.baseUrl,\n\t\treasoning: entry.reasoning,\n\t\tinput: entry.input,\n\t\tcost: entry.cost,\n\t\tcontextWindow: entry.contextWindow,\n\t\tmaxTokens: entry.maxTokens,\n\t\tcompat: entry.compat,\n\t\tthinkingLevelMap: entry.thinkingLevelMap,\n\t};\n}\n\nexport function modelToUserEntry(model: Model<Api>): UserModelEntry {\n\treturn {\n\t\tid: model.id,\n\t\tname: model.name,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tbaseUrl: model.baseUrl,\n\t\treasoning: model.reasoning,\n\t\tinput: model.input,\n\t\tcost: model.cost,\n\t\tcontextWindow: model.contextWindow,\n\t\tmaxTokens: model.maxTokens,\n\t\tcompat: model.compat,\n\t\tthinkingLevelMap: model.thinkingLevelMap,\n\t\tsource: \"user\",\n\t};\n}\n\nexport class UserModelsStore {\n\tprivate readonly path: string | undefined;\n\tprivate file: UserModelsFile;\n\n\tconstructor(path: string | undefined) {\n\t\tthis.path = path;\n\t\tthis.file = path ? readUserModels(path) : emptyUserModels();\n\t}\n\n\tlist(): UserModelEntry[] {\n\t\treturn this.file.models;\n\t}\n\n\tkeys(): Set<string> {\n\t\treturn new Set(this.file.models.map((model) => modelKey(model.provider, model.id)));\n\t}\n\n\tmodelsFor(providerId: string): Model<Api>[] {\n\t\treturn this.file.models.filter((model) => model.provider === providerId).map(userEntryToModel);\n\t}\n\n\tevictOfficial(officialKeys: Iterable<string>): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsOnOfficial(this.file, officialKeys);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tevictProvider(providerId: string): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsByProvider(this.file, providerId);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tupsert(rows: readonly UserModelEntry[]): void {\n\t\tif (rows.length === 0) return;\n\t\tthis.file = upsertUserModels(this.file, rows);\n\t\tthis.persist();\n\t}\n\n\tprivate persist(): void {\n\t\tif (!this.path) return;\n\t\ttry {\n\t\t\twriteUserModels(this.file, this.path);\n\t\t} catch {\n\t\t\t// Persist is best-effort in test / in-memory runtimes.\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"user-models.js","sourceRoot":"","sources":["../../src/core/user-models.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { modelKey } from \"./official-catalog.ts\";\n\nexport const USER_MODELS_FILENAME = \"user-models.json\";\n\nexport interface UserModelEntry {\n\tid: string;\n\tname: string;\n\tapi: Api;\n\tprovider: string;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: Model<Api>[\"cost\"];\n\tcontextWindow: number;\n\tmaxTokens: number;\n\tcompat?: Model<Api>[\"compat\"];\n\tthinkingLevelMap?: Model<Api>[\"thinkingLevelMap\"];\n\tsource: \"user\";\n}\n\nexport interface UserModelsFile {\n\tversion: number;\n\tmodels: UserModelEntry[];\n}\n\nexport function userModelsPath(agentDir: string = getAgentDir()): string {\n\treturn join(agentDir, USER_MODELS_FILENAME);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\treturn undefined;\n}\n\nfunction parseUserEntry(value: unknown): UserModelEntry | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = typeof value.id === \"string\" ? value.id.trim() : \"\";\n\tconst provider = typeof value.provider === \"string\" ? value.provider.trim() : \"\";\n\tif (!id || !provider) return undefined;\n\tconst parsedInput: (\"text\" | \"image\")[] = Array.isArray(value.input)\n\t\t? value.input.filter((entry): entry is \"text\" | \"image\" => entry === \"text\" || entry === \"image\")\n\t\t: [\"text\"];\n\tconst input: (\"text\" | \"image\")[] = parsedInput.length > 0 ? parsedInput : [\"text\"];\n\tconst cost = isRecord(value.cost)\n\t\t? {\n\t\t\t\tinput: asFiniteNumber(value.cost.input) ?? 0,\n\t\t\t\toutput: asFiniteNumber(value.cost.output) ?? 0,\n\t\t\t\tcacheRead: asFiniteNumber(value.cost.cacheRead) ?? 0,\n\t\t\t\tcacheWrite: asFiniteNumber(value.cost.cacheWrite) ?? 0,\n\t\t\t}\n\t\t: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\treturn {\n\t\tid,\n\t\tname: typeof value.name === \"string\" && value.name.trim() ? value.name : id,\n\t\tapi: typeof value.api === \"string\" && value.api.trim() ? value.api : \"openai-completions\",\n\t\tprovider,\n\t\tbaseUrl: typeof value.baseUrl === \"string\" ? value.baseUrl : \"\",\n\t\treasoning: value.reasoning === true,\n\t\tinput,\n\t\tcost,\n\t\tcontextWindow: asFiniteNumber(value.contextWindow) ?? 128000,\n\t\tmaxTokens: asFiniteNumber(value.maxTokens) ?? 8192,\n\t\tcompat: isRecord(value.compat) ? (value.compat as Model<Api>[\"compat\"]) : undefined,\n\t\tthinkingLevelMap: isRecord(value.thinkingLevelMap)\n\t\t\t? (value.thinkingLevelMap as Model<Api>[\"thinkingLevelMap\"])\n\t\t\t: undefined,\n\t\tsource: \"user\",\n\t};\n}\n\nexport function emptyUserModels(): UserModelsFile {\n\treturn { version: 1, models: [] };\n}\n\n/** Read user-models.json. Missing or unreadable files yield an empty document (never throws). */\nexport function readUserModels(path: string): UserModelsFile {\n\ttry {\n\t\tif (!existsSync(path)) return emptyUserModels();\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\tif (!isRecord(parsed) || !Array.isArray(parsed.models)) return emptyUserModels();\n\t\tconst models: UserModelEntry[] = [];\n\t\tfor (const entry of parsed.models) {\n\t\t\tconst model = parseUserEntry(entry);\n\t\t\tif (model) models.push(model);\n\t\t}\n\t\treturn { version: asFiniteNumber(parsed.version) ?? 1, models };\n\t} catch {\n\t\treturn emptyUserModels();\n\t}\n}\n\nexport function writeUserModels(file: UserModelsFile, path: string): void {\n\tmkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n\twriteFileSync(path, `${JSON.stringify({ version: file.version ?? 1, models: file.models }, null, 2)}\\n`, \"utf-8\");\n}\n\nexport function evictUserModelsOnOfficial(\n\tuser: UserModelsFile,\n\tofficialKeys: Iterable<string>,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst hits = new Set(officialKeys);\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (hits.has(modelKey(model.provider, model.id))) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function evictUserModelsByProvider(\n\tuser: UserModelsFile,\n\tproviderId: string,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (model.provider === providerId) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function upsertUserModels(existing: UserModelsFile, rows: readonly UserModelEntry[]): UserModelsFile {\n\tconst byKey = new Map(existing.models.map((model) => [modelKey(model.provider, model.id), model]));\n\tfor (const row of rows) byKey.set(modelKey(row.provider, row.id), { ...row, source: \"user\" });\n\treturn { version: existing.version ?? 1, models: [...byKey.values()] };\n}\n\nexport function userEntryToModel(entry: UserModelEntry): Model<Api> {\n\treturn {\n\t\tid: entry.id,\n\t\tname: entry.name,\n\t\tapi: entry.api,\n\t\tprovider: entry.provider,\n\t\tbaseUrl: entry.baseUrl,\n\t\treasoning: entry.reasoning,\n\t\tinput: entry.input,\n\t\tcost: entry.cost,\n\t\tcontextWindow: entry.contextWindow,\n\t\tmaxTokens: entry.maxTokens,\n\t\tcompat: entry.compat,\n\t\tthinkingLevelMap: entry.thinkingLevelMap,\n\t};\n}\n\nexport function modelToUserEntry(model: Model<Api>): UserModelEntry {\n\treturn {\n\t\tid: model.id,\n\t\tname: model.name,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tbaseUrl: model.baseUrl,\n\t\treasoning: model.reasoning,\n\t\tinput: model.input,\n\t\tcost: model.cost,\n\t\tcontextWindow: model.contextWindow,\n\t\tmaxTokens: model.maxTokens,\n\t\tcompat: model.compat,\n\t\tthinkingLevelMap: model.thinkingLevelMap,\n\t\tsource: \"user\",\n\t};\n}\n\nexport class UserModelsStore {\n\tprivate readonly path: string | undefined;\n\tprivate file: UserModelsFile;\n\n\tconstructor(path: string | undefined) {\n\t\tthis.path = path;\n\t\tthis.file = path ? readUserModels(path) : emptyUserModels();\n\t}\n\n\tlist(): UserModelEntry[] {\n\t\treturn this.file.models;\n\t}\n\n\tkeys(): Set<string> {\n\t\treturn new Set(this.file.models.map((model) => modelKey(model.provider, model.id)));\n\t}\n\n\tmodelsFor(providerId: string): Model<Api>[] {\n\t\treturn this.file.models.filter((model) => model.provider === providerId).map(userEntryToModel);\n\t}\n\n\tevictOfficial(officialKeys: Iterable<string>): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsOnOfficial(this.file, officialKeys);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tevictProvider(providerId: string): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsByProvider(this.file, providerId);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tupsert(rows: readonly UserModelEntry[]): void {\n\t\tif (rows.length === 0) return;\n\t\tthis.file = upsertUserModels(this.file, rows);\n\t\tthis.persist();\n\t}\n\n\tprivate persist(): void {\n\t\tif (!this.path) return;\n\t\ttry {\n\t\t\twriteUserModels(this.file, this.path);\n\t\t} catch {\n\t\t\t// Persist is best-effort in test / in-memory runtimes.\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"user-models.js","sourceRoot":"","sources":["../../src/core/user-models.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { Api, Model } from \"@ashx-j/lunr-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { modelKey } from \"./official-catalog.ts\";\n\nexport const USER_MODELS_FILENAME = \"user-models.json\";\n\nexport interface UserModelEntry {\n\tid: string;\n\tname: string;\n\tapi: Api;\n\tprovider: string;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: Model<Api>[\"cost\"];\n\tcontextWindow: number;\n\tmaxTokens: number;\n\tcompat?: Model<Api>[\"compat\"];\n\tthinkingLevelMap?: Model<Api>[\"thinkingLevelMap\"];\n\tsource: \"user\";\n}\n\nexport interface UserModelsFile {\n\tversion: number;\n\tmodels: UserModelEntry[];\n}\n\nexport function userModelsPath(agentDir: string = getAgentDir()): string {\n\treturn join(agentDir, USER_MODELS_FILENAME);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asFiniteNumber(value: unknown): number | undefined {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\treturn undefined;\n}\n\nfunction parseUserEntry(value: unknown): UserModelEntry | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst id = typeof value.id === \"string\" ? value.id.trim() : \"\";\n\tconst provider = typeof value.provider === \"string\" ? value.provider.trim() : \"\";\n\tif (!id || !provider) return undefined;\n\tconst parsedInput: (\"text\" | \"image\")[] = Array.isArray(value.input)\n\t\t? value.input.filter((entry): entry is \"text\" | \"image\" => entry === \"text\" || entry === \"image\")\n\t\t: [\"text\"];\n\tconst input: (\"text\" | \"image\")[] = parsedInput.length > 0 ? parsedInput : [\"text\"];\n\tconst cost = isRecord(value.cost)\n\t\t? {\n\t\t\t\tinput: asFiniteNumber(value.cost.input) ?? 0,\n\t\t\t\toutput: asFiniteNumber(value.cost.output) ?? 0,\n\t\t\t\tcacheRead: asFiniteNumber(value.cost.cacheRead) ?? 0,\n\t\t\t\tcacheWrite: asFiniteNumber(value.cost.cacheWrite) ?? 0,\n\t\t\t}\n\t\t: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\treturn {\n\t\tid,\n\t\tname: typeof value.name === \"string\" && value.name.trim() ? value.name : id,\n\t\tapi: typeof value.api === \"string\" && value.api.trim() ? value.api : \"openai-completions\",\n\t\tprovider,\n\t\tbaseUrl: typeof value.baseUrl === \"string\" ? value.baseUrl : \"\",\n\t\treasoning: value.reasoning === true,\n\t\tinput,\n\t\tcost,\n\t\tcontextWindow: asFiniteNumber(value.contextWindow) ?? 128000,\n\t\tmaxTokens: asFiniteNumber(value.maxTokens) ?? 8192,\n\t\tcompat: isRecord(value.compat) ? (value.compat as Model<Api>[\"compat\"]) : undefined,\n\t\tthinkingLevelMap: isRecord(value.thinkingLevelMap)\n\t\t\t? (value.thinkingLevelMap as Model<Api>[\"thinkingLevelMap\"])\n\t\t\t: undefined,\n\t\tsource: \"user\",\n\t};\n}\n\nexport function emptyUserModels(): UserModelsFile {\n\treturn { version: 1, models: [] };\n}\n\n/** Read user-models.json. Missing or unreadable files yield an empty document (never throws). */\nexport function readUserModels(path: string): UserModelsFile {\n\ttry {\n\t\tif (!existsSync(path)) return emptyUserModels();\n\t\tconst parsed: unknown = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\tif (!isRecord(parsed) || !Array.isArray(parsed.models)) return emptyUserModels();\n\t\tconst models: UserModelEntry[] = [];\n\t\tfor (const entry of parsed.models) {\n\t\t\tconst model = parseUserEntry(entry);\n\t\t\tif (model) models.push(model);\n\t\t}\n\t\treturn { version: asFiniteNumber(parsed.version) ?? 1, models };\n\t} catch {\n\t\treturn emptyUserModels();\n\t}\n}\n\nexport function writeUserModels(file: UserModelsFile, path: string): void {\n\tmkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n\twriteFileSync(path, `${JSON.stringify({ version: file.version ?? 1, models: file.models }, null, 2)}\\n`, \"utf-8\");\n}\n\nexport function evictUserModelsOnOfficial(\n\tuser: UserModelsFile,\n\tofficialKeys: Iterable<string>,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst hits = new Set(officialKeys);\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (hits.has(modelKey(model.provider, model.id))) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function evictUserModelsByProvider(\n\tuser: UserModelsFile,\n\tproviderId: string,\n): { next: UserModelsFile; evicted: UserModelEntry[] } {\n\tconst kept: UserModelEntry[] = [];\n\tconst evicted: UserModelEntry[] = [];\n\tfor (const model of user.models) {\n\t\tif (model.provider === providerId) evicted.push(model);\n\t\telse kept.push(model);\n\t}\n\treturn { next: { version: user.version, models: kept }, evicted };\n}\n\nexport function upsertUserModels(existing: UserModelsFile, rows: readonly UserModelEntry[]): UserModelsFile {\n\tconst byKey = new Map(existing.models.map((model) => [modelKey(model.provider, model.id), model]));\n\tfor (const row of rows) byKey.set(modelKey(row.provider, row.id), { ...row, source: \"user\" });\n\treturn { version: existing.version ?? 1, models: [...byKey.values()] };\n}\n\nexport function userEntryToModel(entry: UserModelEntry): Model<Api> {\n\treturn {\n\t\tid: entry.id,\n\t\tname: entry.name,\n\t\tapi: entry.api,\n\t\tprovider: entry.provider,\n\t\tbaseUrl: entry.baseUrl,\n\t\treasoning: entry.reasoning,\n\t\tinput: entry.input,\n\t\tcost: entry.cost,\n\t\tcontextWindow: entry.contextWindow,\n\t\tmaxTokens: entry.maxTokens,\n\t\tcompat: entry.compat,\n\t\tthinkingLevelMap: entry.thinkingLevelMap,\n\t};\n}\n\nexport function modelToUserEntry(model: Model<Api>): UserModelEntry {\n\treturn {\n\t\tid: model.id,\n\t\tname: model.name,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tbaseUrl: model.baseUrl,\n\t\treasoning: model.reasoning,\n\t\tinput: model.input,\n\t\tcost: model.cost,\n\t\tcontextWindow: model.contextWindow,\n\t\tmaxTokens: model.maxTokens,\n\t\tcompat: model.compat,\n\t\tthinkingLevelMap: model.thinkingLevelMap,\n\t\tsource: \"user\",\n\t};\n}\n\nexport class UserModelsStore {\n\tprivate readonly path: string | undefined;\n\tprivate file: UserModelsFile;\n\n\tconstructor(path: string | undefined) {\n\t\tthis.path = path;\n\t\tthis.file = path ? readUserModels(path) : emptyUserModels();\n\t}\n\n\tlist(): UserModelEntry[] {\n\t\treturn this.file.models;\n\t}\n\n\tkeys(): Set<string> {\n\t\treturn new Set(this.file.models.map((model) => modelKey(model.provider, model.id)));\n\t}\n\n\tmodelsFor(providerId: string): Model<Api>[] {\n\t\treturn this.file.models.filter((model) => model.provider === providerId).map(userEntryToModel);\n\t}\n\n\tevictOfficial(officialKeys: Iterable<string>): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsOnOfficial(this.file, officialKeys);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tevictProvider(providerId: string): UserModelEntry[] {\n\t\tconst { next, evicted } = evictUserModelsByProvider(this.file, providerId);\n\t\tif (evicted.length > 0) {\n\t\t\tthis.file = next;\n\t\t\tthis.persist();\n\t\t}\n\t\treturn evicted;\n\t}\n\n\tupsert(rows: readonly UserModelEntry[]): void {\n\t\tif (rows.length === 0) return;\n\t\tthis.file = upsertUserModels(this.file, rows);\n\t\tthis.persist();\n\t}\n\n\tprivate persist(): void {\n\t\tif (!this.path) return;\n\t\ttry {\n\t\t\twriteUserModels(this.file, this.path);\n\t\t} catch {\n\t\t\t// Persist is best-effort in test / in-memory runtimes.\n\t\t}\n\t}\n}\n"]}
@@ -27,9 +27,9 @@
27
27
  * during the turn — e.g. the `cron` tool's create action — can read the chat
28
28
  * the message came from and stamp it as the job's delivery origin.
29
29
  */
30
- import type { AgentMessage, ThinkingLevel } from "@earendil-works/pi-agent-core";
31
- import type { ImageContent } from "@earendil-works/pi-ai";
32
- import type { Model } from "@earendil-works/pi-ai/compat";
30
+ import type { AgentMessage, ThinkingLevel } from "@ashx-j/lunr-agent";
31
+ import type { ImageContent } from "@ashx-j/lunr-ai";
32
+ import type { Model } from "@ashx-j/lunr-ai/compat";
33
33
  import type { AgentSessionEvent, SessionStats } from "../core/agent-session.ts";
34
34
  import type { CompactionResult } from "../core/compaction/index.ts";
35
35
  import type { ContextUsage, ToolDefinition } from "../core/extensions/types.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage, ImageContent } from \"@earendil-works/pi-ai\";\nimport type { Model } from \"@earendil-works/pi-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ builtinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: [...builtinExtensions] },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\treset(key: string): void {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
1
+ {"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ builtinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: [...builtinExtensions] },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\treset(key: string): void {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}