@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
@@ -1,12 +1,4 @@
1
- import {
2
- codexAppServerTurnStatusIsActive,
3
- codexAppServerTurnStatusIsSuccessfulComplete,
4
- codexAppServerTurnStatusIsProviderFailure,
5
- normalizeCodexRunText,
6
- CODEX_APP_SERVER_AGENT_RUN_ID,
7
- CODEX_AGENT_PROVIDER,
8
- CODEX_APP_SERVER_RUN_STATE
9
- } from "./codexTurnState.js";
1
+ import { codexAppServerTurnStatusIsActive, codexAppServerTurnStatusIsSuccessfulComplete, codexAppServerTurnStatusIsProviderFailure, CODEX_APP_SERVER_AGENT_RUN_ID, CODEX_AGENT_PROVIDER, CODEX_APP_SERVER_RUN_STATE } from "./codexTurnState.js";
10
2
  import { codexAppServerProviderConnectionGeneration } from "./codexModelCatalog.js";
11
3
  import {
12
4
  codexAppServerAssistantItemText,
@@ -23,9 +15,27 @@ import {
23
15
  codexAppServerContextRefreshReason,
24
16
  codexAppServerNotificationUsageLimitExceeded
25
17
  } from "./codexEvents.js";
18
+ import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
26
19
 
27
- // Native notification routing, goal updates and provider-generation subscriptions.
28
- // Ordering belongs to the notification queue; state belongs to the operation owners.
20
+ /**
21
+ * Native notification routing, goal updates and provider-generation subscriptions. Ordering belongs to the
22
+ * notification queue; state belongs to the operation owners.
23
+ * @param {object} dependencies
24
+ * @param {import("./codexRunJournal.js").CodexRunJournal} dependencies.journal
25
+ * @param {import("./codexTurnRecovery.js").CodexTurnRecovery} dependencies.recovery
26
+ * @param {import("./codexThreadStatus.js").CodexThreadStatus} dependencies.threadStatus
27
+ * @param {import("./codexOutput.js").CodexAppServerOutput} dependencies.output
28
+ * @param {import("./codexMessageDelivery.js").CodexAppServerMessageDelivery} dependencies.delivery
29
+ * @param {import("./codexNotificationQueue.js").CodexAppServerNotificationQueue} dependencies.notificationQueue
30
+ * @param {import("./codexTurn.js").CodexSessionNamespace} dependencies.namespace
31
+ * @param {import("./codexTurn.js").CodexCreateRuntime} dependencies.createRuntime
32
+ * @param {import("./codexTurn.js").CodexCreateStore} dependencies.createStore
33
+ * @param {import("./codexTurn.js").CodexPublish} dependencies.publish
34
+ * @param {import("./codexTurn.js").CodexCaptureContext} dependencies.captureContext
35
+ * @param {import("./codexTurn.js").CodexDebugLog} dependencies.debugLog
36
+ * @param {(kind: string, details: object) => Promise<unknown>} dependencies.onNotificationSignal
37
+ * @param {import("./codexTurn.js").CodexAgentRunTurnStateReader} dependencies.turnStateFromAgentRun
38
+ */
29
39
  export function createCodexNotificationObserver({
30
40
  journal,
31
41
  recovery,
@@ -42,25 +52,6 @@ export function createCodexNotificationObserver({
42
52
  onNotificationSignal,
43
53
  turnStateFromAgentRun
44
54
  }) {
45
- const { resolveTurnId: resolveCodexAppServerTurnId } = journal;
46
- const {
47
- recoverFinalizingTurn: recoverCodexAppServerFinalizingTurn,
48
- markTurnActive: markCodexAppServerTurnActive,
49
- markProviderTurnActive: markCodexAppServerProviderTurnActive,
50
- stopTurnWithProviderFailure: stopCodexAppServerTurnWithProviderFailure,
51
- completeTurn: completeCodexAppServerTurn
52
- } = recovery;
53
- const { reconcileThreadStatus: reconcileCodexAppServerThreadStatus } = threadStatus;
54
- const {
55
- readAgentRunForSession: readCodexAppServerAgentRunForSession,
56
- recordReasoningForSession: recordCodexAppServerReasoningForSession,
57
- writeStream: writeCodexAppServerStream,
58
- recordFinalAssistantResult: recordCodexAppServerFinalAssistantResult,
59
- writeLiveProgress: writeCodexAppServerLiveProgress,
60
- mirrorTerminalAssistantMessage: mirrorCodexAppServerTerminalAssistantMessage
61
- } = output;
62
- const { mirrorTerminalUserMessage: mirrorCodexAppServerTerminalUserMessage } = delivery;
63
- const { run: runCodexAppServerNotificationTask } = notificationQueue;
64
55
  const CODEX_APP_SERVER_GOAL_STATUSES = new Set([
65
56
  "active", "blocked", "budgetLimited", "complete", "paused", "usageLimited"
66
57
  ]);
@@ -68,7 +59,7 @@ export function createCodexNotificationObserver({
68
59
  const codexAppServerAutomaticHookThreads = new Set();
69
60
 
70
61
  function codexAppServerEventSubscriptionKey(providerKey = "", threadId = "") {
71
- return `${normalizeCodexRunText(providerKey)}:${normalizeCodexRunText(threadId)}`;
62
+ return `${normalizeText(providerKey)}:${normalizeText(threadId)}`;
72
63
  }
73
64
 
74
65
  function codexAppServerEventSubscriptionIsCurrent(key = "", provider = null) {
@@ -82,16 +73,16 @@ export function createCodexNotificationObserver({
82
73
 
83
74
  function unsubscribeCodexAppServerEventSubscription(key = "") {
84
75
  const record = codexAppServerEventSubscriptions.get(key);
85
- record?.unsubscribe?.();
76
+ record?.unsubscribe();
86
77
  codexAppServerEventSubscriptions.delete(key);
87
78
  }
88
79
 
89
80
  function queueCodexAppServerReasoning(context, notification) {
90
- return notificationQueue.reasoning(context, notification, recordCodexAppServerReasoningForSession);
81
+ return notificationQueue.reasoning(context, notification, output.recordReasoningForSession);
91
82
  }
92
83
 
93
84
  function queueCodexAppServerStream(context, classification) {
94
- return notificationQueue.stream(context, classification, writeCodexAppServerStream);
85
+ return notificationQueue.stream(context, classification, output.writeStream);
95
86
  }
96
87
 
97
88
  async function reconcileCodexAppServerGoalUpdated(
@@ -100,26 +91,26 @@ export function createCodexNotificationObserver({
100
91
  threadId = "",
101
92
  notification = {}
102
93
  ) {
103
- const normalizedSessionId = normalizeCodexRunText(sessionId);
104
- const normalizedThreadId = normalizeCodexRunText(threadId);
94
+ const normalizedSessionId = normalizeText(sessionId);
95
+ const normalizedThreadId = normalizeText(threadId);
105
96
  const notificationTurnId = codexAppServerNotificationTurnId(notification);
106
- const goalStatus = normalizeCodexRunText(notification.params?.goal?.status);
97
+ const goalStatus = normalizeText(notification.params?.goal?.status);
107
98
  const goalCleared = notification.method === "thread/goal/cleared";
108
99
  const runtime = await createRuntime();
109
- let run = await readCodexAppServerAgentRunForSession(
100
+ let run = await output.readAgentRunForSession(
110
101
  runtime.store,
111
102
  normalizedSessionId
112
103
  );
113
104
  if (run?.providerStatus === "observation_lost") return { ok: true, processed: false, reason: "observation_lost" };
114
- const currentThreadId = normalizeCodexRunText(run?.providerThreadId);
105
+ const currentThreadId = normalizeText(run?.providerThreadId);
115
106
  if (currentThreadId && currentThreadId !== normalizedThreadId) {
116
107
  return { ok: true, processed: false, reason: "goal_thread_changed" };
117
108
  }
118
109
  if (
119
110
  (goalCleared || CODEX_APP_SERVER_GOAL_STATUSES.has(goalStatus)) &&
120
111
  (
121
- normalizeCodexRunText(run?.providerGoalStatus) !== goalStatus ||
122
- normalizeCodexRunText(run?.providerGoalThreadId) !== normalizedThreadId
112
+ normalizeText(run?.providerGoalStatus) !== goalStatus ||
113
+ normalizeText(run?.providerGoalThreadId) !== normalizedThreadId
123
114
  )
124
115
  ) {
125
116
  const updatedAt = new Date().toISOString();
@@ -142,7 +133,7 @@ export function createCodexNotificationObserver({
142
133
  providerGoalUpdatedAt: updatedAt
143
134
  }
144
135
  });
145
- run = await readCodexAppServerAgentRunForSession(
136
+ run = await output.readAgentRunForSession(
146
137
  runtime.store,
147
138
  normalizedSessionId
148
139
  );
@@ -169,7 +160,7 @@ export function createCodexNotificationObserver({
169
160
  turn.threadId === normalizedThreadId &&
170
161
  (!notificationTurnId || turn.turnId === notificationTurnId)
171
162
  ) {
172
- return recoverCodexAppServerFinalizingTurn(
163
+ return recovery.recoverFinalizingTurn(
173
164
  normalizedSessionId,
174
165
  normalizedThreadId,
175
166
  turn.turnId,
@@ -178,7 +169,7 @@ export function createCodexNotificationObserver({
178
169
  }
179
170
  );
180
171
  }
181
- return reconcileCodexAppServerThreadStatus(
172
+ return threadStatus.reconcileThreadStatus(
182
173
  normalizedSessionId,
183
174
  provider,
184
175
  normalizedThreadId,
@@ -190,14 +181,14 @@ export function createCodexNotificationObserver({
190
181
  }
191
182
 
192
183
  function subscribeCodexAppServerEvents(sessionId = "", provider = null, threadId = "", options = {}) {
193
- const normalizedSessionId = normalizeCodexRunText(sessionId);
194
- const normalizedThreadId = normalizeCodexRunText(threadId);
195
- if (!normalizedSessionId || !normalizedThreadId || typeof provider?.subscribe !== "function") {
184
+ const normalizedSessionId = normalizeText(sessionId);
185
+ const normalizedThreadId = normalizeText(threadId);
186
+ if (!normalizedSessionId || !normalizedThreadId || !provider) {
196
187
  throw new Error("Codex thread observation requires a session, thread and provider subscription.");
197
188
  }
198
189
  const projectContext = captureContext();
199
190
  const sessionKey = namespace(normalizedSessionId);
200
- const providerKey = normalizeCodexRunText(options.providerKey);
191
+ const providerKey = normalizeText(options.providerKey);
201
192
  const key = codexAppServerEventSubscriptionKey(providerKey, normalizedThreadId);
202
193
  const existing = codexAppServerEventSubscriptions.get(key);
203
194
  if (existing && codexAppServerEventSubscriptionIsCurrent(key, provider)) {
@@ -211,27 +202,27 @@ export function createCodexNotificationObserver({
211
202
  }
212
203
  // A new observer cannot confirm a previously active phase. Fresh native
213
204
  // events restore it; a stored phase alone must not animate activity.
214
- runCodexAppServerNotificationTask({ projectContext, provider, sessionId: normalizedSessionId, sessionKey }, async () => {
205
+ notificationQueue.run({ projectContext, provider, sessionId: normalizedSessionId, sessionKey }, async () => {
215
206
  const store = await createStore(normalizedSessionId);
216
- const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
207
+ const run = await output.readAgentRunForSession(store, normalizedSessionId);
217
208
  if (!run?.providerPhase || run.providerThreadId !== normalizedThreadId) return;
218
- await markCodexAppServerTurnActive(normalizedSessionId, {
209
+ await recovery.markTurnActive(normalizedSessionId, {
219
210
  phase: "", requireTrackedTurn: true,
220
211
  threadId: normalizedThreadId, turnId: run.providerTurnId
221
212
  });
222
213
  });
223
214
  const onNotification = (notification = {}) => {
224
- const runProviderFailure = (context, turnId, status) => runCodexAppServerNotificationTask(context, () =>
225
- stopCodexAppServerTurnWithProviderFailure(normalizedSessionId, normalizedThreadId, turnId, {
215
+ const runProviderFailure = (context, turnId, status) => notificationQueue.run(context, () =>
216
+ recovery.stopTurnWithProviderFailure(normalizedSessionId, normalizedThreadId, turnId, {
226
217
  error: codexAppServerNotificationError(notification),
227
218
  provider,
228
219
  status,
229
220
  usageLimitExceeded: codexAppServerNotificationUsageLimitExceeded(notification),
230
221
  deferFailureDetails: true
231
222
  }));
232
- const method = normalizeCodexRunText(notification.method);
223
+ const method = normalizeText(notification.method);
233
224
  if (method === "account/rateLimits/updated" || method === "account/updated") {
234
- runCodexAppServerNotificationTask({ method, projectContext, provider, sessionId: normalizedSessionId, sessionKey }, () =>
225
+ notificationQueue.run({ method, projectContext, provider, sessionId: normalizedSessionId, sessionKey }, () =>
235
226
  publish(normalizedSessionId, { reason: "codex-plan-usage" }));
236
227
  return;
237
228
  }
@@ -249,7 +240,7 @@ export function createCodexNotificationObserver({
249
240
  turnId: codexAppServerNotificationTurnId(notification)
250
241
  };
251
242
  if (method === "thread/tokenUsage/updated") {
252
- runCodexAppServerNotificationTask(notificationContext, async () => {
243
+ notificationQueue.run(notificationContext, async () => {
253
244
  const store = await createStore(normalizedSessionId);
254
245
  return onNotificationSignal("usage", {
255
246
  store, sessionId: normalizedSessionId, threadId: normalizedThreadId, notification
@@ -259,10 +250,10 @@ export function createCodexNotificationObserver({
259
250
  const classification = classifyCodexAppServerEvent(notification);
260
251
  if (classification.kind === "provider_error" && classification.text &&
261
252
  codexAppServerNotificationParams(notification).willRetry !== true) {
262
- runCodexAppServerNotificationTask(notificationContext, async () => {
253
+ notificationQueue.run(notificationContext, async () => {
263
254
  return onNotificationSignal("provider_error", {
264
255
  sessionId: normalizedSessionId, threadId: normalizedThreadId, provider,
265
- turnId: await resolveCodexAppServerTurnId(normalizedSessionId, normalizedThreadId,
256
+ turnId: await journal.resolveTurnId(normalizedSessionId, normalizedThreadId,
266
257
  codexAppServerNotificationTurnId(notification)),
267
258
  error: classification.text
268
259
  });
@@ -279,14 +270,14 @@ export function createCodexNotificationObserver({
279
270
  const retrying = classification.phase === "retrying";
280
271
  if (!hookOwned &&
281
272
  (retrying || method === "item/started" || method === "item/completed" && ["contextCompaction", "reasoning"].includes(itemType))) {
282
- runCodexAppServerNotificationTask(notificationContext, async () => {
273
+ notificationQueue.run(notificationContext, async () => {
283
274
  const phase = retrying ? "retrying" : method === "item/started"
284
275
  ? ({ contextCompaction: "compacting", reasoning: "reasoning" })[itemType] || ""
285
276
  : "";
286
277
  const store = await createStore(normalizedSessionId);
287
- const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
288
- if (normalizeCodexRunText(run?.providerPhase) === phase) return;
289
- return markCodexAppServerTurnActive(normalizedSessionId, {
278
+ const run = await output.readAgentRunForSession(store, normalizedSessionId);
279
+ if (normalizeText(run?.providerPhase) === phase) return;
280
+ return recovery.markTurnActive(normalizedSessionId, {
290
281
  phase, requireTrackedTurn: true,
291
282
  threadId: normalizedThreadId,
292
283
  turnId: codexAppServerNotificationTurnId(notification)
@@ -294,7 +285,7 @@ export function createCodexNotificationObserver({
294
285
  });
295
286
  }
296
287
  if (contextRefreshReason) {
297
- runCodexAppServerNotificationTask(notificationContext, () => {
288
+ notificationQueue.run(notificationContext, () => {
298
289
  return onNotificationSignal("context_refresh", {
299
290
  sessionId: normalizedSessionId, threadId: normalizedThreadId,
300
291
  notification, reason: contextRefreshReason
@@ -302,14 +293,14 @@ export function createCodexNotificationObserver({
302
293
  });
303
294
  }
304
295
  if (method === "thread/goal/cleared") {
305
- runCodexAppServerNotificationTask(notificationContext, async () => {
296
+ notificationQueue.run(notificationContext, async () => {
306
297
  await reconcileCodexAppServerGoalUpdated(normalizedSessionId, provider, normalizedThreadId, notification);
307
298
  await publish(normalizedSessionId, { reason: "codex-goal", nativeGoal: { threadId: normalizedThreadId, goal: null } });
308
299
  });
309
300
  return;
310
301
  }
311
302
  if (method === "thread/goal/updated") {
312
- runCodexAppServerNotificationTask(notificationContext, async () => {
303
+ notificationQueue.run(notificationContext, async () => {
313
304
  await publish(normalizedSessionId, { reason: "codex-goal", nativeGoal: { threadId: normalizedThreadId, goal: notification.params.goal } });
314
305
  return reconcileCodexAppServerGoalUpdated(
315
306
  normalizedSessionId,
@@ -333,8 +324,8 @@ export function createCodexNotificationObserver({
333
324
  if (classification.kind === "final_assistant_result") {
334
325
  const event = codexAppServerNotificationEvent(notification);
335
326
  const payload = codexAppServerNotificationEventPayload(notification, event);
336
- runCodexAppServerNotificationTask(notificationContext, () => {
337
- return recordCodexAppServerFinalAssistantResult({
327
+ notificationQueue.run(notificationContext, () => {
328
+ return output.recordFinalAssistantResult({
338
329
  itemId: classification.itemId,
339
330
  notification,
340
331
  sessionId: normalizedSessionId,
@@ -345,11 +336,11 @@ export function createCodexNotificationObserver({
345
336
  });
346
337
  });
347
338
  debugLog("appServerFinalAssistantResult.received", {
348
- eventId: normalizeCodexRunText(event?.id),
339
+ eventId: normalizeText(event?.id),
349
340
  eventType: codexAppServerNotificationEventType(notification, event),
350
- itemId: normalizeCodexRunText(codexAppServerNotificationItem(notification)?.id),
341
+ itemId: normalizeText(codexAppServerNotificationItem(notification)?.id),
351
342
  method,
352
- payloadId: normalizeCodexRunText(payload?.id),
343
+ payloadId: normalizeText(payload?.id),
353
344
  stableItemId: classification.itemId,
354
345
  source: classification.source,
355
346
  sessionId: normalizedSessionId,
@@ -361,28 +352,28 @@ export function createCodexNotificationObserver({
361
352
  if (hookOwned) {
362
353
  return;
363
354
  }
364
- runCodexAppServerNotificationTask(notificationContext, () => {
365
- return writeCodexAppServerLiveProgress(normalizedSessionId, normalizedThreadId, notification);
355
+ notificationQueue.run(notificationContext, () => {
356
+ return output.writeLiveProgress(normalizedSessionId, normalizedThreadId, notification);
366
357
  });
367
358
  }
368
359
  if (method === "item/completed") {
369
360
  const item = codexAppServerNotificationItem(notification);
370
- if (normalizeCodexRunText(item?.type) === "userMessage") {
371
- runCodexAppServerNotificationTask(notificationContext, () => {
372
- return mirrorCodexAppServerTerminalUserMessage(normalizedSessionId, normalizedThreadId, notification);
361
+ if (normalizeText(item?.type) === "userMessage") {
362
+ notificationQueue.run(notificationContext, () => {
363
+ return delivery.mirrorTerminalUserMessage(normalizedSessionId, normalizedThreadId, notification);
373
364
  });
374
365
  return;
375
366
  }
376
367
  if (codexAppServerAssistantItemText(item)) {
377
- runCodexAppServerNotificationTask(notificationContext, () => {
378
- return mirrorCodexAppServerTerminalAssistantMessage(normalizedSessionId, normalizedThreadId, notification);
368
+ notificationQueue.run(notificationContext, () => {
369
+ return output.mirrorTerminalAssistantMessage(normalizedSessionId, normalizedThreadId, notification);
379
370
  });
380
371
  return;
381
372
  }
382
373
  }
383
374
  if (method === "turn/started") {
384
375
  codexAppServerAutomaticHookThreads.delete(normalizedThreadId);
385
- runCodexAppServerNotificationTask(notificationContext, () => markCodexAppServerProviderTurnActive(normalizedSessionId, {
376
+ notificationQueue.run(notificationContext, () => recovery.markProviderTurnActive(normalizedSessionId, {
386
377
  source: "turn_started",
387
378
  status: codexAppServerNotificationTurnStatus(notification) || "inProgress",
388
379
  threadId: normalizedThreadId,
@@ -399,8 +390,8 @@ export function createCodexNotificationObserver({
399
390
  return;
400
391
  }
401
392
  if (codexAppServerTurnStatusIsSuccessfulComplete(status)) {
402
- runCodexAppServerNotificationTask(notificationContext, () => {
403
- return completeCodexAppServerTurn(normalizedSessionId, normalizedThreadId, turnId, {
393
+ notificationQueue.run(notificationContext, () => {
394
+ return recovery.completeTurn(normalizedSessionId, normalizedThreadId, turnId, {
404
395
  provider,
405
396
  status
406
397
  });
@@ -411,13 +402,13 @@ export function createCodexNotificationObserver({
411
402
  if (method === "thread/status/changed") {
412
403
  const status = codexAppServerNotificationTurnStatus(notification);
413
404
  if (codexAppServerTurnStatusIsActive(status)) {
414
- runCodexAppServerNotificationTask(notificationContext, async () => {
415
- const turnId = await resolveCodexAppServerTurnId(
405
+ notificationQueue.run(notificationContext, async () => {
406
+ const turnId = await journal.resolveTurnId(
416
407
  normalizedSessionId,
417
408
  normalizedThreadId,
418
409
  codexAppServerNotificationTurnId(notification)
419
410
  );
420
- await markCodexAppServerProviderTurnActive(normalizedSessionId, {
411
+ await recovery.markProviderTurnActive(normalizedSessionId, {
421
412
  source: "thread_status_changed",
422
413
  status,
423
414
  threadId: normalizedThreadId,
@@ -433,13 +424,13 @@ export function createCodexNotificationObserver({
433
424
  return;
434
425
  }
435
426
  if (codexAppServerTurnStatusIsSuccessfulComplete(status)) {
436
- runCodexAppServerNotificationTask(notificationContext, async () => {
437
- const resolvedTurnId = await resolveCodexAppServerTurnId(
427
+ notificationQueue.run(notificationContext, async () => {
428
+ const resolvedTurnId = await journal.resolveTurnId(
438
429
  normalizedSessionId,
439
430
  normalizedThreadId,
440
431
  turnId
441
432
  );
442
- return completeCodexAppServerTurn(normalizedSessionId, normalizedThreadId, resolvedTurnId, {
433
+ return recovery.completeTurn(normalizedSessionId, normalizedThreadId, resolvedTurnId, {
443
434
  provider,
444
435
  status
445
436
  });
@@ -467,7 +458,7 @@ export function createCodexNotificationObserver({
467
458
  };
468
459
  }
469
460
 
470
- return {
461
+ return Object.freeze({
471
462
  eventSubscriptions: codexAppServerEventSubscriptions,
472
463
  automaticHookThreads: codexAppServerAutomaticHookThreads,
473
464
  eventSubscriptionKey: codexAppServerEventSubscriptionKey,
@@ -475,5 +466,7 @@ export function createCodexNotificationObserver({
475
466
  unsubscribeEventSubscription: unsubscribeCodexAppServerEventSubscription,
476
467
  reconcileGoalUpdated: reconcileCodexAppServerGoalUpdated,
477
468
  subscribeEvents: subscribeCodexAppServerEvents
478
- };
469
+ });
479
470
  }
471
+
472
+ /** @typedef {ReturnType<typeof createCodexNotificationObserver>} CodexNotificationObserver */
@@ -1,12 +1,21 @@
1
- import { normalizeCodexRunText } from "./codexTurnState.js";
1
+ import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
2
 
3
- // Per-session ordered notification tasks. Coalesces only fragments still
4
- // waiting behind a running write; running batches are never merged or delayed.
3
+ function ignoreNotificationTaskError() {}
4
+
5
+ /**
6
+ * Per-session ordered notification tasks. Coalesces only fragments still
7
+ * waiting behind a running write; running batches are never merged or delayed.
8
+ * @param {object} [options]
9
+ * @param {import("./codexTurn.js").CodexSessionNamespace} [options.namespace]
10
+ * @param {() => boolean} [options.isClosing]
11
+ * @param {import("./codexTurn.js").CodexRunInContext} [options.runInContext]
12
+ * @param {(error: unknown, context: object) => void} [options.reportError]
13
+ */
5
14
  export function createCodexAppServerNotificationQueue({
6
- namespace = normalizeCodexRunText,
15
+ namespace = normalizeText,
7
16
  isClosing = () => false,
8
17
  runInContext = (_context, operation) => operation(),
9
- reportError = () => {}
18
+ reportError = ignoreNotificationTaskError
10
19
  } = {}) {
11
20
  const codexAppServerNotificationTasks = new Map();
12
21
  const codexAppServerPendingStreams = new Map();
@@ -16,8 +25,8 @@ export function createCodexAppServerNotificationQueue({
16
25
  if (isClosing()) {
17
26
  return;
18
27
  }
19
- const taskSessionId = normalizeCodexRunText(context.sessionId);
20
- const taskSessionKey = normalizeCodexRunText(context.sessionKey) ||
28
+ const taskSessionId = normalizeText(context.sessionId);
29
+ const taskSessionKey = normalizeText(context.sessionKey) ||
21
30
  namespace(taskSessionId);
22
31
  // Other notifications end the pending stream batch so completion, reasoning
23
32
  // and turn changes retain their position in the provider's event order.
@@ -28,7 +37,7 @@ export function createCodexAppServerNotificationQueue({
28
37
  .catch(() => null)
29
38
  .then(() => runInContext(context.projectContext, operation))
30
39
  .catch((error) => {
31
- context.provider?.failObservation?.(error);
40
+ context.provider?.failObservation(error);
32
41
  reportError(error, context);
33
42
  });
34
43
  codexAppServerNotificationTasks.set(taskSessionKey, task);
@@ -98,7 +107,7 @@ export function createCodexAppServerNotificationQueue({
98
107
  codexAppServerPendingReasoning.set(context.sessionKey, batch);
99
108
  }
100
109
 
101
- return {
110
+ return Object.freeze({
102
111
  tasks: codexAppServerNotificationTasks,
103
112
  pendingStreams: codexAppServerPendingStreams,
104
113
  pendingReasoning: codexAppServerPendingReasoning,
@@ -107,5 +116,7 @@ export function createCodexAppServerNotificationQueue({
107
116
  captureDrain: captureCodexAppServerNotificationDrain,
108
117
  stream: queueCodexAppServerStream,
109
118
  reasoning: queueCodexAppServerReasoning
110
- };
119
+ });
111
120
  }
121
+
122
+ /** @typedef {ReturnType<typeof createCodexAppServerNotificationQueue>} CodexAppServerNotificationQueue */