spiracha 1.5.0 → 2.0.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 (365) hide show
  1. package/AGENTS.md +114 -205
  2. package/README.md +89 -269
  3. package/apps/ui/AGENTS.md +17 -7
  4. package/apps/ui/README.md +34 -8
  5. package/apps/ui/components.json +21 -0
  6. package/apps/ui/package.json +65 -0
  7. package/apps/ui/src/components/antigravity-conversations-table.tsx +206 -0
  8. package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
  9. package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
  10. package/apps/ui/src/components/app-shell.tsx +124 -0
  11. package/apps/ui/src/components/breadcrumbs.tsx +53 -0
  12. package/apps/ui/src/components/claude-code-sessions-table.tsx +114 -0
  13. package/apps/ui/src/components/claude-code-workspaces-table.tsx +55 -0
  14. package/apps/ui/src/components/cursor-threads-table.tsx +189 -0
  15. package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
  16. package/apps/ui/src/components/data-table.tsx +241 -0
  17. package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
  18. package/apps/ui/src/components/export-dialog.tsx +184 -0
  19. package/apps/ui/src/components/json-panel.tsx +17 -0
  20. package/apps/ui/src/components/kiro-sessions-table.tsx +112 -0
  21. package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
  22. package/apps/ui/src/components/list-search-input.tsx +18 -0
  23. package/apps/ui/src/components/loading-panel.tsx +23 -0
  24. package/apps/ui/src/components/metadata-section.tsx +31 -0
  25. package/apps/ui/src/components/metric-card.tsx +19 -0
  26. package/apps/ui/src/components/opencode-sessions-table.tsx +128 -0
  27. package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
  28. package/apps/ui/src/components/page-header.tsx +33 -0
  29. package/apps/ui/src/components/projects-loading-state.tsx +74 -0
  30. package/apps/ui/src/components/projects-table.tsx +108 -0
  31. package/apps/ui/src/components/qoder-sessions-table.tsx +112 -0
  32. package/apps/ui/src/components/qoder-workspaces-table.tsx +53 -0
  33. package/apps/ui/src/components/recent-threads-list.tsx +52 -0
  34. package/apps/ui/src/components/reload-error-panel.tsx +20 -0
  35. package/apps/ui/src/components/text-document-panel.tsx +19 -0
  36. package/apps/ui/src/components/theme-toggle.tsx +48 -0
  37. package/apps/ui/src/components/threads-table.tsx +202 -0
  38. package/apps/ui/src/components/transcript-view.tsx +552 -0
  39. package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
  40. package/apps/ui/src/components/ui/badge.tsx +45 -0
  41. package/apps/ui/src/components/ui/button.tsx +62 -0
  42. package/apps/ui/src/components/ui/checkbox.tsx +29 -0
  43. package/apps/ui/src/components/ui/dialog.tsx +137 -0
  44. package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
  45. package/apps/ui/src/components/ui/input.tsx +21 -0
  46. package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
  47. package/apps/ui/src/components/ui/select.tsx +163 -0
  48. package/apps/ui/src/components/ui/separator.tsx +28 -0
  49. package/apps/ui/src/components/ui/sheet.tsx +107 -0
  50. package/apps/ui/src/components/ui/skeleton.tsx +7 -0
  51. package/apps/ui/src/components/ui/table.tsx +76 -0
  52. package/apps/ui/src/components/ui/tabs.tsx +71 -0
  53. package/apps/ui/src/components/ui/tooltip.tsx +44 -0
  54. package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
  55. package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
  56. package/apps/ui/src/lib/antigravity-conversation-state.ts +28 -0
  57. package/apps/ui/src/lib/antigravity-queries.ts +33 -0
  58. package/apps/ui/src/lib/antigravity-server.ts +128 -0
  59. package/apps/ui/src/lib/antigravity-transcript-events.ts +335 -0
  60. package/apps/ui/src/lib/claude-code-queries.ts +26 -0
  61. package/apps/ui/src/lib/claude-code-server.ts +75 -0
  62. package/apps/ui/src/lib/claude-code-transcript-events.ts +154 -0
  63. package/apps/ui/src/lib/codex-queries.ts +66 -0
  64. package/apps/ui/src/lib/codex-server.ts +209 -0
  65. package/apps/ui/src/lib/cursor-queries.ts +37 -0
  66. package/apps/ui/src/lib/cursor-server.ts +272 -0
  67. package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
  68. package/apps/ui/src/lib/download.ts +166 -0
  69. package/apps/ui/src/lib/formatters.ts +149 -0
  70. package/apps/ui/src/lib/kiro-queries.ts +22 -0
  71. package/apps/ui/src/lib/kiro-server.ts +73 -0
  72. package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
  73. package/apps/ui/src/lib/opencode-queries.ts +37 -0
  74. package/apps/ui/src/lib/opencode-server.ts +73 -0
  75. package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
  76. package/apps/ui/src/lib/package-metadata.ts +16 -0
  77. package/apps/ui/src/lib/path-utils.ts +13 -0
  78. package/apps/ui/src/lib/qoder-queries.ts +22 -0
  79. package/apps/ui/src/lib/qoder-server.ts +79 -0
  80. package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
  81. package/apps/ui/src/lib/route-search.ts +68 -0
  82. package/apps/ui/src/lib/settings-store.tsx +59 -0
  83. package/apps/ui/src/lib/source-session-export-server.ts +56 -0
  84. package/apps/ui/src/lib/text-filter.ts +22 -0
  85. package/apps/ui/src/lib/thread-id.ts +3 -0
  86. package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
  87. package/apps/ui/src/lib/utils.ts +7 -0
  88. package/apps/ui/src/routeTree.gen.ts +730 -0
  89. package/apps/ui/src/router.tsx +20 -0
  90. package/apps/ui/src/routes/$threadId.tsx +22 -0
  91. package/apps/ui/src/routes/__root.tsx +127 -0
  92. package/apps/ui/src/routes/analytics.tsx +143 -0
  93. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +438 -0
  94. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +127 -0
  95. package/apps/ui/src/routes/antigravity.index.tsx +51 -0
  96. package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
  97. package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
  98. package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +12 -0
  99. package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
  100. package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
  101. package/apps/ui/src/routes/api.v1.sources.ts +12 -0
  102. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +342 -0
  103. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +135 -0
  104. package/apps/ui/src/routes/claude-code.index.tsx +48 -0
  105. package/apps/ui/src/routes/codex.$project.tsx +390 -0
  106. package/apps/ui/src/routes/codex.index.tsx +116 -0
  107. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
  108. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +416 -0
  109. package/apps/ui/src/routes/cursor.index.tsx +120 -0
  110. package/apps/ui/src/routes/index.tsx +124 -0
  111. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +345 -0
  112. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +136 -0
  113. package/apps/ui/src/routes/kiro.index.tsx +48 -0
  114. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +334 -0
  115. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +135 -0
  116. package/apps/ui/src/routes/opencode.index.tsx +48 -0
  117. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +341 -0
  118. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +139 -0
  119. package/apps/ui/src/routes/qoder.index.tsx +48 -0
  120. package/apps/ui/src/routes/settings.tsx +91 -0
  121. package/apps/ui/src/routes/threads.$threadId.tsx +619 -0
  122. package/apps/ui/src/styles.css +122 -0
  123. package/apps/ui/tsconfig.json +29 -0
  124. package/apps/ui/vite.config.ts +84 -0
  125. package/bin/spiracha.ts +32 -0
  126. package/package.json +46 -73
  127. package/src/client.ts +290 -0
  128. package/src/lib/antigravity-db.ts +213 -19
  129. package/src/lib/antigravity-exporter-types.ts +1 -0
  130. package/src/lib/codex-analytics.ts +1 -1
  131. package/src/lib/codex-browser-db.ts +410 -74
  132. package/src/lib/codex-browser-export.ts +6 -14
  133. package/src/lib/codex-browser-types.ts +1 -1
  134. package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
  135. package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +64 -28
  136. package/src/lib/conversation-api.ts +792 -0
  137. package/src/lib/conversation-data/adapter-helpers.ts +110 -0
  138. package/src/lib/conversation-data/antigravity-adapter.ts +148 -0
  139. package/src/lib/conversation-data/claude-code-adapter.ts +175 -0
  140. package/src/lib/conversation-data/codex-adapter.ts +245 -0
  141. package/src/lib/conversation-data/cursor-adapter.ts +171 -0
  142. package/src/lib/conversation-data/index.ts +311 -0
  143. package/src/lib/conversation-data/kiro-adapter.ts +140 -0
  144. package/src/lib/conversation-data/message-selector.ts +37 -0
  145. package/src/lib/conversation-data/opencode-adapter.ts +180 -0
  146. package/src/lib/conversation-data/path-match.ts +79 -0
  147. package/src/lib/conversation-data/qoder-adapter.ts +230 -0
  148. package/src/lib/conversation-data/types.ts +116 -0
  149. package/src/lib/cursor-db.ts +67 -21
  150. package/src/lib/cursor-exporter-types.ts +0 -19
  151. package/src/lib/qoder-acp-client.ts +268 -0
  152. package/src/lib/qoder-db.ts +1601 -0
  153. package/src/lib/qoder-exporter-types.ts +114 -0
  154. package/src/lib/qoder-transcript-phase.ts +41 -0
  155. package/src/lib/qoder-transcript.ts +112 -0
  156. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
  157. package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +0 -1
  158. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +0 -7
  159. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
  160. package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +0 -1
  161. package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +0 -1
  162. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
  163. package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +0 -1
  164. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
  165. package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +0 -1
  166. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
  167. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +0 -1
  168. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +0 -1
  169. package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +0 -1
  170. package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +0 -1
  171. package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +0 -1
  172. package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +0 -1
  173. package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +0 -3
  174. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
  175. package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +0 -1
  176. package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +0 -1
  177. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
  178. package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +0 -2
  179. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
  180. package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
  181. package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +0 -7
  182. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
  183. package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +0 -5
  184. package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +0 -1
  185. package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
  186. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
  187. package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +0 -1
  188. package/apps/ui/dist/client/assets/es2015-ad6040dw.js +0 -41
  189. package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +0 -1
  190. package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +0 -1
  191. package/apps/ui/dist/client/assets/index-C-STgIH4.js +0 -166
  192. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
  193. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +0 -1
  194. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +0 -1
  195. package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +0 -1
  196. package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +0 -1
  197. package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +0 -1
  198. package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +0 -1
  199. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
  200. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +0 -1
  201. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +0 -8
  202. package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +0 -1
  203. package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +0 -1
  204. package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +0 -1
  205. package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +0 -1
  206. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
  207. package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +0 -1
  208. package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +0 -1
  209. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
  210. package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +0 -3
  211. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
  212. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
  213. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
  214. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
  215. package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +0 -1
  216. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
  217. package/apps/ui/dist/client/assets/select-CYF6pF37.js +0 -1
  218. package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +0 -1
  219. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
  220. package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +0 -1
  221. package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +0 -7
  222. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
  223. package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +0 -1
  224. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
  225. package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +0 -1
  226. package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +0 -1
  227. package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +0 -1
  228. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +0 -387
  229. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
  230. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
  231. package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
  232. package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
  233. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +0 -613
  234. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
  235. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-eS87Dx7g.js +0 -20
  236. package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
  237. package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
  238. package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
  239. package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
  240. package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
  241. package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
  242. package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
  243. package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
  244. package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
  245. package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
  246. package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
  247. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
  248. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
  249. package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +0 -361
  250. package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +0 -37
  251. package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +0 -72
  252. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +0 -18
  253. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +0 -473
  254. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +0 -11
  255. package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +0 -139
  256. package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +0 -10
  257. package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +0 -28
  258. package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +0 -11
  259. package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +0 -191
  260. package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +0 -92
  261. package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +0 -11
  262. package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
  263. package/apps/ui/dist/server/assets/codex-server-CGXU4H8-.js +0 -2294
  264. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
  265. package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
  266. package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
  267. package/apps/ui/dist/server/assets/cursor-db-CZnYy7aT.js +0 -830
  268. package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
  269. package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
  270. package/apps/ui/dist/server/assets/cursor-recovery-CXpO8KcD.js +0 -361
  271. package/apps/ui/dist/server/assets/cursor-server-DNu52A3K.js +0 -213
  272. package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
  273. package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +0 -582
  274. package/apps/ui/dist/server/assets/cursor-threads._composerId-p9-ARAzc.js +0 -18
  275. package/apps/ui/dist/server/assets/cursor-transcript-B7tGnqtn.js +0 -125
  276. package/apps/ui/dist/server/assets/cursor._workspaceKey-CZuohIUL.js +0 -28
  277. package/apps/ui/dist/server/assets/cursor._workspaceKey-DPp5JSjy.js +0 -401
  278. package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
  279. package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
  280. package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
  281. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
  282. package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
  283. package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
  284. package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
  285. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
  286. package/apps/ui/dist/server/assets/export-dialog-D88ze9Gy.js +0 -240
  287. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
  288. package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +0 -552
  289. package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +0 -37
  290. package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +0 -72
  291. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +0 -11
  292. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +0 -449
  293. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +0 -18
  294. package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +0 -112
  295. package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +0 -27
  296. package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +0 -11
  297. package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +0 -28
  298. package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +0 -193
  299. package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +0 -11
  300. package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +0 -93
  301. package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
  302. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
  303. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
  304. package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +0 -397
  305. package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +0 -50
  306. package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +0 -72
  307. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +0 -518
  308. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +0 -18
  309. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +0 -11
  310. package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +0 -87
  311. package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +0 -141
  312. package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +0 -32
  313. package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +0 -11
  314. package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +0 -28
  315. package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +0 -207
  316. package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +0 -11
  317. package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +0 -93
  318. package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
  319. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
  320. package/apps/ui/dist/server/assets/projects._project-4io5LO0E.js +0 -20
  321. package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
  322. package/apps/ui/dist/server/assets/projects._project-D6ZoGhJ8.js +0 -395
  323. package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
  324. package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
  325. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
  326. package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
  327. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
  328. package/apps/ui/dist/server/assets/router-Sac2DGk6.js +0 -518
  329. package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
  330. package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
  331. package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
  332. package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
  333. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
  334. package/apps/ui/dist/server/assets/shared-DyhChtHf.js +0 -137
  335. package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +0 -38
  336. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
  337. package/apps/ui/dist/server/assets/start-B_HFwkkk.js +0 -4
  338. package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +0 -501
  339. package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
  340. package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +0 -45
  341. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
  342. package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +0 -648
  343. package/apps/ui/dist/server/assets/threads._threadId-Ci0S4mKU.js +0 -18
  344. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
  345. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
  346. package/apps/ui/dist/server/server.js +0 -5895
  347. package/bin/codex-chats-claude.js +0 -5
  348. package/bin/codex-chats.js +0 -5
  349. package/bin/spiracha.js +0 -5
  350. package/src/export-chats.ts +0 -120
  351. package/src/export-claude.ts +0 -36
  352. package/src/export-cursor.ts +0 -244
  353. package/src/lib/claude-exporter.ts +0 -864
  354. package/src/lib/codex-exporter-cli.ts +0 -277
  355. package/src/lib/codex-exporter-db.ts +0 -319
  356. package/src/lib/codex-exporter.ts +0 -115
  357. package/src/lib/cursor-exporter.ts +0 -266
  358. package/src/lib/interactive-cli.ts +0 -433
  359. package/src/lib/native-open.ts +0 -54
  360. package/src/mcp-server.ts +0 -137
  361. package/src/spiracha.ts +0 -116
  362. package/src/ui-cli.ts +0 -310
  363. /package/apps/ui/{dist/client → public}/icon.svg +0 -0
  364. /package/apps/ui/{dist/client → public}/manifest.json +0 -0
  365. /package/apps/ui/{dist/client → public}/robots.txt +0 -0
