@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
|
@@ -1,44 +1,37 @@
|
|
|
1
|
-
import { deleteCodexAppServerThread
|
|
2
|
-
import { directoryExists } from "./
|
|
3
|
-
import { CODEX_HELPER_THREAD_LIFECYCLES } from "./
|
|
4
|
-
import {
|
|
1
|
+
import { deleteCodexAppServerThread } from "./codexProviderThreads.js";
|
|
2
|
+
import { codexAppServerRuntimeStopWasVerified, directoryExists } from "./codexRuntimeInventory.js";
|
|
3
|
+
import { CODEX_HELPER_THREAD_LIFECYCLES } from "./codexHelperThreadRecord.js";
|
|
4
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Recovery operations of the same helper lifecycle. All maps and record mutations belong to the supplied lifecycle;
|
|
8
|
+
* this module creates no owner or registry.
|
|
9
|
+
* @param {import("./codexHelperThreads.js").CodexHelperThreadLifecycle} lifecycle
|
|
10
|
+
* @param {object} dependencies
|
|
11
|
+
* @param {import("./codexProviderOwner.js").CodexAppServerProviderOwner} dependencies.providerOwner
|
|
12
|
+
* @param {string} dependencies.applicationName
|
|
13
|
+
* @param {string} dependencies.errorPrefix
|
|
14
|
+
*/
|
|
8
15
|
export function createCodexHelperThreadRecovery(lifecycle, {
|
|
9
16
|
providerOwner,
|
|
10
17
|
applicationName,
|
|
11
18
|
errorPrefix
|
|
12
19
|
}) {
|
|
13
20
|
const codexAppServerProviders = providerOwner.providers;
|
|
14
|
-
const {
|
|
15
|
-
threads: codexAppServerHelperThreads,
|
|
16
|
-
mutations: codexAppServerHelperThreadMutations,
|
|
17
|
-
restores: codexAppServerHelperThreadRestores,
|
|
18
|
-
key: codexAppServerHelperThreadKey,
|
|
19
|
-
ledger: codexAppServerHelperThreadLedger,
|
|
20
|
-
providerFingerprint: codexAppServerProviderKeyFingerprint,
|
|
21
|
-
attach: attachCodexAppServerHelperThread,
|
|
22
|
-
remove: removeCodexAppServerHelperThread,
|
|
23
|
-
retire: retireCodexAppServerHelperThread,
|
|
24
|
-
ownershipError: codexAppServerHelperOwnershipError,
|
|
25
|
-
ownershipMatches: codexAppServerHelperThreadOwnershipMatches,
|
|
26
|
-
failure: codexAppServerHelperFailure
|
|
27
|
-
} = lifecycle;
|
|
28
21
|
|
|
29
22
|
async function restoreCodexAppServerHelperThread(record = {}, context = {}) {
|
|
30
23
|
const { ledger = null } = context;
|
|
31
|
-
const key =
|
|
32
|
-
const pendingMutation =
|
|
24
|
+
const key = lifecycle.key(record);
|
|
25
|
+
const pendingMutation = lifecycle.mutations.get(key);
|
|
33
26
|
if (pendingMutation) {
|
|
34
27
|
await pendingMutation.catch(() => null);
|
|
35
28
|
}
|
|
36
|
-
const existing =
|
|
29
|
+
const existing = lifecycle.threads.get(key);
|
|
37
30
|
if (existing && (
|
|
38
31
|
existing.ownershipId !== record.ownershipId ||
|
|
39
32
|
existing.revision < record.revision
|
|
40
33
|
)) {
|
|
41
|
-
throw
|
|
34
|
+
throw lifecycle.ownershipError(
|
|
42
35
|
"Persisted Codex helper ownership changed while the controller was running.",
|
|
43
36
|
{ sessionId: record.sessionId, threadId: record.threadId }
|
|
44
37
|
);
|
|
@@ -48,12 +41,12 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
48
41
|
if (!context.retiring) {
|
|
49
42
|
return { record: existing, retiredThreadId: "" };
|
|
50
43
|
}
|
|
51
|
-
await
|
|
44
|
+
await lifecycle.retire(existing);
|
|
52
45
|
return { record: null, retiredThreadId: existing.threadId };
|
|
53
46
|
}
|
|
54
47
|
if (!await directoryExists(record.identity.runtime.runtimeDir)) {
|
|
55
48
|
if (existing) {
|
|
56
|
-
await
|
|
49
|
+
await lifecycle.retire(existing);
|
|
57
50
|
} else {
|
|
58
51
|
await ledger.remove(record);
|
|
59
52
|
}
|
|
@@ -62,7 +55,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
62
55
|
const prepared = await context.prepareProvider();
|
|
63
56
|
const { providerOptions, providerKey } = prepared;
|
|
64
57
|
const contextChanged = !existing &&
|
|
65
|
-
|
|
58
|
+
lifecycle.providerFingerprint(providerKey) !== record.identity.providerKeyFingerprint;
|
|
66
59
|
if (contextChanged) {
|
|
67
60
|
const staleProvider = providerOwner.createNativeProvider({
|
|
68
61
|
...providerOptions,
|
|
@@ -73,7 +66,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
73
66
|
const accountChanged = record.identity.runtime.accountIdentitySignature !==
|
|
74
67
|
normalizeText(currentRuntime.accountIdentitySignature);
|
|
75
68
|
if (accountChanged && !cleanupRequired) {
|
|
76
|
-
throw
|
|
69
|
+
throw lifecycle.ownershipError(
|
|
77
70
|
"Reconnect the original Codex account to resume or delete this helper thread.",
|
|
78
71
|
{ retryable: false, sessionId: record.sessionId, threadId: record.threadId }
|
|
79
72
|
);
|
|
@@ -87,21 +80,21 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
87
80
|
? stopped?.ownershipSuperseded === true || stopped?.processExitVerified === true
|
|
88
81
|
: codexAppServerRuntimeStopWasVerified(stopped);
|
|
89
82
|
if (!retired) {
|
|
90
|
-
throw
|
|
83
|
+
throw lifecycle.ownershipError(
|
|
91
84
|
"The earlier Codex runtime could not be verified as retired; its cleanup record has been preserved.",
|
|
92
85
|
{ sessionId: record.sessionId, threadId: record.threadId }
|
|
93
86
|
);
|
|
94
87
|
}
|
|
95
88
|
await ledger.remove(record);
|
|
96
|
-
|
|
89
|
+
lifecycle.threads.delete(key);
|
|
97
90
|
return { record: null, retiredThreadId: record.threadId };
|
|
98
91
|
}
|
|
99
92
|
} finally {
|
|
100
|
-
staleProvider.close
|
|
93
|
+
staleProvider.close();
|
|
101
94
|
}
|
|
102
95
|
}
|
|
103
96
|
if (existing) {
|
|
104
|
-
await
|
|
97
|
+
await lifecycle.retire(existing);
|
|
105
98
|
return { record: null, retiredThreadId: existing.threadId };
|
|
106
99
|
}
|
|
107
100
|
let provider = codexAppServerProviders.get(providerKey);
|
|
@@ -113,17 +106,8 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
113
106
|
create: (onObservationLost) => providerOwner.createNativeProvider({ ...providerOptions, onObservationLost })
|
|
114
107
|
});
|
|
115
108
|
}
|
|
116
|
-
if (typeof provider.currentRuntimeInfo !== "function") {
|
|
117
|
-
throw codexAppServerHelperOwnershipError(
|
|
118
|
-
"The Codex provider cannot prove persisted runtime ownership.",
|
|
119
|
-
{
|
|
120
|
-
sessionId: record.sessionId,
|
|
121
|
-
threadId: record.threadId
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
109
|
const expectedRuntime = await provider.currentRuntimeInfo();
|
|
126
|
-
if (!
|
|
110
|
+
if (!lifecycle.ownershipMatches(record, {
|
|
127
111
|
providerKey,
|
|
128
112
|
runtime: expectedRuntime
|
|
129
113
|
}, {
|
|
@@ -131,7 +115,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
131
115
|
requireAccount: !cleanupRequired,
|
|
132
116
|
requireEndpoint: false
|
|
133
117
|
})) {
|
|
134
|
-
throw
|
|
118
|
+
throw lifecycle.ownershipError(
|
|
135
119
|
"The current Codex runtime/auth identity does not match persisted helper ownership.",
|
|
136
120
|
{
|
|
137
121
|
retryable: record.identity.runtime.accountIdentitySignature === normalizeText(expectedRuntime.accountIdentitySignature),
|
|
@@ -141,14 +125,14 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
141
125
|
);
|
|
142
126
|
}
|
|
143
127
|
await providerOwner.acquireRuntime({
|
|
144
|
-
operation: () => provider.ensureAvailable
|
|
128
|
+
operation: () => provider.ensureAvailable(),
|
|
145
129
|
provider,
|
|
146
130
|
providerKey,
|
|
147
131
|
providerOptions
|
|
148
132
|
});
|
|
149
133
|
const currentRuntime = await provider.currentRuntimeInfo();
|
|
150
|
-
const currentServer = provider.currentServerInfo
|
|
151
|
-
if (!
|
|
134
|
+
const currentServer = provider.currentServerInfo();
|
|
135
|
+
if (!lifecycle.ownershipMatches(record, {
|
|
152
136
|
providerKey,
|
|
153
137
|
runtime: currentRuntime,
|
|
154
138
|
server: currentServer
|
|
@@ -157,7 +141,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
157
141
|
requireEndpoint: true,
|
|
158
142
|
requireServer: !cleanupRequired
|
|
159
143
|
})) {
|
|
160
|
-
throw
|
|
144
|
+
throw lifecycle.ownershipError(
|
|
161
145
|
"The connected Codex server identity does not match persisted helper ownership.",
|
|
162
146
|
{
|
|
163
147
|
sessionId: record.sessionId,
|
|
@@ -167,7 +151,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
167
151
|
}
|
|
168
152
|
const currentLedger = await ledger.readAll();
|
|
169
153
|
if (currentLedger.failures.length > 0) {
|
|
170
|
-
throw
|
|
154
|
+
throw lifecycle.ownershipError(
|
|
171
155
|
"Persisted Codex helper ownership could not be revalidated before restore.",
|
|
172
156
|
{
|
|
173
157
|
failed: currentLedger.failures,
|
|
@@ -177,7 +161,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
177
161
|
);
|
|
178
162
|
}
|
|
179
163
|
const currentDurable = currentLedger.records.find((candidate) => (
|
|
180
|
-
|
|
164
|
+
lifecycle.key(candidate) === key
|
|
181
165
|
));
|
|
182
166
|
if (!currentDurable) {
|
|
183
167
|
return { record: null, retiredThreadId: record.threadId };
|
|
@@ -186,7 +170,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
186
170
|
currentDurable.ownershipId !== record.ownershipId ||
|
|
187
171
|
currentDurable.revision !== record.revision
|
|
188
172
|
) {
|
|
189
|
-
throw
|
|
173
|
+
throw lifecycle.ownershipError(
|
|
190
174
|
"Persisted Codex helper ownership changed before it could be restored.",
|
|
191
175
|
{
|
|
192
176
|
sessionId: record.sessionId,
|
|
@@ -194,7 +178,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
194
178
|
}
|
|
195
179
|
);
|
|
196
180
|
}
|
|
197
|
-
const attached =
|
|
181
|
+
const attached = lifecycle.attach({
|
|
198
182
|
durable: currentDurable,
|
|
199
183
|
ledger,
|
|
200
184
|
provider
|
|
@@ -203,25 +187,25 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
203
187
|
attached.lifecycle !== CODEX_HELPER_THREAD_LIFECYCLES.READY ||
|
|
204
188
|
context.retiring
|
|
205
189
|
) {
|
|
206
|
-
await
|
|
190
|
+
await lifecycle.retire(attached);
|
|
207
191
|
return { record: null, retiredThreadId: attached.threadId };
|
|
208
192
|
}
|
|
209
193
|
return { record: attached, retiredThreadId: "" };
|
|
210
194
|
}
|
|
211
195
|
|
|
212
196
|
async function restoreCodexAppServerHelperThreads(projectRuntimeRoot = "", context = {}) {
|
|
213
|
-
const previous =
|
|
197
|
+
const previous = lifecycle.restores.get(projectRuntimeRoot) || Promise.resolve();
|
|
214
198
|
// Every failure, including an unreadable ledger or a refused host notice,
|
|
215
199
|
// is reported in the result. Callers that require restoration assert it;
|
|
216
200
|
// cleanup and reconciliation continue with the records that did restore.
|
|
217
201
|
const restore = previous.catch(() => null).then(async () => {
|
|
218
|
-
const ledger =
|
|
202
|
+
const ledger = lifecycle.ledger(projectRuntimeRoot);
|
|
219
203
|
const failed = [];
|
|
220
204
|
let listed = { failures: [], records: [] };
|
|
221
205
|
try {
|
|
222
206
|
listed = await ledger.readAll();
|
|
223
207
|
} catch (error) {
|
|
224
|
-
failed.push(
|
|
208
|
+
failed.push(lifecycle.failure({ projectRuntimeRoot }, error));
|
|
225
209
|
}
|
|
226
210
|
failed.push(...listed.failures.map((failure) => ({
|
|
227
211
|
...failure,
|
|
@@ -244,13 +228,13 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
244
228
|
retiredBySession.set(record.sessionId, retired);
|
|
245
229
|
}
|
|
246
230
|
} catch (error) {
|
|
247
|
-
failed.push(
|
|
231
|
+
failed.push(lifecycle.failure(record, error));
|
|
248
232
|
}
|
|
249
233
|
}
|
|
250
234
|
try {
|
|
251
235
|
await context.publish({ records, failed, retiredBySession, normalizedSessionId });
|
|
252
236
|
} catch (error) {
|
|
253
|
-
failed.push(
|
|
237
|
+
failed.push(lifecycle.failure({ projectRuntimeRoot }, error));
|
|
254
238
|
}
|
|
255
239
|
return {
|
|
256
240
|
failed,
|
|
@@ -260,12 +244,12 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
260
244
|
retiredThreadIds: [...new Set(retiredThreadIds)]
|
|
261
245
|
};
|
|
262
246
|
});
|
|
263
|
-
|
|
247
|
+
lifecycle.restores.set(projectRuntimeRoot, restore);
|
|
264
248
|
try {
|
|
265
249
|
return await restore;
|
|
266
250
|
} finally {
|
|
267
|
-
if (
|
|
268
|
-
|
|
251
|
+
if (lifecycle.restores.get(projectRuntimeRoot) === restore) {
|
|
252
|
+
lifecycle.restores.delete(projectRuntimeRoot);
|
|
269
253
|
}
|
|
270
254
|
}
|
|
271
255
|
}
|
|
@@ -275,10 +259,10 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
275
259
|
projectRuntimeRoot = "",
|
|
276
260
|
prepareProvider
|
|
277
261
|
} = {}) {
|
|
278
|
-
const ledger =
|
|
262
|
+
const ledger = lifecycle.ledger(projectRuntimeRoot);
|
|
279
263
|
const listed = await ledger.readAll();
|
|
280
264
|
if (listed.failures.length > 0) {
|
|
281
|
-
throw
|
|
265
|
+
throw lifecycle.ownershipError(
|
|
282
266
|
`${applicationName} cannot inventory helper threads while durable ownership is malformed.`,
|
|
283
267
|
{ failed: listed.failures, sessionId }
|
|
284
268
|
);
|
|
@@ -296,11 +280,11 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
296
280
|
) {
|
|
297
281
|
continue;
|
|
298
282
|
}
|
|
299
|
-
const record =
|
|
300
|
-
|
|
283
|
+
const record = lifecycle.threads.get(
|
|
284
|
+
lifecycle.key(durable)
|
|
301
285
|
);
|
|
302
286
|
if (!record || record.ownershipId !== durable.ownershipId) {
|
|
303
|
-
throw
|
|
287
|
+
throw lifecycle.ownershipError(
|
|
304
288
|
`${applicationName} cannot retire missing helper ownership without its verified controller record.`,
|
|
305
289
|
{ sessionId, threadId: durable.threadId }
|
|
306
290
|
);
|
|
@@ -312,7 +296,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
312
296
|
continue;
|
|
313
297
|
}
|
|
314
298
|
try {
|
|
315
|
-
await
|
|
299
|
+
await lifecycle.remove(record);
|
|
316
300
|
} catch (error) {
|
|
317
301
|
if (error?.code === `${errorPrefix}codex_helper_thread_unavailable`) {
|
|
318
302
|
continue;
|
|
@@ -343,15 +327,9 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
343
327
|
};
|
|
344
328
|
}
|
|
345
329
|
provider ||= await providerOwner.ensureSession(prepared.sessionContext);
|
|
346
|
-
if (typeof provider.listHelperThreads !== "function") {
|
|
347
|
-
throw codexAppServerHelperOwnershipError(
|
|
348
|
-
"The Codex helper provider cannot authoritatively inventory its threads.",
|
|
349
|
-
{ sessionId }
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
330
|
const inventory = await provider.listHelperThreads();
|
|
353
331
|
if (!Array.isArray(inventory?.threadIds)) {
|
|
354
|
-
throw
|
|
332
|
+
throw lifecycle.ownershipError(
|
|
355
333
|
"The Codex helper provider returned an invalid thread inventory.",
|
|
356
334
|
{ sessionId }
|
|
357
335
|
);
|
|
@@ -366,7 +344,7 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
366
344
|
await deleteCodexAppServerThread({ provider, threadId });
|
|
367
345
|
} catch (error) {
|
|
368
346
|
if (error?.code !== "codex_helper_thread_delete_unconfirmed") throw error;
|
|
369
|
-
throw
|
|
347
|
+
throw lifecycle.ownershipError(
|
|
370
348
|
"Codex did not confirm deletion of an unowned helper thread.",
|
|
371
349
|
{ sessionId, threadId }
|
|
372
350
|
);
|
|
@@ -386,9 +364,9 @@ export function createCodexHelperThreadRecovery(lifecycle, {
|
|
|
386
364
|
};
|
|
387
365
|
}
|
|
388
366
|
|
|
389
|
-
return {
|
|
367
|
+
return Object.freeze({
|
|
390
368
|
restoreThread: restoreCodexAppServerHelperThread,
|
|
391
369
|
restoreThreads: restoreCodexAppServerHelperThreads,
|
|
392
370
|
reconcileRuntimeUnlocked: reconcileCodexAppServerHelperRuntimeUnlocked
|
|
393
|
-
};
|
|
371
|
+
});
|
|
394
372
|
}
|
|
@@ -1,28 +1,86 @@
|
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
1
2
|
import crypto from "node:crypto";
|
|
2
|
-
import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
+
import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
4
|
import { createCodexHelperOwnershipError, prepareDetachedCodexHelperThread } from "./codexHelperExecution.js";
|
|
4
|
-
import { deleteCodexAppServerHelperThread } from "./
|
|
5
|
-
import { directoryExists } from "./
|
|
5
|
+
import { deleteCodexAppServerHelperThread } from "./codexProviderThreads.js";
|
|
6
|
+
import { directoryExists } from "./codexRuntimeInventory.js";
|
|
6
7
|
import { createCodexHelperThreadRecovery } from "./codexHelperThreadRecovery.js";
|
|
7
|
-
import { codexAppServerErrorMessage as errorMessage
|
|
8
|
+
import { codexAppServerErrorMessage as errorMessage } from "./codexTurnState.js";
|
|
8
9
|
import {
|
|
9
10
|
CODEX_HELPER_THREAD_LEDGER_SCHEMA_VERSION,
|
|
10
11
|
CODEX_HELPER_THREAD_LIFECYCLES
|
|
11
|
-
} from "./
|
|
12
|
+
} from "./codexHelperThreadRecord.js";
|
|
12
13
|
|
|
13
14
|
const CODEX_APP_SERVER_PROVIDER_ID = "codex_app_server";
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
function codexAppServerHelperThreadKey({
|
|
17
|
+
projectRuntimeRoot = "",
|
|
18
|
+
sessionId = "",
|
|
19
|
+
threadId = ""
|
|
20
|
+
} = {}) {
|
|
21
|
+
return [
|
|
22
|
+
normalizeText(projectRuntimeRoot),
|
|
23
|
+
normalizeText(sessionId),
|
|
24
|
+
normalizeText(threadId)
|
|
25
|
+
].join("\u001f");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function codexAppServerProviderKeyFingerprint(providerKey = "") {
|
|
29
|
+
return `sha256:${crypto.createHash("sha256")
|
|
30
|
+
.update(normalizeText(providerKey))
|
|
31
|
+
.digest("hex")}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function codexAppServerRuntimeIdentityMatches(expected = {}, actual = {}, {
|
|
35
|
+
requireAccount = true,
|
|
36
|
+
requireEndpoint = true
|
|
37
|
+
} = {}) {
|
|
38
|
+
return (!requireAccount || expected.accountIdentitySignature === normalizeText(actual.accountIdentitySignature)) &&
|
|
39
|
+
(!requireEndpoint || expected.endpoint === normalizeText(actual.endpoint)) &&
|
|
40
|
+
expected.executionMode === normalizeText(actual.executionMode) &&
|
|
41
|
+
expected.executionContextHash === normalizeText(actual.executionContextHash) &&
|
|
42
|
+
expected.provider === normalizeText(actual.provider) &&
|
|
43
|
+
expected.runtimeDir === normalizeText(actual.runtimeDir) &&
|
|
44
|
+
expected.runtimesHash === normalizeText(actual.runtimesHash) &&
|
|
45
|
+
expected.terminalEnvHash === normalizeText(actual.terminalEnvHash) &&
|
|
46
|
+
expected.toolHomeSource === normalizeText(actual.toolHomeSource) &&
|
|
47
|
+
expected.transport === normalizeText(actual.transport);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function codexAppServerHelperThreadOwnershipMatches(record = {}, {
|
|
51
|
+
providerKey = "",
|
|
52
|
+
runtime = {},
|
|
53
|
+
server = null
|
|
54
|
+
} = {}, options = {}) {
|
|
55
|
+
return record.identity?.providerId === normalizeText(record.executionProfile?.providerId) &&
|
|
56
|
+
record.identity?.providerKeyFingerprint === codexAppServerProviderKeyFingerprint(providerKey) &&
|
|
57
|
+
record.identity?.transportId === CODEX_APP_SERVER_PROVIDER_ID &&
|
|
58
|
+
codexAppServerRuntimeIdentityMatches(record.identity?.runtime, runtime, options) &&
|
|
59
|
+
(
|
|
60
|
+
options.requireServer !== true ||
|
|
61
|
+
record.identity?.server?.userAgent === normalizeText(server?.userAgent)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ignoreHelperThreadRetired() {}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Internal facet of the run owner. The application supplies policy and authorized scope inputs; native ownership,
|
|
69
|
+
* pending work and retirement have one owner.
|
|
70
|
+
* @param {object} dependencies
|
|
71
|
+
* @param {import("./codexProviderOwner.js").CodexAppServerProviderOwner} dependencies.providerOwner
|
|
72
|
+
* @param {ReturnType<typeof import("./codexHelperThreadLedger.js").createCodexHelperThreadLedgerOwner>} dependencies.ledgerOwner
|
|
73
|
+
* @param {string} [dependencies.applicationName]
|
|
74
|
+
* @param {string} [dependencies.errorPrefix]
|
|
75
|
+
*/
|
|
17
76
|
export function createCodexHelperThreadLifecycle({
|
|
18
77
|
providerOwner,
|
|
19
78
|
ledgerOwner,
|
|
20
79
|
applicationName = "Application",
|
|
21
80
|
errorPrefix = ""
|
|
22
|
-
}
|
|
81
|
+
}) {
|
|
23
82
|
const codexAppServerProviders = providerOwner.providers;
|
|
24
83
|
const {
|
|
25
|
-
createCodexHelperThreadLedger: codexHelperThreadLedgerFactory,
|
|
26
84
|
defineCodexHelperThreadRecord
|
|
27
85
|
} = ledgerOwner;
|
|
28
86
|
const codexAppServerHelperThreads = new Map();
|
|
@@ -33,18 +91,6 @@ export function createCodexHelperThreadLifecycle({
|
|
|
33
91
|
const codexAppServerHelperTurnStarts = new Map();
|
|
34
92
|
const codexAppServerHelperThreadRestores = new Map();
|
|
35
93
|
|
|
36
|
-
function codexAppServerHelperThreadKey({
|
|
37
|
-
projectRuntimeRoot = "",
|
|
38
|
-
sessionId = "",
|
|
39
|
-
threadId = ""
|
|
40
|
-
} = {}) {
|
|
41
|
-
return [
|
|
42
|
-
normalizeText(projectRuntimeRoot),
|
|
43
|
-
normalizeText(sessionId),
|
|
44
|
-
normalizeText(threadId)
|
|
45
|
-
].join("\u001f");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
94
|
async function withCodexAppServerHelperProjectOperation(
|
|
49
95
|
projectRuntimeRoot = "",
|
|
50
96
|
operation
|
|
@@ -68,13 +114,10 @@ export function createCodexHelperThreadLifecycle({
|
|
|
68
114
|
}
|
|
69
115
|
|
|
70
116
|
function codexAppServerHelperThreadUnavailableError(threadId = "") {
|
|
71
|
-
const error = new
|
|
72
|
-
"This low-cost assistant thread is no longer available. Start the background task again instead of reusing another assistant conversation."
|
|
117
|
+
const error = new AppError(409,
|
|
118
|
+
"This low-cost assistant thread is no longer available. Start the background task again instead of reusing another assistant conversation.",
|
|
119
|
+
{ code: `${errorPrefix}codex_helper_thread_unavailable`, messageCode: "assistant.codex.helperThreadUnavailable" }
|
|
73
120
|
);
|
|
74
|
-
error.code = `${errorPrefix}codex_helper_thread_unavailable`;
|
|
75
|
-
error.statusCode = 409;
|
|
76
|
-
error.messageCode = "assistant.codex.helperThreadUnavailable";
|
|
77
|
-
error.messageParams = {};
|
|
78
121
|
error.threadId = normalizeText(threadId);
|
|
79
122
|
return error;
|
|
80
123
|
}
|
|
@@ -148,7 +191,7 @@ export function createCodexHelperThreadLifecycle({
|
|
|
148
191
|
const normalizedProjectRuntimeRoot = normalizeText(projectRuntimeRoot);
|
|
149
192
|
let ledger = codexAppServerHelperThreadLedgers.get(normalizedProjectRuntimeRoot);
|
|
150
193
|
if (!ledger) {
|
|
151
|
-
ledger =
|
|
194
|
+
ledger = ledgerOwner.createCodexHelperThreadLedger({
|
|
152
195
|
projectRuntimeRoot: normalizedProjectRuntimeRoot
|
|
153
196
|
});
|
|
154
197
|
codexAppServerHelperThreadLedgers.set(normalizedProjectRuntimeRoot, ledger);
|
|
@@ -165,20 +208,11 @@ export function createCodexHelperThreadLifecycle({
|
|
|
165
208
|
return "";
|
|
166
209
|
}
|
|
167
210
|
|
|
168
|
-
function codexAppServerProviderKeyFingerprint(providerKey = "") {
|
|
169
|
-
return `sha256:${crypto.createHash("sha256")
|
|
170
|
-
.update(normalizeText(providerKey))
|
|
171
|
-
.digest("hex")}`;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
211
|
async function codexAppServerHelperThreadIdentity({
|
|
175
212
|
executionProfile = null,
|
|
176
213
|
provider = null
|
|
177
214
|
} = {}) {
|
|
178
|
-
if (
|
|
179
|
-
typeof provider?.currentRuntimeInfo !== "function" ||
|
|
180
|
-
typeof provider?.currentServerInfo !== "function"
|
|
181
|
-
) {
|
|
215
|
+
if (!provider) {
|
|
182
216
|
throw new Error("Codex provider cannot prove durable helper thread ownership.");
|
|
183
217
|
}
|
|
184
218
|
const runtime = await provider.currentRuntimeInfo();
|
|
@@ -209,7 +243,7 @@ export function createCodexHelperThreadLifecycle({
|
|
|
209
243
|
...durable,
|
|
210
244
|
durable,
|
|
211
245
|
ledger,
|
|
212
|
-
onRetired,
|
|
246
|
+
onRetired: onRetired || ignoreHelperThreadRetired,
|
|
213
247
|
provider
|
|
214
248
|
});
|
|
215
249
|
codexAppServerHelperThreads.set(key, record);
|
|
@@ -313,7 +347,7 @@ export function createCodexHelperThreadLifecycle({
|
|
|
313
347
|
}
|
|
314
348
|
codexAppServerHelperThreadCleanups.delete(key);
|
|
315
349
|
codexAppServerHelperThreads.delete(key);
|
|
316
|
-
record.onRetired
|
|
350
|
+
record.onRetired({ threadId: record.threadId });
|
|
317
351
|
return true;
|
|
318
352
|
}
|
|
319
353
|
|
|
@@ -351,22 +385,23 @@ export function createCodexHelperThreadLifecycle({
|
|
|
351
385
|
}
|
|
352
386
|
|
|
353
387
|
function codexAppServerHelperThreadCleanupError(record = {}, error = null, interruptError = null) {
|
|
354
|
-
const failure = new
|
|
355
|
-
`${applicationName} could not retire a low-cost assistant thread. Retry cleanup before shutting down its Codex provider
|
|
388
|
+
const failure = new AppError(503,
|
|
389
|
+
`${applicationName} could not retire a low-cost assistant thread. Retry cleanup before shutting down its Codex provider.`,
|
|
390
|
+
{
|
|
391
|
+
code: `${errorPrefix}codex_helper_thread_cleanup_failed`,
|
|
392
|
+
details: {
|
|
393
|
+
cleanupError: errorMessage(error, "Codex helper thread deletion failed."),
|
|
394
|
+
...(interruptError
|
|
395
|
+
? { interruptError: errorMessage(interruptError, "Codex helper turn interruption failed.") }
|
|
396
|
+
: {}),
|
|
397
|
+
retryable: true,
|
|
398
|
+
sessionId: normalizeText(record.sessionId),
|
|
399
|
+
threadId: normalizeText(record.threadId),
|
|
400
|
+
turnId: normalizeText(record.turnId)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
356
403
|
);
|
|
357
|
-
failure.code = `${errorPrefix}codex_helper_thread_cleanup_failed`;
|
|
358
|
-
failure.statusCode = 503;
|
|
359
404
|
failure.retryable = true;
|
|
360
|
-
failure.details = {
|
|
361
|
-
cleanupError: errorMessage(error, "Codex helper thread deletion failed."),
|
|
362
|
-
...(interruptError
|
|
363
|
-
? { interruptError: errorMessage(interruptError, "Codex helper turn interruption failed.") }
|
|
364
|
-
: {}),
|
|
365
|
-
retryable: true,
|
|
366
|
-
sessionId: normalizeText(record.sessionId),
|
|
367
|
-
threadId: normalizeText(record.threadId),
|
|
368
|
-
turnId: normalizeText(record.turnId)
|
|
369
|
-
};
|
|
370
405
|
return failure;
|
|
371
406
|
}
|
|
372
407
|
|
|
@@ -473,52 +508,16 @@ export function createCodexHelperThreadLifecycle({
|
|
|
473
508
|
if (result.ok !== false) {
|
|
474
509
|
return result;
|
|
475
510
|
}
|
|
476
|
-
const failure = new
|
|
477
|
-
`${applicationName} could not retire every low-cost assistant thread. Retry cleanup before shutting down Codex
|
|
511
|
+
const failure = new AppError(503,
|
|
512
|
+
`${applicationName} could not retire every low-cost assistant thread. Retry cleanup before shutting down Codex.`,
|
|
513
|
+
{ code: `${errorPrefix}codex_helper_thread_cleanup_failed`, details: { failed: result.failed, retryable: true } }
|
|
478
514
|
);
|
|
479
|
-
failure.code = `${errorPrefix}codex_helper_thread_cleanup_failed`;
|
|
480
|
-
failure.statusCode = 503;
|
|
481
515
|
failure.retryable = true;
|
|
482
|
-
failure.details = {
|
|
483
|
-
failed: result.failed,
|
|
484
|
-
retryable: true
|
|
485
|
-
};
|
|
486
516
|
throw failure;
|
|
487
517
|
}
|
|
488
518
|
|
|
489
519
|
const codexAppServerHelperOwnershipError = createCodexHelperOwnershipError({ applicationName, errorPrefix });
|
|
490
520
|
|
|
491
|
-
function codexAppServerRuntimeIdentityMatches(expected = {}, actual = {}, {
|
|
492
|
-
requireAccount = true,
|
|
493
|
-
requireEndpoint = true
|
|
494
|
-
} = {}) {
|
|
495
|
-
return (!requireAccount || expected.accountIdentitySignature === normalizeText(actual.accountIdentitySignature)) &&
|
|
496
|
-
(!requireEndpoint || expected.endpoint === normalizeText(actual.endpoint)) &&
|
|
497
|
-
expected.executionMode === normalizeText(actual.executionMode) &&
|
|
498
|
-
expected.executionContextHash === normalizeText(actual.executionContextHash) &&
|
|
499
|
-
expected.provider === normalizeText(actual.provider) &&
|
|
500
|
-
expected.runtimeDir === normalizeText(actual.runtimeDir) &&
|
|
501
|
-
expected.runtimesHash === normalizeText(actual.runtimesHash) &&
|
|
502
|
-
expected.terminalEnvHash === normalizeText(actual.terminalEnvHash) &&
|
|
503
|
-
expected.toolHomeSource === normalizeText(actual.toolHomeSource) &&
|
|
504
|
-
expected.transport === normalizeText(actual.transport);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function codexAppServerHelperThreadOwnershipMatches(record = {}, {
|
|
508
|
-
providerKey = "",
|
|
509
|
-
runtime = {},
|
|
510
|
-
server = null
|
|
511
|
-
} = {}, options = {}) {
|
|
512
|
-
return record.identity?.providerId === normalizeText(record.executionProfile?.providerId) &&
|
|
513
|
-
record.identity?.providerKeyFingerprint === codexAppServerProviderKeyFingerprint(providerKey) &&
|
|
514
|
-
record.identity?.transportId === CODEX_APP_SERVER_PROVIDER_ID &&
|
|
515
|
-
codexAppServerRuntimeIdentityMatches(record.identity?.runtime, runtime, options) &&
|
|
516
|
-
(
|
|
517
|
-
options.requireServer !== true ||
|
|
518
|
-
record.identity?.server?.userAgent === normalizeText(server?.userAgent)
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
521
|
function codexAppServerHelperFailure(record = null, error = null) {
|
|
523
522
|
return {
|
|
524
523
|
code: normalizeText(error?.code) || `${errorPrefix}codex_helper_ownership_blocked`,
|
|
@@ -646,3 +645,5 @@ export function createCodexHelperThreadLifecycle({
|
|
|
646
645
|
});
|
|
647
646
|
return Object.freeze(lifecycle);
|
|
648
647
|
}
|
|
648
|
+
|
|
649
|
+
/** @typedef {ReturnType<typeof createCodexHelperThreadLifecycle>} CodexHelperThreadLifecycle */
|