@jingyi0605/codingns 0.6.0 → 0.6.5

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 (253) hide show
  1. package/bin/codingns.mjs +240 -2
  2. package/dist/public/assets/AdaptiveButlerPage-CfsUVZKl.js +3 -0
  3. package/dist/public/assets/{App-BZvapsi8.js → App-B9HcTkMT.js} +3 -3
  4. package/dist/public/assets/{BootstrapPage-gHSoa4JN.js → BootstrapPage-BgLdZEKQ.js} +1 -1
  5. package/dist/public/assets/ConversationPage-Cj8go7L0.js +4 -0
  6. package/dist/public/assets/{DesktopDetachPreviewPage-4eMRxiBW.js → DesktopDetachPreviewPage-YCBnyC58.js} +1 -1
  7. package/dist/public/assets/DesktopWindowPage-CrvndE23.js +2 -0
  8. package/dist/public/assets/FileContextPanel-CdC7GGw5.js +1 -0
  9. package/dist/public/assets/GitSidebar-z2SBinQh.js +6 -0
  10. package/dist/public/assets/MobileCreateSessionSheet-Cb2HM-y3.js +1 -0
  11. package/dist/public/assets/{MobileTopHeaderFrame-Bwv8Ovm_.js → MobileTopHeaderFrame-etF2HKlm.js} +1 -1
  12. package/dist/public/assets/MobileWorkspaceSwitcherHeader-CuGJ31kb.js +1 -0
  13. package/dist/public/assets/{RelayConnectEntryPage-D_4YL-YH.js → RelayConnectEntryPage-BB6DbGtP.js} +1 -1
  14. package/dist/public/assets/{ServerSettingsModal-CMSm3BZU.js → ServerSettingsModal-Bl1sacZg.js} +1 -1
  15. package/dist/public/assets/SessionIndexPage-NbF9gJnp.js +1 -0
  16. package/dist/public/assets/SettingsPage-DGsmQpLv.js +1 -0
  17. package/dist/public/assets/TerminalManagerPanel-BOm8Hi_v.js +1 -0
  18. package/dist/public/assets/{TerminalPage-DaooFaJ4.js → TerminalPage-B5JNFU6w.js} +19 -19
  19. package/dist/public/assets/TerminalRuntimeFallbackModal-CM3LRKOJ.js +1 -0
  20. package/dist/public/assets/{ToolFilesPage-CGxBvYG0.js → ToolFilesPage-GSqKQsj_.js} +1 -1
  21. package/dist/public/assets/ToolGitPage-BVFWMMQp.js +1 -0
  22. package/dist/public/assets/ToolProcessesPage-DZ456fYz.js +1 -0
  23. package/dist/public/assets/ToolsHomePage-DsJp0y8A.js +1 -0
  24. package/dist/public/assets/WorkbenchLandingPage-DyPei0e-.js +1 -0
  25. package/dist/public/assets/WorkbenchLayout-DlbgBT3n.js +4 -0
  26. package/dist/public/assets/WorkbenchModal-LNfB69qx.js +1 -0
  27. package/dist/public/assets/WorkbenchShellRoute-BsxumYx5.js +1 -0
  28. package/dist/public/assets/WorkbenchShellRoute-DhQo_0vu.css +1 -0
  29. package/dist/public/assets/WorkspaceDebugDetailPage-CaXj5zVI.js +1 -0
  30. package/dist/public/assets/WorkspaceDetailPage-DOexuuaw.js +1 -0
  31. package/dist/public/assets/WorkspaceHomePage-DkCHNjKD.js +1 -0
  32. package/dist/public/assets/{client-runtime-manager-BZpL17fc.js → client-runtime-manager-6OoYHXGd.js} +1 -1
  33. package/dist/public/assets/{file-tree-icon-Db5LXC8h.js → file-tree-icon-9pt1OStn.js} +1 -1
  34. package/dist/public/assets/index-BwlbvwaA.css +1 -0
  35. package/dist/public/assets/index-DSw-TkQL.js +42 -0
  36. package/dist/public/assets/legna-code-6TqgZ4Ls.png +0 -0
  37. package/dist/public/assets/{login-direct-candidate-resolver-1mxe_Oh8.js → login-direct-candidate-resolver-CLlYtBRq.js} +1 -1
  38. package/dist/public/assets/model-switch-api-C-l8-E1S.js +1 -0
  39. package/dist/public/assets/{preferences-service-DWnzl5a0.js → preferences-service-BCcfYP_d.js} +1 -1
  40. package/dist/public/assets/{relay-entry-C5_Iay0I.js → relay-entry-BmLkMKuq.js} +1 -1
  41. package/dist/public/assets/session-runtime-machine-DgtvREca.js +21 -0
  42. package/dist/public/assets/{terminal-runtime-meta-cdtWVfCm.js → terminal-runtime-meta-0h-75uRy.js} +1 -1
  43. package/dist/public/assets/useRegisteredDebugTemplates-rBVmAqh3.js +1 -0
  44. package/dist/public/index.html +2 -2
  45. package/dist/server/config/env.d.ts +2 -0
  46. package/dist/server/config/env.js +7 -0
  47. package/dist/server/config/env.js.map +1 -1
  48. package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +4 -1
  49. package/dist/server/modules/assistant-capability/assistant-capability-service.js +12 -1
  50. package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
  51. package/dist/server/modules/butler/butler-control-session-service.d.ts +4 -1
  52. package/dist/server/modules/butler/butler-control-session-service.js +17 -1
  53. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  54. package/dist/server/modules/butler/butler-profile-service.d.ts +3 -1
  55. package/dist/server/modules/butler/butler-profile-service.js +16 -4
  56. package/dist/server/modules/butler/butler-profile-service.js.map +1 -1
  57. package/dist/server/modules/model-switch/cc-switch-adapter.d.ts +7 -0
  58. package/dist/server/modules/model-switch/cc-switch-adapter.js +17 -0
  59. package/dist/server/modules/model-switch/cc-switch-adapter.js.map +1 -1
  60. package/dist/server/modules/opencli/opencli-bridge-skill-service.d.ts +12 -0
  61. package/dist/server/modules/opencli/opencli-bridge-skill-service.js +124 -0
  62. package/dist/server/modules/opencli/opencli-bridge-skill-service.js.map +1 -0
  63. package/dist/server/modules/opencli/opencli-catalog-service.d.ts +50 -0
  64. package/dist/server/modules/opencli/opencli-catalog-service.js +345 -0
  65. package/dist/server/modules/opencli/opencli-catalog-service.js.map +1 -0
  66. package/dist/server/modules/opencli/opencli-controller.d.ts +13 -0
  67. package/dist/server/modules/opencli/opencli-controller.js +30 -0
  68. package/dist/server/modules/opencli/opencli-controller.js.map +1 -0
  69. package/dist/server/modules/opencli/opencli-health-service.d.ts +28 -0
  70. package/dist/server/modules/opencli/opencli-health-service.js +106 -0
  71. package/dist/server/modules/opencli/opencli-health-service.js.map +1 -0
  72. package/dist/server/modules/opencli/opencli-install-discovery.d.ts +23 -0
  73. package/dist/server/modules/opencli/opencli-install-discovery.js +130 -0
  74. package/dist/server/modules/opencli/opencli-install-discovery.js.map +1 -0
  75. package/dist/server/modules/opencli/opencli-management-service.d.ts +59 -0
  76. package/dist/server/modules/opencli/opencli-management-service.js +152 -0
  77. package/dist/server/modules/opencli/opencli-management-service.js.map +1 -0
  78. package/dist/server/modules/opencli/opencli-runtime-builder.d.ts +11 -0
  79. package/dist/server/modules/opencli/opencli-runtime-builder.js +214 -0
  80. package/dist/server/modules/opencli/opencli-runtime-builder.js.map +1 -0
  81. package/dist/server/modules/opencli/opencli-runtime-layout.d.ts +3 -0
  82. package/dist/server/modules/opencli/opencli-runtime-layout.js +11 -0
  83. package/dist/server/modules/opencli/opencli-runtime-layout.js.map +1 -0
  84. package/dist/server/modules/opencli/opencli-runtime-profile-service.d.ts +29 -0
  85. package/dist/server/modules/opencli/opencli-runtime-profile-service.js +104 -0
  86. package/dist/server/modules/opencli/opencli-runtime-profile-service.js.map +1 -0
  87. package/dist/server/modules/opencli/opencli-runtime-resolver.d.ts +29 -0
  88. package/dist/server/modules/opencli/opencli-runtime-resolver.js +110 -0
  89. package/dist/server/modules/opencli/opencli-runtime-resolver.js.map +1 -0
  90. package/dist/server/modules/opencli/opencli-session-prompt-service.d.ts +11 -0
  91. package/dist/server/modules/opencli/opencli-session-prompt-service.js +66 -0
  92. package/dist/server/modules/opencli/opencli-session-prompt-service.js.map +1 -0
  93. package/dist/server/modules/parallel-sessions/parallel-session-controller.d.ts +4 -0
  94. package/dist/server/modules/parallel-sessions/parallel-session-controller.js +7 -0
  95. package/dist/server/modules/parallel-sessions/parallel-session-controller.js.map +1 -1
  96. package/dist/server/modules/parallel-sessions/parallel-session-group-service.d.ts +6 -1
  97. package/dist/server/modules/parallel-sessions/parallel-session-group-service.js +36 -2
  98. package/dist/server/modules/parallel-sessions/parallel-session-group-service.js.map +1 -1
  99. package/dist/server/modules/provider/opencode-model-options.d.ts +1 -0
  100. package/dist/server/modules/provider/opencode-model-options.js +54 -12
  101. package/dist/server/modules/provider/opencode-model-options.js.map +1 -1
  102. package/dist/server/modules/provider/provider-catalog-service.d.ts +46 -0
  103. package/dist/server/modules/provider/provider-catalog-service.js +249 -0
  104. package/dist/server/modules/provider/provider-catalog-service.js.map +1 -0
  105. package/dist/server/modules/provider/provider-controller.d.ts +20 -2
  106. package/dist/server/modules/provider/provider-controller.js +65 -5
  107. package/dist/server/modules/provider/provider-controller.js.map +1 -1
  108. package/dist/server/modules/provider/provider-disabled.d.ts +8 -0
  109. package/dist/server/modules/provider/provider-disabled.js +45 -0
  110. package/dist/server/modules/provider/provider-disabled.js.map +1 -0
  111. package/dist/server/modules/provider/provider-discovery-helper-client.d.ts +3 -0
  112. package/dist/server/modules/provider/provider-discovery-helper-client.js.map +1 -1
  113. package/dist/server/modules/provider/provider-discovery-helper-process.js +4 -4
  114. package/dist/server/modules/provider/provider-discovery-helper-process.js.map +1 -1
  115. package/dist/server/modules/provider/provider-discovery-runtime.d.ts +1 -1
  116. package/dist/server/modules/provider/provider-discovery-runtime.js +17 -9
  117. package/dist/server/modules/provider/provider-discovery-runtime.js.map +1 -1
  118. package/dist/server/modules/sessions/claude-compatible-provider-registry.d.ts +16 -0
  119. package/dist/server/modules/sessions/claude-compatible-provider-registry.js +91 -0
  120. package/dist/server/modules/sessions/claude-compatible-provider-registry.js.map +1 -0
  121. package/dist/server/modules/sessions/claude-runtime-helper-client.d.ts +1 -0
  122. package/dist/server/modules/sessions/claude-runtime-helper-client.js +14 -0
  123. package/dist/server/modules/sessions/claude-runtime-helper-client.js.map +1 -1
  124. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +1 -0
  125. package/dist/server/modules/sessions/codex-app-server-helper-client.js +10 -0
  126. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  127. package/dist/server/modules/sessions/provider-session-delete-cli.js +2 -0
  128. package/dist/server/modules/sessions/provider-session-delete-cli.js.map +1 -1
  129. package/dist/server/modules/sessions/session-controller.d.ts +7 -0
  130. package/dist/server/modules/sessions/session-controller.js +22 -0
  131. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  132. package/dist/server/modules/sessions/session-history-service.d.ts +18 -2
  133. package/dist/server/modules/sessions/session-history-service.js +330 -29
  134. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  135. package/dist/server/modules/sessions/session-live-runtime-router-service.js +9 -4
  136. package/dist/server/modules/sessions/session-live-runtime-router-service.js.map +1 -1
  137. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +22 -5
  138. package/dist/server/modules/sessions/session-live-runtime-service.js +350 -122
  139. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  140. package/dist/server/modules/sessions/session-message-attachment-service.js +2 -2
  141. package/dist/server/modules/sessions/session-message-attachment-service.js.map +1 -1
  142. package/dist/server/modules/sessions/session-permission-request-service.d.ts +5 -2
  143. package/dist/server/modules/sessions/session-permission-request-service.js +26 -27
  144. package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
  145. package/dist/server/modules/sessions/session-provider-config-service.d.ts +82 -0
  146. package/dist/server/modules/sessions/session-provider-config-service.js +925 -0
  147. package/dist/server/modules/sessions/session-provider-config-service.js.map +1 -0
  148. package/dist/server/modules/sessions/session-provider-error-mapper.d.ts +2 -0
  149. package/dist/server/modules/sessions/session-provider-error-mapper.js +42 -0
  150. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  151. package/dist/server/modules/skills/skill-manager-service.d.ts +5 -0
  152. package/dist/server/modules/skills/skill-manager-service.js +26 -0
  153. package/dist/server/modules/skills/skill-manager-service.js.map +1 -1
  154. package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +1 -0
  155. package/dist/server/modules/tasks/task-helper-process-handlers.js +1 -1
  156. package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -1
  157. package/dist/server/routes/opencli.d.ts +3 -0
  158. package/dist/server/routes/opencli.js +7 -0
  159. package/dist/server/routes/opencli.js.map +1 -0
  160. package/dist/server/routes/providers.js +4 -2
  161. package/dist/server/routes/providers.js.map +1 -1
  162. package/dist/server/server/create-server.d.ts +8 -0
  163. package/dist/server/server/create-server.js +48 -12
  164. package/dist/server/server/create-server.js.map +1 -1
  165. package/dist/server/storage/repositories/opencli-catalog-entry-repository.d.ts +9 -0
  166. package/dist/server/storage/repositories/opencli-catalog-entry-repository.js +85 -0
  167. package/dist/server/storage/repositories/opencli-catalog-entry-repository.js.map +1 -0
  168. package/dist/server/storage/repositories/opencli-provider-repository.d.ts +8 -0
  169. package/dist/server/storage/repositories/opencli-provider-repository.js +88 -0
  170. package/dist/server/storage/repositories/opencli-provider-repository.js.map +1 -0
  171. package/dist/server/storage/repositories/opencli-runtime-profile-repository.d.ts +11 -0
  172. package/dist/server/storage/repositories/opencli-runtime-profile-repository.js +127 -0
  173. package/dist/server/storage/repositories/opencli-runtime-profile-repository.js.map +1 -0
  174. package/dist/server/storage/repositories/provider-control-repository.d.ts +9 -0
  175. package/dist/server/storage/repositories/provider-control-repository.js +51 -0
  176. package/dist/server/storage/repositories/provider-control-repository.js.map +1 -0
  177. package/dist/server/storage/repositories/session-binding-repository.js +44 -5
  178. package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
  179. package/dist/server/storage/repositories/session-index-repository.js +6 -0
  180. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  181. package/dist/server/storage/sqlite/client.js +72 -0
  182. package/dist/server/storage/sqlite/client.js.map +1 -1
  183. package/dist/server/storage/sqlite/schema.sql +71 -0
  184. package/dist/server/types/domain.d.ts +56 -0
  185. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.d.ts +5 -2
  186. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js +40 -8
  187. package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js.map +1 -1
  188. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +2 -0
  189. package/node_modules/@codingns/session-sync-core/dist/index.js +2 -0
  190. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  191. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +10 -1
  192. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +110 -35
  193. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  194. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.d.ts +11 -0
  195. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +105 -0
  196. package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +1 -0
  197. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +131 -39
  198. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -1
  199. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.d.ts +9 -0
  200. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js +17 -0
  201. package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js.map +1 -0
  202. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.d.ts +8 -1
  203. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js +19 -6
  204. package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js.map +1 -1
  205. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +1 -0
  206. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +13 -8
  207. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  208. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +5 -1
  209. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +103 -51
  210. package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
  211. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +2 -1
  212. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +41 -21
  213. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  214. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +32 -8
  215. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -1
  216. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.d.ts +15 -0
  217. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js +16 -0
  218. package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js.map +1 -0
  219. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +167 -10
  220. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +1 -1
  221. package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +2 -0
  222. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +1 -1
  223. package/node_modules/@codingns/session-sync-core/dist/types.js +1 -1
  224. package/node_modules/@codingns/session-sync-core/dist/types.js.map +1 -1
  225. package/package.json +1 -1
  226. package/dist/public/assets/AdaptiveButlerPage-uFwDdN-F.js +0 -3
  227. package/dist/public/assets/ConversationPage-z3sXtKZ7.js +0 -4
  228. package/dist/public/assets/DesktopWindowPage-CZcoGApB.js +0 -2
  229. package/dist/public/assets/FileContextPanel-C3qex8bb.js +0 -1
  230. package/dist/public/assets/GitSidebar-BK6H16XU.js +0 -6
  231. package/dist/public/assets/MobileCreateSessionSheet-BYfbvK8o.js +0 -1
  232. package/dist/public/assets/MobileSheet-Ckug8hTb.js +0 -1
  233. package/dist/public/assets/MobileWorkspaceSwitcherHeader-RqWrBdn2.js +0 -1
  234. package/dist/public/assets/SessionIndexPage-DuK10DL5.js +0 -1
  235. package/dist/public/assets/SettingsPage-fyD-xaHL.js +0 -1
  236. package/dist/public/assets/TerminalManagerPanel-CCLr1Ypk.js +0 -1
  237. package/dist/public/assets/TerminalRuntimeFallbackModal-aUzjEBwP.js +0 -1
  238. package/dist/public/assets/ToolGitPage-C264yjS9.js +0 -1
  239. package/dist/public/assets/ToolProcessesPage-BOP4A1cb.js +0 -1
  240. package/dist/public/assets/ToolsHomePage-CQxGiKQA.js +0 -1
  241. package/dist/public/assets/WorkbenchLandingPage-CvAY68ca.js +0 -1
  242. package/dist/public/assets/WorkbenchLayout-DGm8Tc5M.js +0 -3
  243. package/dist/public/assets/WorkbenchModal-0tPIIhca.js +0 -1
  244. package/dist/public/assets/WorkbenchShellRoute-BF0nHWOk.css +0 -1
  245. package/dist/public/assets/WorkbenchShellRoute-DBBOsJo9.js +0 -1
  246. package/dist/public/assets/WorkspaceDebugDetailPage-CDerFYd2.js +0 -1
  247. package/dist/public/assets/WorkspaceDetailPage-BlJc1CHE.js +0 -1
  248. package/dist/public/assets/WorkspaceHomePage-BUsKJ3lv.js +0 -1
  249. package/dist/public/assets/default-session-permission-mode-DT4SGiwp.js +0 -1
  250. package/dist/public/assets/index-BZLcEHW3.js +0 -42
  251. package/dist/public/assets/index-BbspQPC2.css +0 -1
  252. package/dist/public/assets/session-runtime-machine-DdLeDqQr.js +0 -17
  253. package/dist/public/assets/useRegisteredDebugTemplates-oFAQNIqh.js +0 -1
