@jskit-ai/assistant-core 0.1.200 → 0.1.202

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,306 @@
1
+ import { deleteCodexAppServerThread } from "./codexProvider.js";
2
+ import { CODEX_HELPER_THREAD_LIFECYCLES } from "./codexHelperThreadLedger.js";
3
+
4
+ function normalizeText(value) {
5
+ return String(value || "").trim();
6
+ }
7
+
8
+ export function errorMessage(value, fallback = "Codex could not be prepared.") {
9
+ return normalizeText(value?.error || value?.message || value) || fallback;
10
+ }
11
+
12
+ export function createCodexHelperOwnershipError({ applicationName = "Application", errorPrefix = "" } = {}) {
13
+ return function codexAppServerHelperOwnershipError(message = "", details = {}) {
14
+ const error = new Error(
15
+ normalizeText(message) ||
16
+ `${applicationName} could not prove ownership of a persisted low-cost assistant thread.`
17
+ );
18
+ error.code = `${errorPrefix}codex_helper_ownership_blocked`;
19
+ error.statusCode = 409;
20
+ error.retryable = details.retryable !== false;
21
+ error.details = {
22
+ ...details,
23
+ retryable: error.retryable
24
+ };
25
+ return error;
26
+ };
27
+ }
28
+
29
+ export async function readCodexAppServerAccountIdentity(provider, codexAppServerHelperOwnershipError) {
30
+ if (typeof provider.currentRuntimeInfo !== "function") {
31
+ throw codexAppServerHelperOwnershipError(
32
+ "The Codex provider cannot identify the selected account."
33
+ );
34
+ }
35
+ const runtimeInfo = await provider.currentRuntimeInfo();
36
+ const accountIdentitySignature = normalizeText(runtimeInfo?.accountIdentitySignature);
37
+ if (!/^sha256:[a-f0-9]{64}$/u.test(accountIdentitySignature)) {
38
+ throw codexAppServerHelperOwnershipError(
39
+ "The Codex provider did not return a stable selected-account identity."
40
+ );
41
+ }
42
+ return accountIdentitySignature;
43
+ }
44
+
45
+ export async function assertCodexAppServerHelperAccountIdentity(provider, expectedSignature = "", codexAppServerHelperOwnershipError) {
46
+ const expected = normalizeText(expectedSignature);
47
+ if (!expected) {
48
+ return;
49
+ }
50
+ if (!/^sha256:[a-f0-9]{64}$/u.test(expected) || typeof provider?.currentRuntimeInfo !== "function") {
51
+ throw codexAppServerHelperOwnershipError(
52
+ "The requested low-cost assistant account identity is invalid. Refresh the task and retry."
53
+ );
54
+ }
55
+ const actual = normalizeText((await provider.currentRuntimeInfo())?.accountIdentitySignature);
56
+ if (actual !== expected) {
57
+ throw codexAppServerHelperOwnershipError(
58
+ "The selected Codex account changed before low-cost assistant work could run. Retry the task with the current account."
59
+ );
60
+ }
61
+ }
62
+
63
+ // Advanced host facility: applications prepare their Helper policy; this shared
64
+ // operation owns native execution-context lookup and the actual dispatch.
65
+ export async function sendPreparedCodexAppServerHelperTurn(provider, prepared, helperIsolation) {
66
+ const turn = await provider.sendTurn(
67
+ prepared.threadId,
68
+ prepared.input,
69
+ prepared.turnSettings((await helperIsolation.executionContext(provider)).cwd)
70
+ );
71
+ return Object.freeze({ executionProfile: prepared.executionProfile, input: prepared.input, turn });
72
+ }
73
+
74
+ // One invocation retains its original mutable record while the shared ownership
75
+ // facet retains all durable state, locks and cross-operation coordination.
76
+ export async function prepareDetachedCodexHelperThread(owner, scope, preparation, {
77
+ applicationName = "Application"
78
+ } = {}) {
79
+ const {
80
+ withProjectOperation: withCodexAppServerHelperProjectOperation,
81
+ remember: rememberCodexAppServerHelperThread,
82
+ update: updateCodexAppServerHelperThread,
83
+ remove: removeCodexAppServerHelperThread,
84
+ retire: retireCodexAppServerHelperThread,
85
+ ownershipError: codexAppServerHelperOwnershipError,
86
+ cleanupError: codexAppServerHelperThreadCleanupError,
87
+ key: codexAppServerHelperThreadKey,
88
+ turnStarts: codexAppServerHelperTurnStarts
89
+ } = owner;
90
+ const {
91
+ executionProfile, onRetired, projectRuntimeRoot, provider, sessionId,
92
+ requestedThreadId, workdir
93
+ } = scope;
94
+ let thread = null;
95
+ let helperThreadRecord = scope.record;
96
+
97
+ async function startAndRememberCodexAppServerHelperThread({
98
+ scope = {},
99
+ executionProfile = null,
100
+ onRetired = null,
101
+ projectRuntimeRoot = "",
102
+ provider = null,
103
+ sessionId = "",
104
+ workdir = ""
105
+ } = {}) {
106
+ return withCodexAppServerHelperProjectOperation(projectRuntimeRoot, async () => {
107
+ const projectContextRoot = normalizeText(scope.projectContextRoot);
108
+ let thread = null;
109
+ try {
110
+ const started = await preparation.isolation.start(provider, async () => {
111
+ const prepared = preparation.start();
112
+ const enforcement = await preparation.isolation.inspect(provider, prepared.inspection);
113
+ return prepared.prepared(enforcement);
114
+ });
115
+ thread = started.thread;
116
+ } catch (error) {
117
+ const failedThreadId = normalizeText(error?.codexAppServerHelperThreadId);
118
+ if (
119
+ error?.codexAppServerHelperThreadCleanupRequired === true &&
120
+ failedThreadId
121
+ ) {
122
+ await rememberCodexAppServerHelperThread({
123
+ executionProfile,
124
+ lifecycle: CODEX_HELPER_THREAD_LIFECYCLES.CLEANUP_REQUIRED,
125
+ projectContextRoot,
126
+ projectRuntimeRoot,
127
+ provider,
128
+ sessionId,
129
+ threadId: failedThreadId,
130
+ workdir
131
+ });
132
+ }
133
+ throw error;
134
+ }
135
+ const threadId = normalizeText(thread?.id || thread?.response?.thread?.id);
136
+ if (!threadId) {
137
+ throw new Error("Codex app-server did not return a helper thread id.");
138
+ }
139
+ try {
140
+ const record = await rememberCodexAppServerHelperThread({
141
+ executionProfile,
142
+ lifecycle: CODEX_HELPER_THREAD_LIFECYCLES.STARTING_TURN,
143
+ onRetired,
144
+ projectContextRoot,
145
+ projectRuntimeRoot,
146
+ provider,
147
+ sessionId,
148
+ threadId,
149
+ workdir
150
+ });
151
+ return { record, thread, threadId };
152
+ } catch (ledgerError) {
153
+ try {
154
+ await deleteCodexAppServerThread({ provider, threadId });
155
+ } catch (cleanupError) {
156
+ const failure = codexAppServerHelperOwnershipError(
157
+ `${applicationName} could not persist or retire a newly created low-cost assistant thread.`,
158
+ {
159
+ cleanupError: errorMessage(cleanupError),
160
+ ledgerError: errorMessage(ledgerError),
161
+ sessionId,
162
+ threadId
163
+ }
164
+ );
165
+ failure.cause = ledgerError;
166
+ throw failure;
167
+ }
168
+ throw ledgerError;
169
+ }
170
+ });
171
+ }
172
+
173
+ if (requestedThreadId) {
174
+ try {
175
+ helperThreadRecord = await updateCodexAppServerHelperThread(
176
+ helperThreadRecord,
177
+ {
178
+ lifecycle: CODEX_HELPER_THREAD_LIFECYCLES.STARTING_TURN,
179
+ onRetired,
180
+ turnId: ""
181
+ }
182
+ );
183
+ const resumed = await preparation.isolation.resume(provider, requestedThreadId, async () => {
184
+ const prepared = preparation.resume();
185
+ const enforcement = await preparation.isolation.inspect(provider, prepared.inspection);
186
+ return prepared.prepared(enforcement);
187
+ });
188
+ thread = resumed.thread;
189
+ } catch (error) {
190
+ if (error?.codexAppServerHelperThreadRetired === true) {
191
+ try {
192
+ await removeCodexAppServerHelperThread(helperThreadRecord);
193
+ } catch (ledgerError) {
194
+ throw codexAppServerHelperThreadCleanupError(
195
+ helperThreadRecord,
196
+ ledgerError
197
+ );
198
+ }
199
+ } else if (error?.codexAppServerHelperThreadCleanupRequired === true) {
200
+ try {
201
+ helperThreadRecord = await updateCodexAppServerHelperThread(
202
+ helperThreadRecord,
203
+ {
204
+ lifecycle: CODEX_HELPER_THREAD_LIFECYCLES.CLEANUP_REQUIRED
205
+ }
206
+ );
207
+ } catch (ledgerError) {
208
+ throw codexAppServerHelperThreadCleanupError(
209
+ helperThreadRecord,
210
+ ledgerError
211
+ );
212
+ }
213
+ } else {
214
+ try {
215
+ await retireCodexAppServerHelperThread(helperThreadRecord);
216
+ } catch (cleanupError) {
217
+ cleanupError.cause = error;
218
+ throw cleanupError;
219
+ }
220
+ }
221
+ throw error;
222
+ }
223
+ }
224
+ if (!thread) {
225
+ const started = await startAndRememberCodexAppServerHelperThread({
226
+ scope,
227
+ executionProfile,
228
+ onRetired,
229
+ projectRuntimeRoot,
230
+ provider,
231
+ sessionId,
232
+ workdir
233
+ });
234
+ helperThreadRecord = started.record;
235
+ thread = started.thread;
236
+ }
237
+
238
+ const discardHelperThread = async () => {
239
+ if (!helperThreadRecord) {
240
+ return;
241
+ }
242
+ await retireCodexAppServerHelperThread(helperThreadRecord);
243
+ };
244
+
245
+ async function dispatch(threadId, failDispatch) {
246
+ let delivery = null;
247
+ let turnId = "";
248
+ let status = "";
249
+ let helperTurnStart = null;
250
+ try {
251
+ const helperThreadKey = codexAppServerHelperThreadKey(helperThreadRecord);
252
+ helperTurnStart = (async () => {
253
+ const currentDelivery = await sendPreparedCodexAppServerHelperTurn(
254
+ provider, preparation.turn(threadId), preparation.isolation
255
+ );
256
+ const currentTurnId = normalizeText(currentDelivery.turn?.id);
257
+ const currentStatus = normalizeText(
258
+ currentDelivery.turn?.status || currentDelivery.turn?.raw?.status
259
+ );
260
+ helperThreadRecord = await updateCodexAppServerHelperThread(
261
+ helperThreadRecord,
262
+ {
263
+ lifecycle: CODEX_HELPER_THREAD_LIFECYCLES.ACTIVE,
264
+ turnId: currentTurnId
265
+ }
266
+ );
267
+ return {
268
+ delivery: currentDelivery,
269
+ status: currentStatus,
270
+ turnId: currentTurnId
271
+ };
272
+ })();
273
+ codexAppServerHelperTurnStarts.set(helperThreadKey, helperTurnStart);
274
+ ({ delivery, status, turnId } = await helperTurnStart);
275
+ } catch (error) {
276
+ await failDispatch(error);
277
+ } finally {
278
+ if (helperTurnStart) {
279
+ const helperThreadKey = codexAppServerHelperThreadKey(helperThreadRecord);
280
+ if (codexAppServerHelperTurnStarts.get(helperThreadKey) === helperTurnStart) {
281
+ codexAppServerHelperTurnStarts.delete(helperThreadKey);
282
+ }
283
+ }
284
+ }
285
+ return { delivery, status, turnId };
286
+ }
287
+
288
+ async function complete() {
289
+ try {
290
+ helperThreadRecord = await updateCodexAppServerHelperThread(helperThreadRecord, {
291
+ lifecycle: CODEX_HELPER_THREAD_LIFECYCLES.READY,
292
+ turnId: ""
293
+ });
294
+ } catch (error) {
295
+ try {
296
+ await retireCodexAppServerHelperThread(helperThreadRecord);
297
+ } catch (cleanupError) {
298
+ cleanupError.cause = error;
299
+ throw cleanupError;
300
+ }
301
+ throw error;
302
+ }
303
+ }
304
+
305
+ return Object.freeze({ thread, dispatch, discard: discardHelperThread, complete });
306
+ }