@jingyi0605/codingns 0.1.4 → 0.2.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 (239) hide show
  1. package/dist/public/assets/{TerminalPage-4ulgBhv9.js → TerminalPage-BlbQuWi1.js} +1 -1
  2. package/dist/public/assets/gemini-D4G1NbrE.png +0 -0
  3. package/dist/public/assets/index-1VIm8lVL.css +1 -0
  4. package/dist/public/assets/index-Dti93O2S.js +109 -0
  5. package/dist/public/assets/kimi-BWNNSh7e.png +0 -0
  6. package/dist/public/index.html +2 -2
  7. package/dist/server/config/env.d.ts +7 -0
  8. package/dist/server/config/env.js +150 -1
  9. package/dist/server/config/env.js.map +1 -1
  10. package/dist/server/config/opencode-system-probe-helper-process.d.ts +24 -0
  11. package/dist/server/config/opencode-system-probe-helper-process.js +70 -5
  12. package/dist/server/config/opencode-system-probe-helper-process.js.map +1 -1
  13. package/dist/server/modules/butler/butler-action-context-service.d.ts +30 -0
  14. package/dist/server/modules/butler/butler-action-context-service.js +108 -0
  15. package/dist/server/modules/butler/butler-action-context-service.js.map +1 -0
  16. package/dist/server/modules/butler/butler-auth-service.d.ts +17 -0
  17. package/dist/server/modules/butler/butler-auth-service.js +91 -0
  18. package/dist/server/modules/butler/butler-auth-service.js.map +1 -0
  19. package/dist/server/modules/butler/butler-control-action-service.d.ts +65 -0
  20. package/dist/server/modules/butler/butler-control-action-service.js +296 -0
  21. package/dist/server/modules/butler/butler-control-action-service.js.map +1 -0
  22. package/dist/server/modules/butler/butler-control-session-service.d.ts +55 -0
  23. package/dist/server/modules/butler/butler-control-session-service.js +367 -0
  24. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -0
  25. package/dist/server/modules/butler/butler-controller.d.ts +367 -0
  26. package/dist/server/modules/butler/butler-controller.js +475 -0
  27. package/dist/server/modules/butler/butler-controller.js.map +1 -0
  28. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.d.ts +34 -0
  29. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js +77 -0
  30. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js.map +1 -0
  31. package/dist/server/modules/butler/butler-follow-up-scheduler.d.ts +23 -0
  32. package/dist/server/modules/butler/butler-follow-up-scheduler.js +57 -0
  33. package/dist/server/modules/butler/butler-follow-up-scheduler.js.map +1 -0
  34. package/dist/server/modules/butler/butler-follow-up-service.d.ts +86 -0
  35. package/dist/server/modules/butler/butler-follow-up-service.js +948 -0
  36. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -0
  37. package/dist/server/modules/butler/butler-inbox-service.d.ts +35 -0
  38. package/dist/server/modules/butler/butler-inbox-service.js +136 -0
  39. package/dist/server/modules/butler/butler-inbox-service.js.map +1 -0
  40. package/dist/server/modules/butler/butler-notification-service.d.ts +12 -0
  41. package/dist/server/modules/butler/butler-notification-service.js +45 -0
  42. package/dist/server/modules/butler/butler-notification-service.js.map +1 -0
  43. package/dist/server/modules/butler/butler-profile-service.d.ts +26 -0
  44. package/dist/server/modules/butler/butler-profile-service.js +529 -0
  45. package/dist/server/modules/butler/butler-profile-service.js.map +1 -0
  46. package/dist/server/modules/butler/butler-project-service.d.ts +48 -0
  47. package/dist/server/modules/butler/butler-project-service.js +253 -0
  48. package/dist/server/modules/butler/butler-project-service.js.map +1 -0
  49. package/dist/server/modules/butler/butler-session-service.d.ts +79 -0
  50. package/dist/server/modules/butler/butler-session-service.js +503 -0
  51. package/dist/server/modules/butler/butler-session-service.js.map +1 -0
  52. package/dist/server/modules/butler/butler-session-summary-service.d.ts +55 -0
  53. package/dist/server/modules/butler/butler-session-summary-service.js +382 -0
  54. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -0
  55. package/dist/server/modules/butler/context-aggregator.d.ts +187 -0
  56. package/dist/server/modules/butler/context-aggregator.js +807 -0
  57. package/dist/server/modules/butler/context-aggregator.js.map +1 -0
  58. package/dist/server/modules/butler/instruction-adapter.d.ts +28 -0
  59. package/dist/server/modules/butler/instruction-adapter.js +101 -0
  60. package/dist/server/modules/butler/instruction-adapter.js.map +1 -0
  61. package/dist/server/modules/butler/patrol-execution-service.d.ts +47 -0
  62. package/dist/server/modules/butler/patrol-execution-service.js +347 -0
  63. package/dist/server/modules/butler/patrol-execution-service.js.map +1 -0
  64. package/dist/server/modules/butler/patrol-plan-service.d.ts +54 -0
  65. package/dist/server/modules/butler/patrol-plan-service.js +272 -0
  66. package/dist/server/modules/butler/patrol-plan-service.js.map +1 -0
  67. package/dist/server/modules/butler/patrol-run-service.d.ts +60 -0
  68. package/dist/server/modules/butler/patrol-run-service.js +185 -0
  69. package/dist/server/modules/butler/patrol-run-service.js.map +1 -0
  70. package/dist/server/modules/butler/patrol-scheduler.d.ts +36 -0
  71. package/dist/server/modules/butler/patrol-scheduler.js +99 -0
  72. package/dist/server/modules/butler/patrol-scheduler.js.map +1 -0
  73. package/dist/server/modules/butler/project-memory-service.d.ts +30 -0
  74. package/dist/server/modules/butler/project-memory-service.js +103 -0
  75. package/dist/server/modules/butler/project-memory-service.js.map +1 -0
  76. package/dist/server/modules/butler/provider-adapter-registry.d.ts +61 -0
  77. package/dist/server/modules/butler/provider-adapter-registry.js +430 -0
  78. package/dist/server/modules/butler/provider-adapter-registry.js.map +1 -0
  79. package/dist/server/modules/butler/session-summary-instruction-adapter.d.ts +28 -0
  80. package/dist/server/modules/butler/session-summary-instruction-adapter.js +79 -0
  81. package/dist/server/modules/butler/session-summary-instruction-adapter.js.map +1 -0
  82. package/dist/server/modules/butler/session-summary-scheduler.d.ts +23 -0
  83. package/dist/server/modules/butler/session-summary-scheduler.js +57 -0
  84. package/dist/server/modules/butler/session-summary-scheduler.js.map +1 -0
  85. package/dist/server/modules/butler/verification-run-service.d.ts +73 -0
  86. package/dist/server/modules/butler/verification-run-service.js +633 -0
  87. package/dist/server/modules/butler/verification-run-service.js.map +1 -0
  88. package/dist/server/modules/file/file-controller.d.ts +12 -1
  89. package/dist/server/modules/file/file-controller.js +72 -1
  90. package/dist/server/modules/file/file-controller.js.map +1 -1
  91. package/dist/server/modules/file/file-preview-link-service.d.ts +22 -0
  92. package/dist/server/modules/file/file-preview-link-service.js +160 -0
  93. package/dist/server/modules/file/file-preview-link-service.js.map +1 -0
  94. package/dist/server/modules/preferences/profile-service.js +8 -2
  95. package/dist/server/modules/preferences/profile-service.js.map +1 -1
  96. package/dist/server/modules/sessions/claude-runtime-helper-process.js +1 -1
  97. package/dist/server/modules/sessions/claude-runtime-helper-process.js.map +1 -1
  98. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +7 -2
  99. package/dist/server/modules/sessions/codex-app-server-helper-client.js +113 -2
  100. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  101. package/dist/server/modules/sessions/codex-app-server-helper-process.js +106 -1
  102. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  103. package/dist/server/modules/sessions/session-controller.d.ts +24 -1
  104. package/dist/server/modules/sessions/session-controller.js +34 -3
  105. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  106. package/dist/server/modules/sessions/session-history-service.d.ts +47 -2
  107. package/dist/server/modules/sessions/session-history-service.js +881 -56
  108. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  109. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +30 -2
  110. package/dist/server/modules/sessions/session-live-runtime-service.js +584 -159
  111. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  112. package/dist/server/modules/sessions/session-provider-error-mapper.js +94 -0
  113. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  114. package/dist/server/modules/workbench/workbench-service.d.ts +7 -1
  115. package/dist/server/modules/workbench/workbench-service.js +31 -7
  116. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  117. package/dist/server/routes/butler.d.ts +3 -0
  118. package/dist/server/routes/butler.js +54 -0
  119. package/dist/server/routes/butler.js.map +1 -0
  120. package/dist/server/routes/files.js +2 -0
  121. package/dist/server/routes/files.js.map +1 -1
  122. package/dist/server/routes/sessions.js +1 -0
  123. package/dist/server/routes/sessions.js.map +1 -1
  124. package/dist/server/server/create-server.d.ts +65 -0
  125. package/dist/server/server/create-server.js +154 -5
  126. package/dist/server/server/create-server.js.map +1 -1
  127. package/dist/server/shared/utils/command-availability.d.ts +1 -0
  128. package/dist/server/shared/utils/command-availability.js +83 -0
  129. package/dist/server/shared/utils/command-availability.js.map +1 -0
  130. package/dist/server/storage/repositories/butler-control-event-repository.d.ts +8 -0
  131. package/dist/server/storage/repositories/butler-control-event-repository.js +78 -0
  132. package/dist/server/storage/repositories/butler-control-event-repository.js.map +1 -0
  133. package/dist/server/storage/repositories/butler-control-session-repository.d.ts +11 -0
  134. package/dist/server/storage/repositories/butler-control-session-repository.js +86 -0
  135. package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -0
  136. package/dist/server/storage/repositories/butler-follow-up-task-repository.d.ts +16 -0
  137. package/dist/server/storage/repositories/butler-follow-up-task-repository.js +252 -0
  138. package/dist/server/storage/repositories/butler-follow-up-task-repository.js.map +1 -0
  139. package/dist/server/storage/repositories/butler-inbox-item-repository.d.ts +15 -0
  140. package/dist/server/storage/repositories/butler-inbox-item-repository.js +111 -0
  141. package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -0
  142. package/dist/server/storage/repositories/butler-notification-archive-repository.d.ts +9 -0
  143. package/dist/server/storage/repositories/butler-notification-archive-repository.js +48 -0
  144. package/dist/server/storage/repositories/butler-notification-archive-repository.js.map +1 -0
  145. package/dist/server/storage/repositories/butler-profile-repository.d.ts +9 -0
  146. package/dist/server/storage/repositories/butler-profile-repository.js +86 -0
  147. package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -0
  148. package/dist/server/storage/repositories/butler-project-repository.d.ts +14 -0
  149. package/dist/server/storage/repositories/butler-project-repository.js +140 -0
  150. package/dist/server/storage/repositories/butler-project-repository.js.map +1 -0
  151. package/dist/server/storage/repositories/butler-session-repository.d.ts +11 -0
  152. package/dist/server/storage/repositories/butler-session-repository.js +106 -0
  153. package/dist/server/storage/repositories/butler-session-repository.js.map +1 -0
  154. package/dist/server/storage/repositories/butler-session-summary-state-repository.d.ts +8 -0
  155. package/dist/server/storage/repositories/butler-session-summary-state-repository.js +62 -0
  156. package/dist/server/storage/repositories/butler-session-summary-state-repository.js.map +1 -0
  157. package/dist/server/storage/repositories/patrol-plan-repository.d.ts +27 -0
  158. package/dist/server/storage/repositories/patrol-plan-repository.js +119 -0
  159. package/dist/server/storage/repositories/patrol-plan-repository.js.map +1 -0
  160. package/dist/server/storage/repositories/patrol-run-repository.d.ts +28 -0
  161. package/dist/server/storage/repositories/patrol-run-repository.js +121 -0
  162. package/dist/server/storage/repositories/patrol-run-repository.js.map +1 -0
  163. package/dist/server/storage/repositories/project-memory-repository.d.ts +15 -0
  164. package/dist/server/storage/repositories/project-memory-repository.js +150 -0
  165. package/dist/server/storage/repositories/project-memory-repository.js.map +1 -0
  166. package/dist/server/storage/repositories/session-checkpoint-repository.d.ts +9 -0
  167. package/dist/server/storage/repositories/session-checkpoint-repository.js +72 -0
  168. package/dist/server/storage/repositories/session-checkpoint-repository.js.map +1 -0
  169. package/dist/server/storage/repositories/session-fork-repository.d.ts +8 -0
  170. package/dist/server/storage/repositories/session-fork-repository.js +69 -0
  171. package/dist/server/storage/repositories/session-fork-repository.js.map +1 -0
  172. package/dist/server/storage/repositories/session-index-repository.js +40 -2
  173. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  174. package/dist/server/storage/repositories/session-message-origin-repository.d.ts +10 -0
  175. package/dist/server/storage/repositories/session-message-origin-repository.js +93 -0
  176. package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -0
  177. package/dist/server/storage/repositories/verification-run-repository.d.ts +29 -0
  178. package/dist/server/storage/repositories/verification-run-repository.js +125 -0
  179. package/dist/server/storage/repositories/verification-run-repository.js.map +1 -0
  180. package/dist/server/storage/sqlite/client.js +146 -0
  181. package/dist/server/storage/sqlite/client.js.map +1 -1
  182. package/dist/server/storage/sqlite/schema.sql +354 -0
  183. package/dist/server/types/domain.d.ts +286 -2
  184. package/dist/server/ws/ws-server.d.ts +2 -1
  185. package/dist/server/ws/ws-server.js +2 -1
  186. package/dist/server/ws/ws-server.js.map +1 -1
  187. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +4 -0
  188. package/node_modules/@codingns/session-sync-core/dist/index.js +4 -0
  189. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  190. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +18 -0
  191. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +659 -0
  192. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +1 -0
  193. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +11 -0
  194. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +72 -0
  195. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +1 -0
  196. package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +8 -0
  197. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +89 -0
  198. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -1
  199. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +6 -1
  200. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +228 -7
  201. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  202. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +26 -1
  203. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +499 -3
  204. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  205. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +41 -0
  206. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +1175 -0
  207. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -0
  208. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +29 -0
  209. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +578 -0
  210. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -0
  211. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +5 -1
  212. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +271 -4
  213. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  214. package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +1 -0
  215. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +147 -19
  216. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
  217. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +2 -0
  218. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +43 -5
  219. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  220. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +12 -0
  221. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +442 -71
  222. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  223. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +21 -0
  224. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +537 -0
  225. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -0
  226. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +38 -0
  227. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +911 -0
  228. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +1 -0
  229. package/node_modules/@codingns/session-sync-core/dist/services.d.ts +2 -1
  230. package/node_modules/@codingns/session-sync-core/dist/services.js +55 -8
  231. package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
  232. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +6 -0
  233. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +9 -0
  234. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +1 -0
  235. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +27 -0
  236. package/node_modules/@codingns/session-sync-core/package.json +8 -0
  237. package/package.json +1 -1
  238. package/dist/public/assets/index-C5lu52cQ.css +0 -1
  239. package/dist/public/assets/index-WpdUo_Vs.js +0 -108
