farming-code 2.2.6

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 (453) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/README.zh_cn.md +329 -0
  4. package/SECURITY.md +29 -0
  5. package/THIRD_PARTY_NOTICES.md +106 -0
  6. package/backend/acp/client-services.js +312 -0
  7. package/backend/acp/patch-decisions.js +137 -0
  8. package/backend/acp/permission-security.js +108 -0
  9. package/backend/acp-runtime.js +1144 -0
  10. package/backend/acp-session-state.js +448 -0
  11. package/backend/acp-transcript.js +169 -0
  12. package/backend/agent-activity.js +186 -0
  13. package/backend/agent-env.js +250 -0
  14. package/backend/agent-json-stream.js +268 -0
  15. package/backend/agent-manager.js +4259 -0
  16. package/backend/agent-memory-report.js +583 -0
  17. package/backend/agent-order.js +162 -0
  18. package/backend/agent-provider-session.js +287 -0
  19. package/backend/agent-session-history.js +888 -0
  20. package/backend/app-server-api.js +917 -0
  21. package/backend/async-cache.js +94 -0
  22. package/backend/auth.js +178 -0
  23. package/backend/claude-settings.js +121 -0
  24. package/backend/cli-agents.js +484 -0
  25. package/backend/codex-app-server-home.js +39 -0
  26. package/backend/codex-app-server-runtime.js +901 -0
  27. package/backend/codex-context-window.js +189 -0
  28. package/backend/codex-models.js +229 -0
  29. package/backend/codex-rollout-follower.js +243 -0
  30. package/backend/codex-session-history.js +553 -0
  31. package/backend/codex-transcript-sanitizer.js +103 -0
  32. package/backend/codex-transcript.js +2341 -0
  33. package/backend/command-runner-child.js +67 -0
  34. package/backend/config-manager.js +815 -0
  35. package/backend/control-api.js +151 -0
  36. package/backend/data/chinese-poetic-words.json +8223 -0
  37. package/backend/data/poetic-word-sources/china.json +49209 -0
  38. package/backend/data/poetic-word-sources/india.json +9763 -0
  39. package/backend/data/poetic-word-sources/japan.json +8455 -0
  40. package/backend/executable-discovery.js +207 -0
  41. package/backend/farming-app-cli.js +790 -0
  42. package/backend/farming-cli.js +400 -0
  43. package/backend/farming-session-store.js +373 -0
  44. package/backend/haiku-token.js +772 -0
  45. package/backend/index-html.js +49 -0
  46. package/backend/input-parts.js +54 -0
  47. package/backend/input-routing.js +24 -0
  48. package/backend/json-cli-runtime.js +161 -0
  49. package/backend/local-session-engine.js +620 -0
  50. package/backend/main-agent-skills.js +292 -0
  51. package/backend/main-page-session.js +119 -0
  52. package/backend/native-pty-host-client.js +516 -0
  53. package/backend/native-pty-host-path.js +22 -0
  54. package/backend/native-pty-host.js +816 -0
  55. package/backend/native-session-engine.js +196 -0
  56. package/backend/network.js +23 -0
  57. package/backend/npm-update-helper.js +196 -0
  58. package/backend/packaged-node-pty.js +79 -0
  59. package/backend/provider-session-id.js +47 -0
  60. package/backend/qr-share-tickets.js +100 -0
  61. package/backend/review-diff-router.js +180 -0
  62. package/backend/review-diff-service.js +1270 -0
  63. package/backend/review-session-router.js +74 -0
  64. package/backend/review-session-service.js +432 -0
  65. package/backend/review-session-store.js +144 -0
  66. package/backend/review-state-router.js +116 -0
  67. package/backend/review-state-store.js +287 -0
  68. package/backend/run-history-store.js +70 -0
  69. package/backend/server.js +2446 -0
  70. package/backend/session-engine-bridge.js +110 -0
  71. package/backend/session-engine-router.js +66 -0
  72. package/backend/session-engine.js +39 -0
  73. package/backend/shell-busy-integration.js +472 -0
  74. package/backend/slash-command-discovery.js +296 -0
  75. package/backend/storage-layout.js +101 -0
  76. package/backend/system-monitor.js +77 -0
  77. package/backend/terminal-runtime-cleanup.js +80 -0
  78. package/backend/terminal-screen-state.js +240 -0
  79. package/backend/terminal-screen-worker-pool.js +165 -0
  80. package/backend/terminal-screen-worker-thread.js +161 -0
  81. package/backend/terminal-screen-worker.js +255 -0
  82. package/backend/terminal-status.js +356 -0
  83. package/backend/theme-manager.js +145 -0
  84. package/backend/update-service.js +1183 -0
  85. package/backend/usage-forecast.js +86 -0
  86. package/backend/usage-monitor.js +1357 -0
  87. package/backend/workspace-discovery.js +311 -0
  88. package/backend/workspace-file-router.js +431 -0
  89. package/backend/workspace-file-service.js +2765 -0
  90. package/bin/farming +5 -0
  91. package/config/farming.deploy.env.example +13 -0
  92. package/config/farming.install.env.example +17 -0
  93. package/dist/assets/App-BrP-ENHg.css +1 -0
  94. package/dist/assets/App-Dza5yEDe.js +124 -0
  95. package/dist/assets/FileEditorMarkdownPreview-DRDNQ8mX.css +1 -0
  96. package/dist/assets/FileEditorMarkdownPreview-elKWc8Im.js +404 -0
  97. package/dist/assets/FileEditorPane-BQLocXOp.js +5 -0
  98. package/dist/assets/IconGlyphs-RkMAdBXF.js +1 -0
  99. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  100. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  101. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  102. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  103. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  104. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  105. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  106. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  107. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  108. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  109. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  110. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  111. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  112. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  113. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  114. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  115. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  116. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  117. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  118. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  119. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  120. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  121. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  122. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  123. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  124. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  125. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  126. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  127. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  128. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  129. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  130. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  131. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  132. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  133. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  134. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  135. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  136. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  137. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  138. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  139. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  140. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  141. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  142. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  143. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  144. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  145. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  146. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  147. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  148. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  149. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  150. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  151. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  152. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  153. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  154. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  155. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  156. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  157. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  158. package/dist/assets/ProjectFilesSection-lS1azbgj.js +12 -0
  159. package/dist/assets/ReviewPage-DgnmgU2T.js +3 -0
  160. package/dist/assets/abap-Cc5D5h5i.js +1 -0
  161. package/dist/assets/abnfDiagram-VRR7QNED-BgawzDQT.js +1 -0
  162. package/dist/assets/apex-BKopwLmx.js +1 -0
  163. package/dist/assets/api-D1lyBYIQ.js +1 -0
  164. package/dist/assets/arc-CWoVlxJw.js +1 -0
  165. package/dist/assets/architecture-TIHT7OUA-CbJBFh3w.js +1 -0
  166. package/dist/assets/architectureDiagram-ZJ3FMSHR-DUShwPg0.js +36 -0
  167. package/dist/assets/array-HeX70jSN.js +1 -0
  168. package/dist/assets/azcli-DI-C34WA.js +1 -0
  169. package/dist/assets/bat-Cbp6u42z.js +1 -0
  170. package/dist/assets/bicep-4QA1jTCM.js +2 -0
  171. package/dist/assets/blockDiagram-677ZJIJ3-CeGZviXR.js +132 -0
  172. package/dist/assets/c4Diagram-LMCZKHZV-CLhYht6S.js +10 -0
  173. package/dist/assets/cameligo-Dda_adPs.js +1 -0
  174. package/dist/assets/channel-Uw90AO0b.js +1 -0
  175. package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +1 -0
  176. package/dist/assets/chunk-32BRIVSS-LFYpT-Cu.js +1 -0
  177. package/dist/assets/chunk-52WLFC77-BH6jDw9J.js +10 -0
  178. package/dist/assets/chunk-5FCAYU7R-C7AmIhYv.js +1 -0
  179. package/dist/assets/chunk-5HE753X5-C8cXaouP.js +1 -0
  180. package/dist/assets/chunk-5JV3BV7I-C1PtimcA.js +1 -0
  181. package/dist/assets/chunk-5TONJI2A-_81zUME8.js +2 -0
  182. package/dist/assets/chunk-5VM5RSS4-Dlw7AXWp.js +15 -0
  183. package/dist/assets/chunk-7BUUIJ7U-ZBRhjLOh.js +1 -0
  184. package/dist/assets/chunk-BIQX33UG-CWmzxeAw.js +1 -0
  185. package/dist/assets/chunk-C7G6YPKG-B_mIUlb7.js +1 -0
  186. package/dist/assets/chunk-CQNSW5MT-CMayolwI.js +1 -0
  187. package/dist/assets/chunk-CYSBUYHQ-lSPbrL3Z.js +1 -0
  188. package/dist/assets/chunk-DECur_0Z.js +1 -0
  189. package/dist/assets/chunk-EMLP6XTP-D9-jtCnM.js +1 -0
  190. package/dist/assets/chunk-EX3LRPZG-C63hVuAJ.js +231 -0
  191. package/dist/assets/chunk-FWX5IMBZ-CboX-WF_.js +2 -0
  192. package/dist/assets/chunk-HOUHSVGY-BOgSd6Un.js +1 -0
  193. package/dist/assets/chunk-ICXQ74PX-C0K-pgZt.js +2 -0
  194. package/dist/assets/chunk-JG7HCLWE-Cib606u_.js +2 -0
  195. package/dist/assets/chunk-JWPE2WC7-DSyBRaRG.js +1 -0
  196. package/dist/assets/chunk-KEIR6QF5-CNOHik0T.js +161 -0
  197. package/dist/assets/chunk-MOJQB5TN-CVMfwN_7.js +88 -0
  198. package/dist/assets/chunk-MOZMSUNE-D6JeZqmE.js +1 -0
  199. package/dist/assets/chunk-OGEWGWER-DLT3pxyw.js +1 -0
  200. package/dist/assets/chunk-OSBZ3O6U-C0W8KSeg.js +1 -0
  201. package/dist/assets/chunk-PUDLZKDR-Cd079yIQ.js +156 -0
  202. package/dist/assets/chunk-Q4XR5HBZ-D0jHa6SO.js +70 -0
  203. package/dist/assets/chunk-QBLGF6JB-p4omp7uO.js +1 -0
  204. package/dist/assets/chunk-R7FJI6CG-CZ_p9hSr.js +1 -0
  205. package/dist/assets/chunk-RYQCIY6F-CZA9pvFX.js +1 -0
  206. package/dist/assets/chunk-U6XO7XAA-CcXKsWxp.js +2 -0
  207. package/dist/assets/chunk-V7JOEXUC-CVDtR75H.js +206 -0
  208. package/dist/assets/chunk-VAUOI2AC-B-W2_9Ot.js +1 -0
  209. package/dist/assets/chunk-VR4S4FIN-FD3kWh32.js +1 -0
  210. package/dist/assets/chunk-WYO6CB5R-C2bGaS5r.js +127 -0
  211. package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +1 -0
  212. package/dist/assets/chunk-Y2CYZVJY-Di0sYzi1.js +1 -0
  213. package/dist/assets/chunk-YOTPTUD7-BXHmEk89.js +1 -0
  214. package/dist/assets/chunk-ZGVPDNZ5-ymgts0zo.js +62 -0
  215. package/dist/assets/chunk-ZIRB5QZD-DncwlQQc.js +32 -0
  216. package/dist/assets/classDiagram-OUVF2IWQ-DKW_dtku.js +1 -0
  217. package/dist/assets/classDiagram-v2-EOCWNBFH-C8Fi7-yv.js +1 -0
  218. package/dist/assets/clojure-CvY8A83L.js +1 -0
  219. package/dist/assets/code-dark-Cjb1QGhu.css +1 -0
  220. package/dist/assets/code-mobile-3C2Rr-CN.css +1 -0
  221. package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  222. package/dist/assets/coffee-4LX2Sv8A.js +1 -0
  223. package/dist/assets/core-ZlAPicox.js +3 -0
  224. package/dist/assets/cose-bilkent-JH36ORCC-BNIAz0nN.js +1 -0
  225. package/dist/assets/cpp-D4AzSV28.js +1 -0
  226. package/dist/assets/csharp-C4nWN4Yd.js +1 -0
  227. package/dist/assets/csp-CiEaaS5Z.js +1 -0
  228. package/dist/assets/css-DXxBRsbk.js +3 -0
  229. package/dist/assets/css.worker-DyDu5ynT.js +89 -0
  230. package/dist/assets/cssMode-DDWxTPm_.js +1 -0
  231. package/dist/assets/cynefin-VYW2F7L2-DgXGHlRt.js +1 -0
  232. package/dist/assets/cynefinDiagram-TSTJHNR4-CwpobXIl.js +62 -0
  233. package/dist/assets/cypher-DOMzqyFA.js +1 -0
  234. package/dist/assets/cytoscape.esm-Cl8iC3D7.js +321 -0
  235. package/dist/assets/dagre-DXlcHgr3.js +1 -0
  236. package/dist/assets/dagre-VKFMJZFB-Dkhlfjna.js +4 -0
  237. package/dist/assets/dart-BWS7MXt_.js +1 -0
  238. package/dist/assets/defaultLocale-GJwWH1Jr.js +1 -0
  239. package/dist/assets/diagram-FQU43EPY-hA8dicp6.js +3 -0
  240. package/dist/assets/diagram-G47NLZAW-BTY6JTzO.js +24 -0
  241. package/dist/assets/diagram-NH7WQ7WH-xkaHHX9M.js +24 -0
  242. package/dist/assets/diagram-OA4YK3LP-gk_fg9UU.js +30 -0
  243. package/dist/assets/diagram-WEI45ONY-BvC8xd6T.js +41 -0
  244. package/dist/assets/dist-ClAL-Tv5.js +1 -0
  245. package/dist/assets/dockerfile-Baf4U3xT.js +1 -0
  246. package/dist/assets/ebnfDiagram-CCIWWBDH-CR7RaNat.js +1 -0
  247. package/dist/assets/ecl-CT7udlkQ.js +1 -0
  248. package/dist/assets/editor-CtbFZlk0.css +1 -0
  249. package/dist/assets/editor.api2-B9h6iFPK.js +872 -0
  250. package/dist/assets/editor.worker-Dpt_xPrs.js +26 -0
  251. package/dist/assets/elixir-CIbAZw5l.js +1 -0
  252. package/dist/assets/erDiagram-Q63AITRT-_M-JFPbj.js +85 -0
  253. package/dist/assets/eventmodeling-45OFAUF4-DXYTQ2nt.js +1 -0
  254. package/dist/assets/file-icons-EFUGSSwf.js +1 -0
  255. package/dist/assets/flow9-Dqiw45WV.js +1 -0
  256. package/dist/assets/flowDiagram-23GEKE2U-DAiS5Y7G.js +1 -0
  257. package/dist/assets/freemarker2-QBsTERH-.js +3 -0
  258. package/dist/assets/fsharp-E8E2L9jU.js +1 -0
  259. package/dist/assets/ganttDiagram-NO4QXBWP-Bl8P1yf_.js +292 -0
  260. package/dist/assets/gitGraph-TEB2WS4Q-CzTBjEvV.js +1 -0
  261. package/dist/assets/gitGraphDiagram-IHSO6WYX-DoHa5ysz.js +106 -0
  262. package/dist/assets/go-CouVSN4D.js +1 -0
  263. package/dist/assets/graphlib-CbSvZcZU.js +1 -0
  264. package/dist/assets/graphql-a6h6h5-t.js +1 -0
  265. package/dist/assets/handlebars-CuoxPo5A.js +1 -0
  266. package/dist/assets/hcl-vso3e6N-.js +1 -0
  267. package/dist/assets/html-gX5PEu0Q.js +1 -0
  268. package/dist/assets/html.worker-IWNXhnIC.js +502 -0
  269. package/dist/assets/htmlMode-DIWUbUxd.js +1 -0
  270. package/dist/assets/index-BpHPeJf0.js +10 -0
  271. package/dist/assets/info-DKCQHKI2-DznP7j3s.js +1 -0
  272. package/dist/assets/infoDiagram-FWYZ7A6U-DSFcSuUs.js +2 -0
  273. package/dist/assets/ini-SwZZkotR.js +1 -0
  274. package/dist/assets/init-BPLPMQ3Y.js +1 -0
  275. package/dist/assets/ishikawaDiagram-FXEZZL3T-DdkqullI.js +70 -0
  276. package/dist/assets/java-CtRLC4_3.js +1 -0
  277. package/dist/assets/javascript-BYKGIUGs.js +1 -0
  278. package/dist/assets/journeyDiagram-5HDEW3XC-B4hRnQJr.js +139 -0
  279. package/dist/assets/json.worker-c4h5s80L.js +58 -0
  280. package/dist/assets/jsonMode-AFmQQP6u.js +7 -0
  281. package/dist/assets/jsx-runtime-BthL93pI.js +1 -0
  282. package/dist/assets/julia-CuJWFKQH.js +1 -0
  283. package/dist/assets/kanban-definition-HUTT4EX6-tm2FZB2E.js +89 -0
  284. package/dist/assets/katex-C8DPyI6s.js +257 -0
  285. package/dist/assets/kotlin-B_kBhsre.js +1 -0
  286. package/dist/assets/less-CtrHIfz3.js +2 -0
  287. package/dist/assets/lexon-yVt1pJLu.js +1 -0
  288. package/dist/assets/line-CmsjK4kn.js +1 -0
  289. package/dist/assets/linear-Dld3_ERu.js +1 -0
  290. package/dist/assets/liquid-DBIl-f0z.js +1 -0
  291. package/dist/assets/lspLanguageFeatures-DK6tqicV.js +4 -0
  292. package/dist/assets/lua-prOwgJ4U.js +1 -0
  293. package/dist/assets/m3-DhrdTQ84.js +1 -0
  294. package/dist/assets/main-DTJwH51M.css +1 -0
  295. package/dist/assets/map-LZWWoeWa.js +1 -0
  296. package/dist/assets/markdown-DoI5HwSM.js +1 -0
  297. package/dist/assets/mdx-DS-bVtZN.js +1 -0
  298. package/dist/assets/mermaid-parser.core-BQtepoLd.js +4 -0
  299. package/dist/assets/mermaid.core-C7UT_EEL.js +11 -0
  300. package/dist/assets/mindmap-definition-LN4V7U3C-DplfjGms.js +96 -0
  301. package/dist/assets/mips-Dye5jsBU.js +1 -0
  302. package/dist/assets/monaco.contribution-CyLosfZI.js +2 -0
  303. package/dist/assets/msdax-TkNOyIL8.js +1 -0
  304. package/dist/assets/mysql-CEc-RKZF.js +1 -0
  305. package/dist/assets/objective-c-CqqCsQz0.js +1 -0
  306. package/dist/assets/ordinal-BLrOss5K.js +1 -0
  307. package/dist/assets/packet-7NZHBO7P-BDEPOPha.js +1 -0
  308. package/dist/assets/pascal-DO_pj8yg.js +1 -0
  309. package/dist/assets/pascaligo-1cbdZdF3.js +1 -0
  310. package/dist/assets/path-YdFzr2W6.js +1 -0
  311. package/dist/assets/pegDiagram-2B236MQR-lu3SdkQs.js +1 -0
  312. package/dist/assets/perl-B_N3PYfC.js +1 -0
  313. package/dist/assets/pgsql-9iYk1FQ7.js +1 -0
  314. package/dist/assets/php-rO2sgoUM.js +1 -0
  315. package/dist/assets/pie-RZYD4A2V-DWIrwPJQ.js +1 -0
  316. package/dist/assets/pieDiagram-ENE6RG2P-3fHbmDf9.js +39 -0
  317. package/dist/assets/pla-Cbgv60kq.js +1 -0
  318. package/dist/assets/postiats-y6xrhy_M.js +1 -0
  319. package/dist/assets/powerquery-BaD8gWNE.js +1 -0
  320. package/dist/assets/powershell-BMhG4JVe.js +1 -0
  321. package/dist/assets/preload-helper-mJowh_hw.js +1 -0
  322. package/dist/assets/protobuf-CdOP5kN3.js +2 -0
  323. package/dist/assets/pug-Ck60hNF9.js +1 -0
  324. package/dist/assets/python-C93q3xXz.js +1 -0
  325. package/dist/assets/qoder-C9LmmOSf.svg +1 -0
  326. package/dist/assets/qoder-Cf9gl0Y5.svg +1 -0
  327. package/dist/assets/qoder-gHCinseV.svg +1 -0
  328. package/dist/assets/qrcode-DV-_rjU8.js +3 -0
  329. package/dist/assets/qsharp-BcaKFfP-.js +1 -0
  330. package/dist/assets/quadrantDiagram-ABIIQ3AL-a5qoWz-X.js +7 -0
  331. package/dist/assets/r-rFNes0iK.js +1 -0
  332. package/dist/assets/radar-I7S5WNFK-B7OaD8vp.js +1 -0
  333. package/dist/assets/railroad-3IZDKUUU-BXW2nuCA.js +1 -0
  334. package/dist/assets/railroad-abnf-AHOZXSZD-Buw9QG2r.js +1 -0
  335. package/dist/assets/railroad-ebnf-EBAXGLYW-BjSi4sWJ.js +1 -0
  336. package/dist/assets/railroad-peg-LSFZ7HO6-DN1wIVhS.js +1 -0
  337. package/dist/assets/railroadDiagram-RFXS5EU6-D2TOuXW9.js +1 -0
  338. package/dist/assets/razor-CBifNGXO.js +1 -0
  339. package/dist/assets/react-dom-gtU6X-Hj.js +1 -0
  340. package/dist/assets/redis-DkpQ3K6G.js +1 -0
  341. package/dist/assets/redshift-Di9N5TI3.js +1 -0
  342. package/dist/assets/requirementDiagram-TGXJPOKE-LQBWpUvd.js +84 -0
  343. package/dist/assets/restructuredtext-By5ZzLt6.js +1 -0
  344. package/dist/assets/review-BcnwQtJv.css +1 -0
  345. package/dist/assets/rough.esm-c4PR5shF.js +1 -0
  346. package/dist/assets/ruby-fF5zGiLp.js +1 -0
  347. package/dist/assets/rust-C5b3AYY6.js +1 -0
  348. package/dist/assets/sankeyDiagram-HTMAVEWB-B1ohYoyK.js +40 -0
  349. package/dist/assets/sb-CD07UNRs.js +1 -0
  350. package/dist/assets/scala-jtn1ip9G.js +1 -0
  351. package/dist/assets/scheme-C4vqMkn_.js +1 -0
  352. package/dist/assets/scss-I4Glxjbz.js +3 -0
  353. package/dist/assets/sequenceDiagram-DBY2YBRQ-C3vM-hMq.js +162 -0
  354. package/dist/assets/shell-DubUcuns.js +1 -0
  355. package/dist/assets/sizeCapture-X5ZJPWSS-daTB6OcF.js +1 -0
  356. package/dist/assets/solidity-CNy2KSl2.js +1 -0
  357. package/dist/assets/sophia-D9_URt84.js +1 -0
  358. package/dist/assets/sparql-DviXpm4C.js +1 -0
  359. package/dist/assets/sql-vDRbAZgK.js +1 -0
  360. package/dist/assets/src-WBW1QxJt.js +1 -0
  361. package/dist/assets/st-CnpnRqA7.js +1 -0
  362. package/dist/assets/stateDiagram-2N3HPSRC-BvFhOryI.js +1 -0
  363. package/dist/assets/stateDiagram-v2-6OUMAXLB-WMrz4PNo.js +1 -0
  364. package/dist/assets/swift-D_g8Ganz.js +1 -0
  365. package/dist/assets/swimlanes-5IMT3BWC-DKF_b-2s.js +2 -0
  366. package/dist/assets/swimlanesDiagram-G3AALYLV-BGswC9oC.js +8 -0
  367. package/dist/assets/systemverilog-OuZa5h1M.js +1 -0
  368. package/dist/assets/tcl-DBm9ucsV.js +1 -0
  369. package/dist/assets/timeline-definition-FHXFAJF6-vAs11ux_.js +120 -0
  370. package/dist/assets/tokens-BwenNn5Q.css +1 -0
  371. package/dist/assets/treeView-QDETBFTQ-BVfICaZQ.js +1 -0
  372. package/dist/assets/treemap-6X3UGDF4-oWHPqo17.js +1 -0
  373. package/dist/assets/ts.worker-BwM5Ha3L.js +67719 -0
  374. package/dist/assets/tsMode-DGZTlTj8.js +11 -0
  375. package/dist/assets/twig-BRzEqX5Y.js +1 -0
  376. package/dist/assets/typescript-KJKfYO7K.js +1 -0
  377. package/dist/assets/typespec-D4ZgGeX8.js +1 -0
  378. package/dist/assets/useWorkspaceMenuKeyboard-CneKAZUJ.js +1 -0
  379. package/dist/assets/vb-DcP4s6WS.js +1 -0
  380. package/dist/assets/vennDiagram-L72KCM5P-BybTX_9B.js +34 -0
  381. package/dist/assets/wardley-OPB4EBWU-Dg8t5y3s.js +1 -0
  382. package/dist/assets/wardleyDiagram-EHGQE667-CicUmMBR.js +78 -0
  383. package/dist/assets/wgsl-CnRJ_QUf.js +298 -0
  384. package/dist/assets/workers-BSQRIkqa.js +1 -0
  385. package/dist/assets/workspace-view-state-CTyDzk2D.js +1 -0
  386. package/dist/assets/xml-C8FAcN6j.js +1 -0
  387. package/dist/assets/xychartDiagram-FW5EYKEG-BhWDyCX-.js +7 -0
  388. package/dist/assets/yaml-BQ4NrRhG.js +1 -0
  389. package/dist/assets/zsh-CLpveKlF.svg +1 -0
  390. package/dist/assets/zsh-FxSpMPbz.svg +1 -0
  391. package/dist/farming-2/app-icon-v2-180.png +0 -0
  392. package/dist/farming-2/app-icon-v2-192.png +0 -0
  393. package/dist/farming-2/app-icon-v2-512.png +0 -0
  394. package/dist/farming-2/app-icon-v2-maskable-512.png +0 -0
  395. package/dist/farming-2/app-icon-v2-master.png +0 -0
  396. package/dist/farming-2/favicon-v2-16.png +0 -0
  397. package/dist/farming-2/favicon-v2-32.png +0 -0
  398. package/dist/farming-2/favicon-v2-48.png +0 -0
  399. package/dist/farming-2/favicon-v2-64.png +0 -0
  400. package/dist/farming-2/favicon-v2.ico +0 -0
  401. package/dist/farming-2/favicon.ico +0 -0
  402. package/dist/farming-2/github-social-preview.png +0 -0
  403. package/dist/farming-2/images/avatar-watercolor-v1-bee-garden.png +0 -0
  404. package/dist/farming-2/images/avatar-watercolor-v2.png +0 -0
  405. package/dist/farming-2/images/avatar-watercolor-v3.png +0 -0
  406. package/dist/farming-2/legacy-icons-v1/favicon.ico +1 -0
  407. package/dist/farming-2/legacy-icons-v1/logo-16.png +0 -0
  408. package/dist/farming-2/legacy-icons-v1/logo-180.png +0 -0
  409. package/dist/farming-2/legacy-icons-v1/logo-192.png +0 -0
  410. package/dist/farming-2/legacy-icons-v1/logo-32.png +0 -0
  411. package/dist/farming-2/legacy-icons-v1/logo-48.png +0 -0
  412. package/dist/farming-2/legacy-icons-v1/logo-512.png +0 -0
  413. package/dist/farming-2/legacy-icons-v1/logo-64.png +0 -0
  414. package/dist/farming-2/logo-16.png +0 -0
  415. package/dist/farming-2/logo-180.png +0 -0
  416. package/dist/farming-2/logo-192.png +0 -0
  417. package/dist/farming-2/logo-32.png +0 -0
  418. package/dist/farming-2/logo-48.png +0 -0
  419. package/dist/farming-2/logo-512.png +0 -0
  420. package/dist/farming-2/logo-64.png +0 -0
  421. package/dist/farming-2/site.webmanifest +27 -0
  422. package/dist/farming-2/style_capsules/watercolor-pastoral.json +94 -0
  423. package/dist/index.html +27 -0
  424. package/frontend/ghostty-loader.js +24 -0
  425. package/frontend/runtime-paths.js +28 -0
  426. package/frontend/session-bridge.js +82 -0
  427. package/frontend/session-modal-bridge.js +331 -0
  428. package/frontend/skin-bridge.js +25 -0
  429. package/frontend/skins/crt/app.js +6940 -0
  430. package/frontend/skins/crt/assets/branding/farming-crt-icon.svg +11 -0
  431. package/frontend/skins/crt/assets/branding/farming-crt-logo-v1.png +0 -0
  432. package/frontend/skins/crt/assets/effects/phosphor-noise.svg +11 -0
  433. package/frontend/skins/crt/assets/fonts/departure-mono/DepartureMonoNerdFontMono-Regular.otf +0 -0
  434. package/frontend/skins/crt/assets/fonts/departure-mono/LICENSE +93 -0
  435. package/frontend/skins/crt/effects.js +8 -0
  436. package/frontend/skins/crt/index.html +1712 -0
  437. package/frontend/skins/crt/styles/billing.css +1003 -0
  438. package/frontend/skins/crt/styles/effects.css +171 -0
  439. package/frontend/skins/crt/styles/history.css +215 -0
  440. package/frontend/skins/crt/styles/monochrome-green.css +509 -0
  441. package/frontend/skins/crt/styles/search.css +271 -0
  442. package/frontend/terminal-bridge.js +192 -0
  443. package/frontend/theme-loader.js +84 -0
  444. package/frontend/themes/terminal/theme.json +24 -0
  445. package/frontend/vendor/ghostty-web/LICENSE +21 -0
  446. package/frontend/vendor/ghostty-web/__vite-browser-external-2447137e.js +4 -0
  447. package/frontend/vendor/ghostty-web/ghostty-vt.wasm +0 -0
  448. package/frontend/vendor/ghostty-web/ghostty-web.js +2963 -0
  449. package/frontend/vendor/ghostty-web/ghostty-web.umd.cjs +13 -0
  450. package/frontend/vendor/ghostty-web/index.d.ts +1842 -0
  451. package/index.html +23 -0
  452. package/package.json +144 -0
  453. package/scripts/sync-ghostty-vendor.js +66 -0
