@ashx-j/lunr 0.1.0 → 0.1.2

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 (869) 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/catalog/models.json +1 -1
  377. package/dist/catalog/providers/opencode-go.json +1 -1
  378. package/dist/catalog/providers/openrouter.json +1 -1
  379. package/dist/catalog/providers/qwen-token-plan-cn.json +1 -1
  380. package/dist/catalog/providers/qwen-token-plan.json +1 -1
  381. package/dist/catalog/publication.json +3 -3
  382. package/dist/cli/args.d.ts +1 -1
  383. package/dist/cli/args.d.ts.map +1 -1
  384. package/dist/cli/args.js.map +1 -1
  385. package/dist/cli/config-selector.d.ts.map +1 -1
  386. package/dist/cli/config-selector.js +1 -1
  387. package/dist/cli/config-selector.js.map +1 -1
  388. package/dist/cli/file-processor.d.ts +1 -1
  389. package/dist/cli/file-processor.d.ts.map +1 -1
  390. package/dist/cli/file-processor.js.map +1 -1
  391. package/dist/cli/initial-message.d.ts +1 -1
  392. package/dist/cli/initial-message.d.ts.map +1 -1
  393. package/dist/cli/initial-message.js.map +1 -1
  394. package/dist/cli/list-models.d.ts.map +1 -1
  395. package/dist/cli/list-models.js +1 -1
  396. package/dist/cli/list-models.js.map +1 -1
  397. package/dist/cli/session-picker.d.ts.map +1 -1
  398. package/dist/cli/session-picker.js +1 -1
  399. package/dist/cli/session-picker.js.map +1 -1
  400. package/dist/cli/startup-ui.d.ts +1 -1
  401. package/dist/cli/startup-ui.d.ts.map +1 -1
  402. package/dist/cli/startup-ui.js +2 -2
  403. package/dist/cli/startup-ui.js.map +1 -1
  404. package/dist/config.d.ts.map +1 -1
  405. package/dist/config.js +1 -1
  406. package/dist/config.js.map +1 -1
  407. package/dist/core/agent-session-services.d.ts +2 -2
  408. package/dist/core/agent-session-services.d.ts.map +1 -1
  409. package/dist/core/agent-session-services.js.map +1 -1
  410. package/dist/core/agent-session.d.ts +2 -2
  411. package/dist/core/agent-session.d.ts.map +1 -1
  412. package/dist/core/agent-session.js +1 -1
  413. package/dist/core/agent-session.js.map +1 -1
  414. package/dist/core/auth-storage.d.ts +1 -1
  415. package/dist/core/auth-storage.d.ts.map +1 -1
  416. package/dist/core/auth-storage.js.map +1 -1
  417. package/dist/core/cache-stats.d.ts +1 -1
  418. package/dist/core/cache-stats.d.ts.map +1 -1
  419. package/dist/core/cache-stats.js.map +1 -1
  420. package/dist/core/catalog-merge.d.ts +1 -1
  421. package/dist/core/catalog-merge.d.ts.map +1 -1
  422. package/dist/core/catalog-merge.js +7 -1
  423. package/dist/core/catalog-merge.js.map +1 -1
  424. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  425. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  426. package/dist/core/compaction/branch-summarization.js +1 -1
  427. package/dist/core/compaction/branch-summarization.js.map +1 -1
  428. package/dist/core/compaction/compaction.d.ts +2 -2
  429. package/dist/core/compaction/compaction.d.ts.map +1 -1
  430. package/dist/core/compaction/compaction.js +1 -1
  431. package/dist/core/compaction/compaction.js.map +1 -1
  432. package/dist/core/compaction/utils.d.ts +2 -2
  433. package/dist/core/compaction/utils.d.ts.map +1 -1
  434. package/dist/core/compaction/utils.js.map +1 -1
  435. package/dist/core/context-breakdown.d.ts +1 -1
  436. package/dist/core/context-breakdown.d.ts.map +1 -1
  437. package/dist/core/context-breakdown.js.map +1 -1
  438. package/dist/core/defaults.d.ts +1 -1
  439. package/dist/core/defaults.d.ts.map +1 -1
  440. package/dist/core/defaults.js.map +1 -1
  441. package/dist/core/export-html/index.d.ts +1 -1
  442. package/dist/core/export-html/index.d.ts.map +1 -1
  443. package/dist/core/export-html/index.js.map +1 -1
  444. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  445. package/dist/core/export-html/tool-renderer.js.map +1 -1
  446. package/dist/core/extensions/loader.d.ts.map +1 -1
  447. package/dist/core/extensions/loader.js +25 -25
  448. package/dist/core/extensions/loader.js.map +1 -1
  449. package/dist/core/extensions/runner.d.ts +3 -3
  450. package/dist/core/extensions/runner.d.ts.map +1 -1
  451. package/dist/core/extensions/runner.js.map +1 -1
  452. package/dist/core/extensions/types.d.ts +5 -5
  453. package/dist/core/extensions/types.d.ts.map +1 -1
  454. package/dist/core/extensions/types.js.map +1 -1
  455. package/dist/core/extensions/wrapper.d.ts +1 -1
  456. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  457. package/dist/core/extensions/wrapper.js.map +1 -1
  458. package/dist/core/grok-cli-auth.d.ts +32 -0
  459. package/dist/core/grok-cli-auth.d.ts.map +1 -0
  460. package/dist/core/grok-cli-auth.js +218 -0
  461. package/dist/core/grok-cli-auth.js.map +1 -0
  462. package/dist/core/keybindings.d.ts +2 -2
  463. package/dist/core/keybindings.d.ts.map +1 -1
  464. package/dist/core/keybindings.js +1 -1
  465. package/dist/core/keybindings.js.map +1 -1
  466. package/dist/core/live-catalog.d.ts +1 -1
  467. package/dist/core/live-catalog.d.ts.map +1 -1
  468. package/dist/core/live-catalog.js.map +1 -1
  469. package/dist/core/messages.d.ts +3 -3
  470. package/dist/core/messages.d.ts.map +1 -1
  471. package/dist/core/messages.js.map +1 -1
  472. package/dist/core/model-registry.d.ts +1 -1
  473. package/dist/core/model-registry.d.ts.map +1 -1
  474. package/dist/core/model-registry.js.map +1 -1
  475. package/dist/core/model-resolver.d.ts +2 -2
  476. package/dist/core/model-resolver.d.ts.map +1 -1
  477. package/dist/core/model-resolver.js +1 -1
  478. package/dist/core/model-resolver.js.map +1 -1
  479. package/dist/core/model-runtime.d.ts +10 -1
  480. package/dist/core/model-runtime.d.ts.map +1 -1
  481. package/dist/core/model-runtime.js +24 -5
  482. package/dist/core/model-runtime.js.map +1 -1
  483. package/dist/core/models-store.d.ts +1 -1
  484. package/dist/core/models-store.d.ts.map +1 -1
  485. package/dist/core/models-store.js.map +1 -1
  486. package/dist/core/official-catalog.d.ts +1 -1
  487. package/dist/core/official-catalog.d.ts.map +1 -1
  488. package/dist/core/official-catalog.js.map +1 -1
  489. package/dist/core/provider-attribution.d.ts +1 -1
  490. package/dist/core/provider-attribution.d.ts.map +1 -1
  491. package/dist/core/provider-attribution.js.map +1 -1
  492. package/dist/core/provider-composer.d.ts +1 -1
  493. package/dist/core/provider-composer.d.ts.map +1 -1
  494. package/dist/core/provider-composer.js +2 -2
  495. package/dist/core/provider-composer.js.map +1 -1
  496. package/dist/core/runtime-credentials.d.ts +1 -1
  497. package/dist/core/runtime-credentials.d.ts.map +1 -1
  498. package/dist/core/runtime-credentials.js.map +1 -1
  499. package/dist/core/sdk.d.ts +3 -3
  500. package/dist/core/sdk.d.ts.map +1 -1
  501. package/dist/core/sdk.js +3 -3
  502. package/dist/core/sdk.js.map +1 -1
  503. package/dist/core/session-manager.d.ts +2 -2
  504. package/dist/core/session-manager.d.ts.map +1 -1
  505. package/dist/core/session-manager.js +1 -1
  506. package/dist/core/session-manager.js.map +1 -1
  507. package/dist/core/settings-manager.d.ts +2 -2
  508. package/dist/core/settings-manager.d.ts.map +1 -1
  509. package/dist/core/settings-manager.js.map +1 -1
  510. package/dist/core/subscriptions.d.ts +1 -1
  511. package/dist/core/subscriptions.d.ts.map +1 -1
  512. package/dist/core/subscriptions.js.map +1 -1
  513. package/dist/core/tools/bash.d.ts +1 -1
  514. package/dist/core/tools/bash.d.ts.map +1 -1
  515. package/dist/core/tools/bash.js +1 -1
  516. package/dist/core/tools/bash.js.map +1 -1
  517. package/dist/core/tools/edit.d.ts +2 -2
  518. package/dist/core/tools/edit.d.ts.map +1 -1
  519. package/dist/core/tools/edit.js +1 -1
  520. package/dist/core/tools/edit.js.map +1 -1
  521. package/dist/core/tools/find.d.ts +1 -1
  522. package/dist/core/tools/find.d.ts.map +1 -1
  523. package/dist/core/tools/find.js +1 -1
  524. package/dist/core/tools/find.js.map +1 -1
  525. package/dist/core/tools/grep.d.ts +1 -1
  526. package/dist/core/tools/grep.d.ts.map +1 -1
  527. package/dist/core/tools/grep.js +1 -1
  528. package/dist/core/tools/grep.js.map +1 -1
  529. package/dist/core/tools/index.d.ts +1 -1
  530. package/dist/core/tools/index.d.ts.map +1 -1
  531. package/dist/core/tools/index.js.map +1 -1
  532. package/dist/core/tools/ls.d.ts +1 -1
  533. package/dist/core/tools/ls.d.ts.map +1 -1
  534. package/dist/core/tools/ls.js +1 -1
  535. package/dist/core/tools/ls.js.map +1 -1
  536. package/dist/core/tools/read.d.ts +1 -1
  537. package/dist/core/tools/read.d.ts.map +1 -1
  538. package/dist/core/tools/read.js +1 -1
  539. package/dist/core/tools/read.js.map +1 -1
  540. package/dist/core/tools/render-utils.d.ts +1 -1
  541. package/dist/core/tools/render-utils.d.ts.map +1 -1
  542. package/dist/core/tools/render-utils.js +1 -1
  543. package/dist/core/tools/render-utils.js.map +1 -1
  544. package/dist/core/tools/tool-definition-wrapper.d.ts +1 -1
  545. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  546. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  547. package/dist/core/tools/write.d.ts +1 -1
  548. package/dist/core/tools/write.d.ts.map +1 -1
  549. package/dist/core/tools/write.js +1 -1
  550. package/dist/core/tools/write.js.map +1 -1
  551. package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
  552. package/dist/core/usage-adapters/openai-codex.js.map +1 -1
  553. package/dist/core/usage-adapters/xai.d.ts.map +1 -1
  554. package/dist/core/usage-adapters/xai.js +4 -0
  555. package/dist/core/usage-adapters/xai.js.map +1 -1
  556. package/dist/core/usage-history.d.ts.map +1 -1
  557. package/dist/core/usage-history.js.map +1 -1
  558. package/dist/core/usage-service.d.ts +11 -1
  559. package/dist/core/usage-service.d.ts.map +1 -1
  560. package/dist/core/usage-service.js +45 -9
  561. package/dist/core/usage-service.js.map +1 -1
  562. package/dist/core/user-models.d.ts +1 -1
  563. package/dist/core/user-models.d.ts.map +1 -1
  564. package/dist/core/user-models.js.map +1 -1
  565. package/dist/gateway/agent-bridge.d.ts +3 -3
  566. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  567. package/dist/gateway/agent-bridge.js.map +1 -1
  568. package/dist/gateway/commands.d.ts.map +1 -1
  569. package/dist/gateway/commands.js.map +1 -1
  570. package/dist/gateway/cron.d.ts.map +1 -1
  571. package/dist/gateway/cron.js.map +1 -1
  572. package/dist/main.d.ts.map +1 -1
  573. package/dist/main.js +1 -1
  574. package/dist/main.js.map +1 -1
  575. package/dist/modes/interactive/components/armin.d.ts +1 -1
  576. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  577. package/dist/modes/interactive/components/armin.js.map +1 -1
  578. package/dist/modes/interactive/components/assistant-message.d.ts +2 -2
  579. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  580. package/dist/modes/interactive/components/assistant-message.js +1 -1
  581. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  582. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  583. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  584. package/dist/modes/interactive/components/bash-execution.js +1 -1
  585. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  586. package/dist/modes/interactive/components/boot-screen.d.ts +1 -1
  587. package/dist/modes/interactive/components/boot-screen.d.ts.map +1 -1
  588. package/dist/modes/interactive/components/boot-screen.js +1 -1
  589. package/dist/modes/interactive/components/boot-screen.js.map +1 -1
  590. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  591. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  592. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  593. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  594. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  595. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  596. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  597. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  598. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  599. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  600. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  601. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  602. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  603. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  604. package/dist/modes/interactive/components/config-selector.js +1 -1
  605. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  606. package/dist/modes/interactive/components/countdown-timer.d.ts +1 -1
  607. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  608. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  609. package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
  610. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  611. package/dist/modes/interactive/components/custom-editor.js +1 -1
  612. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  613. package/dist/modes/interactive/components/custom-entry.d.ts +1 -1
  614. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  615. package/dist/modes/interactive/components/custom-entry.js +1 -1
  616. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  617. package/dist/modes/interactive/components/custom-message.d.ts +1 -1
  618. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  619. package/dist/modes/interactive/components/custom-message.js +1 -1
  620. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  621. package/dist/modes/interactive/components/daxnuts.d.ts +1 -1
  622. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  623. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  624. package/dist/modes/interactive/components/dynamic-border.d.ts +1 -1
  625. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  626. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  627. package/dist/modes/interactive/components/earendil-announcement.d.ts +1 -1
  628. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  629. package/dist/modes/interactive/components/earendil-announcement.js +1 -1
  630. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  631. package/dist/modes/interactive/components/extension-editor.d.ts +1 -1
  632. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  633. package/dist/modes/interactive/components/extension-editor.js +1 -1
  634. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  635. package/dist/modes/interactive/components/extension-input.d.ts +1 -1
  636. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  637. package/dist/modes/interactive/components/extension-input.js +1 -1
  638. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  639. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  640. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  641. package/dist/modes/interactive/components/extension-selector.js +1 -1
  642. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  643. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  644. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  645. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  646. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  647. package/dist/modes/interactive/components/footer.d.ts +1 -1
  648. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  649. package/dist/modes/interactive/components/footer.js +1 -1
  650. package/dist/modes/interactive/components/footer.js.map +1 -1
  651. package/dist/modes/interactive/components/injected-prompt-message.d.ts +1 -1
  652. package/dist/modes/interactive/components/injected-prompt-message.d.ts.map +1 -1
  653. package/dist/modes/interactive/components/injected-prompt-message.js +1 -1
  654. package/dist/modes/interactive/components/injected-prompt-message.js.map +1 -1
  655. package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -1
  656. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  657. package/dist/modes/interactive/components/keybinding-hints.js +1 -1
  658. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  659. package/dist/modes/interactive/components/login-dialog.d.ts +2 -2
  660. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  661. package/dist/modes/interactive/components/login-dialog.js +1 -1
  662. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  663. package/dist/modes/interactive/components/model-selector.d.ts +2 -2
  664. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  665. package/dist/modes/interactive/components/model-selector.js +2 -2
  666. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  667. package/dist/modes/interactive/components/oauth-selector.d.ts +2 -2
  668. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  669. package/dist/modes/interactive/components/oauth-selector.js +1 -1
  670. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  671. package/dist/modes/interactive/components/processes-selector.d.ts +1 -1
  672. package/dist/modes/interactive/components/processes-selector.d.ts.map +1 -1
  673. package/dist/modes/interactive/components/processes-selector.js +1 -1
  674. package/dist/modes/interactive/components/processes-selector.js.map +1 -1
  675. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  676. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  677. package/dist/modes/interactive/components/scoped-models-selector.js +1 -1
  678. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  679. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  680. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  681. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  682. package/dist/modes/interactive/components/session-selector.d.ts +1 -1
  683. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  684. package/dist/modes/interactive/components/session-selector.js +1 -1
  685. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  686. package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
  687. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  688. package/dist/modes/interactive/components/settings-selector.js +1 -1
  689. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  690. package/dist/modes/interactive/components/show-images-selector.d.ts +1 -1
  691. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  692. package/dist/modes/interactive/components/show-images-selector.js +1 -1
  693. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  694. package/dist/modes/interactive/components/skill-invocation-message.d.ts +1 -1
  695. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  696. package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
  697. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  698. package/dist/modes/interactive/components/status-indicator.d.ts +1 -1
  699. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  700. package/dist/modes/interactive/components/status-indicator.js +1 -1
  701. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  702. package/dist/modes/interactive/components/theme-selector.d.ts +1 -1
  703. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  704. package/dist/modes/interactive/components/theme-selector.js +1 -1
  705. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  706. package/dist/modes/interactive/components/thinking-selector.d.ts +2 -2
  707. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  708. package/dist/modes/interactive/components/thinking-selector.js +1 -1
  709. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  710. package/dist/modes/interactive/components/thinking-summary.d.ts +1 -1
  711. package/dist/modes/interactive/components/thinking-summary.d.ts.map +1 -1
  712. package/dist/modes/interactive/components/thinking-summary.js.map +1 -1
  713. package/dist/modes/interactive/components/thinking-tail.d.ts +1 -1
  714. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  715. package/dist/modes/interactive/components/thinking-tail.js +1 -1
  716. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  717. package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
  718. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  719. package/dist/modes/interactive/components/tool-execution.js +1 -1
  720. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  721. package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
  722. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  723. package/dist/modes/interactive/components/tree-selector.js +1 -1
  724. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  725. package/dist/modes/interactive/components/trust-selector.d.ts +1 -1
  726. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  727. package/dist/modes/interactive/components/trust-selector.js +1 -1
  728. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  729. package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
  730. package/dist/modes/interactive/components/usage-view.js +1 -1
  731. package/dist/modes/interactive/components/usage-view.js.map +1 -1
  732. package/dist/modes/interactive/components/user-message-selector.d.ts +1 -1
  733. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  734. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  735. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  736. package/dist/modes/interactive/components/user-message.d.ts +1 -1
  737. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  738. package/dist/modes/interactive/components/user-message.js +1 -1
  739. package/dist/modes/interactive/components/user-message.js.map +1 -1
  740. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  741. package/dist/modes/interactive/components/visual-truncate.js +1 -1
  742. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  743. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  744. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  745. package/dist/modes/interactive/interactive-mode.js +29 -4
  746. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  747. package/dist/modes/interactive/smooth-streaming.d.ts +1 -1
  748. package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
  749. package/dist/modes/interactive/smooth-streaming.js.map +1 -1
  750. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -1
  751. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  752. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  753. package/dist/modes/interactive/theme/theme.d.ts +2 -2
  754. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  755. package/dist/modes/interactive/theme/theme.js +2 -2
  756. package/dist/modes/interactive/theme/theme.js.map +1 -1
  757. package/dist/modes/print-mode.d.ts +1 -1
  758. package/dist/modes/print-mode.d.ts.map +1 -1
  759. package/dist/modes/print-mode.js.map +1 -1
  760. package/dist/modes/rpc/rpc-client.d.ts +2 -2
  761. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  762. package/dist/modes/rpc/rpc-client.js.map +1 -1
  763. package/dist/modes/rpc/rpc-types.d.ts +2 -2
  764. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  765. package/dist/modes/rpc/rpc-types.js.map +1 -1
  766. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  767. package/examples/extensions/bash-spawn-hook.ts +2 -2
  768. package/examples/extensions/bookmark.ts +1 -1
  769. package/examples/extensions/border-status-editor.ts +3 -3
  770. package/examples/extensions/built-in-tool-renderer.ts +3 -3
  771. package/examples/extensions/claude-rules.ts +1 -1
  772. package/examples/extensions/commands.ts +1 -1
  773. package/examples/extensions/confirm-destructive.ts +1 -1
  774. package/examples/extensions/custom-compaction.ts +3 -3
  775. package/examples/extensions/custom-footer.ts +3 -3
  776. package/examples/extensions/custom-header.ts +2 -2
  777. package/examples/extensions/custom-provider-anthropic/index.ts +2 -2
  778. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  779. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  780. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  781. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  782. package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
  783. package/examples/extensions/dirty-repo-guard.ts +1 -1
  784. package/examples/extensions/doom-overlay/doom-component.ts +2 -2
  785. package/examples/extensions/doom-overlay/doom-keys.ts +1 -1
  786. package/examples/extensions/doom-overlay/index.ts +1 -1
  787. package/examples/extensions/dynamic-resources/index.ts +1 -1
  788. package/examples/extensions/dynamic-tools.ts +1 -1
  789. package/examples/extensions/entry-renderer.ts +2 -2
  790. package/examples/extensions/event-bus.ts +1 -1
  791. package/examples/extensions/file-trigger.ts +1 -1
  792. package/examples/extensions/git-checkpoint.ts +1 -1
  793. package/examples/extensions/git-merge-and-resolve.ts +1 -1
  794. package/examples/extensions/github-issue-autocomplete.ts +2 -2
  795. package/examples/extensions/gondolin/index.ts +2 -2
  796. package/examples/extensions/gondolin/package-lock.json +2 -2
  797. package/examples/extensions/gondolin/package.json +1 -1
  798. package/examples/extensions/handoff.ts +4 -4
  799. package/examples/extensions/hello.ts +2 -2
  800. package/examples/extensions/hidden-thinking-label.ts +1 -1
  801. package/examples/extensions/inline-bash.ts +1 -1
  802. package/examples/extensions/input-transform-streaming.ts +1 -1
  803. package/examples/extensions/input-transform.ts +1 -1
  804. package/examples/extensions/interactive-shell.ts +1 -1
  805. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  806. package/examples/extensions/mac-system-theme.ts +1 -1
  807. package/examples/extensions/message-renderer.ts +2 -2
  808. package/examples/extensions/minimal-mode.ts +3 -3
  809. package/examples/extensions/modal-editor.ts +2 -2
  810. package/examples/extensions/model-status.ts +1 -1
  811. package/examples/extensions/notify.ts +1 -1
  812. package/examples/extensions/overlay-qa-tests.ts +3 -3
  813. package/examples/extensions/overlay-test.ts +2 -2
  814. package/examples/extensions/permission-gate.ts +1 -1
  815. package/examples/extensions/pirate.ts +1 -1
  816. package/examples/extensions/plan-mode/index.ts +4 -4
  817. package/examples/extensions/preset.ts +4 -4
  818. package/examples/extensions/project-trust.ts +1 -1
  819. package/examples/extensions/prompt-customizer.ts +1 -1
  820. package/examples/extensions/protected-paths.ts +1 -1
  821. package/examples/extensions/provider-payload.ts +1 -1
  822. package/examples/extensions/qna.ts +3 -3
  823. package/examples/extensions/question.ts +2 -2
  824. package/examples/extensions/questionnaire.ts +2 -2
  825. package/examples/extensions/rainbow-editor.ts +1 -1
  826. package/examples/extensions/reload-runtime.ts +1 -1
  827. package/examples/extensions/rpc-demo.ts +1 -1
  828. package/examples/extensions/sandbox/index.ts +2 -2
  829. package/examples/extensions/send-user-message.ts +1 -1
  830. package/examples/extensions/session-name.ts +1 -1
  831. package/examples/extensions/shutdown-command.ts +1 -1
  832. package/examples/extensions/snake.ts +2 -2
  833. package/examples/extensions/space-invaders.ts +2 -2
  834. package/examples/extensions/ssh.ts +2 -2
  835. package/examples/extensions/status-line.ts +1 -1
  836. package/examples/extensions/structured-output.ts +2 -2
  837. package/examples/extensions/subagent/agents.ts +1 -1
  838. package/examples/extensions/subagent/index.ts +5 -5
  839. package/examples/extensions/summarize.ts +4 -4
  840. package/examples/extensions/system-prompt-header.ts +1 -1
  841. package/examples/extensions/tic-tac-toe.ts +3 -3
  842. package/examples/extensions/timed-confirm.ts +1 -1
  843. package/examples/extensions/titlebar-spinner.ts +1 -1
  844. package/examples/extensions/todo.ts +3 -3
  845. package/examples/extensions/tool-override.ts +2 -2
  846. package/examples/extensions/tools.ts +3 -3
  847. package/examples/extensions/trigger-compact.ts +1 -1
  848. package/examples/extensions/truncated-tool.ts +3 -3
  849. package/examples/extensions/widget-placement.ts +1 -1
  850. package/examples/extensions/with-deps/index.ts +1 -1
  851. package/examples/extensions/with-deps/package-lock.json +2 -2
  852. package/examples/extensions/with-deps/package.json +1 -1
  853. package/examples/extensions/working-indicator.ts +1 -1
  854. package/examples/extensions/working-message-test.ts +1 -1
  855. package/examples/rpc-extension-ui.ts +1 -1
  856. package/examples/sdk/01-minimal.ts +1 -1
  857. package/examples/sdk/02-custom-model.ts +2 -2
  858. package/examples/sdk/03-custom-prompt.ts +1 -1
  859. package/examples/sdk/04-skills.ts +1 -1
  860. package/examples/sdk/05-tools.ts +1 -1
  861. package/examples/sdk/06-extensions.ts +2 -2
  862. package/examples/sdk/07-context-files.ts +1 -1
  863. package/examples/sdk/08-prompt-templates.ts +1 -1
  864. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  865. package/examples/sdk/10-settings.ts +1 -1
  866. package/examples/sdk/11-sessions.ts +1 -1
  867. package/examples/sdk/12-full-control.ts +2 -2
  868. package/examples/sdk/13-session-runtime.ts +1 -1
  869. 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":"xai.d.ts","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,qBAAqB,CAAC;AA+BtE,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAsC7F","sourcesContent":["/**\n * xAI Grok (SuperGrok / X Premium) weekly plan usage.\n *\n * `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` with the\n * OAuth access token (Grok CLI identity header required). Official Grok\n * Build `/usage` and grok.com Settings → Usage read the same weekly pool:\n * config.creditUsagePercent + config.currentPeriod.end\n *\n * Extra Usage Credits (`onDemandCap` > 0) are a secondary window only.\n * `monthlyLimit` / `includedUsed` / `billingPeriodEnd` are the old monthly\n * Extra Usage envelope — never treated as the subscription plan.\n *\n * Pay-as-you-go API keys have no quota endpoint. OAuth-only; any failure\n * or shape mismatch resolves to `undefined` (caller hides the plan section).\n * First-party undocumented protocol — re-smoke against a real OAuth token\n * after xAI changes the billing payload.\n */\n\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow } from \"../usage-service.ts\";\nimport {\n\tasObject,\n\tasString,\n\tclampPercent,\n\tfetchWithTimeout,\n\ttoEpochMs,\n\tunwrapVal,\n\tusedPercentFromUsed,\n} from \"./shared.ts\";\n\nconst XAI_PROVIDER_ID = \"xai\";\nconst XAI_BILLING_URL = \"https://cli-chat-proxy.grok.com/v1/billing?format=credits\";\n\nfunction currentPeriod(config: Record<string, unknown>): Record<string, unknown> | undefined {\n\treturn asObject(config.currentPeriod) ?? asObject(config.current_period);\n}\n\nfunction weeklyWindow(config: Record<string, unknown>): PlanUsageWindow | undefined {\n\tconst period = currentPeriod(config);\n\tconst raw = unwrapVal(config.creditUsagePercent ?? config.credit_usage_percent);\n\t// Omitted proto3 percent + a live currentPeriod means 0% used (CodexBar).\n\tif (raw === undefined && !period) return undefined;\n\tconst usedPercent = raw === undefined ? 0 : clampPercent(raw);\n\treturn {\n\t\tlabel: \"Weekly\",\n\t\tusedPercent,\n\t\tresetsAt: toEpochMs(period?.end),\n\t};\n}\n\nexport async function fetchXaiPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tif (!runtime.isUsingOAuth(XAI_PROVIDER_ID)) return undefined;\n\n\tconst resolution = await runtime.getAuth(XAI_PROVIDER_ID);\n\tconst accessToken = resolution?.auth.apiKey;\n\tif (!accessToken) return undefined;\n\n\tconst response = await fetchWithTimeout(XAI_BILLING_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"x-xai-token-auth\": \"xai-grok-cli\",\n\t\t},\n\t});\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload) return undefined;\n\tconst config = asObject(payload.config) ?? payload;\n\n\tconst windows: PlanUsageWindow[] = [];\n\tconst weekly = weeklyWindow(config);\n\tif (weekly) windows.push(weekly);\n\n\tconst onDemandCap = unwrapVal(config.onDemandCap);\n\tif (onDemandCap !== undefined && onDemandCap > 0) {\n\t\tconst usedPercent = usedPercentFromUsed(onDemandCap, unwrapVal(config.onDemandUsed) ?? 0);\n\t\tif (usedPercent !== undefined) {\n\t\t\twindows.push({\n\t\t\t\tlabel: \"Extra\",\n\t\t\t\tusedPercent,\n\t\t\t\tresetsAt: weekly?.resetsAt,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(config.subscriptionTier) ?? asString(config.subscription_tier);\n\treturn { provider: XAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
