specrails-desktop 2.30.2 → 2.31.0

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 (462) hide show
  1. package/README.md +28 -24
  2. package/client/dist/assets/{ActivityFeedPage-9DZOvIcY.js → ActivityFeedPage-CAhFRUO4.js} +1 -1
  3. package/client/dist/assets/{AgentBrowserCapture-DqyHbzk7.js → AgentBrowserCapture-Ct4G5lsE.js} +1 -1
  4. package/client/dist/assets/AgentModeCodePane-DQt9WIvu.js +2 -0
  5. package/client/dist/assets/{AgentModeJobsPane-CTIHMUKN.js → AgentModeJobsPane-podaJVl9.js} +1 -1
  6. package/client/dist/assets/AgentsPage-DTZdbW80.js +87 -0
  7. package/client/dist/assets/AnalyticsPage-BskgGPm0.js +1 -0
  8. package/client/dist/assets/CodePage-CvtYo1DU.js +2 -0
  9. package/client/dist/assets/{DesktopAnalyticsPage-CkXUFj3F.js → DesktopAnalyticsPage-Dnf99E8e.js} +1 -1
  10. package/client/dist/assets/{DocsDialog-PhdCJNVj.js → DocsDialog-DHi36rZ4.js} +1 -1
  11. package/client/dist/assets/{DocsPage-BY-wDthx.js → DocsPage-DIdVrQz1.js} +1 -1
  12. package/client/dist/assets/{InteractiveJobComposer-C9I0A4KR.js → InteractiveJobComposer-BHeB2inv.js} +1 -1
  13. package/client/dist/assets/{JobDetailModal-I_RoQJV4.js → JobDetailModal-CF1SKUHH.js} +1 -1
  14. package/client/dist/assets/JobDetailPage-De3rY18h.js +1 -0
  15. package/client/dist/assets/{JobsPage-DcllM4Ip.js → JobsPage-CfAqqJTx.js} +1 -1
  16. package/client/dist/assets/{LoopBuilderPage-DGm_iuff.js → LoopBuilderPage-DSBMuTb3.js} +2 -2
  17. package/client/dist/assets/{LoopPreviewModal-BC-TeFdD.js → LoopPreviewModal-B-c37kA0.js} +1 -1
  18. package/client/dist/assets/LoopsPage-DjnNXdmm.js +1 -0
  19. package/client/dist/assets/{MinimizedChatsContext-Cs34FPTz.js → MinimizedChatsContext-DIsnQMVz.js} +1 -1
  20. package/client/dist/assets/PluginsPage-DA5pQ_6u.js +2 -0
  21. package/client/dist/assets/{ProjectSettingsDialog-nEs-ENrE.js → ProjectSettingsDialog-DA23PSi6.js} +1 -1
  22. package/client/dist/assets/{TemplatePreviewModal-D4qW-J9B.js → TemplatePreviewModal-Cp7IQ-g2.js} +1 -1
  23. package/client/dist/assets/TicketDetailModal-BPD2i_0R.js +14 -0
  24. package/client/dist/assets/{Trans-CPISVji8.js → Trans-cWtExGkC.js} +1 -1
  25. package/client/dist/assets/addspec-4WmLJmBG.js +1 -0
  26. package/client/dist/assets/addspec-BRlk8jON.js +1 -0
  27. package/client/dist/assets/addspec-C7XRejU4.js +1 -0
  28. package/client/dist/assets/addspec-CHW9xf_X.js +1 -0
  29. package/client/dist/assets/addspec-DTmRrWrF.js +1 -0
  30. package/client/dist/assets/addspec-DhmnXER8.js +1 -0
  31. package/client/dist/assets/addspec-Di41DpYV.js +1 -0
  32. package/client/dist/assets/addspec-x3mizDxc.js +1 -0
  33. package/client/dist/assets/agent-BKsuIgIT.js +1 -0
  34. package/client/dist/assets/agent-Bs9NilfI.js +1 -0
  35. package/client/dist/assets/agent-C23h684q.js +1 -0
  36. package/client/dist/assets/agent-CqEjy3Z2.js +1 -0
  37. package/client/dist/assets/agent-D5DZTks1.js +1 -0
  38. package/client/dist/assets/agent-DE8hUYPl.js +1 -0
  39. package/client/dist/assets/agent-DsTuJGSc.js +1 -0
  40. package/client/dist/assets/agent-DwZM05-x.js +1 -0
  41. package/client/dist/assets/agents-45GbINfh.js +1 -0
  42. package/client/dist/assets/agents-BHp0_FQt.js +1 -0
  43. package/client/dist/assets/agents-Bvyi3y6R.js +1 -0
  44. package/client/dist/assets/agents-DMKFStHL.js +1 -0
  45. package/client/dist/assets/agents-DaYcxAr1.js +1 -0
  46. package/client/dist/assets/agents-IUOgqT5t.js +1 -0
  47. package/client/dist/assets/agents-ms7TwG16.js +1 -0
  48. package/client/dist/assets/agents-rzPnqoBR.js +1 -0
  49. package/client/dist/assets/agentstudio-8TQqBenW.js +1 -0
  50. package/client/dist/assets/agentstudio-B8Z_mmEX.js +1 -0
  51. package/client/dist/assets/agentstudio-BZR9YmLs.js +1 -0
  52. package/client/dist/assets/agentstudio-Bge9pAWb.js +1 -0
  53. package/client/dist/assets/agentstudio-Bz169Vzt.js +1 -0
  54. package/client/dist/assets/agentstudio-D6L2jpNi.js +1 -0
  55. package/client/dist/assets/agentstudio-K2-Z7CL0.js +1 -0
  56. package/client/dist/assets/agentstudio-Zmgzthv8.js +1 -0
  57. package/client/dist/assets/analytics-B8Qtptbl.js +1 -0
  58. package/client/dist/assets/analytics-BHqUazhP.js +1 -0
  59. package/client/dist/assets/analytics-BPptRY4f.js +1 -0
  60. package/client/dist/assets/analytics-BxDGOIwf.js +1 -0
  61. package/client/dist/assets/analytics-C9RaQcE3.js +1 -0
  62. package/client/dist/assets/analytics-CJIwqrEl.js +1 -0
  63. package/client/dist/assets/analytics-D_OZdQ9O.js +1 -0
  64. package/client/dist/assets/analytics-We6lUMI8.js +1 -0
  65. package/client/dist/assets/builder-3bygQtZi.js +1 -0
  66. package/client/dist/assets/builder-BiUG9Z5K.js +1 -0
  67. package/client/dist/assets/builder-CGRz_-zP.js +1 -0
  68. package/client/dist/assets/builder-CWYPw7Cw.js +1 -0
  69. package/client/dist/assets/builder-CX1437hF.js +1 -0
  70. package/client/dist/assets/{builder-CwA4T7gP.js → builder-Cowk9jtL.js} +1 -1
  71. package/client/dist/assets/builder-DwDexYRE.js +1 -0
  72. package/client/dist/assets/builder-skfnZ1uC.js +1 -0
  73. package/client/dist/assets/code-B0O8-p8k.js +1 -0
  74. package/client/dist/assets/code-B1VLZDhS.js +1 -0
  75. package/client/dist/assets/code-B86IMAfz.js +1 -0
  76. package/client/dist/assets/code-Bpj4Ddml.js +1 -0
  77. package/client/dist/assets/code-BykBZ5-e.js +1 -0
  78. package/client/dist/assets/code-CHPkOkDS.js +1 -0
  79. package/client/dist/assets/code-CkUuW4m9.js +1 -0
  80. package/client/dist/assets/code-cAqaqMTx.js +1 -0
  81. package/client/dist/assets/{dashboard-Dy2Um-US.js → dashboard-BVq6kRJe.js} +1 -1
  82. package/client/dist/assets/{dashboard-CHCEkds4.js → dashboard-BaKPPmLH.js} +1 -1
  83. package/client/dist/assets/{dashboard-CrlTV1ms.js → dashboard-Bm8e4zYG.js} +1 -1
  84. package/client/dist/assets/{dashboard-WhzOQw1S.js → dashboard-Bt6IesnM.js} +1 -1
  85. package/client/dist/assets/{dashboard-Cslu_GCH.js → dashboard-C_NQZle_.js} +1 -1
  86. package/client/dist/assets/{dashboard-DweT3Y4R.js → dashboard-CyZKoGoI.js} +1 -1
  87. package/client/dist/assets/{dashboard-DUU8-_Rg.js → dashboard-HIUdUBQ0.js} +1 -1
  88. package/client/dist/assets/{dashboard-Cqmmbh3l.js → dashboard-pm4Mr229.js} +1 -1
  89. package/client/dist/assets/{dist-js-jwoXo0YD.js → dist-js-BaP-xLOy.js} +1 -1
  90. package/client/dist/assets/{dist-js-Dj72W6qo.js → dist-js-CLlgOa5a.js} +1 -1
  91. package/client/dist/assets/format-command-C0D9IPmV.js +1 -0
  92. package/client/dist/assets/{formatDistanceToNow-ByKli7Q_.js → formatDistanceToNow-DUyzS4rr.js} +1 -1
  93. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-qrcBaLW1.js → getTimezoneOffsetInMilliseconds-BCIwmmTj.js} +1 -1
  94. package/client/dist/assets/{i18n-Bf9Ckd10.js → i18n-D21Pr8Sj.js} +2 -2
  95. package/client/dist/assets/index-BdSDUFnA.css +2 -0
  96. package/client/dist/assets/index-Ci4-JKDr.js +77 -0
  97. package/client/dist/assets/{jobs-CVFD8rar.js → jobs-BAls3bWq.js} +1 -1
  98. package/client/dist/assets/{jobs-DoSaFzAa.js → jobs-B_TeSQxb.js} +1 -1
  99. package/client/dist/assets/jobs-Bgq-PZkC.js +1 -0
  100. package/client/dist/assets/jobs-Bpks6doU.js +1 -0
  101. package/client/dist/assets/jobs-CN_zv4Gr.js +1 -0
  102. package/client/dist/assets/jobs-DUlWSYq2.js +1 -0
  103. package/client/dist/assets/{jobs-B-sDHEay.js → jobs-DhxKgWFq.js} +1 -1
  104. package/client/dist/assets/{jobs-lk1_Dq1U.js → jobs-SwYS2M-B.js} +1 -1
  105. package/client/dist/assets/nav-BHDiSIVu.js +1 -0
  106. package/client/dist/assets/nav-CMiYAq4i.js +1 -0
  107. package/client/dist/assets/nav-CqS7NrDW.js +1 -0
  108. package/client/dist/assets/nav-D-0M7xfv.js +1 -0
  109. package/client/dist/assets/nav-D65O6u9I.js +1 -0
  110. package/client/dist/assets/nav-D_LKYvBr.js +1 -0
  111. package/client/dist/assets/nav-FDTJmU79.js +1 -0
  112. package/client/dist/assets/nav-VaB-C4k5.js +1 -0
  113. package/client/dist/assets/provider-capabilities-BvoExILy.js +1 -0
  114. package/client/dist/assets/settings-BveOJoaI.js +1 -0
  115. package/client/dist/assets/settings-Cj1C6F5k.js +1 -0
  116. package/client/dist/assets/settings-CuVwOpe0.js +1 -0
  117. package/client/dist/assets/settings-D5X4DfRc.js +1 -0
  118. package/client/dist/assets/settings-DXwxe-BW.js +1 -0
  119. package/client/dist/assets/settings-Di81Ba_V.js +1 -0
  120. package/client/dist/assets/settings-jbLjCYC_.js +1 -0
  121. package/client/dist/assets/settings-wIZNPFnn.js +1 -0
  122. package/client/dist/assets/setup-BBiHEO7h.js +1 -0
  123. package/client/dist/assets/setup-BlTWhNIQ.js +1 -0
  124. package/client/dist/assets/setup-Bp2L_oiP.js +1 -0
  125. package/client/dist/assets/setup-CInYXp1-.js +1 -0
  126. package/client/dist/assets/{setup-CITtPjed.js → setup-CYH3bP6K.js} +1 -1
  127. package/client/dist/assets/setup-DwcQ921P.js +1 -0
  128. package/client/dist/assets/setup-PBLjuDSs.js +1 -0
  129. package/client/dist/assets/setup-bx6FtoBp.js +1 -0
  130. package/client/dist/assets/{spending-DMnCXy84.js → spending-BCyUxoOI.js} +1 -1
  131. package/client/dist/assets/{tickets-B8BuAaj5.js → tickets-BL9r1G0e.js} +1 -1
  132. package/client/dist/assets/{tickets-DpmdE8tv.js → tickets-B_NyHJ8f.js} +1 -1
  133. package/client/dist/assets/{tickets-B_yh4qQk.js → tickets-BarF1wuG.js} +1 -1
  134. package/client/dist/assets/{tickets-pAUJkDTu.js → tickets-CclVhpQA.js} +1 -1
  135. package/client/dist/assets/{tickets-D_yDquEw.js → tickets-CqqOHbli.js} +1 -1
  136. package/client/dist/assets/{tickets-CFk2n2Or.js → tickets-DCkFSiqt.js} +1 -1
  137. package/client/dist/assets/{tickets-DQp7JS2R.js → tickets-Djn3ccfT.js} +1 -1
  138. package/client/dist/assets/{tickets-Bs37scwY.js → tickets-MiHBPsy_.js} +1 -1
  139. package/client/dist/assets/{useBrowserCaptureSession-CkBJShUw.js → useBrowserCaptureSession-CkVCDzer.js} +1 -1
  140. package/client/dist/assets/{useDesktop-B4pSBIDd.js → useDesktop-DCP_MHp9.js} +1 -1
  141. package/client/dist/index.html +25 -24
  142. package/docs/README.md +7 -3
  143. package/docs/cli.md +3 -3
  144. package/docs/codex.md +51 -64
  145. package/docs/creating-specs.md +36 -4
  146. package/docs/customizing.md +5 -5
  147. package/docs/gemini.md +35 -47
  148. package/docs/getting-started.md +15 -9
  149. package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +5 -1
  150. package/docs/guide/de/agents/4-custom-agents-catalog.md +7 -4
  151. package/docs/guide/de/getting-started/1-what-is-specrails.md +1 -0
  152. package/docs/guide/de/getting-started/2-installing-and-first-run.md +1 -0
  153. package/docs/guide/de/getting-started/4-the-dashboard-tour.md +4 -4
  154. package/docs/guide/de/integrations/1-ai-providers.md +29 -18
  155. package/docs/guide/de/integrations/2-plugins.md +3 -3
  156. package/docs/guide/de/integrations/6-agent-chat.md +8 -1
  157. package/docs/guide/de/pipeline/1-rails-and-jobs.md +9 -6
  158. package/docs/guide/de/pipeline/2-the-job-detail-view.md +3 -1
  159. package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  160. package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +13 -13
  161. package/docs/guide/de/pipeline/5-the-loop-builder.md +6 -2
  162. package/docs/guide/de/specs/2-add-spec-quick-mode.md +6 -1
  163. package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +7 -1
  164. package/docs/guide/en/agents/4-custom-agents-catalog.md +17 -4
  165. package/docs/guide/en/getting-started/1-what-is-specrails.md +2 -1
  166. package/docs/guide/en/getting-started/2-installing-and-first-run.md +2 -1
  167. package/docs/guide/en/getting-started/4-the-dashboard-tour.md +5 -5
  168. package/docs/guide/en/insights/3-code-explorer.md +5 -0
  169. package/docs/guide/en/integrations/1-ai-providers.md +63 -22
  170. package/docs/guide/en/integrations/2-plugins.md +3 -3
  171. package/docs/guide/en/integrations/6-agent-chat.md +14 -2
  172. package/docs/guide/en/pipeline/1-rails-and-jobs.md +19 -7
  173. package/docs/guide/en/pipeline/2-the-job-detail-view.md +3 -1
  174. package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  175. package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +25 -11
  176. package/docs/guide/en/pipeline/5-the-loop-builder.md +7 -2
  177. package/docs/guide/en/specs/2-add-spec-quick-mode.md +10 -1
  178. package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +5 -1
  179. package/docs/guide/es/agents/4-custom-agents-catalog.md +7 -4
  180. package/docs/guide/es/getting-started/1-what-is-specrails.md +1 -0
  181. package/docs/guide/es/getting-started/2-installing-and-first-run.md +1 -0
  182. package/docs/guide/es/getting-started/4-the-dashboard-tour.md +4 -4
  183. package/docs/guide/es/integrations/1-ai-providers.md +30 -18
  184. package/docs/guide/es/integrations/2-plugins.md +3 -3
  185. package/docs/guide/es/integrations/6-agent-chat.md +8 -1
  186. package/docs/guide/es/pipeline/1-rails-and-jobs.md +9 -6
  187. package/docs/guide/es/pipeline/2-the-job-detail-view.md +3 -1
  188. package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  189. package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +15 -13
  190. package/docs/guide/es/pipeline/5-the-loop-builder.md +6 -2
  191. package/docs/guide/es/specs/2-add-spec-quick-mode.md +6 -1
  192. package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +5 -1
  193. package/docs/guide/fr/agents/4-custom-agents-catalog.md +7 -4
  194. package/docs/guide/fr/getting-started/1-what-is-specrails.md +1 -0
  195. package/docs/guide/fr/getting-started/2-installing-and-first-run.md +1 -0
  196. package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +4 -4
  197. package/docs/guide/fr/integrations/1-ai-providers.md +29 -18
  198. package/docs/guide/fr/integrations/2-plugins.md +3 -3
  199. package/docs/guide/fr/integrations/6-agent-chat.md +8 -1
  200. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +9 -6
  201. package/docs/guide/fr/pipeline/2-the-job-detail-view.md +3 -1
  202. package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  203. package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +15 -13
  204. package/docs/guide/fr/pipeline/5-the-loop-builder.md +6 -2
  205. package/docs/guide/fr/specs/2-add-spec-quick-mode.md +6 -1
  206. package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +5 -1
  207. package/docs/guide/it/agents/4-custom-agents-catalog.md +7 -4
  208. package/docs/guide/it/getting-started/1-what-is-specrails.md +1 -0
  209. package/docs/guide/it/getting-started/2-installing-and-first-run.md +1 -0
  210. package/docs/guide/it/getting-started/4-the-dashboard-tour.md +4 -4
  211. package/docs/guide/it/integrations/1-ai-providers.md +28 -18
  212. package/docs/guide/it/integrations/2-plugins.md +3 -3
  213. package/docs/guide/it/integrations/6-agent-chat.md +8 -1
  214. package/docs/guide/it/pipeline/1-rails-and-jobs.md +9 -6
  215. package/docs/guide/it/pipeline/2-the-job-detail-view.md +3 -1
  216. package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  217. package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +13 -13
  218. package/docs/guide/it/pipeline/5-the-loop-builder.md +6 -2
  219. package/docs/guide/it/specs/2-add-spec-quick-mode.md +6 -1
  220. package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +6 -1
  221. package/docs/guide/ja/agents/4-custom-agents-catalog.md +7 -4
  222. package/docs/guide/ja/getting-started/1-what-is-specrails.md +1 -0
  223. package/docs/guide/ja/getting-started/2-installing-and-first-run.md +1 -0
  224. package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +4 -4
  225. package/docs/guide/ja/integrations/1-ai-providers.md +28 -18
  226. package/docs/guide/ja/integrations/2-plugins.md +3 -3
  227. package/docs/guide/ja/integrations/6-agent-chat.md +7 -1
  228. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +9 -6
  229. package/docs/guide/ja/pipeline/2-the-job-detail-view.md +3 -1
  230. package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  231. package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +14 -13
  232. package/docs/guide/ja/pipeline/5-the-loop-builder.md +6 -2
  233. package/docs/guide/ja/specs/2-add-spec-quick-mode.md +6 -1
  234. package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +5 -1
  235. package/docs/guide/pt/agents/4-custom-agents-catalog.md +7 -4
  236. package/docs/guide/pt/getting-started/1-what-is-specrails.md +1 -0
  237. package/docs/guide/pt/getting-started/2-installing-and-first-run.md +1 -0
  238. package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +4 -4
  239. package/docs/guide/pt/integrations/1-ai-providers.md +28 -18
  240. package/docs/guide/pt/integrations/2-plugins.md +3 -3
  241. package/docs/guide/pt/integrations/6-agent-chat.md +7 -1
  242. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +9 -6
  243. package/docs/guide/pt/pipeline/2-the-job-detail-view.md +2 -1
  244. package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  245. package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +14 -13
  246. package/docs/guide/pt/pipeline/5-the-loop-builder.md +6 -2
  247. package/docs/guide/pt/specs/2-add-spec-quick-mode.md +6 -1
  248. package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +4 -1
  249. package/docs/guide/zh/agents/4-custom-agents-catalog.md +7 -4
  250. package/docs/guide/zh/getting-started/1-what-is-specrails.md +1 -0
  251. package/docs/guide/zh/getting-started/2-installing-and-first-run.md +1 -0
  252. package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +4 -4
  253. package/docs/guide/zh/integrations/1-ai-providers.md +26 -18
  254. package/docs/guide/zh/integrations/2-plugins.md +3 -3
  255. package/docs/guide/zh/integrations/6-agent-chat.md +6 -1
  256. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +9 -6
  257. package/docs/guide/zh/pipeline/2-the-job-detail-view.md +2 -1
  258. package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  259. package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +12 -12
  260. package/docs/guide/zh/pipeline/5-the-loop-builder.md +6 -2
  261. package/docs/guide/zh/specs/2-add-spec-quick-mode.md +6 -1
  262. package/docs/internals/README.md +4 -1
  263. package/docs/internals/adding-a-provider.md +32 -28
  264. package/docs/internals/api-reference.md +37 -13
  265. package/docs/internals/architecture.md +5 -5
  266. package/docs/internals/bundled-framework-build-plan.md +3 -3
  267. package/docs/internals/code-explorer-story.md +4 -0
  268. package/docs/internals/configuration.md +17 -5
  269. package/docs/internals/global-artifacts-alignment-contract.md +1 -1
  270. package/docs/internals/operations-runbook.md +1 -1
  271. package/docs/internals/profiles.md +37 -45
  272. package/docs/internals/project-builder.md +16 -0
  273. package/docs/kimi.md +291 -0
  274. package/docs/mcp.md +9 -2
  275. package/docs/platforms/macos.md +17 -7
  276. package/docs/platforms/windows.md +9 -4
  277. package/docs/running-pipelines.md +46 -12
  278. package/docs/terminal.md +1 -1
  279. package/docs/tracking-cost.md +25 -6
  280. package/package.json +4 -2
  281. package/server/dist/agent-chat-manager.js +74 -32
  282. package/server/dist/agent-chat-router.js +40 -17
  283. package/server/dist/agent-cwd-manager.js +14 -7
  284. package/server/dist/agent-generator.js +69 -85
  285. package/server/dist/agent-mcp-config.js +17 -11
  286. package/server/dist/agent-operator-prompt.js +29 -20
  287. package/server/dist/agent-refine-manager.js +58 -38
  288. package/server/dist/ai-invocations.js +65 -20
  289. package/server/dist/attachment-manager.js +49 -15
  290. package/server/dist/blueprint-chat-manager.js +41 -16
  291. package/server/dist/blueprint-router.js +40 -6
  292. package/server/dist/chat-manager.js +165 -89
  293. package/server/dist/code-explorer-router.js +26 -7
  294. package/server/dist/contract-refine-runner.js +146 -113
  295. package/server/dist/core-compat.js +57 -2
  296. package/server/dist/core-package.js +3 -3
  297. package/server/dist/core-update-manager.js +17 -1
  298. package/server/dist/db.js +60 -5
  299. package/server/dist/desktop-analytics.js +44 -6
  300. package/server/dist/desktop-router.js +127 -11
  301. package/server/dist/explore-cwd-manager.js +19 -3
  302. package/server/dist/feature-flags.js +1 -1
  303. package/server/dist/file-story-manager.js +3 -3
  304. package/server/dist/file-summary-generator.js +21 -15
  305. package/server/dist/file-summary-manager.js +6 -6
  306. package/server/dist/framework-manager.js +19 -2
  307. package/server/dist/framework-migration.js +27 -12
  308. package/server/dist/install-config-path.js +1 -1
  309. package/server/dist/interactive-job-session.js +8 -6
  310. package/server/dist/loop-command-catalog.js +27 -10
  311. package/server/dist/loop-executors.js +59 -27
  312. package/server/dist/loop-factory.js +2 -2
  313. package/server/dist/loop-run-manager.js +100 -39
  314. package/server/dist/loops-router.js +1 -1
  315. package/server/dist/mcp/guide.js +23 -16
  316. package/server/dist/mcp/tools/agents.js +47 -41
  317. package/server/dist/mcp/tools/analytics.js +2 -2
  318. package/server/dist/mcp/tools/loops.js +2 -2
  319. package/server/dist/mcp/tools/plugins.js +14 -9
  320. package/server/dist/mcp/tools/rails.js +12 -13
  321. package/server/dist/mcp/tools/support.js +1 -1
  322. package/server/dist/mobile/mobile-router.js +5 -1
  323. package/server/dist/offline-assemble.js +13 -1
  324. package/server/dist/path-resolver.js +2 -2
  325. package/server/dist/plugin-manager.js +391 -106
  326. package/server/dist/plugins/claude-md-mutation.js +5 -4
  327. package/server/dist/plugins/drift.js +32 -29
  328. package/server/dist/plugins/rail-integration.js +13 -2
  329. package/server/dist/plugins/serena/install.js +25 -17
  330. package/server/dist/plugins/serena/manifest.js +22 -5
  331. package/server/dist/plugins/serena/verify.js +46 -16
  332. package/server/dist/plugins-router.js +59 -8
  333. package/server/dist/profile-manager.js +148 -36
  334. package/server/dist/profiles-router.js +311 -82
  335. package/server/dist/project-registry.js +2 -2
  336. package/server/dist/project-router-chat.js +17 -8
  337. package/server/dist/project-router-helpers.js +93 -5
  338. package/server/dist/project-router-jobs.js +76 -12
  339. package/server/dist/project-router-loop-runs.js +42 -14
  340. package/server/dist/project-router-settings.js +31 -10
  341. package/server/dist/project-router-setup.js +4 -5
  342. package/server/dist/project-router-spending.js +15 -3
  343. package/server/dist/project-router-tickets.js +187 -181
  344. package/server/dist/project-router.js +1 -0
  345. package/server/dist/proposal-manager.js +125 -85
  346. package/server/dist/providers/claude-adapter.js +14 -0
  347. package/server/dist/providers/codex-adapter.js +20 -0
  348. package/server/dist/providers/gemini-adapter.js +14 -0
  349. package/server/dist/providers/index.js +18 -1
  350. package/server/dist/providers/kimi-adapter.js +361 -0
  351. package/server/dist/providers/kimi-skill-prompt.js +332 -0
  352. package/server/dist/providers/runtime.js +137 -0
  353. package/server/dist/queue-manager.js +103 -78
  354. package/server/dist/rail-isolated-launch.js +1 -1
  355. package/server/dist/rails-router.js +51 -38
  356. package/server/dist/result-event.js +5 -0
  357. package/server/dist/schemas/profile.v1.json +3 -4
  358. package/server/dist/setup-manager.js +222 -101
  359. package/server/dist/setup-prerequisites.js +7 -1
  360. package/server/dist/smash-runner.js +104 -57
  361. package/server/dist/spawn-lifecycle.js +18 -14
  362. package/server/dist/spec-launcher-manager.js +66 -54
  363. package/server/dist/spec-models.js +15 -6
  364. package/server/dist/spending.js +110 -17
  365. package/server/dist/util/cli-prompt.js +142 -5
  366. package/server/dist/util/stream-display.js +46 -0
  367. package/server/dist/workspace-manager.js +100 -37
  368. package/server/dist/worktree-manager.js +4 -0
  369. package/server/dist/worktree-overlay.js +12 -3
  370. package/client/dist/assets/AgentModeCodePane-BAYyFRmY.js +0 -2
  371. package/client/dist/assets/AgentsPage-DuyJuaFs.js +0 -86
  372. package/client/dist/assets/AnalyticsPage-BY7gtCx6.js +0 -1
  373. package/client/dist/assets/CodePage-vyQr40Vl.js +0 -2
  374. package/client/dist/assets/JobDetailPage-CU4FEnUC.js +0 -1
  375. package/client/dist/assets/LoopsPage-SqglnXlp.js +0 -1
  376. package/client/dist/assets/PluginsPage-B89S8o1a.js +0 -2
  377. package/client/dist/assets/TicketDetailModal-CI47Dq9E.js +0 -14
  378. package/client/dist/assets/addspec-BXjvkh5q.js +0 -1
  379. package/client/dist/assets/addspec-C4bwrmpi.js +0 -1
  380. package/client/dist/assets/addspec-CArHXUDM.js +0 -1
  381. package/client/dist/assets/addspec-DTKJcXiR.js +0 -1
  382. package/client/dist/assets/addspec-DbNBbrha.js +0 -1
  383. package/client/dist/assets/addspec-Rw8uVBC1.js +0 -1
  384. package/client/dist/assets/addspec-htHGZHOe.js +0 -1
  385. package/client/dist/assets/addspec-lr_LP6jk.js +0 -1
  386. package/client/dist/assets/agent-BABjRt0P.js +0 -1
  387. package/client/dist/assets/agent-BuUpgqVx.js +0 -1
  388. package/client/dist/assets/agent-CCDZesgU.js +0 -1
  389. package/client/dist/assets/agent-CVn64zWH.js +0 -1
  390. package/client/dist/assets/agent-CWeOny0E.js +0 -1
  391. package/client/dist/assets/agent-DOysmad_.js +0 -1
  392. package/client/dist/assets/agent-DSsjTBhn.js +0 -1
  393. package/client/dist/assets/agent-DlZMxfM9.js +0 -1
  394. package/client/dist/assets/agents-6ZOJXWVk.js +0 -1
  395. package/client/dist/assets/agents-BZ0XQKjg.js +0 -1
  396. package/client/dist/assets/agents-BqWklnHR.js +0 -1
  397. package/client/dist/assets/agents-Bz-thHkB.js +0 -1
  398. package/client/dist/assets/agents-CJclElx6.js +0 -1
  399. package/client/dist/assets/agents-D_l9L0VA.js +0 -1
  400. package/client/dist/assets/agents-DhG7mg6-.js +0 -1
  401. package/client/dist/assets/agents-u_C0jPB5.js +0 -1
  402. package/client/dist/assets/agentstudio-BF19hbXj.js +0 -1
  403. package/client/dist/assets/agentstudio-BIUe1Im2.js +0 -1
  404. package/client/dist/assets/agentstudio-BLGvxWBx.js +0 -1
  405. package/client/dist/assets/agentstudio-D9UKiWTj.js +0 -1
  406. package/client/dist/assets/agentstudio-Drs37nIM.js +0 -1
  407. package/client/dist/assets/agentstudio-nEnKkcXM.js +0 -1
  408. package/client/dist/assets/agentstudio-pyFcSBKy.js +0 -1
  409. package/client/dist/assets/agentstudio-zfTzQh3U.js +0 -1
  410. package/client/dist/assets/analytics-BC7mwR4s.js +0 -1
  411. package/client/dist/assets/analytics-BiNg8edP.js +0 -1
  412. package/client/dist/assets/analytics-C0s_T2GN.js +0 -1
  413. package/client/dist/assets/analytics-CdsAPAwZ.js +0 -1
  414. package/client/dist/assets/analytics-Cr1_q-lC.js +0 -1
  415. package/client/dist/assets/analytics-DZ8Aoq3I.js +0 -1
  416. package/client/dist/assets/analytics-IEsKZvpW.js +0 -1
  417. package/client/dist/assets/analytics-SRPf84OT.js +0 -1
  418. package/client/dist/assets/builder-BH8Fua-g.js +0 -1
  419. package/client/dist/assets/builder-BoV7DgKd.js +0 -1
  420. package/client/dist/assets/builder-Cc_Lf79C.js +0 -1
  421. package/client/dist/assets/builder-CwZ7Bme2.js +0 -1
  422. package/client/dist/assets/builder-CxW-JFsR.js +0 -1
  423. package/client/dist/assets/builder-D94lrV7x.js +0 -1
  424. package/client/dist/assets/builder-cqHpCyGa.js +0 -1
  425. package/client/dist/assets/code-76gS0IdL.js +0 -1
  426. package/client/dist/assets/code-BDXEM4ml.js +0 -1
  427. package/client/dist/assets/code-C1VwxlML.js +0 -1
  428. package/client/dist/assets/code-CnMZyJpr.js +0 -1
  429. package/client/dist/assets/code-D20ef9Cf.js +0 -1
  430. package/client/dist/assets/code-DdOXaWRY.js +0 -1
  431. package/client/dist/assets/code-Duohuf9l.js +0 -1
  432. package/client/dist/assets/code-geHmknB9.js +0 -1
  433. package/client/dist/assets/format-command-Dfa1o2d-.js +0 -1
  434. package/client/dist/assets/index-Dh9i4CnN.css +0 -2
  435. package/client/dist/assets/index-F9r8mWP_.js +0 -76
  436. package/client/dist/assets/jobs-BCtGfgUA.js +0 -1
  437. package/client/dist/assets/jobs-BHR_EnDI.js +0 -1
  438. package/client/dist/assets/jobs-BMbZJjJJ.js +0 -1
  439. package/client/dist/assets/jobs-DzSIR8V6.js +0 -1
  440. package/client/dist/assets/nav-BRJBxaGt.js +0 -1
  441. package/client/dist/assets/nav-CAGKv3_F.js +0 -1
  442. package/client/dist/assets/nav-CNskRJKQ.js +0 -1
  443. package/client/dist/assets/nav-DY93irBL.js +0 -1
  444. package/client/dist/assets/nav-DhjvYgqt.js +0 -1
  445. package/client/dist/assets/nav-Gb-r4wop.js +0 -1
  446. package/client/dist/assets/nav-TvumAFD2.js +0 -1
  447. package/client/dist/assets/nav-myFaXRHD.js +0 -1
  448. package/client/dist/assets/settings-B0ypw9uw.js +0 -1
  449. package/client/dist/assets/settings-B9ilOSYO.js +0 -1
  450. package/client/dist/assets/settings-BqlWHRHA.js +0 -1
  451. package/client/dist/assets/settings-D8dlqt4d.js +0 -1
  452. package/client/dist/assets/settings-DAZqjX2a.js +0 -1
  453. package/client/dist/assets/settings-DRh39v1S.js +0 -1
  454. package/client/dist/assets/settings-Mj1OJbfd.js +0 -1
  455. package/client/dist/assets/settings-lRReaSYm.js +0 -1
  456. package/client/dist/assets/setup-3832BJCo.js +0 -1
  457. package/client/dist/assets/setup-B8J-gECe.js +0 -1
  458. package/client/dist/assets/setup-BCIOxVsc.js +0 -1
  459. package/client/dist/assets/setup-BciLj_tU.js +0 -1
  460. package/client/dist/assets/setup-CTkE3cfh.js +0 -1
  461. package/client/dist/assets/setup-ajB4rYm-.js +0 -1
  462. package/client/dist/assets/setup-xFO1wNl0.js +0 -1
