@jskit-ai/assistant-core 0.1.200 → 0.1.202

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 (164) hide show
  1. package/examples/conversation/README.md +8 -1
  2. package/examples/conversation/package.json +5 -5
  3. package/fixtures/responsive-assistant/App.vue +4 -1
  4. package/package.json +99 -36
  5. package/src/client/conversation/AssistantConversationElement.vue +27 -4
  6. package/src/client/conversation/AssistantConversationStatus.vue +24 -0
  7. package/src/client/conversation/AssistantGoalControl.vue +8 -5
  8. package/src/client/conversation/AssistantTranscript.vue +10 -3
  9. package/src/client/conversation/index.js +2 -0
  10. package/src/client/conversation/messageDelivery.js +115 -32
  11. package/src/client/conversation/retainedConversation.js +32 -0
  12. package/src/client/conversation/useAssistantQuestions.js +76 -0
  13. package/src/client/lib/assistantApi.js +72 -2
  14. package/src/server/conversation/agentRun.js +222 -0
  15. package/src/server/conversation/attachments.js +93 -0
  16. package/src/server/conversation/claudeClient.js +221 -0
  17. package/src/server/conversation/claudeHistory.js +256 -0
  18. package/src/server/conversation/claudeProcess.js +327 -0
  19. package/src/server/conversation/claudeTools.js +42 -0
  20. package/src/server/conversation/claudeTurn.js +1082 -0
  21. package/src/server/conversation/codexConfiguration.js +1183 -0
  22. package/src/server/conversation/codexConversations.js +1082 -0
  23. package/src/server/conversation/codexDetachedTurn.js +332 -0
  24. package/src/server/conversation/codexEvents.js +194 -1
  25. package/src/server/conversation/codexGoalCommands.js +124 -0
  26. package/src/server/conversation/codexHelperExecution.js +306 -0
  27. package/src/server/conversation/codexHelperThreadLedger.js +804 -0
  28. package/src/server/conversation/codexHelperThreadRecovery.js +395 -0
  29. package/src/server/conversation/codexHelperThreads.js +638 -0
  30. package/src/server/conversation/codexHistoryAdapter.js +369 -0
  31. package/src/server/conversation/codexMessageCommands.js +663 -0
  32. package/src/server/conversation/codexMessageDelivery.js +211 -0
  33. package/src/server/conversation/codexNativeHistoryExport.js +95 -0
  34. package/src/server/conversation/codexNotificationObserver.js +482 -0
  35. package/src/server/conversation/codexNotificationQueue.js +103 -0
  36. package/src/server/conversation/codexObservationControl.js +174 -0
  37. package/src/server/conversation/codexOutput.js +931 -0
  38. package/src/server/conversation/codexProcess.js +1559 -0
  39. package/src/server/conversation/codexProcessLeader.js +14 -0
  40. package/src/server/conversation/codexProvider.js +2710 -0
  41. package/src/server/conversation/codexProviderOwner.js +1058 -0
  42. package/src/server/conversation/codexProviderSelection.js +218 -0
  43. package/src/server/conversation/codexReasoning.js +314 -0
  44. package/src/server/conversation/codexRenewalCommands.js +290 -0
  45. package/src/server/conversation/codexResultSettlement.js +409 -0
  46. package/src/server/conversation/codexRunJournal.js +658 -0
  47. package/src/server/conversation/codexRuntimeLifecycle.js +288 -0
  48. package/src/server/conversation/codexSessionCleanup.js +96 -0
  49. package/src/server/conversation/codexSessionReconciliation.js +284 -0
  50. package/src/server/conversation/codexThreadReadiness.js +397 -0
  51. package/src/server/conversation/codexThreadStatus.js +354 -0
  52. package/src/server/conversation/codexTurn.js +701 -255
  53. package/src/server/conversation/codexTurnControl.js +317 -0
  54. package/src/server/conversation/codexTurnRecovery.js +990 -0
  55. package/src/server/conversation/codexTurnState.js +549 -0
  56. package/src/server/conversation/commandHook.js +18 -0
  57. package/src/server/conversation/commandWrapper.js +37 -0
  58. package/src/server/conversation/configuration.js +43 -0
  59. package/src/server/conversation/continuity.js +289 -0
  60. package/src/server/conversation/fileStorage.js +55 -0
  61. package/src/server/conversation/hookBridge.js +19 -0
  62. package/src/server/conversation/index.js +13 -0
  63. package/src/server/conversation/localExecution.js +665 -0
  64. package/src/server/conversation/memoryStorage.js +7 -72
  65. package/src/server/conversation/nativeHistoryExport.js +90 -0
  66. package/src/server/conversation/nativeStorage.js +23 -0
  67. package/src/server/conversation/openCodeClient.js +209 -19
  68. package/src/server/conversation/openCodePlugin.js +84 -0
  69. package/src/server/conversation/openCodeProcess.js +476 -0
  70. package/src/server/conversation/openCodeRuntime.js +1578 -0
  71. package/src/server/conversation/openCodeTools.js +60 -0
  72. package/src/server/conversation/openCodeTurn.js +557 -0
  73. package/src/server/conversation/provider.js +30 -0
  74. package/src/server/conversation/providers/api.js +136 -0
  75. package/src/server/conversation/providers/claude.js +63 -0
  76. package/src/server/conversation/providers/claudeDriver.js +425 -0
  77. package/src/server/conversation/providers/codex.js +248 -0
  78. package/src/server/conversation/providers/codexDriver.js +1078 -0
  79. package/src/server/conversation/providers/opencode.js +49 -0
  80. package/src/server/conversation/providers/opencodeDriver.js +650 -0
  81. package/src/server/conversation/providers/scoped.js +312 -0
  82. package/src/server/conversation/runtime.js +2220 -0
  83. package/src/server/conversation/runtimeStateUpgrade.js +125 -0
  84. package/src/server/conversation/storage.js +184 -0
  85. package/src/server/conversation/streams.js +7 -2
  86. package/src/server/conversation/structuredOutput.js +240 -0
  87. package/src/server/conversation/systemPrompt.js +54 -0
  88. package/src/server/conversation/tools.js +84 -0
  89. package/src/server/conversation/transcript.js +5 -4
  90. package/src/server/hosts/claudeHistory.js +5 -0
  91. package/src/server/hosts/claudeProcess.js +11 -0
  92. package/src/server/hosts/claudeTurn.js +4 -0
  93. package/src/server/hosts/codexConfiguration.js +20 -0
  94. package/src/server/hosts/codexEvents.js +13 -0
  95. package/src/server/hosts/codexProcess.js +12 -0
  96. package/src/server/hosts/codexProvider.js +23 -0
  97. package/src/server/hosts/codexTurn.js +20 -0
  98. package/src/server/hosts/nativeHistory.js +3 -0
  99. package/src/server/hosts/openCodeClient.js +3 -0
  100. package/src/server/hosts/openCodeProcess.js +23 -0
  101. package/src/server/hosts/openCodeTurn.js +7 -0
  102. package/src/server/index.js +2 -0
  103. package/src/server/lib/aiConnectionClient.js +13 -6
  104. package/src/server/lib/assistantCompletion.js +190 -0
  105. package/src/server/lib/assistantToolLoop.js +314 -0
  106. package/src/server/lib/serviceToolCatalog.js +25 -10
  107. package/src/shared/assistantResponseText.js +29 -0
  108. package/src/shared/conversation/attachments.js +19 -0
  109. package/src/shared/conversation/index.js +3 -0
  110. package/src/shared/conversation/pagination.js +78 -0
  111. package/src/shared/conversation/patches.js +150 -0
  112. package/src/shared/conversation/questions.js +48 -0
  113. package/src/shared/conversation/streaming.js +7 -4
  114. package/src/shared/index.js +2 -0
  115. package/src/shared/nativeProviders.js +45 -0
  116. package/src/testing/conversationStorageContract.js +31 -1
  117. package/src/testing/nativeCodex.js +11 -0
  118. package/src/testing/nativeOpenCode.js +3 -0
  119. package/test/aiConnectionClient.test.js +2 -0
  120. package/test/apiConversationDriver.test.js +73 -0
  121. package/test/assistantApiSurfaceHeader.test.js +85 -0
  122. package/test/assistantBoundedToolLoop.test.js +285 -0
  123. package/test/assistantScroll.browser.test.js +16 -0
  124. package/test/claudeClient.test.js +189 -0
  125. package/test/claudeConversationSession.test.js +1173 -0
  126. package/test/claudeInstructionResume.test.js +82 -0
  127. package/test/claudeProcess.test.js +204 -0
  128. package/test/claudeTurn.test.js +253 -0
  129. package/test/codexConversationSession.test.js +2122 -0
  130. package/test/codexGoal.test.js +37 -0
  131. package/test/codexHelperDeletion.test.js +163 -0
  132. package/test/codexHelperThreadLedger.test.js +494 -0
  133. package/test/codexHistoryAdapter.test.js +541 -0
  134. package/test/codexInteractiveArguments.test.js +30 -0
  135. package/test/codexIsolation.test.js +756 -0
  136. package/test/codexModelCatalog.test.js +362 -0
  137. package/test/codexNativeHistoryExport.test.js +81 -0
  138. package/test/codexThreadSelection.test.js +136 -0
  139. package/test/codexTurn.test.js +2158 -0
  140. package/test/conversation.test.js +110 -0
  141. package/test/conversationAgentRun.test.js +326 -0
  142. package/test/conversationApiFacilities.test.js +90 -0
  143. package/test/conversationAttachments.test.js +238 -0
  144. package/test/conversationCommandWrapper.test.js +48 -0
  145. package/test/conversationConfiguration.test.js +174 -0
  146. package/test/conversationNativeStorage.test.js +25 -0
  147. package/test/conversationReplacement.test.js +172 -0
  148. package/test/conversationRuntime.test.js +241 -0
  149. package/test/conversationRuntimeStateUpgrade.test.js +144 -0
  150. package/test/conversationSession.test.js +1080 -0
  151. package/test/conversationStreaming.test.js +68 -0
  152. package/test/conversationSystemPrompt.test.js +84 -0
  153. package/test/conversationTools.test.js +416 -0
  154. package/test/fileConversationStorage.test.js +73 -0
  155. package/test/fixtures/codexHelperThreadLedger.js +39 -0
  156. package/test/localConversationExecution.test.js +90 -0
  157. package/test/messageDelivery.test.js +234 -1
  158. package/test/nativeConversationRetirement.test.js +215 -0
  159. package/test/openCodeClient.test.js +515 -0
  160. package/test/openCodeProcess.test.js +177 -0
  161. package/test/openCodeRuntime.test.js +322 -0
  162. package/test/openCodeTools.test.js +287 -0
  163. package/test/opencodeConversationSession.test.js +789 -0
  164. package/test/serviceToolCatalog.test.js +24 -0
