@jskit-ai/assistant-core 0.1.199 → 0.1.201

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/examples/conversation/README.md +8 -1
  2. package/examples/conversation/package.json +5 -5
  3. package/fixtures/responsive-assistant/App.vue +4 -1
  4. package/package.json +99 -36
  5. package/src/client/conversation/AssistantConversationElement.vue +27 -4
  6. package/src/client/conversation/AssistantConversationStatus.vue +24 -0
  7. package/src/client/conversation/AssistantGoalControl.vue +8 -5
  8. package/src/client/conversation/AssistantTranscript.vue +10 -3
  9. package/src/client/conversation/index.js +2 -0
  10. package/src/client/conversation/messageDelivery.js +115 -32
  11. package/src/client/conversation/retainedConversation.js +32 -0
  12. package/src/client/conversation/useAssistantQuestions.js +76 -0
  13. package/src/client/lib/assistantApi.js +72 -2
  14. package/src/server/conversation/agentRun.js +222 -0
  15. package/src/server/conversation/attachments.js +93 -0
  16. package/src/server/conversation/claudeClient.js +221 -0
  17. package/src/server/conversation/claudeHistory.js +256 -0
  18. package/src/server/conversation/claudeProcess.js +327 -0
  19. package/src/server/conversation/claudeTools.js +42 -0
  20. package/src/server/conversation/claudeTurn.js +1082 -0
  21. package/src/server/conversation/codexConfiguration.js +1183 -0
  22. package/src/server/conversation/codexConversations.js +1082 -0
  23. package/src/server/conversation/codexDetachedTurn.js +332 -0
  24. package/src/server/conversation/codexEvents.js +194 -1
  25. package/src/server/conversation/codexGoalCommands.js +124 -0
  26. package/src/server/conversation/codexHelperExecution.js +306 -0
  27. package/src/server/conversation/codexHelperThreadLedger.js +804 -0
  28. package/src/server/conversation/codexHelperThreadRecovery.js +395 -0
  29. package/src/server/conversation/codexHelperThreads.js +638 -0
  30. package/src/server/conversation/codexHistoryAdapter.js +369 -0
  31. package/src/server/conversation/codexMessageCommands.js +663 -0
  32. package/src/server/conversation/codexMessageDelivery.js +211 -0
  33. package/src/server/conversation/codexNativeHistoryExport.js +95 -0
  34. package/src/server/conversation/codexNotificationObserver.js +482 -0
  35. package/src/server/conversation/codexNotificationQueue.js +103 -0
  36. package/src/server/conversation/codexObservationControl.js +174 -0
  37. package/src/server/conversation/codexOutput.js +931 -0
  38. package/src/server/conversation/codexProcess.js +1559 -0
  39. package/src/server/conversation/codexProcessLeader.js +14 -0
  40. package/src/server/conversation/codexProvider.js +2710 -0
  41. package/src/server/conversation/codexProviderOwner.js +1058 -0
  42. package/src/server/conversation/codexProviderSelection.js +218 -0
  43. package/src/server/conversation/codexReasoning.js +314 -0
  44. package/src/server/conversation/codexRenewalCommands.js +290 -0
  45. package/src/server/conversation/codexResultSettlement.js +409 -0
  46. package/src/server/conversation/codexRunJournal.js +658 -0
  47. package/src/server/conversation/codexRuntimeLifecycle.js +288 -0
  48. package/src/server/conversation/codexSessionCleanup.js +96 -0
  49. package/src/server/conversation/codexSessionReconciliation.js +284 -0
  50. package/src/server/conversation/codexThreadReadiness.js +397 -0
  51. package/src/server/conversation/codexThreadStatus.js +354 -0
  52. package/src/server/conversation/codexTurn.js +701 -255
  53. package/src/server/conversation/codexTurnControl.js +317 -0
  54. package/src/server/conversation/codexTurnRecovery.js +990 -0
  55. package/src/server/conversation/codexTurnState.js +549 -0
  56. package/src/server/conversation/commandHook.js +18 -0
  57. package/src/server/conversation/commandWrapper.js +37 -0
  58. package/src/server/conversation/configuration.js +43 -0
  59. package/src/server/conversation/continuity.js +289 -0
  60. package/src/server/conversation/fileStorage.js +55 -0
  61. package/src/server/conversation/hookBridge.js +19 -0
  62. package/src/server/conversation/index.js +13 -0
  63. package/src/server/conversation/localExecution.js +665 -0
  64. package/src/server/conversation/memoryStorage.js +7 -72
  65. package/src/server/conversation/nativeHistoryExport.js +90 -0
  66. package/src/server/conversation/nativeStorage.js +23 -0
  67. package/src/server/conversation/openCodeClient.js +209 -19
  68. package/src/server/conversation/openCodePlugin.js +84 -0
  69. package/src/server/conversation/openCodeProcess.js +476 -0
  70. package/src/server/conversation/openCodeRuntime.js +1578 -0
  71. package/src/server/conversation/openCodeTools.js +60 -0
  72. package/src/server/conversation/openCodeTurn.js +557 -0
  73. package/src/server/conversation/provider.js +30 -0
  74. package/src/server/conversation/providers/api.js +136 -0
  75. package/src/server/conversation/providers/claude.js +63 -0
  76. package/src/server/conversation/providers/claudeDriver.js +425 -0
  77. package/src/server/conversation/providers/codex.js +248 -0
  78. package/src/server/conversation/providers/codexDriver.js +1078 -0
  79. package/src/server/conversation/providers/opencode.js +49 -0
  80. package/src/server/conversation/providers/opencodeDriver.js +650 -0
  81. package/src/server/conversation/providers/scoped.js +312 -0
  82. package/src/server/conversation/runtime.js +2220 -0
  83. package/src/server/conversation/runtimeStateUpgrade.js +125 -0
  84. package/src/server/conversation/storage.js +184 -0
  85. package/src/server/conversation/streams.js +7 -2
  86. package/src/server/conversation/structuredOutput.js +240 -0
  87. package/src/server/conversation/systemPrompt.js +54 -0
  88. package/src/server/conversation/tools.js +84 -0
  89. package/src/server/conversation/transcript.js +5 -4
  90. package/src/server/hosts/claudeHistory.js +5 -0
  91. package/src/server/hosts/claudeProcess.js +11 -0
  92. package/src/server/hosts/claudeTurn.js +4 -0
  93. package/src/server/hosts/codexConfiguration.js +20 -0
  94. package/src/server/hosts/codexEvents.js +13 -0
  95. package/src/server/hosts/codexProcess.js +12 -0
  96. package/src/server/hosts/codexProvider.js +23 -0
  97. package/src/server/hosts/codexTurn.js +20 -0
  98. package/src/server/hosts/nativeHistory.js +3 -0
  99. package/src/server/hosts/openCodeClient.js +3 -0
  100. package/src/server/hosts/openCodeProcess.js +23 -0
  101. package/src/server/hosts/openCodeTurn.js +7 -0
  102. package/src/server/index.js +2 -0
  103. package/src/server/lib/aiConnectionClient.js +13 -6
  104. package/src/server/lib/assistantCompletion.js +190 -0
  105. package/src/server/lib/assistantToolLoop.js +314 -0
  106. package/src/server/lib/serviceToolCatalog.js +25 -10
  107. package/src/shared/assistantResponseText.js +29 -0
  108. package/src/shared/conversation/attachments.js +19 -0
  109. package/src/shared/conversation/index.js +3 -0
  110. package/src/shared/conversation/pagination.js +78 -0
  111. package/src/shared/conversation/patches.js +150 -0
  112. package/src/shared/conversation/questions.js +48 -0
  113. package/src/shared/conversation/streaming.js +7 -4
  114. package/src/shared/index.js +2 -0
  115. package/src/shared/nativeProviders.js +45 -0
  116. package/src/testing/conversationStorageContract.js +31 -1
  117. package/src/testing/nativeCodex.js +11 -0
  118. package/src/testing/nativeOpenCode.js +3 -0
  119. package/test/aiConnectionClient.test.js +2 -0
  120. package/test/apiConversationDriver.test.js +73 -0
  121. package/test/assistantApiSurfaceHeader.test.js +85 -0
  122. package/test/assistantBoundedToolLoop.test.js +285 -0
  123. package/test/assistantScroll.browser.test.js +16 -0
  124. package/test/claudeClient.test.js +189 -0
  125. package/test/claudeConversationSession.test.js +1173 -0
  126. package/test/claudeInstructionResume.test.js +82 -0
  127. package/test/claudeProcess.test.js +204 -0
  128. package/test/claudeTurn.test.js +253 -0
  129. package/test/codexConversationSession.test.js +2122 -0
  130. package/test/codexGoal.test.js +37 -0
  131. package/test/codexHelperDeletion.test.js +163 -0
  132. package/test/codexHelperThreadLedger.test.js +494 -0
  133. package/test/codexHistoryAdapter.test.js +541 -0
  134. package/test/codexInteractiveArguments.test.js +30 -0
  135. package/test/codexIsolation.test.js +756 -0
  136. package/test/codexModelCatalog.test.js +362 -0
  137. package/test/codexNativeHistoryExport.test.js +81 -0
  138. package/test/codexThreadSelection.test.js +136 -0
  139. package/test/codexTurn.test.js +2158 -0
  140. package/test/conversation.test.js +110 -0
  141. package/test/conversationAgentRun.test.js +326 -0
  142. package/test/conversationApiFacilities.test.js +90 -0
  143. package/test/conversationAttachments.test.js +238 -0
  144. package/test/conversationCommandWrapper.test.js +48 -0
  145. package/test/conversationConfiguration.test.js +174 -0
  146. package/test/conversationNativeStorage.test.js +25 -0
  147. package/test/conversationReplacement.test.js +172 -0
  148. package/test/conversationRuntime.test.js +241 -0
  149. package/test/conversationRuntimeStateUpgrade.test.js +144 -0
  150. package/test/conversationSession.test.js +1080 -0
  151. package/test/conversationStreaming.test.js +68 -0
  152. package/test/conversationSystemPrompt.test.js +84 -0
  153. package/test/conversationTools.test.js +416 -0
  154. package/test/fileConversationStorage.test.js +73 -0
  155. package/test/fixtures/codexHelperThreadLedger.js +39 -0
  156. package/test/localConversationExecution.test.js +90 -0
  157. package/test/messageDelivery.test.js +234 -1
  158. package/test/nativeConversationRetirement.test.js +215 -0
  159. package/test/openCodeClient.test.js +515 -0
  160. package/test/openCodeProcess.test.js +177 -0
  161. package/test/openCodeRuntime.test.js +322 -0
  162. package/test/openCodeTools.test.js +287 -0
  163. package/test/opencodeConversationSession.test.js +789 -0
  164. package/test/serviceToolCatalog.test.js +24 -0
