@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,6 +1,8 @@
1
- import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import crypto from "node:crypto";
2
+ import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
3
  import { normalizeNullableText } from "./normalize.js";
3
- import { codexAppServerStatusFromValue, codexAppServerThreadRawValue } from "./codexEvents.js";
4
+ import { codexAppServerStatusFromValue } from "./codexEvents.js";
5
+ import { codexAppServerThreadRawValue } from "./codexThreadSnapshot.js";
4
6
 
5
7
  function codexAppServerTurnStatusIsActive(status = "") {
6
8
  return normalizeNullableText(status) === "inProgress";
@@ -36,6 +38,12 @@ function codexAppServerThreadStatus(thread = {}) {
36
38
  return liveStatus;
37
39
  }
38
40
 
41
+ /**
42
+ * Reads a native thread's status, and with observeLatestTurn its latest turn too.
43
+ * @param {import("./codexProvider.js").CodexAppServerAgentProvider | null} provider
44
+ * @param {string} threadId
45
+ * @param {{ observeLatestTurn?: boolean }} [options]
46
+ */
39
47
  async function codexAppServerReadThreadStatus(provider = null, threadId = "", {
40
48
  observeLatestTurn = false
41
49
  } = {}) {
@@ -43,9 +51,9 @@ async function codexAppServerReadThreadStatus(provider = null, threadId = "", {
43
51
  if (!normalizedThreadId) {
44
52
  return null;
45
53
  }
46
- if (typeof provider?.readThreadStatus === "function") {
54
+ if (provider) {
47
55
  const thread = await provider.readThreadStatus(normalizedThreadId);
48
- if (!observeLatestTurn || typeof provider?.listThreadTurns !== "function") {
56
+ if (!observeLatestTurn) {
49
57
  return thread;
50
58
  }
51
59
  const response = await provider.listThreadTurns(normalizedThreadId, {
@@ -54,7 +62,7 @@ async function codexAppServerReadThreadStatus(provider = null, threadId = "", {
54
62
  sortDirection: "desc"
55
63
  });
56
64
  const observedTurn = Array.isArray(response?.data) ? response.data[0] : null;
57
- if (provider.isControlProbeTurn?.(normalizedThreadId, observedTurn?.id)) return thread;
65
+ if (provider.isControlProbeTurn(normalizedThreadId, observedTurn?.id)) return thread;
58
66
  return isRecord(observedTurn)
59
67
  ? {
60
68
  ...thread,
@@ -65,9 +73,9 @@ async function codexAppServerReadThreadStatus(provider = null, threadId = "", {
65
73
  return null;
66
74
  }
67
75
 
68
- // Original native run projection and guarded mutation sequence. The caller's
69
- // existing runtime/store owns persistence and its mutation lock; no second
70
- // request journal or transcript-derived ownership is introduced here.
76
+ // The native run projection and its guarded mutation sequence. The caller's
77
+ // runtime store owns persistence and its mutation lock; there is no second
78
+ // request journal and no ownership derived from the transcript.
71
79
  const CODEX_APP_SERVER_AGENT_RUN_ID = "codex_app_server";
72
80
  const CODEX_APP_SERVER_RESULT_PROCESSED_EVENT = "codex-app-server-result-processed";
73
81
  const CODEX_AGENT_PROVIDER = "codex";
@@ -95,16 +103,12 @@ const CODEX_APP_SERVER_ACTIVE_RUN_STATES = new Set([
95
103
  CODEX_APP_SERVER_RUN_STATE.STARTING
96
104
  ]);
97
105
 
98
- function normalizeCodexRunText(value) {
99
- return String(value || "").trim();
100
- }
101
-
102
- function codexAppServerRunInputSource(run = {}) {
103
- return normalizeCodexRunText(run?.inputSource);
106
+ function codexAppServerRunInputSource(run) {
107
+ return normalizeText(run?.inputSource);
104
108
  }
105
109
 
106
110
  export function normalizeCodexAppServerRunState(state) {
107
- const normalizedState = normalizeCodexRunText(state) || CODEX_APP_SERVER_RUN_STATE.STARTING;
111
+ const normalizedState = normalizeText(state) || CODEX_APP_SERVER_RUN_STATE.STARTING;
108
112
  if (!CODEX_APP_SERVER_RUN_STATES.has(normalizedState)) {
109
113
  throw Object.assign(new Error(`Invalid Codex app-server run state: ${normalizedState}`), {
110
114
  code: "codex_app_server_invalid_run_state"
@@ -123,19 +127,19 @@ export function codexAppServerRunStateIsTerminal(state) {
123
127
 
124
128
  export function codexAppServerAgentRun(session = {}) {
125
129
  const runs = Array.isArray(session.agentRuns) ? session.agentRuns : [];
126
- return runs.find((run) => normalizeCodexRunText(run?.id) === CODEX_APP_SERVER_AGENT_RUN_ID) || null;
130
+ return runs.find((run) => normalizeText(run?.id) === CODEX_APP_SERVER_AGENT_RUN_ID) || null;
127
131
  }
128
132
 
129
- export function codexAppServerPendingUserMessageClientIds(run = {}) {
133
+ export function codexAppServerPendingUserMessageClientIds(run) {
130
134
  const ids = run?.pendingUserMessageClientIds;
131
135
  return (Array.isArray(ids) ? ids : [])
132
- .map((id) => normalizeCodexRunText(id))
136
+ .map((id) => normalizeText(id))
133
137
  .filter(Boolean);
134
138
  }
135
139
 
136
- export function codexAppServerPendingUserMessageOwnership(run = {}, clientId = "") {
140
+ export function codexAppServerPendingUserMessageOwnership(run, clientId = "") {
137
141
  const pendingClientIds = codexAppServerPendingUserMessageClientIds(run);
138
- const normalizedClientId = normalizeCodexRunText(clientId);
142
+ const normalizedClientId = normalizeText(clientId);
139
143
  const ownedClientId = normalizedClientId
140
144
  ? pendingClientIds.find((id) => id === normalizedClientId)
141
145
  : pendingClientIds[0];
@@ -144,18 +148,18 @@ export function codexAppServerPendingUserMessageOwnership(run = {}, clientId = "
144
148
  }
145
149
  return {
146
150
  clientId: ownedClientId,
147
- inputSource: normalizeCodexRunText(run?.inputSource)
151
+ inputSource: normalizeText(run?.inputSource)
148
152
  };
149
153
  }
150
154
 
151
155
  export function codexAppServerProcessedResultEvent(session = {}, threadId = "", turnId = "") {
152
- const normalizedThreadId = normalizeCodexRunText(threadId);
153
- const normalizedTurnId = normalizeCodexRunText(turnId);
156
+ const normalizedThreadId = normalizeText(threadId);
157
+ const normalizedTurnId = normalizeText(turnId);
154
158
  const events = codexAppServerAgentRun(session)?.events;
155
159
  return (Array.isArray(events) ? events : []).findLast((event) => (
156
- normalizeCodexRunText(event?.kind) === CODEX_APP_SERVER_RESULT_PROCESSED_EVENT &&
157
- normalizeCodexRunText(event?.providerThreadId) === normalizedThreadId &&
158
- normalizeCodexRunText(event?.providerTurnId) === normalizedTurnId
160
+ normalizeText(event?.kind) === CODEX_APP_SERVER_RESULT_PROCESSED_EVENT &&
161
+ normalizeText(event?.providerThreadId) === normalizedThreadId &&
162
+ normalizeText(event?.providerTurnId) === normalizedTurnId
159
163
  )) || null;
160
164
  }
161
165
 
@@ -171,21 +175,21 @@ export function codexAppServerTurnStateFromAgentRun(run = {}, normalizeRunState
171
175
  : "idle";
172
176
  return {
173
177
  active,
174
- completedAt: normalizeCodexRunText(run.finishedAt),
175
- error: normalizeCodexRunText(run.error),
176
- goalStatus: normalizeCodexRunText(run.providerGoalStatus),
177
- goalThreadId: normalizeCodexRunText(run.providerGoalThreadId),
178
- inputSource: normalizeCodexRunText(run.inputSource),
179
- outerTurnId: normalizeCodexRunText(run.outerTurnId),
180
- phase: active && run.providerStatus !== "observation_lost" ? normalizeCodexRunText(run.providerPhase) : "",
181
- runId: normalizeCodexRunText(run.id),
178
+ completedAt: normalizeText(run.finishedAt),
179
+ error: normalizeText(run.error),
180
+ goalStatus: normalizeText(run.providerGoalStatus),
181
+ goalThreadId: normalizeText(run.providerGoalThreadId),
182
+ inputSource: normalizeText(run.inputSource),
183
+ outerTurnId: normalizeText(run.outerTurnId),
184
+ phase: active && run.providerStatus !== "observation_lost" ? normalizeText(run.providerPhase) : "",
185
+ runId: normalizeText(run.id),
182
186
  runState,
183
- startedAt: normalizeCodexRunText(run.startedAt),
187
+ startedAt: normalizeText(run.startedAt),
184
188
  state,
185
- status: normalizeCodexRunText(run.providerStatus || run.status || runState),
186
- threadId: normalizeCodexRunText(run.providerThreadId),
187
- turnId: normalizeCodexRunText(run.providerTurnId),
188
- updatedAt: normalizeCodexRunText(run.updatedAt)
189
+ status: normalizeText(run.providerStatus || run.status || runState),
190
+ threadId: normalizeText(run.providerThreadId),
191
+ turnId: normalizeText(run.providerTurnId),
192
+ updatedAt: normalizeText(run.updatedAt)
189
193
  };
190
194
  }
191
195
 
@@ -215,23 +219,23 @@ export function codexAppServerTurnState(session = {}, normalizeRunState = normal
215
219
  }
216
220
 
217
221
  export function codexAppServerTurnOwnsActiveGoal(turn = {}, threadId = "") {
218
- const normalizedThreadId = normalizeCodexRunText(threadId);
219
- const goalThreadId = normalizeCodexRunText(turn.goalThreadId);
220
- const inputSource = normalizeCodexRunText(turn.inputSource);
221
- return normalizeCodexRunText(turn.goalStatus) === "active" &&
222
+ const normalizedThreadId = normalizeText(threadId);
223
+ const goalThreadId = normalizeText(turn.goalThreadId);
224
+ const inputSource = normalizeText(turn.inputSource);
225
+ return normalizeText(turn.goalStatus) === "active" &&
222
226
  Boolean(normalizedThreadId) &&
223
227
  goalThreadId === normalizedThreadId &&
224
- Boolean(normalizeCodexRunText(turn.outerTurnId)) &&
228
+ Boolean(normalizeText(turn.outerTurnId)) &&
225
229
  Boolean(inputSource) &&
226
230
  inputSource !== "terminal";
227
231
  }
228
232
 
229
233
  export function codexAppServerTurnMatches(turn = {}, threadId = "", turnId = "") {
230
- const normalizedThreadId = normalizeCodexRunText(threadId);
231
- const normalizedTurnId = normalizeCodexRunText(turnId);
232
- const currentThreadId = normalizeCodexRunText(turn.threadId);
233
- const currentTurnId = normalizeCodexRunText(turn.turnId);
234
- if (normalizedThreadId && normalizeCodexRunText(turn.threadId) !== normalizedThreadId) {
234
+ const normalizedThreadId = normalizeText(threadId);
235
+ const normalizedTurnId = normalizeText(turnId);
236
+ const currentThreadId = normalizeText(turn.threadId);
237
+ const currentTurnId = normalizeText(turn.turnId);
238
+ if (normalizedThreadId && normalizeText(turn.threadId) !== normalizedThreadId) {
235
239
  return false;
236
240
  }
237
241
  if (normalizedTurnId && currentTurnId && currentTurnId !== normalizedTurnId) {
@@ -248,26 +252,26 @@ export function codexAppServerTurnMatches(turn = {}, threadId = "", turnId = "")
248
252
 
249
253
  export function codexAppServerTurnCanReceiveProviderCompletion(turn = {}, threadId = "", turnId = "") {
250
254
  return codexAppServerTurnMatches(turn, threadId, turnId) &&
251
- ["active", "finalizing"].includes(normalizeCodexRunText(turn.state));
255
+ ["active", "finalizing"].includes(normalizeText(turn.state));
252
256
  }
253
257
 
254
258
  export function codexAppServerTurnAwaitsProviderIdentity(turn = {}, threadId = "", turnId = "") {
255
- const normalizedThreadId = normalizeCodexRunText(threadId);
256
- const normalizedTurnId = normalizeCodexRunText(turnId);
257
- const currentThreadId = normalizeCodexRunText(turn.threadId);
258
- return normalizeCodexRunText(turn.state) === "starting" &&
259
+ const normalizedThreadId = normalizeText(threadId);
260
+ const normalizedTurnId = normalizeText(turnId);
261
+ const currentThreadId = normalizeText(turn.threadId);
262
+ return normalizeText(turn.state) === "starting" &&
259
263
  Boolean(normalizedTurnId) &&
260
- !normalizeCodexRunText(turn.turnId) &&
264
+ !normalizeText(turn.turnId) &&
261
265
  (!normalizedThreadId || !currentThreadId || normalizedThreadId === currentThreadId);
262
266
  }
263
267
 
264
268
  export function codexAppServerTurnCanReceiveProviderActivity(turn = {}, threadId = "", turnId = "") {
265
269
  if (turn.status === "observation_lost") return false;
266
- const normalizedThreadId = normalizeCodexRunText(threadId);
267
- const normalizedTurnId = normalizeCodexRunText(turnId);
268
- const currentThreadId = normalizeCodexRunText(turn.threadId);
269
- const currentTurnId = normalizeCodexRunText(turn.turnId);
270
- if (!["starting", "active"].includes(normalizeCodexRunText(turn.state))) {
270
+ const normalizedThreadId = normalizeText(threadId);
271
+ const normalizedTurnId = normalizeText(turnId);
272
+ const currentThreadId = normalizeText(turn.threadId);
273
+ const currentTurnId = normalizeText(turn.turnId);
274
+ if (!["starting", "active"].includes(normalizeText(turn.state))) {
271
275
  return false;
272
276
  }
273
277
  if (normalizedThreadId && currentThreadId && currentThreadId !== normalizedThreadId) {
@@ -284,12 +288,12 @@ export function codexAppServerTurnCanReceiveProviderActivity(turn = {}, threadId
284
288
 
285
289
  export function codexAppServerTurnCanAdoptSuccessor(turn = {}, threadId = "", turnId = "") {
286
290
  if (turn.status === "observation_lost") return false;
287
- const normalizedThreadId = normalizeCodexRunText(threadId);
288
- const normalizedTurnId = normalizeCodexRunText(turnId);
289
- const currentThreadId = normalizeCodexRunText(turn.threadId);
290
- const currentTurnId = normalizeCodexRunText(turn.turnId);
291
+ const normalizedThreadId = normalizeText(threadId);
292
+ const normalizedTurnId = normalizeText(turnId);
293
+ const currentThreadId = normalizeText(turn.threadId);
294
+ const currentTurnId = normalizeText(turn.turnId);
291
295
  return turn.active === true &&
292
- ["active", "finalizing"].includes(normalizeCodexRunText(turn.state)) &&
296
+ ["active", "finalizing"].includes(normalizeText(turn.state)) &&
293
297
  Boolean(normalizedThreadId) &&
294
298
  normalizedThreadId === currentThreadId &&
295
299
  Boolean(normalizedTurnId) &&
@@ -298,7 +302,30 @@ export function codexAppServerTurnCanAdoptSuccessor(turn = {}, threadId = "", tu
298
302
  }
299
303
 
300
304
  export function codexAppServerTurnKey(threadId = "", turnId = "") {
301
- return `${normalizeCodexRunText(threadId)}:${normalizeCodexRunText(turnId)}`;
305
+ return `${normalizeText(threadId)}:${normalizeText(turnId)}`;
306
+ }
307
+
308
+ // Identity of a native conversation message, derived from its thread, turn, role and text.
309
+ export function codexAppServerConversationMessageId(
310
+ threadId = "",
311
+ turnId = "",
312
+ role = "",
313
+ text = ""
314
+ ) {
315
+ const normalizedText = normalizeText(text);
316
+ const normalizedThreadId = normalizeText(threadId);
317
+ if (!normalizedText || !normalizedThreadId) {
318
+ return "";
319
+ }
320
+ const digest = crypto.createHash("sha256")
321
+ .update([
322
+ normalizedThreadId,
323
+ normalizeText(turnId) || "*",
324
+ normalizeText(role),
325
+ normalizedText
326
+ ].join("\u0000"))
327
+ .digest("hex");
328
+ return `codex-${digest}`;
302
329
  }
303
330
 
304
331
  // Identity of a native outer turn. Terminal turns and persistent-conversation
@@ -322,31 +349,31 @@ export function codexAppServerAgentRunPatch({
322
349
  const normalizedRunState = normalizeRunState(runState);
323
350
  const currentRun = codexAppServerAgentRun(session);
324
351
  const sameActiveTurn = normalizedRunState === CODEX_APP_SERVER_RUN_STATE.ACTIVE &&
325
- currentRun?.providerThreadId === normalizeCodexRunText(threadId) &&
326
- currentRun?.providerTurnId === normalizeCodexRunText(turnId);
352
+ currentRun?.providerThreadId === normalizeText(threadId) &&
353
+ currentRun?.providerTurnId === normalizeText(turnId);
327
354
  const patch = {
328
- error: normalizeCodexRunText(error),
355
+ error: normalizeText(error),
329
356
  provider: CODEX_AGENT_PROVIDER,
330
357
  providerInterface: "codex_app_server",
331
358
  providerPhase: normalizedRunState === CODEX_APP_SERVER_RUN_STATE.ACTIVE && status !== "observation_lost"
332
- ? normalizeCodexRunText(phase === undefined && sameActiveTurn ? currentRun?.providerPhase : phase)
359
+ ? normalizeText(phase === undefined && sameActiveTurn ? currentRun?.providerPhase : phase)
333
360
  : "",
334
- providerStatus: normalizeCodexRunText(status),
335
- providerThreadId: normalizeCodexRunText(threadId),
336
- providerTurnId: normalizeCodexRunText(turnId),
361
+ providerStatus: normalizeText(status),
362
+ providerThreadId: normalizeText(threadId),
363
+ providerTurnId: normalizeText(turnId),
337
364
  state: normalizedRunState,
338
- updatedAt: normalizeCodexRunText(updatedAt)
365
+ updatedAt: normalizeText(updatedAt)
339
366
  };
340
- const normalizedInputSource = normalizeCodexRunText(inputSource);
367
+ const normalizedInputSource = normalizeText(inputSource);
341
368
  if (normalizedInputSource) {
342
369
  patch.inputSource = normalizedInputSource;
343
370
  }
344
371
  // A native terminal turn starts its own chat owner. Reusing the previous
345
372
  // owner's id would checkpoint two different turns with one identity.
346
- const normalizedOuterTurnId = normalizeCodexRunText(outerTurnId) ||
373
+ const normalizedOuterTurnId = normalizeText(outerTurnId) ||
347
374
  (normalizedInputSource === "terminal" && normalizedRunState === CODEX_APP_SERVER_RUN_STATE.ACTIVE && !sameActiveTurn
348
- ? codexAppServerNativeOuterTurnId(normalizeCodexRunText(threadId), normalizeCodexRunText(turnId))
349
- : normalizeCodexRunText(currentRun?.outerTurnId));
375
+ ? codexAppServerNativeOuterTurnId(normalizeText(threadId), normalizeText(turnId))
376
+ : normalizeText(currentRun?.outerTurnId));
350
377
  if (normalizedOuterTurnId) {
351
378
  patch.outerTurnId = normalizedOuterTurnId;
352
379
  }
@@ -358,10 +385,10 @@ export function codexAppServerAgentRunPatch({
358
385
  patch.pendingUserMessageClientIds = [];
359
386
  }
360
387
  if ([CODEX_APP_SERVER_RUN_STATE.ACTIVE, CODEX_APP_SERVER_RUN_STATE.STARTING].includes(normalizedRunState)) {
361
- patch.startedAt = normalizeCodexRunText(updatedAt);
388
+ patch.startedAt = normalizeText(updatedAt);
362
389
  }
363
390
  if (!codexAppServerRunStateIsActive(normalizedRunState)) {
364
- patch.finishedAt = normalizeCodexRunText(updatedAt);
391
+ patch.finishedAt = normalizeText(updatedAt);
365
392
  }
366
393
  return patch;
367
394
  }
@@ -370,8 +397,8 @@ export function codexAppServerRunIdentityForPatch(session = {}, {
370
397
  threadId = "",
371
398
  turnId = ""
372
399
  } = {}, normalizeRunState = normalizeCodexAppServerRunState) {
373
- const normalizedThreadId = normalizeCodexRunText(threadId);
374
- const normalizedTurnId = normalizeCodexRunText(turnId);
400
+ const normalizedThreadId = normalizeText(threadId);
401
+ const normalizedTurnId = normalizeText(turnId);
375
402
  if (normalizedTurnId) {
376
403
  return {
377
404
  threadId: normalizedThreadId,
@@ -379,10 +406,10 @@ export function codexAppServerRunIdentityForPatch(session = {}, {
379
406
  };
380
407
  }
381
408
  const currentTurn = codexAppServerTurnState(session, normalizeRunState);
382
- const currentThreadId = normalizeCodexRunText(currentTurn.threadId);
409
+ const currentThreadId = normalizeText(currentTurn.threadId);
383
410
  if (
384
- normalizeCodexRunText(currentTurn.turnId) &&
385
- ["active", "finalizing"].includes(normalizeCodexRunText(currentTurn.state)) &&
411
+ normalizeText(currentTurn.turnId) &&
412
+ ["active", "finalizing"].includes(normalizeText(currentTurn.state)) &&
386
413
  (!normalizedThreadId || !currentThreadId || normalizedThreadId === currentThreadId)
387
414
  ) {
388
415
  return {
@@ -399,8 +426,8 @@ export function codexAppServerRunIdentityForPatch(session = {}, {
399
426
  // A settling writer names the turn it settles. While another turn owns the
400
427
  // active run, that write belongs to a predecessor and must not end the run.
401
428
  export function codexAppServerRunPatchSettlesOtherActiveTurn(currentTurn = {}, patch = {}) {
402
- const currentTurnId = normalizeCodexRunText(currentTurn.turnId);
403
- const patchTurnId = normalizeCodexRunText(patch.providerTurnId);
429
+ const currentTurnId = normalizeText(currentTurn.turnId);
430
+ const patchTurnId = normalizeText(patch.providerTurnId);
404
431
  return currentTurn.active === true &&
405
432
  !codexAppServerRunStateIsActive(patch.state) &&
406
433
  Boolean(currentTurnId) &&
@@ -417,10 +444,10 @@ export function codexAppServerRunPatchIsStaleAfterTerminalState(currentTurn = {}
417
444
  if (patchRunState === CODEX_APP_SERVER_RUN_STATE.STARTING) {
418
445
  return false;
419
446
  }
420
- const currentThreadId = normalizeCodexRunText(currentTurn.threadId);
421
- const patchThreadId = normalizeCodexRunText(patch.providerThreadId);
422
- const currentTurnId = normalizeCodexRunText(currentTurn.turnId);
423
- const patchTurnId = normalizeCodexRunText(patch.providerTurnId);
447
+ const currentThreadId = normalizeText(currentTurn.threadId);
448
+ const patchThreadId = normalizeText(patch.providerThreadId);
449
+ const currentTurnId = normalizeText(currentTurn.turnId);
450
+ const patchTurnId = normalizeText(patch.providerTurnId);
424
451
  const threadMatches = !patchThreadId || !currentThreadId || patchThreadId === currentThreadId;
425
452
  const turnMatches = !patchTurnId || (
426
453
  Boolean(currentTurnId) &&
@@ -430,7 +457,7 @@ export function codexAppServerRunPatchIsStaleAfterTerminalState(currentTurn = {}
430
457
  }
431
458
 
432
459
  function codexAppServerErrorMessage(value, fallback = "Codex could not be prepared.") {
433
- return normalizeCodexRunText(value?.error || value?.message || value) || fallback;
460
+ return normalizeText(value?.error || value?.message || value) || fallback;
434
461
  }
435
462
 
436
463
  export function codexAppServerSteerFailure(result = {}, steerFailedCode = "codex_turn_steer_failed") {
@@ -441,10 +468,10 @@ export function codexAppServerSteerFailure(result = {}, steerFailedCode = "codex
441
468
  return null;
442
469
  }
443
470
  return {
444
- code: normalizeCodexRunText(result.code) || steerFailedCode,
471
+ code: normalizeText(result.code) || steerFailedCode,
445
472
  error: codexAppServerErrorMessage(result, "Codex app-server turn could not be steered."),
446
473
  ok: false,
447
- operationOutcome: normalizeCodexRunText(result.operationOutcome) || "steer_failed",
474
+ operationOutcome: normalizeText(result.operationOutcome) || "steer_failed",
448
475
  refreshRecommended: true,
449
476
  retryable: result.retryable === true
450
477
  };
@@ -461,9 +488,9 @@ export function codexAppServerMessageRequiresNewTurn({
461
488
  newTurnRequired: true,
462
489
  ok: true,
463
490
  operationOutcome: "new_turn_required",
464
- reason: normalizeCodexRunText(reason),
465
- threadId: normalizeCodexRunText(threadId),
466
- turnId: normalizeCodexRunText(turnId)
491
+ reason: normalizeText(reason),
492
+ threadId: normalizeText(threadId),
493
+ turnId: normalizeText(turnId)
467
494
  };
468
495
  }
469
496
 
@@ -494,10 +521,10 @@ export function codexAppServerInterruptFailure(result = {}, interruptFailedCode
494
521
  return null;
495
522
  }
496
523
  return {
497
- code: normalizeCodexRunText(result.code) || interruptFailedCode,
524
+ code: normalizeText(result.code) || interruptFailedCode,
498
525
  error: codexAppServerErrorMessage(result, "Codex app-server turn could not be interrupted."),
499
526
  ok: false,
500
- operationOutcome: normalizeCodexRunText(result.operationOutcome) || "interrupt_failed",
527
+ operationOutcome: normalizeText(result.operationOutcome) || "interrupt_failed",
501
528
  refreshRecommended: true,
502
529
  retryable: result.retryable === true
503
530
  };
@@ -518,8 +545,8 @@ export function codexAppServerInterruptUnavailableResponse({
518
545
  operationOutcome: "interrupt_unavailable",
519
546
  refreshRecommended: true,
520
547
  retryable: active === true,
521
- threadId: normalizeCodexRunText(threadId),
522
- turnId: normalizeCodexRunText(turnId)
548
+ threadId: normalizeText(threadId),
549
+ turnId: normalizeText(turnId)
523
550
  };
524
551
  }
525
552
 
@@ -532,41 +559,41 @@ export function codexAppServerFrozenTurnInterruptResponse({
532
559
  ok: true,
533
560
  operationOutcome: "already_idle",
534
561
  status: "interrupted",
535
- threadId: normalizeCodexRunText(threadId),
536
- turnId: normalizeCodexRunText(turnId)
562
+ threadId: normalizeText(threadId),
563
+ turnId: normalizeText(turnId)
537
564
  };
538
565
  }
539
566
 
540
567
  export function codexAppServerConversationTurnIsActive(status = "") {
541
- return ["starting", "inProgress"].includes(normalizeCodexRunText(status));
568
+ return ["starting", "inProgress"].includes(normalizeText(status));
542
569
  }
543
570
 
544
571
  export function codexAppServerMessageText(input = {}) {
545
572
  if (typeof input === "string") {
546
- return normalizeCodexRunText(input);
573
+ return normalizeText(input);
547
574
  }
548
575
  if (!isRecord(input)) {
549
576
  return "";
550
577
  }
551
- return normalizeCodexRunText(input.message);
578
+ return normalizeText(input.message);
552
579
  }
553
580
 
554
581
  export function codexAppServerMessageDisplayText(input = {}, fallback = "") {
555
582
  if (!isRecord(input)) {
556
- return normalizeCodexRunText(fallback || input);
583
+ return normalizeText(fallback || input);
557
584
  }
558
- return normalizeCodexRunText(input.displayMessage || input.message || fallback);
585
+ return normalizeText(input.displayMessage || input.message || fallback);
559
586
  }
560
587
 
561
588
  function codexPromptInputFromRequest(input = {}) {
562
589
  if (typeof input === "string") {
563
- return normalizeCodexRunText(input);
590
+ return normalizeText(input);
564
591
  }
565
- const terminalInput = normalizeCodexRunText(input.terminalInput);
592
+ const terminalInput = normalizeText(input.terminalInput);
566
593
  if (terminalInput) {
567
594
  return terminalInput;
568
595
  }
569
596
  return codexAppServerMessageText(input);
570
597
  }
571
598
 
572
- export { codexAppServerRunInputSource, codexAppServerThreadStatus, codexAppServerReadThreadStatus, codexAppServerTurnStatusIsActive, codexAppServerTurnStatusIsComplete, codexAppServerTurnStatusIsSuccessfulComplete, codexAppServerTurnStatusIsProviderFailure, normalizeCodexRunText, codexAppServerErrorMessage, codexAppServerMessageDeferred, codexPromptInputFromRequest, CODEX_APP_SERVER_AGENT_RUN_ID, CODEX_APP_SERVER_RESULT_PROCESSED_EVENT, CODEX_AGENT_PROVIDER, CODEX_APP_SERVER_ACTIVE_RUN_STATES };
599
+ export { codexAppServerRunInputSource, codexAppServerThreadStatus, codexAppServerReadThreadStatus, codexAppServerTurnStatusIsActive, codexAppServerTurnStatusIsComplete, codexAppServerTurnStatusIsSuccessfulComplete, codexAppServerTurnStatusIsProviderFailure, codexAppServerErrorMessage, codexAppServerMessageDeferred, codexPromptInputFromRequest, CODEX_APP_SERVER_AGENT_RUN_ID, CODEX_APP_SERVER_RESULT_PROCESSED_EVENT, CODEX_AGENT_PROVIDER, CODEX_APP_SERVER_ACTIVE_RUN_STATES };