@jingyi0605/codingns 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/dist/public/assets/{TerminalPage-4ulgBhv9.js → TerminalPage-4p6EBqrR.js} +1 -1
  2. package/dist/public/assets/gemini-D4G1NbrE.png +0 -0
  3. package/dist/public/assets/index-CxeghocY.css +1 -0
  4. package/dist/public/assets/index-DXusStl0.js +108 -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 +6 -0
  8. package/dist/server/config/env.js +145 -0
  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/preferences/profile-service.js +8 -2
  89. package/dist/server/modules/preferences/profile-service.js.map +1 -1
  90. package/dist/server/modules/sessions/claude-runtime-helper-process.js +1 -1
  91. package/dist/server/modules/sessions/claude-runtime-helper-process.js.map +1 -1
  92. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +5 -1
  93. package/dist/server/modules/sessions/codex-app-server-helper-client.js +10 -2
  94. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  95. package/dist/server/modules/sessions/session-controller.d.ts +3 -1
  96. package/dist/server/modules/sessions/session-controller.js +11 -2
  97. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  98. package/dist/server/modules/sessions/session-history-service.d.ts +14 -1
  99. package/dist/server/modules/sessions/session-history-service.js +291 -30
  100. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  101. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +25 -2
  102. package/dist/server/modules/sessions/session-live-runtime-service.js +526 -158
  103. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  104. package/dist/server/modules/sessions/session-provider-error-mapper.js +28 -0
  105. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  106. package/dist/server/modules/workbench/workbench-service.d.ts +7 -1
  107. package/dist/server/modules/workbench/workbench-service.js +31 -7
  108. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  109. package/dist/server/routes/butler.d.ts +3 -0
  110. package/dist/server/routes/butler.js +54 -0
  111. package/dist/server/routes/butler.js.map +1 -0
  112. package/dist/server/server/create-server.d.ts +61 -0
  113. package/dist/server/server/create-server.js +148 -4
  114. package/dist/server/server/create-server.js.map +1 -1
  115. package/dist/server/storage/repositories/butler-control-event-repository.d.ts +8 -0
  116. package/dist/server/storage/repositories/butler-control-event-repository.js +78 -0
  117. package/dist/server/storage/repositories/butler-control-event-repository.js.map +1 -0
  118. package/dist/server/storage/repositories/butler-control-session-repository.d.ts +11 -0
  119. package/dist/server/storage/repositories/butler-control-session-repository.js +86 -0
  120. package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -0
  121. package/dist/server/storage/repositories/butler-follow-up-task-repository.d.ts +16 -0
  122. package/dist/server/storage/repositories/butler-follow-up-task-repository.js +252 -0
  123. package/dist/server/storage/repositories/butler-follow-up-task-repository.js.map +1 -0
  124. package/dist/server/storage/repositories/butler-inbox-item-repository.d.ts +15 -0
  125. package/dist/server/storage/repositories/butler-inbox-item-repository.js +111 -0
  126. package/dist/server/storage/repositories/butler-inbox-item-repository.js.map +1 -0
  127. package/dist/server/storage/repositories/butler-notification-archive-repository.d.ts +9 -0
  128. package/dist/server/storage/repositories/butler-notification-archive-repository.js +48 -0
  129. package/dist/server/storage/repositories/butler-notification-archive-repository.js.map +1 -0
  130. package/dist/server/storage/repositories/butler-profile-repository.d.ts +9 -0
  131. package/dist/server/storage/repositories/butler-profile-repository.js +86 -0
  132. package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -0
  133. package/dist/server/storage/repositories/butler-project-repository.d.ts +14 -0
  134. package/dist/server/storage/repositories/butler-project-repository.js +140 -0
  135. package/dist/server/storage/repositories/butler-project-repository.js.map +1 -0
  136. package/dist/server/storage/repositories/butler-session-repository.d.ts +11 -0
  137. package/dist/server/storage/repositories/butler-session-repository.js +106 -0
  138. package/dist/server/storage/repositories/butler-session-repository.js.map +1 -0
  139. package/dist/server/storage/repositories/butler-session-summary-state-repository.d.ts +8 -0
  140. package/dist/server/storage/repositories/butler-session-summary-state-repository.js +62 -0
  141. package/dist/server/storage/repositories/butler-session-summary-state-repository.js.map +1 -0
  142. package/dist/server/storage/repositories/patrol-plan-repository.d.ts +27 -0
  143. package/dist/server/storage/repositories/patrol-plan-repository.js +119 -0
  144. package/dist/server/storage/repositories/patrol-plan-repository.js.map +1 -0
  145. package/dist/server/storage/repositories/patrol-run-repository.d.ts +28 -0
  146. package/dist/server/storage/repositories/patrol-run-repository.js +121 -0
  147. package/dist/server/storage/repositories/patrol-run-repository.js.map +1 -0
  148. package/dist/server/storage/repositories/project-memory-repository.d.ts +15 -0
  149. package/dist/server/storage/repositories/project-memory-repository.js +150 -0
  150. package/dist/server/storage/repositories/project-memory-repository.js.map +1 -0
  151. package/dist/server/storage/repositories/session-checkpoint-repository.d.ts +9 -0
  152. package/dist/server/storage/repositories/session-checkpoint-repository.js +72 -0
  153. package/dist/server/storage/repositories/session-checkpoint-repository.js.map +1 -0
  154. package/dist/server/storage/repositories/session-message-origin-repository.d.ts +10 -0
  155. package/dist/server/storage/repositories/session-message-origin-repository.js +93 -0
  156. package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -0
  157. package/dist/server/storage/repositories/verification-run-repository.d.ts +29 -0
  158. package/dist/server/storage/repositories/verification-run-repository.js +125 -0
  159. package/dist/server/storage/repositories/verification-run-repository.js.map +1 -0
  160. package/dist/server/storage/sqlite/client.js +39 -0
  161. package/dist/server/storage/sqlite/client.js.map +1 -1
  162. package/dist/server/storage/sqlite/schema.sql +324 -0
  163. package/dist/server/types/domain.d.ts +261 -1
  164. package/dist/server/ws/ws-server.d.ts +2 -1
  165. package/dist/server/ws/ws-server.js +2 -1
  166. package/dist/server/ws/ws-server.js.map +1 -1
  167. package/node_modules/@codingns/session-sync-core/dist/index.d.ts +4 -0
  168. package/node_modules/@codingns/session-sync-core/dist/index.js +4 -0
  169. package/node_modules/@codingns/session-sync-core/dist/index.js.map +1 -1
  170. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +18 -0
  171. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +659 -0
  172. package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +1 -0
  173. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +11 -0
  174. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +72 -0
  175. package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +1 -0
  176. package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +8 -0
  177. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +89 -0
  178. package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +1 -1
  179. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +4 -1
  180. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  181. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +41 -0
  182. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +1086 -0
  183. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -0
  184. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +29 -0
  185. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +578 -0
  186. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -0
  187. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +2 -1
  188. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  189. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +30 -2
  190. package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
  191. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +2 -0
  192. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +43 -5
  193. package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
  194. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +2 -0
  195. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +320 -69
  196. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  197. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +21 -0
  198. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +537 -0
  199. package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +1 -0
  200. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +38 -0
  201. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +911 -0
  202. package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +1 -0
  203. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +6 -0
  204. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +9 -0
  205. package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +1 -0
  206. package/node_modules/@codingns/session-sync-core/package.json +8 -0
  207. package/package.json +1 -1
  208. package/dist/public/assets/index-C5lu52cQ.css +0 -1
  209. 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
