@jskit-ai/assistant-core 0.2.7 → 0.2.8
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,331 @@
|
|
|
1
|
+
import { CODEX_APP_SERVER_DESKTOP_BUS_ENV_NAMES, codexCommandHookDefinition } from "../codexProcess.js";
|
|
2
|
+
import { CodexAppServerAgentProvider } from "../codexProvider.js";
|
|
3
|
+
import {
|
|
4
|
+
assertCodexApplicationToolSchemaIdentity,
|
|
5
|
+
codexApplicationToolConfiguration,
|
|
6
|
+
ensureCodexAppServerThread
|
|
7
|
+
} from "../codexProviderThreads.js";
|
|
8
|
+
import { codexProviderConfiguration } from "../codexConfiguration.js";
|
|
9
|
+
import { codexToolFreeConfiguration } from "../codexIsolation.js";
|
|
10
|
+
import { codexCommandHookCommand } from "../commandWrapper.js";
|
|
11
|
+
import { validateConnectionModel } from "../configuration.js";
|
|
12
|
+
import { jsonHash as hash } from "../normalize.js";
|
|
13
|
+
import { nativeAiProvider, nativeAiModel } from "../../../shared/nativeProviders.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The native side of one driver conversation: its observation, the provider it acquires and stops, the account it
|
|
17
|
+
* selects and the thread settings it prepares. It reads and updates the conversation's session record.
|
|
18
|
+
* @param {object} dependencies
|
|
19
|
+
* @param {import("./codexDriver.js").CodexDriverSession} dependencies.session
|
|
20
|
+
* @param {object | undefined} dependencies.supplied
|
|
21
|
+
* @param {string} dependencies.sessionId
|
|
22
|
+
* @param {string} dependencies.providerKey
|
|
23
|
+
* @param {import("../codexProviderOwner.js").CodexAppServerProviderOwner} dependencies.providerOwner
|
|
24
|
+
* @param {import("../codexRuntimeLifecycle.js").CodexRunOwner} dependencies.owner
|
|
25
|
+
* @param {object} dependencies.runtime
|
|
26
|
+
* @param {((error: Error) => void) | undefined} dependencies.reportFailure
|
|
27
|
+
* @param {(update: (binding: object) => object) => Promise<void>} dependencies.updateBinding
|
|
28
|
+
* @param {(goal: object | null) => Promise<object | null>} dependencies.recordGoal
|
|
29
|
+
* @param {(client: object, signal: AbortSignal) => Promise<string>} dependencies.accountIdentity
|
|
30
|
+
* @param {import("./codexDriver.js").CodexConversationConnections} dependencies.connections
|
|
31
|
+
* @param {Record<string, string>} dependencies.env
|
|
32
|
+
* @param {object} dependencies.host
|
|
33
|
+
* @param {object} dependencies.limits
|
|
34
|
+
* @param {(selected: object) => Promise<object>} dependencies.nativeHost
|
|
35
|
+
* @param {boolean} dependencies.nativeTools
|
|
36
|
+
*/
|
|
37
|
+
export function createCodexDriverConnection({
|
|
38
|
+
session,
|
|
39
|
+
supplied,
|
|
40
|
+
sessionId,
|
|
41
|
+
providerKey,
|
|
42
|
+
providerOwner,
|
|
43
|
+
owner,
|
|
44
|
+
runtime,
|
|
45
|
+
reportFailure,
|
|
46
|
+
updateBinding,
|
|
47
|
+
recordGoal,
|
|
48
|
+
accountIdentity,
|
|
49
|
+
connections,
|
|
50
|
+
env,
|
|
51
|
+
host,
|
|
52
|
+
limits,
|
|
53
|
+
nativeHost,
|
|
54
|
+
nativeTools
|
|
55
|
+
}) {
|
|
56
|
+
const observation = supplied ? null : owner.createObservation(sessionId, {
|
|
57
|
+
target(error) {
|
|
58
|
+
if (session.observationTarget?.error !== error) session.observationTarget = { error, threadId: session.binding.threadId,
|
|
59
|
+
message: "Codex observation was lost. Work is stopped; use Resume or Send to continue.",
|
|
60
|
+
pendingMessage: "Codex observation was lost. A stop is not yet confirmed." };
|
|
61
|
+
return session.observationTarget;
|
|
62
|
+
},
|
|
63
|
+
retireProvider: () => providerOwner.closeProvider(providerKey)
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
async function recordObservationBarrier({ provider, error }) {
|
|
67
|
+
session.observationError = error;
|
|
68
|
+
// Keep admission unavailable in memory even if its durable write fails.
|
|
69
|
+
await reportFailure(error);
|
|
70
|
+
await updateBinding({ observationLoss: {
|
|
71
|
+
message: error.message, providerId: session.nativeProviderId,
|
|
72
|
+
runtime: provider.runtime || provider.runtimeStopOwner || session.binding.observationLoss?.runtime,
|
|
73
|
+
stopped: false
|
|
74
|
+
} });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function completeObservation({ provider, error, sharedStop, threadIds }) {
|
|
78
|
+
if (threadIds && !threadIds.includes(session.binding.threadId)) return;
|
|
79
|
+
await updateBinding({ observationLoss: { ...session.binding.observationLoss, stopped: true },
|
|
80
|
+
...(sharedStop ? { executionId: "" } : {}) });
|
|
81
|
+
await observation.complete({ provider, error, sharedStop, threadIds });
|
|
82
|
+
session.observationError = null;
|
|
83
|
+
await reportFailure(error, { recovered: true });
|
|
84
|
+
session.current?.completion.reject(error);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function acquireProvider(facilities, providerId) {
|
|
88
|
+
session.nativeProviderId = providerId;
|
|
89
|
+
const providerOptions = {
|
|
90
|
+
...facilities.options, runtimeDir: facilities.parameters.runtimeDir, modelProviderId: providerId,
|
|
91
|
+
threadEnv: env,
|
|
92
|
+
readInstructions: () => session.preparedThreadSettings?.developerInstructions,
|
|
93
|
+
prepareThreadEnvironment: environment => session.preparedThreadSettings?.config.shell_environment_policy.set || environment,
|
|
94
|
+
prepareThreadResumeParams(threadId, params) {
|
|
95
|
+
if (threadId !== session.binding.threadId || !session.preparedThreadSettings) {
|
|
96
|
+
throw new Error("Codex requires current authorized settings before resuming this conversation.");
|
|
97
|
+
}
|
|
98
|
+
return { ...session.preparedThreadSettings, ...params,
|
|
99
|
+
config: { ...session.preparedThreadSettings.config, ...params.config } };
|
|
100
|
+
},
|
|
101
|
+
async beforeResumeThread(threadId) {
|
|
102
|
+
owner.assertThreadCanResume(await owner.readAgentRunForSession(runtime.store, sessionId), threadId);
|
|
103
|
+
},
|
|
104
|
+
requestTimeoutMs: limits.admissionTimeoutMs || 30_000
|
|
105
|
+
};
|
|
106
|
+
return providerOwner.withLifecycle(() => providerOwner.createProvider({
|
|
107
|
+
providerKey, providerOptions, preserveProcessExitProof: true,
|
|
108
|
+
resources: {
|
|
109
|
+
release(provider) {
|
|
110
|
+
if (session.native === provider) session.native = null;
|
|
111
|
+
const subscriptionPrefix = `${providerKey}:`;
|
|
112
|
+
for (const key of [...owner.eventSubscriptions.keys()]) {
|
|
113
|
+
if (key.startsWith(subscriptionPrefix)) owner.unsubscribeEventSubscription(key);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
invalidation: {
|
|
117
|
+
async prepare(input) {
|
|
118
|
+
if (session.native !== input.provider) return false;
|
|
119
|
+
await recordObservationBarrier(input);
|
|
120
|
+
await observation.barrier(input);
|
|
121
|
+
},
|
|
122
|
+
async complete({ provider, error, preparationError, stopped }) {
|
|
123
|
+
try {
|
|
124
|
+
if (preparationError) throw preparationError;
|
|
125
|
+
if (stopped.status !== "fulfilled") throw stopped.reason;
|
|
126
|
+
await completeObservation({ provider, error, sharedStop: true });
|
|
127
|
+
} catch (failure) {
|
|
128
|
+
error.cleanupFailed = true;
|
|
129
|
+
error.cause = failure;
|
|
130
|
+
session.observationError = error;
|
|
131
|
+
await reportFailure(error);
|
|
132
|
+
session.current?.completion.reject(error);
|
|
133
|
+
throw failure;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
create: onObservationLost => new CodexAppServerAgentProvider({ ...providerOptions, onObservationLost }, facilities),
|
|
139
|
+
observation: {
|
|
140
|
+
async prepare(input) {
|
|
141
|
+
if (input.threadIds && !input.threadIds.includes(session.binding.threadId)) return new Map();
|
|
142
|
+
session.observationError = input.error;
|
|
143
|
+
await reportFailure(input.error);
|
|
144
|
+
const threads = await observation.prepare(input);
|
|
145
|
+
await recordObservationBarrier(input);
|
|
146
|
+
return threads;
|
|
147
|
+
},
|
|
148
|
+
async barrier(input) {
|
|
149
|
+
if (input.threadIds && !input.threadIds.includes(session.binding.threadId)) return;
|
|
150
|
+
await observation.barrier(input);
|
|
151
|
+
await recordObservationBarrier(input);
|
|
152
|
+
},
|
|
153
|
+
complete: completeObservation
|
|
154
|
+
}
|
|
155
|
+
}), providerKey, providerOptions);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function stop() {
|
|
159
|
+
if (session.stopping) return session.stopping;
|
|
160
|
+
session.stopping = (async () => {
|
|
161
|
+
if (!session.native && session.observationError) {
|
|
162
|
+
// Recover the exact saved owner through the production disk lock
|
|
163
|
+
// and metadata path. Cleanup must not start or resume a thread.
|
|
164
|
+
const saved = session.binding.observationLoss;
|
|
165
|
+
const facilities = await nativeHost({ providerId: saved.providerId,
|
|
166
|
+
accountIdentity: session.binding.connectionIdentities?.[saved.providerId] });
|
|
167
|
+
session.native = await acquireProvider(facilities, saved.providerId);
|
|
168
|
+
session.native.runtimeStopOwner = saved.runtime;
|
|
169
|
+
session.native.observationFailure = session.observationError;
|
|
170
|
+
}
|
|
171
|
+
if (session.native?.observationFailure) {
|
|
172
|
+
await providerOwner.recover(providerKey, session.native.observationFailure);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (session.native && session.binding.threadId) {
|
|
176
|
+
const provider = session.native;
|
|
177
|
+
try {
|
|
178
|
+
const { goal } = await provider.stopThreadForObservationLoss(session.binding.threadId, session.current?.turnId);
|
|
179
|
+
await recordGoal(goal || null);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
await provider.failObservation(error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
})().catch(error => { error.cleanupFailed = true; throw error; }).finally(() => { session.stopping = null; });
|
|
185
|
+
return session.stopping;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function selection(configuration, context, signal, requireBound = false, receiptBinding) {
|
|
189
|
+
if (!configuration.integrationId) {
|
|
190
|
+
if (requireBound && !receiptBinding?.accountIdentity) throw new Error("The completed Codex response has no saved native account fingerprint.");
|
|
191
|
+
if (requireBound && session.binding.accountIdentity !== receiptBinding.accountIdentity) {
|
|
192
|
+
throw new Error("The completed Codex response belongs to a different native account binding.");
|
|
193
|
+
}
|
|
194
|
+
return { model: configuration.model, providerId: "openai" };
|
|
195
|
+
}
|
|
196
|
+
const connection = await connections.resolve({ context, integrationId: configuration.integrationId });
|
|
197
|
+
signal.throwIfAborted();
|
|
198
|
+
const provider = nativeAiProvider(connection.providerId);
|
|
199
|
+
const model = nativeAiModel(connection.model, connection.providerId);
|
|
200
|
+
if (!provider || !model) throw new Error("This exact provider/model is not supported by the Codex integration. Choose a supported model without renaming it.");
|
|
201
|
+
validateConnectionModel(configuration, connection);
|
|
202
|
+
if (configuration.effort && !model.variants.includes(configuration.effort)) throw new Error("The selected Codex model does not support this reasoning effort.");
|
|
203
|
+
const identity = hash([provider.id, provider.baseUrl, connection.apiKey]);
|
|
204
|
+
const previous = session.binding.connectionIdentities?.[provider.id];
|
|
205
|
+
const expected = receiptBinding?.connectionIdentities?.[provider.id];
|
|
206
|
+
if (requireBound && !expected) throw new Error("The completed Codex response has no saved provider account fingerprint.");
|
|
207
|
+
if (requireBound && (previous !== expected || identity !== expected)) {
|
|
208
|
+
throw new Error("The completed Codex response belongs to a different provider account binding.");
|
|
209
|
+
}
|
|
210
|
+
if (previous && previous !== identity) throw new Error("This conversation belongs to another Codex provider account. Restore that connection or start a new conversation.");
|
|
211
|
+
if (!previous) await updateBinding({ connectionIdentities: { ...session.binding.connectionIdentities, [provider.id]: identity } });
|
|
212
|
+
return { model: model.id, providerId: provider.id, accountIdentity: identity, config: codexProviderConfiguration(provider, connection.apiKey) };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function connect(signal, selected) {
|
|
216
|
+
if (session.observationError) throw session.observationError;
|
|
217
|
+
if (session.native && session.nativeProviderId !== selected.providerId) {
|
|
218
|
+
await stop();
|
|
219
|
+
providerOwner.closeProvider(providerKey);
|
|
220
|
+
}
|
|
221
|
+
if (!session.native) {
|
|
222
|
+
const facilities = await nativeHost(selected);
|
|
223
|
+
signal.throwIfAborted();
|
|
224
|
+
const provider = await acquireProvider(facilities, selected.providerId);
|
|
225
|
+
// Account startup has other waiters. Cancellation releases this
|
|
226
|
+
// observer; the runtime owner settles the shared startup.
|
|
227
|
+
const connected = providerOwner.acquireRuntime({ providerKey, provider,
|
|
228
|
+
providerOptions: provider.options, operation: () => provider.connect() });
|
|
229
|
+
const cancelled = Promise.withResolvers();
|
|
230
|
+
const abort = () => cancelled.reject(signal.reason);
|
|
231
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
232
|
+
if (signal.aborted) abort();
|
|
233
|
+
try {
|
|
234
|
+
await Promise.race([connected, cancelled.promise]);
|
|
235
|
+
signal.throwIfAborted();
|
|
236
|
+
// Execution identity is a reference for recovery, not ownership of
|
|
237
|
+
// the account service by this one conversation.
|
|
238
|
+
await updateBinding({ executionId: provider.runtime.executionId });
|
|
239
|
+
session.native = provider;
|
|
240
|
+
session.nativeProviderId = selected.providerId;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
void connected.then(() => providerOwner.closeProvider(providerKey), () => providerOwner.closeProvider(providerKey));
|
|
243
|
+
throw error;
|
|
244
|
+
} finally {
|
|
245
|
+
signal.removeEventListener("abort", abort);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
signal.throwIfAborted();
|
|
249
|
+
if (selected.providerId !== "openai") return;
|
|
250
|
+
const identity = await accountIdentity(session.native.client, signal);
|
|
251
|
+
if (session.binding.accountIdentity && session.binding.accountIdentity !== identity) {
|
|
252
|
+
providerOwner.closeProvider(providerKey);
|
|
253
|
+
throw new Error("This conversation belongs to another Codex account. Restore that account or start a new conversation.");
|
|
254
|
+
}
|
|
255
|
+
if (!session.binding.accountIdentity) await updateBinding({ accountIdentity: identity });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function prepareConfiguration(configuration, context, signal, tools) {
|
|
259
|
+
const { dynamicTools, toolSchemaIdentity } = codexApplicationToolConfiguration(tools);
|
|
260
|
+
assertCodexApplicationToolSchemaIdentity(session.binding.threadId, session.binding.toolSchemaIdentity, toolSchemaIdentity);
|
|
261
|
+
const selected = await selection(configuration, context, signal);
|
|
262
|
+
await connect(signal, selected);
|
|
263
|
+
const [configResult, hookResult] = await Promise.all([
|
|
264
|
+
session.native.client.request("config/read", { cwd: session.binding.workdir, includeLayers: false }, { signal }),
|
|
265
|
+
session.native.client.request("hooks/list", { cwds: [session.binding.workdir] }, { signal })
|
|
266
|
+
]);
|
|
267
|
+
const toolConfiguration = codexToolFreeConfiguration({ configResult, hookResult, workdir: session.binding.workdir });
|
|
268
|
+
// Native control verification runs the host's shell even when model
|
|
269
|
+
// shell tools are disabled. Its login profile needs the standard tools;
|
|
270
|
+
// do not copy credentials or the rest of the host environment here.
|
|
271
|
+
toolConfiguration.shell_environment_policy.set.PATH = env.PATH || "/usr/bin:/bin";
|
|
272
|
+
// A goal can be set while an ordinary turn is already running. Codex
|
|
273
|
+
// must have its native scheduler enabled before that turn starts.
|
|
274
|
+
toolConfiguration.features.goals = true;
|
|
275
|
+
if (host.commandWrapper) {
|
|
276
|
+
const hookCommand = codexCommandHookCommand(host.commandWrapper);
|
|
277
|
+
const owned = hookResult.data[0].hooks.filter(hook => hook.source === "sessionFlags" &&
|
|
278
|
+
hook.eventName === "preToolUse" && hook.handlerType === "command" && hook.command === hookCommand);
|
|
279
|
+
if (owned.length !== 1 || typeof owned[0].currentHash !== "string" || !owned[0].currentHash) {
|
|
280
|
+
throw new Error("Codex did not install the host's required command wrapper.");
|
|
281
|
+
}
|
|
282
|
+
toolConfiguration.features.hooks = true;
|
|
283
|
+
toolConfiguration.hooks.PreToolUse = codexCommandHookDefinition(hookCommand);
|
|
284
|
+
toolConfiguration.hooks.state[owned[0].key] = { enabled: true, trusted_hash: owned[0].currentHash };
|
|
285
|
+
}
|
|
286
|
+
if (nativeTools) {
|
|
287
|
+
Object.assign(toolConfiguration.features, { shell_tool: true, unified_exec: true, view_image: true });
|
|
288
|
+
toolConfiguration.include_environment_context = true;
|
|
289
|
+
toolConfiguration.include_permissions_instructions = true;
|
|
290
|
+
toolConfiguration.tools.update_plan.enabled = true;
|
|
291
|
+
toolConfiguration.shell_environment_policy.set = Object.fromEntries(Object.entries(env).filter(([name, value]) =>
|
|
292
|
+
typeof value === "string" && !CODEX_APP_SERVER_DESKTOP_BUS_ENV_NAMES.has(name)));
|
|
293
|
+
toolConfiguration.web_search = selected.config?.web_search || "disabled";
|
|
294
|
+
}
|
|
295
|
+
const params = { developerInstructions: configuration.systemPrompt,
|
|
296
|
+
cwd: session.binding.workdir, modelProvider: selected.providerId, ...(selected.model ? { model: selected.model } : {}),
|
|
297
|
+
dynamicTools, ...(!nativeTools ? { environments: [], runtimeWorkspaceRoots: [], selectedCapabilityRoots: [] } : {}),
|
|
298
|
+
approvalPolicy: "never", sandbox: nativeTools ? "danger-full-access" : "read-only", allowProviderModelFallback: false,
|
|
299
|
+
config: { ...selected.config, ...toolConfiguration }
|
|
300
|
+
};
|
|
301
|
+
// Cold control recovery uses these authorized settings;
|
|
302
|
+
// it must not resume with ambient defaults or an old instruction prompt.
|
|
303
|
+
session.preparedThreadSettings = params;
|
|
304
|
+
return { selected, params, toolSchemaIdentity };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function prepare(configuration, context, signal, tools) {
|
|
308
|
+
const { selected, params, toolSchemaIdentity } = await prepareConfiguration(configuration, context, signal, tools);
|
|
309
|
+
const retainedThreadId = session.binding.threadId;
|
|
310
|
+
const prepared = await ensureCodexAppServerThread({
|
|
311
|
+
provider: session.native, workdir: session.binding.workdir,
|
|
312
|
+
observeThread: threadId => owner.subscribeEvents(sessionId, session.native, threadId, { providerKey }),
|
|
313
|
+
settings: async () => ({ threadSettings: params, threadStartSettings: params }),
|
|
314
|
+
identity: { read: () => retainedThreadId,
|
|
315
|
+
async write({ threadId }) { await updateBinding({ threadId, toolSchemaIdentity }); } }
|
|
316
|
+
});
|
|
317
|
+
if (!retainedThreadId && prepared.thread.raw?.historyMode !== "paginated") {
|
|
318
|
+
throw new Error("Codex did not create the required paginated history. Update Codex before starting this conversation.");
|
|
319
|
+
}
|
|
320
|
+
owner.subscribeEvents(sessionId, session.native, prepared.threadId, { providerKey });
|
|
321
|
+
return { ...selected, resumeOptions: params };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return Object.freeze({
|
|
325
|
+
stop,
|
|
326
|
+
selection,
|
|
327
|
+
connect,
|
|
328
|
+
prepareConfiguration,
|
|
329
|
+
prepare
|
|
330
|
+
});
|
|
331
|
+
}
|