@jskit-ai/assistant-core 0.2.7 → 0.2.9

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,14 +1,21 @@
1
- import {
2
- codexAppServerTurnStatusIsActive,
3
- codexAppServerThreadStatus,
4
- codexAppServerReadThreadStatus,
5
- normalizeCodexRunText
6
- } from "./codexTurnState.js";
7
- import { codexAppServerThreadIsMissing, ensureCodexAppServerThread } from "./codexProvider.js";
8
- import { codexAppServerThreadRawValue, codexAppServerThreadTurnId } from "./codexEvents.js";
1
+ import { codexAppServerTurnStatusIsActive, codexAppServerThreadStatus, codexAppServerReadThreadStatus } from "./codexTurnState.js";
2
+ import { codexAppServerThreadIsMissing, ensureCodexAppServerThread } from "./codexProviderThreads.js";
3
+ import { codexAppServerThreadRawValue, codexAppServerThreadTurnId } from "./codexThreadSnapshot.js";
4
+ import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
9
5
 
10
- // Native thread readiness, loaded-thread restoration and reconciliation drain.
11
- // Its one Map lives as long as the run owner and is shared with shutdown.
6
+ /**
7
+ * Native thread readiness, loaded-thread restoration and reconciliation drain. Its one Map lives as long as the run
8
+ * owner and is shared with shutdown.
9
+ * @param {object} dependencies
10
+ * @param {import("./codexTurn.js").CodexProviderSessions} [dependencies.providerSessions]
11
+ * @param {import("./codexProviderSelection.js").CodexProviderSelection} dependencies.selection
12
+ * @param {import("./codexNotificationObserver.js").CodexNotificationObserver} dependencies.observer
13
+ * @param {import("./codexThreadStatus.js").CodexThreadStatus} dependencies.threadStatus
14
+ * @param {import("./codexTurnRecovery.js").CodexTurnRecovery} dependencies.recovery
15
+ * @param {import("./codexTurn.js").CodexTurnStateReader} dependencies.turnState
16
+ * @param {import("./codexTurn.js").CodexDebugLog} dependencies.debugLog
17
+ * @param {import("./codexTurn.js").CodexDebugError} dependencies.debugError
18
+ */
12
19
  export function createCodexThreadReadiness({
13
20
  providerSessions,
14
21
  selection,
@@ -27,23 +34,11 @@ export function createCodexThreadReadiness({
27
34
  ensureSessionProvider,
28
35
  rememberManagedSession
29
36
  } = selection;
30
- const {
31
- eventSubscriptionKey: codexAppServerEventSubscriptionKey,
32
- eventSubscriptionIsCurrent: codexAppServerEventSubscriptionIsCurrent,
33
- subscribeEvents: subscribeCodexAppServerEvents
34
- } = observer;
35
- const {
36
- reconcileThreadStatus: reconcileCodexAppServerThreadStatus,
37
- reconcileLoadedThreadStatus: reconcileCodexAppServerLoadedThreadStatus
38
- } = threadStatus;
39
- const {
40
- recoverActiveTurn: recoverCodexAppServerActiveTurn
41
- } = recovery;
42
37
 
43
38
  async function ensureCodexAppServerThreadReady(sessionId, context, preparation) {
44
39
  const { runtime, session, workdir } = context;
45
40
  // Runtime recovery may await a disconnect callback that writes session
46
- // state. This acquisition stays outside the original startup gate.
41
+ // state. This acquisition stays outside the startup gate.
47
42
  const activeProvider = await ensureCodexAppServerProviderForActiveTurn(session, preparation.managedIdentity);
48
43
  let providerOptions = activeProvider?.providerOptions;
49
44
  if (!providerOptions) {
@@ -54,19 +49,19 @@ export function createCodexThreadReadiness({
54
49
  const { currentSession: preparedSession, thread } = await preparation.gate(async currentSession => {
55
50
  const thread = await ensureCodexAppServerThread({
56
51
  ...preparation.preparation(currentSession),
57
- observeThread: threadId => subscribeCodexAppServerEvents(sessionId, provider, threadId,
52
+ observeThread: threadId => observer.subscribeEvents(sessionId, provider, threadId,
58
53
  codexAppServerSessionObserverOptions(sessionId, providerOptions)),
59
54
  provider,
60
55
  workdir
61
56
  });
62
57
  return { currentSession, thread };
63
58
  });
64
- subscribeCodexAppServerEvents(sessionId, provider, thread.threadId,
59
+ observer.subscribeEvents(sessionId, provider, thread.threadId,
65
60
  codexAppServerSessionObserverOptions(sessionId, providerOptions));
66
61
  rememberManagedSession(sessionId, providerOptions, {
67
62
  executionRoot: context.executionRoot, workdir: context.workdir, threadId: thread.threadId
68
63
  });
69
- await reconcileCodexAppServerThreadStatus(sessionId, provider, thread.threadId, {
64
+ await threadStatus.reconcileThreadStatus(sessionId, provider, thread.threadId, {
70
65
  failUnconfirmedTrackedTurn: true,
71
66
  observeLatestTurn: true,
72
67
  requireTrackedTurn: true,
@@ -75,7 +70,7 @@ export function createCodexThreadReadiness({
75
70
  const ready = await preparation.ready(preparedSession);
76
71
  let currentSession = await runtime.getSession(sessionId);
77
72
  if (turnState(currentSession).state === "starting") {
78
- currentSession = await recoverCodexAppServerActiveTurn(sessionId, {
73
+ currentSession = await recovery.recoverActiveTurn(sessionId, {
79
74
  provider,
80
75
  retryOnError: false,
81
76
  runtime
@@ -153,12 +148,12 @@ export function createCodexThreadReadiness({
153
148
  }
154
149
  debugLog("appServerThread.reconcile.pruneWait.start", {
155
150
  pendingCount: pending.length,
156
- projectContextRoot: normalizeCodexRunText(projectContextRoot)
151
+ projectContextRoot: normalizeText(projectContextRoot)
157
152
  });
158
153
  await Promise.allSettled(pending.map(([, reconciliation]) => reconciliation));
159
154
  debugLog("appServerThread.reconcile.pruneWait.done", {
160
155
  pendingCount: pending.length,
161
- projectContextRoot: normalizeCodexRunText(projectContextRoot)
156
+ projectContextRoot: normalizeText(projectContextRoot)
162
157
  });
163
158
  }
164
159
 
@@ -166,11 +161,11 @@ export function createCodexThreadReadiness({
166
161
  reconnect = true
167
162
  } = {}) {
168
163
  providerSessions.assertOpen();
169
- const normalizedProviderKey = normalizeCodexRunText(providerKey);
164
+ const normalizedProviderKey = normalizeText(providerKey);
170
165
  const managed = providerSessions.managed.get(normalizedProviderKey);
171
166
  const provider = providerSessions.owner.providers.get(normalizedProviderKey);
172
- const sessionId = normalizeCodexRunText(managed?.sessionId);
173
- const threadId = normalizeCodexRunText(managed?.threadId);
167
+ const sessionId = normalizeText(managed?.sessionId);
168
+ const threadId = normalizeText(managed?.threadId);
174
169
  if (!managed || !provider || !sessionId) {
175
170
  throw new Error("Codex app-server managed connection is incomplete.");
176
171
  }
@@ -186,7 +181,7 @@ export function createCodexThreadReadiness({
186
181
 
187
182
  if (reconnect) {
188
183
  await providerSessions.owner.acquireRuntime({
189
- operation: () => provider.ensureAvailable?.(),
184
+ operation: () => provider.ensureAvailable(),
190
185
  provider,
191
186
  providerKey: normalizedProviderKey,
192
187
  providerOptions: managed.providerOptions || {}
@@ -220,38 +215,38 @@ export function createCodexThreadReadiness({
220
215
  const session = await current.readSession();
221
216
  current.assertCurrent(session);
222
217
  try {
223
- const controls = await provider.ensureThreadControls?.(threadId);
218
+ const controls = await provider.ensureThreadControls(threadId);
224
219
  if (controls?.recovered) providerThread = await codexAppServerReadThreadStatus(provider, threadId);
225
220
  } catch (error) {
226
221
  current.assertCurrent(session);
227
- // Retain the existing stop owner and its visible recovery state. Do not
222
+ // The stop owner keeps its visible recovery state. This does not
228
223
  // spend automatic goal turns or repeatedly probe a known failed binding.
229
- void Promise.resolve(provider.failObservation?.(error)).catch(() => null);
224
+ void Promise.resolve(provider.failObservation(error)).catch(() => null);
230
225
  throw error;
231
226
  }
232
227
  current.assertCurrent(session);
233
228
  const nativeStatus = codexAppServerThreadRawValue(providerThread).status;
234
- const subscriptionKey = codexAppServerEventSubscriptionKey(
229
+ const subscriptionKey = observer.eventSubscriptionKey(
235
230
  providerKey,
236
231
  threadId
237
232
  );
238
- const subscriptionIsCurrent = codexAppServerEventSubscriptionIsCurrent(subscriptionKey, provider);
233
+ const subscriptionIsCurrent = observer.eventSubscriptionIsCurrent(subscriptionKey, provider);
239
234
  if (!reconnect && (
240
235
  nativeStatus === "notLoaded" || nativeStatus?.type === "notLoaded" || !subscriptionIsCurrent
241
236
  )) {
242
237
  return { ok: true, requiresPreparation: true };
243
238
  }
244
239
  if (!subscriptionIsCurrent) {
245
- subscribeCodexAppServerEvents(
240
+ observer.subscribeEvents(
246
241
  sessionId,
247
242
  provider,
248
243
  threadId,
249
244
  subscriptionOptions
250
245
  );
251
- await provider.resumeThread?.(threadId, {
252
- cwd: normalizeCodexRunText(connection.workdir)
246
+ await provider.resumeThread(threadId, {
247
+ cwd: normalizeText(connection.workdir)
253
248
  });
254
- return reconcileCodexAppServerLoadedThreadStatus(
249
+ return threadStatus.reconcileLoadedThreadStatus(
255
250
  sessionId,
256
251
  provider,
257
252
  threadId,
@@ -277,7 +272,7 @@ export function createCodexThreadReadiness({
277
272
  )) ||
278
273
  (!providerIsActive && trackedTurn.active)
279
274
  ) {
280
- return reconcileCodexAppServerThreadStatus(
275
+ return threadStatus.reconcileThreadStatus(
281
276
  sessionId,
282
277
  provider,
283
278
  threadId,
@@ -295,7 +290,7 @@ export function createCodexThreadReadiness({
295
290
  }
296
291
 
297
292
  async function codexAppServerLoadedThreadIds(provider = null) {
298
- if (typeof provider?.listLoadedThreads !== "function") {
293
+ if (!provider) {
299
294
  return null;
300
295
  }
301
296
  const threadIds = new Set();
@@ -306,12 +301,12 @@ export function createCodexThreadReadiness({
306
301
  limit: 100
307
302
  });
308
303
  for (const threadId of Array.isArray(response?.data) ? response.data : []) {
309
- const normalizedThreadId = normalizeCodexRunText(threadId);
304
+ const normalizedThreadId = normalizeText(threadId);
310
305
  if (normalizedThreadId) {
311
306
  threadIds.add(normalizedThreadId);
312
307
  }
313
308
  }
314
- cursor = normalizeCodexRunText(response?.nextCursor);
309
+ cursor = normalizeText(response?.nextCursor);
315
310
  } while (cursor);
316
311
  return threadIds;
317
312
  }
@@ -320,18 +315,18 @@ export function createCodexThreadReadiness({
320
315
  const { runtime, provider, providerKey, threadId } = context;
321
316
  const loadedThreadIds = await codexAppServerLoadedThreadIds(provider);
322
317
  if (!loadedThreadIds?.has(threadId)) return null;
323
- const subscription = subscribeCodexAppServerEvents(
318
+ const subscription = observer.subscribeEvents(
324
319
  sessionId,
325
320
  provider,
326
321
  threadId,
327
322
  context.subscriptionOptions
328
323
  );
329
- const subscriptionStatus = normalizeCodexRunText(subscription?.status) || "subscribed";
324
+ const subscriptionStatus = normalizeText(subscription?.status) || "subscribed";
330
325
  if (subscriptionStatus !== "alreadySubscribed") {
331
326
  await provider.resumeThread(threadId, context.resumeOptions);
332
327
  }
333
328
  context.remember();
334
- await reconcileCodexAppServerLoadedThreadStatus(
329
+ await threadStatus.reconcileLoadedThreadStatus(
335
330
  sessionId,
336
331
  provider,
337
332
  threadId,
@@ -347,7 +342,7 @@ export function createCodexThreadReadiness({
347
342
  });
348
343
  let loadedSession = await runtime.getSession(sessionId);
349
344
  if (turnState(loadedSession).state === "starting") {
350
- loadedSession = await recoverCodexAppServerActiveTurn(sessionId, {
345
+ loadedSession = await recovery.recoverActiveTurn(sessionId, {
351
346
  provider,
352
347
  retryOnError: false,
353
348
  runtime
@@ -372,12 +367,14 @@ export function createCodexThreadReadiness({
372
367
  };
373
368
  }
374
369
 
375
- return {
370
+ return Object.freeze({
376
371
  threadReconciliations: codexAppServerThreadReconciliations,
377
372
  ensureThreadReady: ensureCodexAppServerThreadReady,
378
373
  reconcileThread: reconcileCodexAppServerThreadForSession,
379
374
  waitForThreadReconciliations: waitForOtherCodexAppServerThreadReconciliations,
380
375
  checkManagedConnection,
381
376
  restoreLoadedThread: restoreCodexAppServerLoadedThread
382
- };
377
+ });
383
378
  }
379
+
380
+ /** @typedef {ReturnType<typeof createCodexThreadReadiness>} CodexThreadReadiness */
@@ -0,0 +1,210 @@
1
+ import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
2
+ import { normalizeNullableText } from "./normalize.js";
3
+ import {
4
+ codexAppServerAssistantItemText,
5
+ codexAppServerErrorText,
6
+ codexAppServerSignedOutText,
7
+ codexAppServerStatusFromValue
8
+ } from "./codexEvents.js";
9
+
10
+ // Like codexAppServerThreadRawValue, but provider results may also wrap the
11
+ // thread once more under `thread`, checked after raw and response.thread.
12
+ function codexAppServerProviderThread(value) {
13
+ if (!isRecord(value?.raw) && !isRecord(value?.response?.thread) && isRecord(value?.thread)) {
14
+ return value.thread;
15
+ }
16
+ return codexAppServerThreadRawValue(value || {});
17
+ }
18
+
19
+ function codexAppServerProviderTurnId(turn = {}) {
20
+ return normalizeNullableText(turn.id || turn.turnId || turn.turn_id || turn.turn?.id || "");
21
+ }
22
+
23
+ function codexAppServerProviderTurnItems(turn = {}) {
24
+ return [
25
+ ...(Array.isArray(turn.items) ? turn.items : []),
26
+ ...(Array.isArray(turn.itemsView) ? turn.itemsView : [])
27
+ ].filter(isRecord);
28
+ }
29
+
30
+ function codexAppServerProviderThreadTurn(value = {}, turnId = "") {
31
+ const normalizedTurnId = normalizeNullableText(turnId);
32
+ if (!normalizedTurnId) {
33
+ return null;
34
+ }
35
+ const thread = codexAppServerProviderThread(value);
36
+ return (Array.isArray(thread.turns) ? thread.turns : [])
37
+ .find((turn) => codexAppServerProviderTurnId(turn) === normalizedTurnId) || null;
38
+ }
39
+
40
+ function codexAppServerProviderTurnAssistantSegments(turn = {}) {
41
+ const seenItemIds = new Set();
42
+ const items = codexAppServerProviderTurnItems(turn);
43
+ // Providers without phases emit progress as ordinary assistant messages.
44
+ // Only the trailing response can be a final; reasoning/tools separate it
45
+ // from earlier updates. Explicit finals retain their native attribution.
46
+ let responseStart = items.length;
47
+ while (responseStart > 0 && codexAppServerAssistantItemText(items[responseStart - 1])) responseStart -= 1;
48
+ const active = ["inProgress", "starting"].includes(codexAppServerStatusFromValue(turn.status));
49
+ return items
50
+ .filter((item, index) => {
51
+ const phase = normalizeNullableText(item.phase);
52
+ return phase === "final_answer" || (!phase && !active && index >= responseStart);
53
+ })
54
+ .map((item) => {
55
+ const itemId = normalizeNullableText(item.id);
56
+ const text = codexAppServerAssistantItemText(item);
57
+ if (
58
+ !itemId ||
59
+ !text ||
60
+ seenItemIds.has(itemId)
61
+ ) {
62
+ return null;
63
+ }
64
+ seenItemIds.add(itemId);
65
+ return {
66
+ itemId,
67
+ text
68
+ };
69
+ })
70
+ .filter(Boolean);
71
+ }
72
+
73
+ function codexAppServerProviderThreadAssistantSegments(value = {}, turnId = "") {
74
+ const turn = codexAppServerProviderThreadTurn(value, turnId);
75
+ return turn ? codexAppServerProviderTurnAssistantSegments(turn) : [];
76
+ }
77
+
78
+ function codexAppServerThreadRawValue(thread = {}) {
79
+ if (isRecord(thread.raw)) {
80
+ return thread.raw;
81
+ }
82
+ if (isRecord(thread.response?.thread)) {
83
+ return thread.response.thread;
84
+ }
85
+ return isRecord(thread) ? thread : {};
86
+ }
87
+
88
+ function codexAppServerThreadTurnId(thread = {}) {
89
+ const observedTurnId = normalizeNullableText(thread.observedTurn?.id || "");
90
+ if (observedTurnId) {
91
+ return observedTurnId;
92
+ }
93
+ const rawThread = codexAppServerThreadRawValue(thread);
94
+ const status = isRecord(rawThread.status) ? rawThread.status : {};
95
+ return normalizeNullableText(
96
+ thread.turnId ||
97
+ thread.turn_id ||
98
+ thread.turn?.id ||
99
+ rawThread.turnId ||
100
+ rawThread.turn_id ||
101
+ rawThread.turn?.id ||
102
+ rawThread.currentTurnId ||
103
+ rawThread.current_turn_id ||
104
+ rawThread.activeTurnId ||
105
+ rawThread.active_turn_id ||
106
+ status.turnId ||
107
+ status.turn_id ||
108
+ status.turn?.id ||
109
+ status.currentTurnId ||
110
+ status.current_turn_id ||
111
+ status.activeTurnId ||
112
+ status.active_turn_id ||
113
+ ""
114
+ );
115
+ }
116
+
117
+ function codexAppServerThreadError(thread = {}) {
118
+ const rawThread = codexAppServerThreadRawValue(thread);
119
+ const status = isRecord(rawThread.status) ? rawThread.status : {};
120
+ return codexAppServerErrorText(rawThread.error || status.error);
121
+ }
122
+
123
+ function codexAppServerProviderThreadTurns(thread = null) {
124
+ const rawThread = codexAppServerThreadRawValue(thread || {});
125
+ return (Array.isArray(rawThread.turns) ? rawThread.turns : [])
126
+ .filter((turn) => isRecord(turn));
127
+ }
128
+
129
+ function codexAppServerProviderTurnStatus(turn = {}) {
130
+ return codexAppServerStatusFromValue(turn.status || turn.state);
131
+ }
132
+
133
+ function codexAppServerProviderTurnClientIds(turn = {}) {
134
+ return codexAppServerProviderTurnItems(turn)
135
+ .map((item) => normalizeNullableText(
136
+ item.clientId ||
137
+ item.client_id ||
138
+ item.clientUserMessageId ||
139
+ item.client_user_message_id ||
140
+ ""
141
+ ))
142
+ .filter(Boolean);
143
+ }
144
+
145
+ function codexAppServerProviderTurnForOperation(thread = null, {
146
+ clientMessageId = "",
147
+ turnId = ""
148
+ } = {}) {
149
+ const normalizedTurnId = normalizeNullableText(turnId || "");
150
+ const normalizedClientMessageId = normalizeNullableText(clientMessageId || "");
151
+ const turns = codexAppServerProviderThreadTurns(thread);
152
+ if (normalizedTurnId) {
153
+ return turns.find((turn) => (
154
+ codexAppServerProviderTurnId(turn) === normalizedTurnId
155
+ )) || null;
156
+ }
157
+ if (!normalizedClientMessageId) {
158
+ return null;
159
+ }
160
+ return [...turns].reverse().find((turn) => (
161
+ codexAppServerProviderTurnClientIds(turn).includes(normalizedClientMessageId)
162
+ )) || null;
163
+ }
164
+
165
+ // Native snapshot matching for renewal handover and seeding.
166
+ // The application projects these classifications into its approval/error policy.
167
+ function inspectCodexAppServerRenewalThread(thread = null, {
168
+ clientMessageId = "",
169
+ turnId: expectedTurnId = "",
170
+ requireFresh = false
171
+ } = {}) {
172
+ const snapshotTurns = codexAppServerProviderThreadTurns(thread);
173
+ const targetTurn = codexAppServerProviderTurnForOperation(thread, {
174
+ clientMessageId,
175
+ turnId: expectedTurnId
176
+ });
177
+ if (expectedTurnId && !targetTurn) return { targetTurn, failure: "turn_missing" };
178
+ if (!requireFresh && !targetTurn && snapshotTurns.length === 0) return { targetTurn, failure: "history_missing" };
179
+ if (requireFresh && snapshotTurns.some((turn) => (
180
+ !targetTurn || codexAppServerProviderTurnId(turn) !== codexAppServerProviderTurnId(targetTurn)
181
+ ))) return { targetTurn, failure: "unrelated_history" };
182
+ return { targetTurn, failure: "" };
183
+ }
184
+
185
+ function codexAppServerProviderTurnText(thread = null, turnId = "") {
186
+ return codexAppServerProviderThreadAssistantSegments(thread || {}, turnId)
187
+ .map((segment) => segment.text)
188
+ .join("\n\n")
189
+ .trim();
190
+ }
191
+
192
+ function codexAppServerProviderTurnError(turn = {}) {
193
+ return codexAppServerSignedOutText(codexAppServerErrorText(turn.error || turn.status?.error || turn.state?.error));
194
+ }
195
+
196
+ export {
197
+ codexAppServerThreadRawValue,
198
+ codexAppServerThreadTurnId,
199
+ codexAppServerThreadError,
200
+ codexAppServerProviderThreadTurns,
201
+ codexAppServerProviderTurnId,
202
+ codexAppServerProviderTurnStatus,
203
+ codexAppServerProviderTurnItems,
204
+ codexAppServerProviderTurnClientIds,
205
+ codexAppServerProviderTurnForOperation,
206
+ inspectCodexAppServerRenewalThread,
207
+ codexAppServerProviderTurnText,
208
+ codexAppServerProviderTurnError,
209
+ codexAppServerProviderThreadAssistantSegments
210
+ };