@jskit-ai/assistant-core 0.2.7 → 0.2.8

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 (214) hide show
  1. package/examples/conversation/package.json +5 -5
  2. package/package.json +18 -7
  3. package/src/client/browser.js +21 -0
  4. package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
  5. package/src/client/conversation/AssistantComposerActions.vue +1 -1
  6. package/src/client/conversation/AssistantConversationElement.vue +14 -52
  7. package/src/client/conversation/AssistantGoalControl.vue +13 -10
  8. package/src/client/conversation/AssistantPromptInput.vue +6 -91
  9. package/src/client/conversation/AssistantTranscript.css +505 -0
  10. package/src/client/conversation/AssistantTranscript.vue +24 -1197
  11. package/src/client/conversation/attachmentFiles.js +158 -0
  12. package/src/client/conversation/attachmentQueueItems.js +121 -0
  13. package/src/client/conversation/attachmentUploadRows.js +265 -0
  14. package/src/client/conversation/conversationDisplay.js +54 -0
  15. package/src/client/conversation/messageDelivery.js +13 -9
  16. package/src/client/conversation/retainedConversation.js +14 -3
  17. package/src/client/conversation/submitText.js +13 -2
  18. package/src/client/conversation/transcriptEntries.js +225 -0
  19. package/src/client/conversation/useAssistantAttachments.js +86 -388
  20. package/src/client/conversation/useAssistantQuestions.js +16 -3
  21. package/src/client/conversation/useAssistantSuggestions.js +16 -2
  22. package/src/client/conversation/useScrollToBottom.js +17 -19
  23. package/src/client/conversation/useTextareaAutoGrow.js +115 -0
  24. package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
  25. package/src/client/lib/assistantApi.js +192 -161
  26. package/src/client/types.js +110 -0
  27. package/src/server/conversation/attachments.js +21 -6
  28. package/src/server/conversation/boundedToolLoop.js +1 -0
  29. package/src/server/conversation/claudeClient.js +71 -20
  30. package/src/server/conversation/claudeCommandRun.js +191 -0
  31. package/src/server/conversation/claudeHistory.js +7 -0
  32. package/src/server/conversation/claudeNativeTurn.js +708 -0
  33. package/src/server/conversation/claudeProcess.js +186 -112
  34. package/src/server/conversation/claudeProcessStart.js +214 -0
  35. package/src/server/conversation/claudePromptDelivery.js +183 -0
  36. package/src/server/conversation/claudeSessionCleanup.js +248 -0
  37. package/src/server/conversation/claudeTools.js +15 -3
  38. package/src/server/conversation/claudeTurn.js +212 -1388
  39. package/src/server/conversation/codexAccountReader.js +168 -0
  40. package/src/server/conversation/codexAuthPreflight.js +5 -4
  41. package/src/server/conversation/codexClient.js +18 -6
  42. package/src/server/conversation/codexConfiguration.js +8 -844
  43. package/src/server/conversation/codexConversationStops.js +377 -0
  44. package/src/server/conversation/codexConversationStore.js +119 -85
  45. package/src/server/conversation/codexConversationTurns.js +378 -0
  46. package/src/server/conversation/codexConversations.js +134 -866
  47. package/src/server/conversation/codexDetachedConversation.js +203 -0
  48. package/src/server/conversation/codexDetachedTurn.js +145 -104
  49. package/src/server/conversation/codexEvents.js +5 -200
  50. package/src/server/conversation/codexGoalCommands.js +14 -4
  51. package/src/server/conversation/codexHelperExecution.js +57 -57
  52. package/src/server/conversation/codexHelperThreadLedger.js +30 -288
  53. package/src/server/conversation/codexHelperThreadRecord.js +304 -0
  54. package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
  55. package/src/server/conversation/codexHelperThreads.js +94 -93
  56. package/src/server/conversation/codexHistoryAdapter.js +13 -8
  57. package/src/server/conversation/codexIsolation.js +672 -0
  58. package/src/server/conversation/codexLiveItems.js +379 -0
  59. package/src/server/conversation/codexMessageCommands.js +68 -286
  60. package/src/server/conversation/codexMessageDelivery.js +46 -37
  61. package/src/server/conversation/codexModelCatalog.js +86 -7
  62. package/src/server/conversation/codexNotificationObserver.js +81 -88
  63. package/src/server/conversation/codexNotificationQueue.js +21 -10
  64. package/src/server/conversation/codexObservationControl.js +105 -89
  65. package/src/server/conversation/codexOutput.js +84 -419
  66. package/src/server/conversation/codexProcess.js +29 -1053
  67. package/src/server/conversation/codexProcessLeader.js +1 -0
  68. package/src/server/conversation/codexPromptDispatch.js +241 -0
  69. package/src/server/conversation/codexProvider.js +147 -1807
  70. package/src/server/conversation/codexProviderAuth.js +348 -0
  71. package/src/server/conversation/codexProviderInvalidation.js +371 -0
  72. package/src/server/conversation/codexProviderInventories.js +437 -0
  73. package/src/server/conversation/codexProviderOwner.js +149 -957
  74. package/src/server/conversation/codexProviderRelease.js +465 -0
  75. package/src/server/conversation/codexProviderSelection.js +26 -15
  76. package/src/server/conversation/codexProviderThreads.js +698 -0
  77. package/src/server/conversation/codexProviderTurns.js +453 -0
  78. package/src/server/conversation/codexReasoning.js +32 -23
  79. package/src/server/conversation/codexRenewalCommands.js +18 -6
  80. package/src/server/conversation/codexRenewalThreads.js +7 -22
  81. package/src/server/conversation/codexResultSettlement.js +70 -88
  82. package/src/server/conversation/codexRunJournal.js +91 -97
  83. package/src/server/conversation/codexRuntime.js +426 -0
  84. package/src/server/conversation/codexRuntimeInventory.js +113 -0
  85. package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
  86. package/src/server/conversation/codexRuntimeStart.js +431 -0
  87. package/src/server/conversation/codexRuntimeStop.js +324 -0
  88. package/src/server/conversation/codexSessionCleanup.js +25 -10
  89. package/src/server/conversation/codexSessionReconciliation.js +21 -7
  90. package/src/server/conversation/codexThreadReadiness.js +49 -52
  91. package/src/server/conversation/codexThreadSnapshot.js +210 -0
  92. package/src/server/conversation/codexThreadStatus.js +48 -62
  93. package/src/server/conversation/codexTurn.js +171 -55
  94. package/src/server/conversation/codexTurnActivity.js +210 -0
  95. package/src/server/conversation/codexTurnControl.js +44 -48
  96. package/src/server/conversation/codexTurnEndings.js +610 -0
  97. package/src/server/conversation/codexTurnRecovery.js +97 -848
  98. package/src/server/conversation/codexTurnState.js +138 -111
  99. package/src/server/conversation/completedResponses.js +227 -0
  100. package/src/server/conversation/configuration.js +2 -2
  101. package/src/server/conversation/continuity.js +43 -6
  102. package/src/server/conversation/conversationDelivery.js +394 -0
  103. package/src/server/conversation/conversationEntries.js +365 -0
  104. package/src/server/conversation/conversationEvents.js +192 -0
  105. package/src/server/conversation/conversationGoals.js +187 -0
  106. package/src/server/conversation/conversationHandles.js +237 -0
  107. package/src/server/conversation/conversationReads.js +206 -0
  108. package/src/server/conversation/conversationRefusals.js +58 -0
  109. package/src/server/conversation/conversationReplacement.js +254 -0
  110. package/src/server/conversation/conversationSending.js +273 -0
  111. package/src/server/conversation/conversationStopping.js +121 -0
  112. package/src/server/conversation/conversationTurnDelivery.js +413 -0
  113. package/src/server/conversation/conversationTurns.js +511 -0
  114. package/src/server/conversation/deliveryTiming.js +25 -3
  115. package/src/server/conversation/fileStorage.js +11 -6
  116. package/src/server/conversation/hookBridge.js +14 -3
  117. package/src/server/conversation/index.js +1 -1
  118. package/src/server/conversation/localExecution.js +82 -625
  119. package/src/server/conversation/nativeCommandCallbacks.js +10 -0
  120. package/src/server/conversation/nativeConversationLifecycle.js +379 -0
  121. package/src/server/conversation/nativeHistoryExport.js +27 -18
  122. package/src/server/conversation/normalize.js +9 -0
  123. package/src/server/conversation/openCodeClient.js +295 -366
  124. package/src/server/conversation/openCodeNativeHistory.js +141 -0
  125. package/src/server/conversation/openCodeNativeSession.js +79 -0
  126. package/src/server/conversation/openCodeProcess.js +109 -53
  127. package/src/server/conversation/openCodeProcessOwner.js +565 -0
  128. package/src/server/conversation/openCodeRuntime.js +111 -1814
  129. package/src/server/conversation/openCodeSessionTurns.js +547 -0
  130. package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
  131. package/src/server/conversation/openCodeTools.js +28 -14
  132. package/src/server/conversation/openCodeTurn.js +80 -74
  133. package/src/server/conversation/openCodeTurnMonitor.js +388 -0
  134. package/src/server/conversation/processIdentity.js +582 -0
  135. package/src/server/conversation/provider.js +21 -6
  136. package/src/server/conversation/providers/api.js +268 -107
  137. package/src/server/conversation/providers/claude.js +29 -6
  138. package/src/server/conversation/providers/claudeDriver.js +325 -250
  139. package/src/server/conversation/providers/codex.js +69 -33
  140. package/src/server/conversation/providers/codexDriver.js +503 -1081
  141. package/src/server/conversation/providers/codexDriverConnection.js +331 -0
  142. package/src/server/conversation/providers/codexDriverTurns.js +464 -0
  143. package/src/server/conversation/providers/opencode.js +30 -4
  144. package/src/server/conversation/providers/opencodeDriver.js +321 -659
  145. package/src/server/conversation/providers/opencodeStandalone.js +475 -0
  146. package/src/server/conversation/providers/scoped.js +64 -19
  147. package/src/server/conversation/runtime.js +171 -2863
  148. package/src/server/conversation/storage.js +91 -50
  149. package/src/server/conversation/streams.js +15 -2
  150. package/src/server/conversation/structuredOutput.js +7 -11
  151. package/src/server/conversation/systemPrompt.js +13 -1
  152. package/src/server/conversation/tools.js +47 -15
  153. package/src/server/conversation/transcript.js +49 -20
  154. package/src/server/hosts/claudeTurn.js +2 -4
  155. package/src/server/hosts/codexConfiguration.js +6 -8
  156. package/src/server/hosts/codexEvents.js +4 -2
  157. package/src/server/hosts/codexProcess.js +1 -1
  158. package/src/server/hosts/codexProvider.js +11 -9
  159. package/src/server/hosts/codexTurn.js +3 -3
  160. package/src/server/hosts/openCodeClient.js +4 -2
  161. package/src/server/index.js +0 -2
  162. package/src/server/lib/aiConnectionClient.js +35 -25
  163. package/src/server/lib/ndjson.js +1 -0
  164. package/src/server/lib/providers/anthropicClient.js +15 -6
  165. package/src/server/lib/providers/common.js +10 -6
  166. package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
  167. package/src/server/lib/serviceToolCatalog.js +29 -283
  168. package/src/server/lib/serviceToolEntries.js +270 -0
  169. package/src/server/types.js +78 -0
  170. package/src/shared/assistantResource.js +0 -23
  171. package/src/shared/conversation/answerChoiceSugar.js +2 -14
  172. package/src/shared/conversation/messageInputSugar.js +18 -0
  173. package/src/shared/conversation/numberedQuestionSugar.js +6 -16
  174. package/src/shared/conversation/pagination.js +1 -1
  175. package/src/shared/conversation/questions.js +1 -0
  176. package/src/shared/conversation/streaming.js +1 -0
  177. package/src/shared/index.js +0 -1
  178. package/src/testing/conversationStorageContract.js +20 -0
  179. package/test/aiConnectionClient.test.js +42 -1
  180. package/test/apiConversationDriver.test.js +217 -38
  181. package/test/assistantBoundedToolLoop.test.js +1 -1
  182. package/test/claudeConversationSession.test.js +15 -11
  183. package/test/claudeInstructionResume.test.js +1 -1
  184. package/test/claudeProcess.test.js +9 -9
  185. package/test/claudeTurn.test.js +21 -8
  186. package/test/codexAccountChange.test.js +5 -3
  187. package/test/codexConversationSession.test.js +22 -21
  188. package/test/codexEvents.test.js +3 -1
  189. package/test/codexHelperDeletion.test.js +3 -7
  190. package/test/codexIsolation.test.js +6 -6
  191. package/test/codexModelCatalog.test.js +6 -7
  192. package/test/codexProcess.test.js +3 -3
  193. package/test/codexThreadSelection.test.js +4 -1
  194. package/test/codexTurn.test.js +29 -20
  195. package/test/conversationApiFacilities.test.js +11 -9
  196. package/test/conversationAttachments.test.js +3 -3
  197. package/test/conversationConfiguration.test.js +4 -4
  198. package/test/conversationNativeStorage.test.js +11 -10
  199. package/test/conversationReplacement.test.js +13 -13
  200. package/test/conversationRuntime.test.js +9 -5
  201. package/test/conversationSession.test.js +78 -22
  202. package/test/conversationTools.test.js +47 -26
  203. package/test/fileConversationStorage.test.js +10 -3
  204. package/test/fixtures/codexHelperThreadLedger.js +3 -3
  205. package/test/frameworkActions.test.js +33 -18
  206. package/test/localConversationExecution.test.js +4 -4
  207. package/test/localExecution.test.js +1 -1
  208. package/test/openCodeConnectionChange.test.js +3 -2
  209. package/test/openCodeRuntime.test.js +8 -7
  210. package/test/openCodeTools.test.js +1 -1
  211. package/test/opencodeConversationSession.test.js +4 -3
  212. package/test/serviceToolCatalog.test.js +3 -8
  213. package/src/server/lib/assistantCompletion.js +0 -190
  214. package/src/server/lib/assistantToolLoop.js +0 -259