package/src/client.ts ADDED
@@ -0,0 +1,290 @@
1
+ import {
2
+ getConversation as getLocalConversation,
3
+ listConversationSources as listLocalConversationSources,
4
+ listConversationsForPath as listLocalConversationsForPath,
5
+ renderConversationMarkdown as renderLocalConversationMarkdown,
6
+ resolveConversationRef as resolveLocalConversationRef,
7
+ } from './lib/conversation-data';
8
+ import type {
9
+ ConversationDataLocations,
10
+ ConversationDetail,
11
+ ConversationMessageSelector,
12
+ ConversationPage,
13
+ ConversationSourceInfo,
14
+ GetConversationOptions,
15
+ ListConversationsForPathOptions,
16
+ ResolvedConversationRef,
17
+ } from './lib/conversation-data/types';
18
+
19
+ export type {
20
+ ConversationDataLocations,
21
+ ConversationDeepLinks,
22
+ ConversationDetail,
23
+ ConversationMessage,
24
+ ConversationMessagePhase,
25
+ ConversationMessageRole,
26
+ ConversationMessageSelector,
27
+ ConversationPage,
28
+ ConversationPathMatch,
29
+ ConversationSource,
30
+ ConversationSourceInfo,
31
+ GetConversationOptions,
32
+ ListConversationsForPathOptions,
33
+ ResolvedConversationRef,
34
+ } from './lib/conversation-data/types';
35
+
36
+ type HttpEnvelope<T> = {
37
+ data?: T;
38
+ error?: {
39
+ message?: string | null;
40
+ } | null;
41
+ meta?: {
42
+ hasNext?: boolean | null;
43
+ nextCursor?: string | null;
44
+ next_cursor?: string | null;
45
+ } | null;
46
+ };
47
+
48
+ export class SpirachaClientError extends Error {
49
+ readonly status: number | null;
50
+
51
+ constructor(message: string, status: number | null = null) {
52
+ super(message);
53
+ this.name = 'SpirachaClientError';
54
+ this.status = status;
55
+ }
56
+ }
57
+
58
+ export type LocalConversationClientOptions = {
59
+ locations?: ConversationDataLocations;
60
+ mode?: 'local';
61
+ };
62
+
63
+ export type HttpConversationClientOptions = {
64
+ baseUrl: string;
65
+ mode: 'http';
66
+ };
67
+
68
+ export type CreateConversationClientOptions = HttpConversationClientOptions | LocalConversationClientOptions;
69
+
70
+ export type ExportConversationMarkdownOptions = GetConversationOptions;
71
+
72
+ export type ConversationClient = {
73
+ exportConversationMarkdown: (options: ExportConversationMarkdownOptions) => Promise<string | null>;
74
+ getConversation: (options: GetConversationOptions) => Promise<ConversationDetail | null>;
75
+ listConversations: (options: ListConversationsForPathOptions) => Promise<ConversationPage>;
76
+ listSources: () => Promise<ConversationSourceInfo[]>;
77
+ resolveConversationRef: (ref: string) => Promise<ResolvedConversationRef | null>;
78
+ };
79
+
80
+ const withDefaultLocations = <T extends { locations?: ConversationDataLocations }>(
81
+ options: T,
82
+ locations: ConversationDataLocations | undefined,
83
+ ): T => {
84
+ return locations && !options.locations ? { ...options, locations } : options;
85
+ };
86
+
87
+ const normalizeBaseUrl = (value: string): URL => {
88
+ try {
89
+ const url = new URL(value);
90
+ if (url.protocol === 'http:' || url.protocol === 'https:') {
91
+ return url;
92
+ }
93
+ } catch {
94
+ // Fall through to a consistent client-facing error.
95
+ }
96
+
97
+ throw new SpirachaClientError(`Invalid Spiracha base URL "${value}". Use an http:// or https:// URL.`);
98
+ };
99
+
100
+ const appendOptionalNumber = (url: URL, key: string, value: number | undefined): void => {
101
+ if (value !== undefined) {
102
+ url.searchParams.set(key, String(value));
103
+ }
104
+ };
105
+
106
+ const appendListOptions = (url: URL, options: ListConversationsForPathOptions): void => {
107
+ url.searchParams.set('cwd', options.cwd);
108
+ if (options.cursor) {
109
+ url.searchParams.set('cursor', options.cursor);
110
+ }
111
+ if (options.includeMessages !== undefined) {
112
+ url.searchParams.set('include_messages', String(options.includeMessages));
113
+ }
114
+ if (options.limit !== undefined) {
115
+ url.searchParams.set('limit', String(options.limit));
116
+ }
117
+ if (options.messageSelector) {
118
+ url.searchParams.set('message_selector', options.messageSelector);
119
+ }
120
+ if (options.sources && options.sources !== 'all') {
121
+ url.searchParams.set('source', options.sources.join(','));
122
+ }
123
+ appendOptionalNumber(url, 'updated_after_ms', options.updatedAfterMs);
124
+ appendOptionalNumber(url, 'updated_before_ms', options.updatedBeforeMs);
125
+ };
126
+
127
+ const appendMessageSelector = (url: URL, messageSelector: ConversationMessageSelector | undefined): void => {
128
+ if (messageSelector) {
129
+ url.searchParams.set('message_selector', messageSelector);
130
+ }
131
+ };
132
+
133
+ const httpErrorMessage = async (response: Response): Promise<string> => {
134
+ const text = await response.text();
135
+ if (!text.trim()) {
136
+ return response.statusText || `HTTP ${response.status}`;
137
+ }
138
+
139
+ try {
140
+ const parsed = JSON.parse(text) as HttpEnvelope<unknown>;
141
+ return parsed.error?.message || text.trim();
142
+ } catch {
143
+ return text.trim();
144
+ }
145
+ };
146
+
147
+ const fetchResponse = async (url: URL, init?: RequestInit): Promise<Response> => {
148
+ try {
149
+ return await fetch(url, init);
150
+ } catch (error) {
151
+ const message = error instanceof Error ? error.message : String(error);
152
+ throw new SpirachaClientError(`Unable to reach Spiracha at ${url.origin}: ${message}`);
153
+ }
154
+ };
155
+
156
+ const assertOkResponse = async (response: Response): Promise<void> => {
157
+ if (response.ok) {
158
+ return;
159
+ }
160
+
161
+ throw new SpirachaClientError(
162
+ `Spiracha API request failed (${response.status}): ${await httpErrorMessage(response)}`,
163
+ response.status,
164
+ );
165
+ };
166
+
167
+ const readJsonEnvelope = async <T>(response: Response): Promise<HttpEnvelope<T>> => {
168
+ try {
169
+ return (await response.json()) as HttpEnvelope<T>;
170
+ } catch {
171
+ throw new SpirachaClientError('Spiracha API returned invalid JSON.', response.status);
172
+ }
173
+ };
174
+
175
+ const fetchJson = async <T>(url: URL, init?: RequestInit): Promise<HttpEnvelope<T>> => {
176
+ const response = await fetchResponse(url, init);
177
+ await assertOkResponse(response);
178
+ return readJsonEnvelope(response);
179
+ };
180
+
181
+ const fetchJsonOrNull = async <T>(url: URL, init?: RequestInit): Promise<HttpEnvelope<T> | null> => {
182
+ const response = await fetchResponse(url, init);
183
+ if (response.status === 404) {
184
+ return null;
185
+ }
186
+
187
+ await assertOkResponse(response);
188
+ return readJsonEnvelope(response);
189
+ };
190
+
191
+ const fetchTextOrNull = async (url: URL, init?: RequestInit): Promise<string | null> => {
192
+ const response = await fetchResponse(url, init);
193
+ if (response.status === 404) {
194
+ return null;
195
+ }
196
+
197
+ await assertOkResponse(response);
198
+ return response.text();
199
+ };
200
+
201
+ const requireData = <T>(envelope: HttpEnvelope<T>, label: string): T => {
202
+ if (envelope.data === undefined) {
203
+ throw new SpirachaClientError(`Spiracha API response did not include ${label}.`);
204
+ }
205
+
206
+ return envelope.data;
207
+ };
208
+
209
+ const normalizePage = (envelope: HttpEnvelope<ConversationDetail[]>): ConversationPage => {
210
+ const data = requireData(envelope, 'a data field');
211
+ if (!Array.isArray(data)) {
212
+ throw new SpirachaClientError('Spiracha API response did not include a data array.');
213
+ }
214
+
215
+ return {
216
+ data,
217
+ meta: {
218
+ hasNext: envelope.meta?.hasNext === true,
219
+ nextCursor: envelope.meta?.nextCursor ?? envelope.meta?.next_cursor ?? null,
220
+ },
221
+ };
222
+ };
223
+
224
+ const makeHttpUrl = (baseUrl: URL, pathname: string): URL => new URL(pathname, baseUrl);
225
+
226
+ const rejectHttpLocations = (locations: ConversationDataLocations | undefined): void => {
227
+ if (locations) {
228
+ throw new SpirachaClientError('`locations` is only supported by local Spiracha clients.');
229
+ }
230
+ };
231
+
232
+ const makeLocalClient = (options: LocalConversationClientOptions): ConversationClient => ({
233
+ exportConversationMarkdown: async (getOptions) => {
234
+ const conversation = await getLocalConversation(withDefaultLocations(getOptions, options.locations));
235
+ return conversation ? renderLocalConversationMarkdown(conversation) : null;
236
+ },
237
+ getConversation: (getOptions) => getLocalConversation(withDefaultLocations(getOptions, options.locations)),
238
+ listConversations: (listOptions) =>
239
+ listLocalConversationsForPath(withDefaultLocations(listOptions, options.locations)),
240
+ listSources: () => listLocalConversationSources(),
241
+ resolveConversationRef: (ref) => resolveLocalConversationRef(ref),
242
+ });
243
+
244
+ const makeHttpClient = (options: HttpConversationClientOptions): ConversationClient => {
245
+ const baseUrl = normalizeBaseUrl(options.baseUrl);
246
+
247
+ return {
248
+ exportConversationMarkdown: async (getOptions) => {
249
+ rejectHttpLocations(getOptions.locations);
250
+ const { id, messageSelector, source } = getOptions;
251
+ const url = makeHttpUrl(baseUrl, `/api/v1/conversations/${source}/${encodeURIComponent(id)}/export`);
252
+ appendMessageSelector(url, messageSelector);
253
+ return fetchTextOrNull(url);
254
+ },
255
+ getConversation: async (getOptions) => {
256
+ rejectHttpLocations(getOptions.locations);
257
+ const { id, messageSelector, source } = getOptions;
258
+ const url = makeHttpUrl(baseUrl, `/api/v1/conversations/${source}/${encodeURIComponent(id)}`);
259
+ appendMessageSelector(url, messageSelector);
260
+ const envelope = await fetchJsonOrNull<ConversationDetail>(url);
261
+ if (!envelope) {
262
+ return null;
263
+ }
264
+ return requireData(envelope, 'a conversation');
265
+ },
266
+ listConversations: async (listOptions) => {
267
+ rejectHttpLocations(listOptions.locations);
268
+ const url = makeHttpUrl(baseUrl, '/api/v1/conversations');
269
+ appendListOptions(url, listOptions);
270
+ return normalizePage(await fetchJson<ConversationDetail[]>(url));
271
+ },
272
+ listSources: async () => {
273
+ const envelope = await fetchJson<ConversationSourceInfo[]>(makeHttpUrl(baseUrl, '/api/v1/sources'));
274
+ return requireData(envelope, 'a source list');
275
+ },
276
+ resolveConversationRef: async (ref) => {
277
+ const url = makeHttpUrl(baseUrl, '/api/v1/resolve');
278
+ url.searchParams.set('ref', ref);
279
+ const envelope = await fetchJsonOrNull<ResolvedConversationRef>(url);
280
+ if (!envelope) {
281
+ return null;
282
+ }
283
+ return requireData(envelope, 'a resolved ref');
284
+ },
285
+ };
286
+ };
287
+
288
+ export const createConversationClient = (options: CreateConversationClientOptions = {}): ConversationClient => {
289
+ return options.mode === 'http' ? makeHttpClient(options) : makeLocalClient(options);
290
+ };
@@ -48,12 +48,23 @@ const isSafeConversationId = (value: string) => SAFE_CONVERSATION_ID_PATTERN.tes
48
48
  type TranscriptFile = {
49
49
  bytes: number;
50
50
  entryCount: number;
51
+ fullPath: string | null;
51
52
  mtimeMs: number;
53
+ model: string | null;
52
54
  path: string;
53
55
  root: string;
54
56
  source: Exclude<AntigravityTranscriptSource, 'safe-storage'>;
55
57
  };
