@jingyi0605/codingns 0.5.1 → 0.6.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 (196) hide show
  1. package/dist/public/assets/{AdaptiveButlerPage-SffCV4Vb.js → AdaptiveButlerPage-uFwDdN-F.js} +3 -3
  2. package/dist/public/assets/App-BZvapsi8.js +30 -0
  3. package/dist/public/assets/App-CcDXqFl1.css +1 -0
  4. package/dist/public/assets/{BootstrapPage--zExdgfM.js → BootstrapPage-gHSoa4JN.js} +1 -1
  5. package/dist/public/assets/ConversationPage-z3sXtKZ7.js +4 -0
  6. package/dist/public/assets/{DesktopDetachPreviewPage-DvI9CIKi.js → DesktopDetachPreviewPage-4eMRxiBW.js} +1 -1
  7. package/dist/public/assets/DesktopWindowPage-CZcoGApB.js +2 -0
  8. package/dist/public/assets/FileContextPanel-C3qex8bb.js +1 -0
  9. package/dist/public/assets/GitSidebar-BK6H16XU.js +6 -0
  10. package/dist/public/assets/{MobileCreateSessionSheet-CXSKMnYn.js → MobileCreateSessionSheet-BYfbvK8o.js} +1 -1
  11. package/dist/public/assets/MobileSheet-Ckug8hTb.js +1 -0
  12. package/dist/public/assets/{MobileTopHeaderFrame-BWorAJ1C.js → MobileTopHeaderFrame-Bwv8Ovm_.js} +1 -1
  13. package/dist/public/assets/MobileWorkspaceSwitcherHeader-RqWrBdn2.js +1 -0
  14. package/dist/public/assets/RelayConnectEntryPage-D_4YL-YH.js +1 -0
  15. package/dist/public/assets/ServerSettingsModal-CMSm3BZU.js +1 -0
  16. package/dist/public/assets/SessionIndexPage-DuK10DL5.js +1 -0
  17. package/dist/public/assets/SettingsPage-fyD-xaHL.js +1 -0
  18. package/dist/public/assets/TerminalManagerPanel-CCLr1Ypk.js +1 -0
  19. package/dist/public/assets/{TerminalPage-CvnHXBhw.js → TerminalPage-DaooFaJ4.js} +19 -19
  20. package/dist/public/assets/{TerminalRuntimeFallbackModal-D7Aq186N.js → TerminalRuntimeFallbackModal-aUzjEBwP.js} +1 -1
  21. package/dist/public/assets/ToolFilesPage-CGxBvYG0.js +1 -0
  22. package/dist/public/assets/ToolGitPage-C264yjS9.js +1 -0
  23. package/dist/public/assets/ToolProcessesPage-BOP4A1cb.js +1 -0
  24. package/dist/public/assets/ToolsHomePage-CQxGiKQA.js +1 -0
  25. package/dist/public/assets/WorkbenchLandingPage-CvAY68ca.js +1 -0
  26. package/dist/public/assets/WorkbenchLayout-DGm8Tc5M.js +3 -0
  27. package/dist/public/assets/{WorkbenchModal-B09hC9b5.js → WorkbenchModal-0tPIIhca.js} +1 -1
  28. package/dist/public/assets/{WorkbenchShellRoute-DsW4mBTX.css → WorkbenchShellRoute-BF0nHWOk.css} +1 -1
  29. package/dist/public/assets/WorkbenchShellRoute-DBBOsJo9.js +1 -0
  30. package/dist/public/assets/WorkspaceDebugDetailPage-CDerFYd2.js +1 -0
  31. package/dist/public/assets/WorkspaceDetailPage-BlJc1CHE.js +1 -0
  32. package/dist/public/assets/WorkspaceHomePage-BUsKJ3lv.js +1 -0
  33. package/dist/public/assets/client-runtime-manager-BZpL17fc.js +1 -0
  34. package/dist/public/assets/{default-session-permission-mode-D0wZ9Jek.js → default-session-permission-mode-DT4SGiwp.js} +1 -1
  35. package/dist/public/assets/file-tree-icon-Db5LXC8h.js +31 -0
  36. package/dist/public/assets/index-BZLcEHW3.js +42 -0
  37. package/dist/public/assets/index-BbspQPC2.css +1 -0
  38. package/dist/public/assets/login-direct-candidate-resolver-1mxe_Oh8.js +1 -0
  39. package/dist/public/assets/{preferences-service-gOt2ZjKZ.js → preferences-service-DWnzl5a0.js} +1 -1
  40. package/dist/public/assets/relay-entry-C5_Iay0I.js +1 -0
  41. package/dist/public/assets/session-runtime-machine-DdLeDqQr.js +17 -0
  42. package/dist/public/assets/{styles-BWPBZvze.css → styles-CsEMfdaS.css} +1 -1
  43. package/dist/public/assets/{terminal-runtime-meta-BMT-rSEe.js → terminal-runtime-meta-cdtWVfCm.js} +1 -1
  44. package/dist/public/assets/{useRegisteredDebugTemplates-zMcEOGca.js → useRegisteredDebugTemplates-oFAQNIqh.js} +1 -1
  45. package/dist/public/assets/window-BVUB8gMK.js +1 -0
  46. package/dist/public/index.html +2 -2
  47. package/dist/server/config/env.d.ts +2 -0
  48. package/dist/server/config/env.js +39 -0
  49. package/dist/server/config/env.js.map +1 -1
  50. package/dist/server/modules/client/npm-global-package-service.d.ts +7 -1
  51. package/dist/server/modules/client/npm-global-package-service.js +149 -43
  52. package/dist/server/modules/client/npm-global-package-service.js.map +1 -1
  53. package/dist/server/modules/client/service-update-task-service.js +6 -2
  54. package/dist/server/modules/client/service-update-task-service.js.map +1 -1
  55. package/dist/server/modules/client/service-update-types.d.ts +2 -0
  56. package/dist/server/modules/git/git-controller.d.ts +3 -0
  57. package/dist/server/modules/git/git-controller.js +3 -0
  58. package/dist/server/modules/git/git-controller.js.map +1 -1
  59. package/dist/server/modules/git/git-read-service.js +47 -1
  60. package/dist/server/modules/git/git-read-service.js.map +1 -1
  61. package/dist/server/modules/git/git-write-service.d.ts +4 -0
  62. package/dist/server/modules/git/git-write-service.js +24 -0
  63. package/dist/server/modules/git/git-write-service.js.map +1 -1
  64. package/dist/server/modules/git/types.d.ts +1 -0
  65. package/dist/server/modules/git/workspace-repo-guard.d.ts +2 -0
  66. package/dist/server/modules/git/workspace-repo-guard.js +24 -10
  67. package/dist/server/modules/git/workspace-repo-guard.js.map +1 -1
  68. package/dist/server/modules/parallel-sessions/parallel-session-controller.d.ts +53 -0
  69. package/dist/server/modules/parallel-sessions/parallel-session-controller.js +70 -0
  70. package/dist/server/modules/parallel-sessions/parallel-session-controller.js.map +1 -0
  71. package/dist/server/modules/parallel-sessions/parallel-session-group-service.d.ts +83 -0
  72. package/dist/server/modules/parallel-sessions/parallel-session-group-service.js +591 -0
  73. package/dist/server/modules/parallel-sessions/parallel-session-group-service.js.map +1 -0
  74. package/dist/server/modules/parallel-sessions/session-isolated-workspace-service.d.ts +56 -0
  75. package/dist/server/modules/parallel-sessions/session-isolated-workspace-service.js +483 -0
  76. package/dist/server/modules/parallel-sessions/session-isolated-workspace-service.js.map +1 -0
  77. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.d.ts +16 -1
  78. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.js.map +1 -1
  79. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.d.ts +2 -1
  80. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js +18 -0
  81. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js.map +1 -1
  82. package/dist/server/modules/relay-tunnel/relay-tunnel-candidate-endpoints.d.ts +2 -0
  83. package/dist/server/modules/relay-tunnel/relay-tunnel-candidate-endpoints.js +129 -0
  84. package/dist/server/modules/relay-tunnel/relay-tunnel-candidate-endpoints.js.map +1 -0
  85. package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.d.ts +13 -0
  86. package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.js +2 -0
  87. package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.js.map +1 -0
  88. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.d.ts +6 -0
  89. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js +110 -10
  90. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js.map +1 -1
  91. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.d.ts +16 -4
  92. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js +220 -102
  93. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js.map +1 -1
  94. package/dist/server/modules/relay-tunnel/relay-tunnel-service.d.ts +4 -1
  95. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js +257 -162
  96. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js.map +1 -1
  97. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +3 -0
  98. package/dist/server/modules/sessions/codex-app-server-helper-client.js +56 -45
  99. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  100. package/dist/server/modules/sessions/codex-app-server-helper-process.js +21 -2
  101. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  102. package/dist/server/modules/sessions/session-activity-inspector.js +6 -8
  103. package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
  104. package/dist/server/modules/sessions/session-history-service.d.ts +11 -1
  105. package/dist/server/modules/sessions/session-history-service.js +204 -21
  106. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  107. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +8 -0
  108. package/dist/server/modules/sessions/session-live-runtime-service.js +208 -25
  109. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  110. package/dist/server/modules/workbench/codex-archive-watcher.d.ts +16 -0
  111. package/dist/server/modules/workbench/codex-archive-watcher.js +50 -0
  112. package/dist/server/modules/workbench/codex-archive-watcher.js.map +1 -0
  113. package/dist/server/modules/workbench/workbench-service.d.ts +43 -4
  114. package/dist/server/modules/workbench/workbench-service.js +72 -9
  115. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  116. package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +1 -1
  117. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +26 -3
  118. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
  119. package/dist/server/modules/workspace/workspace-service.d.ts +3 -1
  120. package/dist/server/modules/workspace/workspace-service.js +10 -2
  121. package/dist/server/modules/workspace/workspace-service.js.map +1 -1
  122. package/dist/server/modules/worktree/worktree-base-ref-resolver.d.ts +20 -0
  123. package/dist/server/modules/worktree/worktree-base-ref-resolver.js +111 -0
  124. package/dist/server/modules/worktree/worktree-base-ref-resolver.js.map +1 -0
  125. package/dist/server/modules/worktree/worktree-cleanup-service.js +9 -3
  126. package/dist/server/modules/worktree/worktree-cleanup-service.js.map +1 -1
  127. package/dist/server/modules/worktree/worktree-manager.d.ts +0 -1
  128. package/dist/server/modules/worktree/worktree-manager.js +14 -20
  129. package/dist/server/modules/worktree/worktree-manager.js.map +1 -1
  130. package/dist/server/routes/git.js +1 -0
  131. package/dist/server/routes/git.js.map +1 -1
  132. package/dist/server/routes/parallel-groups.d.ts +3 -0
  133. package/dist/server/routes/parallel-groups.js +9 -0
  134. package/dist/server/routes/parallel-groups.js.map +1 -0
  135. package/dist/server/server/create-server.d.ts +8 -0
  136. package/dist/server/server/create-server.js +48 -9
  137. package/dist/server/server/create-server.js.map +1 -1
  138. package/dist/server/server/workbench-runtime-terminal-sync.d.ts +14 -0
  139. package/dist/server/server/workbench-runtime-terminal-sync.js +17 -0
  140. package/dist/server/server/workbench-runtime-terminal-sync.js.map +1 -0
  141. package/dist/server/storage/repositories/parallel-session-group-repository.d.ts +11 -0
  142. package/dist/server/storage/repositories/parallel-session-group-repository.js +131 -0
  143. package/dist/server/storage/repositories/parallel-session-group-repository.js.map +1 -0
  144. package/dist/server/storage/repositories/parallel-session-member-repository.d.ts +12 -0
  145. package/dist/server/storage/repositories/parallel-session-member-repository.js +150 -0
  146. package/dist/server/storage/repositories/parallel-session-member-repository.js.map +1 -0
  147. package/dist/server/storage/repositories/session-isolated-workspace-repository.d.ts +15 -0
  148. package/dist/server/storage/repositories/session-isolated-workspace-repository.js +230 -0
  149. package/dist/server/storage/repositories/session-isolated-workspace-repository.js.map +1 -0
  150. package/dist/server/storage/sqlite/schema.sql +73 -0
  151. package/dist/server/types/domain.d.ts +72 -0
  152. package/dist/server/ws/workbench-ws-hub.d.ts +3 -1
  153. package/dist/server/ws/workbench-ws-hub.js +189 -20
  154. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  155. package/dist/server/ws/ws-server.js +141 -8
  156. package/dist/server/ws/ws-server.js.map +1 -1
  157. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +1 -0
  158. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +67 -6
  159. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  160. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +3 -0
  161. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +46 -22
  162. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  163. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +558 -309
  164. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  165. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +29 -5
  166. package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +1 -1
  167. package/package.json +1 -1
  168. package/dist/public/assets/App-DUAg5urj.css +0 -1
  169. package/dist/public/assets/App-WOLwMld_.js +0 -30
  170. package/dist/public/assets/ConversationPage-D9pzRmOg.js +0 -2
  171. package/dist/public/assets/DesktopWindowPage-D8FpOSLE.js +0 -2
  172. package/dist/public/assets/FileContextPanel-C8T7oqRN.js +0 -1
  173. package/dist/public/assets/GitSidebar-Bze7DNnc.js +0 -6
  174. package/dist/public/assets/MobileSheet-Gzc14EpR.js +0 -1
  175. package/dist/public/assets/MobileWorkspaceSwitcherHeader-DOr4pTUq.js +0 -1
  176. package/dist/public/assets/ServerSettingsModal-BYB0GvTl.js +0 -1
  177. package/dist/public/assets/SessionIndexPage-CR3IARXX.js +0 -1
  178. package/dist/public/assets/SettingsPage-B_BQtnwE.js +0 -1
  179. package/dist/public/assets/TerminalManagerPanel-PQ-EM64j.js +0 -1
  180. package/dist/public/assets/ToolFilesPage-Qzkc6K2I.js +0 -1
  181. package/dist/public/assets/ToolGitPage-BdNDN-cV.js +0 -1
  182. package/dist/public/assets/ToolProcessesPage-EXJ9DHWI.js +0 -1
  183. package/dist/public/assets/ToolsHomePage-CjF3CWzR.js +0 -1
  184. package/dist/public/assets/WorkbenchLandingPage-DZPk4SmX.js +0 -1
  185. package/dist/public/assets/WorkbenchLayout-rwQib5In.js +0 -3
  186. package/dist/public/assets/WorkbenchShellRoute-Cerk5uK7.js +0 -1
  187. package/dist/public/assets/WorkspaceDebugDetailPage-Bcq8s-Ma.js +0 -1
  188. package/dist/public/assets/WorkspaceDetailPage-DNAa8pKr.js +0 -1
  189. package/dist/public/assets/WorkspaceHomePage-BoiLuACV.js +0 -1
  190. package/dist/public/assets/client-runtime-manager-CRQ-F5d2.js +0 -1
  191. package/dist/public/assets/file-tree-icon-Dp_xhVfD.js +0 -31
  192. package/dist/public/assets/index-C2G8Gmf1.js +0 -42
  193. package/dist/public/assets/index-CpPTUeA3.css +0 -1
  194. package/dist/public/assets/session-runtime-machine-Dq3pW-UF.js +0 -17
  195. package/dist/public/assets/window-BWqRixxq.js +0 -1
  196. /package/dist/public/assets/{styles-CSUx5LGe.js → styles-DRVvx_kv.js} +0 -0