9
  import { appendJsonLine, createRawRef, ensureDirectory, extractTextBlocks, messageIdFromRawRef, nextTimestamp, normalizeWorkspacePath } 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,48 @@ 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();
70
+ const startThreadStartedAtMs = performance.now();
25
71
  const startedSession = await transport.startThread(request);
72
+ logCodexRuntimeStep("start_session.thread_start", startThreadStartedAtMs, {
73
+ sessionId: request.sessionId,
74
+ providerSessionId: startedSession.providerSessionId
75
+ });
26
76
  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;
77
+ const syntheticRawStoreRef = buildRuntimeRawStoreRef(resolveRuntimeStoreKey(providerSessionId, request.sessionId));
78
+ const rawStoreRef = pickAvailableCodexRawStoreRef([startedSession.rawStoreRef, request.rawStoreRef], syntheticRawStoreRef);
79
+ logCodexRuntimeStep("start_session.raw_store_ref_ready", launchPerfStartedAtMs, {
80
+ sessionId: request.sessionId,
81
+ providerSessionId,
82
+ synthetic: isSyntheticRawStoreRef(rawStoreRef),
83
+ hasProviderRawStoreRef: Boolean(startedSession.rawStoreRef),
84
+ providerRawStoreRefExists: Boolean(startedSession.rawStoreRef && existsSync(startedSession.rawStoreRef))
85
+ });
32
86
  sink.updateSessionBinding({
33
87
  providerSessionId,
34
88
  rawStoreRef
35
89
  });