1
+ {"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,qBAAqB,CAAC;AA+BtE,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAyC7F","sourcesContent":["/**\n * xAI Grok (SuperGrok / X Premium) weekly plan usage.\n *\n * `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` with the\n * OAuth access token (Grok CLI identity header required). Official Grok\n * Build `/usage` and grok.com Settings → Usage read the same weekly pool:\n * config.creditUsagePercent + config.currentPeriod.end\n *\n * Extra Usage Credits (`onDemandCap` > 0) are a secondary window only.\n * `monthlyLimit` / `includedUsed` / `billingPeriodEnd` are the old monthly\n * Extra Usage envelope — never treated as the subscription plan.\n *\n * Pay-as-you-go API keys have no quota endpoint. OAuth-only; any failure\n * or shape mismatch resolves to `undefined` (caller hides the plan section).\n * First-party undocumented protocol — re-smoke against a real OAuth token\n * after xAI changes the billing payload.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow } from \"../usage-service.ts\";\nimport {\n\tasObject,\n\tasString,\n\tclampPercent,\n\tfetchWithTimeout,\n\ttoEpochMs,\n\tunwrapVal,\n\tusedPercentFromUsed,\n} from \"./shared.ts\";\n\nconst XAI_PROVIDER_ID = \"xai\";\nconst XAI_BILLING_URL = \"https://cli-chat-proxy.grok.com/v1/billing?format=credits\";\n\nfunction currentPeriod(config: Record<string, unknown>): Record<string, unknown> | undefined {\n\treturn asObject(config.currentPeriod) ?? asObject(config.current_period);\n}\n\nfunction weeklyWindow(config: Record<string, unknown>): PlanUsageWindow | undefined {\n\tconst period = currentPeriod(config);\n\tconst raw = unwrapVal(config.creditUsagePercent ?? config.credit_usage_percent);\n\t// Omitted proto3 percent + a live currentPeriod means 0% used (CodexBar).\n\tif (raw === undefined && !period) return undefined;\n\tconst usedPercent = raw === undefined ? 0 : clampPercent(raw);\n\treturn {\n\t\tlabel: \"Weekly\",\n\t\tusedPercent,\n\t\tresetsAt: toEpochMs(period?.end),\n\t};\n}\n\nexport async function fetchXaiPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tif (!runtime.isUsingOAuth(XAI_PROVIDER_ID)) return undefined;\n\n\tconst resolution = await runtime.getAuth(XAI_PROVIDER_ID);\n\tconst accessToken = resolution?.auth.apiKey;\n\tif (!accessToken) return undefined;\n\n\tconst response = await fetchWithTimeout(XAI_BILLING_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"x-xai-token-auth\": \"xai-grok-cli\",\n\t\t},\n\t});\n\tif (response.status === 401 || response.status === 403) {\n\t\tthrow new ModelsError(\"oauth\", `xAI billing rejected the session (HTTP ${response.status})`);\n\t}\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload) return undefined;\n\tconst config = asObject(payload.config) ?? payload;\n\n\tconst windows: PlanUsageWindow[] = [];\n\tconst weekly = weeklyWindow(config);\n\tif (weekly) windows.push(weekly);\n\n\tconst onDemandCap = unwrapVal(config.onDemandCap);\n\tif (onDemandCap !== undefined && onDemandCap > 0) {\n\t\tconst usedPercent = usedPercentFromUsed(onDemandCap, unwrapVal(config.onDemandUsed) ?? 0);\n\t\tif (usedPercent !== undefined) {\n\t\t\twindows.push({\n\t\t\t\tlabel: \"Extra\",\n\t\t\t\tusedPercent,\n\t\t\t\tresetsAt: weekly?.resetsAt,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(config.subscriptionTier) ?? asString(config.subscription_tier);\n\treturn { provider: XAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
@@ -15,6 +15,7 @@
15
15
  * First-party undocumented protocol — re-smoke against a real OAuth token
16
16
  * after xAI changes the billing payload.
17
17
  */
18
+ import { ModelsError } from "@ashx-j/lunr-ai";
18
19
  import { asObject, asString, clampPercent, fetchWithTimeout, toEpochMs, unwrapVal, usedPercentFromUsed, } from "./shared.js";
19
20
  const XAI_PROVIDER_ID = "xai";
20
21
  const XAI_BILLING_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
@@ -48,6 +49,9 @@ export async function fetchXaiPlanUsage(runtime) {
48
49
  "x-xai-token-auth": "xai-grok-cli",
49
50
  },
50
51
  });