@@ -12,6 +12,7 @@ import { buildApplyPatchFromFileChangeList, extractApplyPatchTargetPathsFromTool
12
12
  import { loadDatabaseSync } from "../sqlite/node-sqlite.js";
13
13
  import { createCodexThreadPermissionOptions } from "./codex-permissions.js";
14
14
  const CODEX_RUNTIME_DEBUG_ENABLED = /^(1|true|yes)$/i.test(process.env.CODINGNS_PERF_DEBUG?.trim() ?? "");
15
+ const CODEX_APP_SERVER_REQUEST_TIMEOUT_MS = 20_000;
15
16
  function logCodexRuntimeStep(scope, startedAtMs, detail = {}) {
16
17
  if (!CODEX_RUNTIME_DEBUG_ENABLED) {
17
18
  return;
@@ -61,123 +62,115 @@ export class CodexRuntimeAdapter {
61
62
  const transport = this.options.transportFactory
62
63
  ? this.options.transportFactory()
63
64
  : createCodexAppServerTransport(this.options);
64
- const initializeStartedAtMs = performance.now();
65
- await transport.initialize();
66
- logCodexRuntimeStep("start_session.initialize", initializeStartedAtMs, {
67
- sessionId: request.sessionId,
68
- workspacePath: request.workspacePath
69
- });
70
- const abortController = new AbortController();
71
- const eventQueue = createAsyncEventQueue();
72
- const resumedSyntheticSession = await this.resumeSyntheticThreadFromHistory(transport, request);
73
- const startedSession = resumedSyntheticSession ??
74
- await (async () => {
75
- const startThreadStartedAtMs = performance.now();
76
- const started = await transport.startThread(request);
77
- logCodexRuntimeStep("start_session.thread_start", startThreadStartedAtMs, {
78
- sessionId: request.sessionId,
79
- providerSessionId: started.providerSessionId
80
- });
81
- return started;
82
- })();
83
- const providerSessionId = startedSession.providerSessionId;
84
- const syntheticRawStoreRef = buildRuntimeRawStoreRef(resolveRuntimeStoreKey(providerSessionId, request.sessionId));
85
- const rawStoreRef = pickAvailableCodexRawStoreRef(providerSessionId, resumedSyntheticSession
86
- ? [resumedSyntheticSession.rawStoreRef]
87
- : [startedSession.rawStoreRef, request.rawStoreRef], syntheticRawStoreRef);
88
- logCodexRuntimeStep("start_session.raw_store_ref_ready", launchPerfStartedAtMs, {
89
- sessionId: request.sessionId,
90
- providerSessionId,
91
- synthetic: isSyntheticRawStoreRef(rawStoreRef),
92
- hasProviderRawStoreRef: Boolean(startedSession.rawStoreRef),
93
- providerRawStoreRefExists: Boolean(startedSession.rawStoreRef && existsSync(startedSession.rawStoreRef))
94
- });
95
- sink.updateSessionBinding({
96
- providerSessionId,
97
- rawStoreRef
98
- });
99
- let firstNotificationLogged = false;
100
- transport.setNotificationHandler(async (notification) => {
101
- if (!firstNotificationLogged) {
102
- firstNotificationLogged = true;
103
- logCodexRuntimeStep("start_session.first_notification", launchPerfStartedAtMs, {
65
+ try {
66
+ const initializeStartedAtMs = performance.now();
67
+ await transport.initialize();
68
+ logCodexRuntimeStep("start_session.initialize", initializeStartedAtMs, {
69
+ sessionId: request.sessionId,
70
+ workspacePath: request.workspacePath
71
+ });
72
+ const abortController = new AbortController();
73
+ const eventQueue = createAsyncEventQueue();
74
+ const translateNotification = createCodexAppServerNotificationTranslator();
75
+ const forwardTranslatedNotification = createCodexTranslatedNotificationForwarder(eventQueue);
76
+ const resumedSyntheticSession = await this.resumeSyntheticThreadFromHistory(transport, request);
77
+ const startedSession = resumedSyntheticSession ??
78
+ await (async () => {
79
+ const startThreadStartedAtMs = performance.now();
80
+ const started = await transport.startThread(request);
81
+ logCodexRuntimeStep("start_session.thread_start", startThreadStartedAtMs, {
82
+ sessionId: request.sessionId,
83
+ providerSessionId: started.providerSessionId
84
+ });
85
+ return started;
86
+ })();
87
+ const providerSessionId = startedSession.providerSessionId;
88
+ const syntheticRawStoreRef = buildRuntimeRawStoreRef(resolveRuntimeStoreKey(providerSessionId, request.sessionId));
89
+ const rawStoreRef = pickAvailableCodexRawStoreRef(providerSessionId, resumedSyntheticSession
90
+ ? [resumedSyntheticSession.rawStoreRef]
91
+ : [startedSession.rawStoreRef, request.rawStoreRef], syntheticRawStoreRef);
92
+ logCodexRuntimeStep("start_session.raw_store_ref_ready", launchPerfStartedAtMs, {
93
+ sessionId: request.sessionId,
94
+ providerSessionId,
95
+ synthetic: isSyntheticRawStoreRef(rawStoreRef),
96
+ hasProviderRawStoreRef: Boolean(startedSession.rawStoreRef),
97
+ providerRawStoreRefExists: Boolean(startedSession.rawStoreRef && existsSync(startedSession.rawStoreRef))
98
+ });
99
+ sink.updateSessionBinding({
100
+ providerSessionId,
101
+ rawStoreRef
102
+ });
103
+ let firstNotificationLogged = false;
104
+ transport.setNotificationHandler(async (notification) => {
105
+ if (!firstNotificationLogged) {
106
+ firstNotificationLogged = true;
107
+ logCodexRuntimeStep("start_session.first_notification", launchPerfStartedAtMs, {
108
+ sessionId: request.sessionId,
109
+ providerSessionId,
110
+ method: ensureText(notification.method).trim() || null
111
+ });
112
+ }
113
+ const translated = translateNotification(notification);
114
+ forwardTranslatedNotification(translated);
115
+ });
116
+ transport.setServerRequestHandler(async (serverRequest) => {
117
+ if (!this.options.handleServerRequest) {
118
+ throw new Error("CODEX_APP_SERVER_REQUEST_NOT_SUPPORTED");
119
+ }
120
+ return this.options.handleServerRequest({
104
121
  sessionId: request.sessionId,
105
122
  providerSessionId,
106
- method: ensureText(notification.method).trim() || null
123
+ request: serverRequest
107
124
  });
108
- }
109
- const translated = translateCodexAppServerNotification(notification);
110
- if (translated.turnId) {
111
- eventQueue.setTurnId(translated.turnId);
112
- }
113
- for (const event of translated.events) {
114
- eventQueue.push(event);
115
- }
116
- if (translated.terminal) {
125
+ });
126
+ transport.setOnClose((error) => {
127
+ if (error) {
128
+ eventQueue.push({
129
+ type: "turn.failed",
130
+ timestamp: nextTimestamp(),
131
+ error: error.message
132
+ });
133
+ }
117
134
  eventQueue.close();
118
- }
119
- });
120
- transport.setServerRequestHandler(async (serverRequest) => {
121
- if (!this.options.handleServerRequest) {
122
- throw new Error("CODEX_APP_SERVER_REQUEST_NOT_SUPPORTED");
123
- }
124
- return this.options.handleServerRequest({
135
+ });
136
+ const startTurnStartedAtMs = performance.now();
137
+ const startTurnResult = await transport.startTurn(request, providerSessionId);
138
+ const startTurnNotification = startTurnResult?.notification ?? null;
139
+ if (startTurnNotification) {
140
+ const translated = translateNotification(startTurnNotification);
141
+ forwardTranslatedNotification(translated);
142
+ }
143
+ logCodexRuntimeStep("start_session.turn_start", startTurnStartedAtMs, {
125
144
  sessionId: request.sessionId,
126
- providerSessionId,
127
- request: serverRequest
145
+ providerSessionId
128
146
  });
129
- });
130
- transport.setOnClose((error) => {
131
- if (error) {
132
- eventQueue.push({
133
- type: "turn.failed",
134
- timestamp: nextTimestamp(),
135
- error: error.message
136
- });
137
- }
138
- eventQueue.close();
139
- });
140
- const startTurnStartedAtMs = performance.now();
141
- const startTurnResult = await transport.startTurn(request, providerSessionId);
142
- const startTurnNotification = startTurnResult?.notification ?? null;
143
- if (startTurnNotification) {
144
- const translated = translateCodexAppServerNotification(startTurnNotification);
145
- if (translated.turnId) {
146
- eventQueue.setTurnId(translated.turnId);
147
- }
148
- for (const event of translated.events) {
149
- eventQueue.push(event);
150
- }
151
- if (translated.terminal) {
152
- eventQueue.close();
153
- }
147
+ logCodexRuntimeStep("start_session.ready", launchPerfStartedAtMs, {
148
+ sessionId: request.sessionId,
149
+ providerSessionId
150
+ });
151
+ return {
152
+ providerSessionId,
153
+ rawStoreRef,
154
+ submitDuringRun: async (options) => {
155
+ await transport.steerTurn(options);
156
+ },
157
+ interrupt: async () => {
158
+ abortController.abort();
159
+ await transport.interruptTurn().catch(() => {
160
+ return;
161
+ });
162
+ transport.close();
163
+ },
164
+ isAlive: () => transport.isClosed() === false,
165
+ completed: this.runTurn(null, request, sink, providerSessionId, rawStoreRef, abortController, eventQueue.iterator, [], launchedAtMs, launchPerfStartedAtMs).finally(() => {
166
+ transport.close();
167
+ })
168
+ };
169
+ }
170
+ catch (error) {
171
+ transport.close();
172
+ throw error;
154
173
  }
155
- logCodexRuntimeStep("start_session.turn_start", startTurnStartedAtMs, {
156
- sessionId: request.sessionId,
157
- providerSessionId
158
- });
159
- logCodexRuntimeStep("start_session.ready", launchPerfStartedAtMs, {
160
- sessionId: request.sessionId,
161
- providerSessionId
162
- });
163
- return {
164
- providerSessionId,
165
- rawStoreRef,
166
- submitDuringRun: async (options) => {
167
- await transport.steerTurn(options);
168
- },
169
- interrupt: async () => {
170
- abortController.abort();
171
- await transport.interruptTurn().catch(() => {
172
- return;
173
- });
174
- transport.close();
175
- },
176
- isAlive: () => transport.isClosed() === false,
177
- completed: this.runTurn(null, request, sink, providerSessionId, rawStoreRef, abortController, eventQueue.iterator, [], launchedAtMs, launchPerfStartedAtMs).finally(() => {
178
- transport.close();
179
- })
180
- };
181
174
  }
182
175
  async resumeSyntheticThreadFromHistory(transport, request) {
183
176
  const history = buildSyntheticResumeHistory(request.rawStoreRef);
@@ -206,152 +199,144 @@ export class CodexRuntimeAdapter {
206
199
  const transport = this.options.transportFactory
207
200
  ? this.options.transportFactory()
208
201
  : createCodexAppServerTransport(this.options);
209
- const runtimeStartedAtMs = performance.now();
210
- const initializeStartedAtMs = performance.now();
211
- await transport.initialize();
212
- logCodexRuntimeStep("continue_session.initialize", initializeStartedAtMs, {
213
- sessionId: request.sessionId,
214
- providerSessionId
215
- });
216
- const syntheticRawStoreRef = buildRuntimeRawStoreRef(providerSessionId);
217
- let resolvedSessionId = providerSessionId;
218
- let resolvedFallbackHistoryRawStoreRef = null;
219
- const resumeThreadStartedAtMs = performance.now();
220
- let resumed;
221
202
  try {
222
- resumed = await transport.resumeThread(request, resolvedSessionId);
223
- logCodexRuntimeStep("continue_session.thread_resume", resumeThreadStartedAtMs, {
203
+ const runtimeStartedAtMs = performance.now();
204
+ const initializeStartedAtMs = performance.now();
205
+ await transport.initialize();
206
+ logCodexRuntimeStep("continue_session.initialize", initializeStartedAtMs, {
224
207
  sessionId: request.sessionId,
225
- providerSessionId: resolvedSessionId,
226
- fallback: false
227
- });
228
- }
229
- catch (error) {
230
- const fallbackHistorySource = await this.resolveContinueFallbackHistorySource({
231
- providerSessionId,
232
- rawStoreRef: request.rawStoreRef,
233
- workspacePath: request.workspacePath
234
- });
235
- const resumeHistory = fallbackHistorySource?.history ?? [];
236
- if (!shouldFallbackCodexContinueFromHistory(error, resumeHistory)) {
237
- throw error;
238
- }
239
- resolvedFallbackHistoryRawStoreRef = fallbackHistorySource?.rawStoreRef ?? null;
240
- const resumeFallbackStartedAtMs = performance.now();
241
- resumed = await transport.resumeThreadFromHistory({
242
- providerSessionId: null,
243
- workspacePath: request.workspacePath,
244
- history: resumeHistory,
245
- model: request.options.model
246
- });
247
- resolvedSessionId = resumed.providerSessionId;
248
- logCodexRuntimeStep("continue_session.thread_resume_from_history_fallback", resumeFallbackStartedAtMs, {
249
- sessionId: request.sessionId,
250
- requestedProviderSessionId: providerSessionId,
251
- providerSessionId: resolvedSessionId,
252
- historyLength: resumeHistory.length
208
+ providerSessionId
253
209
  });
254
- }
255
- const pickedRawStoreRef = pickAvailableCodexRawStoreRef(resolvedSessionId, [resolvedFallbackHistoryRawStoreRef, request.rawStoreRef, resumed.rawStoreRef], syntheticRawStoreRef);
256
- const rawStoreRef = !resumed.rawStoreRef?.trim() && resolvedFallbackHistoryRawStoreRef
257
- ? resolvedFallbackHistoryRawStoreRef
258
- : pickedRawStoreRef;
259
- const abortController = new AbortController();
260
- const eventQueue = createAsyncEventQueue();
261
- logCodexRuntimeStep("continue_session.raw_store_ref_ready", runtimeStartedAtMs, {
262
- sessionId: request.sessionId,
263
- providerSessionId: resolvedSessionId,
264
- synthetic: isSyntheticRawStoreRef(rawStoreRef),
265
- hasResumedRawStoreRef: Boolean(resumed.rawStoreRef),
266
- hasRequestRawStoreRef: Boolean(request.rawStoreRef),
267
- resumedRawStoreRefExists: Boolean(resumed.rawStoreRef && existsSync(resumed.rawStoreRef))
268
- });
269
- sink.updateSessionBinding({
270
- providerSessionId: resolvedSessionId,
271
- rawStoreRef
272
- });
273
- let firstNotificationLogged = false;
274
- transport.setNotificationHandler(async (notification) => {
275
- if (!firstNotificationLogged) {
276
- firstNotificationLogged = true;
277
- logCodexRuntimeStep("continue_session.first_notification", runtimeStartedAtMs, {
210
+ const syntheticRawStoreRef = buildRuntimeRawStoreRef(providerSessionId);
211
+ let resolvedSessionId = providerSessionId;
212
+ let resolvedFallbackHistoryRawStoreRef = null;
213
+ const resumeThreadStartedAtMs = performance.now();
214
+ let resumed;
215
+ try {
216
+ resumed = await transport.resumeThread(request, resolvedSessionId);
217
+ logCodexRuntimeStep("continue_session.thread_resume", resumeThreadStartedAtMs, {
278
218
  sessionId: request.sessionId,
279
219
  providerSessionId: resolvedSessionId,
280
- method: ensureText(notification.method).trim() || null
220
+ fallback: false
281
221
  });
282
222
  }
283
- const translated = translateCodexAppServerNotification(notification);
284
- if (translated.turnId) {
285
- eventQueue.setTurnId(translated.turnId);
286
- }
287
- for (const event of translated.events) {
288
- eventQueue.push(event);
289
- }
290
- if (translated.terminal) {
291
- eventQueue.close();
292
- }
293
- });
294
- transport.setServerRequestHandler(async (serverRequest) => {
295
- if (!this.options.handleServerRequest) {
296
- throw new Error("CODEX_APP_SERVER_REQUEST_NOT_SUPPORTED");
223
+ catch (error) {
224
+ const fallbackHistorySource = await this.resolveContinueFallbackHistorySource({
225
+ providerSessionId,
226
+ rawStoreRef: request.rawStoreRef,
227
+ workspacePath: request.workspacePath
228
+ });
229
+ const resumeHistory = fallbackHistorySource?.history ?? [];
230
+ if (!shouldFallbackCodexContinueFromHistory(error, resumeHistory)) {
231
+ throw error;
232
+ }
233
+ resolvedFallbackHistoryRawStoreRef = fallbackHistorySource?.rawStoreRef ?? null;
234
+ const resumeFallbackStartedAtMs = performance.now();
235
+ resumed = await transport.resumeThreadFromHistory({
236
+ providerSessionId: null,
237
+ workspacePath: request.workspacePath,
238
+ history: resumeHistory,
239
+ model: request.options.model
240
+ });
241
+ resolvedSessionId = resumed.providerSessionId;
242
+ logCodexRuntimeStep("continue_session.thread_resume_from_history_fallback", resumeFallbackStartedAtMs, {
243
+ sessionId: request.sessionId,
244
+ requestedProviderSessionId: providerSessionId,
245
+ providerSessionId: resolvedSessionId,
246
+ historyLength: resumeHistory.length
247
+ });
297
248
  }
298
- return this.options.handleServerRequest({
249
+ const pickedRawStoreRef = pickAvailableCodexRawStoreRef(resolvedSessionId, [resolvedFallbackHistoryRawStoreRef, request.rawStoreRef, resumed.rawStoreRef], syntheticRawStoreRef);
250
+ const rawStoreRef = !resumed.rawStoreRef?.trim() && resolvedFallbackHistoryRawStoreRef
251
+ ? resolvedFallbackHistoryRawStoreRef
252
+ : pickedRawStoreRef;
253
+ const abortController = new AbortController();
254
+ const eventQueue = createAsyncEventQueue();
255
+ const translateNotification = createCodexAppServerNotificationTranslator();
256
+ const forwardTranslatedNotification = createCodexTranslatedNotificationForwarder(eventQueue);
257
+ logCodexRuntimeStep("continue_session.raw_store_ref_ready", runtimeStartedAtMs, {
299
258
  sessionId: request.sessionId,
300
259
  providerSessionId: resolvedSessionId,
301
- request: serverRequest
260
+ synthetic: isSyntheticRawStoreRef(rawStoreRef),
261
+ hasResumedRawStoreRef: Boolean(resumed.rawStoreRef),
262
+ hasRequestRawStoreRef: Boolean(request.rawStoreRef),
263
+ resumedRawStoreRefExists: Boolean(resumed.rawStoreRef && existsSync(resumed.rawStoreRef))
302
264
  });
303
- });
304
- transport.setOnClose((error) => {
305
- if (error) {
306
- eventQueue.push({
307
- type: "turn.failed",
308
- timestamp: nextTimestamp(),
309
- error: error.message
265
+ sink.updateSessionBinding({
266
+ providerSessionId: resolvedSessionId,
267
+ rawStoreRef
268
+ });
269
+ let firstNotificationLogged = false;
270
+ transport.setNotificationHandler(async (notification) => {
271
+ if (!firstNotificationLogged) {
272
+ firstNotificationLogged = true;
273
+ logCodexRuntimeStep("continue_session.first_notification", runtimeStartedAtMs, {
274
+ sessionId: request.sessionId,
275
+ providerSessionId: resolvedSessionId,
276
+ method: ensureText(notification.method).trim() || null
277
+ });
278
+ }
279
+ const translated = translateNotification(notification);
280
+ forwardTranslatedNotification(translated);
281
+ });
282
+ transport.setServerRequestHandler(async (serverRequest) => {
283
+ if (!this.options.handleServerRequest) {
284
+ throw new Error("CODEX_APP_SERVER_REQUEST_NOT_SUPPORTED");
285
+ }
286
+ return this.options.handleServerRequest({
287
+ sessionId: request.sessionId,
288
+ providerSessionId: resolvedSessionId,
289
+ request: serverRequest
310
290
  });
311
- }
312
- eventQueue.close();
313
- });
314
- const startTurnStartedAtMs = performance.now();
315
- const startTurnResult = await transport.startTurn(request, resolvedSessionId);
316
- const startTurnNotification = startTurnResult?.notification ?? null;
317
- if (startTurnNotification) {
318
- const translated = translateCodexAppServerNotification(startTurnNotification);
319
- if (translated.turnId) {
320
- eventQueue.setTurnId(translated.turnId);
321
- }
322
- for (const event of translated.events) {
323
- eventQueue.push(event);
324
- }
325
- if (translated.terminal) {
291
+ });
292
+ transport.setOnClose((error) => {
293
+ if (error) {
294
+ eventQueue.push({
295
+ type: "turn.failed",
296
+ timestamp: nextTimestamp(),
297
+ error: error.message
298
+ });
299
+ }
326
300
  eventQueue.close();
327
- }
301
+ });
302
+ const startTurnStartedAtMs = performance.now();
303
+ const startTurnResult = await transport.startTurn(request, resolvedSessionId);
304
+ const startTurnNotification = startTurnResult?.notification ?? null;
305
+ if (startTurnNotification) {
306
+ const translated = translateNotification(startTurnNotification);
307
+ forwardTranslatedNotification(translated);
308
+ }
309
+ logCodexRuntimeStep("continue_session.turn_start", startTurnStartedAtMs, {
310
+ sessionId: request.sessionId,
311
+ providerSessionId: resolvedSessionId
312
+ });
313
+ logCodexRuntimeStep("continue_session.ready", runtimeStartedAtMs, {
314
+ sessionId: request.sessionId,
315
+ providerSessionId: resolvedSessionId
316
+ });
317
+ return {
318
+ providerSessionId: resolvedSessionId,
319
+ rawStoreRef,
320
+ submitDuringRun: async (options) => {
321
+ await transport.steerTurn(options);
322
+ },
323
+ interrupt: async () => {
324
+ abortController.abort();
325
+ await transport.interruptTurn().catch(() => {
326
+ return;
327
+ });
328
+ transport.close();
329
+ },
330
+ isAlive: () => transport.isClosed() === false,
331
+ completed: this.runTurn(null, request, sink, resolvedSessionId, rawStoreRef, abortController, eventQueue.iterator, [], Date.now()).finally(() => {
332
+ transport.close();
333
+ })
334
+ };
335
+ }
336
+ catch (error) {
337
+ transport.close();
338
+ throw error;
328
339
  }
329
- logCodexRuntimeStep("continue_session.turn_start", startTurnStartedAtMs, {
330
- sessionId: request.sessionId,
331
- providerSessionId: resolvedSessionId
332
- });
333
- logCodexRuntimeStep("continue_session.ready", runtimeStartedAtMs, {
334
- sessionId: request.sessionId,
335
- providerSessionId: resolvedSessionId
336
- });
337
- return {
338
- providerSessionId: resolvedSessionId,
339
- rawStoreRef,
340
- submitDuringRun: async (options) => {
341
- await transport.steerTurn(options);
342
- },
343
- interrupt: async () => {
344
- abortController.abort();
345
- await transport.interruptTurn().catch(() => {
346
- return;
347
- });
348
- transport.close();
349
- },
350
- isAlive: () => transport.isClosed() === false,
351
- completed: this.runTurn(null, request, sink, resolvedSessionId, rawStoreRef, abortController, eventQueue.iterator, [], Date.now()).finally(() => {
352
- transport.close();
353
- })
354
- };
355
340
  }
356
341
  async resolveContinueFallbackHistorySource(input) {
357
342
  const candidates = [];
@@ -1188,110 +1173,357 @@ function createAsyncEventQueue() {
1188
1173
  }
1189
1174
  };
1190
1175
  }
1191
- function translateCodexAppServerNotification(notification) {
1192
- const method = ensureText(notification.method).trim();
1193
- const params = toRecord(notification.params) ?? {};
1194
- if (method === "turn/started") {
1195
- return {
1196
- events: [],
1197
- terminal: false,
1198
- turnId: ensureText(readProp(readProp(params, "turn"), "id")).trim() || null
1199
- };
1200
- }
1201
- if (method === "turn/completed") {
1202
- const turn = toRecord(params.turn);
1203
- const status = ensureText(turn?.status).trim();
1204
- const itemEvents = translateCodexAppServerTurnItems(turn, "item.completed");
1205
- if (status === "failed") {
1206
- return {
1207
- events: [
1208
- ...itemEvents,
1209
- {
1210
- type: "turn.failed",
1211
- timestamp: nextTimestamp(),
1212
- error: ensureText(readProp(turn?.error, "message")).trim() || "codex turn failed"
1176
+ function createCodexTranslatedNotificationForwarder(eventQueue) {
1177
+ const seenReplayKeys = new Set();
1178
+ return (translated) => {
1179
+ if (translated.turnId) {
1180
+ eventQueue.setTurnId(translated.turnId);
1181
+ }
1182
+ for (const event of translated.events) {
1183
+ const replayKey = buildCodexTranslatedReplayKey(event);
1184
+ if (replayKey) {
1185
+ if (seenReplayKeys.has(replayKey)) {
1186
+ continue;
1187
+ }
1188
+ seenReplayKeys.add(replayKey);
1189
+ while (seenReplayKeys.size > 1024) {
1190
+ const oldest = seenReplayKeys.keys().next().value;
1191
+ if (typeof oldest !== "string") {
1192
+ break;
1213
1193
  }
1214
- ],
1215
- terminal: true,
1216
- turnId: ensureText(turn?.id).trim() || null
1217
- };
1194
+ seenReplayKeys.delete(oldest);
1195
+ }
1196
+ }
1197
+ eventQueue.push(event);
1198
+ }
1199
+ if (translated.terminal) {
1200
+ eventQueue.close();
1201
+ }
1202
+ };
1203
+ }
1204
+ function buildCodexTranslatedReplayKey(event) {
1205
+ const eventType = ensureText(event.type).trim();
1206
+ if (!eventType) {
1207
+ return null;
1208
+ }
1209
+ if (eventType === "turn.completed" || eventType === "turn.failed" || eventType === "turn.interrupted") {
1210
+ return `${eventType}:${ensureText(readProp(event, "turnId")).trim() || ""}`;
1211
+ }
1212
+ if (!eventType.startsWith("item.")) {
1213
+ return null;
1214
+ }
1215
+ const item = toRecord(readProp(event, "item"));
1216
+ if (!item) {
1217
+ return null;
1218
+ }
1219
+ return JSON.stringify({
1220
+ eventType,
1221
+ itemType: ensureText(item.type).trim(),
1222
+ id: ensureText(item.id).trim(),
1223
+ status: ensureText(item.status).trim(),
1224
+ text: ensureText(item.text).trim(),
1225
+ summary: normalizeReplayKeyText(readProp(item, "summary")),
1226
+ content: normalizeReplayKeyText(readProp(item, "content")),
1227
+ command: normalizeReplayKeyText(readProp(item, "command")),
1228
+ result: normalizeReplayKeyText(readProp(item, "result")),
1229
+ output: normalizeReplayKeyText(readProp(item, "output")),
1230
+ aggregatedOutput: normalizeReplayKeyText(readProp(item, "aggregated_output")),
1231
+ error: normalizeReplayKeyText(readProp(item, "error"))
1232
+ });
1233
+ }
1234
+ function normalizeReplayKeyText(value) {
1235
+ if (Array.isArray(value)) {
1236
+ return value.map((entry) => normalizeReplayKeyText(entry)).join("\n");
1237
+ }
1238
+ if (value && typeof value === "object") {
1239
+ try {
1240
+ return JSON.stringify(value);
1241
+ }
1242
+ catch {
1243
+ return String(value);
1244
+ }
1245
+ }
1246
+ return ensureText(value).trim();
1247
+ }
1248
+ function createCodexAppServerNotificationTranslator() {
1249
+ const agentMessageTextById = new Map();
1250
+ const reasoningSummaryPartsById = new Map();
1251
+ const reasoningContentPartsById = new Map();
1252
+ const resetStreamState = () => {
1253
+ agentMessageTextById.clear();
1254
+ reasoningSummaryPartsById.clear();
1255
+ reasoningContentPartsById.clear();
1256
+ };
1257
+ const ensureIndexedTextPart = (store, itemId, index) => {
1258
+ if (!itemId || !Number.isInteger(index) || index < 0) {
1259
+ return null;
1218
1260
  }
1219
- if (status === "interrupted") {
1261
+ const existing = store.get(itemId) ?? [];
1262
+ while (existing.length <= index) {
1263
+ existing.push("");
1264
+ }
1265
+ store.set(itemId, existing);
1266
+ return existing;
1267
+ };
1268
+ const buildReasoningSyntheticItem = (itemId) => {
1269
+ // 这里必须复制一份快照,不能把可变数组引用直接塞进事件队列。
1270
+ // 否则后续 delta 继续追加时,前一帧事件里的 summary/content 也会被同步改掉,
1271
+ // 最终所有帧都会看起来像“最后一帧”,下游稳定消息去重就会把中间增量吃掉。
1272
+ const summary = [...(reasoningSummaryPartsById.get(itemId) ?? [])];
1273
+ const content = [...(reasoningContentPartsById.get(itemId) ?? [])];
1274
+ return {
1275
+ type: "reasoning",
1276
+ id: itemId,
1277
+ summary,
1278
+ content
1279
+ };
1280
+ };
1281
+ const translateAgentMessageDelta = (params) => {
1282
+ const itemId = ensureText(params.itemId).trim();
1283
+ const delta = ensureText(params.delta);
1284
+ if (!itemId || delta.length === 0) {
1220
1285
  return {
1221
- events: [
1222
- ...itemEvents,
1223
- {
1224
- type: "turn.interrupted",
1225
- timestamp: nextTimestamp()
1226
- }
1227
- ],
1228
- terminal: true,
1229
- turnId: ensureText(turn?.id).trim() || null
1286
+ events: [],
1287
+ terminal: false,
1288
+ turnId: ensureText(params.turnId).trim() || null
1230
1289
  };
1231
1290
  }
1291
+ const nextText = `${agentMessageTextById.get(itemId) ?? ""}${delta}`;
1292
+ agentMessageTextById.set(itemId, nextText);
1232
1293
  return {
1233
1294
  events: [
1234
- ...itemEvents,
1235
1295
  {
1236
- type: "turn.completed",
1296
+ type: "item.updated",
1297
+ item: {
1298
+ type: "agent_message",
1299
+ id: itemId,
1300
+ text: nextText
1301
+ },
1237
1302
  timestamp: nextTimestamp()
1238
1303
  }
1239
1304
  ],
1240
- terminal: true,
1241
- turnId: ensureText(turn?.id).trim() || null
1305
+ terminal: false,
1306
+ turnId: ensureText(params.turnId).trim() || null
1242
1307
  };
1243
- }
1244
- if (method === "error") {
1245
- const error = toRecord(params.error);
1246
- const detail = buildCodexAppServerErrorDetail(error);
1247
- if (params.willRetry === true) {
1308
+ };
1309
+ const translateReasoningSummaryPartAdded = (params) => {
1310
+ const itemId = ensureText(params.itemId).trim();
1311
+ const summaryIndex = Math.trunc(Number(params.summaryIndex));
1312
+ ensureIndexedTextPart(reasoningSummaryPartsById, itemId, summaryIndex);
1313
+ return {
1314
+ events: [],
1315
+ terminal: false,
1316
+ turnId: ensureText(params.turnId).trim() || null
1317
+ };
1318
+ };
1319
+ const translateReasoningSummaryTextDelta = (params) => {
1320
+ const itemId = ensureText(params.itemId).trim();
1321
+ const summaryIndex = Math.trunc(Number(params.summaryIndex));
1322
+ const delta = ensureText(params.delta);
1323
+ const parts = ensureIndexedTextPart(reasoningSummaryPartsById, itemId, summaryIndex);
1324
+ if (!parts || delta.length === 0) {
1248
1325
  return {
1249
1326
  events: [],
1250
1327
  terminal: false,
1251
1328
  turnId: ensureText(params.turnId).trim() || null
1252
1329
  };
1253
1330
  }
1331
+ parts[summaryIndex] = `${parts[summaryIndex] ?? ""}${delta}`;
1254
1332
  return {
1255
1333
  events: [
1256
1334
  {
1257
- type: "turn.failed",
1258
- timestamp: nextTimestamp(),
1259
- error: detail
1335
+ type: "item.updated",
1336
+ item: buildReasoningSyntheticItem(itemId),
1337
+ timestamp: nextTimestamp()
1260
1338
  }
1261
1339
  ],
1262
- terminal: true,
1340
+ terminal: false,
1263
1341
  turnId: ensureText(params.turnId).trim() || null
1264
1342
  };
1265
- }
1266
- if (method === "item/started" || method === "item/updated" || method === "item/completed") {
1267
- const item = translateCodexAppServerItem(toRecord(params.item));
1268
- if (!item) {
1343
+ };
1344
+ const translateReasoningTextDelta = (params) => {
1345
+ const itemId = ensureText(params.itemId).trim();
1346
+ const contentIndex = Math.trunc(Number(params.contentIndex));
1347
+ const delta = ensureText(params.delta);
1348
+ const parts = ensureIndexedTextPart(reasoningContentPartsById, itemId, contentIndex);
1349
+ if (!parts || delta.length === 0) {
1269
1350
  return {
1270
1351
  events: [],
1271
1352
  terminal: false,
1272
- turnId: null
1353
+ turnId: ensureText(params.turnId).trim() || null
1273
1354
  };
1274
1355
  }
1356
+ parts[contentIndex] = `${parts[contentIndex] ?? ""}${delta}`;
1275
1357
  return {
1276
1358
  events: [
1277
1359
  {
1278
- type: method === "item/started"
1279
- ? "item.started"
1280
- : method === "item/updated"
1281
- ? "item.updated"
1282
- : "item.completed",
1283
- item,
1360
+ type: "item.updated",
1361
+ item: buildReasoningSyntheticItem(itemId),
1284
1362
  timestamp: nextTimestamp()
1285
1363
  }
1286
1364
  ],
1287
1365
  terminal: false,
1366
+ turnId: ensureText(params.turnId).trim() || null
1367
+ };
1368
+ };
1369
+ return (notification) => {
1370
+ const method = ensureText(notification.method).trim();
1371
+ const params = toRecord(notification.params) ?? {};
1372
+ if (method === "turn/started") {
1373
+ return {
1374
+ events: [],
1375
+ terminal: false,
1376
+ turnId: ensureText(readProp(readProp(params, "turn"), "id")).trim() || null
1377
+ };
1378
+ }
1379
+ if (method === "turn/completed") {
1380
+ const turn = toRecord(params.turn);
1381
+ const status = ensureText(turn?.status).trim();
1382
+ const itemEvents = translateCodexAppServerTurnItems(turn, "item.completed");
1383
+ resetStreamState();
1384
+ if (status === "failed") {
1385
+ return {
1386
+ events: [
1387
+ ...itemEvents,
1388
+ {
1389
+ type: "turn.failed",
1390
+ timestamp: nextTimestamp(),
1391
+ error: ensureText(readProp(turn?.error, "message")).trim() || "codex turn failed"
1392
+ }
1393
+ ],
1394
+ terminal: true,
1395
+ turnId: ensureText(turn?.id).trim() || null
1396
+ };
1397
+ }
1398
+ if (status === "interrupted") {
1399
+ return {
1400
+ events: [
1401
+ ...itemEvents,
1402
+ {
1403
+ type: "turn.interrupted",
1404
+ timestamp: nextTimestamp()
1405
+ }
1406
+ ],
1407
+ terminal: true,
1408
+ turnId: ensureText(turn?.id).trim() || null
1409
+ };
1410
+ }
1411
+ return {
1412
+ events: [
1413
+ ...itemEvents,
1414
+ {
1415
+ type: "turn.completed",
1416
+ timestamp: nextTimestamp()
1417
+ }
1418
+ ],
1419
+ terminal: true,
1420
+ turnId: ensureText(turn?.id).trim() || null
1421
+ };
1422
+ }
1423
+ if (method === "error") {
1424
+ const error = toRecord(params.error);
1425
+ const detail = buildCodexAppServerErrorDetail(error);
1426
+ if (params.willRetry === true) {
1427
+ return {
1428
+ events: [],
1429
+ terminal: false,
1430
+ turnId: ensureText(params.turnId).trim() || null
1431
+ };
1432
+ }
1433
+ resetStreamState();
1434
+ return {
1435
+ events: [
1436
+ {
1437
+ type: "turn.failed",
1438
+ timestamp: nextTimestamp(),
1439
+ error: detail
1440
+ }
1441
+ ],
1442
+ terminal: true,
1443
+ turnId: ensureText(params.turnId).trim() || null
1444
+ };
1445
+ }
1446
+ if (method === "item/agentMessage/delta") {
1447
+ return translateAgentMessageDelta(params);
1448
+ }
1449
+ if (method === "item/reasoning/summaryPartAdded") {
1450
+ return translateReasoningSummaryPartAdded(params);
1451
+ }
1452
+ if (method === "item/reasoning/summaryTextDelta") {
1453
+ return translateReasoningSummaryTextDelta(params);
1454
+ }
1455
+ if (method === "item/reasoning/textDelta") {
1456
+ return translateReasoningTextDelta(params);
1457
+ }
1458
+ if (method === "item/started" || method === "item/updated" || method === "item/completed") {
1459
+ const item = translateCodexAppServerItem(toRecord(params.item));
1460
+ if (!item) {
1461
+ return {
1462
+ events: [],
1463
+ terminal: false,
1464
+ turnId: null
1465
+ };
1466
+ }
1467
+ if (ensureText(item.type).trim() === "agent_message") {
1468
+ const itemId = ensureText(item.id).trim();
1469
+ const itemText = ensureText(item.text);
1470
+ if (itemId) {
1471
+ if (itemText.length > 0) {
1472
+ agentMessageTextById.set(itemId, itemText);
1473
+ }
1474
+ else if (method === "item/completed") {
1475
+ agentMessageTextById.delete(itemId);
1476
+ }
1477
+ }
1478
+ }
1479
+ if (ensureText(item.type).trim() === "reasoning") {
1480
+ const itemId = ensureText(item.id).trim();
1481
+ if (itemId) {
1482
+ const summary = Array.isArray(item.summary)
1483
+ ? item.summary.map((entry) => ensureText(entry))
1484
+ : ensureText(item.summary).trim()
1485
+ ? [ensureText(item.summary)]
1486
+ : [];
1487
+ const content = Array.isArray(item.content)
1488
+ ? item.content.map((entry) => ensureText(entry))
1489
+ : ensureText(item.text).trim()
1490
+ ? [ensureText(item.text)]
1491
+ : [];
1492
+ if (summary.length > 0) {
1493
+ reasoningSummaryPartsById.set(itemId, summary);
1494
+ }
1495
+ else if (method === "item/completed") {
1496
+ reasoningSummaryPartsById.delete(itemId);
1497
+ }
1498
+ if (content.length > 0) {
1499
+ reasoningContentPartsById.set(itemId, content);
1500
+ }
1501
+ else if (method === "item/completed") {
1502
+ reasoningContentPartsById.delete(itemId);
1503
+ }
1504
+ }
1505
+ }
1506
+ return {
1507
+ events: [
1508
+ {
1509
+ type: method === "item/started"
1510
+ ? "item.started"
1511
+ : method === "item/updated"
1512
+ ? "item.updated"
1513
+ : "item.completed",
1514
+ item,
1515
+ timestamp: nextTimestamp()
1516
+ }
1517
+ ],
1518
+ terminal: false,
1519
+ turnId: null
1520
+ };
1521
+ }
1522
+ return {
1523
+ events: [],
1524
+ terminal: false,
1288
1525
  turnId: null
1289
1526
  };
1290
- }
1291
- return {
1292
- events: [],
1293
- terminal: false,
1294
- turnId: null
1295
1527
  };
1296
1528
  }
1297
1529
  function translateCodexAppServerTurnItems(turn, eventType) {
@@ -1754,7 +1986,20 @@ function writeJsonRpcMessage(child, payload) {
1754
1986
  function sendJsonRpcRequest(child, pendingResponses, createRequestId, input) {
1755
1987
  const id = createRequestId();
1756
1988
  return new Promise((resolve, reject) => {
1757
- pendingResponses.set(id, { resolve, reject });
1989
+ const timeout = setTimeout(() => {
1990
+ pendingResponses.delete(id);
1991
+ reject(new Error("SERVER_TIMEOUT"));
1992
+ }, CODEX_APP_SERVER_REQUEST_TIMEOUT_MS);
1993
+ pendingResponses.set(id, {
1994
+ resolve: (value) => {
1995
+ clearTimeout(timeout);
1996
+ resolve(value);
1997
+ },
1998
+ reject: (error) => {
1999
+ clearTimeout(timeout);
2000
+ reject(error);
2001
+ }
2002
+ });
1758
2003
  try {
1759
2004
  writeJsonRpcMessage(child, {
1760
2005
  jsonrpc: "2.0",
@@ -1764,6 +2009,7 @@ function sendJsonRpcRequest(child, pendingResponses, createRequestId, input) {
1764
2009
  });
1765
2010
  }
1766
2011
  catch (error) {
2012
+ clearTimeout(timeout);
1767
2013
  pendingResponses.delete(id);
1768
2014
  reject(error instanceof Error ? error : new Error("CODEX_APP_SERVER_REQUEST_WRITE_FAILED"));
1769
2015
  }
@@ -1801,7 +2047,7 @@ function shouldFallbackCodexContinueFromHistory(error, history) {
1801
2047
  if (history.length === 0) {
1802
2048
  return false;
1803
2049
  }
1804
- return isCodexThreadLoadError(error);
2050
+ return isCodexThreadLoadError(error) || isCodexRequestTimeoutError(error);
1805
2051
  }
1806
2052
  function isCodexThreadLoadError(error) {
1807
2053
  const message = error instanceof Error ? error.message : String(error);
@@ -1809,6 +2055,9 @@ function isCodexThreadLoadError(error) {
1809
2055
  return (normalized.includes("thread not loaded") ||
1810
2056
  normalized.includes("no rollout found for thread id"));
1811
2057
  }
2058
+ function isCodexRequestTimeoutError(error) {
2059
+ return error instanceof Error && error.message === "SERVER_TIMEOUT";
2060
+ }
1812
2061
  function readProp(value, key) {
1813
2062
  if (!value || typeof value !== "object") {
1814
2063
  return null;