@@ -0,0 +1,2341 @@
1
+ const fs = require('fs');
2
+ const fsp = require('fs/promises');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { findCodexRolloutFile } = require('./codex-rollout-follower');
6
+ const {
7
+ heartbeatAssistantMessage,
8
+ heartbeatUserMessage,
9
+ isCodexInjectedContextMessage,
10
+ stripCodexInternalContextBlocks,
11
+ } = require('./codex-transcript-sanitizer');
12
+
13
+ // @deprecated `readCodexTranscript` below is the legacy JSONL file reader.
14
+ // App Server uses the generic event-to-turn projection exported here instead.
15
+ const DEFAULT_MAX_READ_BYTES = 32 * 1024 * 1024;
16
+ const DEFAULT_MAX_TURNS = 240;
17
+ const USER_MESSAGE_BEGIN = '## My request for Codex:';
18
+ const MAX_USER_IMAGES_PER_TURN = 6;
19
+ const MAX_USER_IMAGE_URL_LENGTH = 5 * 1024 * 1024;
20
+ const MAX_LOCAL_IMAGE_BYTES = 5 * 1024 * 1024;
21
+ const MAX_USER_FILES_PER_TURN = 6;
22
+ const MAX_USER_FILE_CONTENT_CHARS = 50_000;
23
+
24
+ const LOCAL_IMAGE_MIME_BY_EXT = {
25
+ '.gif': 'image/gif',
26
+ '.jpeg': 'image/jpeg',
27
+ '.jpg': 'image/jpeg',
28
+ '.png': 'image/png',
29
+ '.svg': 'image/svg+xml',
30
+ '.webp': 'image/webp',
31
+ };
32
+
33
+ function normalizeText(value) {
34
+ return String(value || '')
35
+ .replace(/\r\n/g, '\n')
36
+ .replace(/\r/g, '\n')
37
+ .trim();
38
+ }
39
+
40
+ function normalizeDeltaText(value) {
41
+ return String(value || '')
42
+ .replace(/\r\n/g, '\n')
43
+ .replace(/\r/g, '\n');
44
+ }
45
+
46
+ function extractMemoryCitationBlock(value) {
47
+ const text = normalizeText(value);
48
+ if (!text) return { text: '', detail: '' };
49
+ const blocks = [];
50
+ const cleaned = text.replace(/<oai-mem-citation>[\s\S]*?<\/oai-mem-citation>/g, (block) => {
51
+ blocks.push(block);
52
+ return '';
53
+ });
54
+ return {
55
+ text: normalizeText(cleaned),
56
+ detail: blocks.map(formatRawMemoryCitationBlock).filter(Boolean).join('\n\n'),
57
+ };
58
+ }
59
+
60
+ function formatRawMemoryCitationBlock(block) {
61
+ return normalizeText(block)
62
+ .replace(/<\/?oai-mem-citation>/g, '')
63
+ .replace(/<citation_entries>/g, 'citation entries:\n')
64
+ .replace(/<\/citation_entries>/g, '')
65
+ .replace(/<rollout_ids>/g, '\nrollout ids:\n')
66
+ .replace(/<\/rollout_ids>/g, '')
67
+ .replace(/<[^>]+>/g, '')
68
+ .split('\n')
69
+ .map(line => line.trim())
70
+ .filter(Boolean)
71
+ .join('\n');
72
+ }
73
+
74
+ function detailForMemoryCitation(memoryCitation) {
75
+ if (!memoryCitation || typeof memoryCitation !== 'object') return '';
76
+ const rows = [];
77
+ const entries = Array.isArray(memoryCitation.entries) ? memoryCitation.entries : [];
78
+ for (const entry of entries) {
79
+ if (!entry || typeof entry !== 'object') continue;
80
+ const pathLabel = compactInline(entry.path || '');
81
+ const lineStart = entry.lineStart ?? entry.line_start;
82
+ const lineEnd = entry.lineEnd ?? entry.line_end;
83
+ const lineLabel = Number.isFinite(lineStart)
84
+ ? `:${lineStart}${Number.isFinite(lineEnd) && lineEnd !== lineStart ? `-${lineEnd}` : ''}`
85
+ : '';
86
+ const note = compactInline(entry.note || '');
87
+ rows.push(`${pathLabel}${lineLabel}${note ? ` | ${note}` : ''}`);
88
+ }
89
+ const threadIds = Array.isArray(memoryCitation.threadIds)
90
+ ? memoryCitation.threadIds
91
+ : (Array.isArray(memoryCitation.thread_ids) ? memoryCitation.thread_ids : []);
92
+ if (threadIds.length) rows.push(`threads: ${threadIds.map(id => compactInline(id)).filter(Boolean).join(', ')}`);
93
+ return summarizeOutput(rows.join('\n'));
94
+ }
95
+
96
+ function appendMemoryCitation(turn, id, memoryCitation, rawDetail = '') {
97
+ const detail = [detailForMemoryCitation(memoryCitation), rawDetail].filter(Boolean).join('\n\n');
98
+ if (!detail) return;
99
+ appendProcess(turn, {
100
+ id: `${id || turn.id || 'message'}-memory-citation`,
101
+ type: 'citation',
102
+ title: 'Memory citations',
103
+ detail,
104
+ status: 'completed',
105
+ });
106
+ }
107
+
108
+ function finalMessageText(value, turn, id, memoryCitation) {
109
+ const extracted = extractMemoryCitationBlock(value);
110
+ appendMemoryCitation(turn, id, memoryCitation, extracted.detail);
111
+ const heartbeatMessage = heartbeatAssistantMessage(extracted.text);
112
+ if (heartbeatMessage || heartbeatMessage === '') {
113
+ const text = normalizeText(extracted.text);
114
+ if (/^<heartbeat(?:\s+[^>]*)?>[\s\S]*<\/heartbeat>$/i.test(text)) return heartbeatMessage;
115
+ }
116
+ return stripCodexInternalContextBlocks(extracted.text);
117
+ }
118
+
119
+ function stripUserMessagePrefix(value) {
120
+ const text = normalizeText(value);
121
+ const index = text.indexOf(USER_MESSAGE_BEGIN);
122
+ return index >= 0 ? text.slice(index + USER_MESSAGE_BEGIN.length).trim() : text;
123
+ }
124
+
125
+ function isInjectedContextMessage(value) {
126
+ return isCodexInjectedContextMessage(value);
127
+ }
128
+
129
+ function renderedAttachmentKindSet(options = {}) {
130
+ return new Set(Array.isArray(options.renderedAttachmentKinds) ? options.renderedAttachmentKinds : []);
131
+ }
132
+
133
+ function extractComposerFileAttachments(value) {
134
+ const text = normalizeText(value);
135
+ if (!text) return { text: '', files: [] };
136
+ const files = [];
137
+ const pattern = /(^|\n{2,})\s*Attached file:\s*([^\n]*)(?:\n{2,}|\n)([\s\S]*?)(?=\n{2,}\s*Attached (?:file|image):|\n{2,}\s*<image\b|$)/gi;
138
+ const stripped = text.replace(pattern, (match, prefix, rawName, rawContent) => {
139
+ if (files.length < MAX_USER_FILES_PER_TURN) {
140
+ const name = compactInline(rawName, `attachment-${files.length + 1}`);
141
+ const originalContent = String(rawContent || '')
142
+ .replace(/\r\n/g, '\n')
143
+ .replace(/\r/g, '\n')
144
+ .trimEnd();
145
+ const errorMatch = originalContent.trim().match(/^\[([^\]]+)\]$/);
146
+ const truncatedMarker = originalContent.match(/\n{2,}\[File truncated after\s+(\d+)\s+characters\]\s*$/i);
147
+ const contentWithoutMarker = truncatedMarker
148
+ ? originalContent.slice(0, truncatedMarker.index).trimEnd()
149
+ : originalContent;
150
+ const truncated = Boolean(truncatedMarker) || contentWithoutMarker.length > MAX_USER_FILE_CONTENT_CHARS;
151
+ files.push({
152
+ id: `file-${files.length + 1}`,
153
+ name,
154
+ content: errorMatch ? '' : contentWithoutMarker.slice(0, MAX_USER_FILE_CONTENT_CHARS),
155
+ error: errorMatch ? errorMatch[1] : '',
156
+ truncated,
157
+ });
158
+ }
159
+ return prefix || '';
160
+ }).replace(/\n{3,}/g, '\n\n').trim();
161
+ return { text: stripped, files };
162
+ }
163
+
164
+ function findLastTranscriptTimestamp(text) {
165
+ const pattern = /(?:^|\n)(?:[A-Za-z]+day\s+)?\d{1,2}:\d{2}\s*(?:AM|PM)\s*\n/gi;
166
+ let match = null;
167
+ let next;
168
+ while ((next = pattern.exec(text))) match = next;
169
+ if (!match) return null;
170
+ return {
171
+ index: match.index + (match[0].startsWith('\n') ? 1 : 0),
172
+ end: match.index + match[0].length,
173
+ };
174
+ }
175
+
176
+ function extractReferencedPastedTranscriptContext(value) {
177
+ const text = normalizeText(value);
178
+ if (!text || text.length < 1200 || !/\nReferenced pasted text files:\n/i.test(text)) {
179
+ return { text, files: [] };
180
+ }
181
+
182
+ const referenceMatch = text.match(/\nReferenced pasted text files:\n[\s\S]*?(?=\n(?:Sent as goal|Goal blocked|\d{1,2}:\d{2}\s*(?:AM|PM)|$))/i);
183
+ if (!referenceMatch || referenceMatch.index === undefined) return { text, files: [] };
184
+
185
+ const beforeReference = text.slice(0, referenceMatch.index).trimEnd();
186
+ const timestamp = findLastTranscriptTimestamp(beforeReference);
187
+ if (!timestamp || timestamp.index < 400) return { text, files: [] };
188
+
189
+ const pastedContext = beforeReference.slice(0, timestamp.index).trim();
190
+ const userRequest = beforeReference.slice(timestamp.end).trim();
191
+ if (!pastedContext || !userRequest) return { text, files: [] };
192
+
193
+ const referenceBlock = referenceMatch[0].trim();
194
+ const remaining = text.slice(referenceMatch.index + referenceMatch[0].length).trim();
195
+ const trailingContext = remaining
196
+ ? remaining.replace(/^Sent as goal\s*/i, '').trim()
197
+ : '';
198
+ const content = [pastedContext, trailingContext].filter(Boolean).join('\n\n---\n\n');
199
+ const truncated = content.length > MAX_USER_FILE_CONTENT_CHARS;
200
+
201
+ return {
202
+ text: [userRequest, referenceBlock].filter(Boolean).join('\n\n'),
203
+ files: [{
204
+ id: 'file-pasted-transcript-context',
205
+ name: 'pasted-transcript-context.txt',
206
+ content: content.slice(0, MAX_USER_FILE_CONTENT_CHARS),
207
+ truncated,
208
+ }],
209
+ };
210
+ }
211
+
212
+ function extractCodexApprovalTranscriptContext(value) {
213
+ const text = normalizeText(value);
214
+ if (
215
+ !text ||
216
+ text.length < 500 ||
217
+ !/^The following is the Codex agent history(?: whose request action you are assessing| added since your last approval assessment)\b/i.test(text)
218
+ ) {
219
+ return { text, files: [] };
220
+ }
221
+
222
+ const firstParagraph = text.split(/\n{2,}/)[0].trim();
223
+ const truncated = text.length > MAX_USER_FILE_CONTENT_CHARS;
224
+ return {
225
+ text: firstParagraph,
226
+ files: [{
227
+ id: 'file-codex-approval-transcript',
228
+ name: 'codex-approval-transcript.txt',
229
+ content: text.slice(0, MAX_USER_FILE_CONTENT_CHARS),
230
+ truncated,
231
+ }],
232
+ };
233
+ }
234
+
235
+ function parseSubagentNotification(value) {
236
+ const text = normalizeText(value);
237
+ const match = text.match(/^<subagent_notification(?:\s+[^>]*)?>([\s\S]*?)<\/subagent_notification>$/i);
238
+ if (!match) return null;
239
+ const payload = parseJsonValue(match[1]);
240
+ if (!payload || typeof payload !== 'object') {
241
+ return {
242
+ title: 'Subagent update',
243
+ detail: normalizeText(match[1]),
244
+ status: 'completed',
245
+ };
246
+ }
247
+ const status = payload.status && typeof payload.status === 'object' ? payload.status : {};
248
+ const completed = typeof status.completed === 'string' ? status.completed : '';
249
+ const failed = typeof status.failed === 'string' ? status.failed : '';
250
+ const detail = completed || failed || compactInline(status.message || payload.message || '');
251
+ return {
252
+ title: failed ? 'Subagent failed' : completed ? 'Subagent completed' : 'Subagent update',
253
+ detail,
254
+ status: failed ? 'failed' : 'completed',
255
+ };
256
+ }
257
+
258
+ function stripRenderedAttachmentTagBlocks(value, renderedKinds) {
259
+ const text = normalizeText(value);
260
+ if (!text) return '';
261
+ let next = text;
262
+ for (const kind of renderedKinds) {
263
+ const tagName = String(kind || '').replace(/[^a-z0-9_-]/gi, '');
264
+ if (!tagName) continue;
265
+ const pattern = new RegExp(`(^|\\n{2,})\\s*<${tagName}\\b[\\s\\S]*?<\\/${tagName}>\\s*(?=\\n{2,}|$)`, 'gi');
266
+ next = next.replace(pattern, '$1');
267
+ }
268
+ return next
269
+ .replace(/\n{3,}/g, '\n\n')
270
+ .trim();
271
+ }
272
+
273
+ function stripRenderedComposerAttachmentBlocks(value, renderedKinds) {
274
+ const text = normalizeText(value);
275
+ if (!text) return '';
276
+ let next = text;
277
+ for (const kind of renderedKinds) {
278
+ const label = String(kind || '').replace(/[^a-z0-9_-]/gi, '');
279
+ if (!label) continue;
280
+ if (label.toLowerCase() === 'file') {
281
+ next = extractComposerFileAttachments(next).text;
282
+ continue;
283
+ }
284
+ const title = label.charAt(0).toUpperCase() + label.slice(1);
285
+ const pattern = new RegExp(`(^|\\n{2,})\\s*Attached ${label}:[^\\n]*(?:\\n{2,}|\\n)(?:${title} path:|\\[[^\\]]+\\])[^\\n]*(?=\\n{2,}|$)`, 'gi');
286
+ next = next.replace(pattern, '$1');
287
+ }
288
+ return next
289
+ .replace(/\n{3,}/g, '\n\n')
290
+ .trim();
291
+ }
292
+
293
+ function visibleUserMessageText(value, options = {}) {
294
+ const rawText = stripUserMessagePrefix(value);
295
+ const heartbeatMessage = heartbeatUserMessage(rawText);
296
+ if (heartbeatMessage) return heartbeatMessage;
297
+ const text = stripCodexInternalContextBlocks(rawText);
298
+ if (isInjectedContextMessage(text)) return '';
299
+ const renderedKinds = renderedAttachmentKindSet(options);
300
+ return stripRenderedComposerAttachmentBlocks(stripRenderedAttachmentTagBlocks(text, renderedKinds), renderedKinds);
301
+ }
302
+
303
+ function renderedAttachmentKindsForTurn(turn) {
304
+ const kinds = [];
305
+ if (turn && Array.isArray(turn.userImages) && turn.userImages.length > 0) kinds.push('image');
306
+ if (turn && Array.isArray(turn.userFiles) && turn.userFiles.length > 0) kinds.push('file');
307
+ return kinds;
308
+ }
309
+
310
+ function renderedAttachmentKindsForAttachments({ images = [], files = [] } = {}) {
311
+ const kinds = [];
312
+ if (Array.isArray(images) && images.length > 0) kinds.push('image');
313
+ if (Array.isArray(files) && files.length > 0) kinds.push('file');
314
+ return kinds;
315
+ }
316
+
317
+ function parseJsonValue(value) {
318
+ if (!value) return null;
319
+ if (typeof value === 'object') return value;
320
+ if (typeof value !== 'string') return null;
321
+ try {
322
+ return JSON.parse(value);
323
+ } catch {
324
+ return null;
325
+ }
326
+ }
327
+
328
+ function compactInline(value, fallback = '') {
329
+ const text = normalizeText(value).replace(/\s+/g, ' ');
330
+ return text || fallback;
331
+ }
332
+
333
+ function humanizeType(value, fallback = 'Event') {
334
+ const text = String(value || '')
335
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
336
+ .replace(/[_-]+/g, ' ')
337
+ .trim();
338
+ if (!text) return fallback;
339
+ return text.split(/\s+/)
340
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
341
+ .join(' ');
342
+ }
343
+
344
+ function textFromContent(content) {
345
+ if (typeof content === 'string') return normalizeText(content);
346
+ if (content && typeof content === 'object' && !Array.isArray(content)) {
347
+ if (typeof content.text === 'string') return normalizeText(content.text);
348
+ if (typeof content.content === 'string') return normalizeText(content.content);
349
+ if (Array.isArray(content.content)) return textFromContent(content.content);
350
+ if (Array.isArray(content.content_items)) return textFromContent(content.content_items);
351
+ if (Array.isArray(content.contentItems)) return textFromContent(content.contentItems);
352
+ return '';
353
+ }
354
+ if (!Array.isArray(content)) return '';
355
+ return content
356
+ .map((part) => {
357
+ if (!part || typeof part !== 'object') return '';
358
+ if (typeof part.text === 'string') return part.text;
359
+ if (typeof part.content === 'string') return part.content;
360
+ return '';
361
+ })
362
+ .filter(Boolean)
363
+ .join('\n\n')
364
+ .trim();
365
+ }
366
+
367
+ function imageAltFromContentPart(part, index) {
368
+ const fallback = `Image ${index + 1}`;
369
+ if (!part || typeof part !== 'object') return fallback;
370
+ return compactInline(part.name || part.filename || part.path || part.alt || '', fallback);
371
+ }
372
+
373
+ function imageAltFromPath(filePath, index) {
374
+ const basename = path.basename(String(filePath || ''));
375
+ return basename || `Image ${index + 1}`;
376
+ }
377
+
378
+ function isDisplayableImageUrl(url) {
379
+ if (typeof url !== 'string' || !url) return false;
380
+ if (url.length > MAX_USER_IMAGE_URL_LENGTH) return false;
381
+ return url.startsWith('data:image/') || url.startsWith('http://') || url.startsWith('https://');
382
+ }
383
+
384
+ function imagesFromContent(content) {
385
+ if (!Array.isArray(content)) return [];
386
+ const images = [];
387
+ for (const part of content) {
388
+ if (!part || typeof part !== 'object') continue;
389
+ if (part.type === 'localImage') {
390
+ const image = imageFromLocalPath(part.path, images.length);
391
+ if (image) images.push(image);
392
+ if (images.length >= MAX_USER_IMAGES_PER_TURN) break;
393
+ continue;
394
+ }
395
+ if (part.type !== 'input_image' && part.type !== 'inputImage' && part.type !== 'image') continue;
396
+ const url = part.image_url || part.imageUrl || part.url || part.data;
397
+ if (isDisplayableImageUrl(url)) {
398
+ images.push({
399
+ id: `image-${images.length + 1}`,
400
+ url,
401
+ alt: imageAltFromContentPart(part, images.length),
402
+ });
403
+ } else {
404
+ const localImage = imageFromLocalPath(part.path || part.file || part.localPath, images.length);
405
+ if (localImage) images.push(localImage);
406
+ }
407
+ if (images.length >= MAX_USER_IMAGES_PER_TURN) break;
408
+ }
409
+ return images;
410
+ }
411
+
412
+ function inlineImageFromEntry(entry, index) {
413
+ if (typeof entry === 'string') {
414
+ return isDisplayableImageUrl(entry)
415
+ ? { id: `image-${index + 1}`, url: entry, alt: `Image ${index + 1}` }
416
+ : null;
417
+ }
418
+ if (!entry || typeof entry !== 'object') return null;
419
+ const url = entry.image_url || entry.imageUrl || entry.url || entry.data;
420
+ if (!isDisplayableImageUrl(url)) return imageFromLocalPath(entry.path || entry.file || entry.localPath, index);
421
+ return {
422
+ id: `image-${index + 1}`,
423
+ url,
424
+ alt: imageAltFromContentPart(entry, index),
425
+ };
426
+ }
427
+
428
+ function imageFromValue(value, index, alt = '') {
429
+ if (isDisplayableImageUrl(value)) {
430
+ return { id: `image-${index + 1}`, url: value, alt: compactInline(alt, `Image ${index + 1}`) };
431
+ }
432
+ return imageFromLocalPath(value, index);
433
+ }
434
+
435
+ function imagesFromImageItem(item) {
436
+ if (!item || typeof item !== 'object') return [];
437
+ const candidates = [
438
+ item.result,
439
+ item.src,
440
+ item.url,
441
+ item.saved_path,
442
+ item.savedPath,
443
+ item.path,
444
+ ];
445
+ const images = [];
446
+ for (const candidate of candidates) {
447
+ const image = imageFromValue(candidate, images.length, item.title || item.alt || 'Generated image');
448
+ if (!image || images.some(existing => existing.url === image.url)) continue;
449
+ images.push(image);
450
+ if (images.length >= MAX_USER_IMAGES_PER_TURN) break;
451
+ }
452
+ return images;
453
+ }
454
+
455
+ function imageFromLocalPath(filePath, index) {
456
+ const normalized = String(filePath || '').trim();
457
+ if (!normalized || normalized.includes('\0')) return null;
458
+ const ext = path.extname(normalized).toLowerCase();
459
+ const mime = LOCAL_IMAGE_MIME_BY_EXT[ext];
460
+ if (!mime) return null;
461
+ let stat = null;
462
+ try {
463
+ stat = fs.statSync(normalized);
464
+ } catch {
465
+ return null;
466
+ }
467
+ if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_LOCAL_IMAGE_BYTES) return null;
468
+ try {
469
+ const base64 = fs.readFileSync(normalized).toString('base64');
470
+ return {
471
+ id: `local-image-${index + 1}`,
472
+ url: `data:${mime};base64,${base64}`,
473
+ alt: imageAltFromPath(normalized, index),
474
+ };
475
+ } catch {
476
+ return null;
477
+ }
478
+ }
479
+
480
+ function imagesFromUserMessagePayload(payload) {
481
+ const images = [];
482
+ const appendImage = (image) => {
483
+ if (!image || images.length >= MAX_USER_IMAGES_PER_TURN) return;
484
+ if (images.some(existing => existing.url === image.url)) return;
485
+ images.push({
486
+ ...image,
487
+ id: image.id || `image-${images.length + 1}`,
488
+ });
489
+ };
490
+
491
+ const inlineImages = Array.isArray(payload.images) ? payload.images : [];
492
+ for (const image of imagesFromContent(inlineImages)) {
493
+ appendImage(image);
494
+ }
495
+ for (const entry of inlineImages) {
496
+ appendImage(inlineImageFromEntry(entry, images.length));
497
+ }
498
+
499
+ const localImages = Array.isArray(payload.local_images)
500
+ ? payload.local_images
501
+ : (Array.isArray(payload.localImages) ? payload.localImages : []);
502
+ for (const entry of localImages) {
503
+ const filePath = typeof entry === 'string'
504
+ ? entry
505
+ : (entry && typeof entry === 'object' ? entry.path || entry.file || entry.url : '');
506
+ appendImage(imageFromLocalPath(filePath, images.length));
507
+ }
508
+
509
+ return images;
510
+ }
511
+
512
+ function summarizeStructuredOutput(output) {
513
+ if (typeof output === 'string') return summarizeOutput(output);
514
+ if (Array.isArray(output)) {
515
+ const text = output
516
+ .map(part => {
517
+ if (!part || typeof part !== 'object') return '';
518
+ if (typeof part.text === 'string') return part.text;
519
+ if (typeof part.content === 'string') return part.content;
520
+ if (typeof part.value === 'string') return part.value;
521
+ return JSON.stringify(part);
522
+ })
523
+ .filter(Boolean)
524
+ .join('\n');
525
+ return summarizeOutput(text);
526
+ }
527
+ if (output && typeof output === 'object') {
528
+ if (typeof output.content === 'string') return summarizeOutput(output.content);
529
+ if (Array.isArray(output.content)) return summarizeStructuredOutput(output.content);
530
+ if (Array.isArray(output.content_items)) return summarizeStructuredOutput(output.content_items);
531
+ if (Array.isArray(output.contentItems)) return summarizeStructuredOutput(output.contentItems);
532
+ return summarizeOutput(JSON.stringify(output, null, 2));
533
+ }
534
+ return '';
535
+ }
536
+
537
+ function imagesFromStructuredOutput(output) {
538
+ if (Array.isArray(output)) return imagesFromContent(output);
539
+ if (output && typeof output === 'object') {
540
+ const images = [];
541
+ const append = (image) => {
542
+ if (!image || images.length >= MAX_USER_IMAGES_PER_TURN) return;
543
+ if (images.some(existing => existing.url === image.url)) return;
544
+ images.push(image);
545
+ };
546
+ for (const field of ['content', 'content_items', 'contentItems']) {
547
+ if (!Array.isArray(output[field])) continue;
548
+ for (const image of imagesFromContent(output[field])) append(image);
549
+ }
550
+ return images;
551
+ }
552
+ return [];
553
+ }
554
+
555
+ function commandText(command) {
556
+ if (Array.isArray(command)) return command.map(part => String(part || '')).filter(Boolean).join(' ');
557
+ return normalizeText(command);
558
+ }
559
+
560
+ function titleForParsedCommand(parsed) {
561
+ if (!parsed || typeof parsed !== 'object') return '';
562
+ const kind = String(parsed.type || '').trim();
563
+ const lowerKind = kind.replace(/_/g, '').toLowerCase();
564
+ const commandName = compactInline(parsed.command || parsed.cmd || '');
565
+ if (lowerKind === 'search') {
566
+ const name = compactInline(parsed.query || parsed.name || commandName || parsed.path || '');
567
+ return name ? `Searched ${name}` : 'Searched files';
568
+ }
569
+ const name = compactInline(parsed.name || parsed.path || parsed.query || commandName || '');
570
+ if (lowerKind === 'read') return name ? `Read ${name}` : 'Read file';
571
+ if (lowerKind === 'listfiles') return name ? `Listed ${name}` : 'Listed files';
572
+ if (lowerKind === 'write') return name ? `Edited ${name}` : 'Edited file';
573
+ return '';
574
+ }
575
+
576
+ function titleForCommandExecution(payload) {
577
+ const actions = Array.isArray(payload.command_actions) ? payload.command_actions : payload.commandActions;
578
+ const parsedActions = Array.isArray(actions) ? actions : payload.parsed_cmd;
579
+ const parsed = Array.isArray(parsedActions) && parsedActions.length === 1
580
+ ? titleForParsedCommand(parsedActions[0])
581
+ : '';
582
+ if (parsed) return parsed;
583
+ const cmd = compactInline(commandText(payload.command));
584
+ return cmd ? `Ran ${cmd}` : 'Ran command';
585
+ }
586
+
587
+ function summarizeApplyPatchText(input) {
588
+ const text = normalizeText(input);
589
+ if (!text.startsWith('*** Begin Patch')) return '';
590
+ const rows = [];
591
+ let active = null;
592
+ const flush = () => {
593
+ if (!active) return;
594
+ const statLabel = [
595
+ active.added > 0 ? `+${active.added}` : '',
596
+ active.removed > 0 ? `-${active.removed}` : '',
597
+ ].filter(Boolean).join(' ');
598
+ rows.push(`${active.kind} ${active.path}${statLabel ? ` ${statLabel}` : ''}`);
599
+ };
600
+ for (const line of text.split('\n')) {
601
+ const addMatch = line.match(/^\*\*\* Add File: (.+)$/);
602
+ const updateMatch = line.match(/^\*\*\* Update File: (.+)$/);
603
+ const deleteMatch = line.match(/^\*\*\* Delete File: (.+)$/);
604
+ const moveMatch = line.match(/^\*\*\* Move to: (.+)$/);
605
+ if (addMatch || updateMatch || deleteMatch) {
606
+ flush();
607
+ active = {
608
+ kind: addMatch ? 'add' : deleteMatch ? 'delete' : 'update',
609
+ path: addMatch?.[1] || updateMatch?.[1] || deleteMatch?.[1] || '',
610
+ added: 0,
611
+ removed: 0,
612
+ };
613
+ continue;
614
+ }
615
+ if (moveMatch && active) {
616
+ active.kind = 'move';
617
+ active.path = moveMatch[1] || active.path;
618
+ continue;
619
+ }
620
+ if (!active || line.startsWith('+++') || line.startsWith('---')) continue;
621
+ if (line.startsWith('+')) active.added += 1;
622
+ else if (line.startsWith('-')) active.removed += 1;
623
+ }
624
+ flush();
625
+ return rows.slice(0, 16).join('\n');
626
+ }
627
+
628
+ function applyPatchTitleFromText(input) {
629
+ const summary = summarizeApplyPatchText(input);
630
+ if (!summary) return '';
631
+ const count = summary.split('\n').filter(Boolean).length;
632
+ if (count === 1) return 'Edited 1 file';
633
+ if (count > 1) return `Edited ${count} files`;
634
+ return '';
635
+ }
636
+
637
+ function processTitleForFunctionCall(payload) {
638
+ const name = String(payload.name || '').trim();
639
+ const args = parseJsonValue(payload.arguments);
640
+ if (name === 'exec_command') {
641
+ const cmd = normalizeText(args && args.cmd).replace(/\s+/g, ' ');
642
+ return cmd ? `Ran ${cmd}` : 'Ran command';
643
+ }
644
+ if (name === 'apply_patch') return applyPatchTitleFromText(payload.input || payload.arguments) || 'Applied patch';
645
+ if (name === 'view_image') return 'Viewed image';
646
+ if (name === 'update_plan') return 'Updated plan';
647
+ if (name === 'read_mcp_resource') return 'Read resource';
648
+ if (name) return `Used ${name}`;
649
+ return 'Used tool';
650
+ }
651
+
652
+ function processTypeForFunctionCall(payload) {
653
+ const name = String(payload.name || '').trim();
654
+ if (name === 'exec_command') return 'command';
655
+ if (name === 'apply_patch') return 'patch';
656
+ if (name === 'view_image') return 'image';
657
+ if (name === 'update_plan') return 'plan';
658
+ if (name === 'imagegen' || name === 'image_gen' || name === 'image_generation') return 'image-generation';
659
+ return 'tool';
660
+ }
661
+
662
+ function detailForFunctionCall(payload) {
663
+ const args = normalizeText(payload.arguments || payload.input);
664
+ if (!args) return '';
665
+ if (String(payload.name || '').trim() === 'apply_patch') {
666
+ const patchSummary = summarizeApplyPatchText(args);
667
+ if (patchSummary) return patchSummary;
668
+ }
669
+ const parsed = parseJsonValue(args);
670
+ if (parsed) {
671
+ if (typeof parsed.cmd === 'string') return parsed.cmd;
672
+ if (typeof parsed.prompt === 'string') return parsed.prompt;
673
+ return JSON.stringify(parsed, null, 2);
674
+ }
675
+ return args;
676
+ }
677
+
678
+ function titleForLocalShell(payload) {
679
+ const action = payload.action && typeof payload.action === 'object' ? payload.action : {};
680
+ const command = action.command || action.cmd || payload.command;
681
+ const cmd = compactInline(command);
682
+ return cmd ? `Ran ${cmd}` : 'Ran local shell';
683
+ }
684
+
685
+ function detailForLocalShell(payload) {
686
+ const action = payload.action && typeof payload.action === 'object' ? payload.action : {};
687
+ return summarizeOutput([
688
+ action.cwd ? `cwd: ${action.cwd}` : '',
689
+ action.command || action.cmd || '',
690
+ payload.status ? `status: ${payload.status}` : '',
691
+ ].filter(Boolean).join('\n'));
692
+ }
693
+
694
+ function summarizeOutput(output) {
695
+ const text = normalizeText(output);
696
+ if (!text) return '';
697
+ const lines = text.split('\n').map(line => line.trimEnd()).filter(Boolean);
698
+ const visible = lines.slice(0, 8).join('\n');
699
+ if (lines.length <= 8) return visible;
700
+ return `${visible}\n... +${lines.length - 8} lines`;
701
+ }
702
+
703
+ function summarizeCommandOutput(payload) {
704
+ const chunks = [
705
+ payload.formatted_output,
706
+ payload.aggregated_output,
707
+ payload.aggregatedOutput,
708
+ payload.stdout,
709
+ payload.stderr,
710
+ ].filter(value => normalizeText(value));
711
+ const output = chunks.join('\n');
712
+ const prefix = [];
713
+ if (payload.cwd) prefix.push(`cwd: ${payload.cwd}`);
714
+ if (Number.isFinite(payload.exit_code)) prefix.push(`exit: ${payload.exit_code}`);
715
+ else if (Number.isFinite(payload.exitCode)) prefix.push(`exit: ${payload.exitCode}`);
716
+ const body = summarizeOutput(output);
717
+ return [prefix.join(' · '), body].filter(Boolean).join('\n');
718
+ }
719
+
720
+ function normalizePatchChanges(changes) {
721
+ if (Array.isArray(changes)) {
722
+ return changes.map((change, index) => {
723
+ if (!change || typeof change !== 'object') {
724
+ return { path: `change-${index + 1}`, kind: '', diff: '' };
725
+ }
726
+ return {
727
+ path: change.path || change.file || change.name || `change-${index + 1}`,
728
+ kind: change.kind || change.type || '',
729
+ diff: change.diff || change.unified_diff || change.content || '',
730
+ };
731
+ });
732
+ }
733
+ if (!changes || typeof changes !== 'object') return [];
734
+ return Object.entries(changes).map(([file, change]) => ({
735
+ path: file,
736
+ kind: change && typeof change === 'object' ? change.kind || change.type || '' : '',
737
+ diff: change && typeof change === 'object' ? change.diff || change.unified_diff || change.content || '' : '',
738
+ }));
739
+ }
740
+
741
+ function summarizePatchChanges(changes) {
742
+ const normalized = normalizePatchChanges(changes);
743
+ const rows = normalized.slice(0, 16).map(change => {
744
+ const stats = patchChangeStats(change);
745
+ const statLabel = [
746
+ stats.added > 0 ? `+${stats.added}` : '',
747
+ stats.removed > 0 ? `-${stats.removed}` : '',
748
+ ].filter(Boolean).join(' ');
749
+ return `${change.kind ? `${change.kind} ` : ''}${change.path}${statLabel ? ` ${statLabel}` : ''}`;
750
+ });
751
+ if (normalized.length > 16) rows.push(`... +${normalized.length - 16} files`);
752
+ return rows.join('\n');
753
+ }
754
+
755
+ function diffStats(diff) {
756
+ const text = normalizeText(diff);
757
+ if (!text) return { added: 0, removed: 0 };
758
+ return text.split('\n').reduce((stats, line) => {
759
+ if (line.startsWith('+++') || line.startsWith('---')) return stats;
760
+ if (line.startsWith('+')) stats.added += 1;
761
+ else if (line.startsWith('-')) stats.removed += 1;
762
+ return stats;
763
+ }, { added: 0, removed: 0 });
764
+ }
765
+
766
+ function rawContentLineCount(content) {
767
+ const text = normalizeText(content);
768
+ if (!text) return 0;
769
+ return text.split('\n').length;
770
+ }
771
+
772
+ function patchChangeStats(change) {
773
+ const stats = diffStats(change.diff);
774
+ if (stats.added > 0 || stats.removed > 0) return stats;
775
+ const kind = String(change.kind || '').toLowerCase();
776
+ const lines = rawContentLineCount(change.diff);
777
+ if (!lines) return stats;
778
+ if (kind === 'add' || kind === 'added') return { added: lines, removed: 0 };
779
+ if (kind === 'delete' || kind === 'deleted') return { added: 0, removed: lines };
780
+ return stats;
781
+ }
782
+
783
+ function titleForPatch(payload) {
784
+ if (payload.success === false) return 'Patch failed';
785
+ const changes = normalizePatchChanges(payload.changes).length;
786
+ if (changes === 1) return 'Edited 1 file';
787
+ if (changes > 1) return `Edited ${changes} files`;
788
+ return 'Applied patch';
789
+ }
790
+
791
+ function titleForReviewAction(action) {
792
+ if (!action || typeof action !== 'object') return 'approval';
793
+ const type = String(action.type || '').replace(/[_-]/g, '').toLowerCase();
794
+ if (type === 'command') return compactInline(action.command, 'command');
795
+ if (type === 'execve') return compactInline([action.program, ...(Array.isArray(action.argv) ? action.argv : [])].filter(Boolean).join(' '), 'exec');
796
+ if (type === 'applypatch') {
797
+ const files = Array.isArray(action.files) ? action.files : [];
798
+ return files.length ? `patch ${files.slice(0, 3).join(', ')}` : 'patch';
799
+ }
800
+ if (type === 'networkaccess') return compactInline(action.target || action.host, 'network access');
801
+ if (type === 'mcptoolcall') return compactInline([action.server, action.toolName || action.toolTitle].filter(Boolean).join('/'), 'MCP tool');
802
+ if (type === 'requestpermissions') return compactInline(action.reason, 'permission request');
803
+ return compactInline(action.type, 'approval');
804
+ }
805
+
806
+ function detailForReview(payload) {
807
+ const review = payload && typeof payload.review === 'object' ? payload.review : {};
808
+ const action = payload && typeof payload.action === 'object' ? payload.action : {};
809
+ return [
810
+ review.status ? `status: ${review.status}` : '',
811
+ review.riskLevel ? `risk: ${review.riskLevel}` : '',
812
+ review.rationale || '',
813
+ action.cwd ? `cwd: ${action.cwd}` : '',
814
+ payload.decisionSource ? `decision: ${payload.decisionSource}` : '',
815
+ ].filter(Boolean).join('\n');
816
+ }
817
+
818
+ function detailForHook(run) {
819
+ const entries = Array.isArray(run.entries)
820
+ ? run.entries
821
+ .map(entry => {
822
+ if (!entry || typeof entry !== 'object') return '';
823
+ const kind = compactInline(entry.kind || '');
824
+ const text = normalizeText(entry.text);
825
+ return [kind, text].filter(Boolean).join(': ');
826
+ })
827
+ .filter(Boolean)
828
+ : [];
829
+ return [
830
+ run.statusMessage || run.status_message || '',
831
+ run.sourcePath || run.source_path ? `source: ${run.sourcePath || run.source_path}` : '',
832
+ Number.isFinite(run.durationMs) ? `duration: ${run.durationMs}ms` : '',
833
+ Number.isFinite(run.duration_ms) ? `duration: ${run.duration_ms}ms` : '',
834
+ ...entries,
835
+ ].filter(Boolean).join('\n');
836
+ }
837
+
838
+ function statusForHook(run, fallback = 'running') {
839
+ const status = String(run && run.status || '').toLowerCase();
840
+ if (status === 'failed' || status === 'blocked' || status === 'stopped') return 'failed';
841
+ if (status === 'completed') return 'completed';
842
+ return fallback;
843
+ }
844
+
845
+ function titleForMcp(payload) {
846
+ const invocation = payload.invocation && typeof payload.invocation === 'object' ? payload.invocation : {};
847
+ const server = compactInline(invocation.server || payload.server || '');
848
+ const tool = compactInline(invocation.tool || payload.tool || '');
849
+ if (server && tool) return `Used ${server}/${tool}`;
850
+ if (tool) return `Used ${tool}`;
851
+ return 'Used MCP tool';
852
+ }
853
+
854
+ function detailForMcp(payload) {
855
+ const invocation = payload.invocation && typeof payload.invocation === 'object' ? payload.invocation : {};
856
+ const result = payload.result;
857
+ const detail = [];
858
+ if (invocation.arguments) detail.push(JSON.stringify(invocation.arguments, null, 2));
859
+ else if (payload.arguments) detail.push(JSON.stringify(payload.arguments, null, 2));
860
+ if (result && typeof result === 'object') {
861
+ if (Array.isArray(result.content)) {
862
+ const text = result.content
863
+ .map(part => (part && typeof part.text === 'string' ? part.text : ''))
864
+ .filter(Boolean)
865
+ .join('\n');
866
+ if (text) detail.push(text);
867
+ }
868
+ if (result.is_error) detail.push('is_error: true');
869
+ }
870
+ return summarizeOutput(detail.join('\n'));
871
+ }
872
+
873
+ function textFromUserInput(content) {
874
+ if (typeof content === 'string') return normalizeText(content);
875
+ if (!Array.isArray(content)) return '';
876
+ return content
877
+ .map(part => {
878
+ if (!part || typeof part !== 'object') return '';
879
+ if (part.type === 'Text' && typeof part.text === 'string') return part.text;
880
+ if (part.type === 'text' && typeof part.text === 'string') return part.text;
881
+ if (part.type === 'mention') return compactInline(part.name || part.path || '');
882
+ if (part.type === 'skill') {
883
+ const name = compactInline(part.name || path.basename(part.path || ''));
884
+ return name ? `$${name}` : '';
885
+ }
886
+ if (typeof part.text === 'string') return part.text;
887
+ return '';
888
+ })
889
+ .filter(Boolean)
890
+ .join(' ')
891
+ .replace(/\s+([,.;:!?,。;:!?])/g, '$1')
892
+ .trim();
893
+ }
894
+
895
+ function textFromUserMessagePayload(payload) {
896
+ const direct = normalizeText(payload.message);
897
+ if (direct) return direct;
898
+ const textElements = Array.isArray(payload.text_elements)
899
+ ? payload.text_elements
900
+ : (Array.isArray(payload.textElements) ? payload.textElements : []);
901
+ return textElements
902
+ .map(element => {
903
+ if (typeof element === 'string') return element;
904
+ if (!element || typeof element !== 'object') return '';
905
+ if (typeof element.text === 'string') return element.text;
906
+ if (typeof element.content === 'string') return element.content;
907
+ if (Array.isArray(element.content)) return textFromContent(element.content);
908
+ return '';
909
+ })
910
+ .filter(Boolean)
911
+ .join('\n\n')
912
+ .trim();
913
+ }
914
+
915
+ function turnItemType(item) {
916
+ if (!item || typeof item !== 'object') return '';
917
+ return String(item.type || '').trim().replace(/[_-]/g, '').toLowerCase();
918
+ }
919
+
920
+ function itemStatus(item, fallback = '') {
921
+ return item.status || item.statusText || fallback;
922
+ }
923
+
924
+ function responseItemTurnId(payload) {
925
+ if (!payload || typeof payload !== 'object') return '';
926
+ const metadata = payload.internal_chat_message_metadata_passthrough || payload.internalChatMessageMetadataPassthrough;
927
+ return payload.turn_id || payload.turnId || (metadata && (metadata.turn_id || metadata.turnId)) || '';
928
+ }
929
+
930
+ function normalizeEventEnvelope(event) {
931
+ if (!event || typeof event !== 'object') return { type: '', payload: null };
932
+ if (typeof event.method === 'string') {
933
+ return {
934
+ type: event.method,
935
+ payload: event.params && typeof event.params === 'object' ? event.params : {},
936
+ };
937
+ }
938
+ const payload = event.payload && typeof event.payload === 'object' ? event.payload : event;
939
+ return {
940
+ type: String(event.type || ''),
941
+ payload,
942
+ };
943
+ }
944
+
945
+ function isIgnoredEventType(type) {
946
+ return [
947
+ 'token_count',
948
+ 'thread_goal_updated',
949
+ 'thread_name_updated',
950
+ ].includes(type);
951
+ }
952
+
953
+ function detailForPlanSteps(steps) {
954
+ if (!Array.isArray(steps)) return '';
955
+ return steps
956
+ .map(step => {
957
+ if (!step || typeof step !== 'object') return '';
958
+ const status = String(step.status || '').replace(/[_-]/g, '').toLowerCase();
959
+ const marker = status === 'completed' || step.completed === true
960
+ ? '[x]'
961
+ : status === 'inprogress'
962
+ ? '[>]'
963
+ : '[ ]';
964
+ return `${marker} ${compactInline(step.step || step.text || step.title || '')}`;
965
+ })
966
+ .filter(line => line.trim() !== '[ ]' && line.trim() !== '[x]' && line.trim() !== '[>]')
967
+ .join('\n');
968
+ }
969
+
970
+ function applyUserMessageToTurn(turn, { id = '', message = '', images = [] } = {}) {
971
+ if (!turn) return;
972
+ const subagentNotification = parseSubagentNotification(message);
973
+ if (subagentNotification) {
974
+ appendProcess(turn, {
975
+ id: id || `${turn.id}-subagent-notification-${turn.processItems.length + 1}`,
976
+ type: 'subagent',
977
+ title: subagentNotification.title,
978
+ detail: subagentNotification.detail,
979
+ status: subagentNotification.status,
980
+ });
981
+ return;
982
+ }
983
+ const userImages = Array.isArray(images) ? images.filter(Boolean) : [];
984
+ const pastedTranscript = extractReferencedPastedTranscriptContext(message);
985
+ const approvalTranscript = extractCodexApprovalTranscriptContext(pastedTranscript.text);
986
+ const userFiles = [
987
+ ...pastedTranscript.files,
988
+ ...approvalTranscript.files,
989
+ ...extractComposerFileAttachments(approvalTranscript.text).files,
990
+ ].slice(0, MAX_USER_FILES_PER_TURN);
991
+ const text = visibleUserMessageText(approvalTranscript.text, {
992
+ renderedAttachmentKinds: renderedAttachmentKindsForAttachments({ images: userImages, files: userFiles }),
993
+ });
994
+ if (!text && userImages.length <= 0 && userFiles.length <= 0) return;
995
+
996
+ if (!turn.userMessage) {
997
+ if (text) turn.userMessage = text;
998
+ if (userImages.length) turn.userImages = userImages;
999
+ if (userFiles.length) turn.userFiles = userFiles;
1000
+ return;
1001
+ }
1002
+
1003
+ if (
1004
+ text &&
1005
+ turn.userMessage === text &&
1006
+ userImages.length === 0 &&
1007
+ userFiles.length === 0
1008
+ ) {
1009
+ return;
1010
+ }
1011
+
1012
+ appendProcess(turn, {
1013
+ id: id || `${turn.id}-user-steer-${turn.processItems.length + 1}`,
1014
+ type: 'user-steer',
1015
+ title: text || 'User added context',
1016
+ detail: text,
1017
+ images: userImages,
1018
+ files: userFiles,
1019
+ status: 'completed',
1020
+ });
1021
+ }
1022
+
1023
+ function appendTurnItem(turn, item, status = '') {
1024
+ const type = turnItemType(item);
1025
+ if (!type) return false;
1026
+ if (type === 'usermessage') {
1027
+ const images = imagesFromContent(item.content);
1028
+ applyUserMessageToTurn(turn, {
1029
+ id: item.id,
1030
+ message: textFromUserInput(item.content),
1031
+ images,
1032
+ });
1033
+ return true;
1034
+ }
1035
+ if (type === 'hookprompt') {
1036
+ const fragments = Array.isArray(item.fragments) ? item.fragments : [];
1037
+ const text = fragments.map(fragment => fragment && fragment.text).filter(Boolean).join('\n\n');
1038
+ if (text) appendProcess(turn, { id: item.id, type: 'hook', title: 'Hook prompt', detail: text, status });
1039
+ return true;
1040
+ }
1041
+ if (type === 'agentmessage') {
1042
+ const text = Array.isArray(item.content)
1043
+ ? item.content.map(part => part && part.text).filter(Boolean).join('\n\n')
1044
+ : normalizeText(item.text);
1045
+ if (text && (item.phase === 'final_answer' || !item.phase)) {
1046
+ turn.finalMessage = finalMessageText(text, turn, item.id, item.memoryCitation || item.memory_citation);
1047
+ } else if (text) {
1048
+ const extracted = extractMemoryCitationBlock(text);
1049
+ appendMemoryCitation(turn, item.id, item.memoryCitation || item.memory_citation, extracted.detail);
1050
+ if (extracted.text) appendProcess(turn, { id: item.id, type: 'message', title: extracted.text.split('\n')[0], detail: extracted.text });
1051
+ }
1052
+ return true;
1053
+ }
1054
+ if (type === 'plan') {
1055
+ appendProcess(turn, { id: item.id, type: 'plan', title: 'Updated plan', detail: item.text, status });
1056
+ return true;
1057
+ }
1058
+ if (type === 'todolist') {
1059
+ const detail = detailForPlanSteps(Array.isArray(item.items) ? item.items : []);
1060
+ appendProcess(turn, { id: item.id, type: 'plan', title: 'Updated todos', detail, status });
1061
+ return true;
1062
+ }
1063
+ if (type === 'reasoning') {
1064
+ const text = [
1065
+ item.text,
1066
+ ...(Array.isArray(item.summary_text) ? item.summary_text : []),
1067
+ ...(Array.isArray(item.summary) ? item.summary : []),
1068
+ ...(Array.isArray(item.content) ? item.content : []),
1069
+ ].map(textFromContent).filter(Boolean).join('\n');
1070
+ if (text) appendProcess(turn, { id: item.id, type: 'reasoning', title: 'Reasoned', detail: text, status });
1071
+ return true;
1072
+ }
1073
+ if (type === 'commandexecution') {
1074
+ appendProcess(turn, {
1075
+ id: item.id,
1076
+ type: 'command',
1077
+ title: titleForCommandExecution(item),
1078
+ detail: summarizeCommandOutput(item),
1079
+ status: status || itemStatus(item),
1080
+ });
1081
+ return true;
1082
+ }
1083
+ if (type === 'filechange') {
1084
+ appendProcess(turn, {
1085
+ id: item.id,
1086
+ type: 'patch',
1087
+ title: titleForPatch(item),
1088
+ detail: summarizePatchChanges(item.changes),
1089
+ status: status || itemStatus(item),
1090
+ });
1091
+ return true;
1092
+ }
1093
+ if (type === 'mcptoolcall') {
1094
+ appendProcess(turn, {
1095
+ id: item.id,
1096
+ type: 'mcp',
1097
+ title: titleForMcp(item),
1098
+ detail: detailForMcp(item),
1099
+ images: imagesFromStructuredOutput(item.result),
1100
+ status: status || itemStatus(item),
1101
+ });
1102
+ return true;
1103
+ }
1104
+ if (type === 'dynamictoolcall') {
1105
+ const name = [item.namespace, item.tool].filter(Boolean).join('/');
1106
+ appendProcess(turn, {
1107
+ id: item.id,
1108
+ type: 'tool',
1109
+ title: name ? `Used ${name}` : 'Used tool',
1110
+ detail: summarizeOutput(JSON.stringify(item.arguments || item.content_items || item.contentItems || '', null, 2)),
1111
+ images: imagesFromStructuredOutput(item.content_items || item.contentItems),
1112
+ status: status || itemStatus(item),
1113
+ });
1114
+ return true;
1115
+ }
1116
+ if (type === 'collabagenttoolcall') {
1117
+ const tool = compactInline(item.tool, 'agent tool');
1118
+ const receivers = Array.isArray(item.receiver_thread_ids) ? item.receiver_thread_ids : item.receiverThreadIds;
1119
+ appendProcess(turn, {
1120
+ id: item.id,
1121
+ type: 'agent-tool',
1122
+ title: `Agent ${tool}`,
1123
+ detail: summarizeOutput([item.prompt, Array.isArray(receivers) && receivers.length ? `receivers: ${receivers.join(', ')}` : ''].filter(Boolean).join('\n')),
1124
+ status: status || itemStatus(item),
1125
+ });
1126
+ return true;
1127
+ }
1128
+ if (type === 'subagentactivity') {
1129
+ const kind = compactInline(item.kind, 'activity');
1130
+ const agentPath = compactInline(item.agent_path || item.agentPath || item.agent_thread_id || item.agentThreadId);
1131
+ appendProcess(turn, {
1132
+ id: item.id,
1133
+ type: 'sub-agent',
1134
+ title: agentPath ? `${kind} ${agentPath}` : `Sub-agent ${kind}`,
1135
+ status,
1136
+ });
1137
+ return true;
1138
+ }
1139
+ if (type === 'websearch') {
1140
+ appendProcess(turn, { id: item.id, type: 'web-search', title: compactInline(item.query, 'Searched web'), status });
1141
+ return true;
1142
+ }
1143
+ if (type === 'imageview') {
1144
+ appendProcess(turn, {
1145
+ id: item.id,
1146
+ type: 'image',
1147
+ title: compactInline(item.path, 'Viewed image'),
1148
+ images: imagesFromImageItem(item),
1149
+ status,
1150
+ });
1151
+ return true;
1152
+ }
1153
+ if (type === 'sleep') {
1154
+ const duration = item.duration_ms || item.durationMs;
1155
+ appendProcess(turn, {
1156
+ id: item.id,
1157
+ type: 'sleep',
1158
+ title: duration ? `Slept for ${Math.round(duration / 1000)}s` : 'Slept',
1159
+ status: status || itemStatus(item),
1160
+ });
1161
+ return true;
1162
+ }
1163
+ if (type === 'imagegeneration') {
1164
+ const detail = [
1165
+ item.saved_path || item.savedPath,
1166
+ item.revised_prompt || item.revisedPrompt,
1167
+ item.status,
1168
+ ].map(value => compactInline(value)).filter(Boolean).join('\n');
1169
+ appendProcess(turn, {
1170
+ id: item.id,
1171
+ type: 'image-generation',
1172
+ title: 'Generated image',
1173
+ detail,
1174
+ images: imagesFromImageItem(item),
1175
+ status: status || itemStatus(item),
1176
+ });
1177
+ return true;
1178
+ }
1179
+ if (type === 'enteredreviewmode' || type === 'exitedreviewmode') {
1180
+ appendProcess(turn, {
1181
+ id: item.id,
1182
+ type: 'review',
1183
+ title: type === 'enteredreviewmode' ? 'Entered review mode' : 'Exited review mode',
1184
+ detail: item.review || '',
1185
+ status,
1186
+ });
1187
+ return true;
1188
+ }
1189
+ if (type === 'contextcompaction') {
1190
+ appendProcess(turn, { id: item.id, type: 'compaction', title: 'Compacted context', status });
1191
+ return true;
1192
+ }
1193
+ if (type === 'error') {
1194
+ appendProcess(turn, {
1195
+ id: item.id,
1196
+ type: 'error',
1197
+ title: compactInline(item.message, 'Error'),
1198
+ detail: item.detail || item.error || '',
1199
+ status: itemStatus(item, 'failed'),
1200
+ });
1201
+ return true;
1202
+ }
1203
+ appendProcess(turn, {
1204
+ id: item.id,
1205
+ type: 'event',
1206
+ title: humanizeType(item.type, 'Codex event'),
1207
+ detail: summarizeOutput(JSON.stringify(item, null, 2)),
1208
+ status: status || itemStatus(item),
1209
+ });
1210
+ return true;
1211
+ }
1212
+
1213
+ function appendResponseItem(turn, payload, functionCalls) {
1214
+ if (!turn || !payload || typeof payload !== 'object') return false;
1215
+ if (payload.type === 'message') {
1216
+ const text = textFromContent(payload.content);
1217
+ const images = imagesFromContent(payload.content);
1218
+ if (!text && images.length <= 0) return true;
1219
+ if (payload.role === 'user') {
1220
+ applyUserMessageToTurn(turn, {
1221
+ id: payload.id,
1222
+ message: text,
1223
+ images,
1224
+ });
1225
+ } else if (payload.role === 'assistant' && text && (payload.phase === 'final_answer' || (!payload.phase && !turn.finalMessage))) {
1226
+ turn.finalMessage = finalMessageText(text, turn, payload.id, payload.memoryCitation || payload.memory_citation);
1227
+ } else if (payload.role === 'assistant' && text) {
1228
+ const extracted = extractMemoryCitationBlock(text);
1229
+ appendMemoryCitation(turn, payload.id, payload.memoryCitation || payload.memory_citation, extracted.detail);
1230
+ appendProcess(turn, {
1231
+ id: payload.id,
1232
+ type: 'message',
1233
+ title: (extracted.text || text).split('\n')[0],
1234
+ detail: extracted.text || text,
1235
+ });
1236
+ }
1237
+ return true;
1238
+ }
1239
+
1240
+ if (payload.type === 'agent_message') {
1241
+ const text = textFromContent(payload.content);
1242
+ appendProcess(turn, {
1243
+ id: payload.id,
1244
+ type: 'agent-message',
1245
+ title: [payload.author, payload.recipient].filter(Boolean).join(' -> ') || 'Agent message',
1246
+ detail: text,
1247
+ });
1248
+ return true;
1249
+ }
1250
+
1251
+ if (payload.type === 'reasoning') {
1252
+ const summary = [
1253
+ ...(Array.isArray(payload.summary) ? payload.summary.map(textFromContent) : []),
1254
+ ...(Array.isArray(payload.content) ? payload.content.map(textFromContent) : []),
1255
+ ].filter(Boolean).join('\n');
1256
+ if (summary) {
1257
+ appendProcess(turn, {
1258
+ id: payload.id,
1259
+ type: 'reasoning',
1260
+ title: 'Reasoned',
1261
+ detail: summary,
1262
+ });
1263
+ }
1264
+ return true;
1265
+ }
1266
+
1267
+ if (payload.type === 'additional_tools') {
1268
+ const count = Array.isArray(payload.tools) ? payload.tools.length : 0;
1269
+ appendProcess(turn, {
1270
+ id: payload.id,
1271
+ type: 'tool',
1272
+ title: count ? `Added ${count} tools` : 'Added tools',
1273
+ detail: summarizeOutput(JSON.stringify(payload.tools || '', null, 2)),
1274
+ status: 'completed',
1275
+ });
1276
+ return true;
1277
+ }
1278
+
1279
+ if (payload.type === 'local_shell_call') {
1280
+ appendProcess(turn, {
1281
+ id: payload.call_id || payload.id,
1282
+ type: 'command',
1283
+ title: titleForLocalShell(payload),
1284
+ detail: detailForLocalShell(payload),
1285
+ status: payload.status || 'completed',
1286
+ });
1287
+ return true;
1288
+ }
1289
+
1290
+ if (payload.type === 'function_call' || payload.type === 'custom_tool_call') {
1291
+ const title = processTitleForFunctionCall(payload);
1292
+ const processType = processTypeForFunctionCall(payload);
1293
+ const callId = payload.call_id || payload.id || '';
1294
+ if (callId) functionCalls.set(callId, { title, type: processType });
1295
+ appendProcess(turn, {
1296
+ id: callId || payload.id,
1297
+ type: processType,
1298
+ title,
1299
+ detail: detailForFunctionCall(payload),
1300
+ status: 'running',
1301
+ });
1302
+ return true;
1303
+ }
1304
+
1305
+ if (payload.type === 'function_call_output' || payload.type === 'custom_tool_call_output') {
1306
+ const callId = payload.call_id || '';
1307
+ const callMeta = functionCalls.get(callId);
1308
+ appendProcess(turn, {
1309
+ id: callId || payload.id,
1310
+ type: callMeta?.type || 'tool-output',
1311
+ title: callMeta?.title || 'Tool output',
1312
+ detail: summarizeStructuredOutput(payload.output),
1313
+ images: imagesFromStructuredOutput(payload.output),
1314
+ mergeDetail: true,
1315
+ status: 'completed',
1316
+ });
1317
+ return true;
1318
+ }
1319
+
1320
+ if (payload.type === 'web_search_call') {
1321
+ appendProcess(turn, {
1322
+ id: payload.id,
1323
+ type: 'web-search',
1324
+ title: compactInline(payload.action && payload.action.query, 'Searched web'),
1325
+ detail: payload.action ? summarizeOutput(JSON.stringify(payload.action, null, 2)) : '',
1326
+ status: payload.status || 'completed',
1327
+ });
1328
+ return true;
1329
+ }
1330
+
1331
+ if (payload.type === 'tool_search_call' || payload.type === 'tool_search_output') {
1332
+ const callId = payload.call_id || payload.id || '';
1333
+ appendProcess(turn, {
1334
+ id: callId,
1335
+ type: 'tool',
1336
+ title: 'Searched tools',
1337
+ detail: summarizeOutput(JSON.stringify(payload.arguments || payload.tools || '', null, 2)),
1338
+ status: payload.type === 'tool_search_call' ? (payload.status || 'running') : (payload.status || 'completed'),
1339
+ });
1340
+ return true;
1341
+ }
1342
+
1343
+ if (payload.type === 'image_generation_call') {
1344
+ appendProcess(turn, {
1345
+ id: payload.id,
1346
+ type: 'image-generation',
1347
+ title: 'Generated image',
1348
+ detail: compactInline(payload.revised_prompt || payload.status || ''),
1349
+ images: imagesFromImageItem(payload),
1350
+ status: payload.status || 'running',
1351
+ });
1352
+ return true;
1353
+ }
1354
+
1355
+ if (
1356
+ payload.type === 'compaction' ||
1357
+ payload.type === 'compaction_summary' ||
1358
+ payload.type === 'compaction_trigger' ||
1359
+ payload.type === 'context_compaction'
1360
+ ) {
1361
+ appendProcess(turn, {
1362
+ id: payload.id,
1363
+ type: 'compaction',
1364
+ title: payload.type === 'compaction_trigger' ? 'Compaction triggered' : 'Compacted context',
1365
+ detail: compactInline(payload.encrypted_content || payload.status || ''),
1366
+ status: payload.status || 'completed',
1367
+ });
1368
+ return true;
1369
+ }
1370
+
1371
+ appendProcess(turn, {
1372
+ id: payload.id || payload.call_id,
1373
+ type: 'event',
1374
+ title: humanizeType(payload.type, 'Codex response item'),
1375
+ detail: summarizeOutput(JSON.stringify(payload, null, 2)),
1376
+ status: payload.status || '',
1377
+ });
1378
+ return true;
1379
+ }
1380
+
1381
+ function newTurn(id = '') {
1382
+ return {
1383
+ id: id || `turn-${Date.now()}-${Math.random().toString(36).slice(2)}`,
1384
+ userMessage: '',
1385
+ userImages: [],
1386
+ userFiles: [],
1387
+ finalMessage: '',
1388
+ startedAt: null,
1389
+ completedAt: null,
1390
+ durationMs: null,
1391
+ status: 'inProgress',
1392
+ model: '',
1393
+ effort: '',
1394
+ cwd: '',
1395
+ processItems: [],
1396
+ };
1397
+ }
1398
+
1399
+ function isEmptyTurn(turn) {
1400
+ return !turn.userMessage &&
1401
+ (!Array.isArray(turn.userImages) || turn.userImages.length === 0) &&
1402
+ (!Array.isArray(turn.userFiles) || turn.userFiles.length === 0) &&
1403
+ !turn.finalMessage &&
1404
+ turn.processItems.length === 0;
1405
+ }
1406
+
1407
+ function hasRunningProcessItems(turn) {
1408
+ return turn.processItems.some(item => {
1409
+ const status = String(item.status || '').replace(/[_-]/g, '').toLowerCase();
1410
+ return status === 'running' || status === 'inprogress' || status === 'pending';
1411
+ });
1412
+ }
1413
+
1414
+ function isStandaloneThreadSettingsTurn(turn) {
1415
+ if (!turn || turn.status !== 'inProgress' || turn.userMessage || turn.finalMessage) return false;
1416
+ const items = Array.isArray(turn.processItems) ? turn.processItems : [];
1417
+ return items.length > 0 && items.every(item => (
1418
+ item && item.type === 'event' && item.title === 'Thread Settings Applied'
1419
+ ));
1420
+ }
1421
+
1422
+ function processItemIdentity(item) {
1423
+ return [
1424
+ String(item?.type || ''),
1425
+ String(item?.title || '').trim(),
1426
+ String(item?.detail || '').trim(),
1427
+ String(item?.status || '').trim(),
1428
+ ].join('\u0000');
1429
+ }
1430
+
1431
+ function dedupeAdjacentProcessItems(items) {
1432
+ const deduped = [];
1433
+ let previousKey = '';
1434
+ for (const item of items || []) {
1435
+ const key = processItemIdentity(item);
1436
+ if (key && key === previousKey) continue;
1437
+ deduped.push(item);
1438
+ previousKey = key;
1439
+ }
1440
+ return deduped;
1441
+ }
1442
+
1443
+ function sanitizeProcessItemForOutput(item) {
1444
+ if (!item || typeof item !== 'object') return null;
1445
+ const title = stripCodexInternalContextBlocks(item.title);
1446
+ const detail = stripCodexInternalContextBlocks(item.detail);
1447
+ if (!title && !detail && (!Array.isArray(item.images) || item.images.length <= 0) && (!Array.isArray(item.files) || item.files.length <= 0)) {
1448
+ return null;
1449
+ }
1450
+ return {
1451
+ ...item,
1452
+ title: title || item.title,
1453
+ detail,
1454
+ };
1455
+ }
1456
+
1457
+ function normalizeTurnForOutput(turn) {
1458
+ if (!turn || typeof turn !== 'object') return turn;
1459
+ turn.userMessage = visibleUserMessageText(turn.userMessage, {
1460
+ renderedAttachmentKinds: renderedAttachmentKindsForTurn(turn),
1461
+ });
1462
+ turn.finalMessage = stripCodexInternalContextBlocks(turn.finalMessage);
1463
+ turn.processItems = dedupeAdjacentProcessItems(turn.processItems)
1464
+ .map(sanitizeProcessItemForOutput)
1465
+ .filter(Boolean);
1466
+ if (turn.status === 'inProgress' && turn.finalMessage && !hasRunningProcessItems(turn)) {
1467
+ turn.status = 'completed';
1468
+ }
1469
+ return turn;
1470
+ }
1471
+
1472
+ function completeTurn(turn, payload = {}) {
1473
+ if (!turn) return;
1474
+ turn.status = payload.type === 'turn_aborted' || payload.type === 'task_aborted' ? 'interrupted' : 'completed';
1475
+ if (typeof payload.completed_at === 'number') turn.completedAt = payload.completed_at;
1476
+ if (typeof payload.completedAt === 'number') turn.completedAt = payload.completedAt;
1477
+ if (payload.turn && typeof payload.turn === 'object' && typeof payload.turn.completedAt === 'number') {
1478
+ turn.completedAt = payload.turn.completedAt;
1479
+ }
1480
+ if (typeof payload.duration_ms === 'number') turn.durationMs = payload.duration_ms;
1481
+ if (typeof payload.durationMs === 'number') turn.durationMs = payload.durationMs;
1482
+ if (payload.turn && typeof payload.turn === 'object' && typeof payload.turn.durationMs === 'number') {
1483
+ turn.durationMs = payload.turn.durationMs;
1484
+ }
1485
+ if (!turn.finalMessage && typeof payload.last_agent_message === 'string') {
1486
+ turn.finalMessage = finalMessageText(payload.last_agent_message, turn, `${turn.id}-last-agent-message`);
1487
+ }
1488
+ }
1489
+
1490
+ function appendTurnSnapshot(turns, turn) {
1491
+ if (!turn || typeof turn !== 'object') return false;
1492
+ const normalizedTurn = newTurn(turn.id);
1493
+ normalizedTurn.status = turn.status || normalizedTurn.status;
1494
+ normalizedTurn.startedAt = typeof turn.startedAt === 'number' ? turn.startedAt : null;
1495
+ normalizedTurn.completedAt = typeof turn.completedAt === 'number' ? turn.completedAt : null;
1496
+ normalizedTurn.durationMs = typeof turn.durationMs === 'number' ? turn.durationMs : null;
1497
+ if (Array.isArray(turn.items)) {
1498
+ for (const item of turn.items) {
1499
+ appendTurnItem(normalizedTurn, item, itemStatus(item, normalizedTurn.status));
1500
+ }
1501
+ }
1502
+ if (turn.error) {
1503
+ appendTurnItem(normalizedTurn, { type: 'error', id: `${normalizedTurn.id}-error`, ...turn.error }, 'failed');
1504
+ }
1505
+ if (!isEmptyTurn(normalizedTurn)) turns.push(normalizedTurn);
1506
+ return true;
1507
+ }
1508
+
1509
+ function appendProcess(turn, item) {
1510
+ if (!turn || !item || !item.title) return;
1511
+ const id = item.id || `${turn.id}-process-${turn.processItems.length + 1}`;
1512
+ const detail = normalizeText(item.detail);
1513
+ const files = Array.isArray(item.files) ? item.files.slice(0, MAX_USER_FILES_PER_TURN) : [];
1514
+ const next = {
1515
+ id,
1516
+ type: item.type || 'event',
1517
+ title: normalizeText(item.title).slice(0, 240),
1518
+ detail: detail.slice(0, 4000),
1519
+ images: Array.isArray(item.images) ? item.images.slice(0, MAX_USER_IMAGES_PER_TURN) : [],
1520
+ files,
1521
+ status: item.status || '',
1522
+ };
1523
+ const existing = turn.processItems.find(entry => entry.id === id);
1524
+ if (existing) {
1525
+ existing.type = next.type || existing.type;
1526
+ if (!item.mergeDetail) existing.title = next.title || existing.title;
1527
+ if (item.mergeDetail && existing.detail && next.detail && existing.detail !== next.detail) {
1528
+ existing.detail = `${existing.detail}\n\n${next.detail}`.slice(0, 4000);
1529
+ } else {
1530
+ existing.detail = next.detail || existing.detail;
1531
+ }
1532
+ if (next.images.length) {
1533
+ const urls = new Set((existing.images || []).map(image => image.url));
1534
+ existing.images = [
1535
+ ...(existing.images || []),
1536
+ ...next.images.filter(image => image && image.url && !urls.has(image.url)),
1537
+ ].slice(0, MAX_USER_IMAGES_PER_TURN);
1538
+ }
1539
+ if (next.files.length) {
1540
+ const keys = new Set((existing.files || []).map(file => `${file.name}\0${file.content}\0${file.error}`));
1541
+ existing.files = [
1542
+ ...(existing.files || []),
1543
+ ...next.files.filter(file => file && !keys.has(`${file.name}\0${file.content}\0${file.error}`)),
1544
+ ].slice(0, MAX_USER_FILES_PER_TURN);
1545
+ }
1546
+ existing.status = next.status || existing.status;
1547
+ return;
1548
+ }
1549
+ turn.processItems.push(next);
1550
+ }
1551
+
1552
+ function appendProcessDelta(turn, item) {
1553
+ if (!turn || !item || !item.title) return;
1554
+ const id = item.id || `${turn.id}-process-${turn.processItems.length + 1}`;
1555
+ const delta = normalizeDeltaText(item.delta);
1556
+ const existing = turn.processItems.find(entry => entry.id === id);
1557
+ if (existing) {
1558
+ existing.type = item.type || existing.type;
1559
+ existing.title = item.title || existing.title;
1560
+ if (delta) existing.detail = `${existing.detail || ''}${delta}`.slice(0, 4000);
1561
+ existing.status = item.status || existing.status;
1562
+ return;
1563
+ }
1564
+ turn.processItems.push({
1565
+ id,
1566
+ type: item.type || 'event',
1567
+ title: normalizeText(item.title).slice(0, 240),
1568
+ detail: delta.slice(0, 4000),
1569
+ images: [],
1570
+ files: [],
1571
+ status: item.status || '',
1572
+ });
1573
+ }
1574
+
1575
+ function buildTranscriptFromLines(lines, options = {}) {
1576
+ const turns = [];
1577
+ let active = null;
1578
+ const functionCalls = new Map();
1579
+
1580
+ // `thread/read` is a full snapshot, but Codex can continue emitting live
1581
+ // events for the same in-progress turn after that snapshot. Reuse the
1582
+ // snapshot turn instead of creating a second visual turn for the stream.
1583
+ const resumeSnapshotTurn = (id) => {
1584
+ if (!id) return null;
1585
+ for (let index = turns.length - 1; index >= 0; index -= 1) {
1586
+ if (turns[index] && turns[index].id === id && turns[index].status === 'inProgress') {
1587
+ return turns.splice(index, 1)[0] || null;
1588
+ }
1589
+ }
1590
+ return null;
1591
+ };
1592
+
1593
+ const ensureActive = (id = '') => {
1594
+ if (active && (!id || active.id === id)) return active;
1595
+ if (active && !isEmptyTurn(active)) turns.push(active);
1596
+ active = resumeSnapshotTurn(id) || newTurn(id);
1597
+ return active;
1598
+ };
1599
+
1600
+ const finishActive = (payload = {}) => {
1601
+ if (!active) return;
1602
+ completeTurn(active, payload);
1603
+ if (!isEmptyTurn(active)) turns.push(active);
1604
+ active = null;
1605
+ };
1606
+
1607
+ for (const line of lines) {
1608
+ if (!line.trim()) continue;
1609
+ let event = null;
1610
+ try {
1611
+ event = JSON.parse(line);
1612
+ } catch {
1613
+ continue;
1614
+ }
1615
+ const { type: eventType, payload } = normalizeEventEnvelope(event);
1616
+ if (!payload) continue;
1617
+
1618
+ if (eventType === 'turn_context') {
1619
+ const turn = ensureActive(payload.turn_id);
1620
+ turn.cwd = typeof payload.cwd === 'string' ? payload.cwd : turn.cwd;
1621
+ turn.model = typeof payload.model === 'string' ? payload.model : turn.model;
1622
+ turn.effort = typeof payload.effort === 'string' ? payload.effort : turn.effort;
1623
+ continue;
1624
+ }
1625
+
1626
+ if (eventType === 'compacted') {
1627
+ appendProcess(ensureActive(), {
1628
+ type: 'compaction',
1629
+ title: 'Compacted context',
1630
+ detail: payload.message || '',
1631
+ });
1632
+ continue;
1633
+ }
1634
+
1635
+ if (eventType === 'thread/read' && payload.thread && Array.isArray(payload.thread.turns)) {
1636
+ if (active && !isEmptyTurn(active)) turns.push(active);
1637
+ active = null;
1638
+ // A later thread/read replaces the previous snapshot; retaining both
1639
+ // makes the same historical session appear twice in the chat.
1640
+ turns.length = 0;
1641
+ for (const turn of payload.thread.turns) appendTurnSnapshot(turns, turn);
1642
+ continue;
1643
+ }
1644
+
1645
+ if (eventType === 'thread/items/list' && Array.isArray(payload.data)) {
1646
+ if (active && !isEmptyTurn(active)) turns.push(active);
1647
+ active = newTurn(payload.turnId || payload.turn_id || 'thread-items-list');
1648
+ active.status = 'completed';
1649
+ for (const item of payload.data) {
1650
+ appendTurnItem(active, item, itemStatus(item, active.status));
1651
+ }
1652
+ if (!isEmptyTurn(active)) turns.push(active);
1653
+ active = null;
1654
+ continue;
1655
+ }
1656
+
1657
+ if (eventType === 'turn' || eventType === 'turn/snapshot') {
1658
+ if (appendTurnSnapshot(turns, payload.turn || payload)) continue;
1659
+ }
1660
+
1661
+ if (eventType === 'turn.started' || eventType === 'turn_started' || eventType === 'turn/started') {
1662
+ const turn = ensureActive(payload.turn_id || payload.turnId || payload.turn?.id);
1663
+ turn.status = 'inProgress';
1664
+ if (payload.turn && typeof payload.turn === 'object') {
1665
+ if (typeof payload.turn.startedAt === 'number') turn.startedAt = payload.turn.startedAt;
1666
+ if (typeof payload.turn.status === 'string') turn.status = payload.turn.status;
1667
+ }
1668
+ if (typeof payload.startedAtMs === 'number') turn.startedAt = payload.startedAtMs;
1669
+ continue;
1670
+ }
1671
+
1672
+ if (
1673
+ eventType === 'item.started' ||
1674
+ eventType === 'item.updated' ||
1675
+ eventType === 'item.completed' ||
1676
+ eventType === 'item/started' ||
1677
+ eventType === 'item/updated' ||
1678
+ eventType === 'item/completed'
1679
+ ) {
1680
+ const status = eventType === 'item.started' || eventType === 'item/started'
1681
+ ? 'running'
1682
+ : eventType === 'item.updated' || eventType === 'item/updated'
1683
+ ? itemStatus(payload.item, 'running')
1684
+ : itemStatus(payload.item, 'completed');
1685
+ if (appendTurnItem(ensureActive(payload.turn_id || payload.turnId), payload.item, status)) continue;
1686
+ }
1687
+
1688
+ if (eventType === 'turn.completed' || eventType === 'turn_complete' || eventType === 'turn/completed') {
1689
+ if (payload.turn && Array.isArray(payload.turn.items)) {
1690
+ const turn = ensureActive(payload.turn.id);
1691
+ for (const item of payload.turn.items) {
1692
+ appendTurnItem(turn, item, itemStatus(item, payload.turn.status || 'completed'));
1693
+ }
1694
+ }
1695
+ finishActive({
1696
+ ...payload,
1697
+ type: 'turn_complete',
1698
+ turn_id: payload.turn_id || payload.turnId || payload.turn?.id,
1699
+ duration_ms: payload.duration_ms || payload.durationMs || payload.turn?.durationMs,
1700
+ });
1701
+ continue;
1702
+ }
1703
+
1704
+ if (eventType === 'turn/plan/updated') {
1705
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1706
+ id: payload.id || `${payload.turnId || payload.turn_id || 'turn'}-plan`,
1707
+ type: 'plan',
1708
+ title: 'Updated plan',
1709
+ detail: detailForPlanSteps(payload.plan),
1710
+ status: 'completed',
1711
+ });
1712
+ continue;
1713
+ }
1714
+
1715
+ if (eventType === 'item/agentMessage/delta') {
1716
+ const turn = ensureActive(payload.turn_id || payload.turnId);
1717
+ const delta = normalizeDeltaText(payload.delta);
1718
+ if (delta) {
1719
+ turn.finalMessage = `${turn.finalMessage || ''}${delta}`;
1720
+ }
1721
+ continue;
1722
+ }
1723
+
1724
+ if (eventType === 'item/plan/delta') {
1725
+ appendProcessDelta(ensureActive(payload.turn_id || payload.turnId), {
1726
+ id: payload.item_id || payload.itemId,
1727
+ type: 'plan',
1728
+ title: 'Updated plan',
1729
+ delta: payload.delta,
1730
+ status: 'running',
1731
+ });
1732
+ continue;
1733
+ }
1734
+
1735
+ if (eventType === 'item/reasoning/summaryTextDelta' || eventType === 'item/reasoning/textDelta') {
1736
+ appendProcessDelta(ensureActive(payload.turn_id || payload.turnId), {
1737
+ id: payload.item_id || payload.itemId,
1738
+ type: 'reasoning',
1739
+ title: 'Reasoned',
1740
+ delta: payload.delta,
1741
+ status: 'running',
1742
+ });
1743
+ continue;
1744
+ }
1745
+
1746
+ if (eventType === 'item/reasoning/summaryPartAdded') {
1747
+ appendProcessDelta(ensureActive(payload.turn_id || payload.turnId), {
1748
+ id: payload.item_id || payload.itemId,
1749
+ type: 'reasoning',
1750
+ title: 'Reasoned',
1751
+ delta: '\n',
1752
+ status: 'running',
1753
+ });
1754
+ continue;
1755
+ }
1756
+
1757
+ if (eventType === 'item/commandExecution/outputDelta') {
1758
+ appendProcessDelta(ensureActive(payload.turn_id || payload.turnId), {
1759
+ id: payload.item_id || payload.itemId,
1760
+ type: 'command',
1761
+ title: 'Ran command',
1762
+ delta: payload.delta,
1763
+ status: 'running',
1764
+ });
1765
+ continue;
1766
+ }
1767
+
1768
+ if (eventType === 'item/fileChange/outputDelta') {
1769
+ appendProcessDelta(ensureActive(payload.turn_id || payload.turnId), {
1770
+ id: payload.item_id || payload.itemId,
1771
+ type: 'patch',
1772
+ title: 'Applied patch',
1773
+ delta: payload.delta,
1774
+ status: 'running',
1775
+ });
1776
+ continue;
1777
+ }
1778
+
1779
+ if (eventType === 'item/fileChange/patchUpdated') {
1780
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1781
+ id: payload.item_id || payload.itemId,
1782
+ type: 'patch',
1783
+ title: titleForPatch(payload),
1784
+ detail: summarizePatchChanges(payload.changes),
1785
+ status: 'running',
1786
+ });
1787
+ continue;
1788
+ }
1789
+
1790
+ if (eventType === 'turn/diff/updated') {
1791
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1792
+ id: `${payload.turnId || payload.turn_id || 'turn'}-diff`,
1793
+ type: 'patch',
1794
+ title: 'Updated diff',
1795
+ detail: summarizeOutput(payload.diff),
1796
+ status: 'running',
1797
+ });
1798
+ continue;
1799
+ }
1800
+
1801
+ if (eventType === 'item/autoApprovalReview/started' || eventType === 'item/autoApprovalReview/completed') {
1802
+ const completed = eventType.endsWith('/completed');
1803
+ const actionTitle = titleForReviewAction(payload.action);
1804
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1805
+ id: payload.reviewId || payload.review_id || payload.targetItemId || payload.target_item_id,
1806
+ type: 'review',
1807
+ title: completed ? `Reviewed ${actionTitle}` : `Reviewing ${actionTitle}`,
1808
+ detail: detailForReview(payload),
1809
+ status: completed ? 'completed' : 'running',
1810
+ });
1811
+ continue;
1812
+ }
1813
+
1814
+ if (eventType === 'item/commandExecution/terminalInteraction') {
1815
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1816
+ id: payload.item_id || payload.itemId,
1817
+ type: 'command',
1818
+ title: 'Interacted with terminal',
1819
+ detail: normalizeText(payload.stdin),
1820
+ status: 'running',
1821
+ mergeDetail: true,
1822
+ });
1823
+ continue;
1824
+ }
1825
+
1826
+ if (eventType === 'item/mcpToolCall/progress') {
1827
+ appendProcessDelta(ensureActive(payload.turn_id || payload.turnId), {
1828
+ id: payload.item_id || payload.itemId,
1829
+ type: 'mcp',
1830
+ title: 'MCP progress',
1831
+ delta: payload.message ? `${payload.message}\n` : '',
1832
+ status: 'running',
1833
+ });
1834
+ continue;
1835
+ }
1836
+
1837
+ if (eventType === 'hook/started' || eventType === 'hook/completed') {
1838
+ const run = payload.run && typeof payload.run === 'object' ? payload.run : {};
1839
+ const completed = eventType.endsWith('/completed');
1840
+ const eventName = run.eventName || run.event_name;
1841
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1842
+ id: run.id || `${payload.turnId || payload.turn_id || 'turn'}-hook`,
1843
+ type: 'hook',
1844
+ title: completed
1845
+ ? `Completed ${compactInline(eventName, 'hook')}`
1846
+ : `Running ${compactInline(eventName, 'hook')}`,
1847
+ detail: detailForHook(run),
1848
+ status: statusForHook(run, completed ? 'completed' : 'running'),
1849
+ });
1850
+ continue;
1851
+ }
1852
+
1853
+ if (eventType === 'serverRequest/resolved') {
1854
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1855
+ id: payload.requestId || payload.request_id,
1856
+ type: 'event',
1857
+ title: 'Resolved server request',
1858
+ detail: payload.requestId || payload.request_id || '',
1859
+ status: 'completed',
1860
+ });
1861
+ continue;
1862
+ }
1863
+
1864
+ if (eventType === 'thread/compacted') {
1865
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1866
+ id: `${payload.turnId || payload.turn_id || 'turn'}-compacted`,
1867
+ type: 'compaction',
1868
+ title: 'Compacted context',
1869
+ status: 'completed',
1870
+ });
1871
+ continue;
1872
+ }
1873
+
1874
+ if (eventType === 'model/rerouted') {
1875
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1876
+ id: `${payload.turnId || payload.turn_id || 'turn'}-model-rerouted`,
1877
+ type: 'event',
1878
+ title: `Rerouted ${compactInline(payload.fromModel || payload.from_model, 'model')} to ${compactInline(payload.toModel || payload.to_model, 'model')}`,
1879
+ detail: compactInline(payload.reason),
1880
+ status: 'completed',
1881
+ });
1882
+ continue;
1883
+ }
1884
+
1885
+ if (eventType === 'model/verification') {
1886
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1887
+ id: `${payload.turnId || payload.turn_id || 'turn'}-model-verification`,
1888
+ type: 'event',
1889
+ title: 'Verified model',
1890
+ detail: summarizeOutput(JSON.stringify(payload.verifications || [], null, 2)),
1891
+ status: 'completed',
1892
+ });
1893
+ continue;
1894
+ }
1895
+
1896
+ if (eventType === 'model/safetyBuffering/updated') {
1897
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1898
+ id: `${payload.turnId || payload.turn_id || 'turn'}-safety-buffering`,
1899
+ type: 'warning',
1900
+ title: payload.showBufferingUi === false ? 'Safety buffering hidden' : 'Safety buffering',
1901
+ detail: [
1902
+ payload.model ? `model: ${payload.model}` : '',
1903
+ Array.isArray(payload.useCases) && payload.useCases.length ? `use cases: ${payload.useCases.join(', ')}` : '',
1904
+ Array.isArray(payload.reasons) && payload.reasons.length ? `reasons: ${payload.reasons.join(', ')}` : '',
1905
+ payload.fasterModel ? `faster model: ${payload.fasterModel}` : '',
1906
+ ].filter(Boolean).join('\n'),
1907
+ status: payload.showBufferingUi === false ? 'completed' : 'warning',
1908
+ });
1909
+ continue;
1910
+ }
1911
+
1912
+ if (eventType === 'turn/moderationMetadata') {
1913
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1914
+ id: `${payload.turnId || payload.turn_id || 'turn'}-moderation`,
1915
+ type: 'event',
1916
+ title: 'Updated moderation metadata',
1917
+ detail: summarizeOutput(JSON.stringify(payload.metadata || {}, null, 2)),
1918
+ status: 'completed',
1919
+ });
1920
+ continue;
1921
+ }
1922
+
1923
+ if (eventType === 'rawResponseItem/completed') {
1924
+ const turn = ensureActive(payload.turn_id || payload.turnId);
1925
+ if (appendResponseItem(turn, payload.item, functionCalls)) continue;
1926
+ }
1927
+
1928
+ if (eventType === 'turn.failed' || eventType === 'error') {
1929
+ const turn = ensureActive(payload.turn_id || payload.turnId);
1930
+ const error = payload.error && typeof payload.error === 'object' ? payload.error : payload;
1931
+ appendProcess(turn, {
1932
+ type: 'error',
1933
+ title: compactInline(error.message || error.code, 'Error'),
1934
+ detail: error.detail || error.error || '',
1935
+ status: 'failed',
1936
+ });
1937
+ finishActive({
1938
+ ...payload,
1939
+ type: 'turn_aborted',
1940
+ });
1941
+ continue;
1942
+ }
1943
+
1944
+ if (eventType === 'warning' || eventType === 'guardianWarning' || eventType === 'configWarning' || eventType === 'deprecationNotice') {
1945
+ const message = payload.message || payload.summary || payload.title || 'Warning';
1946
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1947
+ type: 'warning',
1948
+ title: compactInline(message, 'Warning'),
1949
+ detail: payload.details || payload.path || '',
1950
+ status: 'warning',
1951
+ });
1952
+ continue;
1953
+ }
1954
+
1955
+ if (eventType === 'windows/worldWritableWarning') {
1956
+ const paths = Array.isArray(payload.samplePaths) ? payload.samplePaths : [];
1957
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
1958
+ type: 'warning',
1959
+ title: 'World-writable paths detected',
1960
+ detail: paths.concat(payload.extraCount ? [`... +${payload.extraCount} more`] : []).join('\n'),
1961
+ status: 'warning',
1962
+ });
1963
+ continue;
1964
+ }
1965
+
1966
+ if (eventType === 'thread/realtime/transcript/delta' || eventType === 'thread/realtime/transcript/done') {
1967
+ const turn = ensureActive(payload.turn_id || payload.turnId || `${payload.threadId || payload.thread_id || 'realtime'}-realtime`);
1968
+ const text = eventType.endsWith('/done') ? normalizeText(payload.text) : normalizeDeltaText(payload.delta);
1969
+ const role = String(payload.role || '').toLowerCase();
1970
+ if (role === 'user') {
1971
+ const nextMessage = eventType.endsWith('/done') ? text : `${turn.userMessage || ''}${text}`;
1972
+ turn.userMessage = visibleUserMessageText(nextMessage, {
1973
+ renderedAttachmentKinds: renderedAttachmentKindsForTurn(turn),
1974
+ });
1975
+ } else if (text) {
1976
+ turn.finalMessage = eventType.endsWith('/done') ? text : `${turn.finalMessage || ''}${text}`;
1977
+ }
1978
+ continue;
1979
+ }
1980
+
1981
+ if (eventType === 'thread/realtime/itemAdded') {
1982
+ const turn = ensureActive(payload.turn_id || payload.turnId || `${payload.threadId || payload.thread_id || 'realtime'}-realtime`);
1983
+ if (appendTurnItem(turn, payload.item, itemStatus(payload.item, 'completed'))) continue;
1984
+ if (appendResponseItem(turn, payload.item, functionCalls)) continue;
1985
+ }
1986
+
1987
+ if (eventType === 'thread/realtime/error') {
1988
+ appendProcess(ensureActive(payload.turn_id || payload.turnId || `${payload.threadId || payload.thread_id || 'realtime'}-realtime`), {
1989
+ type: 'error',
1990
+ title: 'Realtime error',
1991
+ detail: payload.message || JSON.stringify(payload),
1992
+ status: 'failed',
1993
+ });
1994
+ continue;
1995
+ }
1996
+
1997
+ if (eventType === 'event_msg') {
1998
+ if (payload.type === 'task_started' || payload.type === 'turn_started') {
1999
+ const turn = ensureActive(payload.turn_id);
2000
+ if (typeof payload.started_at === 'number') turn.startedAt = payload.started_at;
2001
+ turn.status = 'inProgress';
2002
+ continue;
2003
+ }
2004
+
2005
+ if (payload.type === 'user_message') {
2006
+ if (active && active.userMessage && (active.finalMessage || active.status !== 'inProgress')) {
2007
+ finishActive();
2008
+ }
2009
+ const turn = ensureActive(payload.turn_id);
2010
+ const images = imagesFromUserMessagePayload(payload);
2011
+ applyUserMessageToTurn(turn, {
2012
+ id: payload.id || payload.call_id,
2013
+ message: textFromUserMessagePayload(payload),
2014
+ images,
2015
+ });
2016
+ continue;
2017
+ }
2018
+
2019
+ if (payload.type === 'agent_message') {
2020
+ const message = normalizeText(payload.message);
2021
+ if (!message) continue;
2022
+ const turn = ensureActive(payload.turn_id);
2023
+ if (payload.phase === 'final_answer' || !payload.phase) {
2024
+ turn.finalMessage = finalMessageText(message, turn, payload.id || payload.call_id || `${turn.id}-agent-message`, payload.memoryCitation || payload.memory_citation);
2025
+ } else {
2026
+ const extracted = extractMemoryCitationBlock(message);
2027
+ appendMemoryCitation(turn, payload.id || payload.call_id || `${turn.id}-agent-message`, payload.memoryCitation || payload.memory_citation, extracted.detail);
2028
+ appendProcess(turn, {
2029
+ type: 'message',
2030
+ title: (extracted.text || message).split('\n')[0],
2031
+ detail: extracted.text || message,
2032
+ });
2033
+ }
2034
+ continue;
2035
+ }
2036
+
2037
+ if (payload.type === 'agent_reasoning' || payload.type === 'agent_reasoning_raw_content') {
2038
+ const text = normalizeText(payload.text || payload.message);
2039
+ if (text) {
2040
+ appendProcess(ensureActive(payload.turn_id), {
2041
+ type: 'reasoning',
2042
+ title: 'Reasoned',
2043
+ detail: text,
2044
+ });
2045
+ }
2046
+ continue;
2047
+ }
2048
+
2049
+ if (payload.type === 'item_completed' || payload.type === 'item_started' || payload.type === 'item_updated') {
2050
+ const turn = ensureActive(payload.turn_id);
2051
+ const status = payload.type === 'item_started'
2052
+ ? 'running'
2053
+ : payload.type === 'item_updated'
2054
+ ? itemStatus(payload.item, 'running')
2055
+ : itemStatus(payload.item, 'completed');
2056
+ if (appendTurnItem(turn, payload.item, status)) continue;
2057
+ }
2058
+
2059
+ if (payload.type === 'plan_update') {
2060
+ appendProcess(ensureActive(payload.turn_id), {
2061
+ id: payload.call_id || payload.id || 'plan-update',
2062
+ type: 'plan',
2063
+ title: 'Updated plan',
2064
+ detail: [
2065
+ normalizeText(payload.explanation || ''),
2066
+ detailForPlanSteps(payload.plan),
2067
+ ].filter(Boolean).join('\n'),
2068
+ status: 'completed',
2069
+ });
2070
+ continue;
2071
+ }
2072
+
2073
+ if (payload.type === 'raw_response_item') {
2074
+ const turn = ensureActive(payload.turn_id);
2075
+ if (appendResponseItem(turn, payload.item, functionCalls)) continue;
2076
+ }
2077
+
2078
+ if (payload.type === 'patch_apply_end') {
2079
+ appendProcess(ensureActive(payload.turn_id), {
2080
+ id: payload.call_id,
2081
+ type: 'patch',
2082
+ title: titleForPatch(payload),
2083
+ detail: [summarizePatchChanges(payload.changes), summarizeOutput([payload.stdout, payload.stderr].filter(Boolean).join('\n'))]
2084
+ .filter(Boolean)
2085
+ .join('\n'),
2086
+ status: payload.success === false ? 'failed' : 'completed',
2087
+ });
2088
+ continue;
2089
+ }
2090
+
2091
+ if (payload.type === 'exec_command_end') {
2092
+ appendProcess(ensureActive(payload.turn_id), {
2093
+ id: payload.call_id || payload.process_id,
2094
+ type: 'command',
2095
+ title: titleForCommandExecution(payload),
2096
+ detail: summarizeCommandOutput(payload),
2097
+ status: Number.isFinite(payload.exit_code) && payload.exit_code !== 0 ? 'failed' : 'completed',
2098
+ });
2099
+ continue;
2100
+ }
2101
+
2102
+ if (payload.type === 'mcp_tool_call_end') {
2103
+ appendProcess(ensureActive(payload.turn_id), {
2104
+ id: payload.call_id,
2105
+ type: 'mcp',
2106
+ title: titleForMcp(payload),
2107
+ detail: detailForMcp(payload),
2108
+ images: imagesFromStructuredOutput(payload.result),
2109
+ status: payload.result && payload.result.is_error ? 'failed' : 'completed',
2110
+ });
2111
+ continue;
2112
+ }
2113
+
2114
+ if (payload.type === 'web_search_end') {
2115
+ const query = payload.query || (payload.action && payload.action.query);
2116
+ appendProcess(ensureActive(payload.turn_id), {
2117
+ id: payload.call_id,
2118
+ type: 'web-search',
2119
+ title: compactInline(query, 'Searched web'),
2120
+ detail: payload.action ? summarizeOutput(JSON.stringify(payload.action, null, 2)) : '',
2121
+ status: 'completed',
2122
+ });
2123
+ continue;
2124
+ }
2125
+
2126
+ if (payload.type === 'image_generation_end') {
2127
+ appendProcess(ensureActive(payload.turn_id), {
2128
+ id: payload.call_id,
2129
+ type: 'image-generation',
2130
+ title: 'Generated image',
2131
+ detail: compactInline(payload.saved_path || payload.revised_prompt || payload.status || ''),
2132
+ images: imagesFromImageItem(payload),
2133
+ status: payload.status || 'completed',
2134
+ });
2135
+ continue;
2136
+ }
2137
+
2138
+ if (payload.type === 'dynamic_tool_call_request' || payload.type === 'dynamic_tool_call_response') {
2139
+ appendProcess(ensureActive(payload.turn_id || payload.turnId), {
2140
+ id: payload.call_id || payload.callId,
2141
+ type: 'tool',
2142
+ title: payload.tool ? `Used ${payload.tool}` : 'Used tool',
2143
+ detail: summarizeOutput(JSON.stringify(payload.arguments || payload.content_items || payload.contentItems || '', null, 2)),
2144
+ images: imagesFromStructuredOutput(payload.content_items || payload.contentItems),
2145
+ mergeDetail: payload.type === 'dynamic_tool_call_response',
2146
+ status: payload.type === 'dynamic_tool_call_request' ? 'running' : (payload.success === false ? 'failed' : 'completed'),
2147
+ });
2148
+ continue;
2149
+ }
2150
+
2151
+ if (payload.type === 'sub_agent_activity') {
2152
+ const kind = compactInline(payload.kind, 'activity');
2153
+ const agentPath = compactInline(payload.agent_path || payload.agentPath || payload.agent_thread_id || payload.agentThreadId);
2154
+ appendProcess(ensureActive(payload.turn_id), {
2155
+ id: payload.id || payload.call_id,
2156
+ type: 'sub-agent',
2157
+ title: agentPath ? `${kind} ${agentPath}` : `Sub-agent ${kind}`,
2158
+ status: 'completed',
2159
+ });
2160
+ continue;
2161
+ }
2162
+
2163
+ if (payload.type === 'entered_review_mode' || payload.type === 'exited_review_mode') {
2164
+ appendProcess(ensureActive(payload.turn_id), {
2165
+ type: 'review',
2166
+ title: payload.type === 'entered_review_mode' ? 'Entered review mode' : 'Exited review mode',
2167
+ detail: summarizeOutput(payload.user_facing_hint || JSON.stringify(payload.review_output || payload.target || '', null, 2)),
2168
+ });
2169
+ continue;
2170
+ }
2171
+
2172
+ if (payload.type === 'error' || payload.type === 'warning' || payload.type === 'guardian_warning' || payload.type === 'stream_error') {
2173
+ appendProcess(ensureActive(payload.turn_id), {
2174
+ type: payload.type === 'error' ? 'error' : 'warning',
2175
+ title: payload.type === 'error' ? 'Error' : 'Warning',
2176
+ detail: payload.message || payload.error || JSON.stringify(payload),
2177
+ status: payload.type === 'error' ? 'failed' : 'warning',
2178
+ });
2179
+ continue;
2180
+ }
2181
+
2182
+ if (payload.type === 'task_complete' || payload.type === 'turn_complete' || payload.type === 'task_aborted' || payload.type === 'turn_aborted') {
2183
+ finishActive(payload);
2184
+ continue;
2185
+ }
2186
+
2187
+ if (payload.type === 'context_compacted') {
2188
+ appendProcess(ensureActive(payload.turn_id), {
2189
+ type: 'compaction',
2190
+ title: 'Compacted context',
2191
+ });
2192
+ continue;
2193
+ }
2194
+
2195
+ if (payload.type === 'thread_rolled_back') {
2196
+ appendProcess(ensureActive(payload.turn_id), {
2197
+ type: 'rollback',
2198
+ title: 'Rolled back thread',
2199
+ detail: payload.message || payload.rollback_to_item_id || '',
2200
+ });
2201
+ continue;
2202
+ }
2203
+
2204
+ if (!isIgnoredEventType(payload.type)) {
2205
+ appendProcess(ensureActive(payload.turn_id), {
2206
+ id: payload.id || payload.call_id,
2207
+ type: 'event',
2208
+ title: humanizeType(payload.type, 'Codex event'),
2209
+ detail: summarizeOutput(JSON.stringify(payload, null, 2)),
2210
+ status: payload.status || '',
2211
+ });
2212
+ }
2213
+ }
2214
+
2215
+ if (eventType === 'response_item') {
2216
+ const turn = ensureActive(responseItemTurnId(payload));
2217
+ if (appendResponseItem(turn, payload, functionCalls)) continue;
2218
+ }
2219
+ }
2220
+
2221
+ if (active && !isEmptyTurn(active)) turns.push(active);
2222
+
2223
+ const maxTurns = Number.isFinite(options.maxTurns) ? Math.max(1, Math.floor(options.maxTurns)) : DEFAULT_MAX_TURNS;
2224
+ return turns
2225
+ .map(normalizeTurnForOutput)
2226
+ .filter(turn => turn && !isEmptyTurn(turn) && !isStandaloneThreadSettingsTurn(turn))
2227
+ .slice(-maxTurns);
2228
+ }
2229
+
2230
+ async function readTailLines(filePath, maxReadBytes = DEFAULT_MAX_READ_BYTES) {
2231
+ const stat = await fsp.stat(filePath);
2232
+ const start = Math.max(0, stat.size - maxReadBytes);
2233
+ const handle = await fsp.open(filePath, 'r');
2234
+ try {
2235
+ const buffer = Buffer.alloc(stat.size - start);
2236
+ const result = await handle.read(buffer, 0, buffer.length, start);
2237
+ let text = buffer.subarray(0, result.bytesRead).toString('utf8');
2238
+ if (start > 0) {
2239
+ const firstNewline = text.indexOf('\n');
2240
+ text = firstNewline >= 0 ? text.slice(firstNewline + 1) : '';
2241
+ }
2242
+ return {
2243
+ truncated: start > 0,
2244
+ lines: text.split('\n'),
2245
+ };
2246
+ } finally {
2247
+ await handle.close().catch(() => {});
2248
+ }
2249
+ }
2250
+
2251
+ function isUserMessageLine(line) {
2252
+ if (!line.trim()) return false;
2253
+ try {
2254
+ const event = JSON.parse(line);
2255
+ const { type: eventType, payload } = normalizeEventEnvelope(event);
2256
+ if (!payload) return false;
2257
+ if (eventType === 'event_msg') {
2258
+ if (payload.type !== 'user_message') return false;
2259
+ const text = textFromUserMessagePayload(payload);
2260
+ const images = imagesFromUserMessagePayload(payload);
2261
+ const files = extractComposerFileAttachments(text).files;
2262
+ return Boolean(visibleUserMessageText(text, {
2263
+ renderedAttachmentKinds: renderedAttachmentKindsForAttachments({ images, files }),
2264
+ }) || images.length > 0 || files.length > 0);
2265
+ }
2266
+ if (eventType === 'response_item') {
2267
+ if (payload.type !== 'message' || payload.role !== 'user') return false;
2268
+ const text = textFromContent(payload.content);
2269
+ const images = imagesFromContent(payload.content);
2270
+ const files = extractComposerFileAttachments(text).files;
2271
+ return Boolean(visibleUserMessageText(text, {
2272
+ renderedAttachmentKinds: renderedAttachmentKindsForAttachments({ images, files }),
2273
+ }) || images.length > 0 || files.length > 0);
2274
+ }
2275
+ if (eventType === 'item/started' || eventType === 'item/completed' || eventType === 'item.started' || eventType === 'item.completed') {
2276
+ if (turnItemType(payload.item) !== 'usermessage') return false;
2277
+ const text = textFromUserInput(payload.item?.content);
2278
+ const images = imagesFromContent(payload.item?.content);
2279
+ const files = extractComposerFileAttachments(text).files;
2280
+ return Boolean(visibleUserMessageText(text, {
2281
+ renderedAttachmentKinds: renderedAttachmentKindsForAttachments({ images, files }),
2282
+ }) || images.length > 0 || files.length > 0);
2283
+ }
2284
+ return false;
2285
+ } catch {
2286
+ return false;
2287
+ }
2288
+ }
2289
+
2290
+ function dropLeadingPartialTurn(lines) {
2291
+ const firstUserIndex = lines.findIndex(isUserMessageLine);
2292
+ return firstUserIndex > 0 ? lines.slice(firstUserIndex) : lines;
2293
+ }
2294
+
2295
+ async function readCodexTranscript(sessionId, options = {}) {
2296
+ const normalizedSessionId = String(sessionId || '').trim();
2297
+ if (!normalizedSessionId) {
2298
+ return { available: false, reason: 'missing-session-id', sessionId: '', turns: [] };
2299
+ }
2300
+
2301
+ const filePath = findCodexRolloutFile(normalizedSessionId, {
2302
+ codexHome: options.codexHome || path.join(os.homedir(), '.codex'),
2303
+ });
2304
+ if (!filePath) {
2305
+ return { available: false, reason: 'history-not-found', sessionId: normalizedSessionId, turns: [] };
2306
+ }
2307
+
2308
+ const tail = await readTailLines(filePath, options.maxReadBytes);
2309
+ const lines = tail.truncated ? dropLeadingPartialTurn(tail.lines) : tail.lines;
2310
+ const stat = fs.statSync(filePath);
2311
+ const maxTurns = Number.isFinite(options.maxTurns) ? Math.max(1, Math.floor(options.maxTurns)) : DEFAULT_MAX_TURNS;
2312
+ const allTurns = buildTranscriptFromLines(lines, { maxTurns: Number.MAX_SAFE_INTEGER });
2313
+ const turns = allTurns.slice(-maxTurns);
2314
+ return {
2315
+ available: true,
2316
+ sessionId: normalizedSessionId,
2317
+ filePath,
2318
+ updatedAt: new Date(stat.mtimeMs).toISOString(),
2319
+ source: 'codex-rollout-jsonl',
2320
+ hasMoreBefore: allTurns.length > maxTurns || tail.truncated,
2321
+ turnLimit: maxTurns,
2322
+ turns,
2323
+ };
2324
+ }
2325
+
2326
+ function buildTranscriptFromEvents(events, options = {}) {
2327
+ const lines = Array.isArray(events)
2328
+ ? events.filter(event => event && typeof event === 'object').map(event => JSON.stringify(event))
2329
+ : [];
2330
+ return buildTranscriptFromLines(lines, options);
2331
+ }
2332
+
2333
+ module.exports = {
2334
+ DEFAULT_MAX_TURNS,
2335
+ buildTranscriptFromEvents,
2336
+ buildTranscriptFromLines,
2337
+ dropLeadingPartialTurn,
2338
+ readCodexTranscript,
2339
+ stripUserMessagePrefix,
2340
+ textFromContent,
2341
+ };