@seqyuan/annodex 0.1.10 → 0.1.12

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 (519) hide show
  1. package/app/api/agent/[id]/events/route.ts +94 -0
  2. package/app/api/agent/[id]/route.ts +83 -0
  3. package/app/api/agent/new/route.ts +53 -0
  4. package/app/api/auth/all-providers/route.ts +21 -0
  5. package/app/api/auth/api-key/[provider]/route.ts +7 -0
  6. package/app/api/auth/login/[provider]/route.ts +7 -0
  7. package/app/api/auth/login/route.ts +22 -0
  8. package/app/api/auth/logout/[provider]/route.ts +7 -0
  9. package/app/api/auth/providers/route.ts +15 -0
  10. package/app/api/auth/status/route.ts +6 -0
  11. package/app/api/default-cwd/route.ts +22 -0
  12. package/app/api/files/[...path]/route.ts +621 -0
  13. package/app/api/harness/route.ts +47 -0
  14. package/app/api/home/route.ts +6 -0
  15. package/app/api/internal/runtime/route.ts +26 -0
  16. package/app/api/models/route.ts +67 -0
  17. package/app/api/models-config/discover/route.ts +42 -0
  18. package/app/api/models-config/route.ts +152 -0
  19. package/app/api/models-config/test/route.ts +154 -0
  20. package/app/api/projects/browse/route.ts +51 -0
  21. package/app/api/projects/route.ts +83 -0
  22. package/app/api/reports/[id]/route.ts +108 -0
  23. package/app/api/search/route.ts +122 -0
  24. package/app/api/sessions/[id]/context/route.ts +23 -0
  25. package/app/api/sessions/[id]/route.ts +124 -0
  26. package/app/api/sessions/new/route.ts +5 -0
  27. package/app/api/sessions/route.ts +16 -0
  28. package/app/api/settings/route.ts +51 -0
  29. package/app/api/skills/install/route.ts +249 -0
  30. package/app/api/skills/route.ts +161 -0
  31. package/app/api/skills/search/route.ts +121 -0
  32. package/app/api/soul/route.ts +47 -0
  33. package/app/api/version/route.ts +55 -0
  34. package/app/globals.css +736 -0
  35. package/app/layout.tsx +40 -0
  36. package/app/login/page.tsx +133 -0
  37. package/app/page.tsx +10 -0
  38. package/components/AppShell.tsx +1058 -0
  39. package/components/ChatInput.tsx +1103 -0
  40. package/components/ChatMinimap.tsx +381 -0
  41. package/components/ChatWindow.tsx +576 -0
  42. package/components/CodeMirrorEditor.tsx +137 -0
  43. package/components/ConversationSearch.tsx +369 -0
  44. package/components/DataTableViewer.tsx +248 -0
  45. package/components/FileExplorer.tsx +758 -0
  46. package/components/FileIcons.tsx +241 -0
  47. package/components/FileViewer.tsx +1273 -0
  48. package/components/GlobalFileEditor.tsx +98 -0
  49. package/components/MarkdownRenderer.tsx +331 -0
  50. package/components/MermaidDiagram.tsx +80 -0
  51. package/components/MessageView.tsx +1141 -0
  52. package/components/ModelsConfig.tsx +1991 -0
  53. package/components/ProjectContext.tsx +252 -0
  54. package/components/ProjectFolderPicker.tsx +202 -0
  55. package/components/ProjectsConfig.tsx +288 -0
  56. package/components/ProviderIcons.tsx +91 -0
  57. package/components/ReportPanel.tsx +237 -0
  58. package/components/ResizeHandle.tsx +105 -0
  59. package/components/SessionSidebar.tsx +1464 -0
  60. package/components/SettingsDialog.tsx +287 -0
  61. package/components/SkillsConfig.tsx +1093 -0
  62. package/components/SubagentPanel.tsx +191 -0
  63. package/components/TabBar.tsx +115 -0
  64. package/components/ToolPanel.tsx +131 -0
  65. package/components/WidgetRenderer.tsx +505 -0
  66. package/components/viewers/DocumentToolbar.tsx +78 -0
  67. package/components/viewers/DocxViewer.tsx +97 -0
  68. package/components/viewers/PdfViewer.tsx +206 -0
  69. package/components/viewers/PptxViewer.tsx +240 -0
  70. package/components/viewers/XlsxViewer.tsx +143 -0
  71. package/hooks/useAgentSession.ts +710 -0
  72. package/hooks/useAudio.ts +50 -0
  73. package/hooks/useDragDrop.ts +52 -0
  74. package/hooks/useResizable.ts +60 -0
  75. package/hooks/useTheme.ts +85 -0
  76. package/lib/agent-client.ts +39 -0
  77. package/lib/annodex-config.ts +556 -0
  78. package/lib/auth-token.ts +74 -0
  79. package/lib/auth.ts +90 -0
  80. package/lib/brand.ts +5 -0
  81. package/lib/code-theme.ts +32 -0
  82. package/lib/codex-compat-proxy.ts +1603 -0
  83. package/lib/codex-home.ts +6 -0
  84. package/lib/codex-server.ts +796 -0
  85. package/lib/codex-session.ts +590 -0
  86. package/lib/codex-usage.ts +213 -0
  87. package/lib/file-paths.ts +34 -0
  88. package/lib/model-discovery.ts +379 -0
  89. package/lib/normalize.ts +30 -0
  90. package/lib/npx.ts +87 -0
  91. package/lib/pi-types.ts +49 -0
  92. package/lib/projects.ts +269 -0
  93. package/lib/provider-api.ts +88 -0
  94. package/lib/report-prompt.ts +61 -0
  95. package/lib/report-store.ts +597 -0
  96. package/lib/report-update-parser.ts +66 -0
  97. package/lib/rpc-manager.ts +668 -0
  98. package/lib/runtime-state.ts +117 -0
  99. package/lib/session-reader.ts +903 -0
  100. package/lib/session-runtime.ts +105 -0
  101. package/lib/subagent-progress.ts +279 -0
  102. package/lib/types.ts +241 -0
  103. package/lib/widget-export.ts +318 -0
  104. package/lib/widget-guidelines.ts +288 -0
  105. package/lib/widget-prompt.ts +76 -0
  106. package/lib/widget-utils.ts +523 -0
  107. package/package.json +23 -18
  108. package/postcss.config.mjs +8 -0
  109. package/proxy.ts +64 -0
  110. package/scripts/postinstall.cjs +25 -0
  111. package/tsconfig.json +41 -0
  112. package/.next/BUILD_ID +0 -1
  113. package/.next/app-path-routes-manifest.json +0 -39
  114. package/.next/build-manifest.json +0 -20
  115. package/.next/diagnostics/build-diagnostics.json +0 -6
  116. package/.next/diagnostics/framework.json +0 -1
  117. package/.next/export-marker.json +0 -6
  118. package/.next/images-manifest.json +0 -68
  119. package/.next/next-minimal-server.js.nft.json +0 -1
  120. package/.next/next-server.js.nft.json +0 -1
  121. package/.next/package.json +0 -1
  122. package/.next/prerender-manifest.json +0 -109
  123. package/.next/react-loadable-manifest.json +0 -2320
  124. package/.next/required-server-files.js +0 -343
  125. package/.next/required-server-files.json +0 -343
  126. package/.next/routes-manifest.json +0 -286
  127. package/.next/server/app/_global-error/page.js +0 -32
  128. package/.next/server/app/_global-error/page.js.nft.json +0 -1
  129. package/.next/server/app/_global-error/page_client-reference-manifest.js +0 -1
  130. package/.next/server/app/_global-error.html +0 -1
  131. package/.next/server/app/_global-error.meta +0 -16
  132. package/.next/server/app/_global-error.rsc +0 -14
  133. package/.next/server/app/_global-error.segments/_full.segment.rsc +0 -14
  134. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +0 -5
  135. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +0 -5
  136. package/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
  137. package/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
  138. package/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  139. package/.next/server/app/_not-found/page.js +0 -2
  140. package/.next/server/app/_not-found/page.js.nft.json +0 -1
  141. package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  142. package/.next/server/app/_not-found.html +0 -1
  143. package/.next/server/app/_not-found.meta +0 -16
  144. package/.next/server/app/_not-found.rsc +0 -18
  145. package/.next/server/app/_not-found.segments/_full.segment.rsc +0 -18
  146. package/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  147. package/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
  148. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  149. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
  150. package/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -4
  151. package/.next/server/app/api/agent/[id]/events/route.js +0 -3
  152. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +0 -1
  153. package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +0 -1
  154. package/.next/server/app/api/agent/[id]/route.js +0 -1
  155. package/.next/server/app/api/agent/[id]/route.js.nft.json +0 -1
  156. package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +0 -1
  157. package/.next/server/app/api/agent/new/route.js +0 -1
  158. package/.next/server/app/api/agent/new/route.js.nft.json +0 -1
  159. package/.next/server/app/api/agent/new/route_client-reference-manifest.js +0 -1
  160. package/.next/server/app/api/auth/all-providers/route.js +0 -1
  161. package/.next/server/app/api/auth/all-providers/route.js.nft.json +0 -1
  162. package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +0 -1
  163. package/.next/server/app/api/auth/api-key/[provider]/route.js +0 -1
  164. package/.next/server/app/api/auth/api-key/[provider]/route.js.nft.json +0 -1
  165. package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +0 -1
  166. package/.next/server/app/api/auth/login/[provider]/route.js +0 -1
  167. package/.next/server/app/api/auth/login/[provider]/route.js.nft.json +0 -1
  168. package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +0 -1
  169. package/.next/server/app/api/auth/login/route.js +0 -1
  170. package/.next/server/app/api/auth/login/route.js.nft.json +0 -1
  171. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +0 -1
  172. package/.next/server/app/api/auth/logout/[provider]/route.js +0 -1
  173. package/.next/server/app/api/auth/logout/[provider]/route.js.nft.json +0 -1
  174. package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +0 -1
  175. package/.next/server/app/api/auth/providers/route.js +0 -1
  176. package/.next/server/app/api/auth/providers/route.js.nft.json +0 -1
  177. package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +0 -1
  178. package/.next/server/app/api/auth/status/route.js +0 -1
  179. package/.next/server/app/api/auth/status/route.js.nft.json +0 -1
  180. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +0 -1
  181. package/.next/server/app/api/default-cwd/route.js +0 -1
  182. package/.next/server/app/api/default-cwd/route.js.nft.json +0 -1
  183. package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +0 -1
  184. package/.next/server/app/api/files/[...path]/route.js +0 -4
  185. package/.next/server/app/api/files/[...path]/route.js.nft.json +0 -1
  186. package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +0 -1
  187. package/.next/server/app/api/harness/route.js +0 -1
  188. package/.next/server/app/api/harness/route.js.nft.json +0 -1
  189. package/.next/server/app/api/harness/route_client-reference-manifest.js +0 -1
  190. package/.next/server/app/api/home/route.js +0 -1
  191. package/.next/server/app/api/home/route.js.nft.json +0 -1
  192. package/.next/server/app/api/home/route_client-reference-manifest.js +0 -1
  193. package/.next/server/app/api/internal/runtime/route.js +0 -1
  194. package/.next/server/app/api/internal/runtime/route.js.nft.json +0 -1
  195. package/.next/server/app/api/internal/runtime/route_client-reference-manifest.js +0 -1
  196. package/.next/server/app/api/models/route.js +0 -1
  197. package/.next/server/app/api/models/route.js.nft.json +0 -1
  198. package/.next/server/app/api/models/route_client-reference-manifest.js +0 -1
  199. package/.next/server/app/api/models-config/discover/route.js +0 -1
  200. package/.next/server/app/api/models-config/discover/route.js.nft.json +0 -1
  201. package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +0 -1
  202. package/.next/server/app/api/models-config/route.js +0 -1
  203. package/.next/server/app/api/models-config/route.js.nft.json +0 -1
  204. package/.next/server/app/api/models-config/route_client-reference-manifest.js +0 -1
  205. package/.next/server/app/api/models-config/test/route.js +0 -1
  206. package/.next/server/app/api/models-config/test/route.js.nft.json +0 -1
  207. package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +0 -1
  208. package/.next/server/app/api/projects/browse/route.js +0 -1
  209. package/.next/server/app/api/projects/browse/route.js.nft.json +0 -1
  210. package/.next/server/app/api/projects/browse/route_client-reference-manifest.js +0 -1
  211. package/.next/server/app/api/projects/route.js +0 -1
  212. package/.next/server/app/api/projects/route.js.nft.json +0 -1
  213. package/.next/server/app/api/projects/route_client-reference-manifest.js +0 -1
  214. package/.next/server/app/api/reports/[id]/route.js +0 -10
  215. package/.next/server/app/api/reports/[id]/route.js.nft.json +0 -1
  216. package/.next/server/app/api/reports/[id]/route_client-reference-manifest.js +0 -1
  217. package/.next/server/app/api/search/route.js +0 -1
  218. package/.next/server/app/api/search/route.js.nft.json +0 -1
  219. package/.next/server/app/api/search/route_client-reference-manifest.js +0 -1
  220. package/.next/server/app/api/sessions/[id]/context/route.js +0 -1
  221. package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +0 -1
  222. package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +0 -1
  223. package/.next/server/app/api/sessions/[id]/route.js +0 -1
  224. package/.next/server/app/api/sessions/[id]/route.js.nft.json +0 -1
  225. package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +0 -1
  226. package/.next/server/app/api/sessions/new/route.js +0 -1
  227. package/.next/server/app/api/sessions/new/route.js.nft.json +0 -1
  228. package/.next/server/app/api/sessions/new/route_client-reference-manifest.js +0 -1
  229. package/.next/server/app/api/sessions/route.js +0 -1
  230. package/.next/server/app/api/sessions/route.js.nft.json +0 -1
  231. package/.next/server/app/api/sessions/route_client-reference-manifest.js +0 -1
  232. package/.next/server/app/api/settings/route.js +0 -1
  233. package/.next/server/app/api/settings/route.js.nft.json +0 -1
  234. package/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
  235. package/.next/server/app/api/skills/install/route.js +0 -5
  236. package/.next/server/app/api/skills/install/route.js.nft.json +0 -1
  237. package/.next/server/app/api/skills/install/route_client-reference-manifest.js +0 -1
  238. package/.next/server/app/api/skills/route.js +0 -6
  239. package/.next/server/app/api/skills/route.js.nft.json +0 -1
  240. package/.next/server/app/api/skills/route_client-reference-manifest.js +0 -1
  241. package/.next/server/app/api/skills/search/route.js +0 -1
  242. package/.next/server/app/api/skills/search/route.js.nft.json +0 -1
  243. package/.next/server/app/api/skills/search/route_client-reference-manifest.js +0 -1
  244. package/.next/server/app/api/soul/route.js +0 -1
  245. package/.next/server/app/api/soul/route.js.nft.json +0 -1
  246. package/.next/server/app/api/soul/route_client-reference-manifest.js +0 -1
  247. package/.next/server/app/api/version/route.js +0 -1
  248. package/.next/server/app/api/version/route.js.nft.json +0 -1
  249. package/.next/server/app/api/version/route_client-reference-manifest.js +0 -1
  250. package/.next/server/app/index.html +0 -1
  251. package/.next/server/app/index.meta +0 -14
  252. package/.next/server/app/index.rsc +0 -17
  253. package/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -6
  254. package/.next/server/app/index.segments/_full.segment.rsc +0 -17
  255. package/.next/server/app/index.segments/_head.segment.rsc +0 -6
  256. package/.next/server/app/index.segments/_index.segment.rsc +0 -5
  257. package/.next/server/app/index.segments/_tree.segment.rsc +0 -4
  258. package/.next/server/app/login/page.js +0 -2
  259. package/.next/server/app/login/page.js.nft.json +0 -1
  260. package/.next/server/app/login/page_client-reference-manifest.js +0 -1
  261. package/.next/server/app/login.html +0 -1
  262. package/.next/server/app/login.meta +0 -15
  263. package/.next/server/app/login.rsc +0 -22
  264. package/.next/server/app/login.segments/_full.segment.rsc +0 -22
  265. package/.next/server/app/login.segments/_head.segment.rsc +0 -6
  266. package/.next/server/app/login.segments/_index.segment.rsc +0 -5
  267. package/.next/server/app/login.segments/_tree.segment.rsc +0 -4
  268. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +0 -9
  269. package/.next/server/app/login.segments/login.segment.rsc +0 -5
  270. package/.next/server/app/page.js +0 -261
  271. package/.next/server/app/page.js.nft.json +0 -1
  272. package/.next/server/app/page_client-reference-manifest.js +0 -1
  273. package/.next/server/app-paths-manifest.json +0 -39
  274. package/.next/server/chunks/1048.js +0 -1
  275. package/.next/server/chunks/1367.js +0 -77
  276. package/.next/server/chunks/1381.js +0 -1
  277. package/.next/server/chunks/165.js +0 -1
  278. package/.next/server/chunks/1681.js +0 -215
  279. package/.next/server/chunks/1688.js +0 -45
  280. package/.next/server/chunks/1703.js +0 -79
  281. package/.next/server/chunks/1712.js +0 -43
  282. package/.next/server/chunks/1813.js +0 -1
  283. package/.next/server/chunks/2325.js +0 -80
  284. package/.next/server/chunks/258.js +0 -1
  285. package/.next/server/chunks/2671.js +0 -287
  286. package/.next/server/chunks/2778.js +0 -1
  287. package/.next/server/chunks/2943.js +0 -1
  288. package/.next/server/chunks/3031.js +0 -226
  289. package/.next/server/chunks/3181.js +0 -1
  290. package/.next/server/chunks/3493.js +0 -1
  291. package/.next/server/chunks/3672.js +0 -1
  292. package/.next/server/chunks/3701.js +0 -104
  293. package/.next/server/chunks/4013.js +0 -1
  294. package/.next/server/chunks/402.js +0 -2
  295. package/.next/server/chunks/4035.js +0 -80
  296. package/.next/server/chunks/4248.js +0 -153
  297. package/.next/server/chunks/4367.js +0 -1
  298. package/.next/server/chunks/4406.js +0 -141
  299. package/.next/server/chunks/4741.js +0 -18
  300. package/.next/server/chunks/4768.js +0 -1
  301. package/.next/server/chunks/4858.js +0 -148
  302. package/.next/server/chunks/4980.js +0 -1
  303. package/.next/server/chunks/5155.js +0 -5
  304. package/.next/server/chunks/5293.js +0 -166
  305. package/.next/server/chunks/5399.js +0 -8
  306. package/.next/server/chunks/5409.js +0 -1
  307. package/.next/server/chunks/5797.js +0 -93
  308. package/.next/server/chunks/5851.js +0 -36
  309. package/.next/server/chunks/6206.js +0 -1
  310. package/.next/server/chunks/6296.js +0 -1
  311. package/.next/server/chunks/63.js +0 -45
  312. package/.next/server/chunks/6346.js +0 -1
  313. package/.next/server/chunks/6406.js +0 -23
  314. package/.next/server/chunks/642.js +0 -1
  315. package/.next/server/chunks/6429.js +0 -50
  316. package/.next/server/chunks/6729.js +0 -64
  317. package/.next/server/chunks/6907.js +0 -115
  318. package/.next/server/chunks/6980.js +0 -1
  319. package/.next/server/chunks/7073.js +0 -24
  320. package/.next/server/chunks/7233.js +0 -24
  321. package/.next/server/chunks/7307.js +0 -1
  322. package/.next/server/chunks/7362.js +0 -9
  323. package/.next/server/chunks/7567.js +0 -29
  324. package/.next/server/chunks/7765.js +0 -1
  325. package/.next/server/chunks/7890.js +0 -1
  326. package/.next/server/chunks/8065.js +0 -1
  327. package/.next/server/chunks/8238.js +0 -34
  328. package/.next/server/chunks/8276.js +0 -1
  329. package/.next/server/chunks/8336.js +0 -1
  330. package/.next/server/chunks/8477.js +0 -3
  331. package/.next/server/chunks/8490.js +0 -1
  332. package/.next/server/chunks/8916.js +0 -1
  333. package/.next/server/chunks/9280.js +0 -252
  334. package/.next/server/chunks/9315.js +0 -1
  335. package/.next/server/chunks/9537.js +0 -90
  336. package/.next/server/chunks/966.js +0 -1
  337. package/.next/server/chunks/9818.js +0 -21
  338. package/.next/server/chunks/static/media/pdf.worker.min.c476e1a0.mjs +0 -6
  339. package/.next/server/functions-config-manifest.json +0 -16
  340. package/.next/server/interception-route-rewrite-manifest.js +0 -1
  341. package/.next/server/middleware-build-manifest.js +0 -1
  342. package/.next/server/middleware-manifest.json +0 -6
  343. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  344. package/.next/server/middleware.js +0 -18
  345. package/.next/server/middleware.js.nft.json +0 -1
  346. package/.next/server/next-font-manifest.js +0 -1
  347. package/.next/server/next-font-manifest.json +0 -1
  348. package/.next/server/pages/404.html +0 -1
  349. package/.next/server/pages/500.html +0 -1
  350. package/.next/server/pages-manifest.json +0 -4
  351. package/.next/server/prefetch-hints.json +0 -1
  352. package/.next/server/server-reference-manifest.js +0 -1
  353. package/.next/server/server-reference-manifest.json +0 -1
  354. package/.next/server/webpack-runtime.js +0 -1
  355. package/.next/static/6cuMSvcr0FVO-GiK5RJZh/_buildManifest.js +0 -1
  356. package/.next/static/6cuMSvcr0FVO-GiK5RJZh/_ssgManifest.js +0 -1
  357. package/.next/static/chunks/0b9a0da7.9075af772487e743.js +0 -62
  358. package/.next/static/chunks/1413.922d232de90c0c41.js +0 -115
  359. package/.next/static/chunks/1643.467a526a1f24f54d.js +0 -24
  360. package/.next/static/chunks/1852.5543122f11aa7fed.js +0 -1
  361. package/.next/static/chunks/1960.b1e26436d7a5f586.js +0 -1
  362. package/.next/static/chunks/2170a4aa.4213bb2183c9cdf9.js +0 -1
  363. package/.next/static/chunks/2274.6cd173f80a1405a2.js +0 -21
  364. package/.next/static/chunks/2419.347fdfe3c170854d.js +0 -166
  365. package/.next/static/chunks/2619.9aac8983f30c7c8a.js +0 -1
  366. package/.next/static/chunks/2623.d20fabd8e18197c6.js +0 -287
  367. package/.next/static/chunks/2729.f5365061a849d659.js +0 -34
  368. package/.next/static/chunks/2821.934bcf60fbdc28c6.js +0 -1
  369. package/.next/static/chunks/2918becc.abff2ece1de37bc1.js +0 -153
  370. package/.next/static/chunks/2947.114e51cb06d1c01a.js +0 -23
  371. package/.next/static/chunks/3079.4c511fa1144e3adf.js +0 -79
  372. package/.next/static/chunks/3274.208ca44844cd7d95.js +0 -148
  373. package/.next/static/chunks/3308.465a94263d04bfea.js +0 -73
  374. package/.next/static/chunks/3325.e4bfe1ca657f3b5b.js +0 -80
  375. package/.next/static/chunks/3506.2a7eaa08b9f55337.js +0 -90
  376. package/.next/static/chunks/363642f4-043c1475ab9af70e.js +0 -1
  377. package/.next/static/chunks/3794-123fdf632563f469.js +0 -32
  378. package/.next/static/chunks/3837.a755ccfe6f9c1c1c.js +0 -5
  379. package/.next/static/chunks/394.91597771688df6d0.js +0 -1
  380. package/.next/static/chunks/3997.1009c06025691712.js +0 -1
  381. package/.next/static/chunks/4453.91a357dc43c21745.js +0 -1
  382. package/.next/static/chunks/4491.44fdf20580ac72bd.js +0 -24
  383. package/.next/static/chunks/4829.cf1d50e43e6d9db5.js +0 -1
  384. package/.next/static/chunks/498.fe1d9da9ecad6c36.js +0 -1
  385. package/.next/static/chunks/4bd1b696-e356ca5ba0218e27.js +0 -1
  386. package/.next/static/chunks/5019.b5a1a2b8daf17525.js +0 -1
  387. package/.next/static/chunks/5034.8f16c3fa3ce75411.js +0 -1
  388. package/.next/static/chunks/5074.d16651da01ec4e02.js +0 -1
  389. package/.next/static/chunks/51fb665c.0950e1b79671348d.js +0 -45
  390. package/.next/static/chunks/532.5956ed631aff722b.js +0 -9
  391. package/.next/static/chunks/5326.69460442bdcd6cd3.js +0 -1
  392. package/.next/static/chunks/5403.ff110bf5bf600758.js +0 -64
  393. package/.next/static/chunks/547.902a733488cfe3f7.js +0 -77
  394. package/.next/static/chunks/5567.540d7fc108ad6ee5.js +0 -215
  395. package/.next/static/chunks/5590.ef62922166d308b4.js +0 -1
  396. package/.next/static/chunks/5690.9d6eb1edb1399995.js +0 -1
  397. package/.next/static/chunks/5749.25faee4a1e55b854.js +0 -226
  398. package/.next/static/chunks/58bb9007.1ccb6bba34b4c635.js +0 -80
  399. package/.next/static/chunks/6121.f3f43f1896ea0cd9.js +0 -1
  400. package/.next/static/chunks/6600.583c88eef37aa524.js +0 -1
  401. package/.next/static/chunks/6696.a41aec266e657d54.js +0 -141
  402. package/.next/static/chunks/6922.42148793782d2fe7.js +0 -1
  403. package/.next/static/chunks/7006.e191611ffc2b9528.js +0 -43
  404. package/.next/static/chunks/7343.9fbb58204d8ac681.js +0 -1
  405. package/.next/static/chunks/73972abe.25a4cffa03b2bcef.js +0 -119
  406. package/.next/static/chunks/7547.58bda8a2aabba0d4.js +0 -93
  407. package/.next/static/chunks/7648.4ae2f183b4db0353.js +0 -1
  408. package/.next/static/chunks/7874.8db6929b94cdf697.js +0 -1
  409. package/.next/static/chunks/7959.1f20a35df316216a.js +0 -104
  410. package/.next/static/chunks/83.85d62d7fc9850b75.js +0 -29
  411. package/.next/static/chunks/8436.cab94b59cca0a8ff.js +0 -1
  412. package/.next/static/chunks/8451.ff6ff72b57dc52e1.js +0 -1
  413. package/.next/static/chunks/8489.45f22859734f514f.js +0 -36
  414. package/.next/static/chunks/8568.f85d8b36fc9a9037.js +0 -1
  415. package/.next/static/chunks/8771-3e14b6810486df1f.js +0 -1
  416. package/.next/static/chunks/8863.be51033a67436277.js +0 -1
  417. package/.next/static/chunks/90542734.dc1a2723e4f6affb.js +0 -1
  418. package/.next/static/chunks/9500.1488aec06ee78127.js +0 -1
  419. package/.next/static/chunks/9633.155548b5fca6e580.js +0 -1
  420. package/.next/static/chunks/9779.673004a62d70e36a.js +0 -1
  421. package/.next/static/chunks/app/_global-error/page-cc518af6b1ffb191.js +0 -1
  422. package/.next/static/chunks/app/_not-found/page-c72daab99269beff.js +0 -1
  423. package/.next/static/chunks/app/api/agent/[id]/events/route-cc518af6b1ffb191.js +0 -1
  424. package/.next/static/chunks/app/api/agent/[id]/route-cc518af6b1ffb191.js +0 -1
  425. package/.next/static/chunks/app/api/agent/new/route-cc518af6b1ffb191.js +0 -1
  426. package/.next/static/chunks/app/api/auth/all-providers/route-cc518af6b1ffb191.js +0 -1
  427. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-cc518af6b1ffb191.js +0 -1
  428. package/.next/static/chunks/app/api/auth/login/[provider]/route-cc518af6b1ffb191.js +0 -1
  429. package/.next/static/chunks/app/api/auth/login/route-cc518af6b1ffb191.js +0 -1
  430. package/.next/static/chunks/app/api/auth/logout/[provider]/route-cc518af6b1ffb191.js +0 -1
  431. package/.next/static/chunks/app/api/auth/providers/route-cc518af6b1ffb191.js +0 -1
  432. package/.next/static/chunks/app/api/auth/status/route-cc518af6b1ffb191.js +0 -1
  433. package/.next/static/chunks/app/api/default-cwd/route-cc518af6b1ffb191.js +0 -1
  434. package/.next/static/chunks/app/api/files/[...path]/route-cc518af6b1ffb191.js +0 -1
  435. package/.next/static/chunks/app/api/harness/route-cc518af6b1ffb191.js +0 -1
  436. package/.next/static/chunks/app/api/home/route-cc518af6b1ffb191.js +0 -1
  437. package/.next/static/chunks/app/api/internal/runtime/route-cc518af6b1ffb191.js +0 -1
  438. package/.next/static/chunks/app/api/models/route-cc518af6b1ffb191.js +0 -1
  439. package/.next/static/chunks/app/api/models-config/discover/route-cc518af6b1ffb191.js +0 -1
  440. package/.next/static/chunks/app/api/models-config/route-cc518af6b1ffb191.js +0 -1
  441. package/.next/static/chunks/app/api/models-config/test/route-cc518af6b1ffb191.js +0 -1
  442. package/.next/static/chunks/app/api/projects/browse/route-cc518af6b1ffb191.js +0 -1
  443. package/.next/static/chunks/app/api/projects/route-cc518af6b1ffb191.js +0 -1
  444. package/.next/static/chunks/app/api/reports/[id]/route-cc518af6b1ffb191.js +0 -1
  445. package/.next/static/chunks/app/api/search/route-cc518af6b1ffb191.js +0 -1
  446. package/.next/static/chunks/app/api/sessions/[id]/context/route-cc518af6b1ffb191.js +0 -1
  447. package/.next/static/chunks/app/api/sessions/[id]/route-cc518af6b1ffb191.js +0 -1
  448. package/.next/static/chunks/app/api/sessions/new/route-cc518af6b1ffb191.js +0 -1
  449. package/.next/static/chunks/app/api/sessions/route-cc518af6b1ffb191.js +0 -1
  450. package/.next/static/chunks/app/api/settings/route-cc518af6b1ffb191.js +0 -1
  451. package/.next/static/chunks/app/api/skills/install/route-cc518af6b1ffb191.js +0 -1
  452. package/.next/static/chunks/app/api/skills/route-cc518af6b1ffb191.js +0 -1
  453. package/.next/static/chunks/app/api/skills/search/route-cc518af6b1ffb191.js +0 -1
  454. package/.next/static/chunks/app/api/soul/route-cc518af6b1ffb191.js +0 -1
  455. package/.next/static/chunks/app/api/version/route-cc518af6b1ffb191.js +0 -1
  456. package/.next/static/chunks/app/layout-be148b7ae915b22a.js +0 -1
  457. package/.next/static/chunks/app/login/page-ebf0e6de99062783.js +0 -1
  458. package/.next/static/chunks/app/page-c45d98ea81c548ca.js +0 -260
  459. package/.next/static/chunks/d3ac728e.7964f816a1ca64e5.js +0 -1
  460. package/.next/static/chunks/framework-711ef29bc66f648c.js +0 -1
  461. package/.next/static/chunks/main-app-45a0f19af99d61b6.js +0 -1
  462. package/.next/static/chunks/main-f74964b7ae52493e.js +0 -5
  463. package/.next/static/chunks/next/dist/client/components/builtin/app-error-cc518af6b1ffb191.js +0 -1
  464. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-cc518af6b1ffb191.js +0 -1
  465. package/.next/static/chunks/next/dist/client/components/builtin/global-error-9bfa08b9491621f2.js +0 -1
  466. package/.next/static/chunks/next/dist/client/components/builtin/not-found-cc518af6b1ffb191.js +0 -1
  467. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-cc518af6b1ffb191.js +0 -1
  468. package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  469. package/.next/static/chunks/webpack-fcf4a889ecbd753c.js +0 -1
  470. package/.next/static/css/45029451a1d7255d.css +0 -3
  471. package/.next/static/media/15605e25b523335c-s.woff2 +0 -0
  472. package/.next/static/media/1a3dce5cfb5f7760-s.woff2 +0 -0
  473. package/.next/static/media/1cdd02902f937a18-s.woff2 +0 -0
  474. package/.next/static/media/4c4b3b30b6bcb2be-s.woff2 +0 -0
  475. package/.next/static/media/641a7b8a5800ee0e-s.woff2 +0 -0
  476. package/.next/static/media/7deddc85b7ffd1dc-s.p.woff2 +0 -0
  477. package/.next/static/media/ec14413c594b3356-s.p.woff2 +0 -0
  478. package/.next/static/media/pdf.worker.min.29aaf158.mjs +0 -6
  479. package/.next/trace +0 -74
  480. package/.next/trace-build +0 -1
  481. package/.next/types/app/api/agent/[id]/events/route.ts +0 -351
  482. package/.next/types/app/api/agent/[id]/route.ts +0 -351
  483. package/.next/types/app/api/agent/new/route.ts +0 -351
  484. package/.next/types/app/api/auth/all-providers/route.ts +0 -351
  485. package/.next/types/app/api/auth/api-key/[provider]/route.ts +0 -351
  486. package/.next/types/app/api/auth/login/[provider]/route.ts +0 -351
  487. package/.next/types/app/api/auth/login/route.ts +0 -351
  488. package/.next/types/app/api/auth/logout/[provider]/route.ts +0 -351
  489. package/.next/types/app/api/auth/providers/route.ts +0 -351
  490. package/.next/types/app/api/auth/status/route.ts +0 -351
  491. package/.next/types/app/api/default-cwd/route.ts +0 -351
  492. package/.next/types/app/api/files/[...path]/route.ts +0 -351
  493. package/.next/types/app/api/harness/route.ts +0 -351
  494. package/.next/types/app/api/home/route.ts +0 -351
  495. package/.next/types/app/api/internal/runtime/route.ts +0 -351
  496. package/.next/types/app/api/models/route.ts +0 -351
  497. package/.next/types/app/api/models-config/discover/route.ts +0 -351
  498. package/.next/types/app/api/models-config/route.ts +0 -351
  499. package/.next/types/app/api/models-config/test/route.ts +0 -351
  500. package/.next/types/app/api/projects/browse/route.ts +0 -351
  501. package/.next/types/app/api/projects/route.ts +0 -351
  502. package/.next/types/app/api/reports/[id]/route.ts +0 -351
  503. package/.next/types/app/api/search/route.ts +0 -351
  504. package/.next/types/app/api/sessions/[id]/context/route.ts +0 -351
  505. package/.next/types/app/api/sessions/[id]/route.ts +0 -351
  506. package/.next/types/app/api/sessions/new/route.ts +0 -351
  507. package/.next/types/app/api/sessions/route.ts +0 -351
  508. package/.next/types/app/api/settings/route.ts +0 -351
  509. package/.next/types/app/api/skills/install/route.ts +0 -351
  510. package/.next/types/app/api/skills/route.ts +0 -351
  511. package/.next/types/app/api/skills/search/route.ts +0 -351
  512. package/.next/types/app/api/soul/route.ts +0 -351
  513. package/.next/types/app/api/version/route.ts +0 -351
  514. package/.next/types/app/layout.ts +0 -87
  515. package/.next/types/app/login/page.ts +0 -87
  516. package/.next/types/app/page.ts +0 -87
  517. package/.next/types/package.json +0 -1
  518. package/.next/types/routes.d.ts +0 -106
  519. package/.next/types/validator.ts +0 -376