@@ -20,7 +20,7 @@ const ALL_SURFACES = [
20
20
  function emptyBySurface() {
21
21
  const out = {};
22
22
  for (const s of ALL_SURFACES)
23
- out[s] = { count: 0, costUsd: 0 };
23
+ out[s] = { count: 0, costUsd: 0, unpricedCount: 0 };
24
24
  return out;
25
25
  }
26
26
  /**
@@ -200,17 +200,30 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
200
200
  COALESCE(SUM(COALESCE(tokens_in, 0) + COALESCE(tokens_out, 0)
201
201
  + COALESCE(tokens_cache_read, 0) + COALESCE(tokens_cache_create, 0)), 0) AS totalTokens,
202
202
  COUNT(*) AS totalRuns,
203
+ COUNT(total_cost_usd) AS pricedRuns,
204
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedRuns,
205
+ SUM(CASE WHEN tokens_in IS NOT NULL OR tokens_out IS NOT NULL
206
+ OR tokens_cache_read IS NOT NULL OR tokens_cache_create IS NOT NULL
207
+ THEN 1 ELSE 0 END) AS usageReportedRuns,
208
+ SUM(CASE WHEN tokens_in IS NULL AND tokens_out IS NULL
209
+ AND tokens_cache_read IS NULL AND tokens_cache_create IS NULL
210
+ THEN 1 ELSE 0 END) AS usageUnavailableRuns,
203
211
  SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS failed,
212
+ SUM(CASE WHEN status = 'success' AND total_cost_usd IS NULL THEN 1 ELSE 0 END) AS successfulUnpriced,
204
213
  AVG(CASE WHEN status = 'success' THEN total_cost_usd END) AS avgCost
205
214
  FROM ai_invocations WHERE ${where.sql}
206
215
  `).get(...where.params);
207
216
  // prev period
208
217
  const prevWhere = buildWhere(projectId, filters, { from: range.prevFrom, to: range.prevTo });
209
218
  const prevRow = db.prepare(`
210
- SELECT COALESCE(SUM(total_cost_usd), 0) AS totalCost
219
+ SELECT
220
+ COALESCE(SUM(total_cost_usd), 0) AS totalCost,
221
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedRuns
211
222
  FROM ai_invocations WHERE ${prevWhere.sql}
212
223
  `).get(...prevWhere.params);
213
- const deltaPct = prevRow.totalCost > 0
224
+ const deltaPct = (summaryRow.unpricedRuns ?? 0) === 0
225
+ && (prevRow.unpricedRuns ?? 0) === 0
226
+ && prevRow.totalCost > 0
214
227
  ? ((summaryRow.totalCost - prevRow.totalCost) / prevRow.totalCost) * 100
215
228
  : null;
216
229
  // byModel (top 10) + per-mode dominant models — one GROUP BY model, surface
@@ -225,21 +238,37 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
225
238
  model, surface,
226
239
  COUNT(*) AS cnt,
227
240
  COALESCE(SUM(total_cost_usd), 0) AS cost,
228
- COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estCost
241
+ COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estCost,
242
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced
229
243
  FROM ai_invocations WHERE ${where.sql} AND model IS NOT NULL
230
244
  GROUP BY provider, model, surface
231
245
  `).all(...where.params);
232
246
  const modelTotals = new Map();
233
247
  for (const r of modelRows) {
234
- const key = `${r.provider}${r.model}`;
235
- const agg = modelTotals.get(key) ?? { provider: r.provider, model: r.model, cnt: 0, cost: 0, est: 0 };
248
+ const key = `${r.provider}\0${r.model}`;
249
+ const agg = modelTotals.get(key) ?? {
250
+ provider: r.provider,
251
+ model: r.model,
252
+ cnt: 0,
253
+ cost: 0,
254
+ est: 0,
255
+ unpriced: 0,
256
+ };
236
257
  agg.cnt += r.cnt;
237
258
  agg.cost += r.cost ?? 0;
238
259
  agg.est += r.estCost ?? 0;
260
+ agg.unpriced += r.unpriced ?? 0;
239
261
  modelTotals.set(key, agg);
240
262
  }
241
263
  const byModel = Array.from(modelTotals.values())
242
- .map((t) => ({ provider: t.provider, model: t.model, count: t.cnt, costUsd: t.cost, estimatedCostUsd: t.est }))
264
+ .map((t) => ({
265
+ provider: t.provider,
266
+ model: t.model,
267
+ count: t.cnt,
268
+ costUsd: t.cost,
269
+ estimatedCostUsd: t.est,
270
+ ...(t.unpriced > 0 ? { unpricedCount: t.unpriced } : {}),
271
+ }))
243
272
  .sort((a, b) => b.costUsd - a.costUsd)
244
273
  .slice(0, 10);
245
274
  // dominantModel is the most-frequent model per surface (provider-agnostic —
@@ -247,11 +276,11 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
247
276
  const dominantBySurface = new Map();
248
277
  const dominantTally = new Map();
249
278
  for (const r of modelRows) {
250
- const k = `${r.surface}${r.model}`;
279
+ const k = `${r.surface}\0${r.model}`;
251
280
  dominantTally.set(k, (dominantTally.get(k) ?? 0) + r.cnt);
252
281
  }
253
282
  for (const [k, cnt] of dominantTally.entries()) {
254
- const sep = k.indexOf('');
283
+ const sep = k.indexOf('\0');
255
284
  const surface = k.slice(0, sep);
256
285
  const model = k.slice(sep + 1);
257
286
  const cur = dominantBySurface.get(surface);
@@ -262,7 +291,11 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
262
291
  // calendar day (BUG-ANALYTICS-21): the client passes its local offset so
263
292
  // "today" lines up with the user's wall clock; offset 0 ⇒ legacy UTC days.
264
293
  const dayRows = db.prepare(`
265
- SELECT ${dayBucketExpr(tzOffset)} AS day, surface, COALESCE(SUM(total_cost_usd), 0) AS cost
294
+ SELECT
295
+ ${dayBucketExpr(tzOffset)} AS day,
296
+ surface,
297
+ COALESCE(SUM(total_cost_usd), 0) AS cost,
298
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced
266
299
  FROM ai_invocations WHERE ${where.sql}
267
300
  GROUP BY day, surface
268
301
  `).all(...where.params);
@@ -279,7 +312,7 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
279
312
  const dayMap = new Map();
280
313
  for (const day of days) {
281
314
  dayMap.set(day, {
282
- date: day, jobsCostUsd: 0, quickCostUsd: 0, exploreCostUsd: 0, aiEditCostUsd: 0, smashCostUsd: 0, fileSummaryCostUsd: 0, loopCostUsd: 0, totalCostUsd: 0,
315
+ date: day, jobsCostUsd: 0, quickCostUsd: 0, exploreCostUsd: 0, aiEditCostUsd: 0, smashCostUsd: 0, fileSummaryCostUsd: 0, loopCostUsd: 0, totalCostUsd: 0, unpricedCount: 0,
283
316
  });
284
317
  }
285
318
  for (const r of dayRows) {
@@ -302,8 +335,13 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
302
335
  else if (r.surface === 'loop')
303
336
  entry.loopCostUsd += c;
304
337
  entry.totalCostUsd += c;
338
+ entry.unpricedCount = (entry.unpricedCount ?? 0) + (r.unpriced ?? 0);
305
339
  }
306
340
  const dailyTimeline = Array.from(dayMap.values());
341
+ for (const day of dailyTimeline) {
342
+ if ((day.unpricedCount ?? 0) === 0)
343
+ delete day.unpricedCount;
344
+ }
307
345
  // byMode (Quick vs Explore) — one GROUP BY surface query for both modes;
308
346
  // dominant models come from modelRows and sparklines from dayRows, which
309
347
  // already carry exactly these aggregates (H24: previously 3 extra queries
@@ -320,8 +358,11 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
320
358
  COUNT(DISTINCT ticket_id) AS ticketsCreated,
321
359
  COALESCE(SUM(total_cost_usd), 0) AS totalCost,
322
360
  COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estCost,
361
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedRuns,
323
362
  COALESCE(SUM(CASE WHEN status = 'success' AND ticket_id IS NOT NULL THEN total_cost_usd ELSE 0 END), 0) AS specCostSum,
324
363
  COUNT(DISTINCT CASE WHEN status = 'success' AND ticket_id IS NOT NULL THEN ticket_id END) AS specCount,
364
+ COUNT(DISTINCT CASE WHEN status = 'success' AND ticket_id IS NOT NULL
365
+ AND total_cost_usd IS NULL THEN ticket_id END) AS unpricedSpecCount,
325
366
  AVG(CASE WHEN status = 'success' THEN duration_ms END) AS avgDur
326
367
  FROM ai_invocations WHERE ${where.sql} AND surface IN ('quick-spec', 'explore-spec')
327
368
  GROUP BY surface
@@ -333,7 +374,12 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
333
374
  const byMode = ['quick-spec', 'explore-spec'].map((surface) => {
334
375
  const modeKey = surface === 'quick-spec' ? 'quick' : 'explore';
335
376
  const r = modeAggBySurface.get(surface);
336
- const avgCostPerSpec = r && r.specCount > 0 ? r.specCostSum / r.specCount : null;
377
+ // A partial average over only providers that report cost is worse than no
378
+ // average: it looks authoritative while silently excluding Kimi. Preserve
379
+ // unavailable until every successful ticket in the denominator is priced.
380
+ const avgCostPerSpec = r && r.specCount > 0 && r.unpricedSpecCount === 0
381
+ ? r.specCostSum / r.specCount
382
+ : null;
337
383
  const sparkline = days.map((d) => costByDaySurface.get(`${d}|${surface}`) ?? 0);
338
384
  return {
339
385
  mode: modeKey,
@@ -341,6 +387,7 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
341
387
  ticketsCreated: r?.ticketsCreated ?? 0,
342
388
  totalCostUsd: r?.totalCost ?? 0,
343
389
  estimatedCostUsd: r?.estCost ?? 0,
390
+ ...((r?.unpricedRuns ?? 0) > 0 ? { unpricedCount: r?.unpricedRuns ?? 0 } : {}),
344
391
  avgCostPerSpec,
345
392
  avgDurationMs: r?.avgDur ?? null,
346
393
  dominantModel: dominantBySurface.get(surface)?.model ?? null,
@@ -396,7 +443,12 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
396
443
  }
397
444
  // topTickets (cross-surface aggregation)
398
445
  const ticketRows = db.prepare(`
399
- SELECT ticket_id, surface, COUNT(*) AS cnt, COALESCE(SUM(total_cost_usd), 0) AS cost
446
+ SELECT
447
+ ticket_id,
448
+ surface,
449
+ COUNT(*) AS cnt,
450
+ COALESCE(SUM(total_cost_usd), 0) AS cost,
451
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced
400
452
  FROM ai_invocations WHERE ${where.sql}
401
453
  GROUP BY ticket_id, surface
402
454
  `).all(...where.params);
@@ -409,6 +461,7 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
409
461
  ticketTitle: null,
410
462
  totalCostUsd: 0,
411
463
  totalRuns: 0,
464
+ unpricedCount: 0,
412
465
  bySurface: emptyBySurface(),
413
466
  isUnattributed: r.ticket_id === null ? true : undefined,
414
467
  });
@@ -416,12 +469,23 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
416
469
  const entry = ticketMap.get(key);
417
470
  entry.bySurface[r.surface].count += r.cnt;
418
471
  entry.bySurface[r.surface].costUsd += r.cost ?? 0;
472
+ entry.bySurface[r.surface].unpricedCount =
473
+ (entry.bySurface[r.surface].unpricedCount ?? 0) + (r.unpriced ?? 0);
419
474
  entry.totalRuns += r.cnt;
420
475
  entry.totalCostUsd += r.cost ?? 0;
476
+ entry.unpricedCount = (entry.unpricedCount ?? 0) + (r.unpriced ?? 0);
421
477
  }
422
478
  const topTickets = Array.from(ticketMap.values())
423
479
  .sort((a, b) => b.totalCostUsd - a.totalCostUsd)
424
480
  .slice(0, 10);
481
+ for (const ticket of topTickets) {
482
+ if ((ticket.unpricedCount ?? 0) === 0)
483
+ delete ticket.unpricedCount;
484
+ for (const bucket of Object.values(ticket.bySurface)) {
485
+ if ((bucket.unpricedCount ?? 0) === 0)
486
+ delete bucket.unpricedCount;
487
+ }
488
+ }
425
489
  // BUG-ANALYTICS-18/36: resolve committed-ticket titles + deleted state via the
426
490
  // injected store reader so the card (and the summary-CSV / modal consumers
427
491
  // built on this same shape) show names instead of "Deleted ticket #N". Without
@@ -440,14 +504,20 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
440
504
  // (H24: previously a dedicated query re-scanned the same rows).
441
505
  const surfaceTotals = new Map();
442
506
  for (const r of ticketRows) {
443
- const agg = surfaceTotals.get(r.surface) ?? { cnt: 0, cost: 0 };
507
+ const agg = surfaceTotals.get(r.surface) ?? { cnt: 0, cost: 0, unpriced: 0 };
444
508
  agg.cnt += r.cnt;
445
509
  agg.cost += r.cost ?? 0;
510
+ agg.unpriced += r.unpriced ?? 0;
446
511
  surfaceTotals.set(r.surface, agg);
447
512
  }
448
513
  const bySurface = ALL_SURFACES.map((s) => {
449
514
  const t = surfaceTotals.get(s);
450
- return { surface: s, count: t?.cnt ?? 0, costUsd: t?.cost ?? 0 };
515
+ return {
516
+ surface: s,
517
+ count: t?.cnt ?? 0,
518
+ costUsd: t?.cost ?? 0,
519
+ ...((t?.unpriced ?? 0) > 0 ? { unpricedCount: t?.unpriced ?? 0 } : {}),
520
+ };
451
521
  });
452
522
  // tracking start (project's first invocation)
453
523
  const trackingRow = db.prepare(`
@@ -461,6 +531,14 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
461
531
  SELECT
462
532
  COALESCE(provider, 'claude') AS provider,
463
533
  COUNT(*) AS cnt,
534
+ COUNT(total_cost_usd) AS priced,
535
+ SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced,
536
+ SUM(CASE WHEN tokens_in IS NOT NULL OR tokens_out IS NOT NULL
537
+ OR tokens_cache_read IS NOT NULL OR tokens_cache_create IS NOT NULL
538
+ THEN 1 ELSE 0 END) AS usageReported,
539
+ SUM(CASE WHEN tokens_in IS NULL AND tokens_out IS NULL
540
+ AND tokens_cache_read IS NULL AND tokens_cache_create IS NULL
541
+ THEN 1 ELSE 0 END) AS usageUnavailable,
464
542
  COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 0 THEN total_cost_usd ELSE 0 END), 0) AS authoritativeCost,
465
543
  COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estimatedCost
466
544
  FROM ai_invocations WHERE ${where.sql}
@@ -472,17 +550,32 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
472
550
  count: r.cnt,
473
551
  costUsd: r.authoritativeCost,
474
552
  estimatedCostUsd: r.estimatedCost,
553
+ pricedCount: r.priced,
554
+ unpricedCount: r.unpriced ?? 0,
555
+ usageReportedCount: r.usageReported ?? 0,
556
+ usageUnavailableCount: r.usageUnavailable ?? 0,
475
557
  }));
476
558
  return {
477
559
  summary: {
478
560
  totalCostUsd: summaryRow.totalCost,
479
561
  totalEstimatedCostUsd: summaryRow.totalEstimatedCost,
480
- totalTokens: summaryRow.totalTokens,
562
+ totalTokens: (summaryRow.usageReportedRuns ?? 0) > 0
563
+ ? summaryRow.totalTokens
564
+ : summaryRow.totalRuns > 0
565
+ ? null
566
+ : 0,
481
567
  totalRuns: summaryRow.totalRuns,
568
+ pricedRuns: summaryRow.pricedRuns,
569
+ unpricedRuns: summaryRow.unpricedRuns ?? 0,
570
+ usageReportedRuns: summaryRow.usageReportedRuns ?? 0,
571
+ usageUnavailableRuns: summaryRow.usageUnavailableRuns ?? 0,
572
+ prevUnpricedRuns: prevRow.unpricedRuns ?? 0,
482
573
  failureRate: summaryRow.totalRuns > 0 ? (summaryRow.failed ?? 0) / summaryRow.totalRuns : 0,
483
574
  prevTotalCostUsd: prevRow.totalCost,
484
575
  deltaPct,
485
- avgCostPerRun: summaryRow.avgCost,
576
+ avgCostPerRun: (summaryRow.successfulUnpriced ?? 0) > 0
577
+ ? null
578
+ : summaryRow.avgCost,
486
579
  },
487
580
  bySurface,
488
581
  byModel,
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // Centralized claude/codex spawn wrapper.
2
+ // Centralized AI CLI spawn wrapper.
3
3
  //
4
4
  // Why this exists:
5
5
  //
@@ -19,7 +19,12 @@
19
19
  // The helpers below detect multi-line argv values on Windows,
20
20
  // reroute them through child stdin (claude reads stdin when
21
21
  // `-p`/`--print` has no positional argument; codex `exec -` does the
22
- // equivalent), and call spawnCli. POSIX is unchanged byte-for-byte.
22
+ // equivalent), and call spawnCli. Kimi's required `-p <prompt>` has no stdin
23
+ // equivalent, so its Windows npm shim is unwrapped and its JavaScript entry is
24
+ // launched with Node directly. POSIX is unchanged byte-for-byte.
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
23
28
  Object.defineProperty(exports, "__esModule", { value: true });
24
29
  exports.appendCodexHeadroomRelayOverride = appendCodexHeadroomRelayOverride;
25
30
  exports.transformClaudeArgsForWindows = transformClaudeArgsForWindows;
@@ -28,7 +33,11 @@ exports.ensureStdinPipe = ensureStdinPipe;
28
33
  exports.spawnClaude = spawnClaude;
29
34
  exports.spawnCodex = spawnCodex;
30
35
  exports.spawnGemini = spawnGemini;
36
+ exports.parseNpmCmdShimEntry = parseNpmCmdShimEntry;
37
+ exports.spawnKimi = spawnKimi;
31
38
  exports.spawnAiCli = spawnAiCli;
39
+ const node_fs_1 = require("node:fs");
40
+ const node_path_1 = __importDefault(require("node:path"));
32
41
  const win_spawn_1 = require("./win-spawn");
33
42
  const headroom_routing_1 = require("../headroom-routing");
34
43
  const process_admission_1 = require("../process-admission");
@@ -149,7 +158,10 @@ function ensureStdinPipe(stdio) {
149
158
  }
150
159
  if (Array.isArray(stdio)) {
151
160
  return [
152
- stdio[0] === 'ignore' ? 'pipe' : (stdio[0] ?? 'pipe'),
161
+ // The transformed prompt always travels on fd 0. `inherit`,
162
+ // `overlapped`, numeric fds, and streams leave child.stdin null just as
163
+ // `ignore` does, so every array form must be upgraded unconditionally.
164
+ 'pipe',
153
165
  stdio[1] ?? 'pipe',
154
166
  stdio[2] ?? 'pipe',
155
167
  ];
@@ -252,10 +264,133 @@ function spawnGemini(args, options = {}) {
252
264
  }
253
265
  return (0, win_spawn_1.spawnCli)('gemini', args, { ...options, env });
254
266
  }
267
+ /**
268
+ * Extract the JavaScript entry from a standard npm-generated Windows `.cmd`
269
+ * launcher. npm writes the target as `%dp0%\<package path>.mjs %*`; resolving
270
+ * that path lets us bypass cmd.exe while preserving the exact argv payload.
271
+ */
272
+ function parseNpmCmdShimEntry(shimPath, contents) {
273
+ const match = contents.match(/%dp0%[\\/]([^"\r\n]*?\.(?:mjs|cjs|js))["']?\s+%\*/i);
274
+ if (!match?.[1])
275
+ return null;
276
+ return node_path_1.default.win32.join(node_path_1.default.win32.dirname(shimPath), match[1]);
277
+ }
278
+ /**
279
+ * Spawn Kimi in daemon-free prompt mode.
280
+ *
281
+ * Kimi 0.27 requires `-p <prompt>` and cannot itself read that prompt from
282
+ * stdin. On Windows, a native executable receives argv directly. For npm's
283
+ * `.cmd` shim, launch a tiny Node bootstrap that reads the complete prompt from
284
+ * stdin, restores it into process.argv at the `-p` value slot, then imports the
285
+ * generated JavaScript entry. This bypasses both cmd.exe newline reparsing and
286
+ * CreateProcess's ~32K command-line ceiling without changing Kimi semantics.
287
+ * A non-standard or unreadable command shim fails closed.
288
+ */
289
+ const KIMI_WINDOWS_ARGV_SAFE_CHARS = 30_000;
290
+ const KIMI_WINDOWS_STDIN_BOOTSTRAP = [
291
+ 'const fs=require("node:fs");',
292
+ 'const {pathToFileURL}=require("node:url");',
293
+ 'const entry=process.argv[1];',
294
+ 'const slot=Number(process.argv[2]);',
295
+ 'const args=process.argv.slice(3);',
296
+ 'args[slot]=fs.readFileSync(0,"utf8");',
297
+ 'process.argv=[process.execPath,entry,...args];',
298
+ 'import(pathToFileURL(entry).href).catch((error)=>{console.error(error);process.exitCode=1;});',
299
+ ].join('');
300
+ function kimiPromptValueIndex(args) {
301
+ const flagIndex = args.indexOf('-p');
302
+ return flagIndex >= 0 && flagIndex + 1 < args.length ? flagIndex + 1 : -1;
303
+ }
304
+ function estimatedWindowsArgvChars(binary, args) {
305
+ return binary.length + 1 + args.reduce((total, arg) => total + arg.length + 3, 0);
306
+ }
307
+ function spawnKimi(args, options = {}) {
308
+ (0, process_admission_1.assertProcessAdmission)();
309
+ if (!isWin())
310
+ return (0, win_spawn_1.spawnCli)('kimi', args, options);
311
+ /* c8 ignore start -- Windows-only branch; unit tests force process.platform */
312
+ const resolved = (0, win_spawn_1.resolveWindowsBinary)('kimi');
313
+ const extension = node_path_1.default.win32.extname(resolved).toLowerCase();
314
+ if (extension !== '.cmd' && extension !== '.bat') {
315
+ const promptIndex = kimiPromptValueIndex(args);
316
+ if (promptIndex >= 0 &&
317
+ estimatedWindowsArgvChars(resolved, args) > KIMI_WINDOWS_ARGV_SAFE_CHARS) {
318
+ throw new Error(`kimi_windows_native_prompt_too_large:${args[promptIndex].length}: ` +
319
+ 'the native Kimi executable cannot receive this prompt within the Windows ' +
320
+ 'CreateProcess limit; install the npm Kimi Code CLI shim or shorten the prompt.');
321
+ }
322
+ return (0, win_spawn_1.spawnCli)(resolved, args, options);
323
+ }
324
+ let entry;
325
+ try {
326
+ entry = parseNpmCmdShimEntry(resolved, (0, node_fs_1.readFileSync)(resolved, 'utf8'));
327
+ }
328
+ catch (error) {
329
+ throw new Error(`unsupported_kimi_windows_shim:${resolved}:` +
330
+ (error instanceof Error ? error.message : String(error)));
331
+ }
332
+ if (!entry) {
333
+ throw new Error(`unsupported_kimi_windows_shim:${resolved}`);
334
+ }
335
+ const localNode = node_path_1.default.win32.join(node_path_1.default.win32.dirname(resolved), 'node.exe');
336
+ const nodeBinary = (0, node_fs_1.existsSync)(localNode) ? localNode : 'node';
337
+ const promptIndex = kimiPromptValueIndex(args);
338
+ if (promptIndex < 0) {
339
+ return (0, win_spawn_1.spawnCli)(nodeBinary, [entry, ...args], options);
340
+ }
341
+ const prompt = args[promptIndex];
342
+ const forwardedArgs = [...args];
343
+ // Preserve the exact argv shape for Kimi; the bootstrap replaces only this
344
+ // placeholder after reading stdin. The real prompt never reaches
345
+ // CreateProcess, cmd.exe, environment variables, or a temporary file.
346
+ forwardedArgs[promptIndex] = '';
347
+ const spawnOptions = {
348
+ ...options,
349
+ stdio: ensureStdinPipe(options.stdio),
350
+ };
351
+ const child = (0, win_spawn_1.spawnCli)(nodeBinary, [
352
+ '-e',
353
+ KIMI_WINDOWS_STDIN_BOOTSTRAP,
354
+ entry,
355
+ String(promptIndex),
356
+ ...forwardedArgs,
357
+ ], spawnOptions);
358
+ if (!child.stdin) {
359
+ try {
360
+ child.kill('SIGTERM');
361
+ }
362
+ catch { /* best-effort */ }
363
+ throw new Error('kimi_windows_bootstrap_stdin_unavailable');
364
+ }
365
+ // A short-lived bootstrap can exit before the buffered write settles.
366
+ // Without an error listener, the resulting EPIPE is an unhandled EventEmitter
367
+ // error and can terminate Desktop itself. Broken pipes are already reflected
368
+ // by the child's terminal status; log any different transport failure.
369
+ child.stdin.on('error', (error) => {
370
+ if (error.code === 'EPIPE')
371
+ return;
372
+ console.error('[cli-prompt] Kimi Windows prompt transport failed:', error);
373
+ // A failed transport must never leave the bootstrap free to invoke Kimi
374
+ // with an empty or partial prompt. Managers will observe the terminated
375
+ // child through their normal lifecycle/error path.
376
+ try {
377
+ child.kill('SIGTERM');
378
+ }
379
+ catch { /* best-effort */ }
380
+ });
381
+ try {
382
+ child.stdin.end(prompt);
383
+ }
384
+ catch (error) {
385
+ if (error?.code !== 'EPIPE')
386
+ throw error;
387
+ }
388
+ return child;
389
+ /* c8 ignore stop */
390
+ }
255
391
  /**
256
392
  * Convenience: dispatch on binary name. Use when callsite picks the
257
- * binary dynamically (claude vs codex vs gemini). Anything else routes through
258
- * the underlying spawnCli unchanged.
393
+ * binary dynamically. Anything else routes through the underlying spawnCli.
259
394
  */
260
395
  function spawnAiCli(binary, args, options = {}) {
261
396
  (0, process_admission_1.assertProcessAdmission)();
@@ -265,5 +400,7 @@ function spawnAiCli(binary, args, options = {}) {
265
400
  return spawnCodex(args, options);
266
401
  if (binary === 'gemini')
267
402
  return spawnGemini(args, options);
403
+ if (binary === 'kimi')
404
+ return spawnKimi(args, options);
268
405
  return (0, win_spawn_1.spawnCli)(binary, args, options);
269
406
  }
@@ -14,6 +14,52 @@ exports.extractDisplayText = extractDisplayText;
14
14
  */
15
15
  function extractDisplayText(event) {
16
16
  const type = event.type;
17
+ // ── Kimi `--output-format stream-json` ────────────────────────────────
18
+ // Kimi assistant frames are role-discriminated (no top-level `type`) and
19
+ // can contain both narration and OpenAI-shaped function calls in one line.
20
+ if (!type && event.role === 'assistant') {
21
+ const parts = [];
22
+ const content = event.content;
23
+ const text = typeof content === 'string'
24
+ ? content
25
+ : Array.isArray(content)
26
+ ? content.map((part) => {
27
+ if (typeof part === 'string')
28
+ return part;
29
+ if (!part || typeof part !== 'object')
30
+ return '';
31
+ const candidate = part;
32
+ return typeof candidate.text === 'string' ? candidate.text : '';
33
+ }).join('')
34
+ : '';
35
+ if (text)
36
+ parts.push(text);
37
+ const toolCalls = Array.isArray(event.tool_calls) ? event.tool_calls : [];
38
+ for (const call of toolCalls) {
39
+ if (!call || typeof call !== 'object')
40
+ continue;
41
+ const fn = call.function;
42
+ if (!fn || typeof fn !== 'object')
43
+ continue;
44
+ const record = fn;
45
+ const name = typeof record.name === 'string' ? record.name : '<unnamed>';
46
+ const rawArgs = record.arguments;
47
+ let args;
48
+ if (typeof rawArgs === 'string') {
49
+ args = rawArgs;
50
+ }
51
+ else {
52
+ try {
53
+ args = JSON.stringify(rawArgs ?? {});
54
+ }
55
+ catch {
56
+ args = String(rawArgs ?? '');
57
+ }
58
+ }
59
+ parts.push(`[tool: ${name}] ${args.slice(0, 120)}`);
60
+ }
61
+ return parts.join('\n') || null;
62
+ }
17
63
  // ── Claude `--output-format stream-json` ───────────────────────────────
18
64
  if (type === 'assistant') {
19
65
  const content = event.message;