@@ -0,0 +1,379 @@
1
+ import crypto from "node:crypto";
2
+ import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
3
+ import { codexAppServerConversationMessageId, codexAppServerTurnCanReceiveProviderActivity } from "./codexTurnState.js";
4
+ import {
5
+ codexAppServerAgentMessageText,
6
+ codexAppServerAssistantItemText,
7
+ classifyCodexAppServerEvent,
8
+ codexAppServerNotificationEvent,
9
+ codexAppServerNotificationEventType,
10
+ codexAppServerNotificationEventPayload,
11
+ codexAppServerNotificationItem,
12
+ codexAppServerNotificationItemId,
13
+ codexAppServerNotificationTurnId
14
+ } from "./codexEvents.js";
15
+
16
+ export function codexAppServerLiveProgressCandidate(notification = {}) {
17
+ const method = normalizeText(notification.method);
18
+ const event = codexAppServerNotificationEvent(notification);
19
+ if (isRecord(event)) {
20
+ const eventType = codexAppServerNotificationEventType(notification, event);
21
+ const payload = codexAppServerNotificationEventPayload(notification, event);
22
+ const payloadType = normalizeText(payload.type);
23
+ const phase = normalizeText(payload.phase || event.phase);
24
+ if (eventType === "event_msg" && payloadType === "agent_message" && phase && phase !== "final_answer") {
25
+ return {
26
+ explicit: Boolean(phase),
27
+ phase,
28
+ source: "event",
29
+ text: codexAppServerAgentMessageText(payload)
30
+ };
31
+ }
32
+ }
33
+
34
+ if (method !== "item/completed") {
35
+ return null;
36
+ }
37
+ const item = codexAppServerNotificationItem(notification);
38
+ const text = codexAppServerAssistantItemText(item);
39
+ if (!text) {
40
+ return null;
41
+ }
42
+ const phase = normalizeText(item?.phase || item?.purpose || item?.category);
43
+ if (phase === "final_answer") {
44
+ return null;
45
+ }
46
+ return {
47
+ explicit: ["commentary", "progress", "status", "thinking"].includes(phase),
48
+ phase,
49
+ source: "item",
50
+ text
51
+ };
52
+ }
53
+
54
+ function codexAppServerLiveProgressKey(sessionId = "", threadId = "", notification = {}) {
55
+ const itemId = codexAppServerNotificationItemId(notification);
56
+ if (!itemId) {
57
+ return "";
58
+ }
59
+ return [
60
+ normalizeText(sessionId),
61
+ normalizeText(threadId),
62
+ codexAppServerNotificationTurnId(notification) || "*",
63
+ "live-progress",
64
+ itemId
65
+ ].join(":");
66
+ }
67
+
68
+ function codexAppServerLiveProgressFingerprintKey(
69
+ sessionId = "",
70
+ threadId = "",
71
+ turnId = "",
72
+ text = ""
73
+ ) {
74
+ const normalizedText = normalizeText(text);
75
+ if (!normalizedText) {
76
+ return "";
77
+ }
78
+ return [
79
+ normalizeText(sessionId),
80
+ normalizeText(threadId),
81
+ normalizeText(turnId) || "*",
82
+ "live-progress-text",
83
+ crypto.createHash("sha256").update(normalizedText).digest("hex")
84
+ ].join(":");
85
+ }
86
+
87
+ function codexAppServerTerminalItemMirrorKey(sessionId = "", threadId = "", notification = {}, role = "") {
88
+ const itemId = codexAppServerNotificationItemId(notification);
89
+ if (!itemId) {
90
+ return "";
91
+ }
92
+ return [
93
+ normalizeText(sessionId),
94
+ normalizeText(threadId),
95
+ codexAppServerNotificationTurnId(notification) || "*",
96
+ normalizeText(role),
97
+ itemId
98
+ ].join(":");
99
+ }
100
+
101
+ /**
102
+ * Native items other than the final answer that the conversation log shows as they happen: live progress
103
+ * (commentary and thinking) and the messages of a turn started from the terminal. Each item is written once.
104
+ * @param {object} dependencies
105
+ * @param {import("./codexTurn.js").CodexCreateStore} dependencies.createStore
106
+ * @param {import("./codexTurn.js").CodexPublish} dependencies.publish
107
+ * @param {import("./codexTurn.js").CodexAgentRunTurnStateReader} dependencies.turnStateFromAgentRun
108
+ * @param {import("./codexTurn.js").CodexDebugLog} dependencies.debugLog
109
+ * @param {(store: object, sessionId?: string) => Promise<object | null>} dependencies.readCodexAppServerAgentRunForSession
110
+ * @param {import("./codexTurn.js").CodexMessageMetadataHooks} dependencies.messageMetadata
111
+ * @param {boolean} dependencies.persistCommentary
112
+ * @param {number} dependencies.liveProgressMaxLength
113
+ */
114
+ export function createCodexAppServerLiveItems({
115
+ createStore,
116
+ publish,
117
+ turnStateFromAgentRun,
118
+ debugLog,
119
+ readCodexAppServerAgentRunForSession,
120
+ messageMetadata,
121
+ persistCommentary,
122
+ liveProgressMaxLength
123
+ }) {
124
+ const codexAppServerLiveProgressItems = new Set();
125
+ const codexAppServerLiveProgressFingerprints = new Set();
126
+ const codexAppServerMirroredTerminalItems = new Set();
127
+
128
+ function codexAppServerLiveProgressText(notification = {}) {
129
+ const candidate = codexAppServerLiveProgressCandidate(notification);
130
+ const text = normalizeText(candidate?.text);
131
+ if (!text) {
132
+ return "";
133
+ }
134
+ if (candidate?.phase === "commentary") {
135
+ return text;
136
+ }
137
+ if (text.length > liveProgressMaxLength) {
138
+ return "";
139
+ }
140
+ if (text.includes("\n") || text.includes("\r") || text.includes("```")) {
141
+ return "";
142
+ }
143
+ return text;
144
+ }
145
+
146
+ async function writeMirroredCodexAppServerTerminalMessage({
147
+ notification = {},
148
+ role = "",
149
+ sessionId = "",
150
+ text = "",
151
+ threadId = ""
152
+ } = {}) {
153
+ const normalizedRole = normalizeText(role);
154
+ const normalizedSessionId = normalizeText(sessionId);
155
+ const normalizedText = normalizeText(text);
156
+ if (!normalizedSessionId || !normalizedText || !["assistant", "user"].includes(normalizedRole)) {
157
+ return null;
158
+ }
159
+ const key = codexAppServerTerminalItemMirrorKey(
160
+ normalizedSessionId,
161
+ threadId,
162
+ notification,
163
+ normalizedRole
164
+ );
165
+ if (!key) {
166
+ return null;
167
+ }
168
+ if (codexAppServerMirroredTerminalItems.has(key)) {
169
+ return null;
170
+ }
171
+ codexAppServerMirroredTerminalItems.add(key);
172
+ let written = null;
173
+ try {
174
+ const store = await createStore(sessionId);
175
+ let turnMetadata = null;
176
+ if (normalizedRole === "user") {
177
+ turnMetadata = await messageMetadata.terminal(store, normalizedSessionId) ?? null;
178
+ }
179
+ const message = {
180
+ messageId: codexAppServerConversationMessageId(
181
+ threadId,
182
+ codexAppServerNotificationTurnId(notification),
183
+ normalizedRole,
184
+ normalizedText
185
+ ),
186
+ text: normalizedText,
187
+ nativeIdentity: { threadId, turnId: codexAppServerNotificationTurnId(notification) },
188
+ ...(turnMetadata ? { turnMetadata } : {})
189
+ };
190
+ written = normalizedRole === "user"
191
+ ? await store.writeConversationUserMessage(normalizedSessionId, message)
192
+ : await store.writeConversationAssistantMessage(normalizedSessionId, message);
193
+ if (!written) {
194
+ codexAppServerMirroredTerminalItems.delete(key);
195
+ return null;
196
+ }
197
+ const reason = normalizedRole === "user"
198
+ ? "codex-app-server-terminal-user-message"
199
+ : "codex-app-server-terminal-assistant-message";
200
+ await publish(normalizedSessionId, {
201
+ payload: {
202
+ conversationLogPatch: {
203
+ turn: written,
204
+ type: "upsert-turn"
205
+ }
206
+ },
207
+ reason
208
+ });
209
+ debugLog(`appServerTerminal${normalizedRole === "user" ? "User" : "Assistant"}Message.mirrored`, {
210
+ itemId: normalizeText(codexAppServerNotificationItem(notification)?.id),
211
+ sessionId: normalizedSessionId,
212
+ threadId: normalizeText(threadId),
213
+ turnId: codexAppServerNotificationTurnId(notification)
214
+ });
215
+ return written;
216
+ } catch (error) {
217
+ if (!written) {
218
+ codexAppServerMirroredTerminalItems.delete(key);
219
+ }
220
+ throw error;
221
+ }
222
+ }
223
+
224
+ async function mirrorCodexAppServerTerminalAssistantMessage(sessionId = "", threadId = "", notification = {}) {
225
+ const normalizedSessionId = normalizeText(sessionId);
226
+ const normalizedThreadId = normalizeText(threadId);
227
+ if (codexAppServerLiveProgressCandidate(notification)?.explicit === true) {
228
+ return;
229
+ }
230
+ const classification = classifyCodexAppServerEvent(notification);
231
+ // Completed finals use the same native-item publication owner as UI chat.
232
+ if (classification.kind === "final_assistant_result") {
233
+ return;
234
+ }
235
+ const text = normalizeText(
236
+ codexAppServerAssistantItemText(codexAppServerNotificationItem(notification))
237
+ );
238
+ if (!normalizedSessionId || !text) {
239
+ return;
240
+ }
241
+ const store = await createStore(normalizedSessionId);
242
+ const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
243
+ if (normalizeText(run?.inputSource) !== "terminal") {
244
+ return;
245
+ }
246
+ await writeMirroredCodexAppServerTerminalMessage({
247
+ notification,
248
+ role: "assistant",
249
+ sessionId: normalizedSessionId,
250
+ text,
251
+ threadId: normalizedThreadId
252
+ });
253
+ }
254
+
255
+ async function writeCodexAppServerLiveProgress(sessionId = "", threadId = "", notification = {}) {
256
+ // Phase-less providers also send short updates as agent messages.
257
+ // Reasoning items are recorded separately from these user-facing updates.
258
+ const normalizedSessionId = normalizeText(sessionId);
259
+ const normalizedThreadId = normalizeText(threadId);
260
+ const candidate = codexAppServerLiveProgressCandidate(notification);
261
+ const text = codexAppServerLiveProgressText(notification);
262
+ if (!normalizedSessionId || !normalizedThreadId || !text) {
263
+ return null;
264
+ }
265
+ const store = await createStore(normalizedSessionId);
266
+ const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
267
+ const turn = turnStateFromAgentRun(run || {});
268
+ const notificationTurnId = codexAppServerNotificationTurnId(notification);
269
+ const ownerTurnId = notificationTurnId || turn.turnId;
270
+ if (!codexAppServerTurnCanReceiveProviderActivity(turn, normalizedThreadId, notificationTurnId)) {
271
+ return null;
272
+ }
273
+ const key = codexAppServerLiveProgressKey(normalizedSessionId, normalizedThreadId, notification);
274
+ if (!key) {
275
+ return null;
276
+ }
277
+ const fingerprintKey = codexAppServerLiveProgressFingerprintKey(
278
+ normalizedSessionId,
279
+ normalizedThreadId,
280
+ ownerTurnId,
281
+ text
282
+ );
283
+ const streamMessageId = codexAppServerConversationMessageId(
284
+ normalizedThreadId, notificationTurnId,
285
+ "assistant-item", codexAppServerNotificationItemId(notification)
286
+ );
287
+ if (
288
+ codexAppServerLiveProgressItems.has(key) ||
289
+ codexAppServerLiveProgressFingerprints.has(fingerprintKey)
290
+ ) {
291
+ await publish(normalizedSessionId, {
292
+ payload: { conversationStream: store.completeConversationStreamMessage(normalizedSessionId, streamMessageId) },
293
+ reason: "assistant-stream"
294
+ });
295
+ return null;
296
+ }
297
+ codexAppServerLiveProgressItems.add(key);
298
+ codexAppServerLiveProgressFingerprints.add(fingerprintKey);
299
+ let written = null;
300
+ try {
301
+ const role = !candidate.phase || candidate.phase === "commentary" ? "commentary" : "thinking";
302
+ const writer = role === "commentary"
303
+ ? store.writeConversationCommentaryMessage
304
+ : store.writeConversationThinkingMessage;
305
+ if (role !== "commentary" || persistCommentary) {
306
+ written = await writer.call(store, normalizedSessionId, {
307
+ ...(streamMessageId ? { outputId: streamMessageId } : {}),
308
+ messageId: codexAppServerConversationMessageId(
309
+ normalizedThreadId,
310
+ ownerTurnId,
311
+ role,
312
+ text
313
+ ),
314
+ requireOpenTurn: false,
315
+ nativeIdentity: { threadId: normalizedThreadId, turnId: ownerTurnId },
316
+ text
317
+ });
318
+ if (!written) {
319
+ codexAppServerLiveProgressItems.delete(key);
320
+ codexAppServerLiveProgressFingerprints.delete(fingerprintKey);
321
+ }
322
+ }
323
+ const payload = {
324
+ conversationStream: await store.completeConversationStreamMessage(normalizedSessionId, streamMessageId, {
325
+ text, role, nativeIdentity: { threadId: normalizedThreadId, turnId: ownerTurnId }
326
+ })
327
+ };
328
+ if (written) {
329
+ payload.conversationLogPatch = { turn: written, type: "upsert-turn" };
330
+ }
331
+ await publish(normalizedSessionId, {
332
+ payload,
333
+ reason: role === "commentary"
334
+ ? "codex-app-server-commentary"
335
+ : "codex-app-server-live-progress"
336
+ });
337
+ return written;
338
+ } catch (error) {
339
+ if (!written) {
340
+ codexAppServerLiveProgressItems.delete(key);
341
+ codexAppServerLiveProgressFingerprints.delete(fingerprintKey);
342
+ }
343
+ throw error;
344
+ }
345
+ }
346
+
347
+ function forgetCodexAppServerLiveTurnItems(threadId = "", turnId = "") {
348
+ const normalizedThreadId = normalizeText(threadId);
349
+ const normalizedTurnId = normalizeText(turnId);
350
+ if (!normalizedThreadId) {
351
+ return;
352
+ }
353
+ const markers = [
354
+ `:${normalizedThreadId}:*:`,
355
+ ...(normalizedTurnId ? [`:${normalizedThreadId}:${normalizedTurnId}:`] : [])
356
+ ];
357
+ for (const set of [
358
+ codexAppServerLiveProgressItems,
359
+ codexAppServerLiveProgressFingerprints,
360
+ codexAppServerMirroredTerminalItems
361
+ ]) {
362
+ for (const key of set) {
363
+ if (markers.some((marker) => key.includes(marker))) {
364
+ set.delete(key);
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ return Object.freeze({
371
+ liveProgressItems: codexAppServerLiveProgressItems,
372
+ liveProgressFingerprints: codexAppServerLiveProgressFingerprints,
373
+ mirroredTerminalItems: codexAppServerMirroredTerminalItems,
374
+ writeLiveProgress: writeCodexAppServerLiveProgress,
375
+ writeMirroredTerminalMessage: writeMirroredCodexAppServerTerminalMessage,
376
+ mirrorTerminalAssistantMessage: mirrorCodexAppServerTerminalAssistantMessage,
377
+ forgetTurnItems: forgetCodexAppServerLiveTurnItems
378
+ });
379
+ }