52
+ if (response.status === 401 || response.status === 403) {
53
+ throw new ModelsError("oauth", `xAI billing rejected the session (HTTP ${response.status})`);
54
+ }
51
55
  if (!response.ok)
52
56
  return undefined;
53
57
  const payload = asObject(await response.json());
@@ -1 +1 @@
1
- {"version":3,"file":"xai.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EACN,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,mBAAmB,GACnB,MAAM,aAAa,CAAC;AAErB,MAAM,eAAe,GAAG,KAAK,CAAC;AAC9B,MAAM,eAAe,GAAG,2DAA2D,CAAC;AAEpF,SAAS,aAAa,CAAC,MAA+B,EAAuC;IAC5F,OAAO,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAAA,CACzE;AAED,SAAS,YAAY,CAAC,MAA+B,EAA+B;IACnF,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChF,0EAA0E;IAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,WAAW,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC9D,OAAO;QACN,KAAK,EAAE,QAAQ;QACf,WAAW;QACX,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC;KAChC,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAqB,EAAkC;IAC9F,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7D,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;IAC5C,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IAEnC,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,eAAe,EAAE;QACxD,OAAO,EAAE;YACR,aAAa,EAAE,UAAU,WAAW,EAAE;YACtC,MAAM,EAAE,kBAAkB;YAC1B,kBAAkB,EAAE,cAAc;SAClC;KACD,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC;IAEnD,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjC,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAClD,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,WAAW,GAAG,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1F,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,OAAO;gBACd,WAAW;gBACX,QAAQ,EAAE,MAAM,EAAE,QAAQ;aAC1B,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3C,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC1F,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAAA,CACzD","sourcesContent":["/**\n * xAI Grok (SuperGrok / X Premium) weekly plan usage.\n *\n * `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` with the\n * OAuth access token (Grok CLI identity header required). Official Grok\n * Build `/usage` and grok.com Settings → Usage read the same weekly pool:\n * config.creditUsagePercent + config.currentPeriod.end\n *\n * Extra Usage Credits (`onDemandCap` > 0) are a secondary window only.\n * `monthlyLimit` / `includedUsed` / `billingPeriodEnd` are the old monthly\n * Extra Usage envelope — never treated as the subscription plan.\n *\n * Pay-as-you-go API keys have no quota endpoint. OAuth-only; any failure\n * or shape mismatch resolves to `undefined` (caller hides the plan section).\n * First-party undocumented protocol — re-smoke against a real OAuth token\n * after xAI changes the billing payload.\n */\n\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow } from \"../usage-service.ts\";\nimport {\n\tasObject,\n\tasString,\n\tclampPercent,\n\tfetchWithTimeout,\n\ttoEpochMs,\n\tunwrapVal,\n\tusedPercentFromUsed,\n} from \"./shared.ts\";\n\nconst XAI_PROVIDER_ID = \"xai\";\nconst XAI_BILLING_URL = \"https://cli-chat-proxy.grok.com/v1/billing?format=credits\";\n\nfunction currentPeriod(config: Record<string, unknown>): Record<string, unknown> | undefined {\n\treturn asObject(config.currentPeriod) ?? asObject(config.current_period);\n}\n\nfunction weeklyWindow(config: Record<string, unknown>): PlanUsageWindow | undefined {\n\tconst period = currentPeriod(config);\n\tconst raw = unwrapVal(config.creditUsagePercent ?? config.credit_usage_percent);\n\t// Omitted proto3 percent + a live currentPeriod means 0% used (CodexBar).\n\tif (raw === undefined && !period) return undefined;\n\tconst usedPercent = raw === undefined ? 0 : clampPercent(raw);\n\treturn {\n\t\tlabel: \"Weekly\",\n\t\tusedPercent,\n\t\tresetsAt: toEpochMs(period?.end),\n\t};\n}\n\nexport async function fetchXaiPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tif (!runtime.isUsingOAuth(XAI_PROVIDER_ID)) return undefined;\n\n\tconst resolution = await runtime.getAuth(XAI_PROVIDER_ID);\n\tconst accessToken = resolution?.auth.apiKey;\n\tif (!accessToken) return undefined;\n\n\tconst response = await fetchWithTimeout(XAI_BILLING_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"x-xai-token-auth\": \"xai-grok-cli\",\n\t\t},\n\t});\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload) return undefined;\n\tconst config = asObject(payload.config) ?? payload;\n\n\tconst windows: PlanUsageWindow[] = [];\n\tconst weekly = weeklyWindow(config);\n\tif (weekly) windows.push(weekly);\n\n\tconst onDemandCap = unwrapVal(config.onDemandCap);\n\tif (onDemandCap !== undefined && onDemandCap > 0) {\n\t\tconst usedPercent = usedPercentFromUsed(onDemandCap, unwrapVal(config.onDemandUsed) ?? 0);\n\t\tif (usedPercent !== undefined) {\n\t\t\twindows.push({\n\t\t\t\tlabel: \"Extra\",\n\t\t\t\tusedPercent,\n\t\t\t\tresetsAt: weekly?.resetsAt,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(config.subscriptionTier) ?? asString(config.subscription_tier);\n\treturn { provider: XAI_PROVIDER_ID, planLabel, windows };\n}\n"]}
1
+ {"version":3,"file":"xai.js","sourceRoot":"","sources":["../../../src/core/usage-adapters/xai.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * xAI Grok (SuperGrok / X Premium) weekly plan usage.\n *\n * `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits` with the\n * OAuth access token (Grok CLI identity header required). Official Grok\n * Build `/usage` and grok.com Settings → Usage read the same weekly pool:\n * config.creditUsagePercent + config.currentPeriod.end\n *\n * Extra Usage Credits (`onDemandCap` > 0) are a secondary window only.\n * `monthlyLimit` / `includedUsed` / `billingPeriodEnd` are the old monthly\n * Extra Usage envelope — never treated as the subscription plan.\n *\n * Pay-as-you-go API keys have no quota endpoint. OAuth-only; any failure\n * or shape mismatch resolves to `undefined` (caller hides the plan section).\n * First-party undocumented protocol — re-smoke against a real OAuth token\n * after xAI changes the billing payload.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"../model-runtime.ts\";\nimport type { PlanUsage, PlanUsageWindow } from \"../usage-service.ts\";\nimport {\n\tasObject,\n\tasString,\n\tclampPercent,\n\tfetchWithTimeout,\n\ttoEpochMs,\n\tunwrapVal,\n\tusedPercentFromUsed,\n} from \"./shared.ts\";\n\nconst XAI_PROVIDER_ID = \"xai\";\nconst XAI_BILLING_URL = \"https://cli-chat-proxy.grok.com/v1/billing?format=credits\";\n\nfunction currentPeriod(config: Record<string, unknown>): Record<string, unknown> | undefined {\n\treturn asObject(config.currentPeriod) ?? asObject(config.current_period);\n}\n\nfunction weeklyWindow(config: Record<string, unknown>): PlanUsageWindow | undefined {\n\tconst period = currentPeriod(config);\n\tconst raw = unwrapVal(config.creditUsagePercent ?? config.credit_usage_percent);\n\t// Omitted proto3 percent + a live currentPeriod means 0% used (CodexBar).\n\tif (raw === undefined && !period) return undefined;\n\tconst usedPercent = raw === undefined ? 0 : clampPercent(raw);\n\treturn {\n\t\tlabel: \"Weekly\",\n\t\tusedPercent,\n\t\tresetsAt: toEpochMs(period?.end),\n\t};\n}\n\nexport async function fetchXaiPlanUsage(runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tif (!runtime.isUsingOAuth(XAI_PROVIDER_ID)) return undefined;\n\n\tconst resolution = await runtime.getAuth(XAI_PROVIDER_ID);\n\tconst accessToken = resolution?.auth.apiKey;\n\tif (!accessToken) return undefined;\n\n\tconst response = await fetchWithTimeout(XAI_BILLING_URL, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"x-xai-token-auth\": \"xai-grok-cli\",\n\t\t},\n\t});\n\tif (response.status === 401 || response.status === 403) {\n\t\tthrow new ModelsError(\"oauth\", `xAI billing rejected the session (HTTP ${response.status})`);\n\t}\n\tif (!response.ok) return undefined;\n\tconst payload = asObject(await response.json());\n\tif (!payload) return undefined;\n\tconst config = asObject(payload.config) ?? payload;\n\n\tconst windows: PlanUsageWindow[] = [];\n\tconst weekly = weeklyWindow(config);\n\tif (weekly) windows.push(weekly);\n\n\tconst onDemandCap = unwrapVal(config.onDemandCap);\n\tif (onDemandCap !== undefined && onDemandCap > 0) {\n\t\tconst usedPercent = usedPercentFromUsed(onDemandCap, unwrapVal(config.onDemandUsed) ?? 0);\n\t\tif (usedPercent !== undefined) {\n\t\t\twindows.push({\n\t\t\t\tlabel: \"Extra\",\n\t\t\t\tusedPercent,\n\t\t\t\tresetsAt: weekly?.resetsAt,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (windows.length === 0) return undefined;\n\tconst planLabel = asString(config.subscriptionTier) ?? asString(config.subscription_tier);\n\treturn { provider: XAI_PROVIDER_ID, planLabel, windows };\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":"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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 \"@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"]}
@@ -9,7 +9,8 @@
9
9
  * fall back to hiding the cost meter.