56
58
 
59
+ export type AntigravityConversationMessage = {
60
+ createdAtMs: number | null;
61
+ metadata: Record<string, unknown>;
62
+ order: number;
63
+ phase: 'final_answer' | 'reasoning' | 'tool_call' | 'tool_output' | 'unknown';
64
+ role: 'assistant' | 'system' | 'tool' | 'unknown' | 'user';
65
+ text: string;
66
+ };
67
+
57
68
  type WorkspaceInfo = Pick<SummaryEntry, 'workspaceFolder' | 'workspaceKey' | 'workspaceLabel' | 'workspaceUri'>;
58
69
 
59
70
  const UNKNOWN_WORKSPACE: WorkspaceInfo = {
@@ -489,6 +500,44 @@ const countJsonlEntries = async (filePath: string): Promise<number> => {
489
500
  }
490
501
  };
491
502
 
503
+ const stripModelQualifier = (value: string): string => value.replace(/\s*\([^)]*\)\s*$/u, '').trim();
504
+
505
+ const extractModelSelection = (content: string): string | null => {
506
+ const marker = '`Model Selection`';
507
+ const markerIndex = content.indexOf(marker);
508
+ if (markerIndex < 0) {
509
+ return null;
510
+ }
511
+
512
+ const toIndex = content.indexOf(' to ', markerIndex + marker.length);
513
+ if (toIndex < 0) {
514
+ return null;
515
+ }
516
+
517
+ const candidate = content.slice(toIndex + ' to '.length);
518
+ const endMarkers = ['. No need', '. If reporting', '</USER_SETTINGS_CHANGE>', '\n'];
519
+ const endIndex = endMarkers.reduce<number | null>((earliest, endMarker) => {
520
+ const index = candidate.indexOf(endMarker);
521
+ return index < 0 || (earliest !== null && index >= earliest) ? earliest : index;
522
+ }, null);
523
+ const rawModel = (endIndex === null ? candidate : candidate.slice(0, endIndex)).trim();
524
+ return rawModel ? stripModelQualifier(rawModel) : null;
525
+ };
526
+
527
+ const extractTranscriptModel = async (filePath: string): Promise<string | null> => {
528
+ const text = await Bun.file(filePath)
529
+ .text()
530
+ .catch(() => '');
531
+ for (const entry of parseLogEntries(text)) {
532
+ const model = extractModelSelection(getString(entry.content) ?? '');
533
+ if (model) {
534
+ return model;
535
+ }
536
+ }
537
+
538
+ return null;
539
+ };
540
+
492
541
  const preferTranscriptFile = (current: TranscriptFile | undefined, candidate: TranscriptFile): TranscriptFile => {
493
542
  if (!current) {
494
543
  return candidate;
@@ -505,6 +554,36 @@ const preferTranscriptFile = (current: TranscriptFile | undefined, candidate: Tr
505
554
  return candidate.entryCount > current.entryCount ? candidate : current;
506
555
  };
507
556
 
557
+ const readTranscriptFileCandidate = async (
558
+ root: string,
559
+ logsDir: string,
560
+ candidate: { name: string; source: TranscriptFile['source'] },
561
+ ): Promise<TranscriptFile | null> => {
562
+ const transcriptPath = path.join(logsDir, candidate.name);
563
+ try {
564
+ const info = await stat(transcriptPath);
565
+ if (!info.isFile()) {
566
+ return null;
567
+ }
568
+
569
+ const fullPath = path.join(logsDir, 'transcript_full.jsonl');
570
+ const hasFullTranscript = await pathExists(fullPath);
571
+ const modelSourcePath = hasFullTranscript ? fullPath : transcriptPath;
572
+ return {
573
+ bytes: info.size,
574
+ entryCount: await countJsonlEntries(modelSourcePath),
575
+ fullPath: hasFullTranscript ? fullPath : null,
576
+ model: await extractTranscriptModel(modelSourcePath),
577
+ mtimeMs: info.mtimeMs,
578
+ path: transcriptPath,
579
+ root,
580
+ source: candidate.source,
581
+ };
582
+ } catch {
583
+ return null;
584
+ }
585
+ };
586
+
508
587
  const readTranscriptFilesForRoot = async (root: string): Promise<Map<string, TranscriptFile>> => {
509
588
  const brainDir = getAntigravityBrainDir(root);
510
589
  let entries: Array<{ isDirectory: () => boolean; name: string }> = [];
@@ -525,25 +604,10 @@ const readTranscriptFilesForRoot = async (root: string): Promise<Map<string, Tra
525
604
  { name: 'overview.txt', source: 'overview' as const },
526
605
  { name: 'transcript.jsonl', source: 'transcript' as const },
527
606
  ]) {
528
- const transcriptPath = path.join(logsDir, candidate.name);
529
- try {
530
- const info = await stat(transcriptPath);
531
- if (!info.isFile()) {
532
- continue;
533
- }
534
-
535
- transcripts.set(
536
- entry.name,
537
- preferTranscriptFile(transcripts.get(entry.name), {
538
- bytes: info.size,
539
- entryCount: await countJsonlEntries(transcriptPath),
540
- mtimeMs: info.mtimeMs,
541
- path: transcriptPath,
542
- root,
543
- source: candidate.source,
544
- }),
545
- );
546
- } catch {}
607
+ const transcript = await readTranscriptFileCandidate(root, logsDir, candidate);
608
+ if (transcript) {
609
+ transcripts.set(entry.name, preferTranscriptFile(transcripts.get(entry.name), transcript));
610
+ }
547
611
  }
548
612
  }
