@jingyi0605/codingns 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/README.md +6 -0
  2. package/bin/codingns.mjs +747 -28
  3. package/bin/office-mcp-server.mjs +620 -0
  4. package/dist/public/assets/{AdaptiveButlerPage-CgBX49t-.js → AdaptiveButlerPage-BsgVNRAa.js} +1 -1
  5. package/dist/public/assets/{App-tXOqoHNl.js → App-tPcbyRdS.js} +3 -3
  6. package/dist/public/assets/{BootstrapPage-DoRMz87R.js → BootstrapPage--MDOigQi.js} +1 -1
  7. package/dist/public/assets/{ConversationPage-DXR6Hp4O.js → ConversationPage-BBss5ED8.js} +6 -6
  8. package/dist/public/assets/{DesktopDetachPreviewPage-Cyk-ZYCk.js → DesktopDetachPreviewPage-CB8DoqwU.js} +1 -1
  9. package/dist/public/assets/{DesktopWindowPage-DNVNK3hs.js → DesktopWindowPage-GtIx5m8K.js} +1 -1
  10. package/dist/public/assets/FileContextPanel-BcM7AIT4.js +1 -0
  11. package/dist/public/assets/{GitSidebar-Cr3Z9OUI.js → GitSidebar-CMtkaxuI.js} +2 -2
  12. package/dist/public/assets/{MobileCreateSessionSheet-DMW0V6GJ.js → MobileCreateSessionSheet-CrFY41_8.js} +1 -1
  13. package/dist/public/assets/{MobileTopHeaderFrame-CkdnZ_MU.js → MobileTopHeaderFrame-DGVOzXyg.js} +1 -1
  14. package/dist/public/assets/{MobileWorkspaceSwitcherHeader-KIbqBYJN.js → MobileWorkspaceSwitcherHeader-DLkACTnQ.js} +1 -1
  15. package/dist/public/assets/{RelayConnectEntryPage-DIRBH3hw.js → RelayConnectEntryPage-0MPPjxtQ.js} +1 -1
  16. package/dist/public/assets/{ServerSettingsModal-C-9RxdWP.js → ServerSettingsModal-vgOhwus4.js} +1 -1
  17. package/dist/public/assets/{SessionIndexPage-bRlIydRA.js → SessionIndexPage-KK626Ra9.js} +1 -1
  18. package/dist/public/assets/{SettingsPage-CMEt4ua9.js → SettingsPage-B3edBJIo.js} +2 -2
  19. package/dist/public/assets/{TerminalManagerPanel-2bi9wVhT.js → TerminalManagerPanel-BxlhZp8c.js} +1 -1
  20. package/dist/public/assets/{TerminalPage-DayZz2Tf.js → TerminalPage-B6Rdhylx.js} +1 -1
  21. package/dist/public/assets/{TerminalRuntimeFallbackModal-DgwYcp-Y.js → TerminalRuntimeFallbackModal-BVLfrpSa.js} +1 -1
  22. package/dist/public/assets/{ToolFilesPage-YvnP_FXW.js → ToolFilesPage-N_gwwUjD.js} +1 -1
  23. package/dist/public/assets/{ToolGitPage-GMcQKtV9.js → ToolGitPage-DOcuuWM1.js} +1 -1
  24. package/dist/public/assets/{ToolProcessesPage-DFIQ7BCd.js → ToolProcessesPage-D-FfJ7Re.js} +1 -1
  25. package/dist/public/assets/{ToolsHomePage-CSilFzXR.js → ToolsHomePage-CHfPxd20.js} +1 -1
  26. package/dist/public/assets/{WorkbenchLandingPage-1VtToSz9.js → WorkbenchLandingPage-CTTnfovY.js} +1 -1
  27. package/dist/public/assets/WorkbenchLayout-CbpJg0g1.js +244 -0
  28. package/dist/public/assets/{WorkbenchModal-BWXYSXmC.js → WorkbenchModal-Bt_1fYmM.js} +1 -1
  29. package/dist/public/assets/WorkbenchShellRoute-B4XB8SwG.css +1 -0
  30. package/dist/public/assets/WorkbenchShellRoute-DyaMnPfS.js +1 -0
  31. package/dist/public/assets/{WorkspaceDebugDetailPage-Ux8_Q7la.js → WorkspaceDebugDetailPage-s7yuDIxR.js} +1 -1
  32. package/dist/public/assets/{WorkspaceDetailPage-B402p99m.js → WorkspaceDetailPage-Cf-gVpqK.js} +1 -1
  33. package/dist/public/assets/{WorkspaceHomePage-D2pob6HI.js → WorkspaceHomePage-COf6I8sT.js} +1 -1
  34. package/dist/public/assets/{client-runtime-manager-C5D76ewj.js → client-runtime-manager-DGdKvYzx.js} +1 -1
  35. package/dist/public/assets/file-tree-icon-BeHqeru9.js +590 -0
  36. package/dist/public/assets/index-CcaQt50x.css +1 -0
  37. package/dist/public/assets/index-CuzMc7q2.js +42 -0
  38. package/dist/public/assets/{login-direct-candidate-resolver-wXSaB0i7.js → login-direct-candidate-resolver-DEP_xCmR.js} +1 -1
  39. package/dist/public/assets/{model-switch-api-CPtou49j.js → model-switch-api-c6kcbBGm.js} +1 -1
  40. package/dist/public/assets/{preferences-service-CdaK7zA8.js → preferences-service-CV6Ih0BG.js} +1 -1
  41. package/dist/public/assets/{realtime-client-BjQazYsK.js → realtime-client-CRCx5xBt.js} +1 -1
  42. package/dist/public/assets/{relay-entry-BwE5nw0l.js → relay-entry-C751A-Sm.js} +1 -1
  43. package/dist/public/assets/{terminal-runtime-meta-C-Lbyx2i.js → terminal-runtime-meta-CRAVR-8G.js} +1 -1
  44. package/dist/public/assets/{useRegisteredDebugTemplates-BM7-c-gx.js → useRegisteredDebugTemplates-D6YtNS0r.js} +1 -1
  45. package/dist/public/index.html +2 -2
  46. package/dist/server/config/env.d.ts +3 -0
  47. package/dist/server/config/env.js +67 -1
  48. package/dist/server/config/env.js.map +1 -1
  49. package/dist/server/config/opencode-base-url-resolver.d.ts +3 -2
  50. package/dist/server/config/opencode-base-url-resolver.js +64 -24
  51. package/dist/server/config/opencode-base-url-resolver.js.map +1 -1
  52. package/dist/server/middlewares/auth-guard.js +4 -0
  53. package/dist/server/middlewares/auth-guard.js.map +1 -1
  54. package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +168 -1
  55. package/dist/server/modules/assistant-capability/assistant-capability-controller.js +205 -4
  56. package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
  57. package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +296 -2
  58. package/dist/server/modules/assistant-capability/assistant-capability-service.js +872 -3
  59. package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
  60. package/dist/server/modules/auth/auth-service.d.ts +21 -1
  61. package/dist/server/modules/auth/auth-service.js +64 -0
  62. package/dist/server/modules/auth/auth-service.js.map +1 -1
  63. package/dist/server/modules/browser-runtime/browser-profile-service.d.ts +26 -0
  64. package/dist/server/modules/browser-runtime/browser-profile-service.js +85 -0
  65. package/dist/server/modules/browser-runtime/browser-profile-service.js.map +1 -0
  66. package/dist/server/modules/browser-runtime/browser-runtime-controller.d.ts +69 -0
  67. package/dist/server/modules/browser-runtime/browser-runtime-controller.js +83 -0
  68. package/dist/server/modules/browser-runtime/browser-runtime-controller.js.map +1 -0
  69. package/dist/server/modules/browser-runtime/browser-runtime-service.d.ts +56 -0
  70. package/dist/server/modules/browser-runtime/browser-runtime-service.js +215 -0
  71. package/dist/server/modules/browser-runtime/browser-runtime-service.js.map +1 -0
  72. package/dist/server/modules/browser-runtime/browser-task-execution-support.d.ts +65 -0
  73. package/dist/server/modules/browser-runtime/browser-task-execution-support.js +432 -0
  74. package/dist/server/modules/browser-runtime/browser-task-execution-support.js.map +1 -0
  75. package/dist/server/modules/browser-runtime/browser-task-executor-registry.d.ts +7 -0
  76. package/dist/server/modules/browser-runtime/browser-task-executor-registry.js +21 -0
  77. package/dist/server/modules/browser-runtime/browser-task-executor-registry.js.map +1 -0
  78. package/dist/server/modules/browser-runtime/browser-task-executor.d.ts +55 -0
  79. package/dist/server/modules/browser-runtime/browser-task-executor.js +2 -0
  80. package/dist/server/modules/browser-runtime/browser-task-executor.js.map +1 -0
  81. package/dist/server/modules/browser-runtime/browser-task-payload.d.ts +31 -0
  82. package/dist/server/modules/browser-runtime/browser-task-payload.js +55 -0
  83. package/dist/server/modules/browser-runtime/browser-task-payload.js.map +1 -0
  84. package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.d.ts +19 -0
  85. package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.js +219 -0
  86. package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.js.map +1 -0
  87. package/dist/server/modules/browser-runtime/opencli-browser-bridge-service.d.ts +15 -0
  88. package/dist/server/modules/browser-runtime/opencli-browser-bridge-service.js +33 -0
  89. package/dist/server/modules/browser-runtime/opencli-browser-bridge-service.js.map +1 -0
  90. package/dist/server/modules/browser-runtime/playwright-browser-executor.d.ts +16 -0
  91. package/dist/server/modules/browser-runtime/playwright-browser-executor.js +272 -0
  92. package/dist/server/modules/browser-runtime/playwright-browser-executor.js.map +1 -0
  93. package/dist/server/modules/butler/butler-auth-service.js +4 -0
  94. package/dist/server/modules/butler/butler-auth-service.js.map +1 -1
  95. package/dist/server/modules/butler/butler-inbox-instruction-adapter.js +1 -0
  96. package/dist/server/modules/butler/butler-inbox-instruction-adapter.js.map +1 -1
  97. package/dist/server/modules/butler/butler-session-summary-service.d.ts +1 -0
  98. package/dist/server/modules/butler/butler-session-summary-service.js +5 -3
  99. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
  100. package/dist/server/modules/butler/butler-workspace-context.js +23 -0
  101. package/dist/server/modules/butler/butler-workspace-context.js.map +1 -1
  102. package/dist/server/modules/channels/wechat-claw-client.d.ts +51 -0
  103. package/dist/server/modules/channels/wechat-claw-client.js +245 -0
  104. package/dist/server/modules/channels/wechat-claw-client.js.map +1 -0
  105. package/dist/server/modules/debug-target/debug-target-service.d.ts +2 -0
  106. package/dist/server/modules/debug-target/debug-target-service.js +14 -0
  107. package/dist/server/modules/debug-target/debug-target-service.js.map +1 -1
  108. package/dist/server/modules/document-runtime/document-docx-fallback-renderer.py +139 -0
  109. package/dist/server/modules/document-runtime/document-export-executor.d.ts +50 -0
  110. package/dist/server/modules/document-runtime/document-export-executor.js +827 -0
  111. package/dist/server/modules/document-runtime/document-export-executor.js.map +1 -0
  112. package/dist/server/modules/document-runtime/document-runtime-controller.d.ts +127 -0
  113. package/dist/server/modules/document-runtime/document-runtime-controller.js +131 -0
  114. package/dist/server/modules/document-runtime/document-runtime-controller.js.map +1 -0
  115. package/dist/server/modules/document-runtime/document-runtime-service.d.ts +125 -0
  116. package/dist/server/modules/document-runtime/document-runtime-service.js +706 -0
  117. package/dist/server/modules/document-runtime/document-runtime-service.js.map +1 -0
  118. package/dist/server/modules/office/office-controller.d.ts +77 -0
  119. package/dist/server/modules/office/office-controller.js +174 -0
  120. package/dist/server/modules/office/office-controller.js.map +1 -0
  121. package/dist/server/modules/office/office-preview-link-service.d.ts +27 -0
  122. package/dist/server/modules/office/office-preview-link-service.js +121 -0
  123. package/dist/server/modules/office/office-preview-link-service.js.map +1 -0
  124. package/dist/server/modules/office/office-service.d.ts +67 -0
  125. package/dist/server/modules/office/office-service.js +359 -0
  126. package/dist/server/modules/office/office-service.js.map +1 -0
  127. package/dist/server/modules/opencli/opencli-bridge-skill-service.js +38 -14
  128. package/dist/server/modules/opencli/opencli-bridge-skill-service.js.map +1 -1
  129. package/dist/server/modules/opencli/opencli-install-discovery.d.ts +4 -0
  130. package/dist/server/modules/opencli/opencli-install-discovery.js +94 -0
  131. package/dist/server/modules/opencli/opencli-install-discovery.js.map +1 -1
  132. package/dist/server/modules/opencli/opencli-runtime-builder.js +29 -0
  133. package/dist/server/modules/opencli/opencli-runtime-builder.js.map +1 -1
  134. package/dist/server/modules/opencli/opencli-runtime-guard.d.ts +2 -0
  135. package/dist/server/modules/opencli/opencli-runtime-guard.js +5 -0
  136. package/dist/server/modules/opencli/opencli-runtime-guard.js.map +1 -0
  137. package/dist/server/modules/ops-runtime/ops-runtime-controller.d.ts +70 -0
  138. package/dist/server/modules/ops-runtime/ops-runtime-controller.js +83 -0
  139. package/dist/server/modules/ops-runtime/ops-runtime-controller.js.map +1 -0
  140. package/dist/server/modules/ops-runtime/ops-runtime-service.d.ts +80 -0
  141. package/dist/server/modules/ops-runtime/ops-runtime-service.js +327 -0
  142. package/dist/server/modules/ops-runtime/ops-runtime-service.js.map +1 -0
  143. package/dist/server/modules/ops-runtime/ssh-ops-executor.d.ts +41 -0
  144. package/dist/server/modules/ops-runtime/ssh-ops-executor.js +478 -0
  145. package/dist/server/modules/ops-runtime/ssh-ops-executor.js.map +1 -0
  146. package/dist/server/modules/presentation/presentation-controller.d.ts +22 -0
  147. package/dist/server/modules/presentation/presentation-controller.js +59 -0
  148. package/dist/server/modules/presentation/presentation-controller.js.map +1 -0
  149. package/dist/server/modules/presentation/presentation-export-task-service.d.ts +24 -0
  150. package/dist/server/modules/presentation/presentation-export-task-service.js +137 -0
  151. package/dist/server/modules/presentation/presentation-export-task-service.js.map +1 -0
  152. package/dist/server/modules/presentation/presentation-export-types.d.ts +12 -0
  153. package/dist/server/modules/presentation/presentation-export-types.js +2 -0
  154. package/dist/server/modules/presentation/presentation-export-types.js.map +1 -0
  155. package/dist/server/modules/presentation/presentation-pdf-export-service.d.ts +20 -0
  156. package/dist/server/modules/presentation/presentation-pdf-export-service.js +29 -0
  157. package/dist/server/modules/presentation/presentation-pdf-export-service.js.map +1 -0
  158. package/dist/server/modules/presentation/presentation-pptx-export-service.d.ts +20 -0
  159. package/dist/server/modules/presentation/presentation-pptx-export-service.js +64 -0
  160. package/dist/server/modules/presentation/presentation-pptx-export-service.js.map +1 -0
  161. package/dist/server/modules/presentation/presentation-renderer.d.ts +21 -0
  162. package/dist/server/modules/presentation/presentation-renderer.js +208 -0
  163. package/dist/server/modules/presentation/presentation-renderer.js.map +1 -0
  164. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js +3 -3
  165. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js.map +1 -1
  166. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js +6 -1
  167. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js.map +1 -1
  168. package/dist/server/modules/sessions/codex-app-server-helper-process.js +2 -1
  169. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  170. package/dist/server/modules/sessions/session-controller.d.ts +1 -0
  171. package/dist/server/modules/sessions/session-controller.js +59 -4
  172. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  173. package/dist/server/modules/sessions/session-history-service.js +17 -5
  174. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  175. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +5 -1
  176. package/dist/server/modules/sessions/session-live-runtime-service.js +86 -8
  177. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  178. package/dist/server/modules/sessions/session-provider-config-service.d.ts +25 -1
  179. package/dist/server/modules/sessions/session-provider-config-service.js +54 -5
  180. package/dist/server/modules/sessions/session-provider-config-service.js.map +1 -1
  181. package/dist/server/modules/sessions/workspace-office-mcp-config.d.ts +14 -0
  182. package/dist/server/modules/sessions/workspace-office-mcp-config.js +54 -0
  183. package/dist/server/modules/sessions/workspace-office-mcp-config.js.map +1 -0
  184. package/dist/server/modules/sessions/workspace-session-auth-service.d.ts +27 -0
  185. package/dist/server/modules/sessions/workspace-session-auth-service.js +109 -0
  186. package/dist/server/modules/sessions/workspace-session-auth-service.js.map +1 -0
  187. package/dist/server/modules/sessions/workspace-session-runtime-context-service.d.ts +50 -0
  188. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js +332 -0
  189. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js.map +1 -0
  190. package/dist/server/modules/skills/assistant-runtime-skill-catalog.js +5 -0
  191. package/dist/server/modules/skills/assistant-runtime-skill-catalog.js.map +1 -1
  192. package/dist/server/modules/skills/builtin-skills/codingns-workspace-session/SKILL.md +67 -0
  193. package/dist/server/modules/skills/builtin-skills/codingns-workspace-session/agents/openai.yaml +4 -0
  194. package/dist/server/modules/skills/builtin-skills/codingns-workspace-session/references/cli-workflow.md +133 -0
  195. package/dist/server/modules/skills/skill-controller.d.ts +7 -0
  196. package/dist/server/modules/skills/skill-controller.js +7 -0
  197. package/dist/server/modules/skills/skill-controller.js.map +1 -1
  198. package/dist/server/modules/skills/skill-manager-service.d.ts +61 -0
  199. package/dist/server/modules/skills/skill-manager-service.js +218 -0
  200. package/dist/server/modules/skills/skill-manager-service.js.map +1 -1
  201. package/dist/server/modules/skills/skill-name-policy.js +2 -1
  202. package/dist/server/modules/skills/skill-name-policy.js.map +1 -1
  203. package/dist/server/modules/tasks/task-helper-client.d.ts +1 -0
  204. package/dist/server/modules/tasks/task-helper-client.js +45 -9
  205. package/dist/server/modules/tasks/task-helper-client.js.map +1 -1
  206. package/dist/server/modules/tasks/task-types.d.ts +5 -0
  207. package/dist/server/modules/tasks/task-types.js +6 -1
  208. package/dist/server/modules/tasks/task-types.js.map +1 -1
  209. package/dist/server/modules/terminal/runtime/conpty-session-agent-process.js +2 -1
  210. package/dist/server/modules/terminal/runtime/conpty-session-agent-process.js.map +1 -1
  211. package/dist/server/modules/terminal/runtime/node-pty-loader.d.ts +5 -0
  212. package/dist/server/modules/terminal/runtime/node-pty-loader.js +68 -0
  213. package/dist/server/modules/terminal/runtime/node-pty-loader.js.map +1 -0
  214. package/dist/server/modules/terminal/runtime/pty-broker-agent-process.js +2 -1
  215. package/dist/server/modules/terminal/runtime/pty-broker-agent-process.js.map +1 -1
  216. package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.js +6 -9
  217. package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.js.map +1 -1
  218. package/dist/server/modules/terminal/runtime/pty-runtime-manager.js +6 -9
  219. package/dist/server/modules/terminal/runtime/pty-runtime-manager.js.map +1 -1
  220. package/dist/server/routes/assistant.d.ts +2 -1
  221. package/dist/server/routes/assistant.js +20 -1
  222. package/dist/server/routes/assistant.js.map +1 -1
  223. package/dist/server/routes/browser-runtime.d.ts +3 -0
  224. package/dist/server/routes/browser-runtime.js +14 -0
  225. package/dist/server/routes/browser-runtime.js.map +1 -0
  226. package/dist/server/routes/document-runtime.d.ts +3 -0
  227. package/dist/server/routes/document-runtime.js +18 -0
  228. package/dist/server/routes/document-runtime.js.map +1 -0
  229. package/dist/server/routes/office.d.ts +3 -0
  230. package/dist/server/routes/office.js +16 -0
  231. package/dist/server/routes/office.js.map +1 -0
  232. package/dist/server/routes/ops-runtime.d.ts +3 -0
  233. package/dist/server/routes/ops-runtime.js +13 -0
  234. package/dist/server/routes/ops-runtime.js.map +1 -0
  235. package/dist/server/routes/presentation.d.ts +3 -0
  236. package/dist/server/routes/presentation.js +5 -0
  237. package/dist/server/routes/presentation.js.map +1 -0
  238. package/dist/server/routes/skills.js +1 -0
  239. package/dist/server/routes/skills.js.map +1 -1
  240. package/dist/server/server/create-server.d.ts +36 -0
  241. package/dist/server/server/create-server.js +215 -4
  242. package/dist/server/server/create-server.js.map +1 -1
  243. package/dist/server/server/release-manifest-sync.d.ts +1 -0
  244. package/dist/server/server/release-manifest-sync.js +2 -2
  245. package/dist/server/server/release-manifest-sync.js.map +1 -1
  246. package/dist/server/server/start-host.js +1 -1
  247. package/dist/server/server/start-host.js.map +1 -1
  248. package/dist/server/storage/repositories/auth-token-repository.js +22 -6
  249. package/dist/server/storage/repositories/auth-token-repository.js.map +1 -1
  250. package/dist/server/storage/repositories/browser-profile-repository.d.ts +18 -0
  251. package/dist/server/storage/repositories/browser-profile-repository.js +134 -0
  252. package/dist/server/storage/repositories/browser-profile-repository.js.map +1 -0
  253. package/dist/server/storage/repositories/document-comment-repository.d.ts +10 -0
  254. package/dist/server/storage/repositories/document-comment-repository.js +118 -0
  255. package/dist/server/storage/repositories/document-comment-repository.js.map +1 -0
  256. package/dist/server/storage/repositories/document-repository.d.ts +16 -0
  257. package/dist/server/storage/repositories/document-repository.js +109 -0
  258. package/dist/server/storage/repositories/document-repository.js.map +1 -0
  259. package/dist/server/storage/repositories/document-revision-repository.d.ts +10 -0
  260. package/dist/server/storage/repositories/document-revision-repository.js +79 -0
  261. package/dist/server/storage/repositories/document-revision-repository.js.map +1 -0
  262. package/dist/server/storage/repositories/document-template-repository.d.ts +13 -0
  263. package/dist/server/storage/repositories/document-template-repository.js +244 -0
  264. package/dist/server/storage/repositories/document-template-repository.js.map +1 -0
  265. package/dist/server/storage/repositories/office-approval-repository.d.ts +11 -0
  266. package/dist/server/storage/repositories/office-approval-repository.js +109 -0
  267. package/dist/server/storage/repositories/office-approval-repository.js.map +1 -0
  268. package/dist/server/storage/repositories/office-artifact-repository.d.ts +10 -0
  269. package/dist/server/storage/repositories/office-artifact-repository.js +89 -0
  270. package/dist/server/storage/repositories/office-artifact-repository.js.map +1 -0
  271. package/dist/server/storage/repositories/office-audit-event-repository.d.ts +8 -0
  272. package/dist/server/storage/repositories/office-audit-event-repository.js +54 -0
  273. package/dist/server/storage/repositories/office-audit-event-repository.js.map +1 -0
  274. package/dist/server/storage/repositories/office-connector-repository.d.ts +10 -0
  275. package/dist/server/storage/repositories/office-connector-repository.js +97 -0
  276. package/dist/server/storage/repositories/office-connector-repository.js.map +1 -0
  277. package/dist/server/storage/repositories/office-receipt-repository.d.ts +8 -0
  278. package/dist/server/storage/repositories/office-receipt-repository.js +48 -0
  279. package/dist/server/storage/repositories/office-receipt-repository.js.map +1 -0
  280. package/dist/server/storage/repositories/office-rollback-record-repository.d.ts +8 -0
  281. package/dist/server/storage/repositories/office-rollback-record-repository.js +60 -0
  282. package/dist/server/storage/repositories/office-rollback-record-repository.js.map +1 -0
  283. package/dist/server/storage/repositories/office-task-repository.d.ts +19 -0
  284. package/dist/server/storage/repositories/office-task-repository.js +199 -0
  285. package/dist/server/storage/repositories/office-task-repository.js.map +1 -0
  286. package/dist/server/storage/repositories/office-task-step-repository.d.ts +10 -0
  287. package/dist/server/storage/repositories/office-task-step-repository.js +110 -0
  288. package/dist/server/storage/repositories/office-task-step-repository.js.map +1 -0
  289. package/dist/server/storage/repositories/ops-target-repository.d.ts +16 -0
  290. package/dist/server/storage/repositories/ops-target-repository.js +119 -0
  291. package/dist/server/storage/repositories/ops-target-repository.js.map +1 -0
  292. package/dist/server/storage/repositories/session-binding-repository.d.ts +4 -0
  293. package/dist/server/storage/repositories/session-binding-repository.js +70 -69
  294. package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
  295. package/dist/server/storage/repositories/session-changed-file-repository.d.ts +6 -0
  296. package/dist/server/storage/repositories/session-changed-file-repository.js +44 -43
  297. package/dist/server/storage/repositories/session-changed-file-repository.js.map +1 -1
  298. package/dist/server/storage/repositories/session-fork-repository.d.ts +2 -0
  299. package/dist/server/storage/repositories/session-fork-repository.js +42 -41
  300. package/dist/server/storage/repositories/session-fork-repository.js.map +1 -1
  301. package/dist/server/storage/repositories/session-index-repository.d.ts +5 -0
  302. package/dist/server/storage/repositories/session-index-repository.js +153 -152
  303. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  304. package/dist/server/storage/repositories/session-message-attachment-repository.d.ts +7 -0
  305. package/dist/server/storage/repositories/session-message-attachment-repository.js +91 -90
  306. package/dist/server/storage/repositories/session-message-attachment-repository.js.map +1 -1
  307. package/dist/server/storage/repositories/session-message-origin-repository.d.ts +2 -0
  308. package/dist/server/storage/repositories/session-message-origin-repository.js +25 -24
  309. package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -1
  310. package/dist/server/storage/repositories/session-state-repository.d.ts +2 -0
  311. package/dist/server/storage/repositories/session-state-repository.js +35 -34
  312. package/dist/server/storage/repositories/session-state-repository.js.map +1 -1
  313. package/dist/server/storage/repositories/session-status-snapshot-repository.d.ts +2 -0
  314. package/dist/server/storage/repositories/session-status-snapshot-repository.js +25 -24
  315. package/dist/server/storage/repositories/session-status-snapshot-repository.js.map +1 -1
  316. package/dist/server/storage/sqlite/client.js +123 -1
  317. package/dist/server/storage/sqlite/client.js.map +1 -1
  318. package/dist/server/storage/sqlite/schema.sql +300 -1
  319. package/dist/server/types/domain.d.ts +205 -1
  320. package/package.json +14 -7
  321. package/scripts/postinstall.mjs +170 -36
  322. package/dist/public/assets/FileContextPanel-xGTYDclT.js +0 -1
  323. package/dist/public/assets/WorkbenchLayout-DScHaza9.js +0 -244
  324. package/dist/public/assets/WorkbenchShellRoute-DN6LdrqC.js +0 -1
  325. package/dist/public/assets/WorkbenchShellRoute-DhQo_0vu.css +0 -1
  326. package/dist/public/assets/file-tree-icon-lfU9Ag77.js +0 -3
  327. package/dist/public/assets/index-CFYXCsyx.css +0 -1
  328. package/dist/public/assets/index-NGxWr8Ix.js +0 -42
  329. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.d.ts +0 -42
  330. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js +0 -346
  331. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js.map +0 -1
  332. package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.d.ts +0 -1
  333. package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.js +0 -80
  334. package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.js.map +0 -1
  335. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +0 -18
  336. package/node_modules/@codingns/session-sync-core/dist/index.js +0 -19
  337. package/node_modules/@codingns/session-sync-core/dist/index.js.map +0 -1
  338. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +0 -18
  339. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +0 -659
  340. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +0 -1
  341. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +0 -11
  342. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +0 -72
  343. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +0 -1
  344. package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +0 -67
  345. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +0 -752
  346. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +0 -1
  347. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +0 -48
  348. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +0 -1184
  349. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +0 -1
  350. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.d.ts +0 -11
  351. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +0 -105
  352. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +0 -1
  353. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +0 -84
  354. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +0 -2436
  355. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +0 -1
  356. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +0 -47
  357. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +0 -1480
  358. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +0 -1
  359. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +0 -33
  360. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +0 -684
  361. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +0 -1
  362. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.d.ts +0 -9
  363. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js +0 -17
  364. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js.map +0 -1
  365. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.d.ts +0 -1
  366. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.js +0 -8
  367. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.js.map +0 -1
  368. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.d.ts +0 -48
  369. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js +0 -373
  370. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js.map +0 -1
  371. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +0 -61
  372. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +0 -1191
  373. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +0 -1
  374. package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +0 -27
  375. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +0 -415
  376. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +0 -1
  377. package/node_modules/@codingns/session-sync-core/dist/registry.d.ts +0 -7
  378. package/node_modules/@codingns/session-sync-core/dist/registry.js +0 -22
  379. package/node_modules/@codingns/session-sync-core/dist/registry.js.map +0 -1
  380. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +0 -24
  381. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +0 -329
  382. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +0 -1
  383. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +0 -30
  384. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +0 -939
  385. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +0 -1
  386. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.d.ts +0 -1
  387. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js +0 -16
  388. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js.map +0 -1
  389. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +0 -70
  390. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +0 -2571
  391. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +0 -1
  392. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +0 -21
  393. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +0 -561
  394. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +0 -1
  395. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +0 -38
  396. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +0 -911
  397. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +0 -1
  398. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.d.ts +0 -15
  399. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js +0 -16
  400. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js.map +0 -1
  401. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.d.ts +0 -37
  402. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +0 -963
  403. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +0 -1
  404. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.d.ts +0 -21
  405. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +0 -168
  406. package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +0 -1
  407. package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +0 -152
  408. package/node_modules/@codingns/session-sync-core/dist/runtime/types.js +0 -2
  409. package/node_modules/@codingns/session-sync-core/dist/runtime/types.js.map +0 -1
  410. package/node_modules/@codingns/session-sync-core/dist/services.d.ts +0 -28
  411. package/node_modules/@codingns/session-sync-core/dist/services.js +0 -148
  412. package/node_modules/@codingns/session-sync-core/dist/services.js.map +0 -1
  413. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +0 -6
  414. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +0 -9
  415. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +0 -1
  416. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +0 -198
  417. package/node_modules/@codingns/session-sync-core/dist/types.js +0 -2
  418. package/node_modules/@codingns/session-sync-core/dist/types.js.map +0 -1
  419. package/node_modules/@codingns/session-sync-core/package.json +0 -33
