spiracha 1.4.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 (319) hide show
  1. package/AGENTS.md +116 -164
  2. package/README.md +92 -237
  3. package/apps/ui/AGENTS.md +36 -7
  4. package/apps/ui/README.md +79 -6
  5. package/apps/ui/components.json +21 -0
  6. package/apps/ui/package.json +65 -0
  7. package/apps/ui/{dist/client → public}/icon.svg +2 -1
  8. package/apps/ui/src/components/antigravity-conversations-table.tsx +206 -0
  9. package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
  10. package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
  11. package/apps/ui/src/components/app-shell.tsx +124 -0
  12. package/apps/ui/src/components/breadcrumbs.tsx +53 -0
  13. package/apps/ui/src/components/claude-code-sessions-table.tsx +114 -0
  14. package/apps/ui/src/components/claude-code-workspaces-table.tsx +55 -0
  15. package/apps/ui/src/components/cursor-threads-table.tsx +189 -0
  16. package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
  17. package/apps/ui/src/components/data-table.tsx +241 -0
  18. package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
  19. package/apps/ui/src/components/export-dialog.tsx +184 -0
  20. package/apps/ui/src/components/json-panel.tsx +17 -0
  21. package/apps/ui/src/components/kiro-sessions-table.tsx +112 -0
  22. package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
  23. package/apps/ui/src/components/list-search-input.tsx +18 -0
  24. package/apps/ui/src/components/loading-panel.tsx +23 -0
  25. package/apps/ui/src/components/metadata-section.tsx +31 -0
  26. package/apps/ui/src/components/metric-card.tsx +19 -0
  27. package/apps/ui/src/components/opencode-sessions-table.tsx +128 -0
  28. package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
  29. package/apps/ui/src/components/page-header.tsx +33 -0
  30. package/apps/ui/src/components/projects-loading-state.tsx +74 -0
  31. package/apps/ui/src/components/projects-table.tsx +108 -0
  32. package/apps/ui/src/components/qoder-sessions-table.tsx +112 -0
  33. package/apps/ui/src/components/qoder-workspaces-table.tsx +53 -0
  34. package/apps/ui/src/components/recent-threads-list.tsx +52 -0
  35. package/apps/ui/src/components/reload-error-panel.tsx +20 -0
  36. package/apps/ui/src/components/text-document-panel.tsx +19 -0
  37. package/apps/ui/src/components/theme-toggle.tsx +48 -0
  38. package/apps/ui/src/components/threads-table.tsx +202 -0
  39. package/apps/ui/src/components/transcript-view.tsx +552 -0
  40. package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
  41. package/apps/ui/src/components/ui/badge.tsx +45 -0
  42. package/apps/ui/src/components/ui/button.tsx +62 -0
  43. package/apps/ui/src/components/ui/checkbox.tsx +29 -0
  44. package/apps/ui/src/components/ui/dialog.tsx +137 -0
  45. package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
  46. package/apps/ui/src/components/ui/input.tsx +21 -0
  47. package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
  48. package/apps/ui/src/components/ui/select.tsx +163 -0
  49. package/apps/ui/src/components/ui/separator.tsx +28 -0
  50. package/apps/ui/src/components/ui/sheet.tsx +107 -0
  51. package/apps/ui/src/components/ui/skeleton.tsx +7 -0
  52. package/apps/ui/src/components/ui/table.tsx +76 -0
  53. package/apps/ui/src/components/ui/tabs.tsx +71 -0
  54. package/apps/ui/src/components/ui/tooltip.tsx +44 -0
  55. package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
  56. package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
  57. package/apps/ui/src/lib/antigravity-conversation-state.ts +28 -0
  58. package/apps/ui/src/lib/antigravity-queries.ts +33 -0
  59. package/apps/ui/src/lib/antigravity-server.ts +128 -0
  60. package/apps/ui/src/lib/antigravity-transcript-events.ts +335 -0
  61. package/apps/ui/src/lib/claude-code-queries.ts +26 -0
  62. package/apps/ui/src/lib/claude-code-server.ts +75 -0
  63. package/apps/ui/src/lib/claude-code-transcript-events.ts +154 -0
  64. package/apps/ui/src/lib/codex-queries.ts +66 -0
  65. package/apps/ui/src/lib/codex-server.ts +209 -0
  66. package/apps/ui/src/lib/cursor-queries.ts +37 -0
  67. package/apps/ui/src/lib/cursor-server.ts +272 -0
  68. package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
  69. package/apps/ui/src/lib/download.ts +166 -0
  70. package/apps/ui/src/lib/formatters.ts +149 -0
  71. package/apps/ui/src/lib/kiro-queries.ts +22 -0
  72. package/apps/ui/src/lib/kiro-server.ts +73 -0
  73. package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
  74. package/apps/ui/src/lib/opencode-queries.ts +37 -0
  75. package/apps/ui/src/lib/opencode-server.ts +73 -0
  76. package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
  77. package/apps/ui/src/lib/package-metadata.ts +16 -0
  78. package/apps/ui/src/lib/path-utils.ts +13 -0
  79. package/apps/ui/src/lib/qoder-queries.ts +22 -0
  80. package/apps/ui/src/lib/qoder-server.ts +79 -0
  81. package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
  82. package/apps/ui/src/lib/route-search.ts +68 -0
  83. package/apps/ui/src/lib/settings-store.tsx +59 -0
  84. package/apps/ui/src/lib/source-session-export-server.ts +56 -0
  85. package/apps/ui/src/lib/text-filter.ts +22 -0
  86. package/apps/ui/src/lib/thread-id.ts +3 -0
  87. package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
  88. package/apps/ui/src/lib/utils.ts +7 -0
  89. package/apps/ui/src/routeTree.gen.ts +730 -0
  90. package/apps/ui/src/router.tsx +20 -0
  91. package/apps/ui/src/routes/$threadId.tsx +22 -0
  92. package/apps/ui/src/routes/__root.tsx +127 -0
  93. package/apps/ui/src/routes/analytics.tsx +143 -0
  94. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +438 -0
  95. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +127 -0
  96. package/apps/ui/src/routes/antigravity.index.tsx +51 -0
  97. package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
  98. package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
  99. package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +12 -0
  100. package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
  101. package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
  102. package/apps/ui/src/routes/api.v1.sources.ts +12 -0
  103. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +342 -0
  104. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +135 -0
  105. package/apps/ui/src/routes/claude-code.index.tsx +48 -0
  106. package/apps/ui/src/routes/codex.$project.tsx +390 -0
  107. package/apps/ui/src/routes/codex.index.tsx +116 -0
  108. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
  109. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +416 -0
  110. package/apps/ui/src/routes/cursor.index.tsx +120 -0
  111. package/apps/ui/src/routes/index.tsx +124 -0
  112. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +345 -0
  113. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +136 -0
  114. package/apps/ui/src/routes/kiro.index.tsx +48 -0
  115. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +334 -0
  116. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +135 -0
  117. package/apps/ui/src/routes/opencode.index.tsx +48 -0
  118. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +341 -0
  119. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +139 -0
  120. package/apps/ui/src/routes/qoder.index.tsx +48 -0
  121. package/apps/ui/src/routes/settings.tsx +91 -0
  122. package/apps/ui/src/routes/threads.$threadId.tsx +619 -0
  123. package/apps/ui/src/styles.css +122 -0
  124. package/apps/ui/tsconfig.json +29 -0
  125. package/apps/ui/vite.config.ts +84 -0
  126. package/bin/spiracha.ts +32 -0
  127. package/package.json +50 -60
  128. package/src/client.ts +290 -0
  129. package/src/lib/antigravity-db.ts +213 -19
  130. package/src/lib/antigravity-exporter-types.ts +1 -0
  131. package/src/lib/claude-code-db.ts +655 -0
  132. package/src/lib/claude-code-exporter-types.ts +110 -0
  133. package/src/lib/claude-code-transcript-phase.ts +25 -0
  134. package/src/lib/claude-code-transcript.ts +180 -0
  135. package/src/lib/codex-analytics.ts +1 -1
  136. package/src/lib/codex-browser-db.ts +856 -61
  137. package/src/lib/codex-browser-export.ts +24 -16
  138. package/src/lib/codex-browser-types.ts +1 -1
  139. package/src/lib/codex-thread-cache.ts +17 -1
  140. package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
  141. package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +64 -28
  142. package/src/lib/concurrency.ts +25 -0
  143. package/src/lib/conversation-api.ts +792 -0
  144. package/src/lib/conversation-data/adapter-helpers.ts +110 -0
  145. package/src/lib/conversation-data/antigravity-adapter.ts +148 -0
  146. package/src/lib/conversation-data/claude-code-adapter.ts +175 -0
  147. package/src/lib/conversation-data/codex-adapter.ts +245 -0
  148. package/src/lib/conversation-data/cursor-adapter.ts +171 -0
  149. package/src/lib/conversation-data/index.ts +311 -0
  150. package/src/lib/conversation-data/kiro-adapter.ts +140 -0
  151. package/src/lib/conversation-data/message-selector.ts +37 -0
  152. package/src/lib/conversation-data/opencode-adapter.ts +180 -0
  153. package/src/lib/conversation-data/path-match.ts +79 -0
  154. package/src/lib/conversation-data/qoder-adapter.ts +230 -0
  155. package/src/lib/conversation-data/types.ts +116 -0
  156. package/src/lib/cursor-db.ts +67 -21
  157. package/src/lib/cursor-exporter-types.ts +0 -19
  158. package/src/lib/kiro-db.ts +949 -0
  159. package/src/lib/kiro-exporter-types.ts +100 -0
  160. package/src/lib/kiro-transcript-phase.ts +41 -0
  161. package/src/lib/kiro-transcript.ts +115 -0
  162. package/src/lib/opencode-db.ts +610 -0
  163. package/src/lib/opencode-exporter-types.ts +129 -0
  164. package/src/lib/opencode-think-tags.ts +126 -0
  165. package/src/lib/opencode-transcript-phase.ts +50 -0
  166. package/src/lib/opencode-transcript.ts +194 -0
  167. package/src/lib/qoder-acp-client.ts +268 -0
  168. package/src/lib/qoder-db.ts +1601 -0
  169. package/src/lib/qoder-exporter-types.ts +114 -0
  170. package/src/lib/qoder-transcript-phase.ts +41 -0
  171. package/src/lib/qoder-transcript.ts +112 -0
  172. package/src/lib/shared.ts +25 -0
  173. package/src/lib/ui-export-archive.ts +61 -0
  174. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
  175. package/apps/ui/dist/client/assets/analytics-DK2jdvmI.js +0 -1
  176. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DmD0sN8X.js +0 -7
  177. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
  178. package/apps/ui/dist/client/assets/antigravity-keychain-panel-BRHVjFAM.js +0 -1
  179. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
  180. package/apps/ui/dist/client/assets/antigravity._workspaceKey-CGV7amas.js +0 -1
  181. package/apps/ui/dist/client/assets/antigravity.index-DEEkjlZa.js +0 -1
  182. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
  183. package/apps/ui/dist/client/assets/badge-DfWvPfKF.js +0 -1
  184. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
  185. package/apps/ui/dist/client/assets/createServerFn-CgRRVpBH.js +0 -3
  186. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
  187. package/apps/ui/dist/client/assets/cursor-threads._composerId-Ti2gTm63.js +0 -1
  188. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
  189. package/apps/ui/dist/client/assets/cursor._workspaceKey-DzD_c6j9.js +0 -1
  190. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
  191. package/apps/ui/dist/client/assets/cursor.index-PkCFVcl4.js +0 -2
  192. package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
  193. package/apps/ui/dist/client/assets/delete-confirm-dialog-BHRmyLHr.js +0 -11
  194. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
  195. package/apps/ui/dist/client/assets/dist-BhZOxAPP.js +0 -1
  196. package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
  197. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
  198. package/apps/ui/dist/client/assets/dropdown-menu-Dy5cLzTy.js +0 -1
  199. package/apps/ui/dist/client/assets/es2015-BkCWttyM.js +0 -41
  200. package/apps/ui/dist/client/assets/export-dialog-C86SYHJn.js +0 -1
  201. package/apps/ui/dist/client/assets/formatters-BM9kB7ed.js +0 -1
  202. package/apps/ui/dist/client/assets/index-CPvAP-jk.js +0 -149
  203. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
  204. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
  205. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
  206. package/apps/ui/dist/client/assets/projects._project-Ckf6muWZ.js +0 -1
  207. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
  208. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
  209. package/apps/ui/dist/client/assets/projects.index-V5ibNZII.js +0 -3
  210. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
  211. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
  212. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
  213. package/apps/ui/dist/client/assets/routes-Df2tgpyd.js +0 -1
  214. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
  215. package/apps/ui/dist/client/assets/select-Bf2GwfPR.js +0 -1
  216. package/apps/ui/dist/client/assets/settings-5VQIzME0.js +0 -1
  217. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
  218. package/apps/ui/dist/client/assets/styles-BhRkXgwB.css +0 -1
  219. package/apps/ui/dist/client/assets/tabs-OO4VU5KR.js +0 -7
  220. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
  221. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
  222. package/apps/ui/dist/client/assets/threads._threadId-BgDhFj2I.js +0 -1
  223. package/apps/ui/dist/client/assets/useMutation-CC_B7uy5.js +0 -1
  224. package/apps/ui/dist/client/assets/useQuery-C5By6WKU.js +0 -1
  225. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CRW2kqj0.js +0 -232
  226. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
  227. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
  228. package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
  229. package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
  230. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BtexWY-K.js +0 -613
  231. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
  232. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-zMrR6v6R.js +0 -20
  233. package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
  234. package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
  235. package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
  236. package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
  237. package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
  238. package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
  239. package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
  240. package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
  241. package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
  242. package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
  243. package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
  244. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
  245. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
  246. package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
  247. package/apps/ui/dist/server/assets/codex-server-CNXSJuc2.js +0 -2004
  248. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
  249. package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
  250. package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
  251. package/apps/ui/dist/server/assets/cursor-db-cYZEU3WQ.js +0 -830
  252. package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
  253. package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
  254. package/apps/ui/dist/server/assets/cursor-recovery-nq-kR62j.js +0 -361
  255. package/apps/ui/dist/server/assets/cursor-server-C3q7hrp-.js +0 -213
  256. package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
  257. package/apps/ui/dist/server/assets/cursor-threads._composerId-BPJFWfJj.js +0 -18
  258. package/apps/ui/dist/server/assets/cursor-threads._composerId-BkMMTQ3v.js +0 -582
  259. package/apps/ui/dist/server/assets/cursor-transcript-2iL3KFSK.js +0 -125
  260. package/apps/ui/dist/server/assets/cursor._workspaceKey-C5PdTaKB.js +0 -28
  261. package/apps/ui/dist/server/assets/cursor._workspaceKey-Ckj7apKI.js +0 -401
  262. package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
  263. package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
  264. package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
  265. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
  266. package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
  267. package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
  268. package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
  269. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
  270. package/apps/ui/dist/server/assets/export-dialog-BItjWgkZ.js +0 -240
  271. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
  272. package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
  273. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
  274. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
  275. package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
  276. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
  277. package/apps/ui/dist/server/assets/projects._project-Bpbc3C-L.js +0 -20
  278. package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
  279. package/apps/ui/dist/server/assets/projects._project-CvZho6EQ.js +0 -395
  280. package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
  281. package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
  282. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
  283. package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
  284. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
  285. package/apps/ui/dist/server/assets/router-6m-ihwqA.js +0 -410
  286. package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
  287. package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
  288. package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
  289. package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
  290. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
  291. package/apps/ui/dist/server/assets/shared-CPRNYIql.js +0 -134
  292. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
  293. package/apps/ui/dist/server/assets/start-CI_0bSiY.js +0 -4
  294. package/apps/ui/dist/server/assets/tabs-CGA13IZM.js +0 -502
  295. package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
  296. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
  297. package/apps/ui/dist/server/assets/threads._threadId-Bs0yuqO9.js +0 -18
  298. package/apps/ui/dist/server/assets/threads._threadId-D2GYkVn6.js +0 -639
  299. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
  300. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
  301. package/apps/ui/dist/server/server.js +0 -5847
  302. package/bin/codex-chats-claude.js +0 -5
  303. package/bin/codex-chats.js +0 -5
  304. package/bin/spiracha.js +0 -5
  305. package/src/export-chats.ts +0 -120
  306. package/src/export-claude.ts +0 -36
  307. package/src/export-cursor.ts +0 -244
  308. package/src/lib/claude-exporter.ts +0 -864
  309. package/src/lib/codex-exporter-cli.ts +0 -277
  310. package/src/lib/codex-exporter-db.ts +0 -319
  311. package/src/lib/codex-exporter.ts +0 -115
  312. package/src/lib/cursor-exporter.ts +0 -266
  313. package/src/lib/interactive-cli.ts +0 -433
  314. package/src/lib/native-open.ts +0 -54
  315. package/src/mcp-server.ts +0 -137
  316. package/src/spiracha.ts +0 -116
  317. package/src/ui-cli.ts +0 -310
  318. /package/apps/ui/{dist/client → public}/manifest.json +0 -0
  319. /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;