549
613
 
@@ -640,6 +704,7 @@ const toConversation = (
640
704
  createdAtMs: summary?.createdAtMs ?? null,
641
705
  indexedItemCount: summary?.indexedItemCount ?? null,
642
706
  lastUpdatedAtMs,
707
+ model: transcript?.model ?? null,
643
708
  sourceRoot,
644
709
  summaryPath: summary?.summaryPath ?? null,
645
710
  title: summary?.title ?? cleanTitle(fallbackTitle, conversationId),
@@ -872,6 +937,135 @@ const renderLogEntry = (entry: AntigravityLogEntry): string[] => {
872
937
  return parts;
873
938
  };
874
939
 
940
+ const logEntryCreatedAtMs = (entry: AntigravityLogEntry): number | null => {
941
+ const timestamp = getString(entry.created_at);
942
+ if (!timestamp) {
943
+ return null;
944
+ }
945
+
946
+ const parsed = Date.parse(timestamp);
947
+ return Number.isFinite(parsed) ? parsed : null;
948
+ };
949
+
950
+ const logEntryOrder = (entry: AntigravityLogEntry, fallback: number): number => {
951
+ return typeof entry.step_index === 'number' && Number.isFinite(entry.step_index) ? entry.step_index : fallback;
952
+ };
953
+
954
+ const isAssistantLogEntry = (entry: AntigravityLogEntry): boolean => {
955
+ return getString(entry.source) === 'MODEL' && getString(entry.type) === 'PLANNER_RESPONSE';
956
+ };
957
+
958
+ const logEntryRole = (entry: AntigravityLogEntry): AntigravityConversationMessage['role'] => {
959
+ const source = getString(entry.source);
960
+ if (source?.startsWith('USER')) {
961
+ return 'user';
962
+ }
963
+ if (isAssistantLogEntry(entry)) {
964
+ return 'assistant';
965
+ }
966
+ if (source === 'SYSTEM') {
967
+ return 'system';
968
+ }
969
+ if (source === 'MODEL') {
970
+ return 'tool';
971
+ }
972
+ return 'unknown';
973
+ };
974
+
975
+ const logEntryPhase = (entry: AntigravityLogEntry): AntigravityConversationMessage['phase'] => {
976
+ const role = logEntryRole(entry);
977
+ if (role === 'assistant') {
978
+ return 'final_answer';
979
+ }
980
+ if (role === 'tool') {
981
+ return 'tool_output';
982
+ }
983
+ return 'unknown';
984
+ };
985
+
986
+ const logEntryMetadata = (entry: AntigravityLogEntry): Record<string, unknown> => ({
987
+ source: getString(entry.source),
988
+ status: getString(entry.status),
989
+ type: getString(entry.type),
990
+ });
991
+
992
+ const toolCallsText = (toolCalls: unknown): string => {
993
+ if (!Array.isArray(toolCalls)) {
994
+ return '';
995
+ }
996
+
997
+ return toolCalls
998
+ .flatMap((call) => {
999
+ if (!call || typeof call !== 'object') {
1000
+ return [];
1001
+ }
1002
+ const { args, name } = call as { args?: unknown; name?: unknown };
1003
+ return [JSON.stringify({ args, name: typeof name === 'string' ? name : 'unknown' })];
1004
+ })
1005
+ .join('\n');
1006
+ };
1007
+
1008
+ const logEntryToMessages = (entry: AntigravityLogEntry, index: number): AntigravityConversationMessage[] => {
1009
+ const order = logEntryOrder(entry, index);
1010
+ const createdAtMs = logEntryCreatedAtMs(entry);
1011
+ const role = logEntryRole(entry);
1012
+ const phase = logEntryPhase(entry);
1013
+ const metadata = logEntryMetadata(entry);
1014
+ const messages: AntigravityConversationMessage[] = [];
1015
+ const thinking = getString(entry.thinking)?.trim();
1016
+ if (thinking && role === 'assistant') {
1017
+ messages.push({
1018
+ createdAtMs,
1019
+ metadata,
1020
+ order,
1021
+ phase: 'reasoning',
1022
+ role: 'assistant',
1023
+ text: thinking,
1024
+ });
1025
+ }
1026
+
1027
+ const content = cleanLogContent(entry);
1028
+ if (content) {
1029
+ messages.push({
1030
+ createdAtMs,
1031
+ metadata,
1032
+ order,
1033
+ phase,
1034
+ role,
1035
+ text: content,
1036
+ });
1037
+ }
1038
+
1039
+ const calls = toolCallsText(entry.tool_calls);
1040
+ if (calls) {
1041
+ messages.push({
1042
+ createdAtMs,
1043
+ metadata,
1044
+ order,
1045
+ phase: 'tool_call',
1046
+ role: 'tool',
1047
+ text: calls,
1048
+ });
1049
+ }
1050
+
1051
+ return messages;
1052
+ };
1053
+
1054
+ export const readAntigravityConversationMessages = async (
1055
+ conversation: AntigravityConversation,
1056
+ ): Promise<AntigravityConversationMessage[]> => {
1057
+ if (!conversation.transcriptPath || !conversation.transcriptSource) {
1058
+ return [];
1059
+ }
1060
+
1061
+ try {
1062
+ const entries = parseLogEntries(await Bun.file(conversation.transcriptPath).text());
1063
+ return entries.flatMap(logEntryToMessages);
1064
+ } catch {
1065
+ return [];
1066
+ }
1067
+ };
1068
+
875
1069
  const renderAntigravityTranscriptMarkdown = async (conversation: AntigravityConversation): Promise<string | null> => {
876
1070
  if (!conversation.transcriptPath || !conversation.transcriptSource) {
877
1071
  return null;
@@ -45,6 +45,7 @@ export type AntigravityConversation = {
45
45
  createdAtMs: number | null;
46
46
  indexedItemCount: number | null;
47
47
  lastUpdatedAtMs: number | null;
48
+ model: string | null;
48
49
  sourceRoot: string | null;
49
50
  summaryPath: string | null;
50
51
  title: string;
@@ -1,6 +1,6 @@
1
1
  import { listScopedThreads } from './codex-browser-db';
2
2
  import type { CodexAnalytics, DistributionItem, ModelTokenSummary } from './codex-browser-types';
3
- import type { ThreadRow } from './codex-exporter-types';
3
+ import type { ThreadRow } from './codex-thread-types';
4
4
  import { mapWithConcurrency } from './concurrency';
5
5
  import { asObject, asString, getPortablePathBasename, readJsonlObjects } from './shared';
6
6
  import { hashCacheKeyParts, hashCacheKeyPartsIterable, withCachedJson } from './ui-cache';