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
@@ -0,0 +1,619 @@
1
+ import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
2
+ import { createFileRoute, Link, useNavigate } from '@tanstack/react-router';
3
+ import { Download, Trash2 } from 'lucide-react';
4
+ import { useState } from 'react';
5
+ import { Breadcrumbs } from '#/components/breadcrumbs';
6
+ import { DeleteConfirmDialog } from '#/components/delete-confirm-dialog';
7
+ import { ExportDialog } from '#/components/export-dialog';
8
+ import { JsonPanel } from '#/components/json-panel';
9
+ import { LoadingPanel } from '#/components/loading-panel';
10
+ import { MetadataSection } from '#/components/metadata-section';
11
+ import { MetricCard } from '#/components/metric-card';
12
+ import { PageHeader } from '#/components/page-header';
13
+ import { TranscriptView } from '#/components/transcript-view';
14
+ import { Badge } from '#/components/ui/badge';
15
+ import { Button } from '#/components/ui/button';
16
+ import { Checkbox } from '#/components/ui/checkbox';
17
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '#/components/ui/tabs';
18
+ import { threadSnapshotQueryOptions, threadTranscriptQueryOptions } from '#/lib/codex-queries';
19
+ import { deleteThreadFn, exportThreadFn, type getThreadSnapshotFn } from '#/lib/codex-server';
20
+ import { downloadTextFile, downloadUrlFile } from '#/lib/download';
21
+ import { formatBooleanLabel, formatBytes, formatDateTime, formatList, formatTokens } from '#/lib/formatters';
22
+ import { useSettings } from '#/lib/settings-store';
23
+
24
+ type ThreadSnapshot = Awaited<ReturnType<typeof getThreadSnapshotFn>>;
25
+
26
+ type TranscriptControlsProps = {
27
+ rawJsonDisabled?: boolean;
28
+ showCommentary: boolean;
29
+ showExtraEvents: boolean;
30
+ showRawJson: boolean;
31
+ showToolCalls: boolean;
32
+ showUserMessages: boolean;
33
+ onShowCommentaryChange: (checked: boolean) => void;
34
+ onShowExtraEventsChange: (checked: boolean) => void;
35
+ onShowRawJsonChange: (checked: boolean) => void;
36
+ onShowToolCallsChange: (checked: boolean) => void;
37
+ onShowUserMessagesChange: (checked: boolean) => void;
38
+ };
39
+
40
+ type ThreadMetadataProps = {
41
+ snapshot: ThreadSnapshot;
42
+ };
43
+
44
+ const buildThreadItems = (snapshot: ThreadSnapshot) => {
45
+ return [
46
+ { label: 'Thread ID', value: <span data-mono="true">{snapshot.thread.id}</span> },
47
+ { label: 'Project', value: snapshot.project },
48
+ { label: 'CWD', value: <span data-mono="true">{snapshot.thread.cwd}</span> },
49
+ {
50
+ label: 'Created',
51
+ value: (
52
+ <span suppressHydrationWarning>
53
+ {formatDateTime(snapshot.thread.created_at_ms ?? snapshot.thread.created_at * 1000)}
54
+ </span>
55
+ ),
56
+ },
57
+ {
58
+ label: 'Updated',
59
+ value: (
60
+ <span suppressHydrationWarning>
61
+ {formatDateTime(snapshot.thread.updated_at_ms ?? snapshot.thread.updated_at * 1000)}
62
+ </span>
63
+ ),
64
+ },
65
+ {
66
+ label: 'Session started',
67
+ value: (
68
+ <span suppressHydrationWarning>
69
+ {formatDateTime(snapshot.transcript?.sessionMeta.timestamp ?? null)}
70
+ </span>
71
+ ),
72
+ },
73
+ { label: 'Rollout size', value: formatBytes(snapshot.rollout.fileSizeBytes) },
74
+ { label: 'Archived', value: formatBooleanLabel(Boolean(snapshot.thread.archived)) },
75
+ {
76
+ label: 'Archived at',
77
+ value: (
78
+ <span suppressHydrationWarning>
79
+ {formatDateTime(snapshot.thread.archived_at ? snapshot.thread.archived_at * 1000 : null)}
80
+ </span>
81
+ ),
82
+ },
83
+ ];
84
+ };
85
+
86
+ const buildRuntimeItems = (snapshot: ThreadSnapshot) => {
87
+ return [
88
+ { label: 'Source', value: snapshot.thread.source },
89
+ { label: 'Originator', value: snapshot.transcript?.sessionMeta.originator ?? 'n/a' },
90
+ { label: 'Model provider', value: snapshot.thread.model_provider },
91
+ { label: 'Reasoning effort', value: snapshot.thread.reasoning_effort ?? 'n/a' },
92
+ { label: 'CLI version', value: snapshot.thread.cli_version },
93
+ { label: 'Approval mode', value: snapshot.thread.approval_mode },
94
+ { label: 'Memory mode', value: snapshot.thread.memory_mode },
95
+ { label: 'Has user event', value: formatBooleanLabel(Boolean(snapshot.thread.has_user_event)) },
96
+ ];
97
+ };
98
+
99
+ const buildGitItems = (snapshot: ThreadSnapshot) => {
100
+ return [
101
+ { label: 'Git branch', value: snapshot.thread.git_branch ?? 'n/a' },
102
+ { label: 'Git SHA', value: snapshot.thread.git_sha ?? 'n/a' },
103
+ { label: 'Git remote', value: snapshot.thread.git_origin_url ?? 'n/a' },
104
+ { label: 'Agent nickname', value: snapshot.thread.agent_nickname ?? 'n/a' },
105
+ { label: 'Agent role', value: snapshot.thread.agent_role ?? 'n/a' },
106
+ { label: 'Agent path', value: snapshot.thread.agent_path ?? 'n/a' },
107
+ ];
108
+ };
109
+
110
+ const buildRelationItems = (snapshot: ThreadSnapshot) => {
111
+ const parentThreadValue = snapshot.relations.parentThreadId ? (
112
+ <Link
113
+ className="text-[var(--accent)]"
114
+ params={{ threadId: snapshot.relations.parentThreadId }}
115
+ to="/threads/$threadId"
116
+ >
117
+ {snapshot.relations.parentThreadId}
118
+ </Link>
119
+ ) : (
120
+ 'n/a'
121
+ );
122
+
123
+ const childThreadValue =
124
+ snapshot.relations.childEdges.length > 0 ? (
125
+ <div className="flex flex-wrap gap-2">
126
+ {snapshot.relations.childEdges.map((edge) => (
127
+ <Link
128
+ key={edge.child_thread_id}
129
+ className="rounded-full border border-[var(--border)] px-3 py-1 text-[var(--accent)] text-xs"
130
+ params={{ threadId: edge.child_thread_id }}
131
+ to="/threads/$threadId"
132
+ >
133
+ {edge.child_thread_id}
134
+ </Link>
135
+ ))}
136
+ </div>
137
+ ) : (
138
+ 'n/a'
139
+ );
140
+
141
+ return [
142
+ { label: 'Parent thread', value: parentThreadValue },
143
+ { label: 'Child threads', value: childThreadValue },
144
+ { label: 'First user message', value: snapshot.thread.first_user_message || 'n/a' },
145
+ { label: 'Preview', value: snapshot.thread.preview || 'n/a' },
146
+ ];
147
+ };
148
+
149
+ const buildTranscriptStatsItems = (snapshot: ThreadSnapshot) => {
150
+ if (!snapshot.transcript) {
151
+ if (snapshot.transcriptState === 'missing') {
152
+ return [
153
+ { label: 'Transcript load', value: 'Transcript file missing from disk' },
154
+ { label: 'Rollout path', value: snapshot.thread.rollout_path },
155
+ {
156
+ label: 'Preview mode',
157
+ value: 'Export and transcript browsing are unavailable until the file exists again.',
158
+ },
159
+ ];
160
+ }
161
+
162
+ return [
163
+ { label: 'Transcript load', value: 'Deferred for oversized rollout' },
164
+ { label: 'Rollout size', value: formatBytes(snapshot.rollout.fileSizeBytes) },
165
+ { label: 'Preview mode', value: 'Load the transcript manually to inspect it.' },
166
+ ];
167
+ }
168
+
169
+ return [
170
+ {
171
+ label: 'Event kinds',
172
+ value: formatList([...new Set(snapshot.transcript.events.map((event) => event.kind))]),
173
+ },
174
+ { label: 'Stats scope', value: snapshot.transcript.statsArePartial ? 'Preview only' : 'Full transcript' },
175
+ { label: 'Tool calls', value: String(snapshot.transcript.stats.toolCallCount) },
176
+ { label: 'Exec calls', value: String(snapshot.transcript.stats.execCommandCount) },
177
+ { label: 'Web search events', value: String(snapshot.transcript.stats.webSearchEventCount) },
178
+ { label: 'Assistant messages', value: String(snapshot.transcript.stats.assistantMessageCount) },
179
+ { label: 'Commentary updates', value: String(snapshot.transcript.stats.commentaryCount) },
180
+ ];
181
+ };
182
+
183
+ function TranscriptControls({
184
+ rawJsonDisabled = false,
185
+ showCommentary,
186
+ showExtraEvents,
187
+ showRawJson,
188
+ showToolCalls,
189
+ showUserMessages,
190
+ onShowCommentaryChange,
191
+ onShowExtraEventsChange,
192
+ onShowRawJsonChange,
193
+ onShowToolCallsChange,
194
+ onShowUserMessagesChange,
195
+ }: TranscriptControlsProps) {
196
+ return (
197
+ <div className="flex flex-wrap gap-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-3 shadow-[var(--panel-shadow)]">
198
+ <div className="flex items-center gap-2 text-sm">
199
+ <Checkbox
200
+ checked={showToolCalls}
201
+ id="transcript-show-tool-calls"
202
+ onCheckedChange={(checked) => onShowToolCallsChange(checked === true)}
203
+ />
204
+ <label htmlFor="transcript-show-tool-calls">Show tool calls</label>
205
+ </div>
206
+ <div className="flex items-center gap-2 text-sm">
207
+ <Checkbox
208
+ checked={showCommentary}
209
+ id="transcript-show-commentary"
210
+ onCheckedChange={(checked) => onShowCommentaryChange(checked === true)}
211
+ />
212
+ <label htmlFor="transcript-show-commentary">Show commentary</label>
213
+ </div>
214
+ <div className="flex items-center gap-2 text-sm">
215
+ <Checkbox
216
+ checked={showExtraEvents}
217
+ id="transcript-show-extra-events"
218
+ onCheckedChange={(checked) => onShowExtraEventsChange(checked === true)}
219
+ />
220
+ <label htmlFor="transcript-show-extra-events">Show extra events</label>
221
+ </div>
222
+ <div className="flex items-center gap-2 text-sm">
223
+ <Checkbox
224
+ checked={showRawJson}
225
+ disabled={rawJsonDisabled}
226
+ id="transcript-show-raw-json"
227
+ onCheckedChange={(checked) => onShowRawJsonChange(checked === true)}
228
+ />
229
+ <label htmlFor="transcript-show-raw-json">Raw JSON</label>
230
+ </div>
231
+ <div className="flex items-center gap-2 text-sm">
232
+ <Checkbox
233
+ checked={showUserMessages}
234
+ id="transcript-show-user-messages"
235
+ onCheckedChange={(checked) => onShowUserMessagesChange(checked === true)}
236
+ />
237
+ <label htmlFor="transcript-show-user-messages">User</label>
238
+ </div>
239
+ </div>
240
+ );
241
+ }
242
+
243
+ function ThreadMetadataPanels({ snapshot }: ThreadMetadataProps) {
244
+ return (
245
+ <div className="grid gap-4 xl:grid-cols-[1.1fr_0.9fr]">
246
+ <div className="space-y-4">
247
+ <MetadataSection items={buildThreadItems(snapshot)} title="Thread" />
248
+ <MetadataSection items={buildRuntimeItems(snapshot)} title="Runtime" />
249
+ <MetadataSection items={buildGitItems(snapshot)} title="Git and agent" />
250
+ </div>
251
+
252
+ <div className="space-y-4">
253
+ <MetadataSection items={buildRelationItems(snapshot)} title="Relations and summary" />
254
+
255
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
256
+ <h3 className="font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]">
257
+ Available tools
258
+ </h3>
259
+ <div className="mt-4 space-y-3">
260
+ {snapshot.availableTools.map((tool) => (
261
+ <div
262
+ key={`${tool.name}-${tool.namespace ?? 'global'}`}
263
+ className="rounded-xl border border-[var(--border)] bg-[var(--panel-secondary)] p-3.5"
264
+ >
265
+ <div className="flex flex-wrap items-center gap-2">
266
+ <p className="font-medium font-mono text-sm">{tool.name}</p>
267
+ {tool.namespace ? <Badge variant="outline">{tool.namespace}</Badge> : null}
268
+ </div>
269
+ <p className="mt-1.5 text-[var(--muted-foreground)] text-sm">
270
+ {tool.description || 'No description.'}
271
+ </p>
272
+ </div>
273
+ ))}
274
+ </div>
275
+ </section>
276
+
277
+ <MetadataSection items={buildTranscriptStatsItems(snapshot)} title="Transcript stats" />
278
+ </div>
279
+ </div>
280
+ );
281
+ }
282
+
283
+ function ThreadRawPanels({ snapshot }: ThreadMetadataProps) {
284
+ if (!snapshot.transcript) {
285
+ return (
286
+ <div className="rounded-xl border border-[var(--border)] bg-[var(--panel)] px-5 py-4 text-sm">
287
+ {snapshot.transcriptState === 'missing'
288
+ ? 'The rollout JSONL file is missing from disk, so raw transcript payloads are unavailable.'
289
+ : 'Raw transcript payloads are deferred for oversized rollouts. Use Export if you only need the full thread contents, or load the transcript manually from the Transcript tab.'}
290
+ </div>
291
+ );
292
+ }
293
+
294
+ return (
295
+ <div className="space-y-4">
296
+ <JsonPanel title="Session meta" value={snapshot.transcript.sessionMeta} />
297
+ <JsonPanel title="Turn contexts" value={snapshot.transcript.turnContexts} />
298
+ <JsonPanel title="Sandbox policy" value={snapshot.thread.sandbox_policy} />
299
+ </div>
300
+ );
301
+ }
302
+
303
+ function DeferredTranscriptNotice({
304
+ fileSizeBytes,
305
+ missing,
306
+ pending,
307
+ onLoad,
308
+ }: {
309
+ fileSizeBytes: number | null;
310
+ missing?: boolean;
311
+ pending: boolean;
312
+ onLoad: () => void;
313
+ }) {
314
+ return (
315
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
316
+ <h3 className="font-semibold text-base">
317
+ {missing ? 'Transcript file missing' : 'This is a very big thread'}
318
+ </h3>
319
+ <p className="mt-2 text-[var(--muted-foreground)] text-sm leading-6">
320
+ {missing
321
+ ? 'The rollout JSONL referenced by this thread is no longer present on disk. Export may still work if the file is restored, but transcript browsing is unavailable right now.'
322
+ : `Spiracha skipped loading the transcript automatically because the rollout file is ${formatBytes(fileSizeBytes)}. Export still works immediately. If you need to inspect the thread here, load a bounded preview manually.`}
323
+ </p>
324
+ {missing ? null : (
325
+ <div className="mt-4">
326
+ <Button disabled={pending} variant="outline" onClick={onLoad}>
327
+ {pending ? 'Loading preview...' : 'Load transcript preview'}
328
+ </Button>
329
+ </div>
330
+ )}
331
+ </section>
332
+ );
333
+ }
334
+
335
+ function ThreadErrorComponent({ error }: { error: Error }) {
336
+ const isSqlite = error.message.includes('unable to open database') || error.message.includes('database is locked');
337
+ return (
338
+ <div className="rounded-xl border border-[var(--border)] bg-[var(--panel)] px-6 py-10 text-center">
339
+ <p className="font-medium text-[var(--destructive)] text-sm">
340
+ {isSqlite ? 'Database unavailable' : 'Failed to load thread'}
341
+ </p>
342
+ <p className="mt-2 text-[var(--muted-foreground)] text-sm">
343
+ {isSqlite ? 'Codex may have an exclusive lock on the database. Reload to retry.' : error.message}
344
+ </p>
345
+ <button
346
+ className="mt-4 text-[var(--accent)] text-sm underline-offset-2 hover:underline"
347
+ type="button"
348
+ onClick={() => window.location.reload()}
349
+ >
350
+ Reload
351
+ </button>
352
+ </div>
353
+ );
354
+ }
355
+
356
+ const getThreadExportErrorMessage = (transcriptMissing: boolean, error: unknown): string | null => {
357
+ if (transcriptMissing) {
358
+ return 'The rollout JSONL file is missing from disk, so this thread cannot be exported right now.';
359
+ }
360
+
361
+ return error instanceof Error ? error.message : null;
362
+ };
363
+
364
+ export const Route = createFileRoute('/threads/$threadId')({
365
+ component: ThreadDetailPage,
366
+ errorComponent: ThreadErrorComponent,
367
+ loader: ({ context, params }) => context.queryClient.ensureQueryData(threadSnapshotQueryOptions(params.threadId)),
368
+ pendingComponent: () => (
369
+ <LoadingPanel
370
+ description="Loading the transcript, metadata, and parsed event stream for this thread."
371
+ title="Loading thread"
372
+ />
373
+ ),
374
+ });
375
+
376
+ function ThreadDetailPage() {
377
+ const navigate = useNavigate();
378
+ const queryClient = useQueryClient();
379
+ const params = Route.useParams();
380
+ const snapshot = useSuspenseQuery(threadSnapshotQueryOptions(params.threadId)).data;
381
+ const { settings } = useSettings();
382
+ const transcriptMissing = snapshot.transcriptState === 'missing';
383
+ const [shouldLoadTranscript, setShouldLoadTranscript] = useState(
384
+ !snapshot.rollout.shouldDeferTranscriptLoad && !transcriptMissing,
385
+ );
386
+ const [showToolCalls, setShowToolCalls] = useState(false);
387
+ const [showCommentary, setShowCommentary] = useState(false);
388
+ const [showExtraEvents, setShowExtraEvents] = useState(false);
389
+ const [showRawJson, setShowRawJson] = useState(false);
390
+ const [showUserMessages, setShowUserMessages] = useState(true);
391
+ const [exportOpen, setExportOpen] = useState(false);
392
+ const [deleteOpen, setDeleteOpen] = useState(false);
393
+ const transcriptQuery = useQuery({
394
+ ...threadTranscriptQueryOptions(params.threadId),
395
+ enabled: shouldLoadTranscript && snapshot.transcript === null && !transcriptMissing,
396
+ });
397
+ const transcript = snapshot.transcript ?? transcriptQuery.data ?? null;
398
+ const viewSnapshot = { ...snapshot, transcript };
399
+
400
+ const exportThreadMutation = useMutation({
401
+ mutationFn: async (options: {
402
+ includeCommentary: boolean;
403
+ includeTools: boolean;
404
+ includeMetadata: boolean;
405
+ outputFormat: 'md' | 'txt';
406
+ zipArchive: boolean;
407
+ }) => {
408
+ console.info('[spiracha:export-ui] request', {
409
+ outputFormat: options.outputFormat,
410
+ project: snapshot.project,
411
+ selectedThreadCount: 1,
412
+ selectedThreadIds: [snapshot.thread.id],
413
+ zipArchive: options.zipArchive,
414
+ });
415
+
416
+ const download = await exportThreadFn({
417
+ data: {
418
+ ...options,
419
+ ...settings,
420
+ threadId: snapshot.thread.id,
421
+ },
422
+ });
423
+
424
+ console.info('[spiracha:export-ui] response', {
425
+ downloadUrl: download.mode === 'download_url' ? download.downloadUrl : null,
426
+ fileName: download.fileName,
427
+ mode: download.mode,
428
+ project: snapshot.project,
429
+ selectedThreadCount: 1,
430
+ });
431
+
432
+ if (download.mode === 'download') {
433
+ downloadTextFile(download.fileName, download.content, download.mimeType);
434
+ return;
435
+ }
436
+
437
+ await downloadUrlFile(download.fileName, download.downloadUrl);
438
+ },
439
+ onError: (error) => {
440
+ console.error('[spiracha:export-ui] failed', {
441
+ error: error instanceof Error ? error.message : String(error),
442
+ project: snapshot.project,
443
+ selectedThreadCount: 1,
444
+ selectedThreadIds: [snapshot.thread.id],
445
+ });
446
+ },
447
+ onSuccess: async () => {
448
+ setExportOpen(false);
449
+ },
450
+ });
451
+
452
+ const deleteThreadMutation = useMutation({
453
+ mutationFn: (input: { deleteSessionFiles: boolean }) =>
454
+ deleteThreadFn({
455
+ data: {
456
+ deleteSessionFiles: input.deleteSessionFiles,
457
+ threadId: snapshot.thread.id,
458
+ },
459
+ }),
460
+ onSuccess: async () => {
461
+ await Promise.all([
462
+ queryClient.invalidateQueries({ queryKey: ['analytics'] }),
463
+ queryClient.invalidateQueries({ queryKey: ['dashboard'] }),
464
+ queryClient.invalidateQueries({ queryKey: ['project-threads', snapshot.project] }),
465
+ queryClient.invalidateQueries({ queryKey: ['projects'] }),
466
+ ]);
467
+
468
+ navigate({
469
+ params: {
470
+ project: snapshot.project,
471
+ },
472
+ to: '/codex/$project',
473
+ });
474
+ },
475
+ });
476
+
477
+ return (
478
+ <div className="space-y-5">
479
+ <PageHeader
480
+ actions={
481
+ <>
482
+ <Button className="rounded-full" variant="outline" onClick={() => setExportOpen(true)}>
483
+ <Download className="mr-2 size-4" />
484
+ Export
485
+ </Button>
486
+ <Button
487
+ className="rounded-full border-[var(--destructive)]/20 text-[var(--destructive)]"
488
+ variant="outline"
489
+ onClick={() => setDeleteOpen(true)}
490
+ >
491
+ <Trash2 className="mr-2 size-4" />
492
+ Delete
493
+ </Button>
494
+ </>
495
+ }
496
+ breadcrumb={
497
+ <Breadcrumbs
498
+ items={[
499
+ { label: 'Codex', to: '/codex' },
500
+ {
501
+ label: snapshot.project,
502
+ params: { project: snapshot.project },
503
+ to: '/codex/$project',
504
+ },
505
+ { label: snapshot.thread.title },
506
+ ]}
507
+ />
508
+ }
509
+ eyebrow={snapshot.project}
510
+ subtitle={snapshot.thread.preview}
511
+ title={snapshot.thread.title}
512
+ />
513
+
514
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
515
+ <MetricCard label="Model" value={snapshot.thread.model ?? 'unknown'} />
516
+ <MetricCard label="Tokens" value={formatTokens(snapshot.thread.tokens_used)} />
517
+ <MetricCard
518
+ label="Updated"
519
+ value={
520
+ <span suppressHydrationWarning>
521
+ {formatDateTime(snapshot.thread.updated_at_ms ?? snapshot.thread.updated_at * 1000)}
522
+ </span>
523
+ }
524
+ />
525
+ <MetricCard
526
+ label="Thread source"
527
+ value={snapshot.thread.thread_source ?? transcript?.sessionMeta.threadSource ?? 'n/a'}
528
+ />
529
+ </div>
530
+
531
+ <Tabs className="space-y-4" defaultValue="transcript">
532
+ <TabsList className="grid w-fit min-w-[24rem] grid-cols-3 rounded-full border border-[var(--border)] bg-[var(--panel)] p-1">
533
+ <TabsTrigger className="rounded-full px-5 text-sm" value="transcript">
534
+ Transcript
535
+ </TabsTrigger>
536
+ <TabsTrigger className="rounded-full px-5 text-sm" value="metadata">
537
+ Metadata
538
+ </TabsTrigger>
539
+ <TabsTrigger className="rounded-full px-5 text-sm" value="raw">
540
+ Raw
541
+ </TabsTrigger>
542
+ </TabsList>
543
+
544
+ <TabsContent className="space-y-3" value="transcript">
545
+ <TranscriptControls
546
+ rawJsonDisabled={!transcript?.rawIncluded}
547
+ showCommentary={showCommentary}
548
+ showExtraEvents={showExtraEvents}
549
+ showRawJson={showRawJson}
550
+ showToolCalls={showToolCalls}
551
+ showUserMessages={showUserMessages}
552
+ onShowCommentaryChange={setShowCommentary}
553
+ onShowExtraEventsChange={setShowExtraEvents}
554
+ onShowRawJsonChange={setShowRawJson}
555
+ onShowToolCallsChange={setShowToolCalls}
556
+ onShowUserMessagesChange={setShowUserMessages}
557
+ />
558
+ {transcript ? (
559
+ <TranscriptView
560
+ assistantModel={snapshot.thread.model}
561
+ events={transcript.events}
562
+ projectPath={snapshot.thread.cwd}
563
+ showCommentary={showCommentary}
564
+ showExtraEvents={showExtraEvents}
565
+ showRawJson={showRawJson && transcript.rawIncluded}
566
+ showToolCalls={showToolCalls}
567
+ showUserMessages={showUserMessages}
568
+ />
569
+ ) : (
570
+ <DeferredTranscriptNotice
571
+ fileSizeBytes={snapshot.rollout.fileSizeBytes}
572
+ missing={snapshot.transcriptState === 'missing'}
573
+ pending={transcriptQuery.isFetching}
574
+ onLoad={() => setShouldLoadTranscript(true)}
575
+ />
576
+ )}
577
+ {transcriptQuery.isError ? (
578
+ <p className="text-[var(--destructive)] text-sm">
579
+ Failed to load transcript preview:{' '}
580
+ {transcriptQuery.error instanceof Error ? transcriptQuery.error.message : 'Unknown error'}
581
+ </p>
582
+ ) : null}
583
+ </TabsContent>
584
+
585
+ <TabsContent value="metadata">
586
+ <ThreadMetadataPanels snapshot={viewSnapshot} />
587
+ </TabsContent>
588
+
589
+ <TabsContent value="raw">
590
+ <ThreadRawPanels snapshot={viewSnapshot} />
591
+ </TabsContent>
592
+ </Tabs>
593
+
594
+ <DeleteConfirmDialog
595
+ confirmLabel={deleteThreadMutation.isPending ? 'Deleting...' : 'Delete thread'}
596
+ defaultDeleteSessionFiles
597
+ description="Delete this thread from the Codex database. Enable Delete Session files if you also want to remove the rollout JSONL from disk."
598
+ open={deleteOpen}
599
+ showDeleteSessionFilesOption
600
+ title="Delete this thread from Codex DB?"
601
+ onConfirm={({ deleteSessionFiles }) => deleteThreadMutation.mutate({ deleteSessionFiles })}
602
+ onOpenChange={setDeleteOpen}
603
+ />
604
+
605
+ <ExportDialog
606
+ disabled={transcriptMissing}
607
+ errorMessage={getThreadExportErrorMessage(transcriptMissing, exportThreadMutation.error)}
608
+ open={exportOpen}
609
+ pending={exportThreadMutation.isPending}
610
+ onExport={(options) => {
611
+ if (!transcriptMissing) {
612
+ exportThreadMutation.mutate(options);
613
+ }
614
+ }}
615
+ onOpenChange={setExportOpen}
616
+ />
617
+ </div>
618
+ );
619
+ }