10
10
  * - ollama-cloud: no usage API exists.
11
11
  *
12
- * All adapter/network errors resolve to `undefined` callers omit the UI.
12
+ * Adapter/network errors resolve to `undefined` (callers omit the plan UI).
13
+ * Auth failures are different: they return `error` and are not cached as empty.
13
14
  */
14
15
  import type { ModelRuntime } from "./model-runtime.ts";
15
16
  export interface PlanUsageWindow {
@@ -23,11 +24,20 @@ export interface PlanUsage {
23
24
  planLabel?: string;
24
25
  windows: PlanUsageWindow[];
25
26
  }
27
+ export interface PlanUsageResult {
28
+ usage?: PlanUsage;
29
+ /** Actionable auth failure. Not cached as an empty success. */
30
+ error?: string;
31
+ }
32
+ /** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */
33
+ export declare function planUsageAuthError(error: unknown): string | undefined;
26
34
  export declare const PLAN_USAGE_CACHE_TTL_MS: number;
27
35
  /** Whether a plan-usage adapter exists for this provider. */
28
36
  export declare function hasPlanUsageAdapter(providerId: string): boolean;
29
37
  /** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */
30
38
  export declare function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined>;
39
+ /** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */
40
+ export declare function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult>;
31
41
  /** Clear the adapter cache (tests, /login changes). */
32
42
  export declare function clearPlanUsageCache(): void;
33
43
  //# sourceMappingURL=usage-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage-service.d.ts","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMvD,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC3B;AAWD,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AAIrD,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAc5G;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (5-minute cache each) so /usage can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * All adapter/network errors resolve to `undefined` callers omit the UI.\n */\n\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return undefined;\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return cached.value;\n\tlet value: PlanUsage | undefined;\n\ttry {\n\t\tvalue = await adapter(runtime);\n\t} catch {\n\t\tvalue = undefined;\n\t}\n\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });\n\treturn value;\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n"]}
1
+ {"version":3,"file":"usage-service.d.ts","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMvD,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAoBD,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CASrE;AAWD,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AAIrD,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAE5G;AAED,sFAAsF;AACtF,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAgB5G;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (5-minute cache each) so /usage can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * Adapter/network errors resolve to `undefined` (callers omit the plan UI).\n * Auth failures are different: they return `error` and are not cached as empty.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\nexport interface PlanUsageResult {\n\tusage?: PlanUsage;\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nconst XAI_RELOGIN = \"xAI login expired. Run /login xai.\";\n\nfunction errorChain(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */\nexport function planUsageAuthError(error: unknown): string | undefined {\n\tconst chain = errorChain(error);\n\tif (/invalid_grant|oauth refresh failed for xai|xai billing rejected/i.test(chain)) {\n\t\treturn XAI_RELOGIN;\n\t}\n\tif (error instanceof ModelsError && (error.code === \"oauth\" || error.code === \"auth\") && /xai/i.test(chain)) {\n\t\treturn XAI_RELOGIN;\n\t}\n\treturn undefined;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\treturn (await getPlanUsageResult(providerId, runtime)).usage;\n}\n\n/** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */\nexport async function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return {};\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return { usage: cached.value };\n\ttry {\n\t\tconst value = await adapter(runtime);\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });\n\t\treturn { usage: value };\n\t} catch (error) {\n\t\tconst authError = planUsageAuthError(error);\n\t\tif (authError) return { error: authError };\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value: undefined });\n\t\treturn {};\n\t}\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n"]}
@@ -9,12 +9,41 @@
9
9
  * fall back to hiding the cost meter.