@@ -0,0 +1,317 @@
1
+ import {
2
+ codexAppServerTurnStatusIsComplete,
3
+ codexAppServerTurnStatusIsProviderFailure,
4
+ codexAppServerThreadStatus,
5
+ codexAppServerReadThreadStatus,
6
+ normalizeCodexRunText,
7
+ codexAppServerTurnCanAdoptSuccessor,
8
+ codexAppServerInterruptFailure,
9
+ codexAppServerInterruptUnavailableResponse,
10
+ codexAppServerFrozenTurnInterruptResponse
11
+ } from "./codexTurnState.js";
12
+ import { codexAppServerRequestIsInvalid } from "./codexProvider.js";
13
+ import { codexAppServerThreadTurnId } from "./codexEvents.js";
14
+
15
+ // Original user Stop control, including preflight and bounded successor races.
16
+ // Existing journal, recovery and status owners retain their state.
17
+ export function createCodexTurnControl({
18
+ journal,
19
+ settlement,
20
+ recovery,
21
+ threadStatus,
22
+ debugError,
23
+ debugLog,
24
+ errorPrefix,
25
+ interruptFailedCode,
26
+ turnOutcomes,
27
+ turnState
28
+ }) {
29
+ const { turnResultWasProcessed: codexAppServerTurnResultWasProcessed } = journal;
30
+ const {
31
+ adoptSuccessorTurn: adoptCodexAppServerSuccessorTurn,
32
+ finalizeAssistantResult: finalizeCodexAppServerAssistantResult
33
+ } = settlement;
34
+ const {
35
+ stopTurnWithProviderFailure: stopCodexAppServerTurnWithProviderFailure,
36
+ completeTurn: completeCodexAppServerTurn
37
+ } = recovery;
38
+ const { reconcileThreadStatus: reconcileCodexAppServerThreadStatus } = threadStatus;
39
+
40
+ async function interruptCodexAppServerTurn(sessionId, input = {}, context = {}) {
41
+ const { runtime } = context;
42
+ const controlRequestId = normalizeCodexRunText(input?.controlRequestId);
43
+ let currentSession = await runtime.getSession(sessionId);
44
+ let currentTurn = turnState(currentSession);
45
+ let threadId = normalizeCodexRunText(currentTurn.threadId) ||
46
+ context.threadId(currentSession);
47
+ let provider = null;
48
+ let providerPreflight = null;
49
+ if (context.admissionError()) {
50
+ return { value: codexAppServerFrozenTurnInterruptResponse({
51
+ threadId,
52
+ turnId: currentTurn.turnId
53
+ }) };
54
+ }
55
+ if (currentTurn.status === "observation_lost" && currentTurn.active) {
56
+ const owner = context.observationOwner(threadId);
57
+ if (owner) {
58
+ await owner.failObservation(owner.observationFailure);
59
+ return { value: { ok: true, interrupted: true }, session: await runtime.getSession(sessionId) };
60
+ }
61
+ return { value: { ok: false, error: "Codex's stop could not be verified. Its runtime owner is unavailable." }, session: currentSession };
62
+ }
63
+ if (currentTurn.active && threadId) {
64
+ provider = await context.acquireProvider(currentSession);
65
+ try {
66
+ const providerThread = await codexAppServerReadThreadStatus(provider, threadId, {
67
+ observeLatestTurn: true
68
+ });
69
+ const providerStatus = codexAppServerThreadStatus(providerThread);
70
+ const providerTurnId = codexAppServerThreadTurnId(providerThread);
71
+ providerPreflight = {
72
+ status: providerStatus,
73
+ threadId,
74
+ turnId: providerTurnId || normalizeCodexRunText(currentTurn.turnId)
75
+ };
76
+ if (
77
+ providerTurnId &&
78
+ codexAppServerTurnCanAdoptSuccessor(currentTurn, threadId, providerTurnId)
79
+ ) {
80
+ await adoptCodexAppServerSuccessorTurn(sessionId, {
81
+ previousTurnId: currentTurn.turnId,
82
+ source: "interrupt_preflight",
83
+ status: "inProgress",
84
+ threadId,
85
+ turnId: providerTurnId
86
+ });
87
+ }
88
+ } catch (error) {
89
+ debugLog("appServerInterrupt.preflight.error", {
90
+ controlRequestId,
91
+ error: debugError(error),
92
+ sessionId,
93
+ threadId,
94
+ turnId: currentTurn.turnId
95
+ });
96
+ }
97
+ }
98
+
99
+ for (let attempt = 0; attempt < 3; attempt += 1) {
100
+ currentSession = await runtime.getSession(sessionId);
101
+ currentTurn = turnState(currentSession);
102
+ threadId = normalizeCodexRunText(currentTurn.threadId) ||
103
+ context.threadId(currentSession);
104
+ const turnId = normalizeCodexRunText(currentTurn.turnId);
105
+ if (context.admissionError()) {
106
+ return { value: codexAppServerFrozenTurnInterruptResponse({ threadId, turnId }) };
107
+ }
108
+ if (currentTurn.state === "finalizing" && threadId) {
109
+ if (codexAppServerTurnResultWasProcessed(currentSession, threadId, turnId)) {
110
+ await finalizeCodexAppServerAssistantResult(sessionId, threadId, turnId, {
111
+ status: currentTurn.status || "completed"
112
+ });
113
+ currentSession = await runtime.getSession(sessionId);
114
+ currentTurn = turnState(currentSession);
115
+ }
116
+ if (!currentTurn.active) {
117
+ return { value: {
118
+ interrupted: false,
119
+ ok: true,
120
+ operationOutcome: "already_idle",
121
+ threadId,
122
+ turnId
123
+ }, session: currentSession };
124
+ }
125
+ const stoppedThreadId = normalizeCodexRunText(currentTurn.threadId) || threadId;
126
+ const stoppedTurnId = normalizeCodexRunText(currentTurn.turnId) || turnId;
127
+ const stopped = await stopCodexAppServerTurnWithProviderFailure(
128
+ sessionId,
129
+ stoppedThreadId,
130
+ stoppedTurnId,
131
+ {
132
+ error: "Stopped by user.",
133
+ ok: true,
134
+ outcome: turnOutcomes.USER_CANCELLED,
135
+ status: "interrupted",
136
+ verifyInactive: false
137
+ }
138
+ );
139
+ return { value: {
140
+ ...stopped,
141
+ operationOutcome: "interrupted",
142
+ threadId: stoppedThreadId,
143
+ turnId: stoppedTurnId
144
+ } };
145
+ }
146
+ if (!currentTurn.active || !threadId || !turnId) {
147
+ debugLog("appServerInterrupt.unavailable", {
148
+ active: currentTurn.active,
149
+ controlRequestId,
150
+ sessionId,
151
+ threadId,
152
+ turnId
153
+ });
154
+ if (currentTurn.active) {
155
+ return { value: codexAppServerInterruptUnavailableResponse({
156
+ active: true,
157
+ threadId,
158
+ turnId
159
+ }, interruptFailedCode) };
160
+ }
161
+ return { value: {
162
+ interrupted: false,
163
+ ok: true,
164
+ operationOutcome: "already_idle",
165
+ threadId,
166
+ turnId
167
+ }, session: currentSession };
168
+ }
169
+ if (!provider) {
170
+ if (context.admissionError()) {
171
+ return { value: codexAppServerFrozenTurnInterruptResponse({ threadId, turnId }) };
172
+ }
173
+ provider = await context.acquireProvider(currentSession);
174
+ }
175
+ debugLog("appServerInterrupt.start", {
176
+ attempt,
177
+ controlRequestId,
178
+ sessionId,
179
+ threadId,
180
+ turnId
181
+ });
182
+ let result;
183
+ let requestError = null;
184
+ if (context.admissionError()) {
185
+ return { value: codexAppServerFrozenTurnInterruptResponse({ threadId, turnId }) };
186
+ }
187
+ try {
188
+ result = await provider.interruptTurn(threadId, turnId);
189
+ } catch (error) {
190
+ if (error?.code === `${provider?.errorPrefix ?? errorPrefix}codex_command_stop_unconfirmed`) throw error;
191
+ requestError = error;
192
+ }
193
+ const interruptFailure = requestError
194
+ ? null
195
+ : codexAppServerInterruptFailure(result, interruptFailedCode);
196
+ if (requestError || interruptFailure) {
197
+ const preflightMatchesTurn = providerPreflight?.threadId === threadId &&
198
+ providerPreflight?.turnId === turnId;
199
+ if (
200
+ preflightMatchesTurn &&
201
+ codexAppServerTurnStatusIsComplete(providerPreflight.status)
202
+ ) {
203
+ if (codexAppServerTurnStatusIsProviderFailure(providerPreflight.status)) {
204
+ await stopCodexAppServerTurnWithProviderFailure(sessionId, threadId, turnId, {
205
+ status: providerPreflight.status,
206
+ verifyInactive: false
207
+ });
208
+ } else {
209
+ await completeCodexAppServerTurn(sessionId, threadId, turnId, {
210
+ status: providerPreflight.status,
211
+ verifyInactive: false
212
+ });
213
+ }
214
+ const settledSession = await runtime.getSession(sessionId);
215
+ return { value: {
216
+ interrupted: false,
217
+ ok: true,
218
+ operationOutcome: "already_idle",
219
+ threadId,
220
+ turnId
221
+ }, session: settledSession };
222
+ }
223
+ providerPreflight = null;
224
+ await reconcileCodexAppServerThreadStatus(sessionId, provider, threadId, {
225
+ source: "interrupt_race"
226
+ }).catch(() => null);
227
+ const reconciledSession = await runtime.getSession(sessionId);
228
+ const reconciledTurn = turnState(reconciledSession);
229
+ const successorIsActive = reconciledTurn.active === true &&
230
+ normalizeCodexRunText(reconciledTurn.threadId) === threadId &&
231
+ Boolean(normalizeCodexRunText(reconciledTurn.turnId)) &&
232
+ normalizeCodexRunText(reconciledTurn.turnId) !== turnId;
233
+ if (successorIsActive && attempt < 2) {
234
+ continue;
235
+ }
236
+ const sameTurnIsActive = reconciledTurn.state === "active" &&
237
+ reconciledTurn.active === true &&
238
+ normalizeCodexRunText(reconciledTurn.threadId) === threadId &&
239
+ normalizeCodexRunText(reconciledTurn.turnId) === turnId;
240
+ if (!sameTurnIsActive) {
241
+ debugLog("appServerInterrupt.alreadyIdle", {
242
+ controlRequestId,
243
+ sessionId,
244
+ threadId,
245
+ turnId
246
+ });
247
+ return { value: {
248
+ interrupted: false,
249
+ ok: true,
250
+ operationOutcome: "already_idle",
251
+ threadId,
252
+ turnId
253
+ }, session: reconciledSession };
254
+ }
255
+ if (requestError) {
256
+ if (codexAppServerRequestIsInvalid(requestError, "turn/interrupt")) {
257
+ return { value: codexAppServerInterruptUnavailableResponse({
258
+ active: true,
259
+ threadId,
260
+ turnId
261
+ }, interruptFailedCode) };
262
+ }
263
+ throw requestError;
264
+ }
265
+ debugLog("appServerInterrupt.failed", {
266
+ controlRequestId,
267
+ error: interruptFailure.error,
268
+ operationOutcome: interruptFailure.operationOutcome,
269
+ sessionId,
270
+ threadId,
271
+ turnId
272
+ });
273
+ return { value: {
274
+ ...interruptFailure,
275
+ result,
276
+ threadId,
277
+ turnId
278
+ } };
279
+ }
280
+ const stopped = await stopCodexAppServerTurnWithProviderFailure(sessionId, threadId, turnId, {
281
+ error: "Stopped by user.",
282
+ ok: true,
283
+ outcome: turnOutcomes.USER_CANCELLED,
284
+ status: "interrupted",
285
+ verifyInactive: false
286
+ });
287
+ if (stopped?.reason === "stale_turn_state" && attempt < 2) {
288
+ await reconcileCodexAppServerThreadStatus(sessionId, provider, threadId, {
289
+ source: "interrupt_settlement_race"
290
+ }).catch(() => null);
291
+ continue;
292
+ }
293
+ debugLog("appServerInterrupt.done", {
294
+ controlRequestId,
295
+ sessionId,
296
+ threadId,
297
+ turnId
298
+ });
299
+ return { value: {
300
+ ...stopped,
301
+ operationOutcome: "interrupted",
302
+ result,
303
+ threadId,
304
+ turnId
305
+ } };
306
+ }
307
+ currentSession = await runtime.getSession(sessionId);
308
+ currentTurn = turnState(currentSession);
309
+ return { value: codexAppServerInterruptUnavailableResponse({
310
+ active: currentTurn.active,
311
+ threadId: currentTurn.threadId,
312
+ turnId: currentTurn.turnId
313
+ }, interruptFailedCode) };
314
+ }
315
+
316
+ return { interruptTurn: interruptCodexAppServerTurn };
317
+ }