@@ -0,0 +1,931 @@
1
+ import { createCodexAppServerReasoning } from "./codexReasoning.js";
2
+ import {
3
+ normalizeCodexRunText,
4
+ codexAppServerProcessedResultEvent,
5
+ codexAppServerTurnCanReceiveProviderCompletion,
6
+ codexAppServerTurnAwaitsProviderIdentity,
7
+ codexAppServerTurnCanReceiveProviderActivity,
8
+ codexAppServerTurnKey,
9
+ CODEX_APP_SERVER_AGENT_RUN_ID,
10
+ codexAppServerRunInputSource
11
+ } from "./codexTurnState.js";
12
+ import crypto from "node:crypto";
13
+ import { isPlainObject } from "./normalize.js";
14
+ import {
15
+ codexAppServerAssistantItemText,
16
+ classifyCodexAppServerEvent,
17
+ codexAppServerContentText,
18
+ codexAppServerNotificationEvent,
19
+ codexAppServerNotificationEventType,
20
+ codexAppServerNotificationEventPayload,
21
+ codexAppServerNotificationItem,
22
+ codexAppServerNotificationItemId,
23
+ codexAppServerNotificationTurnId,
24
+ codexAppServerProviderThreadAssistantSegments
25
+ } from "./codexEvents.js";
26
+
27
+ // Original native output, reasoning persistence and exact-result identity.
28
+ // One instance belongs to each retained run owner; its state is shared by reference.
29
+ export function createCodexAppServerOutput({
30
+ namespace,
31
+ createRuntime,
32
+ createStore,
33
+ publish,
34
+ acquireProvider,
35
+ turnState,
36
+ turnStateFromAgentRun,
37
+ debugLog,
38
+ debugError,
39
+ liveProgressMaxLength,
40
+ storeReadError,
41
+ messageMetadata,
42
+ snapshotRecoveryItemLimit
43
+ }) {
44
+ const codexAppServerFinalizedTurns = new Set();
45
+ const codexAppServerFinalAssistantResults = new Map();
46
+ const {
47
+ reasoningTurns: codexAppServerReasoningTurns,
48
+ reasoningPersistQueues: codexAppServerReasoningPersistQueues,
49
+ recordReasoningForSession: recordCodexAppServerReasoningForSession,
50
+ readReasoningText: readCodexAppServerReasoningText,
51
+ flushReasoningPersist: flushCodexAppServerReasoningPersist,
52
+ cleanupReasoningTurn: cleanupCodexAppServerReasoningTurn,
53
+ splitReasoningTurn: splitCodexAppServerReasoningTurn
54
+ } = createCodexAppServerReasoning({
55
+ createStore,
56
+ publish,
57
+ turnStateFromAgentRun,
58
+ debugLog,
59
+ readCodexAppServerAgentRunForSession
60
+ });
61
+ const codexAppServerLiveProgressItems = new Set();
62
+ const codexAppServerLiveProgressFingerprints = new Set();
63
+ const codexAppServerMirroredTerminalItems = new Set();
64
+
65
+ function codexAppServerResultFinalizationKey(sessionId = "", threadId = "", turnId = "") {
66
+ return [
67
+ namespace(sessionId),
68
+ codexAppServerTurnKey(threadId, turnId || "*")
69
+ ].filter(Boolean).join(":");
70
+ }
71
+
72
+ function codexAppServerLiveProgressCandidate(notification = {}) {
73
+ const method = normalizeCodexRunText(notification.method);
74
+ const event = codexAppServerNotificationEvent(notification);
75
+ if (isPlainObject(event)) {
76
+ const eventType = codexAppServerNotificationEventType(notification, event);
77
+ const payload = codexAppServerNotificationEventPayload(notification, event);
78
+ const payloadType = normalizeCodexRunText(payload.type);
79
+ const phase = normalizeCodexRunText(payload.phase || event.phase);
80
+ if (eventType === "event_msg" && payloadType === "agent_message" && phase && phase !== "final_answer") {
81
+ return {
82
+ explicit: Boolean(phase),
83
+ phase,
84
+ source: "event",
85
+ text: normalizeCodexRunText(
86
+ codexAppServerContentText(payload.message) ||
87
+ codexAppServerContentText(payload.text) ||
88
+ codexAppServerContentText(payload.content)
89
+ )
90
+ };
91
+ }
92
+ }
93
+
94
+ if (method !== "item/completed") {
95
+ return null;
96
+ }
97
+ const item = codexAppServerNotificationItem(notification);
98
+ const text = codexAppServerAssistantItemText(item);
99
+ if (!text) {
100
+ return null;
101
+ }
102
+ const phase = normalizeCodexRunText(item?.phase || item?.purpose || item?.category);
103
+ if (phase === "final_answer") {
104
+ return null;
105
+ }
106
+ return {
107
+ explicit: ["commentary", "progress", "status", "thinking"].includes(phase),
108
+ phase,
109
+ source: "item",
110
+ text
111
+ };
112
+ }
113
+
114
+ function codexAppServerLiveProgressText(notification = {}) {
115
+ const candidate = codexAppServerLiveProgressCandidate(notification);
116
+ const text = normalizeCodexRunText(candidate?.text);
117
+ if (!text) {
118
+ return "";
119
+ }
120
+ if (candidate?.phase === "commentary") {
121
+ return text;
122
+ }
123
+ if (text.length > liveProgressMaxLength) {
124
+ return "";
125
+ }
126
+ if (text.includes("\n") || text.includes("\r") || text.includes("```")) {
127
+ return "";
128
+ }
129
+ return text;
130
+ }
131
+
132
+ function codexAppServerLiveProgressKey(sessionId = "", threadId = "", notification = {}) {
133
+ const itemId = codexAppServerNotificationItemId(notification);
134
+ if (!itemId) {
135
+ return "";
136
+ }
137
+ return [
138
+ normalizeCodexRunText(sessionId),
139
+ normalizeCodexRunText(threadId),
140
+ codexAppServerNotificationTurnId(notification) || "*",
141
+ "live-progress",
142
+ itemId
143
+ ].join(":");
144
+ }
145
+
146
+ function codexAppServerLiveProgressFingerprintKey(
147
+ sessionId = "",
148
+ threadId = "",
149
+ turnId = "",
150
+ text = ""
151
+ ) {
152
+ const normalizedText = normalizeCodexRunText(text);
153
+ if (!normalizedText) {
154
+ return "";
155
+ }
156
+ return [
157
+ normalizeCodexRunText(sessionId),
158
+ normalizeCodexRunText(threadId),
159
+ normalizeCodexRunText(turnId) || "*",
160
+ "live-progress-text",
161
+ crypto.createHash("sha256").update(normalizedText).digest("hex")
162
+ ].join(":");
163
+ }
164
+
165
+ function codexAppServerConversationMessageId(
166
+ threadId = "",
167
+ turnId = "",
168
+ role = "",
169
+ text = ""
170
+ ) {
171
+ const normalizedText = normalizeCodexRunText(text);
172
+ const normalizedThreadId = normalizeCodexRunText(threadId);
173
+ if (!normalizedText || !normalizedThreadId) {
174
+ return "";
175
+ }
176
+ const digest = crypto.createHash("sha256")
177
+ .update([
178
+ normalizedThreadId,
179
+ normalizeCodexRunText(turnId) || "*",
180
+ normalizeCodexRunText(role),
181
+ normalizedText
182
+ ].join("\u0000"))
183
+ .digest("hex");
184
+ return `codex-${digest}`;
185
+ }
186
+
187
+ async function readCodexAppServerAgentRunForSession(store, sessionId = "") {
188
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
189
+ if (!normalizedSessionId) {
190
+ return null;
191
+ }
192
+ if (typeof store?.readAgentRun !== "function") {
193
+ throw new Error(storeReadError);
194
+ }
195
+ return store.readAgentRun(
196
+ normalizedSessionId,
197
+ CODEX_APP_SERVER_AGENT_RUN_ID
198
+ );
199
+ }
200
+
201
+ function codexAppServerTerminalItemMirrorKey(sessionId = "", threadId = "", notification = {}, role = "") {
202
+ const itemId = codexAppServerNotificationItemId(notification);
203
+ if (!itemId) {
204
+ return "";
205
+ }
206
+ return [
207
+ normalizeCodexRunText(sessionId),
208
+ normalizeCodexRunText(threadId),
209
+ codexAppServerNotificationTurnId(notification) || "*",
210
+ normalizeCodexRunText(role),
211
+ itemId
212
+ ].join(":");
213
+ }
214
+
215
+ async function writeMirroredCodexAppServerTerminalMessage({
216
+ notification = {},
217
+ role = "",
218
+ sessionId = "",
219
+ text = "",
220
+ threadId = ""
221
+ } = {}) {
222
+ const normalizedRole = normalizeCodexRunText(role);
223
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
224
+ const normalizedText = normalizeCodexRunText(text);
225
+ if (!normalizedSessionId || !normalizedText || !["assistant", "user"].includes(normalizedRole)) {
226
+ return null;
227
+ }
228
+ const key = codexAppServerTerminalItemMirrorKey(
229
+ normalizedSessionId,
230
+ threadId,
231
+ notification,
232
+ normalizedRole
233
+ );
234
+ if (!key) {
235
+ return null;
236
+ }
237
+ if (codexAppServerMirroredTerminalItems.has(key)) {
238
+ return null;
239
+ }
240
+ codexAppServerMirroredTerminalItems.add(key);
241
+ let written = null;
242
+ try {
243
+ const store = await createStore(sessionId);
244
+ const writer = normalizedRole === "user"
245
+ ? store?.writeConversationUserMessage
246
+ : store?.writeConversationAssistantMessage;
247
+ if (typeof writer !== "function") {
248
+ codexAppServerMirroredTerminalItems.delete(key);
249
+ return null;
250
+ }
251
+ let turnMetadata = null;
252
+ if (normalizedRole === "user" && typeof store.readConversationLog === "function") {
253
+ turnMetadata = await messageMetadata.terminal?.(store, normalizedSessionId) ?? null;
254
+ }
255
+ written = await writer.call(store, normalizedSessionId, {
256
+ messageId: codexAppServerConversationMessageId(
257
+ threadId,
258
+ codexAppServerNotificationTurnId(notification),
259
+ normalizedRole,
260
+ normalizedText
261
+ ),
262
+ text: normalizedText,
263
+ nativeIdentity: { threadId, turnId: codexAppServerNotificationTurnId(notification) },
264
+ ...(turnMetadata ? { turnMetadata } : {})
265
+ });
266
+ if (!written) {
267
+ codexAppServerMirroredTerminalItems.delete(key);
268
+ return null;
269
+ }
270
+ const reason = normalizedRole === "user"
271
+ ? "codex-app-server-terminal-user-message"
272
+ : "codex-app-server-terminal-assistant-message";
273
+ await publish(normalizedSessionId, {
274
+ payload: {
275
+ conversationLogPatch: {
276
+ turn: written,
277
+ type: "upsert-turn"
278
+ }
279
+ },
280
+ reason
281
+ });
282
+ debugLog(`appServerTerminal${normalizedRole === "user" ? "User" : "Assistant"}Message.mirrored`, {
283
+ itemId: normalizeCodexRunText(codexAppServerNotificationItem(notification)?.id),
284
+ sessionId: normalizedSessionId,
285
+ threadId: normalizeCodexRunText(threadId),
286
+ turnId: codexAppServerNotificationTurnId(notification)
287
+ });
288
+ return written;
289
+ } catch (error) {
290
+ if (!written) {
291
+ codexAppServerMirroredTerminalItems.delete(key);
292
+ }
293
+ throw error;
294
+ }
295
+ }
296
+
297
+ async function mirrorCodexAppServerTerminalAssistantMessage(sessionId = "", threadId = "", notification = {}) {
298
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
299
+ const normalizedThreadId = normalizeCodexRunText(threadId);
300
+ if (codexAppServerLiveProgressCandidate(notification)?.explicit === true) {
301
+ return;
302
+ }
303
+ const classification = classifyCodexAppServerEvent(notification);
304
+ // Completed finals use the same native-item publication owner as UI chat.
305
+ if (classification.kind === "final_assistant_result") {
306
+ return;
307
+ }
308
+ const text = normalizeCodexRunText(
309
+ codexAppServerAssistantItemText(codexAppServerNotificationItem(notification))
310
+ );
311
+ if (!normalizedSessionId || !text) {
312
+ return;
313
+ }
314
+ const store = await createStore(normalizedSessionId);
315
+ const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
316
+ if (normalizeCodexRunText(run?.inputSource) !== "terminal") {
317
+ return;
318
+ }
319
+ await writeMirroredCodexAppServerTerminalMessage({
320
+ notification,
321
+ role: "assistant",
322
+ sessionId: normalizedSessionId,
323
+ text,
324
+ threadId: normalizedThreadId
325
+ });
326
+ }
327
+
328
+ function readCodexAppServerFinalAssistantResult(sessionId = "", threadId = "", turnId = "") {
329
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
330
+ const normalizedThreadId = normalizeCodexRunText(threadId);
331
+ const normalizedTurnId = normalizeCodexRunText(turnId);
332
+ if (!normalizedSessionId || !normalizedThreadId || !normalizedTurnId) {
333
+ return null;
334
+ }
335
+ return codexAppServerFinalAssistantResults.get(
336
+ codexAppServerResultFinalizationKey(normalizedSessionId, normalizedThreadId, normalizedTurnId)
337
+ ) || null;
338
+ }
339
+
340
+ async function persistCodexAppServerAssistantReply(runtime, sessionId = "", record = {}, streamItemId = "", outputItemId = streamItemId || record.itemId) {
341
+ if (record.conversationTurn && !streamItemId) {
342
+ return record.conversationTurn;
343
+ }
344
+ const conversationText = normalizeCodexRunText(record.text);
345
+ if (!conversationText) {
346
+ return null;
347
+ }
348
+ const messageId = codexAppServerConversationMessageId(
349
+ record.threadId, record.turnId, "assistant-item", record.itemId
350
+ );
351
+ let written = record.conversationTurn || await runtime.store.writeConversationAssistantMessage(sessionId, {
352
+ messageId,
353
+ outputId: codexAppServerConversationMessageId(
354
+ record.threadId, record.turnId, "assistant-item", outputItemId
355
+ ),
356
+ nativeIdentity: { threadId: record.threadId, turnId: record.turnId },
357
+ text: conversationText
358
+ });
359
+ if (!written) {
360
+ // A replay or correction belongs to its original row, including after restart.
361
+ written = (await runtime.store.readConversationLog(sessionId))
362
+ .find((turn) => turn.assistant?.messageId === messageId);
363
+ if (!written) {
364
+ throw new Error("The saved Codex reply could not be found.");
365
+ }
366
+ if (written.assistant.text !== conversationText) {
367
+ written = await runtime.store.upsertConversationAssistantMessage(sessionId, {
368
+ text: conversationText,
369
+ nativeIdentity: { threadId: record.threadId, turnId: record.turnId },
370
+ turnId: written.turnId
371
+ });
372
+ }
373
+ }
374
+ if (streamItemId) {
375
+ runtime.store.completeConversationStreamMessage(sessionId, codexAppServerConversationMessageId(
376
+ record.threadId, record.turnId, "assistant-item", streamItemId
377
+ ));
378
+ }
379
+ await publish(sessionId, {
380
+ payload: {
381
+ conversationStream: runtime.store.completeConversationStreamMessage(sessionId, messageId),
382
+ conversationLogPatch: {
383
+ turn: written,
384
+ type: "upsert-turn"
385
+ }
386
+ },
387
+ reason: "assistant-response-bundle"
388
+ });
389
+ record.conversationTurn = written;
390
+ debugLog("appServerAssistantResponseBundle.persisted", {
391
+ conversationTurnId: normalizeCodexRunText(written.turnId),
392
+ sessionId: normalizeCodexRunText(sessionId),
393
+ textLength: conversationText.length,
394
+ threadId: normalizeCodexRunText(record.threadId),
395
+ turnId: normalizeCodexRunText(record.turnId)
396
+ });
397
+ return written;
398
+ }
399
+
400
+ async function recordCodexAppServerFinalAssistantResult({
401
+ itemId = "",
402
+ notification = {},
403
+ sessionId = "",
404
+ source = "",
405
+ streamItemId = "",
406
+ outputItemId,
407
+ text = "",
408
+ threadId = "",
409
+ turnId = ""
410
+ } = {}) {
411
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
412
+ const normalizedThreadId = normalizeCodexRunText(threadId);
413
+ const assistantText = normalizeCodexRunText(text);
414
+ const normalizedItemId = normalizeCodexRunText(itemId) || codexAppServerNotificationItemId(notification);
415
+ if (!normalizedSessionId || !normalizedThreadId || !assistantText || !normalizedItemId) {
416
+ return {
417
+ recorded: false,
418
+ reason: normalizedItemId ? "empty" : "missing_item_id"
419
+ };
420
+ }
421
+
422
+ const runtime = await createRuntime();
423
+ const session = await runtime.getSession(normalizedSessionId);
424
+ const currentTurn = turnState(session);
425
+ const currentTurnId = normalizeCodexRunText(currentTurn.turnId);
426
+ const normalizedTurnId = normalizeCodexRunText(turnId) || codexAppServerNotificationTurnId(notification);
427
+ if (!normalizedTurnId) {
428
+ return { recorded: false, reason: "missing_turn_id" };
429
+ }
430
+ const key = codexAppServerResultFinalizationKey(normalizedSessionId, normalizedThreadId, normalizedTurnId);
431
+ const existing = codexAppServerFinalAssistantResults.get(key) || null;
432
+ if (
433
+ !existing &&
434
+ codexAppServerTurnAwaitsProviderIdentity(currentTurn, normalizedThreadId, normalizedTurnId)
435
+ ) {
436
+ return {
437
+ recorded: false,
438
+ reason: "turn_identity_pending",
439
+ turnId: normalizedTurnId
440
+ };
441
+ }
442
+ const completedOwnedTurn = Boolean(codexAppServerProcessedResultEvent(session, normalizedThreadId, normalizedTurnId));
443
+ const stoppedRecovery = source === "provider-recovery" &&
444
+ currentTurn.status === "observation_lost" &&
445
+ currentTurn.threadId === normalizedThreadId &&
446
+ currentTurnId === normalizedTurnId &&
447
+ !currentTurn.active;
448
+ if (
449
+ !existing && !completedOwnedTurn && !stoppedRecovery &&
450
+ !codexAppServerTurnCanReceiveProviderCompletion(currentTurn, normalizedThreadId, normalizedTurnId)
451
+ ) {
452
+ if (!codexAppServerFinalizedTurns.has(key)) {
453
+ codexAppServerFinalizedTurns.add(key);
454
+ debugLog("appServerAgentResult.stale", {
455
+ currentState: currentTurn.state,
456
+ currentStatus: currentTurn.status,
457
+ currentThreadId: currentTurn.threadId,
458
+ currentTurnId: currentTurn.turnId,
459
+ sessionId: normalizedSessionId,
460
+ source: normalizeCodexRunText(source),
461
+ threadId: normalizedThreadId,
462
+ turnId: normalizedTurnId
463
+ });
464
+ }
465
+ return {
466
+ recorded: false,
467
+ reason: "stale_turn_state",
468
+ turnId: normalizedTurnId
469
+ };
470
+ }
471
+
472
+ if (source !== "provider-recovery") {
473
+ // Live item IDs and saved-history IDs differ in native Codex threads.
474
+ // Only saved history supplies durable replies; a live completion triggers
475
+ // that read and retires its provisional stream in the same publication.
476
+ const segments = await recoverCodexAppServerAssistantSegmentsFromProvider(
477
+ normalizedSessionId, normalizedThreadId, normalizedTurnId
478
+ );
479
+ if (!segments.length) throw new Error("Codex's completed reply is missing from its saved turn.");
480
+ // A history read can already include a later native reply. Keep each exact
481
+ // native item identity separate from the original last-publication retirement.
482
+ const sameNativeItem = segments.some(segment => segment.itemId === normalizedItemId);
483
+ let result;
484
+ for (const segment of segments) {
485
+ result = await recordCodexAppServerFinalAssistantResult({
486
+ ...segment,
487
+ sessionId: normalizedSessionId,
488
+ source: "provider-recovery",
489
+ streamItemId: segment === segments.at(-1) ? normalizedItemId : "",
490
+ outputItemId: sameNativeItem ? segment.itemId : undefined,
491
+ threadId: normalizedThreadId,
492
+ turnId: normalizedTurnId
493
+ });
494
+ }
495
+ return result;
496
+ }
497
+ if (existing?.itemId === normalizedItemId && existing.text === assistantText && existing.conversationTurn) {
498
+ if (streamItemId) await persistCodexAppServerAssistantReply(runtime, normalizedSessionId, existing, streamItemId, outputItemId);
499
+ return { ...existing, recorded: true, reason: "duplicate" };
500
+ }
501
+ const record = {
502
+ itemId: normalizedItemId,
503
+ source: normalizeCodexRunText(source),
504
+ text: assistantText,
505
+ threadId: normalizedThreadId,
506
+ turnId: normalizedTurnId
507
+ };
508
+ codexAppServerFinalAssistantResults.set(key, record);
509
+
510
+ try {
511
+ await persistCodexAppServerAssistantReply(runtime, normalizedSessionId, record, streamItemId, outputItemId);
512
+ debugLog("appServerFinalAssistantResult.recorded", {
513
+ itemId: record.itemId,
514
+ sessionId: normalizedSessionId,
515
+ source: record.source,
516
+ threadId: normalizedThreadId,
517
+ turnId: normalizedTurnId
518
+ });
519
+ return {
520
+ ...record,
521
+ recorded: true,
522
+ reason: existing ? "updated" : "recorded"
523
+ };
524
+ } catch (error) {
525
+ if (existing) {
526
+ codexAppServerFinalAssistantResults.set(key, existing);
527
+ } else {
528
+ codexAppServerFinalAssistantResults.delete(key);
529
+ }
530
+ throw error;
531
+ }
532
+ }
533
+
534
+ function cleanupCodexAppServerUntrackedTurn(threadId = "", turnId = "") {
535
+ cleanupCodexAppServerReasoningTurn(threadId, turnId);
536
+ const normalizedThreadId = normalizeCodexRunText(threadId);
537
+ const normalizedTurnId = normalizeCodexRunText(turnId);
538
+ if (!normalizedThreadId) {
539
+ return;
540
+ }
541
+ const markers = [
542
+ `:${normalizedThreadId}:*:`,
543
+ ...(normalizedTurnId ? [`:${normalizedThreadId}:${normalizedTurnId}:`] : [])
544
+ ];
545
+ for (const set of [
546
+ codexAppServerLiveProgressItems,
547
+ codexAppServerLiveProgressFingerprints,
548
+ codexAppServerMirroredTerminalItems
549
+ ]) {
550
+ for (const key of set) {
551
+ if (markers.some((marker) => key.includes(marker))) {
552
+ set.delete(key);
553
+ }
554
+ }
555
+ }
556
+ }
557
+
558
+ function codexAppServerStreamMessage(classification) {
559
+ return {
560
+ nativeIdentity: { threadId: classification.threadId, turnId: classification.turnId },
561
+ turnId: `${classification.threadId}:${classification.turnId}`,
562
+ outputId: codexAppServerConversationMessageId(
563
+ classification.threadId, classification.turnId, "assistant-item", classification.itemId
564
+ ),
565
+ messageId: codexAppServerConversationMessageId(
566
+ classification.threadId, classification.turnId, "assistant-item", classification.itemId
567
+ ),
568
+ role: classification.role,
569
+ delta: classification.delta
570
+ };
571
+ }
572
+
573
+ async function writeCodexAppServerStream(sessionId, classification) {
574
+ if (!classification.itemId || !classification.turnId) return;
575
+ const store = await createStore(sessionId);
576
+ const conversationStream = await store.mutateSession(sessionId, async () => {
577
+ const run = await readCodexAppServerAgentRunForSession(store, sessionId);
578
+ const turn = turnStateFromAgentRun(run || {});
579
+ if (!codexAppServerTurnCanReceiveProviderActivity(turn, classification.threadId, classification.turnId)) return null;
580
+ return store.updateConversationStream(sessionId, codexAppServerStreamMessage(classification));
581
+ });
582
+ if (conversationStream) {
583
+ await publish(sessionId, {
584
+ payload: { conversationStream },
585
+ reason: "assistant-stream"
586
+ });
587
+ }
588
+ }
589
+
590
+ async function writeCodexAppServerLiveProgress(sessionId = "", threadId = "", notification = {}) {
591
+ // Phase-less providers also send short updates as agent messages.
592
+ // Reasoning items are recorded separately from these user-facing updates.
593
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
594
+ const normalizedThreadId = normalizeCodexRunText(threadId);
595
+ const candidate = codexAppServerLiveProgressCandidate(notification);
596
+ const text = codexAppServerLiveProgressText(notification);
597
+ if (!normalizedSessionId || !normalizedThreadId || !text) {
598
+ return null;
599
+ }
600
+ const store = await createStore(normalizedSessionId);
601
+ const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
602
+ const turn = turnStateFromAgentRun(run || {});
603
+ if (!codexAppServerTurnCanReceiveProviderActivity(
604
+ turn,
605
+ normalizedThreadId,
606
+ codexAppServerNotificationTurnId(notification)
607
+ )) {
608
+ return null;
609
+ }
610
+ const key = codexAppServerLiveProgressKey(normalizedSessionId, normalizedThreadId, notification);
611
+ if (!key) {
612
+ return null;
613
+ }
614
+ const fingerprintKey = codexAppServerLiveProgressFingerprintKey(
615
+ normalizedSessionId,
616
+ normalizedThreadId,
617
+ codexAppServerNotificationTurnId(notification) || turn.turnId,
618
+ text
619
+ );
620
+ const streamMessageId = codexAppServerConversationMessageId(
621
+ normalizedThreadId, codexAppServerNotificationTurnId(notification),
622
+ "assistant-item", codexAppServerNotificationItemId(notification)
623
+ );
624
+ if (
625
+ codexAppServerLiveProgressItems.has(key) ||
626
+ codexAppServerLiveProgressFingerprints.has(fingerprintKey)
627
+ ) {
628
+ await publish(normalizedSessionId, {
629
+ payload: { conversationStream: store.completeConversationStreamMessage(normalizedSessionId, streamMessageId) },
630
+ reason: "assistant-stream"
631
+ });
632
+ return null;
633
+ }
634
+ codexAppServerLiveProgressItems.add(key);
635
+ codexAppServerLiveProgressFingerprints.add(fingerprintKey);
636
+ let written = null;
637
+ try {
638
+ const role = !candidate.phase || candidate.phase === "commentary" ? "commentary" : "thinking";
639
+ const writer = role === "commentary"
640
+ ? store.writeConversationCommentaryMessage
641
+ : store.writeConversationThinkingMessage;
642
+ written = await writer.call(store, normalizedSessionId, {
643
+ ...(streamMessageId ? { outputId: streamMessageId } : {}),
644
+ messageId: codexAppServerConversationMessageId(
645
+ normalizedThreadId,
646
+ codexAppServerNotificationTurnId(notification) || turn.turnId,
647
+ role,
648
+ text
649
+ ),
650
+ requireOpenTurn: false,
651
+ nativeIdentity: { threadId: normalizedThreadId, turnId: codexAppServerNotificationTurnId(notification) || turn.turnId },
652
+ text
653
+ });
654
+ if (!written) {
655
+ codexAppServerLiveProgressItems.delete(key);
656
+ codexAppServerLiveProgressFingerprints.delete(fingerprintKey);
657
+ }
658
+ const payload = {
659
+ conversationStream: store.completeConversationStreamMessage(normalizedSessionId, streamMessageId)
660
+ };
661
+ if (written) {
662
+ payload.conversationLogPatch = { turn: written, type: "upsert-turn" };
663
+ }
664
+ await publish(normalizedSessionId, {
665
+ payload,
666
+ reason: role === "commentary"
667
+ ? "codex-app-server-commentary"
668
+ : "codex-app-server-live-progress"
669
+ });
670
+ return written;
671
+ } catch (error) {
672
+ if (!written) {
673
+ codexAppServerLiveProgressItems.delete(key);
674
+ codexAppServerLiveProgressFingerprints.delete(fingerprintKey);
675
+ }
676
+ throw error;
677
+ }
678
+ }
679
+
680
+ async function recoverCodexAppServerAssistantSegmentsFromProvider(sessionId = "", threadId = "", turnId = "") {
681
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
682
+ const normalizedThreadId = normalizeCodexRunText(threadId);
683
+ const normalizedTurnId = normalizeCodexRunText(turnId);
684
+ if (!normalizedSessionId || !normalizedThreadId || !normalizedTurnId) {
685
+ return [];
686
+ }
687
+ const runtime = await createRuntime();
688
+ const session = await runtime.getSession(normalizedSessionId);
689
+ const provider = await acquireProvider({ sessionId: normalizedSessionId, runtime, session });
690
+ if (!provider) {
691
+ return [];
692
+ }
693
+ // Page from the newest turn instead of hydrating the entire conversation.
694
+ // A goal may already have started its successor when completion arrives.
695
+ let cursor;
696
+ let assistantSegments = [];
697
+ do {
698
+ const page = await provider.listThreadTurns(normalizedThreadId, {
699
+ limit: 1, itemsView: "full", sortDirection: "desc", ...(cursor ? { cursor } : {})
700
+ });
701
+ const turn = page.data.find((candidate) => candidate.id === normalizedTurnId);
702
+ if (turn) {
703
+ assistantSegments = codexAppServerProviderThreadAssistantSegments({ turns: [turn] }, normalizedTurnId);
704
+ break;
705
+ }
706
+ cursor = page.nextCursor;
707
+ } while (cursor);
708
+ if (assistantSegments.length) {
709
+ debugLog("appServerAgentResult.recovered", {
710
+ assistantSegmentCount: assistantSegments.length,
711
+ sessionId: normalizedSessionId,
712
+ threadId: normalizedThreadId,
713
+ turnId: normalizedTurnId
714
+ });
715
+ }
716
+ return assistantSegments;
717
+ }
718
+
719
+ async function submitCodexAppServerAssistantResult(sessionId = "", threadId = "", turnId = "", {
720
+ recoverFromProvider = false
721
+ } = {}) {
722
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
723
+ let finalResult = readCodexAppServerFinalAssistantResult(normalizedSessionId, threadId, turnId);
724
+ let assistantText = normalizeCodexRunText(finalResult?.text);
725
+ const reasoningText = readCodexAppServerReasoningText(threadId, turnId);
726
+ try {
727
+ if (normalizedSessionId && recoverFromProvider) {
728
+ const recoveredSegments = await recoverCodexAppServerAssistantSegmentsFromProvider(
729
+ normalizedSessionId,
730
+ threadId,
731
+ turnId
732
+ );
733
+ for (const recoveredSegment of recoveredSegments) {
734
+ await recordCodexAppServerFinalAssistantResult({
735
+ itemId: recoveredSegment.itemId,
736
+ sessionId: normalizedSessionId,
737
+ source: "provider-recovery",
738
+ text: recoveredSegment.text,
739
+ threadId,
740
+ turnId
741
+ });
742
+ }
743
+ if (recoveredSegments.length) {
744
+ finalResult = readCodexAppServerFinalAssistantResult(normalizedSessionId, threadId, turnId);
745
+ assistantText = normalizeCodexRunText(finalResult?.text);
746
+ }
747
+ }
748
+ if (!normalizedSessionId || !assistantText && !reasoningText) {
749
+ return {
750
+ ok: false,
751
+ processed: false,
752
+ reason: "empty"
753
+ };
754
+ }
755
+ const runtime = await createRuntime();
756
+ if (reasoningText) {
757
+ await flushCodexAppServerReasoningPersist(normalizedSessionId, threadId, turnId);
758
+ }
759
+ if (!assistantText) {
760
+ return {
761
+ ok: true,
762
+ processed: false,
763
+ reason: "missing_assistant_text"
764
+ };
765
+ }
766
+ await persistCodexAppServerAssistantReply(runtime, normalizedSessionId, finalResult);
767
+ return {
768
+ ok: true,
769
+ processed: true,
770
+ reason: "assistant_response"
771
+ };
772
+ } catch (error) {
773
+ debugLog("appServerAgentResult.error", {
774
+ error: debugError(error),
775
+ sessionId: normalizedSessionId,
776
+ threadId: normalizeCodexRunText(threadId),
777
+ turnId: normalizeCodexRunText(turnId)
778
+ });
779
+ return {
780
+ error: normalizeCodexRunText(error?.error || error?.message || error) || "Codex app-server response could not be processed.",
781
+ ok: false,
782
+ processed: false,
783
+ reason: "error"
784
+ };
785
+ } finally {
786
+ cleanupCodexAppServerReasoningTurn(threadId, turnId);
787
+ }
788
+ }
789
+
790
+ function codexAppServerSnapshotCursor(run = {}) {
791
+ const cursor = run?.providerSnapshotCursor;
792
+ if (!isPlainObject(cursor)) {
793
+ return {
794
+ itemId: "",
795
+ turnId: ""
796
+ };
797
+ }
798
+ return {
799
+ itemId: normalizeCodexRunText(cursor.itemId),
800
+ turnId: normalizeCodexRunText(cursor.turnId)
801
+ };
802
+ }
803
+
804
+ async function recordCodexAppServerSnapshotCursor(store, sessionId = "", {
805
+ itemId = "",
806
+ turnId = ""
807
+ } = {}) {
808
+ const normalizedItemId = normalizeCodexRunText(itemId);
809
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
810
+ const normalizedTurnId = normalizeCodexRunText(turnId);
811
+ if (!normalizedItemId || !normalizedSessionId || !normalizedTurnId) {
812
+ return;
813
+ }
814
+ const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
815
+ const current = codexAppServerSnapshotCursor(run);
816
+ if (current.itemId === normalizedItemId && current.turnId === normalizedTurnId) {
817
+ return;
818
+ }
819
+ await store.writeAgentRunEvent(normalizedSessionId, CODEX_APP_SERVER_AGENT_RUN_ID, {
820
+ event: {
821
+ kind: "codex-app-server-thread-snapshot-observed",
822
+ message: "",
823
+ providerThreadId: turnStateFromAgentRun(run || {}).threadId,
824
+ providerTurnId: normalizedTurnId
825
+ },
826
+ patch: {
827
+ providerSnapshotCursor: {
828
+ itemId: normalizedItemId,
829
+ turnId: normalizedTurnId
830
+ }
831
+ }
832
+ });
833
+ }
834
+
835
+ async function reconcileCodexAppServerObservedTurnItems(
836
+ sessionId = "",
837
+ threadId = "",
838
+ observedTurn = null,
839
+ provider = null
840
+ ) {
841
+ const normalizedSessionId = normalizeCodexRunText(sessionId);
842
+ const normalizedThreadId = normalizeCodexRunText(threadId);
843
+ const normalizedTurnId = normalizeCodexRunText(observedTurn?.id);
844
+ const items = (Array.isArray(observedTurn?.items) ? observedTurn.items : [])
845
+ .filter((item) => isPlainObject(item) && normalizeCodexRunText(item.id));
846
+ if (!normalizedSessionId || !normalizedThreadId || !normalizedTurnId || !items.length) {
847
+ return;
848
+ }
849
+ try {
850
+ const store = await createStore(normalizedSessionId);
851
+ const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
852
+ for (const segment of codexAppServerProviderThreadAssistantSegments({ turns: [observedTurn] }, normalizedTurnId)) {
853
+ await recordCodexAppServerFinalAssistantResult({
854
+ ...segment,
855
+ sessionId: normalizedSessionId,
856
+ source: "provider-recovery",
857
+ threadId: normalizedThreadId,
858
+ turnId: normalizedTurnId
859
+ });
860
+ }
861
+ if (codexAppServerRunInputSource(run) !== "terminal") {
862
+ return;
863
+ }
864
+ const cursor = codexAppServerSnapshotCursor(run);
865
+ const cursorIndex = cursor.turnId === normalizedTurnId
866
+ ? items.findIndex((item) => normalizeCodexRunText(item.id) === cursor.itemId)
867
+ : -1;
868
+ const unseenItems = cursorIndex >= 0
869
+ ? items.slice(cursorIndex + 1)
870
+ : items.slice(-snapshotRecoveryItemLimit);
871
+ for (const item of unseenItems) {
872
+ if (!codexAppServerAssistantItemText(item)) {
873
+ continue;
874
+ }
875
+ const notification = {
876
+ method: "item/completed",
877
+ params: {
878
+ item,
879
+ threadId: normalizedThreadId,
880
+ turnId: normalizedTurnId
881
+ }
882
+ };
883
+ await writeCodexAppServerLiveProgress(
884
+ normalizedSessionId,
885
+ normalizedThreadId,
886
+ notification
887
+ );
888
+ await mirrorCodexAppServerTerminalAssistantMessage(
889
+ normalizedSessionId,
890
+ normalizedThreadId,
891
+ notification
892
+ );
893
+ }
894
+ await recordCodexAppServerSnapshotCursor(store, normalizedSessionId, {
895
+ itemId: normalizeCodexRunText(items.at(-1)?.id),
896
+ turnId: normalizedTurnId
897
+ });
898
+ } catch (error) {
899
+ await provider.failObservation(error);
900
+ throw error;
901
+ }
902
+ }
903
+
904
+ return {
905
+ finalizedTurns: codexAppServerFinalizedTurns,
906
+ finalAssistantResults: codexAppServerFinalAssistantResults,
907
+ reasoningTurns: codexAppServerReasoningTurns,
908
+ reasoningPersistQueues: codexAppServerReasoningPersistQueues,
909
+ liveProgressItems: codexAppServerLiveProgressItems,
910
+ liveProgressFingerprints: codexAppServerLiveProgressFingerprints,
911
+ mirroredTerminalItems: codexAppServerMirroredTerminalItems,
912
+ resultFinalizationKey: codexAppServerResultFinalizationKey,
913
+ liveProgressCandidate: codexAppServerLiveProgressCandidate,
914
+ conversationMessageId: codexAppServerConversationMessageId,
915
+ readAgentRunForSession: readCodexAppServerAgentRunForSession,
916
+ readFinalAssistantResult: readCodexAppServerFinalAssistantResult,
917
+ persistAssistantReply: persistCodexAppServerAssistantReply,
918
+ recordFinalAssistantResult: recordCodexAppServerFinalAssistantResult,
919
+ recordReasoningForSession: recordCodexAppServerReasoningForSession,
920
+ flushReasoningPersist: flushCodexAppServerReasoningPersist,
921
+ splitReasoningTurn: splitCodexAppServerReasoningTurn,
922
+ cleanupUntrackedTurn: cleanupCodexAppServerUntrackedTurn,
923
+ streamMessage: codexAppServerStreamMessage,
924
+ writeStream: writeCodexAppServerStream,
925
+ writeLiveProgress: writeCodexAppServerLiveProgress,
926
+ submitAssistantResult: submitCodexAppServerAssistantResult,
927
+ writeMirroredTerminalMessage: writeMirroredCodexAppServerTerminalMessage,
928
+ mirrorTerminalAssistantMessage: mirrorCodexAppServerTerminalAssistantMessage,
929
+ reconcileObservedTurnItems: reconcileCodexAppServerObservedTurnItems
930
+ };
931
+ }