@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.
- package/examples/conversation/package.json +5 -5
- package/package.json +18 -7
- package/src/client/browser.js +21 -0
- package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
- package/src/client/conversation/AssistantComposerActions.vue +1 -1
- package/src/client/conversation/AssistantConversationElement.vue +14 -52
- package/src/client/conversation/AssistantGoalControl.vue +13 -10
- package/src/client/conversation/AssistantPromptInput.vue +6 -91
- package/src/client/conversation/AssistantTranscript.css +505 -0
- package/src/client/conversation/AssistantTranscript.vue +24 -1197
- package/src/client/conversation/attachmentFiles.js +158 -0
- package/src/client/conversation/attachmentQueueItems.js +121 -0
- package/src/client/conversation/attachmentUploadRows.js +265 -0
- package/src/client/conversation/conversationDisplay.js +54 -0
- package/src/client/conversation/messageDelivery.js +13 -9
- package/src/client/conversation/retainedConversation.js +14 -3
- package/src/client/conversation/submitText.js +13 -2
- package/src/client/conversation/transcriptEntries.js +225 -0
- package/src/client/conversation/useAssistantAttachments.js +86 -388
- package/src/client/conversation/useAssistantQuestions.js +16 -3
- package/src/client/conversation/useAssistantSuggestions.js +16 -2
- package/src/client/conversation/useScrollToBottom.js +17 -19
- package/src/client/conversation/useTextareaAutoGrow.js +115 -0
- package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
- package/src/client/lib/assistantApi.js +192 -161
- package/src/client/types.js +110 -0
- package/src/server/conversation/attachments.js +21 -6
- package/src/server/conversation/boundedToolLoop.js +1 -0
- package/src/server/conversation/claudeClient.js +71 -20
- package/src/server/conversation/claudeCommandRun.js +191 -0
- package/src/server/conversation/claudeHistory.js +7 -0
- package/src/server/conversation/claudeNativeTurn.js +708 -0
- package/src/server/conversation/claudeProcess.js +186 -112
- package/src/server/conversation/claudeProcessStart.js +214 -0
- package/src/server/conversation/claudePromptDelivery.js +183 -0
- package/src/server/conversation/claudeSessionCleanup.js +248 -0
- package/src/server/conversation/claudeTools.js +15 -3
- package/src/server/conversation/claudeTurn.js +212 -1388
- package/src/server/conversation/codexAccountReader.js +168 -0
- package/src/server/conversation/codexAuthPreflight.js +5 -4
- package/src/server/conversation/codexClient.js +18 -6
- package/src/server/conversation/codexConfiguration.js +8 -844
- package/src/server/conversation/codexConversationStops.js +377 -0
- package/src/server/conversation/codexConversationStore.js +119 -85
- package/src/server/conversation/codexConversationTurns.js +378 -0
- package/src/server/conversation/codexConversations.js +134 -866
- package/src/server/conversation/codexDetachedConversation.js +203 -0
- package/src/server/conversation/codexDetachedTurn.js +145 -104
- package/src/server/conversation/codexEvents.js +5 -200
- package/src/server/conversation/codexGoalCommands.js +14 -4
- package/src/server/conversation/codexHelperExecution.js +57 -57
- package/src/server/conversation/codexHelperThreadLedger.js +30 -288
- package/src/server/conversation/codexHelperThreadRecord.js +304 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
- package/src/server/conversation/codexHelperThreads.js +94 -93
- package/src/server/conversation/codexHistoryAdapter.js +13 -8
- package/src/server/conversation/codexIsolation.js +672 -0
- package/src/server/conversation/codexLiveItems.js +379 -0
- package/src/server/conversation/codexMessageCommands.js +68 -286
- package/src/server/conversation/codexMessageDelivery.js +46 -37
- package/src/server/conversation/codexModelCatalog.js +86 -7
- package/src/server/conversation/codexNotificationObserver.js +81 -88
- package/src/server/conversation/codexNotificationQueue.js +21 -10
- package/src/server/conversation/codexObservationControl.js +105 -89
- package/src/server/conversation/codexOutput.js +84 -419
- package/src/server/conversation/codexProcess.js +29 -1053
- package/src/server/conversation/codexProcessLeader.js +1 -0
- package/src/server/conversation/codexPromptDispatch.js +241 -0
- package/src/server/conversation/codexProvider.js +147 -1807
- package/src/server/conversation/codexProviderAuth.js +348 -0
- package/src/server/conversation/codexProviderInvalidation.js +371 -0
- package/src/server/conversation/codexProviderInventories.js +437 -0
- package/src/server/conversation/codexProviderOwner.js +149 -957
- package/src/server/conversation/codexProviderRelease.js +465 -0
- package/src/server/conversation/codexProviderSelection.js +26 -15
- package/src/server/conversation/codexProviderThreads.js +698 -0
- package/src/server/conversation/codexProviderTurns.js +453 -0
- package/src/server/conversation/codexReasoning.js +32 -23
- package/src/server/conversation/codexRenewalCommands.js +18 -6
- package/src/server/conversation/codexRenewalThreads.js +7 -22
- package/src/server/conversation/codexResultSettlement.js +70 -88
- package/src/server/conversation/codexRunJournal.js +91 -97
- package/src/server/conversation/codexRuntime.js +426 -0
- package/src/server/conversation/codexRuntimeInventory.js +113 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
- package/src/server/conversation/codexRuntimeStart.js +431 -0
- package/src/server/conversation/codexRuntimeStop.js +324 -0
- package/src/server/conversation/codexSessionCleanup.js +25 -10
- package/src/server/conversation/codexSessionReconciliation.js +21 -7
- package/src/server/conversation/codexThreadReadiness.js +49 -52
- package/src/server/conversation/codexThreadSnapshot.js +210 -0
- package/src/server/conversation/codexThreadStatus.js +48 -62
- package/src/server/conversation/codexTurn.js +171 -55
- package/src/server/conversation/codexTurnActivity.js +210 -0
- package/src/server/conversation/codexTurnControl.js +44 -48
- package/src/server/conversation/codexTurnEndings.js +610 -0
- package/src/server/conversation/codexTurnRecovery.js +97 -848
- package/src/server/conversation/codexTurnState.js +138 -111
- package/src/server/conversation/completedResponses.js +227 -0
- package/src/server/conversation/configuration.js +2 -2
- package/src/server/conversation/continuity.js +43 -6
- package/src/server/conversation/conversationDelivery.js +394 -0
- package/src/server/conversation/conversationEntries.js +365 -0
- package/src/server/conversation/conversationEvents.js +192 -0
- package/src/server/conversation/conversationGoals.js +187 -0
- package/src/server/conversation/conversationHandles.js +237 -0
- package/src/server/conversation/conversationReads.js +206 -0
- package/src/server/conversation/conversationRefusals.js +58 -0
- package/src/server/conversation/conversationReplacement.js +254 -0
- package/src/server/conversation/conversationSending.js +273 -0
- package/src/server/conversation/conversationStopping.js +121 -0
- package/src/server/conversation/conversationTurnDelivery.js +413 -0
- package/src/server/conversation/conversationTurns.js +511 -0
- package/src/server/conversation/deliveryTiming.js +25 -3
- package/src/server/conversation/fileStorage.js +11 -6
- package/src/server/conversation/hookBridge.js +14 -3
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/localExecution.js +82 -625
- package/src/server/conversation/nativeCommandCallbacks.js +10 -0
- package/src/server/conversation/nativeConversationLifecycle.js +379 -0
- package/src/server/conversation/nativeHistoryExport.js +27 -18
- package/src/server/conversation/normalize.js +9 -0
- package/src/server/conversation/openCodeClient.js +295 -366
- package/src/server/conversation/openCodeNativeHistory.js +141 -0
- package/src/server/conversation/openCodeNativeSession.js +79 -0
- package/src/server/conversation/openCodeProcess.js +109 -53
- package/src/server/conversation/openCodeProcessOwner.js +565 -0
- package/src/server/conversation/openCodeRuntime.js +111 -1814
- package/src/server/conversation/openCodeSessionTurns.js +547 -0
- package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
- package/src/server/conversation/openCodeTools.js +28 -14
- package/src/server/conversation/openCodeTurn.js +80 -74
- package/src/server/conversation/openCodeTurnMonitor.js +388 -0
- package/src/server/conversation/processIdentity.js +582 -0
- package/src/server/conversation/provider.js +21 -6
- package/src/server/conversation/providers/api.js +268 -107
- package/src/server/conversation/providers/claude.js +29 -6
- package/src/server/conversation/providers/claudeDriver.js +325 -250
- package/src/server/conversation/providers/codex.js +69 -33
- package/src/server/conversation/providers/codexDriver.js +503 -1081
- package/src/server/conversation/providers/codexDriverConnection.js +331 -0
- package/src/server/conversation/providers/codexDriverTurns.js +464 -0
- package/src/server/conversation/providers/opencode.js +30 -4
- package/src/server/conversation/providers/opencodeDriver.js +321 -659
- package/src/server/conversation/providers/opencodeStandalone.js +475 -0
- package/src/server/conversation/providers/scoped.js +64 -19
- package/src/server/conversation/runtime.js +171 -2863
- package/src/server/conversation/storage.js +91 -50
- package/src/server/conversation/streams.js +15 -2
- package/src/server/conversation/structuredOutput.js +7 -11
- package/src/server/conversation/systemPrompt.js +13 -1
- package/src/server/conversation/tools.js +47 -15
- package/src/server/conversation/transcript.js +49 -20
- package/src/server/hosts/claudeTurn.js +2 -4
- package/src/server/hosts/codexConfiguration.js +6 -8
- package/src/server/hosts/codexEvents.js +4 -2
- package/src/server/hosts/codexProcess.js +1 -1
- package/src/server/hosts/codexProvider.js +11 -9
- package/src/server/hosts/codexTurn.js +3 -3
- package/src/server/hosts/openCodeClient.js +4 -2
- package/src/server/index.js +0 -2
- package/src/server/lib/aiConnectionClient.js +35 -25
- package/src/server/lib/ndjson.js +1 -0
- package/src/server/lib/providers/anthropicClient.js +15 -6
- package/src/server/lib/providers/common.js +10 -6
- package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
- package/src/server/lib/serviceToolCatalog.js +29 -283
- package/src/server/lib/serviceToolEntries.js +270 -0
- package/src/server/types.js +78 -0
- package/src/shared/assistantResource.js +0 -23
- package/src/shared/conversation/answerChoiceSugar.js +2 -14
- package/src/shared/conversation/messageInputSugar.js +18 -0
- package/src/shared/conversation/numberedQuestionSugar.js +6 -16
- package/src/shared/conversation/pagination.js +1 -1
- package/src/shared/conversation/questions.js +1 -0
- package/src/shared/conversation/streaming.js +1 -0
- package/src/shared/index.js +0 -1
- package/src/testing/conversationStorageContract.js +20 -0
- package/test/aiConnectionClient.test.js +42 -1
- package/test/apiConversationDriver.test.js +217 -38
- package/test/assistantBoundedToolLoop.test.js +1 -1
- package/test/claudeConversationSession.test.js +15 -11
- package/test/claudeInstructionResume.test.js +1 -1
- package/test/claudeProcess.test.js +9 -9
- package/test/claudeTurn.test.js +21 -8
- package/test/codexAccountChange.test.js +5 -3
- package/test/codexConversationSession.test.js +22 -21
- package/test/codexEvents.test.js +3 -1
- package/test/codexHelperDeletion.test.js +3 -7
- package/test/codexIsolation.test.js +6 -6
- package/test/codexModelCatalog.test.js +6 -7
- package/test/codexProcess.test.js +3 -3
- package/test/codexThreadSelection.test.js +4 -1
- package/test/codexTurn.test.js +29 -20
- package/test/conversationApiFacilities.test.js +11 -9
- package/test/conversationAttachments.test.js +3 -3
- package/test/conversationConfiguration.test.js +4 -4
- package/test/conversationNativeStorage.test.js +11 -10
- package/test/conversationReplacement.test.js +13 -13
- package/test/conversationRuntime.test.js +9 -5
- package/test/conversationSession.test.js +78 -22
- package/test/conversationTools.test.js +47 -26
- package/test/fileConversationStorage.test.js +10 -3
- package/test/fixtures/codexHelperThreadLedger.js +3 -3
- package/test/frameworkActions.test.js +33 -18
- package/test/localConversationExecution.test.js +4 -4
- package/test/localExecution.test.js +1 -1
- package/test/openCodeConnectionChange.test.js +3 -2
- package/test/openCodeRuntime.test.js +8 -7
- package/test/openCodeTools.test.js +1 -1
- package/test/opencodeConversationSession.test.js +4 -3
- package/test/serviceToolCatalog.test.js +3 -8
- package/src/server/lib/assistantCompletion.js +0 -190
- package/src/server/lib/assistantToolLoop.js +0 -259
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { codexAppServerErrorMessage, codexAppServerInterruptFailure, codexAppServerConversationTurnIsActive } from "./codexTurnState.js";
|
|
2
|
+
import { codexAppServerRequestIsInvalid, codexAppServerThreadHasReadableHistory, codexAppServerThreadIsMissing } from "./codexProviderThreads.js";
|
|
3
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Stopping, interrupting, deleting and closing native conversations. The conversation registry, its observation
|
|
7
|
+
* and context acquisition belong to the conversation commands, which pass them in.
|
|
8
|
+
* @param {object} dependencies
|
|
9
|
+
* @param {import("./codexConversations.js").CodexConversationRegistry} dependencies.codexAppServerConversations
|
|
10
|
+
* @param {import("./codexConversations.js").CodexConversationLookup} dependencies.codexAppServerConversation
|
|
11
|
+
* @param {import("./codexConversations.js").CodexConversationContextReader} dependencies.codexAppServerConversationContext
|
|
12
|
+
* @param {import("./codexConversations.js").CodexConversationResult} dependencies.codexAppServerConversationResult
|
|
13
|
+
* @param {import("./codexConversations.js").CodexProviderBorrow} dependencies.withProviderBorrow
|
|
14
|
+
* @param {import("./codexConversations.js").CodexConversationStateUpdate} dependencies.forgetObservation
|
|
15
|
+
* @param {import("./codexConversations.js").CodexConversationStateUpdate} dependencies.holdWhileActive
|
|
16
|
+
* @param {object} dependencies.conversationPreparation The application's conversation preparation.
|
|
17
|
+
* @param {import("./codexHelperThreads.js").CodexHelperThreadLifecycle} dependencies.helperLifecycle
|
|
18
|
+
* @param {import("./codexTurn.js").CodexProviderSessions} dependencies.providerSessions
|
|
19
|
+
* @param {import("./codexTurn.js").CodexSessionNamespace} dependencies.namespace
|
|
20
|
+
* @param {string} dependencies.errorPrefix
|
|
21
|
+
* @param {string} dependencies.interruptFailedCode
|
|
22
|
+
* @param {import("./codexTurn.js").CodexTurnOutcomes} dependencies.turnOutcomes
|
|
23
|
+
*/
|
|
24
|
+
export function createCodexConversationStops({
|
|
25
|
+
codexAppServerConversations,
|
|
26
|
+
codexAppServerConversation,
|
|
27
|
+
codexAppServerConversationContext,
|
|
28
|
+
codexAppServerConversationResult,
|
|
29
|
+
withProviderBorrow,
|
|
30
|
+
forgetObservation,
|
|
31
|
+
holdWhileActive,
|
|
32
|
+
conversationPreparation,
|
|
33
|
+
helperLifecycle,
|
|
34
|
+
providerSessions,
|
|
35
|
+
namespace,
|
|
36
|
+
errorPrefix,
|
|
37
|
+
interruptFailedCode,
|
|
38
|
+
turnOutcomes
|
|
39
|
+
}) {
|
|
40
|
+
// A conversation whose provider lost its observation stops through that failure.
|
|
41
|
+
async function stopThroughObservationFailure(state) {
|
|
42
|
+
await state.provider.failObservation(state.provider.observationFailure);
|
|
43
|
+
if (codexAppServerConversationTurnIsActive(state.status)) {
|
|
44
|
+
throw new Error("Codex's stop is not yet confirmed.");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function stopCodexAppServerConversation(sessionId, input = {}, options = {}, {
|
|
49
|
+
turnOutcome = turnOutcomes.USER_CANCELLED
|
|
50
|
+
} = {}) {
|
|
51
|
+
const conversationId = normalizeText(input.conversationId);
|
|
52
|
+
const conversationState = codexAppServerConversation(sessionId, conversationId);
|
|
53
|
+
if (conversationState?.provider.observationFailure) {
|
|
54
|
+
await stopThroughObservationFailure(conversationState);
|
|
55
|
+
return { conversationId, ok: true, runId: conversationState.runId, status: "interrupted" };
|
|
56
|
+
}
|
|
57
|
+
if (input.ephemeral === true && !conversationState) {
|
|
58
|
+
return {
|
|
59
|
+
conversationExpired: true,
|
|
60
|
+
conversationId,
|
|
61
|
+
ok: true,
|
|
62
|
+
runId: normalizeText(input.runId),
|
|
63
|
+
status: "interrupted"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (input.persistent) {
|
|
67
|
+
return withProviderBorrow(true, async (borrow) => {
|
|
68
|
+
const context = await codexAppServerConversationContext(sessionId, input, options, borrow);
|
|
69
|
+
if (context.ok === false) return context;
|
|
70
|
+
return stopPersistentCodexConversation({
|
|
71
|
+
conversationId,
|
|
72
|
+
conversationState,
|
|
73
|
+
provider: context.provider,
|
|
74
|
+
turnOutcome
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const result = await interruptDetachedCodexAppServerConversationTurn(sessionId, {
|
|
79
|
+
agentSettings: input.agentSettings,
|
|
80
|
+
threadId: input.conversationId,
|
|
81
|
+
turnId: input.runId
|
|
82
|
+
}, options, conversationState);
|
|
83
|
+
return {
|
|
84
|
+
...result,
|
|
85
|
+
conversationId,
|
|
86
|
+
runId: normalizeText(input.runId)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function stopPersistentCodexConversation({
|
|
91
|
+
conversationId,
|
|
92
|
+
conversationState,
|
|
93
|
+
provider,
|
|
94
|
+
turnOutcome = ""
|
|
95
|
+
} = {}) {
|
|
96
|
+
// Set before stopping: the provider can report turn/completed while it stops.
|
|
97
|
+
if (conversationState && codexAppServerConversationTurnIsActive(conversationState.status)) {
|
|
98
|
+
conversationState.turnOutcome = turnOutcome;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
await provider.readThreadStatus(conversationId);
|
|
102
|
+
await provider.stopThreadForObservationLoss(conversationId, "");
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (!codexAppServerThreadIsMissing(error, conversationId) || error.message.toLowerCase().startsWith("thread not loaded:")) {
|
|
105
|
+
if (conversationState) conversationState.turnOutcome = "";
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (conversationState) {
|
|
110
|
+
conversationState.status = "interrupted";
|
|
111
|
+
if (conversationState.goal?.status === "active") conversationState.goal = { ...conversationState.goal, status: "paused" };
|
|
112
|
+
conversationState.watcher?.failNow(new Error("Temporary AI turn was stopped."));
|
|
113
|
+
conversationState.watcher = null;
|
|
114
|
+
holdWhileActive(conversationState);
|
|
115
|
+
}
|
|
116
|
+
return { ok: true, conversationId, status: "interrupted" };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function interruptDetachedCodexAppServerConversationTurn(sessionId, input = {}, options = {}, conversationState = null) {
|
|
120
|
+
return interruptCodexAppServerConversation(conversationState, dispatch =>
|
|
121
|
+
codexAppServerConversationControl(sessionId, input, options, "interrupt", dispatch));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function interruptCodexAppServerConversation(conversationState, operation) {
|
|
125
|
+
const result = await operation(async ({ provider, threadId, turnId }) => {
|
|
126
|
+
const result = await provider.interruptTurn(threadId, turnId);
|
|
127
|
+
const interruptFailure = codexAppServerInterruptFailure(result, interruptFailedCode);
|
|
128
|
+
if (interruptFailure) {
|
|
129
|
+
return {
|
|
130
|
+
...interruptFailure,
|
|
131
|
+
result,
|
|
132
|
+
threadId,
|
|
133
|
+
turnId
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
ok: true,
|
|
138
|
+
result,
|
|
139
|
+
status: "interrupted",
|
|
140
|
+
threadId,
|
|
141
|
+
turnId
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
if (result.ok !== false && conversationState) {
|
|
145
|
+
conversationState.status = "interrupted";
|
|
146
|
+
conversationState.watcher?.failNow(new Error("Temporary AI turn was stopped."));
|
|
147
|
+
conversationState.watcher = null;
|
|
148
|
+
holdWhileActive(conversationState);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function deleteScopedCodexAppServerConversation(sessionId, input = {}, options = {}) {
|
|
154
|
+
const conversationId = normalizeText(input.conversationId);
|
|
155
|
+
const sessionKey = namespace(sessionId);
|
|
156
|
+
const conversations = codexAppServerConversations.get(sessionKey);
|
|
157
|
+
// An ephemeral native thread lives only in the process that created it, so
|
|
158
|
+
// one this server no longer tracks has nothing left to delete.
|
|
159
|
+
const conversationExpired = input.ephemeral === true && !conversations?.has(conversationId);
|
|
160
|
+
let result;
|
|
161
|
+
if (conversationExpired) {
|
|
162
|
+
result = {
|
|
163
|
+
conversationExpired: true
|
|
164
|
+
};
|
|
165
|
+
} else {
|
|
166
|
+
result = await deleteDetachedCodexAppServerConversationThread(sessionId, {
|
|
167
|
+
agentSettings: input.agentSettings,
|
|
168
|
+
threadId: input.conversationId
|
|
169
|
+
}, options, { conversationId, sessionKey, conversations });
|
|
170
|
+
if (result.ok === false) {
|
|
171
|
+
return { ...result, conversationId };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const providerExit = options.assistantScope ? await stopCodexScopeRuntimes(sessionKey) : null;
|
|
175
|
+
return {
|
|
176
|
+
...result,
|
|
177
|
+
conversationId,
|
|
178
|
+
ok: conversationExpired ? providerExit?.ok !== false : result?.ok !== false,
|
|
179
|
+
...(providerExit ? { providerExit } : {})
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function deleteDetachedCodexAppServerConversationThread(sessionId, input = {}, options = {}, conversation = null) {
|
|
184
|
+
return deleteCodexAppServerConversation(conversation, dispatch =>
|
|
185
|
+
codexAppServerConversationControl(sessionId, input, options, "delete", dispatch));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function deleteCodexAppServerConversation(conversation, operation) {
|
|
189
|
+
const result = await operation(async ({ provider, threadId }) => {
|
|
190
|
+
try {
|
|
191
|
+
const result = await provider.deleteThread(threadId);
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
result,
|
|
195
|
+
status: "deleted",
|
|
196
|
+
threadId
|
|
197
|
+
};
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (
|
|
200
|
+
codexAppServerRequestIsInvalid(error, "thread/delete") &&
|
|
201
|
+
!await codexAppServerThreadHasReadableHistory(provider, threadId)
|
|
202
|
+
) {
|
|
203
|
+
return {
|
|
204
|
+
ok: true,
|
|
205
|
+
status: "notFound",
|
|
206
|
+
threadId
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
if (result.ok !== false && conversation) {
|
|
213
|
+
const { conversationId, sessionKey, conversations } = conversation;
|
|
214
|
+
const state = conversations?.get(conversationId);
|
|
215
|
+
state?.watcher?.failNow(new Error("Temporary AI conversation was closed."));
|
|
216
|
+
if (state) forgetObservation(state);
|
|
217
|
+
conversations?.delete(conversationId);
|
|
218
|
+
if (conversations?.size === 0) {
|
|
219
|
+
codexAppServerConversations.delete(sessionKey);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function codexAppServerConversationControl(sessionId, input, {
|
|
226
|
+
assistantScope = null,
|
|
227
|
+
runtime = null,
|
|
228
|
+
session = null
|
|
229
|
+
} = {}, operation, dispatch) {
|
|
230
|
+
return codexAppServerConversationResult(async () => {
|
|
231
|
+
const prepared = conversationPreparation.control(sessionId, input, operation);
|
|
232
|
+
if (prepared.result) return prepared.result;
|
|
233
|
+
const { admission, threadId, turnId } = prepared;
|
|
234
|
+
// Stopping or deleting a thread is cleanup, not a use that keeps Codex running.
|
|
235
|
+
const borrow = { release() {} };
|
|
236
|
+
try {
|
|
237
|
+
let context = null;
|
|
238
|
+
try {
|
|
239
|
+
context = await codexAppServerConversationContext(sessionId, input, { assistantScope, runtime, session }, borrow);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
const unavailable = prepared.unavailable();
|
|
242
|
+
if (unavailable) return unavailable;
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
if (context.ok === false) return context;
|
|
246
|
+
const unavailable = prepared.unavailable();
|
|
247
|
+
if (unavailable) return unavailable;
|
|
248
|
+
const policyResult = helperLifecycle.inspectControl(operation, input, {
|
|
249
|
+
sessionId, threadId, turnId,
|
|
250
|
+
get retiredThreadIds() { return context.helperRestore?.retiredThreadIds; },
|
|
251
|
+
get projectRuntimeRoot() { return context.runtime?.stateRoot; },
|
|
252
|
+
get provider() { return context.provider; },
|
|
253
|
+
get workdir() { return context.workdir; }
|
|
254
|
+
});
|
|
255
|
+
// A Helper-retirement policy result releases admission before its
|
|
256
|
+
// promise settles; only the native dispatch below is awaited here.
|
|
257
|
+
if (policyResult !== undefined) return policyResult;
|
|
258
|
+
if (operation === "interrupt") {
|
|
259
|
+
const unavailable = prepared.unavailable();
|
|
260
|
+
if (unavailable) return unavailable;
|
|
261
|
+
// readable-check: allow method-probe - optional operation: thread deletion is the one control a provider may lack, answered by its catalogued unavailable result
|
|
262
|
+
} else if (typeof context.provider.deleteThread !== "function") {
|
|
263
|
+
return {
|
|
264
|
+
code: `${errorPrefix}codex_detached_thread_delete_unavailable`,
|
|
265
|
+
error: "Codex app-server thread deletion is not available.",
|
|
266
|
+
messageCode: "assistant.codex.threadDeleteUnavailable",
|
|
267
|
+
messageParams: {},
|
|
268
|
+
ok: false,
|
|
269
|
+
statusCode: 409,
|
|
270
|
+
threadId
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return await dispatch({ provider: context.provider, threadId, turnId });
|
|
274
|
+
} finally {
|
|
275
|
+
borrow.release();
|
|
276
|
+
admission.release();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// A scope's delete stops only what this server runs for it: its live providers,
|
|
282
|
+
// and a runtime whose earlier stop could not prove its exit. It never acquires
|
|
283
|
+
// a provider, which would start a Codex app-server only to stop it, so it needs
|
|
284
|
+
// no account, tool home or workdir. A runtime other providers share stays theirs.
|
|
285
|
+
async function stopCodexScopeRuntimes(sessionKey) {
|
|
286
|
+
const owner = providerSessions.owner;
|
|
287
|
+
const attempted = new Set([...owner.providers]
|
|
288
|
+
.filter(([providerKey]) => owner.providerOwners.get(providerKey)?.sessionKey === sessionKey)
|
|
289
|
+
.map(([, provider]) => provider));
|
|
290
|
+
const stopped = await owner.stopCachedProvidersForSession(sessionKey, { requireStopped: true });
|
|
291
|
+
const failed = [...stopped.failed];
|
|
292
|
+
const results = [...stopped.results];
|
|
293
|
+
for (const record of [...owner.ownedRuntimes.values()]) {
|
|
294
|
+
if (record.sessionKey !== sessionKey || attempted.has(record.provider) ||
|
|
295
|
+
owner.runtimeIsShared(record.providerKey, record.providerOptions)) continue;
|
|
296
|
+
try {
|
|
297
|
+
// Its provider is no longer cached, so the runtime's own options name its lock.
|
|
298
|
+
results.push(await owner.withLifecycle(() => owner.stopOwnedRuntime(record),
|
|
299
|
+
record.providerKey, record.providerOptions));
|
|
300
|
+
} catch (error) {
|
|
301
|
+
failed.push({ code: normalizeText(error?.code), error: codexAppServerErrorMessage(error),
|
|
302
|
+
providerKey: record.providerKey, retryable: error?.retryable === true });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (failed.length) {
|
|
306
|
+
const error = new Error(failed.map((failure) => failure.error).join(" "));
|
|
307
|
+
error.code = failed[0].code;
|
|
308
|
+
error.retryable = failed.every((failure) => failure.retryable);
|
|
309
|
+
error.details = { failed };
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
return { ok: true, results, stopped: results.some((result) => result?.stopped === true) };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Close stops each persistent conversation through the provider that observes
|
|
316
|
+
// it. A closing session refuses a rebuilt context, and a new connection could
|
|
317
|
+
// not see the running turn anyway. One conversation's failure does not skip
|
|
318
|
+
// the others; it stays registered so that a retried close stops it.
|
|
319
|
+
async function stopObservedCodexConversation(state) {
|
|
320
|
+
const { provider } = state;
|
|
321
|
+
if (provider.observationFailure) {
|
|
322
|
+
await stopThroughObservationFailure(state);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// A provider its owner has since closed observes nothing left to stop here:
|
|
326
|
+
// the owner stopped or detached its process, and reconnecting it would
|
|
327
|
+
// start a process nothing owns.
|
|
328
|
+
if (![...providerSessions.owner.providers].some(([, owned]) => owned === provider)) return;
|
|
329
|
+
// Closing is not the person stopping the turn; its status alone decides the outcome.
|
|
330
|
+
await stopPersistentCodexConversation({
|
|
331
|
+
conversationId: state.conversationId, conversationState: state, provider, turnOutcome: ""
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function closeCodexAppServerConversations(sessionId) {
|
|
336
|
+
const key = namespace(sessionId);
|
|
337
|
+
const conversations = codexAppServerConversations.get(key);
|
|
338
|
+
const failed = [];
|
|
339
|
+
for (const state of [...(conversations?.values() || [])]) {
|
|
340
|
+
if (!state.persistent) {
|
|
341
|
+
await deleteScopedCodexAppServerConversation(sessionId, { conversationId: state.conversationId }).catch(() => null);
|
|
342
|
+
conversations.delete(state.conversationId);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
await stopObservedCodexConversation(state);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
failed.push({ conversationId: state.conversationId, error: codexAppServerErrorMessage(error) });
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
forgetObservation(state);
|
|
352
|
+
conversations.delete(state.conversationId);
|
|
353
|
+
}
|
|
354
|
+
if (conversations && codexAppServerConversations.get(key) === conversations && !conversations.size) {
|
|
355
|
+
codexAppServerConversations.delete(key);
|
|
356
|
+
}
|
|
357
|
+
if (failed.length) {
|
|
358
|
+
const error = new Error(`Codex could not confirm that ${failed.length === 1 ? "a conversation" : `${failed.length} conversations`} stopped: ${
|
|
359
|
+
failed.map(({ conversationId, error: reason }) => `${conversationId} (${reason})`).join("; ")}. Retry closing the session.`);
|
|
360
|
+
error.code = `${errorPrefix}codex_conversation_close_failed`;
|
|
361
|
+
error.retryable = true;
|
|
362
|
+
error.details = { failed, sessionId: normalizeText(sessionId) };
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return Object.freeze({
|
|
368
|
+
stopCodexAppServerConversation,
|
|
369
|
+
stopPersistentCodexConversation,
|
|
370
|
+
interruptDetachedCodexAppServerConversationTurn,
|
|
371
|
+
deleteScopedCodexAppServerConversation,
|
|
372
|
+
deleteDetachedCodexAppServerConversationThread,
|
|
373
|
+
closeCodexAppServerConversations
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** @typedef {ReturnType<typeof createCodexConversationStops>} CodexConversationStops */
|
|
@@ -3,11 +3,20 @@ import { createReentrantConversationStorage } from "./storage.js";
|
|
|
3
3
|
import { isCompletedEnvelopeTurn } from "./transcript.js";
|
|
4
4
|
import { conversationAgentRunRecord, conversationAgentRunEvent } from "./agentRun.js";
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/** @typedef {import("../types.js").ConversationTranscript} ConversationTranscript */
|
|
7
|
+
/** @typedef {import("../types.js").ConversationStreams} ConversationStreams */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The native run occupies its bound conversation segment. Reading a binding without a run never constructs
|
|
11
|
+
* historical ownership or writes state.
|
|
12
|
+
* @param {object} dependencies
|
|
13
|
+
* @param {object} dependencies.storage The conversation storage the transcript uses.
|
|
14
|
+
* @param {string} dependencies.scope
|
|
15
|
+
* @param {string} dependencies.segmentId
|
|
16
|
+
* @param {() => boolean} dependencies.isCurrent The provider ownership fence.
|
|
17
|
+
* @param {() => Date} [dependencies.clock]
|
|
8
18
|
*/
|
|
9
|
-
export function createCodexConversationRunStore({ storage, scope, segmentId, isCurrent, clock = () => new Date() }
|
|
10
|
-
if (typeof isCurrent !== "function") throw new TypeError("A native run store requires its provider ownership fence.");
|
|
19
|
+
export function createCodexConversationRunStore({ storage, scope, segmentId, isCurrent, clock = () => new Date() }) {
|
|
11
20
|
storage = createReentrantConversationStorage(storage);
|
|
12
21
|
const runId = "codex_app_server";
|
|
13
22
|
|
|
@@ -41,32 +50,43 @@ export function createCodexConversationRunStore({ storage, scope, segmentId, isC
|
|
|
41
50
|
});
|
|
42
51
|
}
|
|
43
52
|
|
|
53
|
+
async function getSession(sessionId) {
|
|
54
|
+
const run = await readAgentRun(sessionId, runId);
|
|
55
|
+
return { sessionId, agentRuns: run ? [run] : [] };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function writeAgentRunEvent(sessionId, requestedRunId, input = {}) {
|
|
59
|
+
assertScope(sessionId, requestedRunId);
|
|
60
|
+
return mutateSession(sessionId, async transaction => {
|
|
61
|
+
const previous = await readAgentRun(sessionId, runId) || { events: [], id: runId };
|
|
62
|
+
const record = conversationAgentRunEvent(previous, runId, input, { now: clock });
|
|
63
|
+
const metadata = await currentMetadata(transaction);
|
|
64
|
+
metadata.runtime.binding.codexAppServerRun = record;
|
|
65
|
+
await transaction.writeMetadata(metadata);
|
|
66
|
+
return record;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
44
70
|
return Object.freeze({
|
|
45
71
|
readAgentRun,
|
|
46
72
|
mutateSession,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return { sessionId, agentRuns: run ? [run] : [] };
|
|
50
|
-
},
|
|
51
|
-
async writeAgentRunEvent(sessionId, requestedRunId, input = {}) {
|
|
52
|
-
assertScope(sessionId, requestedRunId);
|
|
53
|
-
return mutateSession(sessionId, async transaction => {
|
|
54
|
-
const previous = await readAgentRun(sessionId, runId) || { events: [], id: runId };
|
|
55
|
-
const record = conversationAgentRunEvent(previous, runId, input, { now: clock });
|
|
56
|
-
const metadata = await currentMetadata(transaction);
|
|
57
|
-
metadata.runtime.binding.codexAppServerRun = record;
|
|
58
|
-
await transaction.writeMetadata(metadata);
|
|
59
|
-
return record;
|
|
60
|
-
});
|
|
61
|
-
}
|
|
73
|
+
getSession,
|
|
74
|
+
writeAgentRunEvent
|
|
62
75
|
});
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
/**
|
|
66
|
-
* the same fenced transaction as its
|
|
67
|
-
* a projection of the one authored request, never another stored journal.
|
|
78
|
+
/**
|
|
79
|
+
* The native owner uses the transcript and transient streams through the same fenced transaction as its run. Its
|
|
80
|
+
* private delivery read is a projection of the one authored request, never another stored journal.
|
|
81
|
+
* @param {object} dependencies
|
|
82
|
+
* @param {object} dependencies.storage The conversation storage the transcript uses.
|
|
83
|
+
* @param {string} dependencies.scope
|
|
84
|
+
* @param {string} dependencies.segmentId
|
|
85
|
+
* @param {() => boolean} dependencies.isCurrent The provider ownership fence.
|
|
86
|
+
* @param {ConversationTranscript} dependencies.transcript
|
|
87
|
+
* @param {ConversationStreams} dependencies.streams
|
|
68
88
|
*/
|
|
69
|
-
export function createCodexConversationStore({ storage, scope, segmentId, isCurrent, transcript, streams }
|
|
89
|
+
export function createCodexConversationStore({ storage, scope, segmentId, isCurrent, transcript, streams }) {
|
|
70
90
|
const runStore = createCodexConversationRunStore({ storage, scope, segmentId, isCurrent });
|
|
71
91
|
const assertCurrent = sessionId => {
|
|
72
92
|
if (sessionId !== scope || !isCurrent()) throw new Error("A retired provider cannot change this conversation.");
|
|
@@ -116,74 +136,88 @@ export function createCodexConversationStore({ storage, scope, segmentId, isCurr
|
|
|
116
136
|
} });
|
|
117
137
|
return transaction.readTurn(written.turnId);
|
|
118
138
|
});
|
|
139
|
+
|
|
140
|
+
async function writeConversationUserMessage(sessionId, input = {}) {
|
|
141
|
+
return runStore.mutateSession(sessionId, async transaction => {
|
|
142
|
+
if (input.messageId && await transaction.hasMessage(input.messageId)) return null;
|
|
143
|
+
const metadata = await transaction.readMetadata();
|
|
144
|
+
const request = input.authoredRequest?.messageId === input.messageId ? input.authoredRequest
|
|
145
|
+
: metadata.runtime.request?.messageId === input.messageId ? metadata.runtime.request : null;
|
|
146
|
+
if (!request) {
|
|
147
|
+
const written = await transcript.writeConversationUserMessage(sessionId, input);
|
|
148
|
+
if (!written) return null;
|
|
149
|
+
await transaction.updateTurnMetadata(written.turnId, { runtime: {
|
|
150
|
+
status: "running", engine: "codex", segmentId, origin: "user",
|
|
151
|
+
...(input.nativeIdentity?.turnId ? { nativeTurnId: input.nativeIdentity.turnId } : {})
|
|
152
|
+
} });
|
|
153
|
+
return transaction.readTurn(written.turnId);
|
|
154
|
+
}
|
|
155
|
+
const turnId = await transaction.nextTurnId();
|
|
156
|
+
await transaction.appendMessage(turnId, {
|
|
157
|
+
role: request.origin === "application" ? "system" : "user",
|
|
158
|
+
messageId: request.messageId, text: request.text, origin: request.origin || "user",
|
|
159
|
+
attachments: request.attachments || input.attachments || [], at: request.at || new Date().toISOString(),
|
|
160
|
+
...(request.data !== undefined ? { data: request.data } : {}), ...(request.goal ? { goal: request.goal } : {}),
|
|
161
|
+
turnMetadata: { ...input.turnMetadata, runtime: { ...input.turnMetadata?.runtime,
|
|
162
|
+
status: "running", engine: "codex", segmentId, origin: request.origin || "user",
|
|
163
|
+
...(metadata.runtime.request?.messageId === input.messageId && metadata.runtime.request.completedEnvelope === true
|
|
164
|
+
? { completedEnvelope: true } : {}),
|
|
165
|
+
...(input.nativeIdentity?.turnId ? { nativeTurnId: input.nativeIdentity.turnId } : {}),
|
|
166
|
+
...(request.goal ? { goalMessageId: request.messageId } : {}) } }
|
|
167
|
+
});
|
|
168
|
+
return transaction.readTurn(turnId);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function readMetadataValue(sessionId, key) {
|
|
173
|
+
assertCurrent(sessionId);
|
|
174
|
+
if (key !== "assistant_delivery") throw new Error("The native conversation requested unknown delivery metadata.");
|
|
175
|
+
return storage.read(sessionId, async transaction => {
|
|
176
|
+
const { runtime } = await transaction.readMetadata();
|
|
177
|
+
if (!isCurrent() || runtime.segmentId !== segmentId || runtime.engine !== "codex") {
|
|
178
|
+
throw new Error("A retired native conversation cannot read current delivery metadata.");
|
|
179
|
+
}
|
|
180
|
+
const request = runtime.request;
|
|
181
|
+
return JSON.stringify({ engines: { codex: { pending: request && { ...request,
|
|
182
|
+
displayMessage: request.displayMessage ?? request.text,
|
|
183
|
+
displayAttachments: request.displayAttachments ?? request.attachments } } } });
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function updateConversationStream(sessionId, input) {
|
|
188
|
+
return runStore.mutateSession(sessionId, async transaction => {
|
|
189
|
+
const authorship = await nativeAuthorship(transaction, input.nativeIdentity);
|
|
190
|
+
return streams.update(sessionId, { ...input, origin: authorship?.origin, authorship });
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function completeConversationStreamMessage(sessionId, messageId, options) {
|
|
195
|
+
assertCurrent(sessionId);
|
|
196
|
+
if (options?.nativeIdentity) return runStore.mutateSession(sessionId, async transaction => {
|
|
197
|
+
const authorship = await nativeAuthorship(transaction, options.nativeIdentity);
|
|
198
|
+
return streams.complete(sessionId, messageId, { ...options, authorship,
|
|
199
|
+
...(!authorship ? { text: undefined } : {}) });
|
|
200
|
+
});
|
|
201
|
+
return streams.complete(sessionId, messageId, options);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function clearConversationStream(sessionId) { assertCurrent(sessionId); return streams.clear(sessionId); }
|
|
205
|
+
|
|
119
206
|
return Object.freeze({
|
|
120
|
-
|
|
207
|
+
readAgentRun: runStore.readAgentRun,
|
|
208
|
+
mutateSession: runStore.mutateSession,
|
|
209
|
+
getSession: runStore.getSession,
|
|
210
|
+
writeAgentRunEvent: runStore.writeAgentRunEvent,
|
|
121
211
|
readConversationLog: reader("readConversationLog"),
|
|
122
212
|
conversationMessageIdExists: reader("conversationMessageIdExists"),
|
|
123
213
|
writeConversationAssistantMessage: writer("writeConversationAssistantMessage"),
|
|
124
214
|
writeConversationCommentaryMessage: writer("writeConversationCommentaryMessage"),
|
|
125
215
|
writeConversationThinkingMessage: writer("writeConversationThinkingMessage"),
|
|
126
216
|
upsertConversationAssistantMessage: writer("upsertConversationAssistantMessage"),
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
: metadata.runtime.request?.messageId === input.messageId ? metadata.runtime.request : null;
|
|
133
|
-
if (!request) {
|
|
134
|
-
const written = await transcript.writeConversationUserMessage(sessionId, input);
|
|
135
|
-
if (!written) return null;
|
|
136
|
-
await transaction.updateTurnMetadata(written.turnId, { runtime: {
|
|
137
|
-
status: "running", engine: "codex", segmentId, origin: "user",
|
|
138
|
-
...(input.nativeIdentity?.turnId ? { nativeTurnId: input.nativeIdentity.turnId } : {})
|
|
139
|
-
} });
|
|
140
|
-
return transaction.readTurn(written.turnId);
|
|
141
|
-
}
|
|
142
|
-
const turnId = await transaction.nextTurnId();
|
|
143
|
-
await transaction.appendMessage(turnId, {
|
|
144
|
-
role: request.origin === "application" ? "system" : "user",
|
|
145
|
-
messageId: request.messageId, text: request.text, origin: request.origin || "user",
|
|
146
|
-
attachments: request.attachments || input.attachments || [], at: request.at || new Date().toISOString(),
|
|
147
|
-
...(request.data !== undefined ? { data: request.data } : {}), ...(request.goal ? { goal: request.goal } : {}),
|
|
148
|
-
turnMetadata: { ...input.turnMetadata, runtime: { ...input.turnMetadata?.runtime,
|
|
149
|
-
status: "running", engine: "codex", segmentId, origin: request.origin || "user",
|
|
150
|
-
...(metadata.runtime.request?.messageId === input.messageId && metadata.runtime.request.completedEnvelope === true
|
|
151
|
-
? { completedEnvelope: true } : {}),
|
|
152
|
-
...(input.nativeIdentity?.turnId ? { nativeTurnId: input.nativeIdentity.turnId } : {}),
|
|
153
|
-
...(request.goal ? { goalMessageId: request.messageId } : {}) } }
|
|
154
|
-
});
|
|
155
|
-
return transaction.readTurn(turnId);
|
|
156
|
-
});
|
|
157
|
-
},
|
|
158
|
-
readMetadataValue(sessionId, key) {
|
|
159
|
-
assertCurrent(sessionId);
|
|
160
|
-
if (key !== "assistant_delivery") throw new Error("The native conversation requested unknown delivery metadata.");
|
|
161
|
-
return storage.read(sessionId, async transaction => {
|
|
162
|
-
const { runtime } = await transaction.readMetadata();
|
|
163
|
-
if (!isCurrent() || runtime.segmentId !== segmentId || runtime.engine !== "codex") {
|
|
164
|
-
throw new Error("A retired native conversation cannot read current delivery metadata.");
|
|
165
|
-
}
|
|
166
|
-
const request = runtime.request;
|
|
167
|
-
return JSON.stringify({ engines: { codex: { pending: request && { ...request,
|
|
168
|
-
displayMessage: request.displayMessage ?? request.text,
|
|
169
|
-
displayAttachments: request.displayAttachments ?? request.attachments } } } });
|
|
170
|
-
});
|
|
171
|
-
},
|
|
172
|
-
updateConversationStream(sessionId, input) {
|
|
173
|
-
return runStore.mutateSession(sessionId, async transaction => {
|
|
174
|
-
const authorship = await nativeAuthorship(transaction, input.nativeIdentity);
|
|
175
|
-
return streams.update(sessionId, { ...input, origin: authorship?.origin, authorship });
|
|
176
|
-
});
|
|
177
|
-
},
|
|
178
|
-
completeConversationStreamMessage(sessionId, messageId, options) {
|
|
179
|
-
assertCurrent(sessionId);
|
|
180
|
-
if (options?.nativeIdentity) return runStore.mutateSession(sessionId, async transaction => {
|
|
181
|
-
const authorship = await nativeAuthorship(transaction, options.nativeIdentity);
|
|
182
|
-
return streams.complete(sessionId, messageId, { ...options, authorship,
|
|
183
|
-
...(!authorship ? { text: undefined } : {}) });
|
|
184
|
-
});
|
|
185
|
-
return streams.complete(sessionId, messageId, options);
|
|
186
|
-
},
|
|
187
|
-
clearConversationStream(sessionId) { assertCurrent(sessionId); return streams.clear(sessionId); }
|
|
217
|
+
writeConversationUserMessage,
|
|
218
|
+
readMetadataValue,
|
|
219
|
+
updateConversationStream,
|
|
220
|
+
completeConversationStreamMessage,
|
|
221
|
+
clearConversationStream
|
|
188
222
|
});
|
|
189
223
|
}
|