@@ -1,2436 +0,0 @@
1
- import { basename, dirname, join } from "node:path";
2
- import { existsSync, readFileSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
3
- import crypto from "node:crypto";
4
- import { appendJsonLine, createRawRef, encodeCursor, ensureDirectory, extractTextBlocks, ensureText, messageIdFromRawRef, messageIdFromStableKey, nextTimestamp, normalizeWorkspacePath, readFirstNonEmptyLine, readJsonLines, readTrailingJsonLines, safeDate, sliceHistory, stringifyStructuredValue, walkJsonlFiles } from "./utils.js";
5
- import { buildCodexResumeHistoryFromRawStore } from "../codex-resume-history.js";
6
- import { buildApplyPatchFromCodexCommandLikeValue } from "../patch-builder.js";
7
- import { loadDatabaseSync } from "../sqlite/node-sqlite.js";
8
- const CODEX_SESSION_TITLE_MAX_LENGTH = 48;
9
- const HISTORY_CACHE_LIMIT = 6;
10
- const SESSION_SUMMARY_CACHE_LIMIT = 512;
11
- const SPAWN_RELATION_SCAN_CACHE_LIMIT = 512;
12
- const RECENT_HISTORY_INITIAL_BYTES = 256 * 1024;
13
- const RECENT_HISTORY_MAX_BYTES = 4 * 1024 * 1024;
14
- const RECENT_HISTORY_BUFFER_MESSAGES = 24;
15
- const CODEX_CONFIG_CONTEXT_WINDOW_PATTERN = /(?:^|\n)\s*model_context_window\s*=\s*(\d+)\s*(?:\n|$)/i;
16
- const KNOWN_CODEX_CONTEXT_WINDOWS = new Map([
17
- ["gpt-5.3-codex", 400_000],
18
- ["codex-mini-latest", 200_000]
19
- ]);
20
- export class CodexAdapter {
21
- options;
22
- providerId = "codex";
23
- historyCache = new Map();
24
- sessionSummaryCache = new Map();
25
- spawnRelationScanCache = new Map();
26
- threadMetadataIndexCache = null;
27
- constructor(options) {
28
- this.options = options;
29
- }
30
- async detectSessions(workspacePath, options) {
31
- const discovery = await this.detectSessionsDetailed(workspacePath, options);
32
- return discovery.sessions;
33
- }
34
- async detectSessionsDetailed(workspacePath, options) {
35
- const startedAt = Date.now();
36
- const targetPath = normalizeWorkspacePath(workspacePath);
37
- const knownSessions = (options?.knownSessions ?? []).filter((session) => session.provider === this.providerId);
38
- const threadMetadataIndex = this.readThreadMetadataIndex();
39
- const files = this.listSessionFiles(targetPath, threadMetadataIndex, knownSessions);
40
- const knownByRawStoreRef = new Map(knownSessions.map((session) => [session.rawStoreRef, session]));
41
- const knownByProviderSessionId = new Map(knownSessions.map((session) => [session.providerSessionId, session]));
42
- const sessionsByProviderSessionId = new Map();
43
- const retainedSummaries = [];
44
- const pendingFiles = [];
45
- let scannedFiles = 0;
46
- let skippedByMtimeSize = 0;
47
- let parsedFiles = 0;
48
- let bytesRead = 0;
49
- for (const filePath of files) {
50
- scannedFiles += 1;
51
- const stats = statSync(filePath);
52
- const cachedSummary = this.sessionSummaryCache.get(filePath);
53
- const fileSessionId = basename(filePath, ".jsonl");
54
- const sessionIdentity = this.readSessionIdentity(filePath, fileSessionId);
55
- if (cachedSummary
56
- && cachedSummary.mtimeMs === stats.mtimeMs
57
- && cachedSummary.size === stats.size
58
- && (!cachedSummary.summary
59
- || !sessionIdentity
60
- || cachedSummary.summary.providerSessionId === sessionIdentity.threadId)) {
61
- this.touchSessionSummaryCache(filePath, cachedSummary);
62
- if (cachedSummary.summary
63
- && hasUsableCodexTitle(cachedSummary.summary.title)
64
- && normalizeWorkspacePath(cachedSummary.summary.workspacePath) === targetPath) {
65
- skippedByMtimeSize += 1;
66
- retainedSummaries.push({
67
- filePath,
68
- stats,
69
- sessionIdentity,
70
- summary: cachedSummary.summary
71
- });
72
- continue;
73
- }
74
- if (cachedSummary.workspacePath
75
- && normalizeWorkspacePath(cachedSummary.workspacePath) !== targetPath) {
76
- continue;
77
- }
78
- }
79
- const knownByPath = knownByRawStoreRef.get(filePath);
80
- if (knownByPath
81
- && knownByPath.sourceMtimeMs === stats.mtimeMs
82
- && knownByPath.sourceSizeBytes === stats.size
83
- && hasUsableCodexTitle(knownByPath.title)
84
- && (!sessionIdentity
85
- || knownByPath.providerSessionId === sessionIdentity.threadId)) {
86
- skippedByMtimeSize += 1;
87
- if (normalizeWorkspacePath(knownByPath.workspacePath) === targetPath) {
88
- retainedSummaries.push({
89
- filePath,
90
- stats,
91
- sessionIdentity,
92
- summary: knownByPath
93
- });
94
- continue;
95
- }
96
- this.touchSessionSummaryCache(filePath, {
97
- filePath,
98
- mtimeMs: stats.mtimeMs,
99
- size: stats.size,
100
- workspacePath: knownByPath.workspacePath,
101
- providerSessionId: knownByPath.providerSessionId,
102
- title: null,
103
- summary: null
104
- });
105
- continue;
106
- }
107
- if (sessionIdentity?.cwd
108
- && normalizeWorkspacePath(sessionIdentity.cwd) !== targetPath) {
109
- this.touchSessionSummaryCache(filePath, {
110
- filePath,
111
- mtimeMs: stats.mtimeMs,
112
- size: stats.size,
113
- workspacePath: sessionIdentity.cwd,
114
- providerSessionId: sessionIdentity.threadId,
115
- title: null,
116
- summary: null
117
- });
118
- continue;
119
- }
120
- pendingFiles.push({
121
- filePath,
122
- fileSessionId,
123
- stats: {
124
- mtimeMs: stats.mtimeMs,
125
- size: stats.size
126
- },
127
- sessionIdentity
128
- });
129
- }
130
- if (pendingFiles.length === 0 && retainedSummaries.length === 0) {
131
- const sessions = [...sessionsByProviderSessionId.values()].sort((left, right) => (left.lastMessageAt ?? "").localeCompare(right.lastMessageAt ?? ""));
132
- const diagnostic = {
133
- provider: this.providerId,
134
- status: "success",
135
- durationMs: Date.now() - startedAt,
136
- sessionCount: sessions.length,
137
- isComplete: true,
138
- errorMessage: null,
139
- scannedFiles,
140
- skippedByMtimeSize,
141
- parsedFiles,
142
- bytesRead
143
- };
144
- return {
145
- sessions,
146
- isComplete: true,
147
- providerDiagnostics: [diagnostic]
148
- };
149
- }
150
- const pendingThreadIds = new Set([...pendingFiles, ...retainedSummaries]
151
- .map((entry) => entry.sessionIdentity?.threadId ?? null)
152
- .filter((value) => value !== null));
153
- const spawnedAgentRelationIndex = this.readSpawnedAgentRelationIndex([...pendingFiles, ...retainedSummaries].map((entry) => ({
154
- filePath: entry.filePath,
155
- stats: entry.stats,
156
- sessionIdentity: entry.sessionIdentity
157
- })), targetPath, threadMetadataIndex, pendingThreadIds);
158
- for (const entry of retainedSummaries) {
159
- const currentThreadId = entry.sessionIdentity?.threadId ?? entry.summary.providerSessionId;
160
- const currentThreadMetadata = threadMetadataIndex.get(currentThreadId) ?? null;
161
- const currentSpawnRelation = spawnedAgentRelationIndex.get(currentThreadId) ?? null;
162
- const summary = this.hydrateSessionSummary({
163
- ...entry.summary,
164
- workspacePath: entry.sessionIdentity?.cwd || entry.summary.workspacePath
165
- }, entry.filePath, entry.stats, currentThreadMetadata, currentSpawnRelation);
166
- this.touchSessionSummaryCache(entry.filePath, {
167
- filePath: entry.filePath,
168
- mtimeMs: entry.stats.mtimeMs,
169
- size: entry.stats.size,
170
- workspacePath: summary.workspacePath,
171
- providerSessionId: summary.providerSessionId,
172
- title: summary.title,
173
- summary
174
- });
175
- sessionsByProviderSessionId.set(summary.providerSessionId, summary);
176
- }
177
- for (const entry of pendingFiles) {
178
- const { filePath, fileSessionId, stats, sessionIdentity } = entry;
179
- parsedFiles += 1;
180
- bytesRead += stats.size;
181
- const records = readJsonLines(filePath);
182
- const meta = records.find((record) => record.data.type === "session_meta")?.data;
183
- const metaPayload = (meta?.payload ?? {});
184
- const codexSessionId = this.resolveCodexSessionId(metaPayload, fileSessionId);
185
- if (shouldIgnoreCodingNsDraftSession(metaPayload)) {
186
- this.touchSessionSummaryCache(filePath, {
187
- filePath,
188
- mtimeMs: stats.mtimeMs,
189
- size: stats.size,
190
- workspacePath: null,
191
- providerSessionId: codexSessionId,
192
- title: null,
193
- summary: null
194
- });
195
- continue;
196
- }
197
- const cwd = ensureText(metaPayload.cwd);
198
- const cachedWorkspacePath = cwd || null;
199
- const sessionWorkspacePath = cwd || workspacePath;
200
- if (normalizeWorkspacePath(cwd) !== targetPath) {
201
- this.touchSessionSummaryCache(filePath, {
202
- filePath,
203
- mtimeMs: stats.mtimeMs,
204
- size: stats.size,
205
- workspacePath: cachedWorkspacePath,
206
- providerSessionId: codexSessionId,
207
- title: null,
208
- summary: null
209
- });
210
- continue;
211
- }
212
- const currentThreadMetadata = threadMetadataIndex.get(codexSessionId) ??
213
- (sessionIdentity ? threadMetadataIndex.get(sessionIdentity.threadId) : null);
214
- const currentSpawnRelation = spawnedAgentRelationIndex.get(codexSessionId) ??
215
- (sessionIdentity ? spawnedAgentRelationIndex.get(sessionIdentity.threadId) : null);
216
- const knownBySessionId = knownByProviderSessionId.get(codexSessionId);
217
- if (knownBySessionId
218
- && knownBySessionId.sourceMtimeMs === stats.mtimeMs
219
- && knownBySessionId.sourceSizeBytes === stats.size
220
- && hasUsableCodexTitle(knownBySessionId.title)) {
221
- const summary = this.hydrateSessionSummary({
222
- ...knownBySessionId,
223
- workspacePath: sessionWorkspacePath
224
- }, filePath, stats, currentThreadMetadata, currentSpawnRelation);
225
- this.touchSessionSummaryCache(filePath, {
226
- filePath,
227
- mtimeMs: stats.mtimeMs,
228
- size: stats.size,
229
- workspacePath: sessionWorkspacePath,
230
- providerSessionId: summary.providerSessionId,
231
- title: summary.title,
232
- summary
233
- });
234
- sessionsByProviderSessionId.set(codexSessionId, summary);
235
- continue;
236
- }
237
- const messages = this.parseMessagesFromEntries(filePath, records, codexSessionId);
238
- const title = this.resolveIndexedTitle(threadMetadataIndex, codexSessionId) ??
239
- resolveCodexFallbackTitle(messages) ??
240
- fileSessionId;
241
- const lastMessageAt = messages.at(-1)?.timestamp ?? (ensureText(metaPayload.timestamp) || null);
242
- const summary = this.hydrateSessionSummary({
243
- provider: this.providerId,
244
- providerSessionId: codexSessionId,
245
- title,
246
- workspacePath: sessionWorkspacePath,
247
- rawStoreRef: filePath,
248
- lastMessageAt,
249
- messageCount: messages.length,
250
- isArchived: false
251
- }, filePath, stats, currentThreadMetadata, currentSpawnRelation);
252
- sessionsByProviderSessionId.set(codexSessionId, summary);
253
- this.touchSessionSummaryCache(filePath, {
254
- filePath,
255
- mtimeMs: stats.mtimeMs,
256
- size: stats.size,
257
- workspacePath: sessionWorkspacePath,
258
- providerSessionId: summary.providerSessionId,
259
- title: summary.title,
260
- summary
261
- });
262
- }
263
- const sessions = [...sessionsByProviderSessionId.values()].sort((left, right) => (left.lastMessageAt ?? "").localeCompare(right.lastMessageAt ?? ""));
264
- const diagnostic = {
265
- provider: this.providerId,
266
- status: "success",
267
- durationMs: Date.now() - startedAt,
268
- sessionCount: sessions.length,
269
- isComplete: true,
270
- errorMessage: null,
271
- scannedFiles,
272
- skippedByMtimeSize,
273
- parsedFiles,
274
- bytesRead
275
- };
276
- return {
277
- sessions,
278
- isComplete: true,
279
- providerDiagnostics: [diagnostic]
280
- };
281
- }
282
- async readSessionHistory(providerSessionId, rawStoreRef, cursor, limit, direction = "forward") {
283
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
284
- if (!existsSync(resolvedStoreRef)) {
285
- return {
286
- messages: [],
287
- cursor,
288
- nextCursor: null,
289
- total: 0
290
- };
291
- }
292
- const messages = this.getParsedMessages(resolvedStoreRef, providerSessionId);
293
- return sliceHistory(messages, cursor, limit, direction);
294
- }
295
- async readRecentSessionHistory(providerSessionId, rawStoreRef, totalMessageCount, limit) {
296
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
297
- if (!existsSync(resolvedStoreRef)) {
298
- return null;
299
- }
300
- const stats = statSync(resolvedStoreRef);
301
- const cached = this.historyCache.get(resolvedStoreRef);
302
- if (cached
303
- && cached.providerSessionId === providerSessionId
304
- && cached.mtimeMs === stats.mtimeMs
305
- && cached.size === stats.size) {
306
- this.touchHistoryCache(resolvedStoreRef, cached);
307
- return sliceHistory(cached.messages, null, limit, "backward");
308
- }
309
- const safeLimit = Math.max(1, Math.min(Math.trunc(limit), 100));
310
- let maxBytes = Math.min(RECENT_HISTORY_INITIAL_BYTES, stats.size);
311
- while (maxBytes > 0) {
312
- const lines = readTrailingJsonLines(resolvedStoreRef, maxBytes);
313
- if (lines.length > 0) {
314
- const messages = this.parseMessagesFromEntries(resolvedStoreRef, lines, providerSessionId);
315
- if (messages.length > 0
316
- && (messages.length >= Math.min(totalMessageCount, safeLimit + RECENT_HISTORY_BUFFER_MESSAGES)
317
- || maxBytes >= stats.size)) {
318
- return buildRecentHistoryPage(messages, totalMessageCount, safeLimit);
319
- }
320
- }
321
- if (maxBytes >= stats.size || maxBytes >= RECENT_HISTORY_MAX_BYTES) {
322
- break;
323
- }
324
- maxBytes = Math.min(stats.size, maxBytes * 2, RECENT_HISTORY_MAX_BYTES);
325
- }
326
- return null;
327
- }
328
- subscribeSession(providerSessionId, rawStoreRef, cursor, limit, onEvent) {
329
- let currentCursor = cursor;
330
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
331
- let lastMtime = existsSync(resolvedStoreRef) ? statSync(resolvedStoreRef).mtimeMs : 0;
332
- const timer = setInterval(async () => {
333
- if (!existsSync(resolvedStoreRef)) {
334
- return;
335
- }
336
- const nextStat = statSync(resolvedStoreRef);
337
- if (nextStat.mtimeMs <= lastMtime) {
338
- return;
339
- }
340
- lastMtime = nextStat.mtimeMs;
341
- const page = await this.readSessionHistory(providerSessionId, rawStoreRef, currentCursor, limit);
342
- if (page.messages.length === 0) {
343
- return;
344
- }
345
- currentCursor = page.cursor;
346
- await onEvent({
347
- messages: page.messages,
348
- cursor: page.cursor
349
- });
350
- }, 300);
351
- return {
352
- close() {
353
- clearInterval(timer);
354
- }
355
- };
356
- }
357
- async resumeSession(providerSessionId, rawStoreRef) {
358
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
359
- statSync(resolvedStoreRef);
360
- return {
361
- provider: this.providerId,
362
- providerSessionId,
363
- resumedAt: nextTimestamp(),
364
- rawStoreRef: resolvedStoreRef
365
- };
366
- }
367
- async startSession(workspacePath, options) {
368
- const now = new Date();
369
- const sessionId = `rollout-${now.toISOString().replaceAll(":", "-")}-${crypto.randomUUID()}`;
370
- const folder = join(this.options.homeDir, "sessions", `${now.getUTCFullYear()}`, `${String(now.getUTCMonth() + 1).padStart(2, "0")}`, `${String(now.getUTCDate()).padStart(2, "0")}`);
371
- ensureDirectory(folder);
372
- const filePath = join(folder, `${sessionId}.jsonl`);
373
- const nowIso = nextTimestamp();
374
- appendJsonLine(filePath, {
375
- timestamp: nowIso,
376
- type: "session_meta",
377
- payload: {
378
- id: sessionId,
379
- timestamp: nowIso,
380
- cwd: workspacePath,
381
- originator: "CodingNS Host",
382
- source: "codingns"
383
- }
384
- });
385
- if (options.initialPrompt) {
386
- appendJsonLine(filePath, {
387
- timestamp: nextTimestamp(),
388
- type: "event_msg",
389
- payload: {
390
- type: "user_message",
391
- message: options.initialPrompt
392
- }
393
- });
394
- }
395
- return {
396
- session: {
397
- provider: this.providerId,
398
- providerSessionId: sessionId,
399
- title: options.initialPrompt?.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH) || "New Codex session",
400
- workspacePath,
401
- rawStoreRef: filePath,
402
- isArchived: false,
403
- lastMessageAt: nextTimestamp(),
404
- messageCount: options.initialPrompt ? 1 : 0
405
- },
406
- initialCursor: encodeCursor(options.initialPrompt ? 1 : 0)
407
- };
408
- }
409
- async forkSession(providerSessionId, workspacePath, options) {
410
- const transportFactory = this.options.forkTransportFactory;
411
- if (!transportFactory) {
412
- throw new Error("CODEX_FORK_TRANSPORT_NOT_CONFIGURED");
413
- }
414
- const transport = transportFactory();
415
- try {
416
- await transport.initialize();
417
- if (options.sourceType === "session") {
418
- const forked = await this.forkThreadWithHistoryFallback(transport, providerSessionId, workspacePath, options.rawStoreRef);
419
- return await this.buildForkResultFromTransport({
420
- providerSessionId: forked.providerSessionId,
421
- rawStoreRef: forked.rawStoreRef,
422
- workspacePath,
423
- fallbackParentProviderSessionId: providerSessionId,
424
- forkMethod: "native_session_fork",
425
- forkSourceType: "session",
426
- providerSourceMessageId: null
427
- });
428
- }
429
- const targetMessageId = options.sourceMessageId?.trim();
430
- if (!targetMessageId) {
431
- throw new Error("FORK_SOURCE_MESSAGE_ID_REQUIRED");
432
- }
433
- if (options.strategy === "reconstruct-only") {
434
- throw new Error("CODEX_RECONSTRUCTED_MESSAGE_FORK_NOT_SUPPORTED");
435
- }
436
- const resolvedStoreRef = this.resolveSessionFilePath(options.rawStoreRef, providerSessionId);
437
- const parsedMessages = this.getParsedMessages(resolvedStoreRef, providerSessionId);
438
- const targetMessage = parsedMessages.find((message) => message.messageId === targetMessageId);
439
- if (!targetMessage) {
440
- throw new Error("FORK_SOURCE_MESSAGE_NOT_FOUND");
441
- }
442
- const targetSnapshot = applyForkSourceMessageSnapshot(targetMessage, options.sourceMessageSnapshot);
443
- const threadReadResult = await transport.readThread(providerSessionId);
444
- const threadSnapshot = extractCodexThreadHistorySnapshot(threadReadResult);
445
- const truncatedHistory = truncateCodexThreadHistory(threadSnapshot.value, parsedMessages, targetSnapshot);
446
- if (truncatedHistory.length === 0) {
447
- throw new Error("CODEX_FORK_HISTORY_EMPTY");
448
- }
449
- if (threadSnapshot.kind !== "turns") {
450
- throw new Error("CODEX_RECONSTRUCTED_MESSAGE_FORK_NOT_SUPPORTED");
451
- }
452
- const rollbackPlan = buildCodexTurnRollbackPlan(threadSnapshot, parsedMessages, targetSnapshot);
453
- const forked = await transport.forkThread(providerSessionId);
454
- const finalized = rollbackPlan.numTurnsToRollback > 0
455
- ? await transport.rollbackThread(forked.providerSessionId, rollbackPlan.numTurnsToRollback)
456
- : forked;
457
- const childThreadReadResult = await transport.readThread(finalized.providerSessionId);
458
- if (!this.isForkedChildHistoryAligned(childThreadReadResult, truncatedHistory)) {
459
- throw new Error("CODEX_NATIVE_MESSAGE_FORK_DIRTY");
460
- }
461
- return await this.buildForkResultFromTransport({
462
- providerSessionId: finalized.providerSessionId,
463
- rawStoreRef: finalized.rawStoreRef,
464
- workspacePath,
465
- fallbackParentProviderSessionId: providerSessionId,
466
- forkMethod: "native_message_fork",
467
- forkSourceType: "message",
468
- providerSourceMessageId: null,
469
- messageCountOverride: targetSnapshot.sequence,
470
- inheritedPrefixMessageCountOverride: targetSnapshot.sequence,
471
- lastMessageAtOverride: targetSnapshot.timestamp
472
- });
473
- }
474
- finally {
475
- transport.close();
476
- }
477
- }
478
- async sendMessage(providerSessionId, rawStoreRef, content, clientRequestId, _permissionMode) {
479
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
480
- const lineNumber = readJsonLines(resolvedStoreRef).length + 1;
481
- const acceptedAt = nextTimestamp();
482
- appendJsonLine(resolvedStoreRef, {
483
- timestamp: acceptedAt,
484
- type: "event_msg",
485
- payload: {
486
- type: "user_message",
487
- message: content,
488
- clientRequestId
489
- }
490
- });
491
- const rawRef = createRawRef(this.providerId, resolvedStoreRef, lineNumber);
492
- this.historyCache.delete(resolvedStoreRef);
493
- return {
494
- acceptedAt,
495
- clientRequestId,
496
- message: {
497
- messageId: messageIdFromRawRef(rawRef),
498
- provider: this.providerId,
499
- providerSessionId,
500
- role: "user",
501
- kind: "text",
502
- content,
503
- toolCall: null,
504
- timestamp: acceptedAt,
505
- sequence: this.parseMessages(rawStoreRef, readJsonLines(resolvedStoreRef), providerSessionId).length,
506
- rawRef
507
- }
508
- };
509
- }
510
- async readSessionTitle(providerSessionId, rawStoreRef) {
511
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
512
- const fileSessionId = basename(resolvedStoreRef, ".jsonl");
513
- const sessionIdentity = this.readSessionIdentity(resolvedStoreRef, fileSessionId);
514
- const threadMetadataIndex = this.readThreadMetadataIndex();
515
- const indexedTitle = this.resolveIndexedTitle(threadMetadataIndex, providerSessionId) ??
516
- (sessionIdentity
517
- ? this.resolveIndexedTitle(threadMetadataIndex, sessionIdentity.threadId)
518
- : null);
519
- if (indexedTitle) {
520
- return indexedTitle;
521
- }
522
- const stats = statSync(resolvedStoreRef);
523
- const cachedSummary = this.sessionSummaryCache.get(resolvedStoreRef);
524
- if (cachedSummary
525
- && cachedSummary.mtimeMs === stats.mtimeMs
526
- && cachedSummary.size === stats.size
527
- && (cachedSummary.providerSessionId === providerSessionId
528
- || (sessionIdentity
529
- && cachedSummary.providerSessionId === sessionIdentity.threadId))) {
530
- this.touchSessionSummaryCache(resolvedStoreRef, cachedSummary);
531
- if (cachedSummary.title) {
532
- return cachedSummary.title;
533
- }
534
- if (cachedSummary.summary) {
535
- return cachedSummary.summary.title;
536
- }
537
- }
538
- const records = readJsonLines(resolvedStoreRef);
539
- const meta = records.find((record) => record.data.type === "session_meta")?.data;
540
- const metaPayload = (meta?.payload ?? {});
541
- const codexSessionId = this.resolveCodexSessionId(metaPayload, providerSessionId || fileSessionId);
542
- const messages = this.parseMessagesFromEntries(resolvedStoreRef, records, codexSessionId);
543
- const resolvedTitle = (this.resolveIndexedTitle(threadMetadataIndex, codexSessionId) ??
544
- resolveCodexFallbackTitle(messages) ??
545
- fileSessionId);
546
- this.touchSessionSummaryCache(resolvedStoreRef, {
547
- filePath: resolvedStoreRef,
548
- mtimeMs: stats.mtimeMs,
549
- size: stats.size,
550
- workspacePath: (sessionIdentity?.cwd ?? ensureText(metaPayload.cwd)) || null,
551
- providerSessionId: codexSessionId,
552
- title: resolvedTitle,
553
- summary: null
554
- });
555
- return resolvedTitle;
556
- }
557
- async renameSessionTitle(providerSessionId, rawStoreRef, title) {
558
- const nextTitle = title.trim();
559
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
560
- const indexPath = join(this.options.homeDir, "session_index.jsonl");
561
- const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
562
- statSync(resolvedStoreRef);
563
- ensureDirectory(this.options.homeDir);
564
- appendJsonLine(indexPath, {
565
- id: providerSessionId,
566
- thread_name: nextTitle
567
- });
568
- if (stateDbPath) {
569
- const DatabaseSync = loadDatabaseSync();
570
- let db = null;
571
- try {
572
- db = new DatabaseSync(stateDbPath, { open: true });
573
- db.prepare("UPDATE threads SET title = ? WHERE id = ?").run(nextTitle, providerSessionId);
574
- }
575
- finally {
576
- db?.close();
577
- }
578
- }
579
- this.sessionSummaryCache.delete(resolvedStoreRef);
580
- return nextTitle;
581
- }
582
- async updateSessionArchiveState(providerSessionId, rawStoreRef, isArchived) {
583
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
584
- const currentFileName = basename(resolvedStoreRef) || `${providerSessionId}.jsonl`;
585
- const nextStoreRef = isArchived
586
- ? join(this.options.homeDir, "archived_sessions", currentFileName)
587
- : buildCodexActiveSessionPath(this.options.homeDir, currentFileName);
588
- const controlResult = await this.updateArchiveStateViaThreadControlTransport(providerSessionId, resolvedStoreRef, nextStoreRef, isArchived);
589
- let finalStoreRef = nextStoreRef;
590
- if (controlResult) {
591
- finalStoreRef = controlResult.rawStoreRef;
592
- }
593
- else {
594
- const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
595
- statSync(resolvedStoreRef);
596
- if (resolvedStoreRef !== nextStoreRef) {
597
- ensureDirectory(dirname(nextStoreRef));
598
- renameSync(resolvedStoreRef, nextStoreRef);
599
- }
600
- if (stateDbPath) {
601
- const DatabaseSync = loadDatabaseSync();
602
- let db = null;
603
- try {
604
- db = new DatabaseSync(stateDbPath, { open: true });
605
- db.prepare(`UPDATE threads
606
- SET archived = ?,
607
- archived_at = ?,
608
- rollout_path = ?
609
- WHERE id = ?`).run(isArchived ? 1 : 0, isArchived ? Math.floor(Date.now() / 1000) : null, nextStoreRef, providerSessionId);
610
- }
611
- finally {
612
- db?.close();
613
- }
614
- }
615
- }
616
- this.sessionSummaryCache.delete(resolvedStoreRef);
617
- this.sessionSummaryCache.delete(finalStoreRef);
618
- // 归档切换后线程索引的 archived / rollout_path 也变了,不能继续赌文件系统 mtime 一定会跳。
619
- this.invalidateThreadMetadataIndexCache();
620
- return {
621
- rawStoreRef: finalStoreRef,
622
- isArchived
623
- };
624
- }
625
- async deleteSession(providerSessionId, rawStoreRef) {
626
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
627
- const threadMetadata = this.readThreadMetadataIndex().get(providerSessionId) ?? null;
628
- const resolvedMetadataStoreRef = threadMetadata?.rolloutPath && threadMetadata.rolloutPath.trim()
629
- ? this.resolveSessionFilePath(threadMetadata.rolloutPath, providerSessionId)
630
- : null;
631
- const candidateFilePaths = new Set([resolvedStoreRef, resolvedMetadataStoreRef].filter((value) => typeof value === "string" && value.trim().length > 0));
632
- const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
633
- let deletedAny = false;
634
- for (const filePath of candidateFilePaths) {
635
- if (!existsSync(filePath)) {
636
- continue;
637
- }
638
- rmSync(filePath, { force: true });
639
- this.historyCache.delete(filePath);
640
- this.sessionSummaryCache.delete(filePath);
641
- deletedAny = true;
642
- }
643
- if (stateDbPath) {
644
- const DatabaseSync = loadDatabaseSync();
645
- let db = null;
646
- try {
647
- db = new DatabaseSync(stateDbPath, { open: true });
648
- const result = db.prepare("DELETE FROM threads WHERE id = ?").run(providerSessionId);
649
- deletedAny = deletedAny || result.changes > 0;
650
- }
651
- finally {
652
- db?.close();
653
- }
654
- }
655
- this.invalidateThreadMetadataIndexCache();
656
- if (!deletedAny) {
657
- throw new Error("PROVIDER_SESSION_NOT_FOUND");
658
- }
659
- }
660
- async updateArchiveStateViaThreadControlTransport(providerSessionId, resolvedStoreRef, nextStoreRef, isArchived) {
661
- const createTransport = this.options.threadControlTransportFactory;
662
- if (!createTransport) {
663
- return null;
664
- }
665
- const transport = createTransport();
666
- try {
667
- await transport.initialize();
668
- if (isArchived) {
669
- await transport.archiveThread(providerSessionId);
670
- }
671
- else {
672
- await transport.unarchiveThread(providerSessionId);
673
- }
674
- const result = await transport.readThread(providerSessionId).catch(() => null);
675
- const thread = result && typeof result === "object"
676
- ? (result.thread ?? null)
677
- : null;
678
- const appServerRawStoreRef = ensureText(thread?.path).trim();
679
- const resolvedNextStoreRef = appServerRawStoreRef.length > 0
680
- ? this.resolveSessionFilePath(appServerRawStoreRef, providerSessionId)
681
- : this.resolveSessionFilePath(nextStoreRef, providerSessionId);
682
- this.sessionSummaryCache.delete(resolvedStoreRef);
683
- this.sessionSummaryCache.delete(resolvedNextStoreRef);
684
- return {
685
- rawStoreRef: resolvedNextStoreRef
686
- };
687
- }
688
- catch {
689
- return null;
690
- }
691
- finally {
692
- transport.close();
693
- }
694
- }
695
- getProviderCapabilities() {
696
- return {
697
- provider: this.providerId,
698
- canStartSession: true,
699
- canResumeSession: true,
700
- canSendMessage: true,
701
- inRunInputMode: "streaming_guidance",
702
- supportsSubagents: true,
703
- supportsInterrupt: true,
704
- supportsStructuredToolCalls: true,
705
- supportsTokenUsage: true,
706
- supportsAttachments: true,
707
- supportsPermissionPrompt: true,
708
- supportsCheckpoint: false,
709
- supportsSessionFork: true,
710
- supportsSessionDelete: true,
711
- limitations: [
712
- "运行中追加消息依赖 Codex CLI app-server 暴露 turn/steer;当前项目实测 codex-cli 0.118.0 可用。",
713
- "当前 npm SDK 仍只有 run/runStreamed 轮询式接口,宿主运行时需经由 Codex CLI app-server 才能直发 steer。"
714
- ]
715
- };
716
- }
717
- async getSessionCapabilities() {
718
- return this.getProviderCapabilities();
719
- }
720
- async readContextUsage(providerSessionId, rawStoreRef) {
721
- const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
722
- const records = readJsonLines(resolvedStoreRef).map((record) => record.data);
723
- for (let index = records.length - 1; index >= 0; index -= 1) {
724
- const record = records[index];
725
- if (ensureText(record.type).trim() !== "event_msg") {
726
- continue;
727
- }
728
- const payload = (record.payload ?? {});
729
- if (ensureText(payload.type).trim() !== "token_count") {
730
- continue;
731
- }
732
- const info = (payload.info ?? {});
733
- const lastUsage = (info.last_token_usage ?? {});
734
- const uncachedInputTokens = readNonNegativeInteger(lastUsage.input_tokens);
735
- const cachedInputTokens = readNonNegativeInteger(lastUsage.cached_input_tokens);
736
- if (uncachedInputTokens === null && cachedInputTokens === null) {
737
- continue;
738
- }
739
- const promptTokens = uncachedInputTokens ?? 0;
740
- const modelId = ensureText(info.model ?? info.model_id).trim() || null;
741
- const runtimeContextWindow = readNonNegativeInteger(info.model_context_window);
742
- const contextWindow = runtimeContextWindow
743
- ?? resolveCodexKnownContextWindow(modelId)
744
- ?? readCodexConfigContextWindow(this.options.homeDir);
745
- if (contextWindow === null || contextWindow <= 0) {
746
- return null;
747
- }
748
- return {
749
- provider: this.providerId,
750
- promptTokens,
751
- uncachedInputTokens: uncachedInputTokens ?? 0,
752
- cachedInputTokens: cachedInputTokens ?? 0,
753
- contextWindow,
754
- usageRatio: clampUsageRatio(promptTokens, contextWindow),
755
- source: "provider-log",
756
- contextWindowSource: runtimeContextWindow !== null
757
- ? "provider-log"
758
- : resolveCodexKnownContextWindow(modelId) !== null
759
- ? "model-map"
760
- : "provider-config",
761
- modelId,
762
- capturedAt: safeDate(record.timestamp, "").trim() || null,
763
- isEstimated: runtimeContextWindow === null
764
- };
765
- }
766
- return null;
767
- }
768
- readThreadMetadataIndex() {
769
- const indexPath = join(this.options.homeDir, "session_index.jsonl");
770
- const indexPathMtimeMs = existsSync(indexPath) ? statSync(indexPath).mtimeMs : null;
771
- const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
772
- const stateDbMtimeMs = stateDbPath && existsSync(stateDbPath) ? statSync(stateDbPath).mtimeMs : null;
773
- const cached = this.threadMetadataIndexCache;
774
- if (cached
775
- && cached.indexPathMtimeMs === indexPathMtimeMs
776
- && cached.stateDbPath === stateDbPath
777
- && cached.stateDbMtimeMs === stateDbMtimeMs) {
778
- return cached.index;
779
- }
780
- const index = new Map();
781
- if (existsSync(indexPath)) {
782
- const lines = readFileSync(indexPath, "utf8")
783
- .split(/\r?\n/)
784
- .filter((line) => line.trim().length > 0);
785
- // 这里容忍单行脏数据,避免某一条坏记录把整个会话列表拖死。
786
- for (const line of lines) {
787
- try {
788
- const record = JSON.parse(line);
789
- const id = ensureText(record.id).trim();
790
- if (id.length === 0) {
791
- continue;
792
- }
793
- index.set(id, {
794
- title: normalizeCodexIndexedTitle(ensureText(record.thread_name)) || null,
795
- cwd: null,
796
- createdAtMs: null,
797
- firstUserMessage: null,
798
- agentNickname: null,
799
- agentRole: null,
800
- isArchived: null,
801
- rolloutPath: null
802
- });
803
- }
804
- catch {
805
- continue;
806
- }
807
- }
808
- }
809
- if (!stateDbPath) {
810
- this.threadMetadataIndexCache = {
811
- indexPathMtimeMs,
812
- stateDbPath: null,
813
- stateDbMtimeMs: null,
814
- index
815
- };
816
- return index;
817
- }
818
- const DatabaseSync = loadDatabaseSync();
819
- let db = null;
820
- try {
821
- db = new DatabaseSync(stateDbPath, { open: true, readOnly: true });
822
- const rows = db.prepare(`SELECT
823
- id,
824
- title,
825
- cwd,
826
- created_at,
827
- archived,
828
- first_user_message,
829
- agent_nickname,
830
- agent_role,
831
- rollout_path
832
- FROM threads`).all();
833
- for (const row of rows) {
834
- const id = ensureText(row.id).trim();
835
- if (id.length === 0) {
836
- continue;
837
- }
838
- const current = index.get(id);
839
- const dbTitle = normalizeCodexIndexedTitle(ensureText(row.title)) || null;
840
- const createdAtSeconds = typeof row.created_at === "number"
841
- ? row.created_at
842
- : Number.parseInt(ensureText(row.created_at), 10);
843
- index.set(id, {
844
- title: current?.title ?? dbTitle,
845
- cwd: ensureText(row.cwd).trim() || (current?.cwd ?? null),
846
- createdAtMs: Number.isFinite(createdAtSeconds) ? createdAtSeconds * 1000 : null,
847
- firstUserMessage: ensureText(row.first_user_message).trim() || (current?.firstUserMessage ?? null),
848
- agentNickname: ensureText(row.agent_nickname).trim() || (current?.agentNickname ?? null),
849
- agentRole: ensureText(row.agent_role).trim() || (current?.agentRole ?? null),
850
- rolloutPath: ensureText(row.rollout_path).trim() || (current?.rolloutPath ?? null),
851
- isArchived: typeof row.archived === "number"
852
- ? row.archived === 1
853
- : ensureText(row.rollout_path).includes("archived_sessions")
854
- ? true
855
- : (current?.isArchived ?? null)
856
- });
857
- }
858
- }
859
- catch {
860
- this.threadMetadataIndexCache = {
861
- indexPathMtimeMs,
862
- stateDbPath,
863
- stateDbMtimeMs,
864
- index
865
- };
866
- return index;
867
- }
868
- finally {
869
- db?.close();
870
- }
871
- this.threadMetadataIndexCache = {
872
- indexPathMtimeMs,
873
- stateDbPath,
874
- stateDbMtimeMs,
875
- index
876
- };
877
- return index;
878
- }
879
- listSessionFiles(targetPath, threadMetadataIndex, knownSessions) {
880
- const activeFiles = walkJsonlFiles(join(this.options.homeDir, "sessions"));
881
- const archivedFiles = this.listArchivedSessionFiles(targetPath, threadMetadataIndex, knownSessions);
882
- return [...activeFiles, ...archivedFiles];
883
- }
884
- listArchivedSessionFiles(targetPath, threadMetadataIndex, knownSessions) {
885
- const archivedFiles = new Set();
886
- for (const metadata of threadMetadataIndex.values()) {
887
- if (metadata.isArchived !== true
888
- || !metadata.rolloutPath) {
889
- continue;
890
- }
891
- if (targetPath.length > 0
892
- && normalizeWorkspacePath(metadata.cwd ?? "") !== targetPath) {
893
- continue;
894
- }
895
- archivedFiles.add(metadata.rolloutPath);
896
- }
897
- for (const metadata of threadMetadataIndex.values()) {
898
- if (metadata.isArchived === true
899
- || !metadata.rolloutPath) {
900
- continue;
901
- }
902
- if (targetPath.length > 0
903
- && normalizeWorkspacePath(metadata.cwd ?? "") !== targetPath) {
904
- continue;
905
- }
906
- const archivedCandidate = this.resolveArchivedSessionCandidate(metadata.rolloutPath);
907
- if (archivedCandidate) {
908
- archivedFiles.add(archivedCandidate);
909
- }
910
- }
911
- for (const session of knownSessions) {
912
- if (session.isArchived !== true
913
- || normalizeWorkspacePath(session.workspacePath) !== targetPath
914
- || !isCodexArchivedFilePath(session.rawStoreRef)) {
915
- continue;
916
- }
917
- archivedFiles.add(session.rawStoreRef);
918
- }
919
- for (const session of knownSessions) {
920
- if (session.isArchived === true
921
- || normalizeWorkspacePath(session.workspacePath) !== targetPath) {
922
- continue;
923
- }
924
- const archivedCandidate = this.resolveArchivedSessionCandidate(session.rawStoreRef);
925
- if (archivedCandidate) {
926
- archivedFiles.add(archivedCandidate);
927
- }
928
- }
929
- if (archivedFiles.size > 0) {
930
- return [...archivedFiles].filter((filePath) => existsSync(filePath));
931
- }
932
- if (threadMetadataIndex.size === 0) {
933
- return walkJsonlFiles(join(this.options.homeDir, "archived_sessions"));
934
- }
935
- return [];
936
- }
937
- resolveArchivedSessionCandidate(filePath) {
938
- if (!filePath || existsSync(filePath)) {
939
- return null;
940
- }
941
- const archivedCandidate = join(this.options.homeDir, "archived_sessions", basename(filePath));
942
- if (!existsSync(archivedCandidate)) {
943
- return null;
944
- }
945
- return archivedCandidate;
946
- }
947
- resolveSessionFilePath(rawStoreRef, providerSessionId) {
948
- const matchedByThreadId = this.findSessionFileByThreadId(providerSessionId);
949
- if (existsSync(rawStoreRef)) {
950
- const boundThreadId = this.readThreadIdFromRawStore(rawStoreRef);
951
- if (!boundThreadId || boundThreadId === providerSessionId) {
952
- return rawStoreRef;
953
- }
954
- if (matchedByThreadId) {
955
- return matchedByThreadId;
956
- }
957
- return buildSyntheticCodexHistoryPath(this.options.homeDir, providerSessionId);
958
- }
959
- const fileName = basename(rawStoreRef) || `${providerSessionId}.jsonl`;
960
- const match = fileName.match(/^rollout-(\d{4})-(\d{2})-(\d{2})T.+\.jsonl$/);
961
- const candidates = [
962
- join(this.options.homeDir, "archived_sessions", fileName),
963
- match
964
- ? join(this.options.homeDir, "sessions", match[1], match[2], match[3], fileName)
965
- : null
966
- ].filter((value) => value !== null);
967
- const matchedPath = candidates.find((candidate) => existsSync(candidate));
968
- if (matchedPath) {
969
- return matchedPath;
970
- }
971
- if (matchedByThreadId) {
972
- return matchedByThreadId;
973
- }
974
- if (isSyntheticCodexHistoryPath(rawStoreRef)) {
975
- return rawStoreRef;
976
- }
977
- return buildSyntheticCodexHistoryPath(this.options.homeDir, providerSessionId);
978
- }
979
- findSessionFileByThreadId(providerSessionId) {
980
- const threadMetadataIndex = this.readThreadMetadataIndex();
981
- const activeFiles = walkJsonlFiles(join(this.options.homeDir, "sessions"));
982
- const archivedFiles = this.listArchivedSessionFiles("", threadMetadataIndex, []);
983
- for (const filePath of [...activeFiles, ...archivedFiles]) {
984
- const threadId = this.readThreadIdFromRawStore(filePath);
985
- if (threadId === providerSessionId) {
986
- return filePath;
987
- }
988
- }
989
- return null;
990
- }
991
- readThreadIdFromRawStore(filePath) {
992
- if (!existsSync(filePath)) {
993
- return null;
994
- }
995
- const firstLine = readFirstNonEmptyLine(filePath);
996
- if (!firstLine) {
997
- return null;
998
- }
999
- try {
1000
- const record = JSON.parse(firstLine);
1001
- if (ensureText(record.type).trim() !== "session_meta") {
1002
- return null;
1003
- }
1004
- const threadId = ensureText(record.payload?.id).trim();
1005
- return threadId.length > 0 ? threadId : null;
1006
- }
1007
- catch {
1008
- return null;
1009
- }
1010
- }
1011
- getParsedMessages(filePath, providerSessionId) {
1012
- const stats = statSync(filePath);
1013
- const cached = this.historyCache.get(filePath);
1014
- if (cached
1015
- && cached.providerSessionId === providerSessionId
1016
- && cached.mtimeMs === stats.mtimeMs
1017
- && cached.size === stats.size) {
1018
- this.touchHistoryCache(filePath, cached);
1019
- return cached.messages;
1020
- }
1021
- const records = readJsonLines(filePath);
1022
- const messages = this.parseMessagesFromEntries(filePath, records, providerSessionId);
1023
- this.touchHistoryCache(filePath, {
1024
- filePath,
1025
- providerSessionId,
1026
- mtimeMs: stats.mtimeMs,
1027
- size: stats.size,
1028
- messages
1029
- });
1030
- return messages;
1031
- }
1032
- touchHistoryCache(filePath, entry) {
1033
- this.historyCache.delete(filePath);
1034
- this.historyCache.set(filePath, entry);
1035
- while (this.historyCache.size > HISTORY_CACHE_LIMIT) {
1036
- const oldestKey = this.historyCache.keys().next().value;
1037
- if (!oldestKey) {
1038
- break;
1039
- }
1040
- this.historyCache.delete(oldestKey);
1041
- }
1042
- }
1043
- async buildForkResultFromTransport(input) {
1044
- const resolvedStoreRef = input.rawStoreRef
1045
- ? this.resolveSessionFilePath(input.rawStoreRef, input.providerSessionId)
1046
- : this.findSessionFileByThreadId(input.providerSessionId)
1047
- ?? buildCodexActiveSessionPath(this.options.homeDir, `${input.providerSessionId}.jsonl`);
1048
- const messages = existsSync(resolvedStoreRef)
1049
- ? this.getParsedMessages(resolvedStoreRef, input.providerSessionId)
1050
- : [];
1051
- const threadMetadataIndex = this.readThreadMetadataIndex();
1052
- const threadMetadata = threadMetadataIndex.get(input.providerSessionId) ?? null;
1053
- const title = this.resolveIndexedTitle(threadMetadataIndex, input.providerSessionId)
1054
- ?? resolveCodexFallbackTitle(messages)
1055
- ?? "";
1056
- return {
1057
- session: {
1058
- provider: this.providerId,
1059
- providerSessionId: input.providerSessionId,
1060
- title,
1061
- workspacePath: input.workspacePath,
1062
- rawStoreRef: resolvedStoreRef,
1063
- isArchived: resolveCodexArchivedState(threadMetadata, resolvedStoreRef),
1064
- lastMessageAt: input.lastMessageAtOverride ?? messages.at(-1)?.timestamp ?? nextTimestamp(),
1065
- messageCount: input.messageCountOverride ?? messages.length,
1066
- parentProviderSessionId: input.fallbackParentProviderSessionId
1067
- },
1068
- forkMethod: input.forkMethod,
1069
- forkSourceType: input.forkSourceType,
1070
- inheritedPrefixMessageCount: input.inheritedPrefixMessageCountOverride ?? messages.length,
1071
- providerSourceMessageId: input.providerSourceMessageId
1072
- };
1073
- }
1074
- async forkThreadWithHistoryFallback(transport, providerSessionId, workspacePath, rawStoreRef) {
1075
- try {
1076
- return await transport.forkThread(providerSessionId);
1077
- }
1078
- catch (error) {
1079
- const history = buildCodexResumeHistoryFromRawStore(rawStoreRef);
1080
- if (!shouldFallbackCodexForkFromHistory(error, history)) {
1081
- throw error;
1082
- }
1083
- // app-server 的 thread/fork 依赖源 thread 已经挂在当前连接上。
1084
- // 这个前提跨请求就会失效,所以这里退回到本地 transcript 冷恢复一次。
1085
- const rebuilt = await transport.resumeThreadFromHistory({
1086
- providerSessionId: null,
1087
- workspacePath,
1088
- history,
1089
- model: null
1090
- });
1091
- return await transport.forkThread(rebuilt.providerSessionId);
1092
- }
1093
- }
1094
- touchSessionSummaryCache(filePath, entry) {
1095
- this.sessionSummaryCache.delete(filePath);
1096
- this.sessionSummaryCache.set(filePath, entry);
1097
- while (this.sessionSummaryCache.size > SESSION_SUMMARY_CACHE_LIMIT) {
1098
- const oldestKey = this.sessionSummaryCache.keys().next().value;
1099
- if (!oldestKey) {
1100
- break;
1101
- }
1102
- this.sessionSummaryCache.delete(oldestKey);
1103
- }
1104
- }
1105
- invalidateThreadMetadataIndexCache() {
1106
- this.threadMetadataIndexCache = null;
1107
- }
1108
- touchSpawnRelationScanCache(filePath, entry) {
1109
- this.spawnRelationScanCache.delete(filePath);
1110
- this.spawnRelationScanCache.set(filePath, entry);
1111
- while (this.spawnRelationScanCache.size > SPAWN_RELATION_SCAN_CACHE_LIMIT) {
1112
- const oldestKey = this.spawnRelationScanCache.keys().next().value;
1113
- if (!oldestKey) {
1114
- break;
1115
- }
1116
- this.spawnRelationScanCache.delete(oldestKey);
1117
- }
1118
- }
1119
- isForkedChildHistoryAligned(childThreadReadResult, expectedHistory) {
1120
- const expectedSignatures = collectCodexForkComparableSignatures(expectedHistory);
1121
- if (expectedSignatures.length === 0) {
1122
- return false;
1123
- }
1124
- let childHistory;
1125
- try {
1126
- childHistory = extractCodexThreadHistory(childThreadReadResult);
1127
- }
1128
- catch {
1129
- return false;
1130
- }
1131
- const childSignatures = collectCodexForkComparableSignatures(childHistory);
1132
- if (childSignatures.length !== expectedSignatures.length) {
1133
- return false;
1134
- }
1135
- return expectedSignatures.every((signature, index) => childSignatures[index] === signature);
1136
- }
1137
- resolveCodexSessionId(metaPayload, providerSessionId) {
1138
- const metaId = ensureText(metaPayload.id).trim();
1139
- const normalizedProviderSessionId = ensureText(providerSessionId).trim();
1140
- if (looksLikeCodexThreadId(metaId)) {
1141
- return metaId;
1142
- }
1143
- const matched = normalizedProviderSessionId.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
1144
- if (matched?.[1]) {
1145
- return matched[1];
1146
- }
1147
- return metaId || normalizedProviderSessionId;
1148
- }
1149
- resolveIndexedTitle(index, sessionId) {
1150
- const metadata = index.get(sessionId);
1151
- const indexedTitle = normalizeCodexIndexedTitle(metadata?.title);
1152
- const normalizedFirstUserMessage = normalizeCodexIndexedTitle(metadata?.firstUserMessage);
1153
- if (indexedTitle) {
1154
- // Codex 有时会把第一条用户消息原样回填成 title,这种脏标题仍然按统一长度预算裁掉。
1155
- if (normalizedFirstUserMessage && indexedTitle === normalizedFirstUserMessage) {
1156
- return indexedTitle.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH);
1157
- }
1158
- return indexedTitle;
1159
- }
1160
- return normalizeCodexMessageTitle(metadata?.firstUserMessage);
1161
- }
1162
- readSpawnedAgentRelationIndex(files, targetPath, threadMetadataIndex, candidateThreadIds) {
1163
- const directRelations = new Map();
1164
- const spawnRecords = [];
1165
- for (const entry of files) {
1166
- const { filePath, stats } = entry;
1167
- const cached = this.spawnRelationScanCache.get(filePath);
1168
- if (cached
1169
- && cached.mtimeMs === stats.mtimeMs
1170
- && cached.size === stats.size) {
1171
- this.touchSpawnRelationScanCache(filePath, cached);
1172
- if (cached.workspacePath !== targetPath) {
1173
- continue;
1174
- }
1175
- for (const [threadId, relation] of cached.directRelations) {
1176
- directRelations.set(threadId, relation);
1177
- }
1178
- spawnRecords.push(...cached.spawnRecords);
1179
- continue;
1180
- }
1181
- const sessionIdentity = entry.sessionIdentity ?? this.readSessionIdentity(filePath, basename(filePath, ".jsonl"));
1182
- if (!sessionIdentity) {
1183
- this.touchSpawnRelationScanCache(filePath, {
1184
- filePath,
1185
- mtimeMs: stats.mtimeMs,
1186
- size: stats.size,
1187
- workspacePath: null,
1188
- directRelations: [],
1189
- spawnRecords: []
1190
- });
1191
- continue;
1192
- }
1193
- const workspacePath = normalizeWorkspacePath(sessionIdentity.cwd);
1194
- if (workspacePath !== targetPath) {
1195
- this.touchSpawnRelationScanCache(filePath, {
1196
- filePath,
1197
- mtimeMs: stats.mtimeMs,
1198
- size: stats.size,
1199
- workspacePath,
1200
- directRelations: [],
1201
- spawnRecords: []
1202
- });
1203
- continue;
1204
- }
1205
- if (sessionIdentity.parentThreadId) {
1206
- const relation = {
1207
- parentProviderSessionId: sessionIdentity.parentThreadId,
1208
- kind: sessionIdentity.parentThreadKind ?? "fork"
1209
- };
1210
- directRelations.set(sessionIdentity.threadId, relation);
1211
- this.touchSpawnRelationScanCache(filePath, {
1212
- filePath,
1213
- mtimeMs: stats.mtimeMs,
1214
- size: stats.size,
1215
- workspacePath,
1216
- directRelations: [[sessionIdentity.threadId, relation]],
1217
- spawnRecords: []
1218
- });
1219
- continue;
1220
- }
1221
- const records = readJsonLines(filePath).map((record) => record.data);
1222
- const spawnCallById = new Map();
1223
- const fileSpawnRecords = [];
1224
- const fileDirectRelations = [];
1225
- for (const record of records) {
1226
- if (record.type !== "response_item") {
1227
- continue;
1228
- }
1229
- const payload = (record.payload ?? {});
1230
- const payloadType = ensureText(payload.type).trim();
1231
- if (payloadType === "function_call" && ensureText(payload.name).trim() === "spawn_agent") {
1232
- const callId = ensureText(payload.call_id).trim();
1233
- const args = parseStructuredJson(ensureText(payload.arguments));
1234
- const message = ensureText(args?.message).trim();
1235
- if (callId.length === 0 || message.length === 0) {
1236
- continue;
1237
- }
1238
- const spawnRecord = {
1239
- parentProviderSessionId: sessionIdentity.threadId,
1240
- workspacePath: sessionIdentity.cwd,
1241
- message,
1242
- timestampMs: toTimestampMs(record.timestamp)
1243
- };
1244
- spawnCallById.set(callId, spawnRecord);
1245
- fileSpawnRecords.push(spawnRecord);
1246
- spawnRecords.push(spawnRecord);
1247
- continue;
1248
- }
1249
- if (payloadType !== "function_call_output") {
1250
- continue;
1251
- }
1252
- const callId = ensureText(payload.call_id).trim();
1253
- const spawnRecord = spawnCallById.get(callId);
1254
- if (!spawnRecord) {
1255
- continue;
1256
- }
1257
- const agentId = parseCodexAgentIdFromToolOutput(ensureText(payload.output));
1258
- if (!agentId) {
1259
- continue;
1260
- }
1261
- const relation = {
1262
- parentProviderSessionId: spawnRecord.parentProviderSessionId,
1263
- kind: "spawn"
1264
- };
1265
- directRelations.set(agentId, relation);
1266
- fileDirectRelations.push([agentId, relation]);
1267
- }
1268
- this.touchSpawnRelationScanCache(filePath, {
1269
- filePath,
1270
- mtimeMs: stats.mtimeMs,
1271
- size: stats.size,
1272
- workspacePath,
1273
- directRelations: fileDirectRelations,
1274
- spawnRecords: fileSpawnRecords
1275
- });
1276
- }
1277
- const relationCandidates = candidateThreadIds === undefined
1278
- ? [...threadMetadataIndex.entries()]
1279
- : [...new Set(candidateThreadIds)]
1280
- .map((threadId) => [threadId, threadMetadataIndex.get(threadId) ?? null])
1281
- .filter((entry) => entry[1] !== null);
1282
- for (const [threadId, metadata] of relationCandidates) {
1283
- if (directRelations.has(threadId)) {
1284
- continue;
1285
- }
1286
- if (!metadata.agentRole && !metadata.agentNickname) {
1287
- continue;
1288
- }
1289
- const firstUserMessage = metadata.firstUserMessage?.trim();
1290
- const workspacePath = metadata.cwd ? normalizeWorkspacePath(metadata.cwd) : null;
1291
- if (!firstUserMessage || workspacePath !== targetPath) {
1292
- continue;
1293
- }
1294
- const matchedSpawn = pickClosestCodexSpawnRecord(spawnRecords, workspacePath, firstUserMessage, metadata.createdAtMs);
1295
- if (!matchedSpawn) {
1296
- continue;
1297
- }
1298
- directRelations.set(threadId, {
1299
- parentProviderSessionId: matchedSpawn.parentProviderSessionId,
1300
- kind: "spawn"
1301
- });
1302
- }
1303
- return directRelations;
1304
- }
1305
- readSessionIdentity(filePath, fallbackSessionId) {
1306
- if (!existsSync(filePath)) {
1307
- return null;
1308
- }
1309
- const firstLine = readFirstNonEmptyLine(filePath);
1310
- if (!firstLine) {
1311
- return null;
1312
- }
1313
- try {
1314
- const record = JSON.parse(firstLine);
1315
- if (ensureText(record.type).trim() !== "session_meta") {
1316
- return null;
1317
- }
1318
- const payload = (record.payload ?? {});
1319
- const parentThreadRelation = resolveCodexParentThreadRelation(payload);
1320
- return {
1321
- threadId: this.resolveCodexSessionId(payload, fallbackSessionId),
1322
- cwd: ensureText(payload.cwd).trim(),
1323
- parentThreadId: parentThreadRelation.parentThreadId,
1324
- parentThreadKind: parentThreadRelation.kind
1325
- };
1326
- }
1327
- catch {
1328
- return null;
1329
- }
1330
- }
1331
- hydrateSessionSummary(summary, filePath, stats, metadata, relation) {
1332
- const resolvedRelation = relation ?? null;
1333
- const resolvedMetadata = metadata ?? null;
1334
- const isSubagent = resolvedMetadata || resolvedRelation
1335
- ? isCodexSubagentThread(resolvedMetadata, resolvedRelation)
1336
- : Boolean(summary.isSubagent);
1337
- return {
1338
- ...summary,
1339
- rawStoreRef: filePath,
1340
- isArchived: resolveCodexArchivedState(resolvedMetadata, filePath),
1341
- parentProviderSessionId: resolvedRelation?.parentProviderSessionId ?? summary.parentProviderSessionId ?? null,
1342
- isSubagent,
1343
- subagentLabel: resolvedMetadata !== null
1344
- ? buildCodexSubagentLabel(resolvedMetadata)
1345
- : summary.subagentLabel ?? null,
1346
- sourceMtimeMs: stats.mtimeMs,
1347
- sourceSizeBytes: stats.size
1348
- };
1349
- }
1350
- parseMessages(filePath, records, providerSessionId) {
1351
- return this.parseMessagesFromEntries(filePath, records, providerSessionId);
1352
- }
1353
- parseMessagesFromEntries(filePath, records, providerSessionId) {
1354
- const effectiveRecords = filterRolledBackCodexRecords(records);
1355
- const messages = [];
1356
- const messageIndexesByKey = new Map();
1357
- const toolNameById = new Map();
1358
- const toolInputById = new Map();
1359
- const commandPatchByCallId = collectCodexCommandPatchesByCallId(effectiveRecords, filePath);
1360
- let sequence = 0;
1361
- const pushMessage = (source, message) => {
1362
- const dedupeKey = buildCodexMessageDedupeKey(message);
1363
- const candidateIndexes = messageIndexesByKey.get(dedupeKey) ?? [];
1364
- for (let index = candidateIndexes.length - 1; index >= 0; index -= 1) {
1365
- const existingIndex = candidateIndexes[index];
1366
- const existing = messages[existingIndex];
1367
- if (!isEquivalentCodexMessage(existing.message, message)) {
1368
- continue;
1369
- }
1370
- const mergedEquivalent = mergeEquivalentCodexMessages(existing.message, existing.source, message, source);
1371
- if (mergedEquivalent.source !== existing.source
1372
- || mergedEquivalent.message.messageId !== existing.message.messageId
1373
- || mergedEquivalent.message.rawRef !== existing.message.rawRef
1374
- || mergedEquivalent.message.timestamp !== existing.message.timestamp
1375
- || JSON.stringify(mergedEquivalent.message.toolCall) !== JSON.stringify(existing.message.toolCall)) {
1376
- messages[existingIndex] = {
1377
- source: mergedEquivalent.source,
1378
- dedupeKey: buildCodexMessageDedupeKey(mergedEquivalent.message),
1379
- message: {
1380
- ...mergedEquivalent.message,
1381
- sequence: existing.message.sequence
1382
- }
1383
- };
1384
- }
1385
- return;
1386
- }
1387
- sequence += 1;
1388
- messageIndexesByKey.set(dedupeKey, [...candidateIndexes, messages.length]);
1389
- messages.push({
1390
- source,
1391
- dedupeKey,
1392
- message: {
1393
- ...message,
1394
- sequence
1395
- }
1396
- });
1397
- };
1398
- effectiveRecords.forEach(({ lineNumber, partIndex, data: record }) => {
1399
- const rawRef = createRawRef(this.providerId, filePath, lineNumber, partIndex || undefined);
1400
- if (record.type === "event_msg") {
1401
- const payload = (record.payload ?? {});
1402
- const eventType = ensureText(payload.type);
1403
- if (eventType === "user_message") {
1404
- const content = ensureText(payload.message);
1405
- if (content.length > 0) {
1406
- pushMessage("event_msg", {
1407
- messageId: resolveCodexParsedMessageId({
1408
- providerSessionId,
1409
- rawRef,
1410
- role: "user",
1411
- kind: "text",
1412
- payloadId: payload.id
1413
- }),
1414
- provider: this.providerId,
1415
- providerSessionId,
1416
- role: "user",
1417
- kind: "text",
1418
- content,
1419
- toolCall: null,
1420
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1421
- rawRef
1422
- });
1423
- }
1424
- }
1425
- if (eventType === "agent_message") {
1426
- const content = ensureText(payload.message);
1427
- if (content.length > 0) {
1428
- pushMessage("event_msg", {
1429
- messageId: resolveCodexParsedMessageId({
1430
- providerSessionId,
1431
- rawRef,
1432
- role: "assistant",
1433
- kind: "text",
1434
- payloadId: payload.id
1435
- }),
1436
- provider: this.providerId,
1437
- providerSessionId,
1438
- role: "assistant",
1439
- kind: "text",
1440
- content,
1441
- toolCall: null,
1442
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1443
- rawRef
1444
- });
1445
- }
1446
- }
1447
- if (eventType === "agent_reasoning") {
1448
- const content = extractTextBlocks(payload.text ?? payload.message).trim();
1449
- if (content.length > 0) {
1450
- pushMessage("event_msg", {
1451
- messageId: resolveCodexParsedMessageId({
1452
- providerSessionId,
1453
- rawRef,
1454
- role: "assistant",
1455
- kind: "thinking",
1456
- payloadId: payload.id
1457
- }),
1458
- provider: this.providerId,
1459
- providerSessionId,
1460
- role: "assistant",
1461
- kind: "thinking",
1462
- content,
1463
- toolCall: null,
1464
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1465
- rawRef
1466
- });
1467
- }
1468
- }
1469
- }
1470
- if (record.type === "response_item") {
1471
- const payload = (record.payload ?? {});
1472
- const payloadType = ensureText(payload.type);
1473
- if (payloadType === "reasoning") {
1474
- const content = extractTextFromArray(payload.summary);
1475
- if (content.length === 0) {
1476
- return;
1477
- }
1478
- pushMessage("response_item", {
1479
- messageId: resolveCodexParsedMessageId({
1480
- providerSessionId,
1481
- rawRef,
1482
- role: "assistant",
1483
- kind: "thinking",
1484
- payloadId: payload.id
1485
- }),
1486
- provider: this.providerId,
1487
- providerSessionId,
1488
- role: "assistant",
1489
- kind: "thinking",
1490
- content,
1491
- toolCall: null,
1492
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1493
- rawRef
1494
- });
1495
- return;
1496
- }
1497
- if (payloadType === "function_call" || payloadType === "custom_tool_call") {
1498
- const callId = ensureText(payload.call_id).trim() || rawRef;
1499
- const rawName = ensureText(payload.name).trim() || "tool";
1500
- const inputSource = payloadType === "custom_tool_call" ? payload.input : payload.arguments;
1501
- const commandPatch = buildApplyPatchFromCodexCommandLikeValue(inputSource) ?? commandPatchByCallId.get(callId) ?? null;
1502
- const name = commandPatch ? "apply_patch" : rawName;
1503
- const input = commandPatch ?? stringifyStructuredValue(inputSource);
1504
- toolNameById.set(callId, name);
1505
- toolInputById.set(callId, input);
1506
- pushMessage("response_item", {
1507
- messageId: resolveCodexParsedMessageId({
1508
- providerSessionId,
1509
- rawRef,
1510
- role: "tool",
1511
- kind: "tool_call",
1512
- callId
1513
- }),
1514
- provider: this.providerId,
1515
- providerSessionId,
1516
- role: "tool",
1517
- kind: "tool_call",
1518
- content: input,
1519
- toolCall: {
1520
- callId,
1521
- name,
1522
- input,
1523
- output: null,
1524
- error: null,
1525
- status: "running"
1526
- },
1527
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1528
- rawRef
1529
- });
1530
- return;
1531
- }
1532
- if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
1533
- const callId = ensureText(payload.call_id).trim() || rawRef;
1534
- const output = extractTextBlocks(payload.output).trim() || stringifyStructuredValue(payload.output);
1535
- const outputPatch = buildApplyPatchFromCodexCommandLikeValue(payload.output);
1536
- const name = outputPatch ? "apply_patch" : (toolNameById.get(callId) ?? "tool");
1537
- const input = resolveCodexCommandPatchResultInput(outputPatch, toolInputById.get(callId));
1538
- const resultState = resolveToolResultState(payload, output);
1539
- pushMessage("response_item", {
1540
- messageId: resolveCodexParsedMessageId({
1541
- providerSessionId,
1542
- rawRef,
1543
- role: "tool",
1544
- kind: "tool_result",
1545
- callId
1546
- }),
1547
- provider: this.providerId,
1548
- providerSessionId,
1549
- role: "tool",
1550
- kind: "tool_result",
1551
- content: output,
1552
- toolCall: {
1553
- callId,
1554
- name,
1555
- input,
1556
- output: resultState.status === "failed" ? null : output,
1557
- error: resultState.status === "failed" ? output : null,
1558
- status: resultState.status
1559
- },
1560
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1561
- rawRef
1562
- });
1563
- return;
1564
- }
1565
- if (payloadType !== "message") {
1566
- return;
1567
- }
1568
- const role = ensureText(payload.role);
1569
- const content = extractTextFromArray(payload.content);
1570
- if (content.length === 0 || (role !== "assistant" && role !== "user")) {
1571
- return;
1572
- }
1573
- pushMessage("response_item", {
1574
- messageId: resolveCodexParsedMessageId({
1575
- providerSessionId,
1576
- rawRef,
1577
- role,
1578
- kind: "text",
1579
- payloadId: payload.id
1580
- }),
1581
- provider: this.providerId,
1582
- providerSessionId,
1583
- role,
1584
- kind: "text",
1585
- content,
1586
- toolCall: null,
1587
- timestamp: safeDate(record.timestamp, nextTimestamp()),
1588
- rawRef
1589
- });
1590
- }
1591
- });
1592
- return messages.map((entry) => entry.message);
1593
- }
1594
- }
1595
- function resolveCodexCommandPatchResultInput(outputPatch, storedInput) {
1596
- if (!outputPatch) {
1597
- return storedInput ?? "";
1598
- }
1599
- if (storedInput && isCodexPatchWithHunks(storedInput) && !isCodexPatchWithHunks(outputPatch)) {
1600
- return storedInput;
1601
- }
1602
- return outputPatch;
1603
- }
1604
- function isCodexPatchWithHunks(value) {
1605
- return /(?:^|\n)@@\s/.test(value);
1606
- }
1607
- function collectCodexCommandPatchesByCallId(records, filePath) {
1608
- const patches = new Map();
1609
- for (const { lineNumber, partIndex, data: record } of records) {
1610
- if (record.type !== "response_item") {
1611
- continue;
1612
- }
1613
- const payload = (record.payload ?? {});
1614
- const payloadType = ensureText(payload.type).trim();
1615
- if (payloadType !== "function_call_output" && payloadType !== "custom_tool_call_output") {
1616
- continue;
1617
- }
1618
- const rawRef = createRawRef("codex", filePath, lineNumber, partIndex || undefined);
1619
- const callId = ensureText(payload.call_id).trim() || rawRef;
1620
- const patchText = buildApplyPatchFromCodexCommandLikeValue(payload.output);
1621
- if (patchText && !patches.has(callId)) {
1622
- patches.set(callId, patchText);
1623
- }
1624
- }
1625
- return patches;
1626
- }
1627
- function filterRolledBackCodexRecords(records) {
1628
- const completedTurnSegments = [];
1629
- let activeTurnStartLineNumber = null;
1630
- let sawRollbackEvent = false;
1631
- for (const recordEntry of records) {
1632
- const record = recordEntry.data;
1633
- if (record.type !== "event_msg") {
1634
- continue;
1635
- }
1636
- const payload = (record.payload ?? {});
1637
- const eventType = ensureText(payload.type).trim();
1638
- if (eventType === "task_started") {
1639
- activeTurnStartLineNumber = recordEntry.lineNumber;
1640
- continue;
1641
- }
1642
- if (eventType === "task_complete" || eventType === "task_failed") {
1643
- if (activeTurnStartLineNumber !== null) {
1644
- completedTurnSegments.push({
1645
- startLineNumber: activeTurnStartLineNumber,
1646
- endLineNumber: recordEntry.lineNumber,
1647
- rolledBack: false
1648
- });
1649
- }
1650
- activeTurnStartLineNumber = null;
1651
- continue;
1652
- }
1653
- if (eventType !== "thread_rolled_back") {
1654
- continue;
1655
- }
1656
- sawRollbackEvent = true;
1657
- const requestedTurnCount = Math.max(0, Math.trunc(typeof payload.num_turns === "number"
1658
- ? payload.num_turns
1659
- : Number.parseInt(ensureText(payload.num_turns), 10)) || 0);
1660
- if (requestedTurnCount <= 0) {
1661
- continue;
1662
- }
1663
- let remainingTurnsToRollback = requestedTurnCount;
1664
- for (let index = completedTurnSegments.length - 1; index >= 0; index -= 1) {
1665
- const segment = completedTurnSegments[index];
1666
- if (!segment || segment.rolledBack) {
1667
- continue;
1668
- }
1669
- segment.rolledBack = true;
1670
- remainingTurnsToRollback -= 1;
1671
- if (remainingTurnsToRollback <= 0) {
1672
- break;
1673
- }
1674
- }
1675
- }
1676
- if (!sawRollbackEvent) {
1677
- return records;
1678
- }
1679
- const rolledBackSegments = completedTurnSegments
1680
- .filter((segment) => segment.rolledBack)
1681
- .sort((left, right) => left.startLineNumber - right.startLineNumber);
1682
- if (rolledBackSegments.length === 0) {
1683
- return records;
1684
- }
1685
- const filteredRecords = [];
1686
- let segmentIndex = 0;
1687
- for (const recordEntry of records) {
1688
- while (segmentIndex < rolledBackSegments.length
1689
- && recordEntry.lineNumber > rolledBackSegments[segmentIndex].endLineNumber) {
1690
- segmentIndex += 1;
1691
- }
1692
- const activeSegment = segmentIndex < rolledBackSegments.length ? rolledBackSegments[segmentIndex] : null;
1693
- if (activeSegment
1694
- && recordEntry.lineNumber >= activeSegment.startLineNumber
1695
- && recordEntry.lineNumber <= activeSegment.endLineNumber) {
1696
- continue;
1697
- }
1698
- filteredRecords.push(recordEntry);
1699
- }
1700
- return filteredRecords;
1701
- }
1702
- function buildRecentHistoryPage(messages, totalMessageCount, limit) {
1703
- const effectiveTotal = Math.max(totalMessageCount, messages.length);
1704
- const pageMessages = messages.slice(-Math.min(limit, messages.length)).map((message, index, items) => ({
1705
- ...message,
1706
- sequence: effectiveTotal - items.length + index + 1
1707
- }));
1708
- const nextCursor = effectiveTotal > pageMessages.length
1709
- ? encodeCursor(effectiveTotal - pageMessages.length)
1710
- : null;
1711
- return {
1712
- messages: pageMessages,
1713
- cursor: encodeCursor(effectiveTotal),
1714
- nextCursor,
1715
- total: effectiveTotal
1716
- };
1717
- }
1718
- function buildCodexMessageDedupeKey(message) {
1719
- const comparable = toComparableCodexMessage(message);
1720
- return JSON.stringify({
1721
- role: comparable.role,
1722
- kind: comparable.kind,
1723
- content: comparable.content,
1724
- toolCall: comparable.toolCall
1725
- ? {
1726
- callId: comparable.toolCall.callId,
1727
- name: comparable.toolCall.name,
1728
- input: comparable.toolCall.input,
1729
- output: comparable.toolCall.output,
1730
- error: comparable.toolCall.error,
1731
- status: comparable.toolCall.status
1732
- }
1733
- : null
1734
- });
1735
- }
1736
- function mergeEquivalentCodexMessages(current, currentSource, incoming, incomingSource) {
1737
- const preferredBySource = codexMessageSourcePriority(incomingSource) > codexMessageSourcePriority(currentSource)
1738
- ? incoming
1739
- : current;
1740
- const preferredSource = codexMessageSourcePriority(incomingSource) > codexMessageSourcePriority(currentSource)
1741
- ? incomingSource
1742
- : currentSource;
1743
- const preferredStableMessageId = pickPreferredCodexEquivalentMessageId(current, incoming);
1744
- return {
1745
- source: preferredSource,
1746
- message: {
1747
- ...preferredBySource,
1748
- messageId: preferredStableMessageId
1749
- }
1750
- };
1751
- }
1752
- function pickPreferredCodexEquivalentMessageId(current, incoming) {
1753
- const currentUsesStableIdentity = current.messageId !== messageIdFromRawRef(current.rawRef);
1754
- const incomingUsesStableIdentity = incoming.messageId !== messageIdFromRawRef(incoming.rawRef);
1755
- if (currentUsesStableIdentity !== incomingUsesStableIdentity) {
1756
- return currentUsesStableIdentity ? current.messageId : incoming.messageId;
1757
- }
1758
- return incoming.messageId;
1759
- }
1760
- function resolveCodexFallbackTitle(messages) {
1761
- const preferredMessage = messages.find((message) => message.role === "user" && !looksLikeCodexRulesMessage(message.content));
1762
- if (preferredMessage) {
1763
- return normalizeCodexMessageTitle(preferredMessage.content);
1764
- }
1765
- const firstUserMessage = messages.find((message) => message.role === "user");
1766
- return normalizeCodexMessageTitle(firstUserMessage?.content);
1767
- }
1768
- function looksLikeCodexRulesMessage(content) {
1769
- const normalized = content.trim();
1770
- const beginsWithRulesHeader = /^#?\s*AGENTS\.md instructions for\b/i.test(normalized);
1771
- if (beginsWithRulesHeader) {
1772
- return true;
1773
- }
1774
- return /AGENTS\.md instructions for/i.test(normalized)
1775
- && /<INSTRUCTIONS>/i.test(normalized);
1776
- }
1777
- function codexMessageSourcePriority(source) {
1778
- return source === "response_item" ? 2 : 1;
1779
- }
1780
- function isEquivalentCodexMessage(left, right) {
1781
- const comparableLeft = toComparableCodexMessage(left);
1782
- const comparableRight = toComparableCodexMessage(right);
1783
- if (comparableLeft.role !== comparableRight.role ||
1784
- comparableLeft.kind !== comparableRight.kind ||
1785
- comparableLeft.content !== comparableRight.content) {
1786
- return false;
1787
- }
1788
- if (JSON.stringify(comparableLeft.toolCall) !== JSON.stringify(comparableRight.toolCall)) {
1789
- return false;
1790
- }
1791
- return areCodexTimestampsNear(left.timestamp, right.timestamp);
1792
- }
1793
- function toComparableCodexMessage(message) {
1794
- return {
1795
- role: message.role,
1796
- kind: message.kind,
1797
- content: normalizeComparableCodexContent(message.kind, message.content),
1798
- toolCall: message.toolCall
1799
- ? {
1800
- callId: message.toolCall.callId,
1801
- name: message.toolCall.name,
1802
- input: normalizeComparableCodexLineEndings(message.toolCall.input),
1803
- output: message.toolCall.output === null
1804
- ? null
1805
- : normalizeComparableCodexLineEndings(message.toolCall.output),
1806
- error: message.toolCall.error === null
1807
- ? null
1808
- : normalizeComparableCodexLineEndings(message.toolCall.error),
1809
- status: message.toolCall.status
1810
- }
1811
- : null
1812
- };
1813
- }
1814
- function normalizeComparableCodexContent(kind, content) {
1815
- const normalized = normalizeComparableCodexLineEndings(content);
1816
- if (kind === "text" || kind === "thinking") {
1817
- return normalized.trimEnd();
1818
- }
1819
- return normalized;
1820
- }
1821
- function normalizeComparableCodexLineEndings(content) {
1822
- return content.replace(/\r\n/g, "\n");
1823
- }
1824
- function areCodexTimestampsNear(left, right) {
1825
- const leftMs = Date.parse(left);
1826
- const rightMs = Date.parse(right);
1827
- if (!Number.isFinite(leftMs) || !Number.isFinite(rightMs)) {
1828
- return left === right;
1829
- }
1830
- return Math.abs(leftMs - rightMs) <= 1000;
1831
- }
1832
- function resolveCodexParsedMessageId(input) {
1833
- const stableIdentity = resolveCodexStableIdentity(input);
1834
- if (!stableIdentity) {
1835
- return messageIdFromRawRef(input.rawRef);
1836
- }
1837
- return messageIdFromStableKey(buildCodexStableMessageKey(input.providerSessionId, stableIdentity));
1838
- }
1839
- function resolveCodexStableIdentity(input) {
1840
- if (input.kind === "tool_call" || input.kind === "tool_result") {
1841
- const normalizedCallId = ensureText(input.callId).trim();
1842
- if (!normalizedCallId) {
1843
- return null;
1844
- }
1845
- return input.kind === "tool_call"
1846
- ? `tool:call:${normalizedCallId}`
1847
- : `tool:result:${normalizedCallId}`;
1848
- }
1849
- if (input.role !== "assistant"
1850
- && input.role !== "user") {
1851
- return null;
1852
- }
1853
- const normalizedPayloadId = ensureText(input.payloadId).trim();
1854
- if (!normalizedPayloadId) {
1855
- return null;
1856
- }
1857
- const identityKind = input.kind === "thinking" ? "thinking" : "text";
1858
- return `${input.role}:${identityKind}:${normalizedPayloadId}`;
1859
- }
1860
- function buildCodexStableMessageKey(providerSessionId, stableIdentity) {
1861
- return `codex:${providerSessionId}:${stableIdentity}`;
1862
- }
1863
- function extractTextFromArray(value) {
1864
- if (!Array.isArray(value)) {
1865
- return "";
1866
- }
1867
- return value
1868
- .map((item) => extractTextBlocks(item).trim())
1869
- .filter((item) => item.length > 0)
1870
- .join("\n");
1871
- }
1872
- function resolveToolResultState(payload, output) {
1873
- const statusText = ensureText(payload.status).trim().toLowerCase();
1874
- if (statusText === "failed" || statusText === "error") {
1875
- return { status: "failed" };
1876
- }
1877
- if (output.toLowerCase().includes("apply_patch was requested via exec_command")) {
1878
- return { status: "failed" };
1879
- }
1880
- if (statusText === "completed" || statusText === "success" || statusText === "succeeded") {
1881
- return { status: "completed" };
1882
- }
1883
- if (typeof payload.success === "boolean") {
1884
- return {
1885
- status: payload.success ? "completed" : "failed"
1886
- };
1887
- }
1888
- if (typeof payload.is_error === "boolean") {
1889
- return {
1890
- status: payload.is_error ? "failed" : "completed"
1891
- };
1892
- }
1893
- if (typeof payload.exit_code === "number") {
1894
- return {
1895
- status: payload.exit_code === 0 ? "completed" : "failed"
1896
- };
1897
- }
1898
- const exitCodeMatch = output.match(/(?:^|\n)Exit code:\s*(-?\d+)/i);
1899
- if (exitCodeMatch) {
1900
- return {
1901
- status: Number(exitCodeMatch[1]) === 0 ? "completed" : "failed"
1902
- };
1903
- }
1904
- if (payload.error != null) {
1905
- return { status: "failed" };
1906
- }
1907
- return { status: "completed" };
1908
- }
1909
- function shouldIgnoreCodingNsDraftSession(metaPayload) {
1910
- const source = ensureText(metaPayload.source).trim().toLowerCase();
1911
- const sessionId = ensureText(metaPayload.id).trim().toLowerCase();
1912
- return source === "codingns" && sessionId.startsWith("rollout-");
1913
- }
1914
- function looksLikeCodexThreadId(value) {
1915
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
1916
- }
1917
- function findLatestCodexStateDatabase(homeDir) {
1918
- if (!existsSync(homeDir)) {
1919
- return null;
1920
- }
1921
- const candidates = readdirSync(homeDir, { withFileTypes: true })
1922
- .filter((entry) => entry.isFile() && /^state_\d+\.sqlite$/i.test(entry.name))
1923
- .map((entry) => {
1924
- const filePath = join(homeDir, entry.name);
1925
- return {
1926
- filePath,
1927
- mtimeMs: statSync(filePath).mtimeMs
1928
- };
1929
- })
1930
- .sort((left, right) => right.mtimeMs - left.mtimeMs);
1931
- return candidates[0]?.filePath ?? null;
1932
- }
1933
- function parseStructuredJson(value) {
1934
- const text = value.trim();
1935
- if (text.length === 0) {
1936
- return null;
1937
- }
1938
- try {
1939
- const parsed = JSON.parse(text);
1940
- return parsed && typeof parsed === "object" ? parsed : null;
1941
- }
1942
- catch {
1943
- return null;
1944
- }
1945
- }
1946
- function readNonNegativeInteger(value) {
1947
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
1948
- return Math.trunc(value);
1949
- }
1950
- if (typeof value === "string" && /^\d+$/.test(value.trim())) {
1951
- return Number.parseInt(value.trim(), 10);
1952
- }
1953
- return null;
1954
- }
1955
- function clampUsageRatio(promptTokens, contextWindow) {
1956
- if (contextWindow <= 0) {
1957
- return 0;
1958
- }
1959
- return Math.min(Math.max(promptTokens / contextWindow, 0), 1);
1960
- }
1961
- function resolveCodexKnownContextWindow(modelId) {
1962
- if (!modelId) {
1963
- return null;
1964
- }
1965
- return KNOWN_CODEX_CONTEXT_WINDOWS.get(modelId) ?? null;
1966
- }
1967
- function readCodexConfigContextWindow(homeDir) {
1968
- const configPath = join(homeDir, "config.toml");
1969
- if (!existsSync(configPath)) {
1970
- return null;
1971
- }
1972
- try {
1973
- const content = readFileSync(configPath, "utf8");
1974
- const matched = content.match(CODEX_CONFIG_CONTEXT_WINDOW_PATTERN);
1975
- if (!matched?.[1]) {
1976
- return null;
1977
- }
1978
- return Number.parseInt(matched[1], 10);
1979
- }
1980
- catch {
1981
- return null;
1982
- }
1983
- }
1984
- function parseCodexAgentIdFromToolOutput(output) {
1985
- const parsed = parseStructuredJson(output);
1986
- const agentId = ensureText(parsed?.agent_id ?? parsed?.agentId).trim();
1987
- if (looksLikeCodexThreadId(agentId)) {
1988
- return agentId;
1989
- }
1990
- const matched = output.match(/"agent(?:_id|Id)"\s*:\s*"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"/i);
1991
- return matched?.[1] ?? null;
1992
- }
1993
- function resolveCodexParentThreadRelation(payload) {
1994
- const source = typeof payload.source === "object" && payload.source !== null
1995
- ? payload.source
1996
- : null;
1997
- const subagent = typeof source?.subagent === "object" && source.subagent !== null
1998
- ? source.subagent
1999
- : null;
2000
- const threadSpawn = typeof subagent?.thread_spawn === "object" && subagent.thread_spawn !== null
2001
- ? subagent.thread_spawn
2002
- : null;
2003
- const nestedParentThreadId = ensureText(threadSpawn?.parent_thread_id).trim();
2004
- if (nestedParentThreadId.length > 0) {
2005
- return {
2006
- parentThreadId: nestedParentThreadId,
2007
- kind: "spawn"
2008
- };
2009
- }
2010
- const directParentThreadId = ensureText(payload.forked_from_id).trim();
2011
- if (directParentThreadId.length > 0) {
2012
- return {
2013
- parentThreadId: directParentThreadId,
2014
- kind: "fork"
2015
- };
2016
- }
2017
- return {
2018
- parentThreadId: null,
2019
- kind: null
2020
- };
2021
- }
2022
- function toTimestampMs(value) {
2023
- const timestampMs = Date.parse(ensureText(value).trim());
2024
- return Number.isFinite(timestampMs) ? timestampMs : null;
2025
- }
2026
- function pickClosestCodexSpawnRecord(spawnRecords, workspacePath, message, createdAtMs) {
2027
- const matchedRecords = spawnRecords.filter((record) => record.workspacePath !== null &&
2028
- normalizeWorkspacePath(record.workspacePath) === workspacePath &&
2029
- record.message === message);
2030
- if (matchedRecords.length === 0) {
2031
- return null;
2032
- }
2033
- if (createdAtMs === null) {
2034
- return matchedRecords.at(-1) ?? null;
2035
- }
2036
- const closeRecord = matchedRecords
2037
- .filter((record) => record.timestampMs !== null)
2038
- .sort((left, right) => Math.abs((left.timestampMs ?? createdAtMs) - createdAtMs) -
2039
- Math.abs((right.timestampMs ?? createdAtMs) - createdAtMs))
2040
- .find((record) => Math.abs((record.timestampMs ?? createdAtMs) - createdAtMs) <= 120_000);
2041
- return closeRecord ?? matchedRecords.at(-1) ?? null;
2042
- }
2043
- function isCodexSubagentThread(metadata, relation) {
2044
- return Boolean(relation?.kind === "spawn" || metadata?.agentRole || metadata?.agentNickname);
2045
- }
2046
- function buildCodexSubagentLabel(metadata) {
2047
- const agentRole = metadata?.agentRole?.trim() || "";
2048
- const agentNickname = metadata?.agentNickname?.trim() || "";
2049
- if (agentRole && agentNickname) {
2050
- return `${agentRole} · ${agentNickname}`;
2051
- }
2052
- return agentNickname || agentRole || null;
2053
- }
2054
- function resolveCodexArchivedState(metadata, filePath) {
2055
- if (isCodexArchivedFilePath(filePath)) {
2056
- return true;
2057
- }
2058
- if (typeof metadata?.isArchived === "boolean") {
2059
- return metadata.isArchived;
2060
- }
2061
- return false;
2062
- }
2063
- function isCodexArchivedFilePath(filePath) {
2064
- return filePath.replaceAll("\\", "/").includes("/archived_sessions/");
2065
- }
2066
- function buildCodexActiveSessionPath(homeDir, fileName) {
2067
- const match = fileName.match(/^rollout-(\d{4})-(\d{2})-(\d{2})T.+\.jsonl$/);
2068
- if (!match) {
2069
- return join(homeDir, "sessions", fileName);
2070
- }
2071
- return join(homeDir, "sessions", match[1], match[2], match[3], fileName);
2072
- }
2073
- function buildSyntheticCodexHistoryPath(homeDir, providerSessionId) {
2074
- return join(homeDir, "runtime", "codex", `${providerSessionId}.jsonl`);
2075
- }
2076
- function isSyntheticCodexHistoryPath(rawStoreRef) {
2077
- const normalized = rawStoreRef.replaceAll("\\", "/").toLowerCase();
2078
- return normalized.includes("/runtime/codex/") || normalized.startsWith("runtime/codex/");
2079
- }
2080
- function hasUsableCodexTitle(title) {
2081
- return normalizeCodexIndexedTitle(title) !== null;
2082
- }
2083
- function normalizeCodexIndexedTitle(title) {
2084
- const normalized = ensureText(title).trim();
2085
- if (normalized.length === 0 || looksLikeCodexRulesMessage(normalized)) {
2086
- return null;
2087
- }
2088
- return normalized;
2089
- }
2090
- function normalizeCodexMessageTitle(content) {
2091
- const normalized = normalizeCodexIndexedTitle(content);
2092
- return normalized ? normalized.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH) : null;
2093
- }
2094
- function extractCodexThreadHistory(result) {
2095
- const snapshot = extractCodexThreadHistorySnapshot(result);
2096
- return snapshot.value;
2097
- }
2098
- function extractCodexThreadHistorySnapshot(result) {
2099
- const directHistory = pickCodexHistoryArray(result)
2100
- ?? pickCodexHistoryArray(toRecord(result.thread))
2101
- ?? pickCodexHistoryArray(toRecord(result.data));
2102
- if (directHistory) {
2103
- return {
2104
- kind: "entries",
2105
- value: directHistory,
2106
- comparableEntries: directHistory.flatMap((entry, entryIndex) => {
2107
- const signature = buildCodexThreadHistorySignature(entry);
2108
- return signature ? [{ signature, entryIndex }] : [];
2109
- })
2110
- };
2111
- }
2112
- const turns = pickCodexTurnArray(result.turns)
2113
- ?? pickCodexTurnArray(toRecord(result.thread)?.turns)
2114
- ?? pickCodexTurnArray(toRecord(result.data)?.turns);
2115
- if (!turns) {
2116
- throw new Error("CODEX_THREAD_HISTORY_MISSING");
2117
- }
2118
- const comparableEntries = turns.flatMap((turn, turnIndex) => collectCodexTurnComparableEntries(turn, turnIndex));
2119
- if (comparableEntries.length === 0) {
2120
- throw new Error("CODEX_THREAD_HISTORY_MISSING");
2121
- }
2122
- return {
2123
- kind: "turns",
2124
- value: turns,
2125
- comparableEntries
2126
- };
2127
- }
2128
- function isSyntheticCodexSessionTitle(title) {
2129
- const normalizedTitle = ensureText(title).trim();
2130
- if (normalizedTitle.length === 0) {
2131
- return false;
2132
- }
2133
- return (/^rollout-\d{4}-\d{2}-\d{2}t/i.test(normalizedTitle) ||
2134
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedTitle));
2135
- }
2136
- function pickCodexHistoryArray(value) {
2137
- const record = toRecord(value);
2138
- if (!record) {
2139
- return null;
2140
- }
2141
- for (const key of ["history", "items"]) {
2142
- const candidate = record[key];
2143
- if (Array.isArray(candidate)
2144
- && candidate.some((entry) => buildCodexThreadHistorySignature(entry) !== null)) {
2145
- return candidate;
2146
- }
2147
- }
2148
- return null;
2149
- }
2150
- function pickCodexTurnArray(value) {
2151
- return Array.isArray(value) ? value : null;
2152
- }
2153
- function collectCodexTurnComparableEntries(value, turnIndex, parentPath = []) {
2154
- const record = toRecord(value);
2155
- if (!record) {
2156
- return [];
2157
- }
2158
- for (const key of ["history", "items"]) {
2159
- const candidate = record[key];
2160
- if (Array.isArray(candidate)
2161
- && candidate.some((entry) => buildCodexThreadHistorySignature(entry) !== null)) {
2162
- const containerPath = [...parentPath, key];
2163
- return candidate.flatMap((entry, entryIndex) => {
2164
- const signature = buildCodexThreadHistorySignature(entry);
2165
- return signature
2166
- ? [{
2167
- signature,
2168
- turnIndex,
2169
- containerPath,
2170
- entryIndex
2171
- }]
2172
- : [];
2173
- });
2174
- }
2175
- }
2176
- return [
2177
- ["input", record.input],
2178
- ["output", record.output],
2179
- ["turn", record.turn],
2180
- ["data", record.data],
2181
- ["result", record.result]
2182
- ].flatMap(([key, candidate]) => collectCodexTurnComparableEntries(candidate, turnIndex, [...parentPath, key]));
2183
- }
2184
- function toRecord(value) {
2185
- return typeof value === "object" && value !== null
2186
- ? value
2187
- : null;
2188
- }
2189
- function truncateCodexThreadHistory(history, parsedMessages, targetMessage) {
2190
- const snapshot = normalizeCodexThreadHistorySnapshot(history);
2191
- if (snapshot.kind === "entries") {
2192
- const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
2193
- return snapshot.value.slice(0, targetEntry.entryIndex + 1);
2194
- }
2195
- const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
2196
- const truncatedTurns = snapshot.value.slice(0, targetEntry.turnIndex + 1);
2197
- const lastTurn = truncatedTurns.at(-1);
2198
- if (!lastTurn) {
2199
- throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
2200
- }
2201
- return [
2202
- ...flattenCodexTurnHistory(truncatedTurns.slice(0, -1)),
2203
- ...collectCodexTurnHistoryItems(truncateCodexTurnAtPath(lastTurn, targetEntry.containerPath, targetEntry.entryIndex))
2204
- ];
2205
- }
2206
- function normalizeCodexThreadHistorySnapshot(history) {
2207
- const directComparableEntries = history.flatMap((entry, entryIndex) => {
2208
- const signature = buildCodexThreadHistorySignature(entry);
2209
- return signature ? [{ signature, entryIndex }] : [];
2210
- });
2211
- if (directComparableEntries.length > 0) {
2212
- return {
2213
- kind: "entries",
2214
- value: history,
2215
- comparableEntries: directComparableEntries
2216
- };
2217
- }
2218
- const turnComparableEntries = history.flatMap((turn, turnIndex) => collectCodexTurnComparableEntries(turn, turnIndex));
2219
- if (turnComparableEntries.length > 0) {
2220
- return {
2221
- kind: "turns",
2222
- value: history,
2223
- comparableEntries: turnComparableEntries
2224
- };
2225
- }
2226
- throw new Error("CODEX_THREAD_HISTORY_MISSING");
2227
- }
2228
- function resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage) {
2229
- const targetSignature = buildCodexThreadHistorySignature(targetMessage);
2230
- if (!targetSignature) {
2231
- throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
2232
- }
2233
- const matchingEntries = snapshot.comparableEntries.filter((entry) => entry.signature === targetSignature);
2234
- if (matchingEntries.length === 0) {
2235
- throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
2236
- }
2237
- const targetOccurrence = resolveCodexMessageSignatureOccurrence(parsedMessages, targetMessage);
2238
- return matchingEntries[Math.min(targetOccurrence, matchingEntries.length) - 1] ?? matchingEntries.at(-1);
2239
- }
2240
- function resolveCodexMessageSignatureOccurrence(parsedMessages, targetMessage) {
2241
- const targetSignature = buildCodexThreadHistorySignature(targetMessage);
2242
- if (!targetSignature) {
2243
- return 1;
2244
- }
2245
- let occurrence = 0;
2246
- for (const message of parsedMessages) {
2247
- if (buildCodexThreadHistorySignature(message) !== targetSignature) {
2248
- continue;
2249
- }
2250
- occurrence += 1;
2251
- if (message.messageId === targetMessage.messageId) {
2252
- return occurrence;
2253
- }
2254
- }
2255
- return Math.max(1, occurrence);
2256
- }
2257
- function buildCodexTurnRollbackPlan(snapshot, parsedMessages, targetMessage) {
2258
- const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
2259
- const turnEntries = snapshot.comparableEntries.filter((entry) => entry.turnIndex === targetEntry.turnIndex);
2260
- const lastComparableEntryInTurn = turnEntries.at(-1) ?? null;
2261
- if (!lastComparableEntryInTurn) {
2262
- throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
2263
- }
2264
- if (lastComparableEntryInTurn.containerPath.join("/") !== targetEntry.containerPath.join("/")
2265
- || lastComparableEntryInTurn.entryIndex !== targetEntry.entryIndex) {
2266
- throw new Error("CODEX_MESSAGE_FORK_TURN_BOUNDARY_REQUIRED");
2267
- }
2268
- return {
2269
- targetTurnIndex: targetEntry.turnIndex,
2270
- numTurnsToRollback: Math.max(0, snapshot.value.length - targetEntry.turnIndex - 1)
2271
- };
2272
- }
2273
- function truncateCodexTurnAtPath(turn, containerPath, entryIndex) {
2274
- if (containerPath.length === 0) {
2275
- return turn;
2276
- }
2277
- const record = toRecord(turn);
2278
- if (!record) {
2279
- return turn;
2280
- }
2281
- const [head, ...rest] = containerPath;
2282
- const current = record[head];
2283
- if (rest.length === 0) {
2284
- if (!Array.isArray(current)) {
2285
- return turn;
2286
- }
2287
- return {
2288
- ...record,
2289
- [head]: current.slice(0, entryIndex + 1)
2290
- };
2291
- }
2292
- return {
2293
- ...record,
2294
- [head]: truncateCodexTurnAtPath(current, rest, entryIndex)
2295
- };
2296
- }
2297
- function flattenCodexTurnHistory(turns) {
2298
- return turns.flatMap((turn) => collectCodexTurnHistoryItems(turn));
2299
- }
2300
- function collectCodexTurnHistoryItems(value) {
2301
- const direct = pickCodexHistoryArray(value);
2302
- if (direct) {
2303
- return direct;
2304
- }
2305
- const record = toRecord(value);
2306
- if (!record) {
2307
- return [];
2308
- }
2309
- return [
2310
- record.input,
2311
- record.output,
2312
- record.turn,
2313
- record.data,
2314
- record.result
2315
- ].flatMap((candidate) => collectCodexTurnHistoryItems(candidate));
2316
- }
2317
- function buildCodexThreadHistorySignature(value) {
2318
- if (!value || typeof value !== "object") {
2319
- return null;
2320
- }
2321
- const item = value;
2322
- const normalizedKind = ensureText(item.kind).trim();
2323
- const normalizedRole = ensureText(item.role).trim();
2324
- const normalizedContent = ensureText(item.content).trim();
2325
- if (normalizedKind && normalizedRole) {
2326
- return `${normalizedRole}:${normalizedKind}:${normalizedContent}`;
2327
- }
2328
- const type = ensureText(item.type).trim();
2329
- if (type === "userMessage") {
2330
- const content = stringifyCodexThreadMessageContent(item.content);
2331
- return content ? `user:text:${content}` : null;
2332
- }
2333
- if (type === "agentMessage") {
2334
- const content = ensureText(item.text).trim() || stringifyCodexThreadMessageContent(item.content);
2335
- return content ? `assistant:text:${content}` : null;
2336
- }
2337
- if (type === "message") {
2338
- const role = ensureText(item.role).trim();
2339
- const content = stringifyCodexThreadMessageContent(item.content);
2340
- if (!role || !content) {
2341
- return null;
2342
- }
2343
- return `${role}:text:${content}`;
2344
- }
2345
- if (type === "reasoning") {
2346
- const content = stringifyCodexReasoningContent(item.summary ?? item.content);
2347
- return content ? `assistant:thinking:${content}` : null;
2348
- }
2349
- if (type === "function_call" || type === "tool_call") {
2350
- const name = ensureText(item.name).trim();
2351
- const inputValue = item.arguments ?? item.input;
2352
- const content = stringifyStructuredValue(inputValue);
2353
- return name || content ? `assistant:tool_call:${name}:${content}` : null;
2354
- }
2355
- if (type === "function_call_output" || type === "tool_result") {
2356
- const content = ensureText(item.output ?? item.content).trim();
2357
- return content ? `tool:tool_result:${content}` : null;
2358
- }
2359
- if (normalizedRole && normalizedContent) {
2360
- return `${normalizedRole}:text:${normalizedContent}`;
2361
- }
2362
- return null;
2363
- }
2364
- function applyForkSourceMessageSnapshot(targetMessage, snapshot) {
2365
- if (!snapshot) {
2366
- return targetMessage;
2367
- }
2368
- return {
2369
- ...targetMessage,
2370
- role: snapshot.role,
2371
- kind: snapshot.kind,
2372
- content: snapshot.content
2373
- };
2374
- }
2375
- function collectCodexForkComparableSignatures(history) {
2376
- try {
2377
- return normalizeCodexThreadHistorySnapshot(history).comparableEntries.map((entry) => entry.signature);
2378
- }
2379
- catch {
2380
- return [];
2381
- }
2382
- }
2383
- function stringifyCodexThreadMessageContent(content) {
2384
- if (typeof content === "string") {
2385
- return content.trim();
2386
- }
2387
- if (!Array.isArray(content)) {
2388
- return "";
2389
- }
2390
- return content
2391
- .map((part) => {
2392
- if (!part || typeof part !== "object") {
2393
- return "";
2394
- }
2395
- const record = part;
2396
- return ensureText(record.text
2397
- ?? record.input_text
2398
- ?? record.output_text
2399
- ?? (ensureText(record.type).trim() === "text" ? record.text : null)).trim();
2400
- })
2401
- .filter((value) => value.length > 0)
2402
- .join("\n")
2403
- .trim();
2404
- }
2405
- function stringifyCodexReasoningContent(content) {
2406
- if (typeof content === "string") {
2407
- return content.trim();
2408
- }
2409
- if (!Array.isArray(content)) {
2410
- return "";
2411
- }
2412
- return content
2413
- .map((part) => {
2414
- if (!part || typeof part !== "object") {
2415
- return "";
2416
- }
2417
- const record = part;
2418
- return ensureText(record.text ?? record.summary_text).trim();
2419
- })
2420
- .filter((value) => value.length > 0)
2421
- .join("\n")
2422
- .trim();
2423
- }
2424
- function shouldFallbackCodexForkFromHistory(error, history) {
2425
- if (history.length === 0) {
2426
- return false;
2427
- }
2428
- return isCodexThreadLoadError(error);
2429
- }
2430
- function isCodexThreadLoadError(error) {
2431
- const message = error instanceof Error ? error.message : String(error);
2432
- const normalized = message.trim().toLowerCase();
2433
- return (normalized.includes("thread not loaded") ||
2434
- normalized.includes("no rollout found for thread id"));
2435
- }
2436
- //# sourceMappingURL=codex.js.map