@@ -2,12 +2,51 @@ import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
3
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
+ import { performance } from "node:perf_hooks";
5
6
  import { basename, dirname, join, resolve } from "node:path";
6
7
  import { createInterface } from "node:readline";
7
- import { DatabaseSync } from "node:sqlite";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
9
- import { appendJsonLine, createRawRef, ensureDirectory, extractTextBlocks, messageIdFromRawRef, nextTimestamp, normalizeWorkspacePath } from "../providers/utils.js";
9
+ import { appendJsonLine, createRawRef, ensureDirectory, extractTextBlocks, messageIdFromRawRef, nextTimestamp, normalizeWorkspacePath, readJsonLines } from "../providers/utils.js";
10
+ import { loadDatabaseSync } from "../sqlite/node-sqlite.js";
10
11
  import { createCodexThreadPermissionOptions } from "./codex-permissions.js";
12
+ const CODEX_RUNTIME_DEBUG_ENABLED = /^(1|true|yes)$/i.test(process.env.CODINGNS_PERF_DEBUG?.trim() ?? "");
13
+ function logCodexRuntimeStep(scope, startedAtMs, detail = {}) {
14
+ if (!CODEX_RUNTIME_DEBUG_ENABLED) {
15
+ return;
16
+ }
17
+ const durationMs = Math.round(performance.now() - startedAtMs);
18
+ const suffix = formatCodexRuntimeDebugDetail(detail);
19
+ console.info(`[perf][codex-runtime] ${scope} ${durationMs}ms${suffix ? ` ${suffix}` : ""}`);
20
+ }
21
+ function formatCodexRuntimeDebugDetail(detail) {
22
+ const entries = Object.entries(detail).filter(([, value]) => value !== undefined);
23
+ if (entries.length === 0) {
24
+ return "";
25
+ }
26
+ return entries
27
+ .map(([key, value]) => `${key}=${formatCodexRuntimeDebugValue(value)}`)
28
+ .join(" ");
29
+ }
30
+ function formatCodexRuntimeDebugValue(value) {
31
+ if (value === null) {
32
+ return "null";
33
+ }
34
+ if (typeof value === "string") {
35
+ return JSON.stringify(value);
36
+ }
37
+ if (typeof value === "number") {
38
+ return Number.isFinite(value) ? String(Math.round(value)) : String(value);
39
+ }
40
+ if (typeof value === "boolean") {
41
+ return value ? "true" : "false";
42
+ }
43
+ try {
44
+ return JSON.stringify(value);
45
+ }
46
+ catch {
47
+ return String(value);
48
+ }
49
+ }
11
50
  export class CodexRuntimeAdapter {
12
51
  options;
13
52
  providerId = "codex";
@@ -16,24 +55,55 @@ export class CodexRuntimeAdapter {
16
55
  }
17
56
  async startSession(request, sink) {
18
57
  const launchedAtMs = Date.now();
58
+ const launchPerfStartedAtMs = performance.now();
19
59
  const transport = this.options.transportFactory
20
60
  ? this.options.transportFactory()
21
61
  : createCodexAppServerTransport(this.options);
62
+ const initializeStartedAtMs = performance.now();
22
63
  await transport.initialize();
64
+ logCodexRuntimeStep("start_session.initialize", initializeStartedAtMs, {
65
+ sessionId: request.sessionId,
66
+ workspacePath: request.workspacePath
67
+ });
23
68
  const abortController = new AbortController();
24
69
  const eventQueue = createAsyncEventQueue();
25
- const startedSession = await transport.startThread(request);
70
+ const resumedSyntheticSession = await this.resumeSyntheticThreadFromHistory(transport, request);
71
+ const startedSession = resumedSyntheticSession ??
72
+ await (async () => {
73
+ const startThreadStartedAtMs = performance.now();
74
+ const started = await transport.startThread(request);
75
+ logCodexRuntimeStep("start_session.thread_start", startThreadStartedAtMs, {
76
+ sessionId: request.sessionId,
77
+ providerSessionId: started.providerSessionId
78
+ });
79
+ return started;
80
+ })();
26
81
  const providerSessionId = startedSession.providerSessionId;
27
- const fallbackRawStoreRef = startedSession.rawStoreRef ??
28
- request.rawStoreRef ??
29
- buildRuntimeRawStoreRef(resolveRuntimeStoreKey(providerSessionId, request.sessionId));
30
- const resolvedBinding = await this.resolveExistingSessionBinding(providerSessionId, fallbackRawStoreRef, request.workspacePath);
31
- const rawStoreRef = resolvedBinding?.rawStoreRef ?? fallbackRawStoreRef;
82
+ const syntheticRawStoreRef = buildRuntimeRawStoreRef(resolveRuntimeStoreKey(providerSessionId, request.sessionId));
83
+ const rawStoreRef = pickAvailableCodexRawStoreRef(resumedSyntheticSession
84
+ ? [resumedSyntheticSession.rawStoreRef]
85
+ : [startedSession.rawStoreRef, request.rawStoreRef], syntheticRawStoreRef);
86
+ logCodexRuntimeStep("start_session.raw_store_ref_ready", launchPerfStartedAtMs, {
87
+ sessionId: request.sessionId,
88
+ providerSessionId,
89
+ synthetic: isSyntheticRawStoreRef(rawStoreRef),
90
+ hasProviderRawStoreRef: Boolean(startedSession.rawStoreRef),
91
+ providerRawStoreRefExists: Boolean(startedSession.rawStoreRef && existsSync(startedSession.rawStoreRef))
92
+ });
32
93
  sink.updateSessionBinding({
33
94
  providerSessionId,
34
95
  rawStoreRef
35
96
  });
97
+ let firstNotificationLogged = false;
36
98
  transport.setNotificationHandler(async (notification) => {
99
+ if (!firstNotificationLogged) {
100
+ firstNotificationLogged = true;
101
+ logCodexRuntimeStep("start_session.first_notification", launchPerfStartedAtMs, {
102
+ sessionId: request.sessionId,
103
+ providerSessionId,
104
+ method: ensureText(notification.method).trim() || null
105
+ });
106
+ }
37
107
  const translated = translateCodexAppServerNotification(notification);
38
108
  if (translated.turnId) {
39
109
  eventQueue.setTurnId(translated.turnId);
@@ -65,7 +135,16 @@ export class CodexRuntimeAdapter {
65
135
  }
66
136
  eventQueue.close();
67
137
  });
138
+ const startTurnStartedAtMs = performance.now();
68
139
  await transport.startTurn(request, providerSessionId);
140
+ logCodexRuntimeStep("start_session.turn_start", startTurnStartedAtMs, {
141
+ sessionId: request.sessionId,
142
+ providerSessionId
143
+ });
144
+ logCodexRuntimeStep("start_session.ready", launchPerfStartedAtMs, {
145
+ sessionId: request.sessionId,
146
+ providerSessionId
147
+ });
69
148
  return {
70
149
  providerSessionId,
71
150
  rawStoreRef,
@@ -77,11 +156,30 @@ export class CodexRuntimeAdapter {
77
156
  transport.close();
78
157
  },
79
158
  isAlive: () => transport.isClosed() === false,
80
- completed: this.runTurn(null, request, sink, providerSessionId, rawStoreRef, abortController, eventQueue.iterator, [], launchedAtMs).finally(() => {
159
+ completed: this.runTurn(null, request, sink, providerSessionId, rawStoreRef, abortController, eventQueue.iterator, [], launchedAtMs, launchPerfStartedAtMs).finally(() => {
81
160
  transport.close();
82
161
  })
83
162
  };
84
163
  }
164
+ async resumeSyntheticThreadFromHistory(transport, request) {
165
+ const history = buildSyntheticResumeHistory(request.rawStoreRef);
166
+ if (history.length === 0) {
167
+ return null;
168
+ }
169
+ const resumeStartedAtMs = performance.now();
170
+ const resumed = await transport.resumeThreadFromHistory({
171
+ providerSessionId: null,
172
+ workspacePath: request.workspacePath,
173
+ history,
174
+ model: request.options.model
175
+ });
176
+ logCodexRuntimeStep("start_session.thread_resume_from_history", resumeStartedAtMs, {
177
+ sessionId: request.sessionId,
178
+ providerSessionId: resumed.providerSessionId,
179
+ messageCount: history.length
180
+ });
181
+ return resumed;
182
+ }
85
183
  async continueSession(request, sink) {
86
184
  const providerSessionId = resolveResumeThreadId(request.providerSessionId, request.rawStoreRef);
87
185
  if (!providerSessionId) {
@@ -90,19 +188,46 @@ export class CodexRuntimeAdapter {
90
188
  const transport = this.options.transportFactory
91
189
  ? this.options.transportFactory()
92
190
  : createCodexAppServerTransport(this.options);
191
+ const runtimeStartedAtMs = performance.now();
192
+ const initializeStartedAtMs = performance.now();
93
193
  await transport.initialize();
94
- const fallbackRawStoreRef = request.rawStoreRef ?? buildRuntimeRawStoreRef(providerSessionId);
95
- const resolvedBinding = await this.resolveExistingSessionBinding(providerSessionId, fallbackRawStoreRef, request.workspacePath);
96
- const resolvedSessionId = resolvedBinding?.providerSessionId ?? providerSessionId;
194
+ logCodexRuntimeStep("continue_session.initialize", initializeStartedAtMs, {
195
+ sessionId: request.sessionId,
196
+ providerSessionId
197
+ });
198
+ const syntheticRawStoreRef = buildRuntimeRawStoreRef(providerSessionId);
199
+ const resolvedSessionId = providerSessionId;
200
+ const resumeThreadStartedAtMs = performance.now();
97
201
  const resumed = await transport.resumeThread(request, resolvedSessionId);
98
- const rawStoreRef = resolvedBinding?.rawStoreRef ?? resumed.rawStoreRef ?? fallbackRawStoreRef;
202
+ logCodexRuntimeStep("continue_session.thread_resume", resumeThreadStartedAtMs, {
203
+ sessionId: request.sessionId,
204
+ providerSessionId: resolvedSessionId
205
+ });
206
+ const rawStoreRef = pickAvailableCodexRawStoreRef([request.rawStoreRef, resumed.rawStoreRef], syntheticRawStoreRef);
99
207
  const abortController = new AbortController();
100
208
  const eventQueue = createAsyncEventQueue();
209
+ logCodexRuntimeStep("continue_session.raw_store_ref_ready", runtimeStartedAtMs, {
210
+ sessionId: request.sessionId,
211
+ providerSessionId: resolvedSessionId,
212
+ synthetic: isSyntheticRawStoreRef(rawStoreRef),
213
+ hasResumedRawStoreRef: Boolean(resumed.rawStoreRef),
214
+ hasRequestRawStoreRef: Boolean(request.rawStoreRef),
215
+ resumedRawStoreRefExists: Boolean(resumed.rawStoreRef && existsSync(resumed.rawStoreRef))
216
+ });
101
217
  sink.updateSessionBinding({
102
218
  providerSessionId: resolvedSessionId,
103
219
  rawStoreRef
104
220
  });
221
+ let firstNotificationLogged = false;
105
222
  transport.setNotificationHandler(async (notification) => {
223
+ if (!firstNotificationLogged) {
224
+ firstNotificationLogged = true;
225
+ logCodexRuntimeStep("continue_session.first_notification", runtimeStartedAtMs, {
226
+ sessionId: request.sessionId,
227
+ providerSessionId: resolvedSessionId,
228
+ method: ensureText(notification.method).trim() || null
229
+ });
230
+ }
106
231
  const translated = translateCodexAppServerNotification(notification);
107
232
  if (translated.turnId) {
108
233
  eventQueue.setTurnId(translated.turnId);
@@ -134,7 +259,16 @@ export class CodexRuntimeAdapter {
134
259
  }
135
260
  eventQueue.close();
136
261
  });
262
+ const startTurnStartedAtMs = performance.now();
137
263
  await transport.startTurn(request, resolvedSessionId);
264
+ logCodexRuntimeStep("continue_session.turn_start", startTurnStartedAtMs, {
265
+ sessionId: request.sessionId,
266
+ providerSessionId: resolvedSessionId
267
+ });
268
+ logCodexRuntimeStep("continue_session.ready", runtimeStartedAtMs, {
269
+ sessionId: request.sessionId,
270
+ providerSessionId: resolvedSessionId
271
+ });
138
272
  return {
139
273
  providerSessionId: resolvedSessionId,
140
274
  rawStoreRef,
@@ -151,7 +285,7 @@ export class CodexRuntimeAdapter {
151
285
  })
152
286
  };
153
287
  }
154
- async runTurn(thread, request, sink, providerSessionId, rawStoreRef, abortController, preparedEvents, bufferedEvents = [], launchedAtMs = Date.now()) {
288
+ async runTurn(thread, request, sink, providerSessionId, rawStoreRef, abortController, preparedEvents, bufferedEvents = [], launchedAtMs = Date.now(), launchPerfStartedAtMs = performance.now()) {
155
289
  const context = {
156
290
  providerSessionId,
157
291
  rawStoreRef,
@@ -159,10 +293,13 @@ export class CodexRuntimeAdapter {
159
293
  // 否则前端会把新 assistant/tool 消息排到旧消息前面,表现成用户消息一直挂在底部。
160
294
  sequence: Math.max(0, request.sequenceBase ?? 0),
161
295
  toolNameByCallId: new Map(),
296
+ stableMessageRefByIdentity: new Map(),
297
+ lastSignatureByIdentity: new Map(),
162
298
  sink,
163
299
  workspacePath: request.workspacePath,
164
300
  firstUserMessage: request.options.content,
165
- launchedAtMs
301
+ launchedAtMs,
302
+ launchPerfStartedAtMs
166
303
  };
167
304
  try {
168
305
  await this.refreshSessionBindingIfNeeded(context);
@@ -219,6 +356,13 @@ export class CodexRuntimeAdapter {
219
356
  if (eventType.length === 0) {
220
357
  return;
221
358
  }
359
+ if (context.lastSignatureByIdentity.size === 0 && eventType.startsWith("item.")) {
360
+ logCodexRuntimeStep("turn.first_item_event", context.launchPerfStartedAtMs, {
361
+ sessionId: request.sessionId,
362
+ providerSessionId: context.providerSessionId,
363
+ eventType
364
+ });
365
+ }
222
366
  if (eventType === "turn.completed") {
223
367
  await context.sink.emit({
224
368
  type: "complete",
@@ -265,36 +409,30 @@ export class CodexRuntimeAdapter {
265
409
  if (itemType.length === 0) {
266
410
  return;
267
411
  }
268
- if (itemType === "agent_message" && eventType === "item.completed") {
412
+ if (itemType === "agent_message" &&
413
+ (eventType === "item.updated" || eventType === "item.completed")) {
269
414
  const content = pickFirstNonEmpty(ensureText(readProp(item, "text")).trim(), extractTextBlocks(readProp(item, "content")).trim());
270
415
  if (content.length > 0) {
271
- await context.sink.emit({
272
- type: "message",
273
- message: this.buildMessage(request, context, {
274
- role: "assistant",
275
- kind: "text",
276
- content
277
- }),
278
- providerSessionId: context.providerSessionId,
279
- rawStoreRef: context.rawStoreRef,
280
- timestamp: pickTimestamp(item, event)
416
+ await this.emitStableMessage(context, {
417
+ identity: `assistant:text:${ensureText(readProp(item, "id")).trim() || "default"}`,
418
+ timestamp: pickTimestamp(item, event),
419
+ role: "assistant",
420
+ kind: "text",
421
+ content
281
422
  });
282
423
  }
283
424
  return;
284
425
  }
285
- if (itemType === "reasoning" && eventType === "item.completed") {
426
+ if (itemType === "reasoning" &&
427
+ (eventType === "item.updated" || eventType === "item.completed")) {
286
428
  const content = pickFirstNonEmpty(ensureText(readProp(item, "text")).trim(), extractTextBlocks(readProp(item, "summary")).trim(), extractTextBlocks(readProp(item, "content")).trim());
287
429
  if (content.length > 0) {
288
- await context.sink.emit({
289
- type: "message",
290
- message: this.buildMessage(request, context, {
291
- role: "assistant",
292
- kind: "thinking",
293
- content
294
- }),
295
- providerSessionId: context.providerSessionId,
296
- rawStoreRef: context.rawStoreRef,
297
- timestamp: pickTimestamp(item, event)
430
+ await this.emitStableMessage(context, {
431
+ identity: `assistant:thinking:${ensureText(readProp(item, "id")).trim() || "default"}`,
432
+ timestamp: pickTimestamp(item, event),
433
+ role: "assistant",
434
+ kind: "thinking",
435
+ content
298
436
  });
299
437
  }
300
438
  return;
@@ -315,17 +453,37 @@ export class CodexRuntimeAdapter {
315
453
  status: "running"
316
454
  };
317
455
  context.toolNameByCallId.set(callId, name);
318
- await context.sink.emit({
319
- type: "message",
320
- message: this.buildMessage(request, context, {
321
- role: "tool",
322
- kind: "tool_call",
323
- content: input,
324
- toolCall
325
- }),
326
- providerSessionId: context.providerSessionId,
327
- rawStoreRef: context.rawStoreRef,
328
- timestamp: pickTimestamp(item, event)
456
+ await this.emitStableMessage(context, {
457
+ identity: `tool:call:${callId}`,
458
+ timestamp: pickTimestamp(item, event),
459
+ role: "tool",
460
+ kind: "tool_call",
461
+ content: input,
462
+ toolCall
463
+ });
464
+ return;
465
+ }
466
+ if (eventType === "item.updated") {
467
+ const output = pickFirstNonEmpty(extractTextBlocks(readProp(item, "result")).trim(), extractTextBlocks(readProp(item, "output")).trim(), extractTextBlocks(readProp(item, "aggregated_output")).trim(), extractTextBlocks(readProp(item, "error")).trim());
468
+ if (output.length === 0) {
469
+ return;
470
+ }
471
+ const knownName = context.toolNameByCallId.get(callId) ?? name;
472
+ context.toolNameByCallId.set(callId, knownName);
473
+ await this.emitStableMessage(context, {
474
+ identity: `tool:result:${callId}`,
475
+ timestamp: pickTimestamp(item, event),
476
+ role: "tool",
477
+ kind: "tool_result",
478
+ content: output,
479
+ toolCall: {
480
+ callId,
481
+ name: knownName,
482
+ input: "",
483
+ output,
484
+ error: null,
485
+ status: "running"
486
+ }
329
487
  });
330
488
  return;
331
489
  }
@@ -341,26 +499,44 @@ export class CodexRuntimeAdapter {
341
499
  error: success ? null : output,
342
500
  status: success ? "completed" : "failed"
343
501
  };
344
- await context.sink.emit({
345
- type: "message",
346
- message: this.buildMessage(request, context, {
347
- role: "tool",
348
- kind: "tool_result",
349
- content: output,
350
- toolCall
351
- }),
352
- providerSessionId: context.providerSessionId,
353
- rawStoreRef: context.rawStoreRef,
354
- timestamp: pickTimestamp(item, event)
502
+ await this.emitStableMessage(context, {
503
+ identity: `tool:result:${callId}`,
504
+ timestamp: pickTimestamp(item, event),
505
+ role: "tool",
506
+ kind: "tool_result",
507
+ content: output,
508
+ toolCall
355
509
  });
356
510
  }
357
511
  }
512
+ async emitStableMessage(context, input) {
513
+ const message = this.buildMessage(context, {
514
+ role: input.role,
515
+ kind: input.kind,
516
+ content: input.content,
517
+ toolCall: input.toolCall ?? null,
518
+ stableIdentity: input.identity
519
+ });
520
+ const signature = buildCodexMessageSignature(message);
521
+ if (context.lastSignatureByIdentity.get(input.identity) === signature) {
522
+ return;
523
+ }
524
+ context.lastSignatureByIdentity.set(input.identity, signature);
525
+ await context.sink.emit({
526
+ type: "message",
527
+ message,
528
+ providerSessionId: context.providerSessionId,
529
+ rawStoreRef: context.rawStoreRef,
530
+ timestamp: input.timestamp,
531
+ rawEventRef: message.rawRef
532
+ });
533
+ }
358
534
  async refreshSessionBindingIfNeeded(context) {
359
535
  if (!isSyntheticRawStoreRef(context.rawStoreRef)) {
360
536
  return;
361
537
  }
362
- const resolved = await this.resolveExistingSessionBinding(context.providerSessionId, context.rawStoreRef, context.workspacePath) ??
363
- await this.resolveLaunchedSessionBinding(context.workspacePath, context.firstUserMessage, context.launchedAtMs);
538
+ const resolved = await this.resolveLaunchedSessionBinding(context.workspacePath, context.firstUserMessage, context.launchedAtMs) ??
539
+ await this.resolveExistingSessionBinding(context.providerSessionId, context.rawStoreRef, context.workspacePath);
364
540
  if (!resolved ||
365
541
  (resolved.providerSessionId === context.providerSessionId &&
366
542
  resolved.rawStoreRef === context.rawStoreRef)) {
@@ -465,6 +641,7 @@ export class CodexRuntimeAdapter {
465
641
  if (!dbPath) {
466
642
  return null;
467
643
  }
644
+ const DatabaseSync = loadDatabaseSync();
468
645
  let db = null;
469
646
  try {
470
647
  db = new DatabaseSync(dbPath, { open: true, readOnly: true });
@@ -499,11 +676,14 @@ export class CodexRuntimeAdapter {
499
676
  getCodexHomeDir() {
500
677
  return this.options.homeDir?.trim() || process.env.CODINGNS_CODEX_HOME || join(homedir(), ".codex");
501
678
  }
502
- buildMessage(request, context, input) {
503
- context.sequence += 1;
504
- const rawRef = createRawRef(this.providerId, context.rawStoreRef, context.sequence);
679
+ buildMessage(context, input) {
680
+ const stableRef = this.resolveStableMessageRef(context, input.stableIdentity ?? null);
681
+ const rawRef = stableRef?.rawRef ??
682
+ createRawRef(this.providerId, context.rawStoreRef, ++context.sequence);
683
+ const sequence = stableRef?.sequence ?? context.sequence;
684
+ const messageId = stableRef?.messageId ?? messageIdFromRawRef(rawRef);
505
685
  return {
506
- messageId: messageIdFromRawRef(rawRef),
686
+ messageId,
507
687
  provider: this.providerId,
508
688
  providerSessionId: context.providerSessionId,
509
689
  role: input.role,
@@ -511,10 +691,28 @@ export class CodexRuntimeAdapter {
511
691
  content: input.content,
512
692
  toolCall: input.toolCall ?? null,
513
693
  timestamp: nextTimestamp(),
514
- sequence: context.sequence,
694
+ sequence,
515
695
  rawRef
516
696
  };
517
697
  }
698
+ resolveStableMessageRef(context, stableIdentity) {
699
+ if (!stableIdentity) {
700
+ return null;
701
+ }
702
+ const existing = context.stableMessageRefByIdentity.get(stableIdentity);
703
+ if (existing) {
704
+ return existing;
705
+ }
706
+ context.sequence += 1;
707
+ const rawRef = createRawRef(this.providerId, context.rawStoreRef, context.sequence);
708
+ const created = {
709
+ sequence: context.sequence,
710
+ rawRef,
711
+ messageId: messageIdFromRawRef(rawRef)
712
+ };
713
+ context.stableMessageRefByIdentity.set(stableIdentity, created);
714
+ return created;
715
+ }
518
716
  async awaitThreadStarted(thread, events, workspacePath, firstUserMessage, launchedAtMs) {
519
717
  const bufferedEvents = [];
520
718
  while (true) {
@@ -658,6 +856,7 @@ function createCodexAppServerTransport(options) {
658
856
  });
659
857
  return {
660
858
  async initialize() {
859
+ const startedAtMs = performance.now();
661
860
  await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("initialize", () => ++requestSequence), {
662
861
  method: "initialize",
663
862
  params: {
@@ -673,8 +872,10 @@ function createCodexAppServerTransport(options) {
673
872
  method: "initialized",
674
873
  params: {}
675
874
  });
875
+ logCodexRuntimeStep("transport.initialize", startedAtMs);
676
876
  },
677
877
  async startThread(request) {
878
+ const startedAtMs = performance.now();
678
879
  const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("thread-start", () => ++requestSequence), {
679
880
  method: "thread/start",
680
881
  params: createThreadStartParams(request)
@@ -685,29 +886,64 @@ function createCodexAppServerTransport(options) {
685
886
  throw new Error("CODEX_APP_SERVER_THREAD_ID_MISSING");
686
887
  }
687
888
  activeThreadId = providerSessionId;
889
+ logCodexRuntimeStep("transport.thread_start", startedAtMs, {
890
+ sessionId: request.sessionId,
891
+ providerSessionId
892
+ });
688
893
  return {
689
894
  providerSessionId,
690
895
  rawStoreRef: normalizeText(thread?.path) || null
691
896
  };
692
897
  },
693
898
  async resumeThread(request, providerSessionId) {
899
+ const startedAtMs = performance.now();
694
900
  const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("thread-resume", () => ++requestSequence), {
695
901
  method: "thread/resume",
696
902
  params: createThreadResumeParams(request, providerSessionId)
697
903
  });
698
904
  const thread = toRecord(result.thread);
699
905
  activeThreadId = ensureText(thread?.id).trim() || providerSessionId;
906
+ logCodexRuntimeStep("transport.thread_resume", startedAtMs, {
907
+ sessionId: request.sessionId,
908
+ providerSessionId: activeThreadId
909
+ });
700
910
  return {
701
911
  providerSessionId: activeThreadId,
702
912
  rawStoreRef: normalizeText(thread?.path) || null
703
913
  };
704
914
  },
915
+ async resumeThreadFromHistory(input) {
916
+ const startedAtMs = performance.now();
917
+ const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("thread-resume-history", () => ++requestSequence), {
918
+ method: "thread/resume",
919
+ params: createThreadResumeWithHistoryParams(input)
920
+ });
921
+ const thread = toRecord(result.thread);
922
+ const providerSessionId = ensureText(thread?.id).trim();
923
+ if (!providerSessionId) {
924
+ throw new Error("CODEX_APP_SERVER_THREAD_ID_MISSING");
925
+ }
926
+ activeThreadId = providerSessionId;
927
+ logCodexRuntimeStep("transport.thread_resume_from_history", startedAtMs, {
928
+ providerSessionId
929
+ });
930
+ return {
931
+ providerSessionId,
932
+ rawStoreRef: normalizeText(thread?.path) || null
933
+ };
934
+ },
705
935
  async startTurn(request, providerSessionId) {
936
+ const startedAtMs = performance.now();
706
937
  const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("turn-start", () => ++requestSequence), {
707
938
  method: "turn/start",
708
939
  params: createTurnStartParams(request, providerSessionId)
709
940
  });
710
941
  activeTurnId = ensureText(readProp(readProp(result, "turn"), "id")).trim() || activeTurnId;
942
+ logCodexRuntimeStep("transport.turn_start", startedAtMs, {
943
+ sessionId: request.sessionId,
944
+ providerSessionId,
945
+ turnId: activeTurnId
946
+ });
711
947
  },
712
948
  async interruptTurn() {
713
949
  if (!activeThreadId || !activeTurnId) {
@@ -849,7 +1085,27 @@ function translateCodexAppServerNotification(notification) {
849
1085
  turnId: ensureText(turn?.id).trim() || null
850
1086
  };
851
1087
  }
852
- if (method === "item/started" || method === "item/completed") {
1088
+ if (method === "error") {
1089
+ const error = toRecord(params.error);
1090
+ const detail = buildCodexAppServerErrorDetail(error);
1091
+ if (params.willRetry === true) {
1092
+ return {
1093
+ event: null,
1094
+ terminal: false,
1095
+ turnId: ensureText(params.turnId).trim() || null
1096
+ };
1097
+ }
1098
+ return {
1099
+ event: {
1100
+ type: "turn.failed",
1101
+ timestamp: nextTimestamp(),
1102
+ error: detail
1103
+ },
1104
+ terminal: true,
1105
+ turnId: ensureText(params.turnId).trim() || null
1106
+ };
1107
+ }
1108
+ if (method === "item/started" || method === "item/updated" || method === "item/completed") {
853
1109
  const item = translateCodexAppServerItem(toRecord(params.item));
854
1110
  if (!item) {
855
1111
  return {
@@ -860,7 +1116,11 @@ function translateCodexAppServerNotification(notification) {
860
1116
  }
861
1117
  return {
862
1118
  event: {
863
- type: method === "item/started" ? "item.started" : "item.completed",
1119
+ type: method === "item/started"
1120
+ ? "item.started"
1121
+ : method === "item/updated"
1122
+ ? "item.updated"
1123
+ : "item.completed",
864
1124
  item,
865
1125
  timestamp: nextTimestamp()
866
1126
  },
@@ -874,6 +1134,31 @@ function translateCodexAppServerNotification(notification) {
874
1134
  turnId: null
875
1135
  };
876
1136
  }
1137
+ function buildCodexAppServerErrorDetail(error) {
1138
+ const message = ensureText(error?.message).trim();
1139
+ const additionalDetails = ensureText(error?.additionalDetails).trim();
1140
+ if (message && additionalDetails && !message.includes(additionalDetails)) {
1141
+ return `${message}\n${additionalDetails}`;
1142
+ }
1143
+ return message || additionalDetails || "codex app-server error";
1144
+ }
1145
+ function buildCodexMessageSignature(message) {
1146
+ return JSON.stringify({
1147
+ role: message.role,
1148
+ kind: message.kind,
1149
+ content: message.content,
1150
+ toolCall: message.toolCall
1151
+ ? {
1152
+ callId: message.toolCall.callId,
1153
+ name: message.toolCall.name,
1154
+ input: message.toolCall.input,
1155
+ output: message.toolCall.output,
1156
+ error: message.toolCall.error,
1157
+ status: message.toolCall.status
1158
+ }
1159
+ : null
1160
+ });
1161
+ }
877
1162
  function translateCodexAppServerItem(item) {
878
1163
  if (!item) {
879
1164
  return null;
@@ -886,15 +1171,15 @@ function translateCodexAppServerItem(item) {
886
1171
  return {
887
1172
  type: "agent_message",
888
1173
  id: item.id,
889
- text: item.text
1174
+ text: ensureText(item.text).trim()
890
1175
  };
891
1176
  }
892
1177
  if (itemType === "reasoning") {
893
1178
  return {
894
1179
  type: "reasoning",
895
1180
  id: item.id,
896
- text: Array.isArray(item.content) ? item.content.join("\n") : "",
897
- summary: Array.isArray(item.summary) ? item.summary.join("\n") : ""
1181
+ text: Array.isArray(item.content) ? item.content.join("\n") : ensureText(item.text).trim(),
1182
+ summary: Array.isArray(item.summary) ? item.summary.join("\n") : ensureText(item.summary).trim()
898
1183
  };
899
1184
  }
900
1185
  if (itemType === "commandExecution") {
@@ -999,6 +1284,20 @@ function createThreadResumeParams(request, providerSessionId) {
999
1284
  }
1000
1285
  return params;
1001
1286
  }
1287
+ function createThreadResumeWithHistoryParams(input) {
1288
+ const params = {
1289
+ threadId: input.providerSessionId && input.providerSessionId.trim().length > 0
1290
+ ? input.providerSessionId.trim()
1291
+ : "__history_resume__",
1292
+ cwd: input.workspacePath,
1293
+ history: input.history,
1294
+ approvalsReviewer: "user"
1295
+ };
1296
+ if (input.model) {
1297
+ params.model = input.model;
1298
+ }
1299
+ return params;
1300
+ }
1002
1301
  function createTurnStartParams(request, providerSessionId) {
1003
1302
  const permissionOptions = createCodexThreadPermissionOptions(request.options.permissionMode ?? "default");
1004
1303
  const params = {
@@ -1116,6 +1415,18 @@ function resolveNodeModulesCandidate(currentDirectory, relativeSegments) {
1116
1415
  function buildRuntimeRawStoreRef(providerSessionId) {
1117
1416
  return resolve(process.cwd(), "runtime", "codex", `${providerSessionId}.stream`);
1118
1417
  }
1418
+ function pickAvailableCodexRawStoreRef(candidates, fallbackRawStoreRef) {
1419
+ for (const candidate of candidates) {
1420
+ const normalized = candidate?.trim();
1421
+ if (!normalized) {
1422
+ continue;
1423
+ }
1424
+ if (existsSync(normalized)) {
1425
+ return normalized;
1426
+ }
1427
+ }
1428
+ return fallbackRawStoreRef;
1429
+ }
1119
1430
  function resolveRuntimeStoreKey(providerSessionId, sessionId) {
1120
1431
  return providerSessionId.trim() || sessionId;
1121
1432
  }
@@ -1204,6 +1515,66 @@ function resolveResumeThreadId(providerSessionId, rawStoreRef) {
1204
1515
  }
1205
1516
  return null;
1206
1517
  }
1518
+ function buildSyntheticResumeHistory(rawStoreRef) {
1519
+ const filePath = ensureText(rawStoreRef).trim();
1520
+ if (!filePath || !existsSync(filePath)) {
1521
+ return [];
1522
+ }
1523
+ const threadId = readThreadIdFromRawStore(filePath);
1524
+ if (!threadId || looksLikeCodexThreadId(threadId)) {
1525
+ return [];
1526
+ }
1527
+ const history = [];
1528
+ for (const recordEntry of readJsonLines(filePath)) {
1529
+ const record = toRecord(recordEntry.data) ?? {};
1530
+ const recordType = ensureText(record.type).trim();
1531
+ if (recordType === "event_msg") {
1532
+ const payload = toRecord(record.payload) ?? {};
1533
+ const eventType = ensureText(payload.type).trim();
1534
+ const content = ensureText(payload.message).trim();
1535
+ if (content.length === 0) {
1536
+ continue;
1537
+ }
1538
+ if (eventType === "user_message") {
1539
+ history.push(createResumeHistoryMessage("user", content));
1540
+ continue;
1541
+ }
1542
+ if (eventType === "agent_message") {
1543
+ history.push(createResumeHistoryMessage("assistant", content));
1544
+ }
1545
+ continue;
1546
+ }
1547
+ if (recordType !== "response_item") {
1548
+ continue;
1549
+ }
1550
+ const payload = toRecord(record.payload) ?? {};
1551
+ if (ensureText(payload.type).trim() !== "message") {
1552
+ continue;
1553
+ }
1554
+ const role = ensureText(payload.role).trim();
1555
+ if (role !== "user" && role !== "assistant") {
1556
+ continue;
1557
+ }
1558
+ const content = extractTextBlocks(payload.content).trim();
1559
+ if (content.length === 0) {
1560
+ continue;
1561
+ }
1562
+ history.push(createResumeHistoryMessage(role, content));
1563
+ }
1564
+ return history;
1565
+ }
1566
+ function createResumeHistoryMessage(role, content) {
1567
+ return {
1568
+ type: "message",
1569
+ role,
1570
+ content: [
1571
+ {
1572
+ type: role === "user" ? "input_text" : "output_text",
1573
+ text: content
1574
+ }
1575
+ ]
1576
+ };
1577
+ }
1207
1578
  function readProp(value, key) {
1208
1579
  if (!value || typeof value !== "object") {
1209
1580
  return null;