@@ -0,0 +1,903 @@
1
+ /**
2
+ * Session Reader — Codex app-server thread-based session management
3
+ *
4
+ * Sessions are managed by codex app-server (threads in ~/.codex/).
5
+ * This module provides helpers to query threads via the app-server API.
6
+ * No separate session registry — annodex and codex CLI share sessions.
7
+ */
8
+
9
+ import { dirname, join } from "path";
10
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync, writeFileSync } from "fs";
11
+ import type {
12
+ AgentMessage,
13
+ AssistantMessage,
14
+ SessionInfo,
15
+ SessionEntry,
16
+ SessionContext,
17
+ SessionTreeNode,
18
+ SubagentRun,
19
+ TextContent,
20
+ ToolResultMessage,
21
+ } from "./types";
22
+ import {
23
+ type CodexThread,
24
+ type CodexThreadItem,
25
+ type CodexUserInput,
26
+ type CodexServerConnection,
27
+ getServerForCwd,
28
+ getOrCreateCodexServer,
29
+ releaseCodexServerConnection,
30
+ codexServerEnvForModel,
31
+ readThread,
32
+ } from "./codex-server";
33
+ import { getCodexHome } from "./codex-home";
34
+ import { readSessionRuntime, upsertSessionRuntime } from "./session-runtime";
35
+ import { mergeSubagentRuns, subagentRunsFromCollabEvent, subagentRunsFromCollabItem } from "./subagent-progress";
36
+ import { readCodexUsageFromFile } from "./codex-usage";
37
+ import { findProvider, normalizeRuntimeProviderId, readMergedConfig } from "./annodex-config";
38
+
39
+ // ============================================================================
40
+ // Agent dir (used for SOUL.md/HARNESS.md location)
41
+ // ============================================================================
42
+
43
+ export function getAgentDir(): string {
44
+ const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
45
+ return join(homeDir, ".config", "annodex");
46
+ }
47
+
48
+ export function getSessionsDir(): string {
49
+ return join(getAgentDir(), "sessions");
50
+ }
51
+
52
+ // ============================================================================
53
+ // Session path cache
54
+ // ============================================================================
55
+
56
+ declare global {
57
+ var __annodexSessionPathCache: Map<string, string> | undefined;
58
+ }
59
+
60
+ function getPathCache(): Map<string, string> {
61
+ if (!globalThis.__annodexSessionPathCache) globalThis.__annodexSessionPathCache = new Map();
62
+ return globalThis.__annodexSessionPathCache;
63
+ }
64
+
65
+ export function cacheSessionPath(sessionId: string, cwd: string): void {
66
+ getPathCache().set(sessionId, cwd);
67
+ }
68
+
69
+ export function invalidateSessionPathCache(sessionId: string): void {
70
+ getPathCache().delete(sessionId);
71
+ }
72
+
73
+ export async function resolveSessionPath(sessionId: string): Promise<string | null> {
74
+ const cached = getPathCache().get(sessionId);
75
+ if (cached) return cached;
76
+
77
+ return getSessionInfoById(sessionId)?.cwd ?? null;
78
+ }
79
+
80
+ // ============================================================================
81
+ // Session listing (fast local read from ~/.codex/sessions)
82
+ // ============================================================================
83
+
84
+ export async function listAllSessions(options: { cwd?: string | null } = {}): Promise<SessionInfo[]> {
85
+ return listLocalCodexSessions(options);
86
+ }
87
+
88
+ function getCodexSessionsRoot(): string {
89
+ return join(getCodexHome(), "sessions");
90
+ }
91
+
92
+ const MAX_SESSION_SUMMARY_BYTES = 2 * 1024 * 1024;
93
+
94
+ function normalizeCwdPath(cwd: string): string {
95
+ const trimmed = cwd.trim();
96
+ return trimmed.replace(/[\\/]+$/, "") || trimmed;
97
+ }
98
+
99
+ function listJsonlFiles(dir: string, out: string[] = []): string[] {
100
+ try {
101
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
102
+ const fullPath = join(dir, entry.name);
103
+ if (entry.isDirectory()) {
104
+ listJsonlFiles(fullPath, out);
105
+ } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
106
+ out.push(fullPath);
107
+ }
108
+ }
109
+ } catch {
110
+ return out;
111
+ }
112
+ return out;
113
+ }
114
+
115
+ function isRecord(value: unknown): value is Record<string, unknown> {
116
+ return typeof value === "object" && value !== null && !Array.isArray(value);
117
+ }
118
+
119
+ function parseJsonLine(line: string): Record<string, unknown> | null {
120
+ try {
121
+ const parsed = JSON.parse(line) as unknown;
122
+ return isRecord(parsed) ? parsed : null;
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+
128
+ function payloadRecord(line: Record<string, unknown>): Record<string, unknown> | null {
129
+ return isRecord(line.payload) ? line.payload : null;
130
+ }
131
+
132
+ function objectString(obj: Record<string, unknown>, key: string): string | undefined {
133
+ const value = obj[key];
134
+ return typeof value === "string" && value.trim() ? value : undefined;
135
+ }
136
+
137
+ function annodexProviderId(value: string | undefined, preferCodex = false): string | undefined {
138
+ if (!value) return undefined;
139
+ try {
140
+ return normalizeRuntimeProviderId(readMergedConfig(), value, { preferCodex });
141
+ } catch {
142
+ return value;
143
+ }
144
+ }
145
+
146
+ function textFromResponseContent(content: unknown): string {
147
+ if (typeof content === "string") return content;
148
+ if (!Array.isArray(content)) return "";
149
+ return content
150
+ .map((block) => {
151
+ if (!isRecord(block)) return "";
152
+ if (typeof block.text === "string") return block.text;
153
+ if (typeof block.content === "string") return block.content;
154
+ return "";
155
+ })
156
+ .filter(Boolean)
157
+ .join("\n");
158
+ }
159
+
160
+ function timestampMs(value: unknown): number | undefined {
161
+ if (typeof value !== "string" || !value) return undefined;
162
+ const time = Date.parse(value);
163
+ return Number.isFinite(time) ? time : undefined;
164
+ }
165
+
166
+ interface LocalCodexSessionMeta {
167
+ id: string;
168
+ cwd: string;
169
+ created: string;
170
+ modelProvider?: string;
171
+ parentSessionId?: string;
172
+ }
173
+
174
+ interface SessionHistoryCacheRecord {
175
+ filePath: string;
176
+ size: number;
177
+ mtimeMs: number;
178
+ summary: SessionInfo;
179
+ model?: {
180
+ provider?: string;
181
+ modelId?: string;
182
+ thinkingLevel?: string;
183
+ };
184
+ }
185
+
186
+ interface SessionHistoryCacheFile {
187
+ version: 1;
188
+ records: Record<string, SessionHistoryCacheRecord>;
189
+ }
190
+
191
+ interface LocalFileStamp {
192
+ size: number;
193
+ mtimeMs: number;
194
+ modified: string;
195
+ }
196
+
197
+ interface SessionFileRuntime {
198
+ provider?: string;
199
+ modelId?: string;
200
+ thinkingLevel?: string;
201
+ }
202
+
203
+ function statSessionFile(filePath: string): LocalFileStamp | null {
204
+ try {
205
+ const stat = statSync(filePath);
206
+ return {
207
+ size: Number(stat.size),
208
+ mtimeMs: Number(stat.mtimeMs),
209
+ modified: stat.mtime.toISOString(),
210
+ };
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+
216
+ function getSessionHistoryCachePath(): string {
217
+ return join(getAgentDir(), "session-history-cache.json");
218
+ }
219
+
220
+ function readSessionHistoryCache(): SessionHistoryCacheFile {
221
+ const path = getSessionHistoryCachePath();
222
+ if (!existsSync(path)) return { version: 1, records: {} };
223
+ try {
224
+ const parsed = JSON.parse(readFileSync(path, "utf-8")) as Partial<SessionHistoryCacheFile>;
225
+ return {
226
+ version: 1,
227
+ records: parsed.records && typeof parsed.records === "object" ? parsed.records : {},
228
+ };
229
+ } catch {
230
+ return { version: 1, records: {} };
231
+ }
232
+ }
233
+
234
+ function writeSessionHistoryCache(cache: SessionHistoryCacheFile): void {
235
+ const path = getSessionHistoryCachePath();
236
+ const dir = dirname(path);
237
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
238
+ writeFileSync(path, JSON.stringify(cache, null, 2), "utf-8");
239
+ }
240
+
241
+ function readFilePrefix(filePath: string, bytes = 64 * 1024): string {
242
+ let fd: number | null = null;
243
+ try {
244
+ fd = openSync(filePath, "r");
245
+ const buffer = Buffer.alloc(bytes);
246
+ const read = readSync(fd, buffer, 0, bytes, 0);
247
+ return buffer.subarray(0, read).toString("utf-8");
248
+ } catch {
249
+ return "";
250
+ } finally {
251
+ if (fd !== null) {
252
+ try { closeSync(fd); } catch { /* ignore */ }
253
+ }
254
+ }
255
+ }
256
+
257
+ function unescapeJsonString(value: string): string {
258
+ try {
259
+ return JSON.parse(`"${value}"`) as string;
260
+ } catch {
261
+ return value;
262
+ }
263
+ }
264
+
265
+ function readCodexSessionMeta(filePath: string): LocalCodexSessionMeta | null {
266
+ const prefix = readFilePrefix(filePath);
267
+ if (!/"type"\s*:\s*"session_meta"/.test(prefix)) return null;
268
+ const firstLine = prefix.split(/\r?\n/, 1)[0] ?? "";
269
+ const parsed = parseJsonLine(firstLine);
270
+ const payload = parsed ? payloadRecord(parsed) : null;
271
+ const source = payload ?? parsed;
272
+ if (source) {
273
+ const id = objectString(source, "id");
274
+ const cwdRaw = objectString(source, "cwd");
275
+ if (id && cwdRaw) {
276
+ const parentFromSource = isRecord(source.source)
277
+ && isRecord(source.source.subagent)
278
+ && isRecord(source.source.subagent.thread_spawn)
279
+ ? objectString(source.source.subagent.thread_spawn, "parent_thread_id")
280
+ : undefined;
281
+ return {
282
+ id,
283
+ cwd: normalizeCwdPath(cwdRaw),
284
+ created: objectString(source, "timestamp") ?? objectString(parsed ?? {}, "timestamp") ?? new Date(0).toISOString(),
285
+ modelProvider: annodexProviderId(objectString(source, "model_provider"), true),
286
+ parentSessionId: objectString(source, "forked_from_id") ?? parentFromSource,
287
+ };
288
+ }
289
+ }
290
+
291
+ const idMatch = /"id"\s*:\s*"((?:\\.|[^"\\])*)"/.exec(prefix);
292
+ const cwdMatch = /"cwd"\s*:\s*"((?:\\.|[^"\\])*)"/.exec(prefix);
293
+ const timestampMatch = /"timestamp"\s*:\s*"((?:\\.|[^"\\])*)"/.exec(prefix);
294
+ const modelProviderMatch = /"model_provider"\s*:\s*"((?:\\.|[^"\\])*)"/.exec(prefix);
295
+ const forkedFromMatch = /"forked_from_id"\s*:\s*"((?:\\.|[^"\\])*)"/.exec(prefix);
296
+ const id = idMatch ? unescapeJsonString(idMatch[1]) : null;
297
+ const cwd = cwdMatch ? normalizeCwdPath(unescapeJsonString(cwdMatch[1])) : null;
298
+ if (!id || !cwd) return null;
299
+ const created = timestampMatch ? unescapeJsonString(timestampMatch[1]) : new Date(0).toISOString();
300
+ return {
301
+ id,
302
+ cwd,
303
+ created,
304
+ modelProvider: annodexProviderId(modelProviderMatch ? unescapeJsonString(modelProviderMatch[1]) : undefined, true),
305
+ parentSessionId: forkedFromMatch ? unescapeJsonString(forkedFromMatch[1]) : undefined,
306
+ };
307
+ }
308
+
309
+ function cleanPreview(text: string): string {
310
+ const trimmed = text.trim();
311
+ if (!trimmed) return "";
312
+ if (trimmed.includes("AGENTS.md instructions")) return "";
313
+ const markerIndex = trimmed.lastIndexOf("<environment_context>");
314
+ const withoutPrependedContext = markerIndex >= 0
315
+ ? trimmed.slice(markerIndex).replace(/^<environment_context>[\s\S]*?<\/environment_context>\s*/m, "").trim()
316
+ : trimmed;
317
+ const singleLine = withoutPrependedContext.replace(/\s+/g, " ").trim();
318
+ return singleLine.length > 180 ? `${singleLine.slice(0, 180)}...` : singleLine;
319
+ }
320
+
321
+ function maybeSetFirstMessage(current: string, candidate: string): string {
322
+ if (current) return current;
323
+ const cleaned = cleanPreview(candidate);
324
+ return cleaned || current;
325
+ }
326
+
327
+ function summarizeCodexSessionFile(
328
+ filePath: string,
329
+ meta: LocalCodexSessionMeta,
330
+ stamp: LocalFileStamp,
331
+ ): SessionHistoryCacheRecord | null {
332
+ const content = readFilePrefix(filePath, MAX_SESSION_SUMMARY_BYTES);
333
+ if (!content) return null;
334
+
335
+ const lines = content.split("\n").filter((line) => line.trim());
336
+
337
+ let firstMessage = "";
338
+ let eventMessageCount = 0;
339
+ let responseMessageCount = 0;
340
+ let modelId: string | undefined;
341
+ let thinkingLevel: string | undefined;
342
+
343
+ for (const line of lines) {
344
+ const item = parseJsonLine(line);
345
+ if (!item) continue;
346
+ const itemPayload = payloadRecord(item) ?? item;
347
+
348
+ if (item.type === "event_msg") {
349
+ const payloadType = itemPayload.type;
350
+ if (payloadType === "user_message" || payloadType === "agent_message") {
351
+ eventMessageCount++;
352
+ }
353
+ if (payloadType === "user_message" && typeof itemPayload.message === "string") {
354
+ firstMessage = maybeSetFirstMessage(firstMessage, itemPayload.message);
355
+ }
356
+ continue;
357
+ }
358
+
359
+ if (item.type === "response_item" && itemPayload.type === "message") {
360
+ const role = itemPayload.role;
361
+ if (role === "user" || role === "assistant") responseMessageCount++;
362
+ if (role === "user") {
363
+ firstMessage = maybeSetFirstMessage(firstMessage, textFromResponseContent(itemPayload.content));
364
+ }
365
+ continue;
366
+ }
367
+
368
+ if (item.type === "turn_context") {
369
+ modelId = objectString(itemPayload, "model") ?? modelId;
370
+ thinkingLevel = objectString(itemPayload, "effort") ?? thinkingLevel;
371
+ }
372
+ }
373
+
374
+ cacheSessionPath(meta.id, meta.cwd);
375
+ const summary: SessionInfo = {
376
+ id: meta.id,
377
+ path: filePath,
378
+ cwd: meta.cwd,
379
+ cwdExists: existsSync(meta.cwd),
380
+ created: meta.created,
381
+ modified: stamp.modified,
382
+ messageCount: eventMessageCount || responseMessageCount || (firstMessage ? 1 : 0),
383
+ firstMessage: firstMessage || "(no messages)",
384
+ parentSessionId: meta.parentSessionId,
385
+ };
386
+ const model = {
387
+ provider: meta.modelProvider,
388
+ modelId,
389
+ thinkingLevel,
390
+ };
391
+ const existingRuntime = readSessionRuntime(meta.id);
392
+ const runtimePatch = {
393
+ cwd: meta.cwd,
394
+ provider: existingRuntime?.provider ?? model.provider,
395
+ modelId: existingRuntime?.modelId ?? model.modelId,
396
+ thinkingLevel: existingRuntime?.thinkingLevel ?? model.thinkingLevel,
397
+ };
398
+ if (runtimePatch.provider || runtimePatch.modelId || runtimePatch.thinkingLevel) {
399
+ upsertSessionRuntime(meta.id, {
400
+ cwd: runtimePatch.cwd,
401
+ provider: runtimePatch.provider,
402
+ modelId: runtimePatch.modelId,
403
+ thinkingLevel: runtimePatch.thinkingLevel,
404
+ });
405
+ }
406
+ return {
407
+ filePath,
408
+ size: stamp.size,
409
+ mtimeMs: stamp.mtimeMs,
410
+ summary,
411
+ model,
412
+ };
413
+ }
414
+
415
+ function listLocalCodexSessions(options: { cwd?: string | null } = {}): SessionInfo[] {
416
+ const sessions: SessionInfo[] = [];
417
+ const seenIds = new Set<string>();
418
+ const explicitCwd = options.cwd?.trim() ? normalizeCwdPath(options.cwd) : undefined;
419
+ const root = getCodexSessionsRoot();
420
+ if (!existsSync(root)) return sessions;
421
+ const cache = readSessionHistoryCache();
422
+ const nextRecords: Record<string, SessionHistoryCacheRecord> = {};
423
+
424
+ for (const filePath of listJsonlFiles(root)) {
425
+ const stamp = statSessionFile(filePath);
426
+ if (!stamp) continue;
427
+ const cached = cache.records[filePath];
428
+ let record = cached && cached.size === stamp.size && cached.mtimeMs === stamp.mtimeMs
429
+ ? cached
430
+ : null;
431
+ if (!record) {
432
+ const meta = readCodexSessionMeta(filePath);
433
+ if (!meta) continue;
434
+ record = summarizeCodexSessionFile(filePath, meta, stamp);
435
+ }
436
+ if (!record) continue;
437
+ nextRecords[filePath] = record;
438
+ const session: SessionInfo = {
439
+ ...record.summary,
440
+ modified: new Date(record.mtimeMs).toISOString(),
441
+ cwdExists: existsSync(record.summary.cwd),
442
+ };
443
+ cacheSessionPath(session.id, session.cwd);
444
+ if (explicitCwd && normalizeCwdPath(session.cwd) !== explicitCwd) continue;
445
+ if (!session || seenIds.has(session.id)) continue;
446
+ seenIds.add(session.id);
447
+ sessions.push(session);
448
+ }
449
+
450
+ writeSessionHistoryCache({ version: 1, records: nextRecords });
451
+ return sessions.sort((a, b) => b.modified.localeCompare(a.modified));
452
+ }
453
+
454
+ export function getSessionInfoById(sessionId: string): SessionInfo | null {
455
+ const root = getCodexSessionsRoot();
456
+ if (!sessionId || !existsSync(root)) return null;
457
+
458
+ const files = listJsonlFiles(root);
459
+ const ordered = [
460
+ ...files.filter((filePath) => filePath.includes(sessionId)),
461
+ ...files.filter((filePath) => !filePath.includes(sessionId)),
462
+ ];
463
+
464
+ for (const filePath of ordered) {
465
+ const stamp = statSessionFile(filePath);
466
+ if (!stamp) continue;
467
+ const meta = readCodexSessionMeta(filePath);
468
+ if (meta?.id !== sessionId) continue;
469
+ const record = summarizeCodexSessionFile(filePath, meta, stamp);
470
+ if (!record) return null;
471
+ return {
472
+ ...record.summary,
473
+ modified: new Date(record.mtimeMs).toISOString(),
474
+ cwdExists: existsSync(record.summary.cwd),
475
+ };
476
+ }
477
+
478
+ return null;
479
+ }
480
+
481
+ function isoFromTimestamp(value: number | string | undefined): string {
482
+ if (typeof value === "number" && Number.isFinite(value)) {
483
+ return new Date(value * 1000).toISOString();
484
+ }
485
+ if (typeof value === "string" && value) return value;
486
+ return new Date(0).toISOString();
487
+ }
488
+
489
+ // ============================================================================
490
+ // Session entries / tree (simplified — data comes from codex thread API)
491
+ // ============================================================================
492
+
493
+ export function getSessionEntries(filePath: string): SessionEntry[] {
494
+ void filePath;
495
+ // Codex app-server sessions are read asynchronously through readThread().
496
+ // This synchronous API is kept only for older call sites.
497
+ return [];
498
+ }
499
+
500
+ export function buildTree(entries: SessionEntry[]): SessionTreeNode[] {
501
+ void entries;
502
+ return [];
503
+ }
504
+
505
+ export function buildSessionContext(
506
+ entries: SessionEntry[],
507
+ leafId?: string | null,
508
+ ): SessionContext {
509
+ void entries;
510
+ void leafId;
511
+ return { messages: [], entryIds: [], thinkingLevel: "off", model: null, subagentRuns: [], sessionStats: null, contextUsage: null };
512
+ }
513
+
514
+ export function getLeafId(entries: SessionEntry[]): string | null {
515
+ if (entries.length === 0) return null;
516
+ return entries[entries.length - 1]?.id ?? null;
517
+ }
518
+
519
+ export async function loadCodexSessionContext(
520
+ sessionId: string,
521
+ cwd: string,
522
+ leafId?: string | null,
523
+ ): Promise<{
524
+ context: SessionContext;
525
+ tree: SessionTreeNode[];
526
+ leafId: string | null;
527
+ thread: CodexThread | null;
528
+ }> {
529
+ // Look up the session's stored provider so we use the correct codex
530
+ // app-server instance. Otherwise a thread created under DeepRouter would
531
+ // be queried through the DeepSeek server and return "thread not found".
532
+ const sessionRt = readSessionRuntime(sessionId);
533
+ let conn: CodexServerConnection;
534
+ let ownsConn = false;
535
+ if (sessionRt?.provider) {
536
+ const cfg = readMergedConfig();
537
+ const np = normalizeRuntimeProviderId(cfg, sessionRt.provider);
538
+ const pc = np ? findProvider(cfg, np) : undefined;
539
+ if (pc) {
540
+ conn = await getOrCreateCodexServer(
541
+ cwd,
542
+ codexServerEnvForModel(pc, sessionRt.modelId, sessionRt.thinkingLevel),
543
+ );
544
+ ownsConn = true;
545
+ } else {
546
+ const ec = getServerForCwd(cwd);
547
+ conn = ec ?? await getOrCreateCodexServer(cwd);
548
+ ownsConn = !ec;
549
+ }
550
+ } else {
551
+ const ec = getServerForCwd(cwd);
552
+ conn = ec ?? await getOrCreateCodexServer(cwd);
553
+ ownsConn = !ec;
554
+ }
555
+ let thread: CodexThread | null = null;
556
+ try {
557
+ thread = await readThread(conn, sessionId);
558
+ } finally {
559
+ if (ownsConn) releaseCodexServerConnection(conn);
560
+ }
561
+ if (!thread) {
562
+ return {
563
+ context: { messages: [], entryIds: [], thinkingLevel: "off", model: null, subagentRuns: [], sessionStats: null, contextUsage: null },
564
+ tree: [],
565
+ leafId: null,
566
+ thread: null,
567
+ };
568
+ }
569
+
570
+ cacheSessionPath(thread.id, thread.cwd || cwd);
571
+ if (thread.model || thread.modelProvider) {
572
+ upsertSessionRuntime(thread.id, {
573
+ cwd: thread.cwd || cwd,
574
+ provider: thread.modelProvider,
575
+ modelId: thread.model,
576
+ });
577
+ }
578
+ const fileRuntime = runtimeFromSessionFile(thread.id);
579
+ const runtime = readSessionRuntime(thread.id);
580
+ const runtimeProvider = runtime?.provider ?? fileRuntime?.provider;
581
+ const runtimeModelId = runtime?.modelId ?? fileRuntime?.modelId;
582
+ const runtimeThinkingLevel = runtime?.thinkingLevel ?? fileRuntime?.thinkingLevel;
583
+ const fallbackModel = thread.model
584
+ ? { provider: thread.modelProvider ?? runtimeProvider ?? "codex", modelId: thread.model }
585
+ : runtimeModelId
586
+ ? { provider: runtimeProvider ?? "codex", modelId: runtimeModelId }
587
+ : null;
588
+ const entries = entriesFromThread(thread, fallbackModel);
589
+ const selectedEntries = leafId ? entriesUntil(entries, leafId) : entries;
590
+ const usage = readCodexUsageFromFile(findCodexSessionFile(thread.id), runtimeModelId ?? thread.model);
591
+ const usageEntryByTurnId = new Map<string, string>();
592
+ for (const entry of selectedEntries) {
593
+ if (entry.turnId && entry.message.role === "assistant" && assistantHasText(entry.message)) {
594
+ usageEntryByTurnId.set(entry.turnId, entry.id);
595
+ }
596
+ }
597
+ const messages: AgentMessage[] = [];
598
+ const entryIds: string[] = [];
599
+ for (const entry of selectedEntries) {
600
+ const message = entry.message;
601
+ if (entry.turnId && message.role === "assistant" && usageEntryByTurnId.get(entry.turnId) === entry.id) {
602
+ const messageUsage = usage.messageUsageByTurnId.get(entry.turnId);
603
+ messages.push(messageUsage ? { ...message, usage: messageUsage } : message);
604
+ } else {
605
+ messages.push(message);
606
+ }
607
+ entryIds.push(entry.id);
608
+ }
609
+
610
+ const currentLeafId = selectedEntries[selectedEntries.length - 1]?.id ?? entries[entries.length - 1]?.id ?? null;
611
+ const model = thread.model
612
+ ? { provider: thread.modelProvider ?? runtimeProvider ?? "codex", modelId: thread.model }
613
+ : runtimeModelId
614
+ ? { provider: runtimeProvider ?? "codex", modelId: runtimeModelId }
615
+ : null;
616
+ return {
617
+ context: {
618
+ messages,
619
+ entryIds,
620
+ thinkingLevel: runtimeThinkingLevel ?? "off",
621
+ model,
622
+ subagentRuns: mergeSubagentRuns(subagentRunsFromThread(thread), subagentRunsFromSessionFile(sessionId)),
623
+ sessionStats: usage.sessionStats,
624
+ contextUsage: usage.contextUsage,
625
+ },
626
+ tree: buildTreeFromMessageEntries(entries),
627
+ leafId: currentLeafId,
628
+ thread,
629
+ };
630
+ }
631
+
632
+ export async function searchCodexSessionEntries(sessionId: string, cwd: string): Promise<SessionEntry[]> {
633
+ const loaded = await loadCodexSessionContext(sessionId, cwd);
634
+ const runtime = readSessionRuntime(sessionId);
635
+ const fileRuntime = runtimeFromSessionFile(sessionId);
636
+ const runtimeProvider = runtime?.provider ?? fileRuntime?.provider;
637
+ const runtimeModelId = runtime?.modelId ?? fileRuntime?.modelId;
638
+ const fallbackModel = loaded.thread?.model
639
+ ? { provider: loaded.thread.modelProvider ?? runtimeProvider ?? "codex", modelId: loaded.thread.model }
640
+ : runtimeModelId
641
+ ? { provider: runtimeProvider ?? "codex", modelId: runtimeModelId }
642
+ : null;
643
+ return entriesFromThread(loaded.thread, fallbackModel);
644
+ }
645
+
646
+ function entriesUntil(
647
+ entries: Array<SessionEntry & { message: AgentMessage; turnId?: string }>,
648
+ targetId: string,
649
+ ): Array<SessionEntry & { message: AgentMessage; turnId?: string }> {
650
+ const idx = entries.findIndex((entry) => entry.id === targetId);
651
+ return idx >= 0 ? entries.slice(0, idx + 1) : entries;
652
+ }
653
+
654
+ function entriesFromThread(
655
+ thread: CodexThread | null,
656
+ fallbackModel?: { provider: string; modelId: string } | null,
657
+ ): Array<SessionEntry & { message: AgentMessage; turnId?: string }> {
658
+ if (!thread) return [];
659
+ const entries: Array<SessionEntry & { message: AgentMessage; turnId?: string }> = [];
660
+ let parentId: string | null = null;
661
+
662
+ for (const turn of thread.turns ?? []) {
663
+ const timestamp = isoFromTimestamp(thread.updatedAt);
664
+ for (const item of turn.items) {
665
+ const message = messageFromItem(item, thread, fallbackModel);
666
+ if (!message) continue;
667
+ const entry = {
668
+ type: "message" as const,
669
+ id: item.id,
670
+ parentId,
671
+ timestamp,
672
+ message,
673
+ turnId: turn.id,
674
+ };
675
+ entries.push(entry);
676
+ parentId = item.id;
677
+ }
678
+ }
679
+ return entries;
680
+ }
681
+
682
+ function assistantHasText(message: AssistantMessage): boolean {
683
+ return message.content.some((block) => block.type === "text" && block.text.trim());
684
+ }
685
+
686
+ function subagentRunsFromThread(thread: CodexThread | null): SubagentRun[] {
687
+ if (!thread) return [];
688
+ const runs: SubagentRun[] = [];
689
+ for (const turn of thread.turns ?? []) {
690
+ for (const item of turn.items) {
691
+ if (item.type === "collabAgentToolCall") {
692
+ runs.push(...subagentRunsFromCollabItem(item, thread.updatedAt ? thread.updatedAt * 1000 : undefined));
693
+ }
694
+ }
695
+ }
696
+ return runs;
697
+ }
698
+
699
+ export function findCodexSessionFile(sessionId: string): string | null {
700
+ const root = getCodexSessionsRoot();
701
+ if (!sessionId || !existsSync(root)) return null;
702
+ const files = listJsonlFiles(root);
703
+ return files.find((filePath) => filePath.includes(sessionId)) ?? files.find((filePath) => {
704
+ const meta = readCodexSessionMeta(filePath);
705
+ return meta?.id === sessionId;
706
+ }) ?? null;
707
+ }
708
+
709
+ function subagentRunsFromSessionFile(sessionId: string): SubagentRun[] {
710
+ const filePath = findCodexSessionFile(sessionId);
711
+ if (!filePath) return [];
712
+ let content = "";
713
+ try {
714
+ content = readFileSync(filePath, "utf-8");
715
+ } catch {
716
+ return [];
717
+ }
718
+ if (!content) return [];
719
+ const runs: SubagentRun[] = [];
720
+ for (const line of content.split("\n")) {
721
+ if (!line.trim() || !line.includes("collab_")) continue;
722
+ const item = parseJsonLine(line);
723
+ if (!item || item.type !== "event_msg") continue;
724
+ const payload = payloadRecord(item);
725
+ if (!payload) continue;
726
+ runs.push(...subagentRunsFromCollabEvent(payload, timestampMs(item.timestamp)));
727
+ }
728
+ return runs;
729
+ }
730
+
731
+ function runtimeFromSessionFile(sessionId: string): SessionFileRuntime | null {
732
+ const filePath = findCodexSessionFile(sessionId);
733
+ if (!filePath) return null;
734
+ let content = "";
735
+ try {
736
+ content = readFilePrefix(filePath, MAX_SESSION_SUMMARY_BYTES);
737
+ } catch {
738
+ return null;
739
+ }
740
+ let provider: string | undefined;
741
+ let modelId: string | undefined;
742
+ let thinkingLevel: string | undefined;
743
+ for (const line of content.split("\n")) {
744
+ if (!line.trim()) continue;
745
+ const item = parseJsonLine(line);
746
+ if (!item) continue;
747
+ const payload = payloadRecord(item) ?? item;
748
+ if (item.type === "session_meta") {
749
+ provider = annodexProviderId(objectString(payload, "model_provider"), true) ?? provider;
750
+ continue;
751
+ }
752
+ if (item.type === "turn_context") {
753
+ modelId = objectString(payload, "model") ?? modelId;
754
+ thinkingLevel = objectString(payload, "effort") ?? thinkingLevel;
755
+ }
756
+ }
757
+ return provider || modelId || thinkingLevel ? { provider, modelId, thinkingLevel } : null;
758
+ }
759
+
760
+ function messageFromItem(
761
+ item: CodexThreadItem,
762
+ thread: CodexThread,
763
+ fallbackModel?: { provider: string; modelId: string } | null,
764
+ ): AgentMessage | null {
765
+ if (item.type === "userMessage") {
766
+ return {
767
+ role: "user",
768
+ content: contentFromUserInputs(item.content ?? []),
769
+ };
770
+ }
771
+ if (item.type === "agentMessage") {
772
+ return {
773
+ role: "assistant",
774
+ content: [{ type: "text", text: item.text ?? "" }],
775
+ model: thread.model ?? fallbackModel?.modelId ?? "",
776
+ provider: thread.modelProvider ?? fallbackModel?.provider ?? "",
777
+ } satisfies AssistantMessage;
778
+ }
779
+ if (item.type === "reasoning") {
780
+ const thinking = [...(item.summary ?? []), ...(item.content ?? [])].join("\n").trim();
781
+ if (!thinking) return null;
782
+ return {
783
+ role: "assistant",
784
+ content: [{ type: "thinking", thinking }],
785
+ model: thread.model ?? fallbackModel?.modelId ?? "",
786
+ provider: thread.modelProvider ?? fallbackModel?.provider ?? "",
787
+ } satisfies AssistantMessage;
788
+ }
789
+ if (item.type === "commandExecution") {
790
+ const text = [item.aggregatedOutput ?? "", item.exitCode !== null && item.exitCode !== undefined ? `Exit code: ${item.exitCode}` : ""]
791
+ .filter(Boolean)
792
+ .join("\n");
793
+ return toolResult(item.id, "bash", text, item.status === "failed" || item.status === "declined");
794
+ }
795
+ if (item.type === "fileChange") {
796
+ return toolResult(item.id, "edit", JSON.stringify(item.changes ?? []), item.status === "failed" || item.status === "declined");
797
+ }
798
+ if (item.type === "mcpToolCall") {
799
+ return toolResult(item.id, item.tool || item.server || "mcp", JSON.stringify(item.result ?? item.error ?? ""), !!item.error);
800
+ }
801
+ if (item.type === "dynamicToolCall") {
802
+ return toolResult(item.id, item.tool || "tool", JSON.stringify(item.contentItems ?? []), item.success === false);
803
+ }
804
+ if (item.type === "collabAgentToolCall") {
805
+ const runs = subagentRunsFromCollabItem(item, thread.updatedAt ? thread.updatedAt * 1000 : undefined);
806
+ return {
807
+ role: "custom",
808
+ customType: "subagent-progress",
809
+ content: "Agent progress updated",
810
+ display: false,
811
+ details: { runs },
812
+ timestamp: thread.updatedAt ? thread.updatedAt * 1000 : undefined,
813
+ };
814
+ }
815
+ return null;
816
+ }
817
+
818
+ function toolResult(toolCallId: string, toolName: string, text: string, isError: boolean): ToolResultMessage {
819
+ return {
820
+ role: "toolResult",
821
+ toolCallId,
822
+ toolName,
823
+ content: [{ type: "text", text }],
824
+ isError,
825
+ };
826
+ }
827
+
828
+ function contentFromUserInputs(inputs: CodexUserInput[]): string | TextContent[] {
829
+ const textBlocks: TextContent[] = [];
830
+ for (const input of inputs) {
831
+ if (input.type === "text") {
832
+ textBlocks.push({ type: "text", text: input.text });
833
+ } else if (input.type === "image") {
834
+ textBlocks.push({ type: "text", text: `[image] ${input.url.slice(0, 120)}` });
835
+ } else if (input.type === "localImage") {
836
+ textBlocks.push({ type: "text", text: `[image] ${input.path}` });
837
+ } else if (input.type === "mention") {
838
+ textBlocks.push({ type: "text", text: `@${input.path}` });
839
+ } else if (input.type === "skill") {
840
+ textBlocks.push({ type: "text", text: `$${input.name}` });
841
+ }
842
+ }
843
+ if (textBlocks.length === 0) return "";
844
+ if (textBlocks.length === 1) return textBlocks[0].text;
845
+ return textBlocks;
846
+ }
847
+
848
+ function buildTreeFromMessageEntries(entries: Array<SessionEntry & { message: AgentMessage }>): SessionTreeNode[] {
849
+ const entriesById = new Map<string, SessionEntry & { message: AgentMessage }>();
850
+ const childCounts = new Map<string, number>();
851
+ let rootCount = 0;
852
+ for (const entry of entries) {
853
+ entriesById.set(entry.id, entry);
854
+ if (entry.parentId) {
855
+ childCounts.set(entry.parentId, (childCounts.get(entry.parentId) ?? 0) + 1);
856
+ } else {
857
+ rootCount++;
858
+ }
859
+ }
860
+
861
+ let hasBranch = rootCount > 1;
862
+ if (!hasBranch) {
863
+ for (const count of childCounts.values()) {
864
+ if (count > 1) {
865
+ hasBranch = true;
866
+ break;
867
+ }
868
+ }
869
+ }
870
+ if (!hasBranch) return [];
871
+
872
+ const keep = new Set<string>();
873
+ for (const entry of entries) {
874
+ const childCount = childCounts.get(entry.id) ?? 0;
875
+ const siblingCount = entry.parentId ? (childCounts.get(entry.parentId) ?? 0) : rootCount;
876
+ if (!entry.parentId || childCount === 0 || childCount > 1 || siblingCount > 1) {
877
+ keep.add(entry.id);
878
+ }
879
+ }
880
+
881
+ const nodes = new Map<string, SessionTreeNode>();
882
+ const roots: SessionTreeNode[] = [];
883
+ for (const entry of entries) {
884
+ if (!keep.has(entry.id)) continue;
885
+ nodes.set(entry.id, { entry, children: [] });
886
+ }
887
+
888
+ for (const entry of entries) {
889
+ if (!keep.has(entry.id)) continue;
890
+ const node = nodes.get(entry.id);
891
+ if (!node) continue;
892
+ let parentId = entry.parentId;
893
+ while (parentId && !keep.has(parentId)) {
894
+ parentId = entriesById.get(parentId)?.parentId ?? null;
895
+ }
896
+ if (parentId && nodes.has(parentId)) {
897
+ nodes.get(parentId)!.children.push(node);
898
+ } else {
899
+ roots.push(node);
900
+ }
901
+ }
902
+ return roots;
903
+ }