90
+ let firstNotificationLogged = false;
36
91
  transport.setNotificationHandler(async (notification) => {
92
+ if (!firstNotificationLogged) {
93
+ firstNotificationLogged = true;
94
+ logCodexRuntimeStep("start_session.first_notification", launchPerfStartedAtMs, {
95
+ sessionId: request.sessionId,
96
+ providerSessionId,
97
+ method: ensureText(notification.method).trim() || null
98
+ });
99
+ }
37
100
  const translated = translateCodexAppServerNotification(notification);
38
101
  if (translated.turnId) {
39
102
  eventQueue.setTurnId(translated.turnId);
@@ -65,7 +128,16 @@ export class CodexRuntimeAdapter {
65
128
  }
66
129
  eventQueue.close();
67
130
  });
131
+ const startTurnStartedAtMs = performance.now();
68
132
  await transport.startTurn(request, providerSessionId);
133
+ logCodexRuntimeStep("start_session.turn_start", startTurnStartedAtMs, {
134
+ sessionId: request.sessionId,
135
+ providerSessionId
136
+ });
137
+ logCodexRuntimeStep("start_session.ready", launchPerfStartedAtMs, {
138
+ sessionId: request.sessionId,
139
+ providerSessionId
140
+ });
69
141
  return {
70
142
  providerSessionId,
71
143
  rawStoreRef,
@@ -77,7 +149,7 @@ export class CodexRuntimeAdapter {
77
149
  transport.close();
78
150
  },
79
151
  isAlive: () => transport.isClosed() === false,
80
- completed: this.runTurn(null, request, sink, providerSessionId, rawStoreRef, abortController, eventQueue.iterator, [], launchedAtMs).finally(() => {
152
+ completed: this.runTurn(null, request, sink, providerSessionId, rawStoreRef, abortController, eventQueue.iterator, [], launchedAtMs, launchPerfStartedAtMs).finally(() => {
81
153
  transport.close();
82
154
  })
83
155
  };
@@ -90,19 +162,46 @@ export class CodexRuntimeAdapter {
90
162
  const transport = this.options.transportFactory
91
163
  ? this.options.transportFactory()
92
164
  : createCodexAppServerTransport(this.options);
165
+ const runtimeStartedAtMs = performance.now();
166
+ const initializeStartedAtMs = performance.now();
93
167
  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;
168
+ logCodexRuntimeStep("continue_session.initialize", initializeStartedAtMs, {
169
+ sessionId: request.sessionId,
170
+ providerSessionId
171
+ });
172
+ const syntheticRawStoreRef = buildRuntimeRawStoreRef(providerSessionId);
173
+ const resolvedSessionId = providerSessionId;
174
+ const resumeThreadStartedAtMs = performance.now();
97
175
  const resumed = await transport.resumeThread(request, resolvedSessionId);
98
- const rawStoreRef = resolvedBinding?.rawStoreRef ?? resumed.rawStoreRef ?? fallbackRawStoreRef;
176
+ logCodexRuntimeStep("continue_session.thread_resume", resumeThreadStartedAtMs, {
177
+ sessionId: request.sessionId,
178
+ providerSessionId: resolvedSessionId
179
+ });
180
+ const rawStoreRef = pickAvailableCodexRawStoreRef([request.rawStoreRef, resumed.rawStoreRef], syntheticRawStoreRef);
99
181
  const abortController = new AbortController();
100
182
  const eventQueue = createAsyncEventQueue();
183
+ logCodexRuntimeStep("continue_session.raw_store_ref_ready", runtimeStartedAtMs, {
184
+ sessionId: request.sessionId,
185
+ providerSessionId: resolvedSessionId,
186
+ synthetic: isSyntheticRawStoreRef(rawStoreRef),
187
+ hasResumedRawStoreRef: Boolean(resumed.rawStoreRef),
188
+ hasRequestRawStoreRef: Boolean(request.rawStoreRef),
189
+ resumedRawStoreRefExists: Boolean(resumed.rawStoreRef && existsSync(resumed.rawStoreRef))
190
+ });
101
191
  sink.updateSessionBinding({
102
192
  providerSessionId: resolvedSessionId,
103
193
  rawStoreRef
104
194
  });
195
+ let firstNotificationLogged = false;
105
196
  transport.setNotificationHandler(async (notification) => {
197
+ if (!firstNotificationLogged) {
198
+ firstNotificationLogged = true;
199
+ logCodexRuntimeStep("continue_session.first_notification", runtimeStartedAtMs, {
200
+ sessionId: request.sessionId,
201
+ providerSessionId: resolvedSessionId,
202
+ method: ensureText(notification.method).trim() || null
203
+ });
204
+ }
106
205
  const translated = translateCodexAppServerNotification(notification);
107
206
  if (translated.turnId) {
108
207
  eventQueue.setTurnId(translated.turnId);
@@ -134,7 +233,16 @@ export class CodexRuntimeAdapter {
134
233
  }
135
234
  eventQueue.close();
136
235
  });
236
+ const startTurnStartedAtMs = performance.now();
137
237
  await transport.startTurn(request, resolvedSessionId);
238
+ logCodexRuntimeStep("continue_session.turn_start", startTurnStartedAtMs, {
239
+ sessionId: request.sessionId,
240
+ providerSessionId: resolvedSessionId
241
+ });
242
+ logCodexRuntimeStep("continue_session.ready", runtimeStartedAtMs, {
243
+ sessionId: request.sessionId,
244
+ providerSessionId: resolvedSessionId
245
+ });
138
246
  return {
139
247
  providerSessionId: resolvedSessionId,
140
248
  rawStoreRef,
@@ -151,7 +259,7 @@ export class CodexRuntimeAdapter {
151
259
  })
152
260
  };
153
261
  }
154
- async runTurn(thread, request, sink, providerSessionId, rawStoreRef, abortController, preparedEvents, bufferedEvents = [], launchedAtMs = Date.now()) {
262
+ async runTurn(thread, request, sink, providerSessionId, rawStoreRef, abortController, preparedEvents, bufferedEvents = [], launchedAtMs = Date.now(), launchPerfStartedAtMs = performance.now()) {
155
263
  const context = {
156
264
  providerSessionId,
157
265
  rawStoreRef,
@@ -159,10 +267,13 @@ export class CodexRuntimeAdapter {
159
267
  // 否则前端会把新 assistant/tool 消息排到旧消息前面,表现成用户消息一直挂在底部。
160
268
  sequence: Math.max(0, request.sequenceBase ?? 0),
161
269
  toolNameByCallId: new Map(),
270
+ stableMessageRefByIdentity: new Map(),
271
+ lastSignatureByIdentity: new Map(),
162
272
  sink,
163
273
  workspacePath: request.workspacePath,
164
274
  firstUserMessage: request.options.content,
165
- launchedAtMs
275
+ launchedAtMs,
276
+ launchPerfStartedAtMs
166
277
  };
167
278
  try {
168
279
  await this.refreshSessionBindingIfNeeded(context);
@@ -219,6 +330,13 @@ export class CodexRuntimeAdapter {
219
330
  if (eventType.length === 0) {
220
331
  return;
221
332
  }
333
+ if (context.lastSignatureByIdentity.size === 0 && eventType.startsWith("item.")) {
334
+ logCodexRuntimeStep("turn.first_item_event", context.launchPerfStartedAtMs, {
335
+ sessionId: request.sessionId,
336
+ providerSessionId: context.providerSessionId,
337
+ eventType
338
+ });
339
+ }
222
340
  if (eventType === "turn.completed") {
223
341
  await context.sink.emit({
224
342
  type: "complete",
@@ -265,36 +383,30 @@ export class CodexRuntimeAdapter {
265
383
  if (itemType.length === 0) {
266
384
  return;
267
385
  }
268
- if (itemType === "agent_message" && eventType === "item.completed") {
386
+ if (itemType === "agent_message" &&
387
+ (eventType === "item.updated" || eventType === "item.completed")) {
269
388
  const content = pickFirstNonEmpty(ensureText(readProp(item, "text")).trim(), extractTextBlocks(readProp(item, "content")).trim());
270
389
  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)
390
+ await this.emitStableMessage(context, {
391
+ identity: `assistant:text:${ensureText(readProp(item, "id")).trim() || "default"}`,
392
+ timestamp: pickTimestamp(item, event),
393
+ role: "assistant",
394
+ kind: "text",
395
+ content
281
396
  });
282
397
  }
283
398
  return;
284
399
  }
285
- if (itemType === "reasoning" && eventType === "item.completed") {
400
+ if (itemType === "reasoning" &&
401
+ (eventType === "item.updated" || eventType === "item.completed")) {
286
402
  const content = pickFirstNonEmpty(ensureText(readProp(item, "text")).trim(), extractTextBlocks(readProp(item, "summary")).trim(), extractTextBlocks(readProp(item, "content")).trim());
287
403
  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)
404
+ await this.emitStableMessage(context, {
405
+ identity: `assistant:thinking:${ensureText(readProp(item, "id")).trim() || "default"}`,
406
+ timestamp: pickTimestamp(item, event),
407
+ role: "assistant",
408
+ kind: "thinking",
409
+ content
298
410
  });
299
411
  }
300
412
  return;
@@ -315,17 +427,37 @@ export class CodexRuntimeAdapter {
315
427
  status: "running"
316
428
  };
317
429
  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)
430
+ await this.emitStableMessage(context, {
431
+ identity: `tool:call:${callId}`,
432
+ timestamp: pickTimestamp(item, event),
433
+ role: "tool",
434
+ kind: "tool_call",
435
+ content: input,
436
+ toolCall
437
+ });
438
+ return;
439
+ }
440
+ if (eventType === "item.updated") {
441
+ const output = pickFirstNonEmpty(extractTextBlocks(readProp(item, "result")).trim(), extractTextBlocks(readProp(item, "output")).trim(), extractTextBlocks(readProp(item, "aggregated_output")).trim(), extractTextBlocks(readProp(item, "error")).trim());
442
+ if (output.length === 0) {
443
+ return;
444
+ }
445
+ const knownName = context.toolNameByCallId.get(callId) ?? name;
446
+ context.toolNameByCallId.set(callId, knownName);
447
+ await this.emitStableMessage(context, {
448
+ identity: `tool:result:${callId}`,
449
+ timestamp: pickTimestamp(item, event),
450
+ role: "tool",
451
+ kind: "tool_result",
452
+ content: output,
453
+ toolCall: {
454
+ callId,
455
+ name: knownName,
456
+ input: "",
457
+ output,
458
+ error: null,
459
+ status: "running"
460
+ }
329
461
  });
330
462
  return;
331
463
  }
@@ -341,26 +473,44 @@ export class CodexRuntimeAdapter {
341
473
  error: success ? null : output,
342
474
  status: success ? "completed" : "failed"
343
475
  };
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)
476
+ await this.emitStableMessage(context, {
477
+ identity: `tool:result:${callId}`,
478
+ timestamp: pickTimestamp(item, event),
479
+ role: "tool",
480
+ kind: "tool_result",
481
+ content: output,
482
+ toolCall
355
483
  });
356
484
  }
357
485
  }
486
+ async emitStableMessage(context, input) {
487
+ const message = this.buildMessage(context, {
488
+ role: input.role,
489
+ kind: input.kind,
490
+ content: input.content,
491
+ toolCall: input.toolCall ?? null,
492
+ stableIdentity: input.identity
493
+ });
494
+ const signature = buildCodexMessageSignature(message);
495
+ if (context.lastSignatureByIdentity.get(input.identity) === signature) {
496
+ return;
497
+ }
498
+ context.lastSignatureByIdentity.set(input.identity, signature);
499
+ await context.sink.emit({
500
+ type: "message",
501
+ message,
502
+ providerSessionId: context.providerSessionId,
503
+ rawStoreRef: context.rawStoreRef,
504
+ timestamp: input.timestamp,
505
+ rawEventRef: message.rawRef
506
+ });
507
+ }
358
508
  async refreshSessionBindingIfNeeded(context) {
359
509
  if (!isSyntheticRawStoreRef(context.rawStoreRef)) {
360
510
  return;
361
511
  }
362
- const resolved = await this.resolveExistingSessionBinding(context.providerSessionId, context.rawStoreRef, context.workspacePath) ??
363
- await this.resolveLaunchedSessionBinding(context.workspacePath, context.firstUserMessage, context.launchedAtMs);
512
+ const resolved = await this.resolveLaunchedSessionBinding(context.workspacePath, context.firstUserMessage, context.launchedAtMs) ??
513
+ await this.resolveExistingSessionBinding(context.providerSessionId, context.rawStoreRef, context.workspacePath);
364
514
  if (!resolved ||
365
515
  (resolved.providerSessionId === context.providerSessionId &&
366
516
  resolved.rawStoreRef === context.rawStoreRef)) {
@@ -465,6 +615,7 @@ export class CodexRuntimeAdapter {
465
615
  if (!dbPath) {
466
616
  return null;
467
617
  }
618
+ const DatabaseSync = loadDatabaseSync();
468
619
  let db = null;
469
620
  try {
470
621
  db = new DatabaseSync(dbPath, { open: true, readOnly: true });
@@ -499,11 +650,14 @@ export class CodexRuntimeAdapter {
499
650
  getCodexHomeDir() {
500
651
  return this.options.homeDir?.trim() || process.env.CODINGNS_CODEX_HOME || join(homedir(), ".codex");
501
652
  }
502
- buildMessage(request, context, input) {
503
- context.sequence += 1;
504
- const rawRef = createRawRef(this.providerId, context.rawStoreRef, context.sequence);
653
+ buildMessage(context, input) {
654
+ const stableRef = this.resolveStableMessageRef(context, input.stableIdentity ?? null);
655
+ const rawRef = stableRef?.rawRef ??
656
+ createRawRef(this.providerId, context.rawStoreRef, ++context.sequence);
657
+ const sequence = stableRef?.sequence ?? context.sequence;
658
+ const messageId = stableRef?.messageId ?? messageIdFromRawRef(rawRef);
505
659
  return {
506
- messageId: messageIdFromRawRef(rawRef),
660
+ messageId,
507
661
  provider: this.providerId,
508
662
  providerSessionId: context.providerSessionId,
509
663
  role: input.role,
@@ -511,10 +665,28 @@ export class CodexRuntimeAdapter {
511
665
  content: input.content,
512
666
  toolCall: input.toolCall ?? null,
513
667
  timestamp: nextTimestamp(),
514
- sequence: context.sequence,
668
+ sequence,
515
669
  rawRef
516
670
  };
517
671
  }
672
+ resolveStableMessageRef(context, stableIdentity) {
673
+ if (!stableIdentity) {
674
+ return null;
675
+ }
676
+ const existing = context.stableMessageRefByIdentity.get(stableIdentity);
677
+ if (existing) {
678
+ return existing;
679
+ }
680
+ context.sequence += 1;
681
+ const rawRef = createRawRef(this.providerId, context.rawStoreRef, context.sequence);
682
+ const created = {
683
+ sequence: context.sequence,
684
+ rawRef,
685
+ messageId: messageIdFromRawRef(rawRef)
686
+ };
687
+ context.stableMessageRefByIdentity.set(stableIdentity, created);
688
+ return created;
689
+ }
518
690
  async awaitThreadStarted(thread, events, workspacePath, firstUserMessage, launchedAtMs) {
519
691
  const bufferedEvents = [];
520
692
  while (true) {
@@ -658,6 +830,7 @@ function createCodexAppServerTransport(options) {
658
830
  });
659
831
  return {
660
832
  async initialize() {
833
+ const startedAtMs = performance.now();
661
834
  await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("initialize", () => ++requestSequence), {
662
835
  method: "initialize",
663
836
  params: {
@@ -673,8 +846,10 @@ function createCodexAppServerTransport(options) {
673
846
  method: "initialized",
674
847
  params: {}
675
848
  });
849
+ logCodexRuntimeStep("transport.initialize", startedAtMs);
676
850
  },
677
851
  async startThread(request) {
852
+ const startedAtMs = performance.now();
678
853
  const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("thread-start", () => ++requestSequence), {
679
854
  method: "thread/start",
680
855
  params: createThreadStartParams(request)
@@ -685,29 +860,44 @@ function createCodexAppServerTransport(options) {
685
860
  throw new Error("CODEX_APP_SERVER_THREAD_ID_MISSING");
686
861
  }
687
862
  activeThreadId = providerSessionId;
863
+ logCodexRuntimeStep("transport.thread_start", startedAtMs, {
864
+ sessionId: request.sessionId,
865
+ providerSessionId
866
+ });
688
867
  return {
689
868
  providerSessionId,
690
869
  rawStoreRef: normalizeText(thread?.path) || null
691
870
  };
692
871
  },
693
872
  async resumeThread(request, providerSessionId) {
873
+ const startedAtMs = performance.now();
694
874
  const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("thread-resume", () => ++requestSequence), {
695
875
  method: "thread/resume",
696
876
  params: createThreadResumeParams(request, providerSessionId)
697
877
  });
698
878
  const thread = toRecord(result.thread);
699
879
  activeThreadId = ensureText(thread?.id).trim() || providerSessionId;
880
+ logCodexRuntimeStep("transport.thread_resume", startedAtMs, {
881
+ sessionId: request.sessionId,
882
+ providerSessionId: activeThreadId
883
+ });
700
884
  return {
701
885
  providerSessionId: activeThreadId,
702
886
  rawStoreRef: normalizeText(thread?.path) || null
703
887
  };
704
888
  },
705
889
  async startTurn(request, providerSessionId) {
890
+ const startedAtMs = performance.now();
706
891
  const result = await sendJsonRpcRequest(child, pendingResponses, () => nextJsonRpcId("turn-start", () => ++requestSequence), {
707
892
  method: "turn/start",
708
893
  params: createTurnStartParams(request, providerSessionId)
709
894
  });
710
895
  activeTurnId = ensureText(readProp(readProp(result, "turn"), "id")).trim() || activeTurnId;
896
+ logCodexRuntimeStep("transport.turn_start", startedAtMs, {
897
+ sessionId: request.sessionId,
898
+ providerSessionId,
899
+ turnId: activeTurnId
900
+ });
711
901
  },
712
902
  async interruptTurn() {
713
903
  if (!activeThreadId || !activeTurnId) {
@@ -849,7 +1039,27 @@ function translateCodexAppServerNotification(notification) {
849
1039
  turnId: ensureText(turn?.id).trim() || null
850
1040
  };
851
1041
  }
852
- if (method === "item/started" || method === "item/completed") {
1042
+ if (method === "error") {
1043
+ const error = toRecord(params.error);
1044
+ const detail = buildCodexAppServerErrorDetail(error);
1045
+ if (params.willRetry === true) {
1046
+ return {
1047
+ event: null,
1048
+ terminal: false,
1049
+ turnId: ensureText(params.turnId).trim() || null
1050
+ };
1051
+ }
1052
+ return {
1053
+ event: {
1054
+ type: "turn.failed",
1055
+ timestamp: nextTimestamp(),
1056
+ error: detail
1057
+ },
1058
+ terminal: true,
1059
+ turnId: ensureText(params.turnId).trim() || null
1060
+ };
1061
+ }
1062
+ if (method === "item/started" || method === "item/updated" || method === "item/completed") {
853
1063
  const item = translateCodexAppServerItem(toRecord(params.item));
854
1064
  if (!item) {
855
1065
  return {
@@ -860,7 +1070,11 @@ function translateCodexAppServerNotification(notification) {
860
1070
  }
861
1071
  return {
862
1072
  event: {
863
- type: method === "item/started" ? "item.started" : "item.completed",
1073
+ type: method === "item/started"
1074
+ ? "item.started"
1075
+ : method === "item/updated"
1076
+ ? "item.updated"
1077
+ : "item.completed",
864
1078
  item,
865
1079
  timestamp: nextTimestamp()
866
1080
  },
@@ -874,6 +1088,31 @@ function translateCodexAppServerNotification(notification) {
874
1088
  turnId: null
875
1089
  };
876
1090
  }
1091
+ function buildCodexAppServerErrorDetail(error) {
1092
+ const message = ensureText(error?.message).trim();
1093
+ const additionalDetails = ensureText(error?.additionalDetails).trim();
1094
+ if (message && additionalDetails && !message.includes(additionalDetails)) {
1095
+ return `${message}\n${additionalDetails}`;
1096
+ }
1097
+ return message || additionalDetails || "codex app-server error";
1098
+ }
1099
+ function buildCodexMessageSignature(message) {
1100
+ return JSON.stringify({
1101
+ role: message.role,
1102
+ kind: message.kind,
1103
+ content: message.content,
1104
+ toolCall: message.toolCall
1105
+ ? {
1106
+ callId: message.toolCall.callId,
1107
+ name: message.toolCall.name,
1108
+ input: message.toolCall.input,
1109
+ output: message.toolCall.output,
1110
+ error: message.toolCall.error,
1111
+ status: message.toolCall.status
1112
+ }
1113
+ : null
1114
+ });
1115
+ }
877
1116
  function translateCodexAppServerItem(item) {
878
1117
  if (!item) {
879
1118
  return null;
@@ -886,15 +1125,15 @@ function translateCodexAppServerItem(item) {
886
1125
  return {
887
1126
  type: "agent_message",
888
1127
  id: item.id,
889
- text: item.text
1128
+ text: ensureText(item.text).trim()
890
1129
  };
891
1130
  }
892
1131
  if (itemType === "reasoning") {
893
1132
  return {
894
1133
  type: "reasoning",
895
1134
  id: item.id,
896
- text: Array.isArray(item.content) ? item.content.join("\n") : "",
897
- summary: Array.isArray(item.summary) ? item.summary.join("\n") : ""
1135
+ text: Array.isArray(item.content) ? item.content.join("\n") : ensureText(item.text).trim(),
1136
+ summary: Array.isArray(item.summary) ? item.summary.join("\n") : ensureText(item.summary).trim()
898
1137
  };
899
1138
  }
900
1139
  if (itemType === "commandExecution") {
@@ -1116,6 +1355,18 @@ function resolveNodeModulesCandidate(currentDirectory, relativeSegments) {
1116
1355
  function buildRuntimeRawStoreRef(providerSessionId) {
1117
1356
  return resolve(process.cwd(), "runtime", "codex", `${providerSessionId}.stream`);
1118
1357
  }
1358
+ function pickAvailableCodexRawStoreRef(candidates, fallbackRawStoreRef) {
1359
+ for (const candidate of candidates) {
1360
+ const normalized = candidate?.trim();
1361
+ if (!normalized) {
1362
+ continue;
1363
+ }
1364
+ if (existsSync(normalized)) {
1365
+ return normalized;
1366
+ }
1367
+ }
1368
+ return fallbackRawStoreRef;
1369
+ }
1119
1370
  function resolveRuntimeStoreKey(providerSessionId, sessionId) {
1120
1371
  return providerSessionId.trim() || sessionId;
1121
1372
  }