@@ -1,17 +1,20 @@
1
- import { existsSync, readdirSync } from "node:fs";
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { performance } from "node:perf_hooks";
4
- import { ClaudeRuntimeAdapter, CodexRuntimeAdapter, GeminiRuntimeAdapter, KimiRuntimeAdapter, OpenCodeRuntimeAdapter, ProviderRuntimeService } from "@codingns/session-sync-core";
4
+ import { ClaudeRuntimeAdapter, CodexRuntimeAdapter, GeminiRuntimeAdapter, KimiRuntimeAdapter, LegnaRuntimeAdapter, OpenCodeRuntimeAdapter, ProviderRuntimeService } from "@codingns/session-sync-core";
5
5
  import { AppError, isAppError } from "../../shared/errors/app-error.js";
6
6
  import { createId } from "../../shared/utils/id.js";
7
7
  import { isPerfDebugEnabled, logPerformance } from "../../shared/utils/perf-log.js";
8
8
  import { logPermissionDebug } from "../../shared/utils/permission-debug-log.js";
9
9
  import { nowIso } from "../../shared/utils/time.js";
10
10
  import { SessionActivityAuthorityService } from "./session-activity-authority-service.js";
11
+ import { createProviderCapabilityBlockedError } from "../provider/provider-disabled.js";
11
12
  import { SessionPermissionRequestService } from "./session-permission-request-service.js";