10
10
  * - ollama-cloud: no usage API exists.
11
11
  *
12
- * All adapter/network errors resolve to `undefined` callers omit the UI.
12
+ * Adapter/network errors resolve to `undefined` (callers omit the plan UI).
13
+ * Auth failures are different: they return `error` and are not cached as empty.
13
14
  */
15
+ import { ModelsError } from "@ashx-j/lunr-ai";
14
16
  import { fetchKimiPlanUsage } from "./usage-adapters/kimi-coding.js";
15
17
  import { fetchCodexPlanUsage } from "./usage-adapters/openai-codex.js";
16
18
  import { fetchXaiPlanUsage } from "./usage-adapters/xai.js";
17
19
  import { fetchZaiPlanUsage } from "./usage-adapters/zai.js";
20
+ const XAI_RELOGIN = "xAI login expired. Run /login xai.";
21
+ function errorChain(error) {
22
+ const parts = [];
23
+ let current = error;
24
+ const seen = new Set();
25
+ while (current && typeof current === "object" && !seen.has(current)) {
26
+ seen.add(current);
27
+ if (current instanceof Error) {
28
+ parts.push(current.message);
29
+ current = current.cause;
30
+ continue;
31
+ }
32
+ break;
33
+ }
34
+ return parts.join(" ");
35
+ }
36
+ /** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */
37
+ export function planUsageAuthError(error) {
38
+ const chain = errorChain(error);
39
+ if (/invalid_grant|oauth refresh failed for xai|xai billing rejected/i.test(chain)) {
40
+ return XAI_RELOGIN;
41
+ }
42
+ if (error instanceof ModelsError && (error.code === "oauth" || error.code === "auth") && /xai/i.test(chain)) {
43
+ return XAI_RELOGIN;
44
+ }
45
+ return undefined;
46
+ }
18
47
  const ADAPTERS = {
19
48
  "openai-codex": fetchCodexPlanUsage,
20
49
  "kimi-coding": fetchKimiPlanUsage,
@@ -29,22 +58,29 @@ export function hasPlanUsageAdapter(providerId) {
29
58
  }
30
59
  /** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */
31
60
  export async function getPlanUsage(providerId, runtime) {
61
+ return (await getPlanUsageResult(providerId, runtime)).usage;
62
+ }
63
+ /** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */
64
+ export async function getPlanUsageResult(providerId, runtime) {
32
65
  const adapter = ADAPTERS[providerId];
33
66
  if (!adapter)
34
- return undefined;
67
+ return {};
35
68
  const now = Date.now();
36
69
  const cached = cache.get(providerId);
37
70
  if (cached && cached.expiresAt > now)
38
- return cached.value;
39
- let value;
71
+ return { usage: cached.value };
40
72
  try {
41
- value = await adapter(runtime);
73
+ const value = await adapter(runtime);
74
+ cache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });
75
+ return { usage: value };
42
76
  }
