@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.
- package/examples/conversation/README.md +8 -1
- package/examples/conversation/package.json +5 -5
- package/fixtures/responsive-assistant/App.vue +4 -1
- package/package.json +99 -36
- package/src/client/conversation/AssistantConversationElement.vue +27 -4
- package/src/client/conversation/AssistantConversationStatus.vue +24 -0
- package/src/client/conversation/AssistantGoalControl.vue +8 -5
- package/src/client/conversation/AssistantTranscript.vue +10 -3
- package/src/client/conversation/index.js +2 -0
- package/src/client/conversation/messageDelivery.js +115 -32
- package/src/client/conversation/retainedConversation.js +32 -0
- package/src/client/conversation/useAssistantQuestions.js +76 -0
- package/src/client/lib/assistantApi.js +72 -2
- package/src/server/conversation/agentRun.js +222 -0
- package/src/server/conversation/attachments.js +93 -0
- package/src/server/conversation/claudeClient.js +221 -0
- package/src/server/conversation/claudeHistory.js +256 -0
- package/src/server/conversation/claudeProcess.js +327 -0
- package/src/server/conversation/claudeTools.js +42 -0
- package/src/server/conversation/claudeTurn.js +1082 -0
- package/src/server/conversation/codexConfiguration.js +1183 -0
- package/src/server/conversation/codexConversations.js +1082 -0
- package/src/server/conversation/codexDetachedTurn.js +332 -0
- package/src/server/conversation/codexEvents.js +194 -1
- package/src/server/conversation/codexGoalCommands.js +124 -0
- package/src/server/conversation/codexHelperExecution.js +306 -0
- package/src/server/conversation/codexHelperThreadLedger.js +804 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +395 -0
- package/src/server/conversation/codexHelperThreads.js +638 -0
- package/src/server/conversation/codexHistoryAdapter.js +369 -0
- package/src/server/conversation/codexMessageCommands.js +663 -0
- package/src/server/conversation/codexMessageDelivery.js +211 -0
- package/src/server/conversation/codexNativeHistoryExport.js +95 -0
- package/src/server/conversation/codexNotificationObserver.js +482 -0
- package/src/server/conversation/codexNotificationQueue.js +103 -0
- package/src/server/conversation/codexObservationControl.js +174 -0
- package/src/server/conversation/codexOutput.js +931 -0
- package/src/server/conversation/codexProcess.js +1559 -0
- package/src/server/conversation/codexProcessLeader.js +14 -0
- package/src/server/conversation/codexProvider.js +2710 -0
- package/src/server/conversation/codexProviderOwner.js +1058 -0
- package/src/server/conversation/codexProviderSelection.js +218 -0
- package/src/server/conversation/codexReasoning.js +314 -0
- package/src/server/conversation/codexRenewalCommands.js +290 -0
- package/src/server/conversation/codexResultSettlement.js +409 -0
- package/src/server/conversation/codexRunJournal.js +658 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +288 -0
- package/src/server/conversation/codexSessionCleanup.js +96 -0
- package/src/server/conversation/codexSessionReconciliation.js +284 -0
- package/src/server/conversation/codexThreadReadiness.js +397 -0
- package/src/server/conversation/codexThreadStatus.js +354 -0
- package/src/server/conversation/codexTurn.js +701 -255
- package/src/server/conversation/codexTurnControl.js +317 -0
- package/src/server/conversation/codexTurnRecovery.js +990 -0
- package/src/server/conversation/codexTurnState.js +549 -0
- package/src/server/conversation/commandHook.js +18 -0
- package/src/server/conversation/commandWrapper.js +37 -0
- package/src/server/conversation/configuration.js +43 -0
- package/src/server/conversation/continuity.js +289 -0
- package/src/server/conversation/fileStorage.js +55 -0
- package/src/server/conversation/hookBridge.js +19 -0
- package/src/server/conversation/index.js +13 -0
- package/src/server/conversation/localExecution.js +665 -0
- package/src/server/conversation/memoryStorage.js +7 -72
- package/src/server/conversation/nativeHistoryExport.js +90 -0
- package/src/server/conversation/nativeStorage.js +23 -0
- package/src/server/conversation/openCodeClient.js +209 -19
- package/src/server/conversation/openCodePlugin.js +84 -0
- package/src/server/conversation/openCodeProcess.js +476 -0
- package/src/server/conversation/openCodeRuntime.js +1578 -0
- package/src/server/conversation/openCodeTools.js +60 -0
- package/src/server/conversation/openCodeTurn.js +557 -0
- package/src/server/conversation/provider.js +30 -0
- package/src/server/conversation/providers/api.js +136 -0
- package/src/server/conversation/providers/claude.js +63 -0
- package/src/server/conversation/providers/claudeDriver.js +425 -0
- package/src/server/conversation/providers/codex.js +248 -0
- package/src/server/conversation/providers/codexDriver.js +1078 -0
- package/src/server/conversation/providers/opencode.js +49 -0
- package/src/server/conversation/providers/opencodeDriver.js +650 -0
- package/src/server/conversation/providers/scoped.js +312 -0
- package/src/server/conversation/runtime.js +2220 -0
- package/src/server/conversation/runtimeStateUpgrade.js +125 -0
- package/src/server/conversation/storage.js +184 -0
- package/src/server/conversation/streams.js +7 -2
- package/src/server/conversation/structuredOutput.js +240 -0
- package/src/server/conversation/systemPrompt.js +54 -0
- package/src/server/conversation/tools.js +84 -0
- package/src/server/conversation/transcript.js +5 -4
- package/src/server/hosts/claudeHistory.js +5 -0
- package/src/server/hosts/claudeProcess.js +11 -0
- package/src/server/hosts/claudeTurn.js +4 -0
- package/src/server/hosts/codexConfiguration.js +20 -0
- package/src/server/hosts/codexEvents.js +13 -0
- package/src/server/hosts/codexProcess.js +12 -0
- package/src/server/hosts/codexProvider.js +23 -0
- package/src/server/hosts/codexTurn.js +20 -0
- package/src/server/hosts/nativeHistory.js +3 -0
- package/src/server/hosts/openCodeClient.js +3 -0
- package/src/server/hosts/openCodeProcess.js +23 -0
- package/src/server/hosts/openCodeTurn.js +7 -0
- package/src/server/index.js +2 -0
- package/src/server/lib/aiConnectionClient.js +13 -6
- package/src/server/lib/assistantCompletion.js +190 -0
- package/src/server/lib/assistantToolLoop.js +314 -0
- package/src/server/lib/serviceToolCatalog.js +25 -10
- package/src/shared/assistantResponseText.js +29 -0
- package/src/shared/conversation/attachments.js +19 -0
- package/src/shared/conversation/index.js +3 -0
- package/src/shared/conversation/pagination.js +78 -0
- package/src/shared/conversation/patches.js +150 -0
- package/src/shared/conversation/questions.js +48 -0
- package/src/shared/conversation/streaming.js +7 -4
- package/src/shared/index.js +2 -0
- package/src/shared/nativeProviders.js +45 -0
- package/src/testing/conversationStorageContract.js +31 -1
- package/src/testing/nativeCodex.js +11 -0
- package/src/testing/nativeOpenCode.js +3 -0
- package/test/aiConnectionClient.test.js +2 -0
- package/test/apiConversationDriver.test.js +73 -0
- package/test/assistantApiSurfaceHeader.test.js +85 -0
- package/test/assistantBoundedToolLoop.test.js +285 -0
- package/test/assistantScroll.browser.test.js +16 -0
- package/test/claudeClient.test.js +189 -0
- package/test/claudeConversationSession.test.js +1173 -0
- package/test/claudeInstructionResume.test.js +82 -0
- package/test/claudeProcess.test.js +204 -0
- package/test/claudeTurn.test.js +253 -0
- package/test/codexConversationSession.test.js +2122 -0
- package/test/codexGoal.test.js +37 -0
- package/test/codexHelperDeletion.test.js +163 -0
- package/test/codexHelperThreadLedger.test.js +494 -0
- package/test/codexHistoryAdapter.test.js +541 -0
- package/test/codexInteractiveArguments.test.js +30 -0
- package/test/codexIsolation.test.js +756 -0
- package/test/codexModelCatalog.test.js +362 -0
- package/test/codexNativeHistoryExport.test.js +81 -0
- package/test/codexThreadSelection.test.js +136 -0
- package/test/codexTurn.test.js +2158 -0
- package/test/conversation.test.js +110 -0
- package/test/conversationAgentRun.test.js +326 -0
- package/test/conversationApiFacilities.test.js +90 -0
- package/test/conversationAttachments.test.js +238 -0
- package/test/conversationCommandWrapper.test.js +48 -0
- package/test/conversationConfiguration.test.js +174 -0
- package/test/conversationNativeStorage.test.js +25 -0
- package/test/conversationReplacement.test.js +172 -0
- package/test/conversationRuntime.test.js +241 -0
- package/test/conversationRuntimeStateUpgrade.test.js +144 -0
- package/test/conversationSession.test.js +1080 -0
- package/test/conversationStreaming.test.js +68 -0
- package/test/conversationSystemPrompt.test.js +84 -0
- package/test/conversationTools.test.js +416 -0
- package/test/fileConversationStorage.test.js +73 -0
- package/test/fixtures/codexHelperThreadLedger.js +39 -0
- package/test/localConversationExecution.test.js +90 -0
- package/test/messageDelivery.test.js +234 -1
- package/test/nativeConversationRetirement.test.js +215 -0
- package/test/openCodeClient.test.js +515 -0
- package/test/openCodeProcess.test.js +177 -0
- package/test/openCodeRuntime.test.js +322 -0
- package/test/openCodeTools.test.js +287 -0
- package/test/opencodeConversationSession.test.js +789 -0
- package/test/serviceToolCatalog.test.js +24 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { codexAppServerTurnStatusIsSuccessfulComplete, codexAppServerTurnStatusIsProviderFailure, normalizeCodexRunText, codexAppServerErrorMessage } from "./codexTurnState.js";
|
|
2
|
+
import { createCodexAppServerDetachedTurnWatcher, waitForCodexAppServerTurn } from "./codexDetachedTurn.js";
|
|
3
|
+
import { resumeExactCodexAppServerThread, startFreshCodexAppServerThread, sendCodexAppServerPrompt } from "./codexProvider.js";
|
|
4
|
+
import { codexAppServerProviderTurnId, codexAppServerProviderTurnError, inspectCodexAppServerRenewalThread } from "./codexEvents.js";
|
|
5
|
+
|
|
6
|
+
// Private composition of unchanged operations from the original run owner.
|
|
7
|
+
export function createCodexRenewalCommands({
|
|
8
|
+
claimCodexAppServerTurnStart,
|
|
9
|
+
codexAppServerNotificationQueue,
|
|
10
|
+
completeCodexAppServerTurn,
|
|
11
|
+
errorPrefix,
|
|
12
|
+
markCodexAppServerTurnActive,
|
|
13
|
+
markCodexAppServerTurnIdle,
|
|
14
|
+
observer,
|
|
15
|
+
selection,
|
|
16
|
+
turnAlreadyRunningError,
|
|
17
|
+
writeCodexAppServerUserMessageOwnership
|
|
18
|
+
}) {
|
|
19
|
+
async function runCodexAppServerRenewalHandover(sessionId, input = {}, context = {}) {
|
|
20
|
+
const { clientMessageId, operationId, timeoutMs } = input;
|
|
21
|
+
const { runtime, provider, errors } = context;
|
|
22
|
+
const resumed = await resumeExactCodexAppServerThread(context.threadPreparation);
|
|
23
|
+
const threadId = resumed.threadId;
|
|
24
|
+
const expectedTurnId = context.readExpectedTurnId(threadId);
|
|
25
|
+
const { targetTurn, failure: snapshotFailure } = inspectCodexAppServerRenewalThread(resumed.threadSnapshot, {
|
|
26
|
+
clientMessageId,
|
|
27
|
+
turnId: expectedTurnId
|
|
28
|
+
});
|
|
29
|
+
if (snapshotFailure === "turn_missing") {
|
|
30
|
+
throw errors.withIdentity(
|
|
31
|
+
errors.create("handover_turn_missing"),
|
|
32
|
+
{ clientMessageId, operationId, threadId, turnId: expectedTurnId }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (snapshotFailure === "history_missing") {
|
|
36
|
+
throw errors.withIdentity(
|
|
37
|
+
errors.create("handover_history_missing"),
|
|
38
|
+
{ clientMessageId, operationId, threadId }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
observer.subscribeEvents(sessionId, provider, threadId,
|
|
43
|
+
selection.codexAppServerSessionObserverOptions(sessionId, context.providerOptions));
|
|
44
|
+
selection.rememberCodexAppServerPreparedThread(sessionId, context.providerOptions, context, resumed);
|
|
45
|
+
|
|
46
|
+
let result = null;
|
|
47
|
+
let reconciled = Boolean(targetTurn);
|
|
48
|
+
let turnId = codexAppServerProviderTurnId(targetTurn || {});
|
|
49
|
+
if (targetTurn) {
|
|
50
|
+
try {
|
|
51
|
+
result = await waitForCodexAppServerTurn(
|
|
52
|
+
provider,
|
|
53
|
+
threadId,
|
|
54
|
+
targetTurn,
|
|
55
|
+
{ timeoutMs, createError: errors.create }
|
|
56
|
+
);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw errors.withIdentity(error, {
|
|
59
|
+
clientMessageId,
|
|
60
|
+
operationId,
|
|
61
|
+
threadId,
|
|
62
|
+
turnId
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
const claim = await claimCodexAppServerTurnStart(
|
|
67
|
+
runtime,
|
|
68
|
+
sessionId,
|
|
69
|
+
clientMessageId,
|
|
70
|
+
{ inputSource: "session_renewal_handover" }
|
|
71
|
+
);
|
|
72
|
+
if (!claim?.claimed) {
|
|
73
|
+
return { response: claim?.response || {
|
|
74
|
+
code: `${errorPrefix}agent_turn_already_running`,
|
|
75
|
+
error: turnAlreadyRunningError,
|
|
76
|
+
ok: false
|
|
77
|
+
} };
|
|
78
|
+
}
|
|
79
|
+
// The exact old thread is already known before prompt delivery. Bind
|
|
80
|
+
// it to the durable claim now, so an immediate provider response can
|
|
81
|
+
// supply the turn id and complete the run even when its notifications
|
|
82
|
+
// arrive before sendTurn() returns.
|
|
83
|
+
await markCodexAppServerTurnActive(sessionId, {
|
|
84
|
+
status: "starting",
|
|
85
|
+
threadId
|
|
86
|
+
});
|
|
87
|
+
await context.writeMetadata({
|
|
88
|
+
clientMessageId,
|
|
89
|
+
operationId,
|
|
90
|
+
threadId
|
|
91
|
+
});
|
|
92
|
+
await writeCodexAppServerUserMessageOwnership(
|
|
93
|
+
runtime.store,
|
|
94
|
+
sessionId,
|
|
95
|
+
clientMessageId,
|
|
96
|
+
{
|
|
97
|
+
eventKind: "codex-app-server-renewal-handover-owned",
|
|
98
|
+
owned: true
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
const watcher = createCodexAppServerDetachedTurnWatcher(provider, threadId, {
|
|
102
|
+
timeoutMs
|
|
103
|
+
});
|
|
104
|
+
const pending = watcher.wait();
|
|
105
|
+
void pending.catch(() => null);
|
|
106
|
+
let delivery = null;
|
|
107
|
+
let status = "";
|
|
108
|
+
try {
|
|
109
|
+
delivery = await sendCodexAppServerPrompt({
|
|
110
|
+
clientUserMessageId: clientMessageId,
|
|
111
|
+
prompt: input.prompt,
|
|
112
|
+
provider,
|
|
113
|
+
threadId
|
|
114
|
+
}, context);
|
|
115
|
+
turnId = normalizeCodexRunText(delivery.turn?.id);
|
|
116
|
+
status = normalizeCodexRunText(delivery.turn?.status || delivery.turn?.raw?.status);
|
|
117
|
+
if (!turnId) {
|
|
118
|
+
throw errors.create("handover_identity_missing");
|
|
119
|
+
}
|
|
120
|
+
await context.writeMetadata({ turnId });
|
|
121
|
+
await markCodexAppServerTurnActive(sessionId, {
|
|
122
|
+
requireTrackedTurn: true,
|
|
123
|
+
status: "inProgress",
|
|
124
|
+
threadId,
|
|
125
|
+
turnId
|
|
126
|
+
});
|
|
127
|
+
watcher.setTurnId(turnId);
|
|
128
|
+
if (codexAppServerTurnStatusIsProviderFailure(status)) {
|
|
129
|
+
watcher.failAfterDetailGrace(errors.create(
|
|
130
|
+
"failed",
|
|
131
|
+
{ status },
|
|
132
|
+
codexAppServerProviderTurnError(delivery.turn || {})
|
|
133
|
+
));
|
|
134
|
+
} else if (codexAppServerTurnStatusIsSuccessfulComplete(status)) {
|
|
135
|
+
await watcher.completeNow(status);
|
|
136
|
+
}
|
|
137
|
+
result = await pending;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
watcher.failNow(error);
|
|
140
|
+
await writeCodexAppServerUserMessageOwnership(
|
|
141
|
+
runtime.store,
|
|
142
|
+
sessionId,
|
|
143
|
+
clientMessageId,
|
|
144
|
+
{
|
|
145
|
+
eventKind: "codex-app-server-renewal-handover-released",
|
|
146
|
+
owned: false
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
await markCodexAppServerTurnIdle(sessionId, {
|
|
150
|
+
error: codexAppServerErrorMessage(error, "Codex handover generation failed."),
|
|
151
|
+
status: "failed",
|
|
152
|
+
threadId,
|
|
153
|
+
turnId
|
|
154
|
+
});
|
|
155
|
+
throw errors.withIdentity(error, {
|
|
156
|
+
clientMessageId,
|
|
157
|
+
operationId,
|
|
158
|
+
threadId,
|
|
159
|
+
turnId
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await codexAppServerNotificationQueue.drain(sessionId);
|
|
165
|
+
if (codexAppServerTurnStatusIsSuccessfulComplete(result.status)) {
|
|
166
|
+
// A reconciled turn may already be complete before this process
|
|
167
|
+
// subscribes, so no completion notification is guaranteed. Route it
|
|
168
|
+
// through the ordinary finalizer as well; its turn identity guards
|
|
169
|
+
// make the live-notification case an idempotent no-op.
|
|
170
|
+
await completeCodexAppServerTurn(
|
|
171
|
+
sessionId,
|
|
172
|
+
threadId,
|
|
173
|
+
result.turnId || turnId,
|
|
174
|
+
{
|
|
175
|
+
provider,
|
|
176
|
+
status: result.status,
|
|
177
|
+
verifyInactive: false
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return { result, reconciled, turnId, threadId };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function runCodexAppServerRenewalSeed(input = {}, context = {}) {
|
|
185
|
+
const { clientMessageId, operationId, timeoutMs } = input;
|
|
186
|
+
const { provider, errors } = context;
|
|
187
|
+
const started = await startFreshCodexAppServerThread(context.threadPreparation);
|
|
188
|
+
const threadId = started.threadId;
|
|
189
|
+
const expectedTurnId = context.readExpectedTurnId(threadId);
|
|
190
|
+
const { targetTurn, failure: snapshotFailure } = inspectCodexAppServerRenewalThread(started.threadSnapshot, {
|
|
191
|
+
clientMessageId,
|
|
192
|
+
turnId: expectedTurnId,
|
|
193
|
+
requireFresh: true
|
|
194
|
+
});
|
|
195
|
+
if (snapshotFailure === "turn_missing") {
|
|
196
|
+
throw errors.withIdentity(
|
|
197
|
+
errors.create("seed_turn_missing"),
|
|
198
|
+
{ clientMessageId, operationId, threadId, turnId: expectedTurnId }
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
if (snapshotFailure === "unrelated_history") {
|
|
202
|
+
throw errors.withIdentity(
|
|
203
|
+
errors.create("seed_unrelated_history"),
|
|
204
|
+
{
|
|
205
|
+
clientMessageId,
|
|
206
|
+
operationId,
|
|
207
|
+
threadId,
|
|
208
|
+
turnId: codexAppServerProviderTurnId(targetTurn || {})
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let result = null;
|
|
214
|
+
let turnId = codexAppServerProviderTurnId(targetTurn || {});
|
|
215
|
+
const reconciled = Boolean(targetTurn);
|
|
216
|
+
if (targetTurn) {
|
|
217
|
+
try {
|
|
218
|
+
result = await waitForCodexAppServerTurn(provider, threadId, targetTurn, {
|
|
219
|
+
timeoutMs, createError: errors.create
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
throw errors.withIdentity(error, {
|
|
223
|
+
clientMessageId,
|
|
224
|
+
handoverPromptAccepted: true,
|
|
225
|
+
operationId,
|
|
226
|
+
threadId,
|
|
227
|
+
turnId
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
await context.writeMetadata({
|
|
232
|
+
clientMessageId,
|
|
233
|
+
operationId,
|
|
234
|
+
threadId
|
|
235
|
+
});
|
|
236
|
+
const watcher = createCodexAppServerDetachedTurnWatcher(provider, threadId, {
|
|
237
|
+
timeoutMs
|
|
238
|
+
});
|
|
239
|
+
const pending = watcher.wait();
|
|
240
|
+
void pending.catch(() => null);
|
|
241
|
+
let delivery = null;
|
|
242
|
+
let handoverPromptAccepted = false;
|
|
243
|
+
let status = "";
|
|
244
|
+
try {
|
|
245
|
+
delivery = await sendCodexAppServerPrompt({
|
|
246
|
+
clientUserMessageId: clientMessageId,
|
|
247
|
+
outputSchema: input.outputSchema,
|
|
248
|
+
prompt: input.prompt,
|
|
249
|
+
provider,
|
|
250
|
+
readOnly: true,
|
|
251
|
+
threadId
|
|
252
|
+
}, context);
|
|
253
|
+
turnId = normalizeCodexRunText(delivery.turn?.id);
|
|
254
|
+
status = normalizeCodexRunText(delivery.turn?.status || delivery.turn?.raw?.status);
|
|
255
|
+
if (!turnId) {
|
|
256
|
+
throw errors.create("seed_identity_missing");
|
|
257
|
+
}
|
|
258
|
+
handoverPromptAccepted = true;
|
|
259
|
+
await context.writeMetadata({ turnId });
|
|
260
|
+
watcher.setTurnId(turnId);
|
|
261
|
+
if (codexAppServerTurnStatusIsProviderFailure(status)) {
|
|
262
|
+
watcher.failAfterDetailGrace(errors.create(
|
|
263
|
+
"failed",
|
|
264
|
+
{ status },
|
|
265
|
+
codexAppServerProviderTurnError(delivery.turn || {})
|
|
266
|
+
));
|
|
267
|
+
} else if (codexAppServerTurnStatusIsSuccessfulComplete(status)) {
|
|
268
|
+
await watcher.completeNow(status);
|
|
269
|
+
}
|
|
270
|
+
result = await pending;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
watcher.failNow(error);
|
|
273
|
+
throw errors.withIdentity(error, {
|
|
274
|
+
clientMessageId,
|
|
275
|
+
handoverPromptAccepted,
|
|
276
|
+
operationId,
|
|
277
|
+
threadId,
|
|
278
|
+
turnId
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return { result, reconciled, turnId, threadId, freshThread: started.fresh };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
runCodexAppServerRenewalHandover,
|
|
288
|
+
runCodexAppServerRenewalSeed
|
|
289
|
+
};
|
|
290
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeCodexRunText,
|
|
3
|
+
CODEX_APP_SERVER_AGENT_RUN_ID,
|
|
4
|
+
CODEX_APP_SERVER_RESULT_PROCESSED_EVENT,
|
|
5
|
+
codexAppServerTurnCanReceiveProviderCompletion,
|
|
6
|
+
codexAppServerProcessedResultEvent,
|
|
7
|
+
codexAppServerTurnAwaitsProviderIdentity,
|
|
8
|
+
codexAppServerTurnCanAdoptSuccessor,
|
|
9
|
+
codexAppServerAgentRun,
|
|
10
|
+
CODEX_APP_SERVER_RUN_STATE,
|
|
11
|
+
codexAppServerTurnKey
|
|
12
|
+
} from "./codexTurnState.js";
|
|
13
|
+
|
|
14
|
+
// Original result settlement and successor adoption for one retained run owner.
|
|
15
|
+
// Journal/output collections remain with their owners and are shared by reference.
|
|
16
|
+
export function createCodexResultSettlement({ journal, output, createRuntime, publish, debugLog, debugError, turnState }) {
|
|
17
|
+
const {
|
|
18
|
+
processedTurns: codexAppServerProcessedTurns,
|
|
19
|
+
completedTurns: codexAppServerCompletedTurns,
|
|
20
|
+
agentRunPatch,
|
|
21
|
+
markTurnIdle: markCodexAppServerTurnIdle,
|
|
22
|
+
turnResultWasProcessed: codexAppServerTurnResultWasProcessed,
|
|
23
|
+
turnWasCompleted: codexAppServerTurnWasCompleted,
|
|
24
|
+
runInputSource: codexAppServerRunInputSource,
|
|
25
|
+
clearFinalizingTimer: clearCodexAppServerFinalizingTimer,
|
|
26
|
+
agentRunRealtimePayload: codexAppServerAgentRunRealtimePayload,
|
|
27
|
+
clearActiveTimer: clearCodexAppServerActiveTimer
|
|
28
|
+
} = journal;
|
|
29
|
+
const {
|
|
30
|
+
finalizedTurns: codexAppServerFinalizedTurns,
|
|
31
|
+
resultFinalizationKey: codexAppServerResultFinalizationKey,
|
|
32
|
+
cleanupUntrackedTurn: cleanupCodexAppServerUntrackedTurn,
|
|
33
|
+
submitAssistantResult: submitCodexAppServerAssistantResult,
|
|
34
|
+
flushReasoningPersist: flushCodexAppServerReasoningPersist,
|
|
35
|
+
readFinalAssistantResult: readCodexAppServerFinalAssistantResult,
|
|
36
|
+
persistAssistantReply: persistCodexAppServerAssistantReply
|
|
37
|
+
} = output;
|
|
38
|
+
const codexAppServerResultFinalizations = new Map();
|
|
39
|
+
|
|
40
|
+
async function recordCodexAppServerProcessedResult(runtime, sessionId = "", threadId = "", turnId = "", result = {}) {
|
|
41
|
+
await runtime.store.writeAgentRunEvent(sessionId, CODEX_APP_SERVER_AGENT_RUN_ID, {
|
|
42
|
+
event: {
|
|
43
|
+
kind: CODEX_APP_SERVER_RESULT_PROCESSED_EVENT,
|
|
44
|
+
providerThreadId: normalizeCodexRunText(threadId),
|
|
45
|
+
providerTurnId: normalizeCodexRunText(turnId),
|
|
46
|
+
resultReason: normalizeCodexRunText(result.reason)
|
|
47
|
+
},
|
|
48
|
+
patch: {}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function settleCodexAppServerProcessedTurn(sessionId = "", threadId = "", turnId = "", {
|
|
53
|
+
result = {
|
|
54
|
+
ok: true,
|
|
55
|
+
processed: true,
|
|
56
|
+
reason: "already_processed"
|
|
57
|
+
},
|
|
58
|
+
status = "completed"
|
|
59
|
+
} = {}) {
|
|
60
|
+
const normalizedSessionId = normalizeCodexRunText(sessionId);
|
|
61
|
+
const normalizedThreadId = normalizeCodexRunText(threadId);
|
|
62
|
+
const normalizedTurnId = normalizeCodexRunText(turnId);
|
|
63
|
+
const key = codexAppServerResultFinalizationKey(normalizedSessionId, normalizedThreadId, normalizedTurnId);
|
|
64
|
+
const runtime = await createRuntime();
|
|
65
|
+
const session = await runtime.getSession(normalizedSessionId);
|
|
66
|
+
const turn = turnState(session);
|
|
67
|
+
if (!codexAppServerTurnCanReceiveProviderCompletion(turn, normalizedThreadId, normalizedTurnId)) {
|
|
68
|
+
codexAppServerProcessedTurns.delete(key);
|
|
69
|
+
codexAppServerFinalizedTurns.add(key);
|
|
70
|
+
cleanupCodexAppServerUntrackedTurn(normalizedThreadId, normalizedTurnId);
|
|
71
|
+
debugLog("appServerAgentResult.processedSettlementStale", {
|
|
72
|
+
currentState: turn.state,
|
|
73
|
+
currentStatus: turn.status,
|
|
74
|
+
currentThreadId: turn.threadId,
|
|
75
|
+
currentTurnId: turn.turnId,
|
|
76
|
+
sessionId: normalizedSessionId,
|
|
77
|
+
threadId: normalizedThreadId,
|
|
78
|
+
turnId: normalizedTurnId
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
...result,
|
|
82
|
+
reason: "stale_turn_state"
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
await markCodexAppServerTurnIdle(normalizedSessionId, {
|
|
86
|
+
status,
|
|
87
|
+
threadId: normalizedThreadId,
|
|
88
|
+
turnId: normalizedTurnId
|
|
89
|
+
});
|
|
90
|
+
codexAppServerProcessedTurns.delete(key);
|
|
91
|
+
codexAppServerFinalizedTurns.add(key);
|
|
92
|
+
cleanupCodexAppServerUntrackedTurn(normalizedThreadId, normalizedTurnId);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function finalizeCodexAppServerAssistantResult(sessionId = "", threadId = "", turnId = "", {
|
|
97
|
+
recoverFromProvider = false,
|
|
98
|
+
status = "completed"
|
|
99
|
+
} = {}) {
|
|
100
|
+
const normalizedSessionId = normalizeCodexRunText(sessionId);
|
|
101
|
+
const normalizedThreadId = normalizeCodexRunText(threadId);
|
|
102
|
+
const normalizedTurnId = normalizeCodexRunText(turnId);
|
|
103
|
+
const key = codexAppServerResultFinalizationKey(normalizedSessionId, normalizedThreadId, normalizedTurnId);
|
|
104
|
+
if (!normalizedSessionId || !normalizedThreadId || !normalizedTurnId) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
processed: false,
|
|
108
|
+
reason: "missing_turn"
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (codexAppServerProcessedTurns.has(key)) {
|
|
112
|
+
return settleCodexAppServerProcessedTurn(
|
|
113
|
+
normalizedSessionId,
|
|
114
|
+
normalizedThreadId,
|
|
115
|
+
normalizedTurnId,
|
|
116
|
+
{
|
|
117
|
+
status
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (codexAppServerFinalizedTurns.has(key)) {
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
processed: true,
|
|
125
|
+
reason: "already_finalized"
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const existing = codexAppServerResultFinalizations.get(key);
|
|
129
|
+
if (existing) {
|
|
130
|
+
return existing;
|
|
131
|
+
}
|
|
132
|
+
const operation = (async () => {
|
|
133
|
+
const runtime = await createRuntime();
|
|
134
|
+
const session = await runtime.getSession(normalizedSessionId);
|
|
135
|
+
const turn = turnState(session);
|
|
136
|
+
const processedEvent = codexAppServerProcessedResultEvent(
|
|
137
|
+
session,
|
|
138
|
+
normalizedThreadId,
|
|
139
|
+
normalizedTurnId
|
|
140
|
+
);
|
|
141
|
+
if (processedEvent) {
|
|
142
|
+
codexAppServerProcessedTurns.add(key);
|
|
143
|
+
return settleCodexAppServerProcessedTurn(
|
|
144
|
+
normalizedSessionId,
|
|
145
|
+
normalizedThreadId,
|
|
146
|
+
normalizedTurnId,
|
|
147
|
+
{
|
|
148
|
+
result: {
|
|
149
|
+
ok: true,
|
|
150
|
+
processed: true,
|
|
151
|
+
reason: normalizeCodexRunText(processedEvent.resultReason) || "already_processed"
|
|
152
|
+
},
|
|
153
|
+
status
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
if (codexAppServerTurnAwaitsProviderIdentity(turn, normalizedThreadId, normalizedTurnId)) {
|
|
158
|
+
return {
|
|
159
|
+
ok: true,
|
|
160
|
+
processed: false,
|
|
161
|
+
reason: "turn_identity_pending"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (!codexAppServerTurnCanReceiveProviderCompletion(turn, normalizedThreadId, normalizedTurnId)) {
|
|
165
|
+
codexAppServerFinalizedTurns.add(key);
|
|
166
|
+
cleanupCodexAppServerUntrackedTurn(normalizedThreadId, normalizedTurnId);
|
|
167
|
+
debugLog("appServerAgentResult.stale", {
|
|
168
|
+
currentState: turn.state,
|
|
169
|
+
currentStatus: turn.status,
|
|
170
|
+
currentThreadId: turn.threadId,
|
|
171
|
+
currentTurnId: turn.turnId,
|
|
172
|
+
sessionId: normalizedSessionId,
|
|
173
|
+
threadId: normalizedThreadId,
|
|
174
|
+
turnId: normalizedTurnId
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
ok: true,
|
|
178
|
+
processed: false,
|
|
179
|
+
reason: "stale_turn_state"
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const result = await submitCodexAppServerAssistantResult(
|
|
183
|
+
normalizedSessionId,
|
|
184
|
+
normalizedThreadId,
|
|
185
|
+
normalizedTurnId,
|
|
186
|
+
{
|
|
187
|
+
recoverFromProvider
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
if (result?.processed) {
|
|
191
|
+
codexAppServerProcessedTurns.add(key);
|
|
192
|
+
await recordCodexAppServerProcessedResult(
|
|
193
|
+
runtime,
|
|
194
|
+
normalizedSessionId,
|
|
195
|
+
normalizedThreadId,
|
|
196
|
+
normalizedTurnId,
|
|
197
|
+
result
|
|
198
|
+
);
|
|
199
|
+
return settleCodexAppServerProcessedTurn(
|
|
200
|
+
normalizedSessionId,
|
|
201
|
+
normalizedThreadId,
|
|
202
|
+
normalizedTurnId,
|
|
203
|
+
{
|
|
204
|
+
result,
|
|
205
|
+
status
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
})().finally(() => {
|
|
211
|
+
codexAppServerResultFinalizations.delete(key);
|
|
212
|
+
});
|
|
213
|
+
codexAppServerResultFinalizations.set(key, operation);
|
|
214
|
+
return operation;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function recoverCodexAppServerFinalResponseBeforeOutcome(
|
|
218
|
+
sessionId = "",
|
|
219
|
+
threadId = "",
|
|
220
|
+
turnId = "",
|
|
221
|
+
status = ""
|
|
222
|
+
) {
|
|
223
|
+
const result = await finalizeCodexAppServerAssistantResult(
|
|
224
|
+
sessionId,
|
|
225
|
+
threadId,
|
|
226
|
+
turnId,
|
|
227
|
+
{
|
|
228
|
+
recoverFromProvider: true,
|
|
229
|
+
status
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
if (result?.processed !== true) {
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
if (result.reason !== "already_finalized") {
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
const runtime = await createRuntime();
|
|
239
|
+
const session = await runtime.getSession(sessionId);
|
|
240
|
+
return codexAppServerTurnResultWasProcessed(session, threadId, turnId)
|
|
241
|
+
? result
|
|
242
|
+
: {
|
|
243
|
+
...result,
|
|
244
|
+
processed: false,
|
|
245
|
+
reason: "already_finalized_without_response"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function adoptCodexAppServerSuccessorTurn(sessionId = "", input = {}) {
|
|
250
|
+
const normalizedSessionId = normalizeCodexRunText(sessionId);
|
|
251
|
+
const normalizedThreadId = normalizeCodexRunText(input.threadId);
|
|
252
|
+
const normalizedTurnId = normalizeCodexRunText(input.turnId);
|
|
253
|
+
const expectedPreviousTurnId = normalizeCodexRunText(input.previousTurnId);
|
|
254
|
+
if (!normalizedSessionId || !normalizedThreadId || !normalizedTurnId || !expectedPreviousTurnId) {
|
|
255
|
+
return {
|
|
256
|
+
ok: true,
|
|
257
|
+
processed: false,
|
|
258
|
+
reason: "missing_successor_identity"
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const runtime = await createRuntime();
|
|
262
|
+
let previousTurnId = "";
|
|
263
|
+
let runPatch = null;
|
|
264
|
+
let outcome = {
|
|
265
|
+
ok: true,
|
|
266
|
+
processed: false,
|
|
267
|
+
reason: "turn_changed"
|
|
268
|
+
};
|
|
269
|
+
const updatedSession = await runtime.store.mutateSession(normalizedSessionId, async () => {
|
|
270
|
+
const currentSession = await runtime.getSession(normalizedSessionId);
|
|
271
|
+
const currentTurn = turnState(currentSession);
|
|
272
|
+
if (
|
|
273
|
+
normalizeCodexRunText(currentTurn.threadId) === normalizedThreadId &&
|
|
274
|
+
normalizeCodexRunText(currentTurn.turnId) === normalizedTurnId
|
|
275
|
+
) {
|
|
276
|
+
outcome = {
|
|
277
|
+
ok: true,
|
|
278
|
+
processed: false,
|
|
279
|
+
reason: "already_current"
|
|
280
|
+
};
|
|
281
|
+
return currentSession;
|
|
282
|
+
}
|
|
283
|
+
if (codexAppServerTurnWasCompleted(currentSession, normalizedThreadId, normalizedTurnId)) {
|
|
284
|
+
outcome = {
|
|
285
|
+
ok: true,
|
|
286
|
+
processed: false,
|
|
287
|
+
reason: "completed_turn"
|
|
288
|
+
};
|
|
289
|
+
return currentSession;
|
|
290
|
+
}
|
|
291
|
+
if (
|
|
292
|
+
normalizeCodexRunText(currentTurn.turnId) !== expectedPreviousTurnId ||
|
|
293
|
+
!codexAppServerTurnCanAdoptSuccessor(currentTurn, normalizedThreadId, normalizedTurnId)
|
|
294
|
+
) {
|
|
295
|
+
outcome = {
|
|
296
|
+
ok: true,
|
|
297
|
+
processed: false,
|
|
298
|
+
reason: "turn_changed"
|
|
299
|
+
};
|
|
300
|
+
return currentSession;
|
|
301
|
+
}
|
|
302
|
+
previousTurnId = normalizeCodexRunText(currentTurn.turnId);
|
|
303
|
+
const updatedAt = new Date().toISOString();
|
|
304
|
+
runPatch = agentRunPatch({
|
|
305
|
+
runState: CODEX_APP_SERVER_RUN_STATE.ACTIVE,
|
|
306
|
+
session: currentSession,
|
|
307
|
+
status: normalizeCodexRunText(input.status) || "inProgress",
|
|
308
|
+
threadId: normalizedThreadId,
|
|
309
|
+
turnId: normalizedTurnId,
|
|
310
|
+
updatedAt
|
|
311
|
+
});
|
|
312
|
+
const currentInputSource = codexAppServerRunInputSource(codexAppServerAgentRun(currentSession));
|
|
313
|
+
if (currentInputSource) {
|
|
314
|
+
runPatch.inputSource = currentInputSource;
|
|
315
|
+
}
|
|
316
|
+
runPatch.finishedAt = "";
|
|
317
|
+
await runtime.store.writeAgentRunEvent(normalizedSessionId, CODEX_APP_SERVER_AGENT_RUN_ID, {
|
|
318
|
+
event: {
|
|
319
|
+
kind: "codex-app-server-turn-continued",
|
|
320
|
+
message: "",
|
|
321
|
+
previousProviderTurnId: previousTurnId,
|
|
322
|
+
providerThreadId: normalizedThreadId,
|
|
323
|
+
providerTurnId: normalizedTurnId,
|
|
324
|
+
source: normalizeCodexRunText(input.source),
|
|
325
|
+
state: runPatch.state
|
|
326
|
+
},
|
|
327
|
+
patch: runPatch
|
|
328
|
+
});
|
|
329
|
+
outcome = {
|
|
330
|
+
ok: true,
|
|
331
|
+
previousTurnId,
|
|
332
|
+
processed: true,
|
|
333
|
+
reason: "successor_turn_adopted",
|
|
334
|
+
threadId: normalizedThreadId,
|
|
335
|
+
turnId: normalizedTurnId
|
|
336
|
+
};
|
|
337
|
+
return runtime.getSession(normalizedSessionId);
|
|
338
|
+
});
|
|
339
|
+
if (!outcome.processed) {
|
|
340
|
+
return outcome;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
clearCodexAppServerFinalizingTimer(
|
|
344
|
+
normalizedSessionId,
|
|
345
|
+
normalizedThreadId,
|
|
346
|
+
previousTurnId
|
|
347
|
+
);
|
|
348
|
+
codexAppServerCompletedTurns.add(codexAppServerTurnKey(
|
|
349
|
+
normalizedThreadId,
|
|
350
|
+
previousTurnId
|
|
351
|
+
));
|
|
352
|
+
const previousResultKey = codexAppServerResultFinalizationKey(
|
|
353
|
+
normalizedSessionId,
|
|
354
|
+
normalizedThreadId,
|
|
355
|
+
previousTurnId
|
|
356
|
+
);
|
|
357
|
+
codexAppServerProcessedTurns.delete(previousResultKey);
|
|
358
|
+
codexAppServerFinalizedTurns.add(previousResultKey);
|
|
359
|
+
try {
|
|
360
|
+
await flushCodexAppServerReasoningPersist(
|
|
361
|
+
normalizedSessionId,
|
|
362
|
+
normalizedThreadId,
|
|
363
|
+
previousTurnId
|
|
364
|
+
);
|
|
365
|
+
const previousAssistantResult = readCodexAppServerFinalAssistantResult(
|
|
366
|
+
normalizedSessionId,
|
|
367
|
+
normalizedThreadId,
|
|
368
|
+
previousTurnId
|
|
369
|
+
);
|
|
370
|
+
if (previousAssistantResult?.text) {
|
|
371
|
+
await persistCodexAppServerAssistantReply(
|
|
372
|
+
runtime,
|
|
373
|
+
normalizedSessionId,
|
|
374
|
+
previousAssistantResult
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
} catch (error) {
|
|
378
|
+
debugLog("appServerTurn.continuedOutput.error", {
|
|
379
|
+
error: debugError(error),
|
|
380
|
+
previousTurnId,
|
|
381
|
+
sessionId: normalizedSessionId,
|
|
382
|
+
threadId: normalizedThreadId,
|
|
383
|
+
turnId: normalizedTurnId
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
cleanupCodexAppServerUntrackedTurn(normalizedThreadId, previousTurnId);
|
|
387
|
+
await publish(normalizedSessionId, {
|
|
388
|
+
payload: codexAppServerAgentRunRealtimePayload(runPatch),
|
|
389
|
+
reason: "codex-app-server-turn-active",
|
|
390
|
+
session: updatedSession
|
|
391
|
+
});
|
|
392
|
+
debugLog("appServerTurn.continued", {
|
|
393
|
+
previousTurnId,
|
|
394
|
+
sessionId: normalizedSessionId,
|
|
395
|
+
source: normalizeCodexRunText(input.source),
|
|
396
|
+
threadId: normalizedThreadId,
|
|
397
|
+
turnId: normalizedTurnId
|
|
398
|
+
});
|
|
399
|
+
clearCodexAppServerActiveTimer(normalizedSessionId);
|
|
400
|
+
return outcome;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
resultFinalizations: codexAppServerResultFinalizations,
|
|
405
|
+
finalizeAssistantResult: finalizeCodexAppServerAssistantResult,
|
|
406
|
+
recoverFinalResponseBeforeOutcome: recoverCodexAppServerFinalResponseBeforeOutcome,
|
|
407
|
+
adoptSuccessorTurn: adoptCodexAppServerSuccessorTurn
|
|
408
|
+
};
|
|
409
|
+
}
|