12
- import { mapSessionProviderError } from "./session-provider-error-mapper.js";
13
+ import { appendSessionProviderErrorContext, mapSessionProviderError } from "./session-provider-error-mapper.js";
14
+ import { buildClaudeCompatibleHookBridgeUrl, buildClaudeCompatibleRawStoreRef, buildClaudeCompatibleSessionTitle, findClaudeCompatibleSessionFile, isClaudeCompatibleProvider } from "./claude-compatible-provider-registry.js";
13
15
  import { ClaudeRuntimeHelperAdapter } from "./claude-runtime-helper-client.js";
14
16
  import { CodexAppServerHelperClient } from "./codex-app-server-helper-client.js";
17
+ const OPENCODE_ORDER_DEBUG_ENABLED = /^(1|true|yes)$/i.test(process.env.CODINGNS_OPENCODE_ORDER_DEBUG?.trim() ?? "");
15
18
  const RUNTIME_START_BINDING_WAIT_TIMEOUT_MS = 10_000;
16
19
  const START_BINDING_POLL_INTERVAL_MS = 50;
17
20
  const EXTERNAL_RUNTIME_INTERRUPT_SUPPRESSION_MS = 30_000;
@@ -27,7 +30,9 @@ export class SessionLiveRuntimeService {
27
30
  sessionIndexRepository;
28
31
  sessionStateRepository;
29
32
  sessionStatusSnapshotRepository;
33
+ sessionProviderConfigService;
30
34
  config;
35
+ openCliSessionPromptService;
31
36
  providerRuntimeService;
32
37
  sessionActivityAuthorityService;
33
38
  sessionPermissionRequestService;
@@ -42,7 +47,7 @@ export class SessionLiveRuntimeService {
42
47
  queueDispatchSessions = new Set();
43
48
  queueRetryTimers = new Map();
44
49
  pendingSendDebugTracesBySessionId = new Map();
45
- constructor(sessionHistoryService, sessionMessageAttachmentService, workspaceService, sessionChangedFileService, sessionBindingRepository, authUserRepository, sessionSendQueueRepository, sessionIndexRepository, sessionStateRepository, sessionStatusSnapshotRepository, config, sessionActivityAuthorityService = new SessionActivityAuthorityService()) {
50
+ constructor(sessionHistoryService, sessionMessageAttachmentService, workspaceService, sessionChangedFileService, sessionBindingRepository, authUserRepository, sessionSendQueueRepository, sessionIndexRepository, sessionStateRepository, sessionStatusSnapshotRepository, sessionProviderConfigService, config, sessionActivityAuthorityService = new SessionActivityAuthorityService(), openCliSessionPromptService = null) {
46
51
  this.sessionHistoryService = sessionHistoryService;
47
52
  this.sessionMessageAttachmentService = sessionMessageAttachmentService;
48
53
  this.workspaceService = workspaceService;
@@ -53,7 +58,9 @@ export class SessionLiveRuntimeService {
53
58
  this.sessionIndexRepository = sessionIndexRepository;
54
59
  this.sessionStateRepository = sessionStateRepository;
55
60
  this.sessionStatusSnapshotRepository = sessionStatusSnapshotRepository;
61
+ this.sessionProviderConfigService = sessionProviderConfigService;
56
62
  this.config = config;
63
+ this.openCliSessionPromptService = openCliSessionPromptService;
57
64
  this.sessionActivityAuthorityService = sessionActivityAuthorityService;
58
65
  this.sessionPermissionRequestService = new SessionPermissionRequestService(sessionHistoryService, sessionBindingRepository, authUserRepository, workspaceService, config, async (envelope) => {
59
66
  await this.emitExternalRuntimeEnvelope(envelope);
@@ -79,12 +86,25 @@ export class SessionLiveRuntimeService {
79
86
  });
80
87
  try {
81
88
  const capabilities = this.sessionHistoryService.getProviderCapabilitiesSnapshot(input.provider);
82
- this.ensurePendingSessionBinding(sessionId, workspace.id, input.provider);
89
+ const providerBinding = this.sessionProviderConfigService.prepareSessionBinding({
90
+ sessionId,
91
+ provider: input.provider,
92
+ providerConfigMode: input.providerConfigMode,
93
+ providerPresetId: input.providerPresetId ?? null
94
+ });
95
+ const providerLaunchContext = this.sessionProviderConfigService.resolveLaunchContext({
96
+ provider: input.provider,
97
+ providerConfigMode: providerBinding.providerConfigMode,
98
+ providerPresetId: providerBinding.providerPresetId,
99
+ runtimeHomeDir: providerBinding.runtimeHomeDir
100
+ });
101
+ this.ensurePendingSessionBinding(sessionId, workspace.id, input.provider, providerBinding);
83
102
  const persistedAttachments = this.persistMessageAttachments(sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
84
103
  const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(input.provider, input.content, persistedAttachments.runtimeAttachments);
104
+ const resolvedProviderPrompt = this.composeProviderPrompt(input.provider, providerPrompt, providerLaunchContext.runtimeEnv);
85
105
  const providerInstructionFilePath = resolveRuntimeInstructionFilePath(input.provider, workspace.path, input.runtimeOptions?.providerInstructionFilePath ?? null);
86
- this.ensureCapability(capabilities.canStartSession, "provider", "provider 不支持 start-live");
87
- this.ensureCapability(capabilities.canSendMessage, "provider", "provider 不支持实时对话");
106
+ this.ensureCapability(capabilities, "provider", "canStartSession", "provider 不支持 start-live");
107
+ this.ensureCapability(capabilities, "provider", "canSendMessage", "provider 不支持实时对话");
88
108
  const launchRuntimeStartedAtMs = performance.now();
89
109
  const handle = await this.launchRuntimeRun({
90
110
  sessionId,
@@ -93,6 +113,8 @@ export class SessionLiveRuntimeService {
93
113
  provider: input.provider,
94
114
  providerSessionId: null,
95
115
  rawStoreRef: null,
116
+ runtimeHomeDir: providerLaunchContext.runtimeHomeDir,
117
+ runtimeEnv: providerLaunchContext.runtimeEnv,
96
118
  sequenceBase: 1,
97
119
  options: {
98
120
  content: input.content,
@@ -100,11 +122,16 @@ export class SessionLiveRuntimeService {
100
122
  model: input.runtimeOptions?.model ?? null,
101
123
  reasoningLevel: input.runtimeOptions?.reasoningLevel ?? null,
102
124
  permissionMode: input.runtimeOptions?.permissionMode ?? null,
103
- providerPrompt,
125
+ providerPrompt: resolvedProviderPrompt,
104
126
  providerInstructionFilePath,
105
127
  attachments: persistedAttachments.runtimeAttachments
106
128
  }
107
- }, "start");
129
+ }, "start", {
130
+ provider: input.provider,
131
+ providerConfigMode: providerBinding.providerConfigMode,
132
+ providerPresetId: providerBinding.providerPresetId,
133
+ runtimeHomeDir: providerBinding.runtimeHomeDir
134
+ });
108
135
  this.logSendDebugStep(debugTrace, "launch_runtime", launchRuntimeStartedAtMs, {
109
136
  userId: input.userId
110
137
  });
@@ -149,7 +176,7 @@ export class SessionLiveRuntimeService {
149
176
  if (!shouldAwaitStartBindingBeforeAcceptedUserLookup(input.provider)) {
150
177
  void startBindingTask;
151
178
  }
152
- const acceptedAt = acceptedMessage?.timestamp ?? nowIso();
179
+ const acceptedAt = acceptedMessage?.timestamp ?? requestStartedAt;
153
180
  const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
154
181
  const session = this.resolveStartedSession({
155
182
  sessionId,
@@ -203,6 +230,7 @@ export class SessionLiveRuntimeService {
203
230
  }
204
231
  async enqueueLiveMessage(input) {
205
232
  const session = await this.resolveQueueDispatchSession(input.sessionId, input.userId);
233
+ this.resolveEffectiveSessionProviderBinding(session, input);
206
234
  this.persistMessageAttachments(input.sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
207
235
  const timestamp = nowIso();
208
236
  const queueItem = {
@@ -330,16 +358,25 @@ export class SessionLiveRuntimeService {
330
358
  deleted: true
331
359
  };
332
360
  }
333
- getClaudeHookBridgeConfig() {
334
- return buildClaudeHookBridgeConfig(this.config);
335
- }
336
- async ingestClaudeHookEvent(payload) {
337
- const hookEventName = normalizeClaudeHookEventName(payload.hook_event_name);
361
+ getClaudeHookBridgeConfig(provider = "claude-code") {
362
+ return buildClaudeHookBridgeConfig(this.config, provider);
363
+ }
364
+ async ingestClaudeHookEvent(providerOrPayload, payload) {
365
+ const hasExplicitProvider = typeof providerOrPayload === "string"
366
+ && isClaudeCompatibleProvider(providerOrPayload);
367
+ const provider = hasExplicitProvider
368
+ ? providerOrPayload
369
+ : "claude-code";
370
+ const resolvedPayload = hasExplicitProvider
371
+ ? (payload ?? {})
372
+ : providerOrPayload;
373
+ const hookEventName = normalizeClaudeHookEventName(resolvedPayload.hook_event_name);
338
374
  logPermissionDebug("claude_hook_event.ingest.begin", {
375
+ provider,
339
376
  hookEventName,
340
- sessionId: payload.session_id ?? null,
341
- cwd: payload.cwd ?? null,
342
- transcriptPath: payload.transcript_path ?? null
377
+ sessionId: resolvedPayload.session_id ?? null,
378
+ cwd: resolvedPayload.cwd ?? null,
379
+ transcriptPath: resolvedPayload.transcript_path ?? null
343
380
  });
344
381
  if (!hookEventName) {
345
382
  throw new AppError({
@@ -365,23 +402,23 @@ export class SessionLiveRuntimeService {
365
402
  hookEventName,
366
403
  route: "handleClaudePreToolUse"
367
404
  });
368
- return this.sessionPermissionRequestService.handleClaudePreToolUse(payload);
405
+ return this.sessionPermissionRequestService.handleClaudePreToolUse(resolvedPayload, provider);
369
406
  }
370
407
  if (hookEventName === "PermissionRequest") {
371
408
  logPermissionDebug("claude_hook_event.route", {
372
409
  hookEventName,
373
410
  route: "handleClaudePermissionRequest"
374
411
  });
375
- return this.sessionPermissionRequestService.handleClaudePermissionRequest(payload);
412
+ return this.sessionPermissionRequestService.handleClaudePermissionRequest(resolvedPayload, provider);
376
413
  }
377
- const providerSessionId = normalizeRequiredText(payload.session_id, "session_id");
378
- const workspacePath = normalizeRequiredText(payload.cwd, "cwd");
414
+ const providerSessionId = normalizeRequiredText(resolvedPayload.session_id, "session_id");
415
+ const workspacePath = normalizeRequiredText(resolvedPayload.cwd, "cwd");
379
416
  const workspace = this.workspaceService.findWorkspaceByPath(workspacePath);
380
417
  if (!workspace) {
381
418
  logPermissionDebug("claude_hook_event.workspace_not_found", {
382
419
  hookEventName,
383
- sessionId: payload.session_id ?? null,
384
- cwd: payload.cwd ?? null
420
+ sessionId: resolvedPayload.session_id ?? null,
421
+ cwd: resolvedPayload.cwd ?? null
385
422
  });
386
423
  return {
387
424
  accepted: true,
@@ -391,13 +428,14 @@ export class SessionLiveRuntimeService {
391
428
  };
392
429
  }
393
430
  const binding = await this.resolveClaudeExternalBinding({
431
+ provider,
394
432
  providerSessionId,
395
433
  workspaceId: workspace.id,
396
434
  workspacePath: workspace.path,
397
- transcriptPath: normalizeOptionalText(payload.transcript_path)
435
+ transcriptPath: normalizeOptionalText(resolvedPayload.transcript_path)
398
436
  });
399
437
  const timestamp = nowIso();
400
- const runtimeUpdate = mapClaudeHookToRuntimeUpdate(hookEventName, payload, timestamp);
438
+ const runtimeUpdate = mapClaudeHookToRuntimeUpdate(hookEventName, resolvedPayload, timestamp);
401
439
  logPermissionDebug("claude_hook_event.runtime_update", {
402
440
  hookEventName,
403
441
  sessionId: binding.sessionId,
@@ -422,6 +460,7 @@ export class SessionLiveRuntimeService {
422
460
  };
423
461
  }
424
462
  await this.applyExternalRuntimeUpdate({
463
+ provider,
425
464
  sessionId: binding.sessionId,
426
465
  workspaceId: workspace.id,
427
466
  providerSessionId,
@@ -483,7 +522,7 @@ export class SessionLiveRuntimeService {
483
522
  if (externalRuntimeSnapshot) {
484
523
  return {
485
524
  sessionId,
486
- provider: "claude-code",
525
+ provider: externalRuntimeSnapshot.provider,
487
526
  providerSessionId: externalRuntimeSnapshot.providerSessionId,
488
527
  runningState: resolution.runningState,
489
528
  hasActiveRun: externalHasActiveRun,
@@ -649,6 +688,16 @@ export class SessionLiveRuntimeService {
649
688
  if (!envelope) {
650
689
  return;
651
690
  }
691
+ logOpenCodeOrderEnvelopeDebug("runtime.envelope.forward", {
692
+ sessionId,
693
+ runtimeSessionId,
694
+ provider: event.provider ?? null,
695
+ eventType: event.type ?? null,
696
+ envelopeType: envelope.type,
697
+ message: "message" in envelope && envelope.message
698
+ ? summarizeOpenCodeOrderMessage(envelope.message)
699
+ : null
700
+ });
652
701
  await onEnvelope(envelope);
653
702
  });
654
703
  const externalSubscription = this.subscribeExternalRuntime(runtimeSessionId, async (envelope) => {
@@ -867,10 +916,10 @@ export class SessionLiveRuntimeService {
867
916
  }
868
917
  async resolveClaudeExternalBinding(input) {
869
918
  const rawStoreRef = input.transcriptPath ??
870
- findClaudeSessionFile(this.config.claudeCodeHomeDir, input.providerSessionId) ??
871
- buildClaudeRawStoreRef(this.config.claudeCodeHomeDir, input.workspacePath, input.providerSessionId);
872
- let binding = this.sessionBindingRepository.findByProviderSession("claude-code", input.providerSessionId) ??
873
- this.sessionBindingRepository.findByRawStoreRef("claude-code", rawStoreRef);
919
+ findClaudeCompatibleSessionFile(this.config, input.provider, input.workspacePath, input.providerSessionId) ??
920
+ buildClaudeCompatibleRawStoreRef(this.config, input.provider, input.workspacePath, input.providerSessionId);
921
+ let binding = this.sessionBindingRepository.findByProviderSession(input.provider, input.providerSessionId) ??
922
+ this.sessionBindingRepository.findByRawStoreRef(input.provider, rawStoreRef);
874
923
  if (!binding) {
875
924
  const userIds = this.authUserRepository.listIds();
876
925
  const bootstrapUserId = userIds[0] ?? null;
@@ -883,8 +932,8 @@ export class SessionLiveRuntimeService {
883
932
  });
884
933
  }
885
934
  binding =
886
- this.sessionBindingRepository.findByProviderSession("claude-code", input.providerSessionId) ??
887
- this.sessionBindingRepository.findByRawStoreRef("claude-code", rawStoreRef);
935
+ this.sessionBindingRepository.findByProviderSession(input.provider, input.providerSessionId) ??
936
+ this.sessionBindingRepository.findByRawStoreRef(input.provider, rawStoreRef);
888
937
  }
889
938
  if (binding) {
890
939
  return {
@@ -895,19 +944,19 @@ export class SessionLiveRuntimeService {
895
944
  const sessionId = createId();
896
945
  const timestamp = nowIso();
897
946
  this.sessionHistoryService.persistSessionBinding(sessionId, input.workspaceId, {
898
- provider: "claude-code",
947
+ provider: input.provider,
899
948
  providerSessionId: input.providerSessionId,
900
949
  rawStoreRef
901
950
  });
902
951
  this.sessionIndexRepository.upsert({
903
952
  sessionId,
904
953
  workspaceId: input.workspaceId,
905
- provider: "claude-code",
954
+ provider: input.provider,
906
955
  parentSessionId: null,
907
956
  sessionKind: "default",
908
957
  isSubagent: false,
909
958
  subagentLabel: null,
910
- title: `Claude 会话 ${input.providerSessionId.slice(0, 8)}`,
959
+ title: buildClaudeCompatibleSessionTitle(input.provider, input.providerSessionId),
911
960
  messageCount: 0,
912
961
  isArchived: false,
913
962
  lastMessageAt: null,
@@ -988,7 +1037,7 @@ export class SessionLiveRuntimeService {
988
1037
  if (input.runningState === "running") {
989
1038
  this.externalRuntimeSnapshots.set(input.sessionId, {
990
1039
  sessionId: input.sessionId,
991
- provider: "claude-code",
1040
+ provider: input.provider,
992
1041
  providerSessionId: input.providerSessionId,
993
1042
  rawStoreRef: input.rawStoreRef,
994
1043
  runningState: input.runningState,
@@ -1027,14 +1076,14 @@ export class SessionLiveRuntimeService {
1027
1076
  void this.dispatchNextQueuedMessage(input.sessionId);
1028
1077
  }
1029
1078
  }
1030
- async startRuntimeRun(request, userId, mode) {
1079
+ async startRuntimeRun(request, userId, mode, providerBinding) {
1031
1080
  this.runtimeMessageSeenSessions.delete(request.sessionId);
1032
1081
  this.runtimeHistoryFallbackSentSessions.delete(request.sessionId);
1033
1082
  this.clearExternalRuntimeInterruptSuppression(request.sessionId);
1034
- if (request.provider === "claude-code") {
1083
+ if (isClaudeCompatibleProvider(request.provider)) {
1035
1084
  this.clearExternalRuntimeSnapshot(request.sessionId);
1036
1085
  }
1037
- const handle = await this.launchRuntimeRun(request, mode);
1086
+ const handle = await this.launchRuntimeRun(request, mode, providerBinding);
1038
1087
  const snapshot = handle.getSnapshot();
1039
1088
  const currentState = this.sessionStateRepository.findBySessionAndUser(request.sessionId, userId);
1040
1089
  this.attachRuntimePersistence(handle, request.sessionId, request.workspaceId, userId);
@@ -1066,24 +1115,61 @@ export class SessionLiveRuntimeService {
1066
1115
  const capabilities = await this.sessionHistoryService.getSessionCapabilities(input.sessionId);
1067
1116
  const workspace = this.workspaceService.getWorkspaceOrThrow(session.workspaceId);
1068
1117
  const runtimeMode = shouldStartNativeSessionOnFirstMessage(session);
1118
+ const existingBinding = this.getSessionBindingOrThrow(session.sessionId);
1119
+ const resolvedProviderBinding = this.resolveRequestedSessionProviderBinding(session, input, existingBinding);
1120
+ const runtimeSessionId = this.resolveRuntimeSessionId(input.sessionId);
1121
+ const activeRun = this.getLiveRuntimeSnapshot(runtimeSessionId);
1122
+ const externalRuntimeSnapshot = this.getFreshExternalRuntimeSnapshot(runtimeSessionId);
1123
+ const hasActiveRun = Boolean(activeRun && isActiveRuntimeState(activeRun.runningState));
1124
+ if (hasActiveRun && isClaudeCompatibleProvider(activeRun?.provider)) {
1125
+ this.clearExternalRuntimeSnapshot(runtimeSessionId);
1126
+ }
1127
+ if (hasActiveRun
1128
+ || (!activeRun &&
1129
+ isClaudeCompatibleProvider(session.provider) &&
1130
+ externalRuntimeSnapshot &&
1131
+ isActiveRuntimeState(externalRuntimeSnapshot.runningState))) {
1132
+ this.assertProviderBindingStableDuringActiveRun(existingBinding, resolvedProviderBinding);
1133
+ }
1134
+ if (!activeRun &&
1135
+ isClaudeCompatibleProvider(session.provider) &&
1136
+ externalRuntimeSnapshot &&
1137
+ isActiveRuntimeState(externalRuntimeSnapshot.runningState)) {
1138
+ throw new AppError({
1139
+ statusCode: 409,
1140
+ errorCode: "SESSION_EXTERNAL_RUN_ACTIVE",
1141
+ detail: "当前 Claude 外部会话仍在运行,不能直接追加;请加入队列或等待当前轮结束",
1142
+ field: "sessionId"
1143
+ });
1144
+ }
1145
+ const providerBinding = hasActiveRun
1146
+ ? existingBinding
1147
+ : this.persistResolvedSessionProviderBinding(existingBinding, resolvedProviderBinding);
1148
+ const providerLaunchContext = this.sessionProviderConfigService.resolveLaunchContext(providerBinding);
1069
1149
  const syntheticForkRawStoreRef = runtimeMode === "start" && shouldResumeCodexSyntheticForkSession(session)
1070
1150
  ? session.rawStoreRef
1071
1151
  : null;
1152
+ const runtimeRawStoreRef = runtimeMode === "start"
1153
+ ? syntheticForkRawStoreRef
1154
+ : await this.resolveCodexRuntimeRequestRawStoreRef(session, providerBinding);
1072
1155
  const nextUserSequence = runtimeMode === "start"
1073
1156
  ? 1
1074
1157
  : await this.resolveNextUserSequence(input.sessionId, session.messageCount);
1075
1158
  const resolvedAttachments = persistedAttachments
1076
1159
  ?? this.persistMessageAttachments(input.sessionId, input.clientRequestId, input.runtimeOptions?.attachments ?? []);
1077
1160
  const providerPrompt = this.sessionMessageAttachmentService.buildProviderPrompt(session.provider, input.content, resolvedAttachments.runtimeAttachments);
1161
+ const resolvedProviderPrompt = this.composeProviderPrompt(session.provider, providerPrompt, providerLaunchContext.runtimeEnv);
1078
1162
  const providerInstructionFilePath = resolveRuntimeInstructionFilePath(session.provider, workspace.path, input.runtimeOptions?.providerInstructionFilePath ?? null);
1079
- this.ensureCapability(capabilities.canSendMessage, "sessionId", "provider 不支持实时对话");
1163
+ this.ensureCapability(capabilities, "sessionId", "canSendMessage", "provider 不支持实时对话");
1080
1164
  const runtimeRequest = {
1081
1165
  sessionId: input.sessionId,
1082
1166
  workspaceId: session.workspaceId,
1083
1167
  workspacePath: workspace.path,
1084
1168
  provider: session.provider,
1085
1169
  providerSessionId: runtimeMode === "start" ? null : session.providerSessionId,
1086
- rawStoreRef: runtimeMode === "start" ? syntheticForkRawStoreRef : session.rawStoreRef,
1170
+ rawStoreRef: runtimeRawStoreRef,
1171
+ runtimeHomeDir: providerLaunchContext.runtimeHomeDir,
1172
+ runtimeEnv: providerLaunchContext.runtimeEnv,
1087
1173
  sequenceBase: nextUserSequence,
1088
1174
  options: {
1089
1175
  content: input.content,
@@ -1091,31 +1177,12 @@ export class SessionLiveRuntimeService {
1091
1177
  model: input.runtimeOptions?.model ?? null,
1092
1178
  reasoningLevel: input.runtimeOptions?.reasoningLevel ?? null,
1093
1179
  permissionMode: input.runtimeOptions?.permissionMode ?? null,
1094
- providerPrompt,
1180
+ providerPrompt: resolvedProviderPrompt,
1095
1181
  providerInstructionFilePath,
1096
1182
  attachments: resolvedAttachments.runtimeAttachments
1097
1183
  }
1098
1184
  };
1099
- const runtimeSessionId = this.resolveRuntimeSessionId(input.sessionId);
1100
- const activeRun = this.getLiveRuntimeSnapshot(runtimeSessionId);
1101
- const externalRuntimeSnapshot = this.getFreshExternalRuntimeSnapshot(runtimeSessionId);
1102
- if (activeRun &&
1103
- activeRun.provider === "claude-code" &&
1104
- isActiveRuntimeState(activeRun.runningState)) {
1105
- this.clearExternalRuntimeSnapshot(runtimeSessionId);
1106
- }
1107
- if (!activeRun &&
1108
- session.provider === "claude-code" &&
1109
- externalRuntimeSnapshot &&
1110
- isActiveRuntimeState(externalRuntimeSnapshot.runningState)) {
1111
- throw new AppError({
1112
- statusCode: 409,
1113
- errorCode: "SESSION_EXTERNAL_RUN_ACTIVE",
1114
- detail: "当前 Claude 外部会话仍在运行,不能直接追加;请加入队列或等待当前轮结束",
1115
- field: "sessionId"
1116
- });
1117
- }
1118
- if (activeRun && isActiveRuntimeState(activeRun.runningState)) {
1185
+ if (hasActiveRun) {
1119
1186
  const submitStartedAtMs = performance.now();
1120
1187
  try {
1121
1188
  await this.providerRuntimeService.submitToActiveRun(runtimeSessionId, runtimeRequest.options);
@@ -1125,13 +1192,23 @@ export class SessionLiveRuntimeService {
1125
1192
  });
1126
1193
  }
1127
1194
  catch (error) {
1128
- const mapped = mapSessionProviderError(error);
1195
+ const mapped = appendSessionProviderErrorContext(mapSessionProviderError(error), this.sessionProviderConfigService.describeBinding({
1196
+ provider: session.provider,
1197
+ providerConfigMode: providerBinding.providerConfigMode,
1198
+ providerPresetId: providerBinding.providerPresetId,
1199
+ runtimeHomeDir: providerBinding.runtimeHomeDir
1200
+ }));
1129
1201
  // 运行时句柄还没来得及收尾时,steer 可能会撞上 provider 已终态。
1130
1202
  // 这里直接失败只会把一条正常消息变成偶发 409,属于纯粹的坏味道。
1131
1203
  if (mapped.errorCode === "SESSION_NOT_RUNNING") {
1132
1204
  await this.providerRuntimeService.abandonRun(runtimeSessionId);
1133
1205
  const restartRuntimeStartedAtMs = performance.now();
1134
- await this.startRuntimeRun(runtimeRequest, input.userId, runtimeMode);
1206
+ await this.startRuntimeRun(runtimeRequest, input.userId, runtimeMode, {
1207
+ provider: session.provider,
1208
+ providerConfigMode: providerBinding.providerConfigMode,
1209
+ providerPresetId: providerBinding.providerPresetId,
1210
+ runtimeHomeDir: providerBinding.runtimeHomeDir
1211
+ });
1135
1212
  this.logSendDebugStep(debugTrace, "restart_runtime_after_stale_active_run", restartRuntimeStartedAtMs, {
1136
1213
  runtimeMode
1137
1214
  });
@@ -1143,7 +1220,12 @@ export class SessionLiveRuntimeService {
1143
1220
  }
1144
1221
  else {
1145
1222
  const startRuntimeStartedAtMs = performance.now();
1146
- await this.startRuntimeRun(runtimeRequest, input.userId, runtimeMode);
1223
+ await this.startRuntimeRun(runtimeRequest, input.userId, runtimeMode, {
1224
+ provider: session.provider,
1225
+ providerConfigMode: providerBinding.providerConfigMode,
1226
+ providerPresetId: providerBinding.providerPresetId,
1227
+ runtimeHomeDir: providerBinding.runtimeHomeDir
1228
+ });
1147
1229
  this.logSendDebugStep(debugTrace, "start_runtime_run", startRuntimeStartedAtMs, {
1148
1230
  runtimeMode
1149
1231
  });
@@ -1154,7 +1236,7 @@ export class SessionLiveRuntimeService {
1154
1236
  this.logSendDebugStep(debugTrace, "accepted_user_lookup", acceptedLookupStartedAtMs, {
1155
1237
  matched: Boolean(acceptedMessage)
1156
1238
  });
1157
- const acceptedAt = acceptedMessage?.timestamp ?? nowIso();
1239
+ const acceptedAt = acceptedMessage?.timestamp ?? requestStartedAt;
1158
1240
  this.sessionHistoryService.resolveMessageOriginByClientRequestId(input.sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null, acceptedAt);
1159
1241
  const boundAttachments = this.sessionMessageAttachmentService.bindClientRequestToMessage(input.sessionId, input.clientRequestId, acceptedMessage?.messageId ?? null);
1160
1242
  this.refreshSyntheticSessionTitle(session, input.content, input.userId);
@@ -1186,6 +1268,20 @@ export class SessionLiveRuntimeService {
1186
1268
  throw error;
1187
1269
  }
1188
1270
  }
1271
+ composeProviderPrompt(provider, basePrompt, runtimeEnv) {
1272
+ const openCliPrompt = this.openCliSessionPromptService?.buildPrompt({
1273
+ provider,
1274
+ runtimeEnv
1275
+ }) ?? null;
1276
+ if (!openCliPrompt) {
1277
+ return basePrompt;
1278
+ }
1279
+ const normalizedBasePrompt = basePrompt?.trim() ?? "";
1280
+ if (!normalizedBasePrompt) {
1281
+ return openCliPrompt;
1282
+ }
1283
+ return `${normalizedBasePrompt}\n\n${openCliPrompt}`;
1284
+ }
1189
1285
  async dispatchNextQueuedMessage(sessionId) {
1190
1286
  if (this.queueDispatchSessions.has(sessionId)) {
1191
1287
  return;
@@ -1263,7 +1359,7 @@ export class SessionLiveRuntimeService {
1263
1359
  if (externalRuntimeSnapshot && isActiveRuntimeState(externalRuntimeSnapshot.runningState)) {
1264
1360
  return true;
1265
1361
  }
1266
- if (session.provider === "claude-code" && isPendingSessionRunningState(session.runningState)) {
1362
+ if (isClaudeCompatibleProvider(session.provider) && isPendingSessionRunningState(session.runningState)) {
1267
1363
  return true;
1268
1364
  }
1269
1365
  return false;
@@ -1291,7 +1387,7 @@ export class SessionLiveRuntimeService {
1291
1387
  }
1292
1388
  async resolveQueueDispatchSession(sessionId, userId) {
1293
1389
  const session = this.sessionHistoryService.getSession(sessionId, userId);
1294
- if (session.provider !== "claude-code"
1390
+ if (!isClaudeCompatibleProvider(session.provider)
1295
1391
  || !isPendingSessionRunningState(session.runningState)) {
1296
1392
  return session;
1297
1393
  }
@@ -1305,14 +1401,18 @@ export class SessionLiveRuntimeService {
1305
1401
  .then((refreshedSession) => refreshedSession ?? session)
1306
1402
  .catch(() => session);
1307
1403
  }
1308
- async launchRuntimeRun(request, mode) {
1404
+ async launchRuntimeRun(request, mode, providerBinding) {
1309
1405
  try {
1310
1406
  return await (mode === "start"
1311
1407
  ? this.providerRuntimeService.startSession(request)
1312
1408
  : this.providerRuntimeService.continueSession(request));
1313
1409
  }
1314
1410
  catch (error) {
1315
- throw mapSessionProviderError(error);
1411
+ const mapped = mapSessionProviderError(error);
1412
+ if (!providerBinding) {
1413
+ throw mapped;
1414
+ }
1415
+ throw appendSessionProviderErrorContext(mapped, this.sessionProviderConfigService.describeBinding(providerBinding));
1316
1416
  }
1317
1417
  }
1318
1418
  attachRuntimePersistence(handle, sessionId, workspaceId, userId) {
@@ -1390,8 +1490,109 @@ export class SessionLiveRuntimeService {
1390
1490
  });
1391
1491
  this.sessionActivityAuthorityService.observe(createRuntimeActivityObservation(input.sessionId, input.snapshot));
1392
1492
  }
1393
- ensurePendingSessionBinding(sessionId, workspaceId, provider) {
1394
- this.sessionHistoryService.persistSessionBinding(sessionId, workspaceId, this.buildBindingSnapshot(sessionId, provider, null, null));
1493
+ ensurePendingSessionBinding(sessionId, workspaceId, provider, providerBinding) {
1494
+ const snapshot = this.buildBindingSnapshot(sessionId, provider, null, null);
1495
+ const timestamp = nowIso();
1496
+ const existingBinding = this.sessionBindingRepository.findBySessionId(sessionId);
1497
+ this.sessionBindingRepository.upsert({
1498
+ sessionId,
1499
+ workspaceId,
1500
+ provider: snapshot.provider,
1501
+ providerSessionId: snapshot.providerSessionId,
1502
+ rawStoreRef: snapshot.rawStoreRef,
1503
+ providerConfigMode: providerBinding?.providerConfigMode ?? existingBinding?.providerConfigMode ?? "global-default",
1504
+ providerPresetId: providerBinding?.providerPresetId ?? existingBinding?.providerPresetId ?? null,
1505
+ runtimeHomeDir: providerBinding?.runtimeHomeDir ?? existingBinding?.runtimeHomeDir ?? null,
1506
+ createdAt: existingBinding?.createdAt ?? timestamp,
1507
+ updatedAt: timestamp
1508
+ });
1509
+ }
1510
+ resolveEffectiveSessionProviderBinding(session, input) {
1511
+ const existingBinding = this.getSessionBindingOrThrow(session.sessionId);
1512
+ const providerBinding = this.resolveRequestedSessionProviderBinding(session, input, existingBinding);
1513
+ return this.persistResolvedSessionProviderBinding(existingBinding, providerBinding);
1514
+ }
1515
+ getSessionBindingOrThrow(sessionId) {
1516
+ const existingBinding = this.sessionBindingRepository.findBySessionId(sessionId);
1517
+ if (!existingBinding) {
1518
+ throw new AppError({
1519
+ statusCode: 404,
1520
+ errorCode: "SESSION_NOT_FOUND",
1521
+ detail: "session 不存在"
1522
+ });
1523
+ }
1524
+ return existingBinding;
1525
+ }
1526
+ resolveRequestedSessionProviderBinding(session, input, existingBinding) {
1527
+ return this.sessionProviderConfigService.resolveSessionBinding({
1528
+ sessionId: session.sessionId,
1529
+ provider: session.provider,
1530
+ existingBinding,
1531
+ providerConfigMode: input.providerConfigMode,
1532
+ providerPresetId: input.providerPresetId ?? null
1533
+ });
1534
+ }
1535
+ persistResolvedSessionProviderBinding(existingBinding, providerBinding) {
1536
+ if (!this.hasSessionProviderBindingChanged(existingBinding, providerBinding)) {
1537
+ return existingBinding;
1538
+ }
1539
+ const nextBinding = {
1540
+ ...existingBinding,
1541
+ providerConfigMode: providerBinding.providerConfigMode,
1542
+ providerPresetId: providerBinding.providerPresetId,
1543
+ runtimeHomeDir: providerBinding.runtimeHomeDir,
1544
+ updatedAt: nowIso()
1545
+ };
1546
+ this.sessionBindingRepository.upsert(nextBinding);
1547
+ return nextBinding;
1548
+ }
1549
+ assertProviderBindingStableDuringActiveRun(existingBinding, requestedBinding) {
1550
+ if (!this.hasSessionProviderBindingChanged(existingBinding, requestedBinding)) {
1551
+ return;
1552
+ }
1553
+ throw new AppError({
1554
+ statusCode: 409,
1555
+ errorCode: "SESSION_PROVIDER_CONFIG_CHANGE_REQUIRES_NEW_RUN",
1556
+ detail: "当前会话仍在执行,不能中途切换模型配置文件;请等本轮结束后再发起新一轮",
1557
+ field: "sessionId"
1558
+ });
1559
+ }
1560
+ hasSessionProviderBindingChanged(currentBinding, nextBinding) {
1561
+ return (currentBinding.providerConfigMode !== nextBinding.providerConfigMode
1562
+ || normalizeOptionalBindingValue(currentBinding.providerPresetId)
1563
+ !== normalizeOptionalBindingValue(nextBinding.providerPresetId)
1564
+ || normalizeOptionalBindingValue(currentBinding.runtimeHomeDir)
1565
+ !== normalizeOptionalBindingValue(nextBinding.runtimeHomeDir));
1566
+ }
1567
+ async resolveCodexRuntimeRequestRawStoreRef(session, providerBinding) {
1568
+ if (session.provider !== "codex") {
1569
+ return session.rawStoreRef;
1570
+ }
1571
+ const currentRawStoreRef = session.rawStoreRef?.trim() || null;
1572
+ if (currentRawStoreRef && existsSync(currentRawStoreRef)) {
1573
+ return currentRawStoreRef;
1574
+ }
1575
+ const messages = await Promise.resolve(this.sessionHistoryService.readAllTextHistoryMessages(session.sessionId)).catch(() => []);
1576
+ if (!Array.isArray(messages) || messages.length === 0) {
1577
+ return currentRawStoreRef;
1578
+ }
1579
+ const baseDir = providerBinding.runtimeHomeDir?.trim()
1580
+ || path.resolve(path.dirname(this.config.databasePath), "runtime", "codex-resume-history");
1581
+ const syntheticDir = path.join(baseDir, ".codingns-synthetic-resume");
1582
+ const syntheticFilePath = path.join(syntheticDir, `${session.sessionId}.jsonl`);
1583
+ const serialized = messages
1584
+ .map((message) => JSON.stringify({
1585
+ timestamp: message.timestamp,
1586
+ type: "event_msg",
1587
+ payload: {
1588
+ type: message.role === "assistant" ? "agent_message" : "user_message",
1589
+ message: message.content
1590
+ }
1591
+ }))
1592
+ .join("\n");
1593
+ mkdirSync(syntheticDir, { recursive: true });
1594
+ writeFileSync(syntheticFilePath, `${serialized}\n`, "utf8");
1595
+ return syntheticFilePath;
1395
1596
  }
1396
1597
  buildBindingSnapshot(sessionId, provider, providerSessionId, rawStoreRef) {
1397
1598
  const pendingValue = `pending://${provider}/${sessionId}`;
@@ -1762,7 +1963,7 @@ export class SessionLiveRuntimeService {
1762
1963
  }
1763
1964
  }
1764
1965
  async resolveNextUserSequence(sessionId, messageCount) {
1765
- let maxSequence = Math.max(messageCount, 0);
1966
+ let maxSequence = 0;
1766
1967
  const envelope = await Promise.resolve(this.sessionHistoryService.readRecentHistoryEnvelope(sessionId, 10)).catch(() => {
1767
1968
  return null;
1768
1969
  });
@@ -1771,6 +1972,9 @@ export class SessionLiveRuntimeService {
1771
1972
  maxSequence = message.sequence;
1772
1973
  }
1773
1974
  }
1975
+ if (maxSequence <= 0) {
1976
+ maxSequence = Math.max(messageCount, 0);
1977
+ }
1774
1978
  return Math.max(maxSequence + 1, 1);
1775
1979
  }
1776
1980
  async waitForResolvedStartBinding(sessionId, workspaceId, provider, handle) {
@@ -1859,7 +2063,7 @@ export class SessionLiveRuntimeService {
1859
2063
  };
1860
2064
  }
1861
2065
  async maybeEmitRuntimeHistoryFallback(sessionId, event) {
1862
- if (event.provider !== "claude-code") {
2066
+ if (!isClaudeCompatibleProvider(event.provider)) {
1863
2067
  return;
1864
2068
  }
1865
2069
  if (event.status === "starting") {
@@ -1880,16 +2084,11 @@ export class SessionLiveRuntimeService {
1880
2084
  this.runtimeHistoryFallbackSentSessions.add(sessionId);
1881
2085
  await this.emitExternalRuntimeEnvelope(envelope);
1882
2086
  }
1883
- ensureCapability(enabled, field, detail) {
1884
- if (enabled) {
2087
+ ensureCapability(capabilities, field, capability, detail) {
2088
+ if (capabilities[capability]) {
1885
2089
  return;
1886
2090
  }
1887
- throw new AppError({
1888
- statusCode: 400,
1889
- errorCode: "CAPABILITY_NOT_SUPPORTED",
1890
- detail,
1891
- field
1892
- });
2091
+ throw createProviderCapabilityBlockedError(capabilities, field, detail);
1893
2092
  }
1894
2093
  upsertSnapshot(sessionId, input) {
1895
2094
  this.sessionStatusSnapshotRepository.upsert({
@@ -1901,7 +2100,7 @@ export class SessionLiveRuntimeService {
1901
2100
  shouldIgnoreClaudeExternalRuntimeUpdate(sessionId) {
1902
2101
  const runtimeSnapshot = this.getLiveRuntimeSnapshot(sessionId);
1903
2102
  return Boolean(runtimeSnapshot &&
1904
- runtimeSnapshot.provider === "claude-code" &&
2103
+ isClaudeCompatibleProvider(runtimeSnapshot.provider) &&
1905
2104
  isActiveRuntimeState(runtimeSnapshot.runningState));
1906
2105
  }
1907
2106
  clearExternalRuntimeSnapshot(sessionId) {
@@ -1943,7 +2142,7 @@ export class SessionLiveRuntimeService {
1943
2142
  async resolveActiveClaudePermissionSession(input) {
1944
2143
  const activeSnapshots = this.providerRuntimeService
1945
2144
  .listSnapshots()
1946
- .filter((snapshot) => snapshot.provider === "claude-code" &&
2145
+ .filter((snapshot) => snapshot.provider === input.provider &&
1947
2146
  snapshot.workspaceId === input.workspaceId &&
1948
2147
  isActiveRuntimeState(snapshot.runningState));
1949
2148
  if (activeSnapshots.length !== 1) {
@@ -1955,9 +2154,9 @@ export class SessionLiveRuntimeService {
1955
2154
  }
1956
2155
  const rawStoreRef = input.transcriptPath ??
1957
2156
  activeSnapshot.rawStoreRef ??
1958
- buildClaudeRawStoreRef(this.config.claudeCodeHomeDir, input.workspacePath, input.providerSessionId);
2157
+ buildClaudeCompatibleRawStoreRef(this.config, input.provider, input.workspacePath, input.providerSessionId);
1959
2158
  this.sessionHistoryService.persistSessionBinding(activeSnapshot.sessionId, input.workspaceId, {
1960
- provider: "claude-code",
2159
+ provider: input.provider,
1961
2160
  providerSessionId: input.providerSessionId,
1962
2161
  rawStoreRef
1963
2162
  });
@@ -2272,7 +2471,8 @@ function waitForAcceptedUserLookupWindow() {
2272
2471
  });
2273
2472
  }
2274
2473
  function createProviderRuntimeAdapters(config, options = {}) {
2275
- const claudeHookBridgeConfig = buildClaudeHookBridgeConfig(config);
2474
+ const claudeHookBridgeConfig = buildClaudeHookBridgeConfig(config, "claude-code");
2475
+ const legnaHookBridgeConfig = buildClaudeHookBridgeConfig(config, "legna-code");
2276
2476
  const claudeAdapter = process.env.VITEST
2277
2477
  ? new ClaudeRuntimeAdapter({
2278
2478
  homeDir: config.claudeCodeHomeDir,
@@ -2294,21 +2494,38 @@ function createProviderRuntimeAdapters(config, options = {}) {
2294
2494
  if ("dispose" in claudeAdapter && typeof claudeAdapter.dispose === "function") {
2295
2495
  disposables.push(claudeAdapter);
2296
2496
  }
2297
- const codexTransportHelper = process.env.VITEST
2298
- ? null
2299
- : new CodexAppServerHelperClient(config.codexCliPath, {
2300
- homeDir: config.codexHomeDir
2301
- });
2302
- if (codexTransportHelper) {
2303
- disposables.push(codexTransportHelper);
2304
- }
2305
2497
  return {
2306
2498
  adapters: [
2307
2499
  claudeAdapter,
2500
+ new LegnaRuntimeAdapter({
2501
+ homeDir: config.legnaCodeHomeDir,
2502
+ commandPath: config.legnaCodeCliPath,
2503
+ legacyClaudeHomeDir: config.claudeCodeHomeDir,
2504
+ hookBridge: {
2505
+ url: legnaHookBridgeConfig.bridgeUrl,
2506
+ token: config.claudeHookBridgeToken,
2507
+ scriptPath: legnaHookBridgeConfig.scriptPath
2508
+ }
2509
+ }),
2308
2510
  new CodexRuntimeAdapter({
2309
2511
  homeDir: config.codexHomeDir,
2310
2512
  commandPath: config.codexCliPath,
2311
- transportFactory: codexTransportHelper?.createTransport.bind(codexTransportHelper),
2513
+ transportFactory: process.env.VITEST
2514
+ ? undefined
2515
+ : (request) => {
2516
+ const client = new CodexAppServerHelperClient(config.codexCliPath, {
2517
+ homeDir: request.runtimeHomeDir?.trim() || config.codexHomeDir,
2518
+ runtimeEnv: request.runtimeEnv ?? null
2519
+ });
2520
+ const transport = client.createTransport();
2521
+ return {
2522
+ ...transport,
2523
+ close() {
2524
+ transport.close();
2525
+ client.dispose();
2526
+ }
2527
+ };
2528
+ },
2312
2529
  handleServerRequest: options.handleCodexServerRequest
2313
2530
  }),
2314
2531
  new GeminiRuntimeAdapter({
@@ -2333,20 +2550,27 @@ function resolveRuntimeInstructionFilePath(provider, workspacePath, explicitFile
2333
2550
  const resolvedExplicit = path.resolve(normalizedExplicit);
2334
2551
  return existsSync(resolvedExplicit) ? resolvedExplicit : null;
2335
2552
  }
2336
- if (provider !== "claude-code") {
2553
+ const defaultInstructionFileNames = {
2554
+ "claude-code": "CLAUDE.md",
2555
+ "legna-code": "LEGNA.md"
2556
+ };
2557
+ const defaultInstructionFileName = defaultInstructionFileNames[provider];
2558
+ const defaultInstructionPath = defaultInstructionFileName
2559
+ ? path.join(workspacePath, defaultInstructionFileName)
2560
+ : null;
2561
+ if (!defaultInstructionPath) {
2337
2562
  return null;
2338
2563
  }
2339
- const defaultClaudeInstructionPath = path.join(workspacePath, "CLAUDE.md");
2340
- return existsSync(defaultClaudeInstructionPath)
2341
- ? path.resolve(defaultClaudeInstructionPath)
2564
+ return existsSync(defaultInstructionPath)
2565
+ ? path.resolve(defaultInstructionPath)
2342
2566
  : null;
2343
2567
  }
2344
- function buildClaudeHookBridgeConfig(config) {
2345
- const bridgeUrl = `http://127.0.0.1:${config.port}/api/providers/claude-code/hook-bridge/events`;
2568
+ function buildClaudeHookBridgeConfig(config, provider) {
2569
+ const bridgeUrl = buildClaudeCompatibleHookBridgeUrl(config, provider);
2346
2570
  const scriptPath = resolveClaudeHookBridgeScriptPath();
2347
2571
  const command = `node "${scriptPath}" --url "${bridgeUrl}" --token "${config.claudeHookBridgeToken}"`;
2348
2572
  return {
2349
- provider: "claude-code",
2573
+ provider,
2350
2574
  bridgeUrl,
2351
2575
  token: config.claudeHookBridgeToken,
2352
2576
  scriptPath,
@@ -2376,26 +2600,30 @@ function resolveClaudeHookBridgeScriptPath() {
2376
2600
  }
2377
2601
  return candidates[0];
2378
2602
  }
2379
- function buildClaudeRawStoreRef(homeDir, workspacePath, sessionId) {
2380
- return path.join(homeDir, "projects", workspaceSlug(workspacePath), `${sessionId}.jsonl`);
2603
+ function normalizeOptionalBindingValue(value) {
2604
+ const normalized = value?.trim();
2605
+ return normalized && normalized.length > 0 ? normalized : null;
2381
2606
  }
2382
- function findClaudeSessionFile(homeDir, sessionId) {
2383
- const projectsDir = path.join(homeDir, "projects");
2384
- if (!existsSync(projectsDir)) {
2385
- return null;
2607
+ function logOpenCodeOrderEnvelopeDebug(scope, detail) {
2608
+ if (!OPENCODE_ORDER_DEBUG_ENABLED) {
2609
+ return;
2386
2610
  }
2387
- const candidates = readdirSync(projectsDir, { withFileTypes: true })
2388
- .filter((entry) => entry.isDirectory())
2389
- .map((entry) => path.join(projectsDir, entry.name, `${sessionId}.jsonl`))
2390
- .filter((candidate) => existsSync(candidate));
2391
- return candidates[0] ?? null;
2611
+ console.info(`[opencode-order-envelope] ${scope}`, {
2612
+ timestamp: new Date().toISOString(),
2613
+ ...detail
2614
+ });
2392
2615
  }
2393
- function workspaceSlug(workspacePath) {
2394
- const trimmed = workspacePath.replace(/[\\/]+$/, "");
2395
- const normalizedDriveLetter = trimmed.replace(/^[A-Z](?=:)/, (value) => value.toLowerCase());
2396
- return normalizedDriveLetter
2397
- .replaceAll(":", "-")
2398
- .replaceAll("\\", "-")
2399
- .replaceAll("/", "-");
2616
+ function summarizeOpenCodeOrderMessage(message) {
2617
+ const content = typeof message.content === "string" ? message.content : "";
2618
+ const normalized = content.replace(/\s+/g, " ").trim();
2619
+ return {
2620
+ messageId: message.messageId ?? null,
2621
+ role: message.role ?? null,
2622
+ kind: message.kind ?? null,
2623
+ sequence: message.sequence ?? null,
2624
+ timestamp: message.timestamp ?? null,
2625
+ rawRef: message.rawRef ?? null,
2626
+ contentPreview: normalized.length > 80 ? `${normalized.slice(0, 80)}...` : normalized
2627
+ };
2400
2628
  }
2401
2629
  //# sourceMappingURL=session-live-runtime-service.js.map