43
- catch {
44
- value = undefined;
77
+ catch (error) {
78
+ const authError = planUsageAuthError(error);
79
+ if (authError)
80
+ return { error: authError };
81
+ cache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value: undefined });
82
+ return {};
45
83
  }
46
- cache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });
47
- return value;
48
84
  }
49
85
  /** Clear the adapter cache (tests, /login changes). */
50
86
  export function clearPlanUsageCache() {
@@ -1 +1 @@
1
- {"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAiB5D,MAAM,QAAQ,GAA2C;IACxD,cAAc,EAAE,mBAAmB;IACnC,aAAa,EAAE,kBAAkB;IACjC,GAAG,EAAE,iBAAiB;IACtB,GAAG,EAAE,iBAAiB;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAErD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+D,CAAC;AAErF,6DAA6D;AAC7D,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAW;IAChE,OAAO,UAAU,IAAI,QAAQ,CAAC;AAAA,CAC9B;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB,EAAE,OAAqB,EAAkC;IAC7G,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,GAAG;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IAC1D,IAAI,KAA4B,CAAC;IACjC,IAAI,CAAC;QACJ,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACR,KAAK,GAAG,SAAS,CAAC;IACnB,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,uBAAuB,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC;AAAA,CACb;AAED,uDAAuD;AACvD,MAAM,UAAU,mBAAmB,GAAS;IAC3C,KAAK,CAAC,KAAK,EAAE,CAAC;AAAA,CACd;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (5-minute cache each) so /usage can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * All adapter/network errors resolve to `undefined` callers omit the UI.\n */\n\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return undefined;\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return cached.value;\n\tlet value: PlanUsage | undefined;\n\ttry {\n\t\tvalue = await adapter(runtime);\n\t} catch {\n\t\tvalue = undefined;\n\t}\n\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });\n\treturn value;\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\n// ---------------------------------------------------------------------------\n"]}
1
+ {"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/core/usage-service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plan/subscription usage service. Per-provider adapters query provider quota\n * endpoints (5-minute cache each) so /usage can show subscription windows\n * alongside session/context totals.\n *\n * Deliberately absent adapters:\n * - anthropic: Anthropic policy prohibits subscription usage tracking in\n * third-party coding agents (2026-07-18 decision). OAuth Anthropic sessions\n * fall back to hiding the cost meter.\n * - ollama-cloud: no usage API exists.\n *\n * Adapter/network errors resolve to `undefined` (callers omit the plan UI).\n * Auth failures are different: they return `error` and are not cached as empty.\n */\n\nimport { ModelsError } from \"@ashx-j/lunr-ai\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport { fetchKimiPlanUsage } from \"./usage-adapters/kimi-coding.ts\";\nimport { fetchCodexPlanUsage } from \"./usage-adapters/openai-codex.ts\";\nimport { fetchXaiPlanUsage } from \"./usage-adapters/xai.ts\";\nimport { fetchZaiPlanUsage } from \"./usage-adapters/zai.ts\";\n\nexport interface PlanUsageWindow {\n\tlabel: string;\n\tusedPercent: number;\n\t/** Epoch ms when the window resets. */\n\tresetsAt?: number;\n}\n\nexport interface PlanUsage {\n\tprovider: string;\n\tplanLabel?: string;\n\twindows: PlanUsageWindow[];\n}\n\nexport interface PlanUsageResult {\n\tusage?: PlanUsage;\n\t/** Actionable auth failure. Not cached as an empty success. */\n\terror?: string;\n}\n\nconst XAI_RELOGIN = \"xAI login expired. Run /login xai.\";\n\nfunction errorChain(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current: unknown = error;\n\tconst seen = new Set<unknown>();\n\twhile (current && typeof current === \"object\" && !seen.has(current)) {\n\t\tseen.add(current);\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Classify adapter/getAuth failures that mean the SuperGrok session is dead. */\nexport function planUsageAuthError(error: unknown): string | undefined {\n\tconst chain = errorChain(error);\n\tif (/invalid_grant|oauth refresh failed for xai|xai billing rejected/i.test(chain)) {\n\t\treturn XAI_RELOGIN;\n\t}\n\tif (error instanceof ModelsError && (error.code === \"oauth\" || error.code === \"auth\") && /xai/i.test(chain)) {\n\t\treturn XAI_RELOGIN;\n\t}\n\treturn undefined;\n}\n\ntype UsageAdapter = (runtime: ModelRuntime) => Promise<PlanUsage | undefined>;\n\nconst ADAPTERS: Readonly<Record<string, UsageAdapter>> = {\n\t\"openai-codex\": fetchCodexPlanUsage,\n\t\"kimi-coding\": fetchKimiPlanUsage,\n\tzai: fetchZaiPlanUsage,\n\txai: fetchXaiPlanUsage,\n};\n\nexport const PLAN_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;\n\nconst cache = new Map<string, { expiresAt: number; value: PlanUsage | undefined }>();\n\n/** Whether a plan-usage adapter exists for this provider. */\nexport function hasPlanUsageAdapter(providerId: string): boolean {\n\treturn providerId in ADAPTERS;\n}\n\n/** Plan usage for a provider, or undefined when unsupported/unavailable. Never throws. */\nexport async function getPlanUsage(providerId: string, runtime: ModelRuntime): Promise<PlanUsage | undefined> {\n\treturn (await getPlanUsageResult(providerId, runtime)).usage;\n}\n\n/** Like getPlanUsage, but surfaces SuperGrok/auth failures instead of hiding them. */\nexport async function getPlanUsageResult(providerId: string, runtime: ModelRuntime): Promise<PlanUsageResult> {\n\tconst adapter = ADAPTERS[providerId];\n\tif (!adapter) return {};\n\tconst now = Date.now();\n\tconst cached = cache.get(providerId);\n\tif (cached && cached.expiresAt > now) return { usage: cached.value };\n\ttry {\n\t\tconst value = await adapter(runtime);\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value });\n\t\treturn { usage: value };\n\t} catch (error) {\n\t\tconst authError = planUsageAuthError(error);\n\t\tif (authError) return { error: authError };\n\t\tcache.set(providerId, { expiresAt: now + PLAN_USAGE_CACHE_TTL_MS, value: undefined });\n\t\treturn {};\n\t}\n}\n\n/** Clear the adapter cache (tests, /login changes). */\nexport function clearPlanUsageCache(): void {\n\tcache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Extension bridge\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"]}