@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,672 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { isPlainRecord, normalizeNullableText } from "./normalize.js";
|
|
3
|
+
import { codexAppServerTextHasControlCharacters } from "./codexConfiguration.js";
|
|
4
|
+
|
|
5
|
+
const DISABLED_FEATURES = Object.freeze([
|
|
6
|
+
"apps",
|
|
7
|
+
"artifact",
|
|
8
|
+
"browser_use",
|
|
9
|
+
"browser_use_external",
|
|
10
|
+
"browser_use_full_cdp_access",
|
|
11
|
+
"code_mode",
|
|
12
|
+
"code_mode_host",
|
|
13
|
+
"computer_use",
|
|
14
|
+
"default_mode_request_user_input",
|
|
15
|
+
"deferred_executor",
|
|
16
|
+
"goals",
|
|
17
|
+
"hooks",
|
|
18
|
+
"image_generation",
|
|
19
|
+
"in_app_browser",
|
|
20
|
+
"memories",
|
|
21
|
+
"multi_agent",
|
|
22
|
+
"multi_agent_v2",
|
|
23
|
+
"plugins",
|
|
24
|
+
"psp",
|
|
25
|
+
"recommended_plugins",
|
|
26
|
+
"request_permissions_tool",
|
|
27
|
+
"shell_tool",
|
|
28
|
+
"skill_mcp_dependency_install",
|
|
29
|
+
"skill_search",
|
|
30
|
+
"tool_call_mcp_elicitation",
|
|
31
|
+
"tool_suggest",
|
|
32
|
+
"unified_exec",
|
|
33
|
+
"unified_exec_zsh_fork",
|
|
34
|
+
"view_image"
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
function deepFreezeCodexAppServerHelperConfig(value) {
|
|
38
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value)) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
for (const nested of Object.values(value)) {
|
|
42
|
+
deepFreezeCodexAppServerHelperConfig(nested);
|
|
43
|
+
}
|
|
44
|
+
return Object.freeze(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function codexAppServerIsolationPolicyError(message = "", details = {}) {
|
|
48
|
+
const error = new Error(normalizeNullableText(message) || "Codex cannot prove the helper execution policy.");
|
|
49
|
+
error.code = "codex_policy_unenforceable";
|
|
50
|
+
error.details = Object.freeze({ ...details });
|
|
51
|
+
for (const [name, value] of Object.entries(error.details)) {
|
|
52
|
+
if (!["code", "details", "message", "name"].includes(name)) {
|
|
53
|
+
error[name] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Native helper isolation verification; hosts supply their profile policy.
|
|
61
|
+
* @param {object} [policy]
|
|
62
|
+
* @param {string} [policy.clientName]
|
|
63
|
+
* @param {string} [policy.minimumVersion]
|
|
64
|
+
* @param {readonly string[]} [policy.disabledFeatures]
|
|
65
|
+
* @param {Record<string, number>} [policy.limits]
|
|
66
|
+
* @param {(message?: string, details?: Record<string, unknown>) => Error} [policy.createError]
|
|
67
|
+
*/
|
|
68
|
+
export function createCodexAppServerIsolation({
|
|
69
|
+
clientName = "jskit",
|
|
70
|
+
minimumVersion = "0.151.0",
|
|
71
|
+
disabledFeatures = DISABLED_FEATURES,
|
|
72
|
+
limits: {
|
|
73
|
+
userAgentMaxLength = 512,
|
|
74
|
+
mcpServerMaxCount = 128,
|
|
75
|
+
mcpServerNameMaxLength = 256,
|
|
76
|
+
configResponseMaxBytes = 256 * 1024,
|
|
77
|
+
hookMaxCount = 256,
|
|
78
|
+
hookErrorMaxCount = 256,
|
|
79
|
+
hookFieldMaxLength = 2048,
|
|
80
|
+
hookFingerprintMaxLength = 256 * 1024,
|
|
81
|
+
hookResponseMaxBytes = 512 * 1024
|
|
82
|
+
} = {},
|
|
83
|
+
createError = codexAppServerIsolationPolicyError
|
|
84
|
+
} = {}) {
|
|
85
|
+
const codexAppServerHelperIsolationConfigs = new WeakSet();
|
|
86
|
+
|
|
87
|
+
function assertCodexAppServerHelperResponseBounded(value, maxBytes, label) {
|
|
88
|
+
let bytes = 0;
|
|
89
|
+
try {
|
|
90
|
+
bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
91
|
+
} catch {
|
|
92
|
+
throw createError(
|
|
93
|
+
`Codex helper execution received an invalid ${label}.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (bytes > maxBytes) {
|
|
97
|
+
throw createError(
|
|
98
|
+
`Codex helper execution received an oversized ${label}.`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function codexAppServerHelperConnectionGeneration(provider) {
|
|
104
|
+
// readable-check: allow method-probe - safety invariant: a provider without a connection generation would let helper policy run on an unverified connection
|
|
105
|
+
if (typeof provider?.currentConnectionGeneration !== "function") {
|
|
106
|
+
throw createError(
|
|
107
|
+
"Codex helper execution cannot verify the app-server connection generation."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const generation = provider.currentConnectionGeneration();
|
|
111
|
+
if (!Number.isSafeInteger(generation) || generation <= 0) {
|
|
112
|
+
throw createError(
|
|
113
|
+
"Codex helper execution found no active app-server connection."
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return generation;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function codexAppServerHelperExecutionContext(provider) {
|
|
120
|
+
// readable-check: allow method-probe - safety invariant: only the dedicated helper provider reports an isolated execution context
|
|
121
|
+
if (typeof provider?.currentHelperExecutionContext !== "function") {
|
|
122
|
+
throw createError(
|
|
123
|
+
"Codex helper execution requires its dedicated isolated provider."
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
const context = await provider.currentHelperExecutionContext();
|
|
127
|
+
const cwd = normalizeNullableText(context?.cwd);
|
|
128
|
+
const accountIdentitySignature = normalizeNullableText(context?.accountIdentitySignature);
|
|
129
|
+
if (
|
|
130
|
+
context?.executionMode !== "helper" ||
|
|
131
|
+
!cwd ||
|
|
132
|
+
!path.isAbsolute(cwd) ||
|
|
133
|
+
!/^sha256:[a-f0-9]{64}$/u.test(accountIdentitySignature)
|
|
134
|
+
) {
|
|
135
|
+
throw createError(
|
|
136
|
+
"Codex helper runtime isolation could not be verified."
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return Object.freeze({
|
|
140
|
+
accountIdentitySignature,
|
|
141
|
+
cwd,
|
|
142
|
+
executionMode: "helper"
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function codexAppServerSemanticVersionParts(value = "") {
|
|
147
|
+
const match = normalizeNullableText(value).match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u);
|
|
148
|
+
if (!match) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
const parts = match.slice(1).map((part) => Number.parseInt(part, 10));
|
|
152
|
+
return parts.every(Number.isSafeInteger) ? parts : null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function codexAppServerUserAgentVersionParts(value = "") {
|
|
156
|
+
const userAgent = normalizeNullableText(value);
|
|
157
|
+
if (
|
|
158
|
+
!userAgent ||
|
|
159
|
+
userAgent.length > userAgentMaxLength ||
|
|
160
|
+
codexAppServerTextHasControlCharacters(userAgent)
|
|
161
|
+
) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const prefix = `${clientName}/`;
|
|
165
|
+
if (!userAgent.startsWith(prefix)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const match = userAgent.slice(prefix.length).match(/^((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*))(?:$|[ \t].*$)/u);
|
|
169
|
+
return match ? codexAppServerSemanticVersionParts(match[1]) : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function assertCodexAppServerHelperCompatibility(provider) {
|
|
173
|
+
// readable-check: allow method-probe - safety invariant: a provider that cannot report its app-server version would run helpers on an unchecked version
|
|
174
|
+
if (typeof provider?.currentServerInfo !== "function") {
|
|
175
|
+
throw createError(
|
|
176
|
+
"Codex helper execution cannot verify the app-server version. Update Codex and retry.",
|
|
177
|
+
{ minimumVersion }
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
const userAgent = normalizeNullableText(provider.currentServerInfo()?.userAgent);
|
|
181
|
+
const actualParts = codexAppServerUserAgentVersionParts(userAgent);
|
|
182
|
+
if (!actualParts) {
|
|
183
|
+
throw createError(
|
|
184
|
+
"Codex helper execution received an unrecognised app-server version. Update Codex and retry.",
|
|
185
|
+
{
|
|
186
|
+
minimumVersion
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const actualVersion = actualParts.join(".");
|
|
191
|
+
const minimumParts = codexAppServerSemanticVersionParts(minimumVersion);
|
|
192
|
+
const differingPart = actualParts.findIndex((part, index) => part !== minimumParts[index]);
|
|
193
|
+
if (differingPart !== -1 && actualParts[differingPart] < minimumParts[differingPart]) {
|
|
194
|
+
throw createError(
|
|
195
|
+
`Codex helper execution requires app-server ${minimumVersion} or newer; current version is ${actualVersion}. Update Codex and retry.`,
|
|
196
|
+
{
|
|
197
|
+
actualVersion,
|
|
198
|
+
minimumVersion
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return Object.freeze({
|
|
203
|
+
minimumVersion,
|
|
204
|
+
version: actualVersion
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function codexAppServerHelperMcpServerNames(configResult = null) {
|
|
209
|
+
if (!isPlainRecord(configResult?.config)) {
|
|
210
|
+
throw createError(
|
|
211
|
+
"Codex helper execution could not read the effective app-server configuration."
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const servers = configResult.config.mcp_servers;
|
|
215
|
+
if (servers === undefined || servers === null) {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
// Only the MCP inventory participates in this isolation check. The native
|
|
219
|
+
// response can also contain large model catalogues and configuration layers.
|
|
220
|
+
assertCodexAppServerHelperResponseBounded(
|
|
221
|
+
servers,
|
|
222
|
+
configResponseMaxBytes,
|
|
223
|
+
"MCP configuration"
|
|
224
|
+
);
|
|
225
|
+
if (!isPlainRecord(servers)) {
|
|
226
|
+
throw createError(
|
|
227
|
+
"Codex helper execution received an invalid MCP server configuration."
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const names = Object.keys(servers);
|
|
231
|
+
if (
|
|
232
|
+
names.length > mcpServerMaxCount ||
|
|
233
|
+
names.some((name) => (
|
|
234
|
+
!name ||
|
|
235
|
+
name.length > mcpServerNameMaxLength ||
|
|
236
|
+
codexAppServerTextHasControlCharacters(name)
|
|
237
|
+
))
|
|
238
|
+
) {
|
|
239
|
+
throw createError(
|
|
240
|
+
"Codex helper execution received an oversized or invalid MCP server inventory."
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return names.sort();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function codexAppServerHelperHookState(result = null, cwd = "") {
|
|
247
|
+
assertCodexAppServerHelperResponseBounded(
|
|
248
|
+
result,
|
|
249
|
+
hookResponseMaxBytes,
|
|
250
|
+
"hook inventory"
|
|
251
|
+
);
|
|
252
|
+
if (!Array.isArray(result?.data)) {
|
|
253
|
+
throw createError(
|
|
254
|
+
"Codex helper execution could not enumerate app-server hooks."
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
if (result.data.length !== 1) {
|
|
258
|
+
throw createError(
|
|
259
|
+
"Codex helper execution received an unexpected hook inventory."
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
const record = result.data[0];
|
|
263
|
+
if (
|
|
264
|
+
normalizeNullableText(record?.cwd) !== cwd ||
|
|
265
|
+
normalizeNullableText(record?.cwd).length > hookFieldMaxLength ||
|
|
266
|
+
!Array.isArray(record.hooks) ||
|
|
267
|
+
!Array.isArray(record.errors) ||
|
|
268
|
+
record.hooks.length > hookMaxCount ||
|
|
269
|
+
record.errors.length > hookErrorMaxCount
|
|
270
|
+
) {
|
|
271
|
+
throw createError(
|
|
272
|
+
"Codex helper execution received an incomplete hook inventory."
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
if (record.errors.length > 0) {
|
|
276
|
+
throw createError(
|
|
277
|
+
"Codex helper execution cannot continue while hook discovery has errors.",
|
|
278
|
+
{ hookErrorCount: record.errors.length }
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
const hooks = record.hooks.map((hook) => {
|
|
282
|
+
const key = normalizeNullableText(hook?.key);
|
|
283
|
+
const currentHash = normalizeNullableText(hook?.currentHash);
|
|
284
|
+
const handlerType = normalizeNullableText(hook?.handlerType);
|
|
285
|
+
const sourcePath = normalizeNullableText(hook?.sourcePath);
|
|
286
|
+
if (
|
|
287
|
+
!key ||
|
|
288
|
+
[key, currentHash, handlerType, sourcePath].some((field) => (
|
|
289
|
+
field.length > hookFieldMaxLength ||
|
|
290
|
+
codexAppServerTextHasControlCharacters(field)
|
|
291
|
+
))
|
|
292
|
+
) {
|
|
293
|
+
throw createError(
|
|
294
|
+
"Codex helper execution found an invalid hook inventory entry."
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (hook?.isManaged === true && hook?.enabled === true) {
|
|
298
|
+
throw createError(
|
|
299
|
+
"Codex helper execution cannot disable a managed hook."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
currentHash,
|
|
304
|
+
enabled: hook?.enabled === true,
|
|
305
|
+
handlerType,
|
|
306
|
+
isManaged: hook?.isManaged === true,
|
|
307
|
+
key,
|
|
308
|
+
sourcePath
|
|
309
|
+
};
|
|
310
|
+
}).sort((left, right) => left.key.localeCompare(right.key));
|
|
311
|
+
const fingerprint = JSON.stringify(hooks);
|
|
312
|
+
if (fingerprint.length > hookFingerprintMaxLength) {
|
|
313
|
+
throw createError(
|
|
314
|
+
"Codex helper execution received an oversized hook inventory."
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
fingerprint,
|
|
319
|
+
hookKeys: hooks.filter((hook) => !hook.isManaged).map((hook) => hook.key)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function codexAppServerHelperIsolationConfig({
|
|
324
|
+
effort = "",
|
|
325
|
+
summary = "",
|
|
326
|
+
hookKeys = [],
|
|
327
|
+
mcpServerNames = []
|
|
328
|
+
} = {}) {
|
|
329
|
+
const config = {
|
|
330
|
+
features: Object.fromEntries(
|
|
331
|
+
disabledFeatures.map((feature) => [feature, false])
|
|
332
|
+
),
|
|
333
|
+
hooks: {
|
|
334
|
+
state: Object.fromEntries(hookKeys.map((key) => [key, { enabled: false }]))
|
|
335
|
+
},
|
|
336
|
+
include_apps_instructions: false,
|
|
337
|
+
include_collaboration_mode_instructions: false,
|
|
338
|
+
include_environment_context: false,
|
|
339
|
+
include_permissions_instructions: false,
|
|
340
|
+
...(effort ? { model_reasoning_effort: effort } : {}),
|
|
341
|
+
...(summary ? { model_reasoning_summary: summary } : {}),
|
|
342
|
+
mcp_servers: Object.fromEntries(
|
|
343
|
+
mcpServerNames.map((name) => [name, { enabled: false }])
|
|
344
|
+
),
|
|
345
|
+
memories: {
|
|
346
|
+
dedicated_tools: false,
|
|
347
|
+
generate_memories: false,
|
|
348
|
+
use_memories: false
|
|
349
|
+
},
|
|
350
|
+
notify: [],
|
|
351
|
+
orchestrator: {
|
|
352
|
+
mcp: {
|
|
353
|
+
enabled: false
|
|
354
|
+
},
|
|
355
|
+
skills: {
|
|
356
|
+
enabled: false
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
project_doc_max_bytes: 0,
|
|
360
|
+
shell_environment_policy: {
|
|
361
|
+
inherit: "none",
|
|
362
|
+
set: {}
|
|
363
|
+
},
|
|
364
|
+
skills: {
|
|
365
|
+
include_instructions: false
|
|
366
|
+
},
|
|
367
|
+
tools: {
|
|
368
|
+
experimental_request_user_input: {
|
|
369
|
+
enabled: false
|
|
370
|
+
},
|
|
371
|
+
update_plan: {
|
|
372
|
+
enabled: false
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
web_search: "disabled"
|
|
376
|
+
};
|
|
377
|
+
const frozenConfig = deepFreezeCodexAppServerHelperConfig(config);
|
|
378
|
+
codexAppServerHelperIsolationConfigs.add(frozenConfig);
|
|
379
|
+
return frozenConfig;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function isolationConfigFrom(configResult, hookResult, cwd, { effort = "", summary = "" } = {}) {
|
|
383
|
+
const mcpServerNames = codexAppServerHelperMcpServerNames(configResult);
|
|
384
|
+
const hookState = codexAppServerHelperHookState(hookResult, cwd);
|
|
385
|
+
return {
|
|
386
|
+
mcpServerNames,
|
|
387
|
+
hookState,
|
|
388
|
+
config: codexAppServerHelperIsolationConfig({
|
|
389
|
+
effort,
|
|
390
|
+
summary,
|
|
391
|
+
hookKeys: hookState.hookKeys,
|
|
392
|
+
mcpServerNames
|
|
393
|
+
})
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async function codexAppServerHelperIsolationState(provider, { effort = "", summary = "" } = {}) {
|
|
398
|
+
assertCodexAppServerHelperCompatibility(provider);
|
|
399
|
+
// readable-check: allow method-probe - safety invariant: without configuration and hook inventories the helper surface cannot be proved tool-free
|
|
400
|
+
if (typeof provider?.readConfig !== "function" || typeof provider?.listHooks !== "function") {
|
|
401
|
+
throw createError(
|
|
402
|
+
"Codex helper execution cannot inventory configuration and hooks."
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const executionContext = await codexAppServerHelperExecutionContext(provider);
|
|
406
|
+
const generation = codexAppServerHelperConnectionGeneration(provider);
|
|
407
|
+
const configResult = await provider.readConfig({
|
|
408
|
+
cwd: executionContext.cwd,
|
|
409
|
+
includeLayers: false
|
|
410
|
+
});
|
|
411
|
+
if (codexAppServerHelperConnectionGeneration(provider) !== generation) {
|
|
412
|
+
throw createError(
|
|
413
|
+
"Codex app-server reconnected during helper policy verification."
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
const hookResult = await provider.listHooks([executionContext.cwd]);
|
|
417
|
+
if (codexAppServerHelperConnectionGeneration(provider) !== generation) {
|
|
418
|
+
throw createError(
|
|
419
|
+
"Codex app-server reconnected during helper policy verification."
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
const { mcpServerNames, hookState, config } = isolationConfigFrom(configResult, hookResult, executionContext.cwd, {
|
|
423
|
+
effort,
|
|
424
|
+
summary
|
|
425
|
+
});
|
|
426
|
+
return Object.freeze({
|
|
427
|
+
accountIdentitySignature: executionContext.accountIdentitySignature,
|
|
428
|
+
config,
|
|
429
|
+
connectionGeneration: generation,
|
|
430
|
+
executionCwd: executionContext.cwd,
|
|
431
|
+
hookFingerprint: hookState.fingerprint,
|
|
432
|
+
hookKeys: Object.freeze([...hookState.hookKeys]),
|
|
433
|
+
mcpServerNames: Object.freeze([...mcpServerNames])
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function codexAppServerHelperIsolationMatches(left = null, right = null) {
|
|
438
|
+
return left?.accountIdentitySignature === right?.accountIdentitySignature &&
|
|
439
|
+
left?.connectionGeneration === right?.connectionGeneration &&
|
|
440
|
+
left?.executionCwd === right?.executionCwd &&
|
|
441
|
+
left?.hookFingerprint === right?.hookFingerprint &&
|
|
442
|
+
JSON.stringify(left?.mcpServerNames || []) === JSON.stringify(right?.mcpServerNames || []);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function codexAppServerHelperVerificationFailure(error, cleanupError, threadId = "") {
|
|
446
|
+
const normalizedThreadId = normalizeNullableText(threadId);
|
|
447
|
+
if (!cleanupError) {
|
|
448
|
+
error.codexAppServerHelperThreadId = normalizedThreadId;
|
|
449
|
+
error.codexAppServerHelperThreadRetired = true;
|
|
450
|
+
return error;
|
|
451
|
+
}
|
|
452
|
+
const failure = createError(
|
|
453
|
+
"Codex could not retire a helper thread after policy verification failed.",
|
|
454
|
+
{
|
|
455
|
+
cleanupFailed: true,
|
|
456
|
+
threadId: normalizedThreadId
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
failure.codexAppServerHelperThreadCleanupRequired = true;
|
|
460
|
+
failure.codexAppServerHelperThreadId = normalizedThreadId;
|
|
461
|
+
return failure;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function throwAfterCodexAppServerHelperVerificationFailure({
|
|
465
|
+
error = null,
|
|
466
|
+
provider = null,
|
|
467
|
+
threadId = ""
|
|
468
|
+
} = {}) {
|
|
469
|
+
let cleanupError = null;
|
|
470
|
+
try {
|
|
471
|
+
await provider.deleteThread(threadId);
|
|
472
|
+
} catch (caught) {
|
|
473
|
+
cleanupError = caught;
|
|
474
|
+
}
|
|
475
|
+
throw codexAppServerHelperVerificationFailure(error, cleanupError, threadId);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Re-inspect after the native thread exists; a changed surface retires it.
|
|
479
|
+
async function verifyHelperThread(provider, enforcement, thread, threadId, phase) {
|
|
480
|
+
try {
|
|
481
|
+
const verified = await codexAppServerHelperIsolationState(
|
|
482
|
+
provider,
|
|
483
|
+
{
|
|
484
|
+
effort: enforcement.config.model_reasoning_effort,
|
|
485
|
+
summary: enforcement.config.model_reasoning_summary
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
if (!codexAppServerHelperIsolationMatches(enforcement, verified)) {
|
|
489
|
+
throw createError(
|
|
490
|
+
`Codex execution surfaces changed while the helper thread was ${phase}.`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
return Object.freeze({
|
|
494
|
+
enforcement: verified,
|
|
495
|
+
thread,
|
|
496
|
+
threadId
|
|
497
|
+
});
|
|
498
|
+
} catch (error) {
|
|
499
|
+
await throwAfterCodexAppServerHelperVerificationFailure({
|
|
500
|
+
error,
|
|
501
|
+
provider,
|
|
502
|
+
threadId
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async function startCodexAppServerHelperThread(provider, prepare) {
|
|
508
|
+
// readable-check: allow method-probe - safety invariant: a helper thread that cannot be deleted would outlive a failed policy verification
|
|
509
|
+
if (typeof provider?.startThread !== "function" || typeof provider?.deleteThread !== "function") {
|
|
510
|
+
throw createError(
|
|
511
|
+
"Codex helper execution cannot own and clean up its app-server thread."
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
const prepared = await prepare();
|
|
515
|
+
const thread = await provider.startThread(prepared.settings);
|
|
516
|
+
const threadId = normalizeNullableText(thread?.id);
|
|
517
|
+
if (!threadId) {
|
|
518
|
+
throw createError(
|
|
519
|
+
"Codex app-server did not return a helper thread id."
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
return verifyHelperThread(provider, prepared.enforcement, thread, threadId, "starting");
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function resumeCodexAppServerHelperThread(provider, threadId, prepare) {
|
|
526
|
+
// readable-check: allow method-probe - safety invariant: a resumed helper thread that cannot be deleted would outlive a failed policy verification
|
|
527
|
+
if (typeof provider?.resumeThread !== "function" || typeof provider?.deleteThread !== "function") {
|
|
528
|
+
throw createError(
|
|
529
|
+
"Codex helper execution cannot safely resume and clean up its app-server thread."
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const normalizedThreadId = normalizeNullableText(threadId);
|
|
533
|
+
if (!normalizedThreadId) {
|
|
534
|
+
throw createError(
|
|
535
|
+
"Codex helper resume requires a controller-owned thread id."
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
const prepared = await prepare();
|
|
539
|
+
const enforcement = prepared.enforcement;
|
|
540
|
+
const thread = await provider.resumeThread(
|
|
541
|
+
normalizedThreadId,
|
|
542
|
+
prepared.settings
|
|
543
|
+
);
|
|
544
|
+
return verifyHelperThread(provider, enforcement, thread, normalizedThreadId, "resuming");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function hasConfiguration(config) {
|
|
548
|
+
return codexAppServerHelperIsolationConfigs.has(config);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function configuration({ configResult, hookResult, workdir, effort = "", summary = "" }) {
|
|
552
|
+
return isolationConfigFrom(configResult, hookResult, workdir, { effort, summary }).config;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Native Helper requests retain their verified configuration object and the
|
|
556
|
+
// distinct Start/Resume field sets. Profile policy is validated by the host.
|
|
557
|
+
function codexAppServerHelperThreadSettings({
|
|
558
|
+
approvalPolicy,
|
|
559
|
+
baseInstructions,
|
|
560
|
+
config,
|
|
561
|
+
cwd,
|
|
562
|
+
effectiveSettings,
|
|
563
|
+
sandbox,
|
|
564
|
+
systemPrompt
|
|
565
|
+
}) {
|
|
566
|
+
return {
|
|
567
|
+
allowProviderModelFallback: false,
|
|
568
|
+
approvalPolicy,
|
|
569
|
+
baseInstructions,
|
|
570
|
+
config,
|
|
571
|
+
cwd,
|
|
572
|
+
systemPrompt: systemPrompt || null,
|
|
573
|
+
dynamicTools: [],
|
|
574
|
+
environments: [],
|
|
575
|
+
model: effectiveSettings.model,
|
|
576
|
+
runtimeWorkspaceRoots: [],
|
|
577
|
+
sandbox,
|
|
578
|
+
selectedCapabilityRoots: []
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function codexAppServerHelperThreadStartSettings(settings, threadSource) {
|
|
583
|
+
return {
|
|
584
|
+
...settings,
|
|
585
|
+
sessionStartSource: "startup",
|
|
586
|
+
threadSource
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function codexAppServerHelperThreadResumeSettings(settings) {
|
|
591
|
+
return {
|
|
592
|
+
approvalPolicy: settings.approvalPolicy,
|
|
593
|
+
baseInstructions: settings.baseInstructions,
|
|
594
|
+
config: settings.config,
|
|
595
|
+
cwd: settings.cwd,
|
|
596
|
+
systemPrompt: settings.systemPrompt,
|
|
597
|
+
model: settings.model,
|
|
598
|
+
runtimeWorkspaceRoots: settings.runtimeWorkspaceRoots,
|
|
599
|
+
sandbox: settings.sandbox
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function codexAppServerHelperTurnSettings({
|
|
604
|
+
approvalPolicy,
|
|
605
|
+
cwd,
|
|
606
|
+
effectiveSettings,
|
|
607
|
+
outputSchema
|
|
608
|
+
}) {
|
|
609
|
+
const settings = {
|
|
610
|
+
approvalPolicy,
|
|
611
|
+
cwd,
|
|
612
|
+
environments: [],
|
|
613
|
+
model: effectiveSettings.model,
|
|
614
|
+
outputSchema,
|
|
615
|
+
runtimeWorkspaceRoots: [],
|
|
616
|
+
sandboxPolicy: {
|
|
617
|
+
networkAccess: false,
|
|
618
|
+
type: "readOnly"
|
|
619
|
+
},
|
|
620
|
+
summary: "none"
|
|
621
|
+
};
|
|
622
|
+
if (effectiveSettings.request.reasoning) {
|
|
623
|
+
settings.effort = effectiveSettings.thinking;
|
|
624
|
+
}
|
|
625
|
+
return settings;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return Object.freeze({
|
|
629
|
+
assertCompatibility: assertCodexAppServerHelperCompatibility,
|
|
630
|
+
executionContext: codexAppServerHelperExecutionContext,
|
|
631
|
+
inspect: codexAppServerHelperIsolationState,
|
|
632
|
+
configuration,
|
|
633
|
+
hasConfiguration,
|
|
634
|
+
threadSettings: codexAppServerHelperThreadSettings,
|
|
635
|
+
threadStartSettings: codexAppServerHelperThreadStartSettings,
|
|
636
|
+
threadResumeSettings: codexAppServerHelperThreadResumeSettings,
|
|
637
|
+
turnSettings: codexAppServerHelperTurnSettings,
|
|
638
|
+
start: startCodexAppServerHelperThread,
|
|
639
|
+
resume: resumeCodexAppServerHelperThread
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const codexConversationIsolation = createCodexAppServerIsolation();
|
|
644
|
+
|
|
645
|
+
/** Disable ambient tools while allowing the caller to add its authorized tools. */
|
|
646
|
+
export function codexToolFreeConfiguration(options) {
|
|
647
|
+
return structuredClone(codexConversationIsolation.configuration(options));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export async function readCodexToolFreeConfiguration(
|
|
651
|
+
provider = null,
|
|
652
|
+
workdir = "",
|
|
653
|
+
assertCompatibility = codexConversationIsolation.assertCompatibility
|
|
654
|
+
) {
|
|
655
|
+
assertCompatibility(provider);
|
|
656
|
+
// readable-check: allow method-probe - safety invariant: without these inventories an ephemeral conversation cannot be proved tool-free
|
|
657
|
+
if (typeof provider?.readConfig !== "function" || typeof provider?.listHooks !== "function" || typeof provider?.currentConnectionGeneration !== "function") {
|
|
658
|
+
throw new Error("Codex cannot verify tool isolation for this ephemeral conversation.");
|
|
659
|
+
}
|
|
660
|
+
const connectionGeneration = provider.currentConnectionGeneration();
|
|
661
|
+
if (!Number.isSafeInteger(connectionGeneration) || connectionGeneration <= 0) {
|
|
662
|
+
throw new Error("Codex has no active connection for this ephemeral conversation.");
|
|
663
|
+
}
|
|
664
|
+
const [configResult, hookResult] = await Promise.all([
|
|
665
|
+
provider.readConfig({ cwd: workdir, includeLayers: false }),
|
|
666
|
+
provider.listHooks([workdir])
|
|
667
|
+
]);
|
|
668
|
+
if (provider.currentConnectionGeneration() !== connectionGeneration) {
|
|
669
|
+
throw new Error("Codex reconnected while verifying this ephemeral conversation.");
|
|
670
|
+
}
|
|
671
|
+
return codexToolFreeConfiguration({ configResult, hookResult, workdir });
|
|
672
|
+
}
|