@kodelyth/codex 2026.5.42 → 2026.6.2

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.
Files changed (138) hide show
  1. package/package.json +17 -2
  2. package/doctor-contract-api.test.ts +0 -44
  3. package/doctor-contract-api.ts +0 -68
  4. package/harness.ts +0 -72
  5. package/index.test.ts +0 -230
  6. package/index.ts +0 -66
  7. package/media-understanding-provider.test.ts +0 -486
  8. package/media-understanding-provider.ts +0 -521
  9. package/prompt-overlay-runtime-contract.test.ts +0 -48
  10. package/prompt-overlay.ts +0 -21
  11. package/provider-catalog.ts +0 -83
  12. package/provider-discovery.ts +0 -45
  13. package/provider.test.ts +0 -384
  14. package/provider.ts +0 -243
  15. package/src/app-server/app-inventory-cache.test.ts +0 -176
  16. package/src/app-server/app-inventory-cache.ts +0 -324
  17. package/src/app-server/approval-bridge.test.ts +0 -1471
  18. package/src/app-server/approval-bridge.ts +0 -1211
  19. package/src/app-server/auth-bridge.test.ts +0 -1449
  20. package/src/app-server/auth-bridge.ts +0 -614
  21. package/src/app-server/auth-profile-runtime-contract.test.ts +0 -239
  22. package/src/app-server/capabilities.ts +0 -27
  23. package/src/app-server/client-factory.ts +0 -24
  24. package/src/app-server/client.test.ts +0 -563
  25. package/src/app-server/client.ts +0 -715
  26. package/src/app-server/compact.test.ts +0 -710
  27. package/src/app-server/compact.ts +0 -500
  28. package/src/app-server/computer-use.test.ts +0 -788
  29. package/src/app-server/computer-use.ts +0 -683
  30. package/src/app-server/config.test.ts +0 -879
  31. package/src/app-server/config.ts +0 -1038
  32. package/src/app-server/context-engine-projection.test.ts +0 -252
  33. package/src/app-server/context-engine-projection.ts +0 -403
  34. package/src/app-server/delivery-no-reply-runtime-contract.test.ts +0 -80
  35. package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
  36. package/src/app-server/dynamic-tool-profile.ts +0 -69
  37. package/src/app-server/dynamic-tools.test.ts +0 -1302
  38. package/src/app-server/dynamic-tools.ts +0 -623
  39. package/src/app-server/elicitation-bridge.test.ts +0 -1056
  40. package/src/app-server/elicitation-bridge.ts +0 -783
  41. package/src/app-server/event-projector.test.ts +0 -2668
  42. package/src/app-server/event-projector.ts +0 -2057
  43. package/src/app-server/image-payload-sanitizer.test.ts +0 -49
  44. package/src/app-server/image-payload-sanitizer.ts +0 -167
  45. package/src/app-server/klaw-owned-tool-runtime-contract.test.ts +0 -456
  46. package/src/app-server/local-runtime-attribution.ts +0 -39
  47. package/src/app-server/managed-binary.test.ts +0 -139
  48. package/src/app-server/managed-binary.ts +0 -193
  49. package/src/app-server/models.test.ts +0 -246
  50. package/src/app-server/models.ts +0 -172
  51. package/src/app-server/native-hook-relay.test.ts +0 -271
  52. package/src/app-server/native-hook-relay.ts +0 -150
  53. package/src/app-server/native-subagent-task-mirror.test.ts +0 -573
  54. package/src/app-server/native-subagent-task-mirror.ts +0 -497
  55. package/src/app-server/outcome-fallback-runtime-contract.test.ts +0 -404
  56. package/src/app-server/plugin-activation.test.ts +0 -336
  57. package/src/app-server/plugin-activation.ts +0 -283
  58. package/src/app-server/plugin-app-cache-key.ts +0 -74
  59. package/src/app-server/plugin-approval-roundtrip.ts +0 -122
  60. package/src/app-server/plugin-inventory.test.ts +0 -355
  61. package/src/app-server/plugin-inventory.ts +0 -357
  62. package/src/app-server/plugin-thread-config.test.ts +0 -865
  63. package/src/app-server/plugin-thread-config.ts +0 -455
  64. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
  65. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
  66. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
  67. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
  68. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
  69. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
  70. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
  71. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
  72. package/src/app-server/protocol-validators.test.ts +0 -75
  73. package/src/app-server/protocol-validators.ts +0 -203
  74. package/src/app-server/protocol.ts +0 -520
  75. package/src/app-server/rate-limit-cache.ts +0 -48
  76. package/src/app-server/rate-limits.test.ts +0 -202
  77. package/src/app-server/rate-limits.ts +0 -583
  78. package/src/app-server/request.ts +0 -73
  79. package/src/app-server/run-attempt.context-engine.test.ts +0 -1004
  80. package/src/app-server/run-attempt.test.ts +0 -9477
  81. package/src/app-server/run-attempt.ts +0 -4683
  82. package/src/app-server/run-attempt.vision-tools.test.ts +0 -35
  83. package/src/app-server/schema-normalization-runtime-contract.test.ts +0 -206
  84. package/src/app-server/session-binding.test.ts +0 -303
  85. package/src/app-server/session-binding.ts +0 -398
  86. package/src/app-server/session-history.ts +0 -44
  87. package/src/app-server/shared-client.test.ts +0 -589
  88. package/src/app-server/shared-client.ts +0 -289
  89. package/src/app-server/side-question.test.ts +0 -1175
  90. package/src/app-server/side-question.ts +0 -1007
  91. package/src/app-server/test-support.ts +0 -48
  92. package/src/app-server/thread-lifecycle.test.ts +0 -447
  93. package/src/app-server/thread-lifecycle.ts +0 -939
  94. package/src/app-server/thread-lifecycle.user-mcp-servers.test.ts +0 -442
  95. package/src/app-server/timeout.ts +0 -9
  96. package/src/app-server/tool-progress-normalization.ts +0 -77
  97. package/src/app-server/trajectory.test.ts +0 -205
  98. package/src/app-server/trajectory.ts +0 -365
  99. package/src/app-server/transcript-mirror.test.ts +0 -524
  100. package/src/app-server/transcript-mirror.ts +0 -208
  101. package/src/app-server/transcript-repair-runtime-contract.test.ts +0 -44
  102. package/src/app-server/transport-stdio.test.ts +0 -171
  103. package/src/app-server/transport-stdio.ts +0 -107
  104. package/src/app-server/transport-websocket.test.ts +0 -69
  105. package/src/app-server/transport-websocket.ts +0 -90
  106. package/src/app-server/transport.ts +0 -117
  107. package/src/app-server/user-input-bridge.test.ts +0 -249
  108. package/src/app-server/user-input-bridge.ts +0 -316
  109. package/src/app-server/version.ts +0 -4
  110. package/src/app-server/vision-tools.ts +0 -12
  111. package/src/command-account.ts +0 -544
  112. package/src/command-formatters.ts +0 -425
  113. package/src/command-handlers.ts +0 -2004
  114. package/src/command-rpc.test.ts +0 -16
  115. package/src/command-rpc.ts +0 -142
  116. package/src/commands.test.ts +0 -3312
  117. package/src/commands.ts +0 -65
  118. package/src/conversation-binding-data.ts +0 -124
  119. package/src/conversation-binding.test.ts +0 -599
  120. package/src/conversation-binding.ts +0 -561
  121. package/src/conversation-control.test.ts +0 -126
  122. package/src/conversation-control.ts +0 -303
  123. package/src/conversation-turn-collector.test.ts +0 -191
  124. package/src/conversation-turn-collector.ts +0 -186
  125. package/src/conversation-turn-input.test.ts +0 -141
  126. package/src/conversation-turn-input.ts +0 -106
  127. package/src/manifest.test.ts +0 -20
  128. package/src/migration/apply.ts +0 -501
  129. package/src/migration/helpers.ts +0 -55
  130. package/src/migration/plan.ts +0 -461
  131. package/src/migration/provider.test.ts +0 -1741
  132. package/src/migration/provider.ts +0 -41
  133. package/src/migration/source.ts +0 -643
  134. package/src/migration/targets.ts +0 -25
  135. package/src/node-cli-sessions.test.ts +0 -180
  136. package/src/node-cli-sessions.ts +0 -711
  137. package/test-api.ts +0 -82
  138. package/tsconfig.json +0 -16
@@ -1,939 +0,0 @@
1
- import {
2
- embeddedAgentLog,
3
- isActiveHarnessContextEngine,
4
- type EmbeddedRunAttemptParams,
5
- } from "klaw/plugin-sdk/agent-harness-runtime";
6
- import { buildCodexUserMcpServersThreadConfigPatch } from "klaw/plugin-sdk/codex-mcp-projection";
7
- import { listRegisteredPluginAgentPromptGuidance } from "klaw/plugin-sdk/plugin-runtime";
8
- import { CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY } from "../../prompt-overlay.js";
9
- import { isModernCodexModel } from "../../provider.js";
10
- import { isCodexAppServerConnectionClosedError, type CodexAppServerClient } from "./client.js";
11
- import { codexSandboxPolicyForTurn, type CodexAppServerRuntimeOptions } from "./config.js";
12
- import {
13
- resolveCodexContextEngineProjectionMaxChars,
14
- resolveCodexContextEngineProjectionReserveTokens,
15
- } from "./context-engine-projection.js";
16
- import { invalidInlineImageText, sanitizeInlineImageDataUrl } from "./image-payload-sanitizer.js";
17
- import {
18
- isCodexPluginThreadBindingStale,
19
- mergeCodexThreadConfigs,
20
- type CodexPluginThreadConfig,
21
- } from "./plugin-thread-config.js";
22
- import {
23
- assertCodexThreadResumeResponse,
24
- assertCodexThreadStartResponse,
25
- } from "./protocol-validators.js";
26
- import {
27
- isJsonObject,
28
- type CodexDynamicToolSpec,
29
- type CodexSandboxPolicy,
30
- type CodexThreadResumeParams,
31
- type CodexThreadStartParams,
32
- type CodexTurnStartParams,
33
- type JsonObject,
34
- type CodexUserInput,
35
- type JsonValue,
36
- } from "./protocol.js";
37
- import {
38
- clearCodexAppServerBinding,
39
- isCodexAppServerNativeAuthProfile,
40
- readCodexAppServerBinding,
41
- writeCodexAppServerBinding,
42
- type CodexAppServerAuthProfileLookup,
43
- type CodexAppServerContextEngineBinding,
44
- type CodexAppServerContextEngineProjectionBinding,
45
- type CodexAppServerThreadBinding,
46
- } from "./session-binding.js";
47
-
48
- export type CodexAppServerThreadLifecycle = {
49
- action: "started" | "resumed";
50
- rotatedContextEngineBinding?: boolean;
51
- };
52
-
53
- export type CodexAppServerThreadLifecycleBinding = CodexAppServerThreadBinding & {
54
- lifecycle: CodexAppServerThreadLifecycle;
55
- };
56
-
57
- export type CodexContextEngineThreadBootstrapProjection = {
58
- mode: "thread_bootstrap";
59
- epoch: string;
60
- fingerprint?: string;
61
- };
62
-
63
- export type CodexPluginThreadConfigProvider = {
64
- enabled: boolean;
65
- inputFingerprint?: string;
66
- enabledPluginConfigKeys?: readonly string[];
67
- build: () => Promise<CodexPluginThreadConfig>;
68
- };
69
-
70
- export const CODEX_CODE_MODE_THREAD_CONFIG: JsonObject = {
71
- "features.code_mode": true,
72
- "features.code_mode_only": false,
73
- };
74
-
75
- export const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG: JsonObject = {
76
- "features.code_mode": false,
77
- "features.code_mode_only": false,
78
- };
79
-
80
- const CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG: JsonObject = {
81
- project_doc_max_bytes: 0,
82
- };
83
-
84
- export async function startOrResumeThread(params: {
85
- client: CodexAppServerClient;
86
- params: EmbeddedRunAttemptParams;
87
- agentId?: string;
88
- cwd: string;
89
- dynamicTools: CodexDynamicToolSpec[];
90
- appServer: CodexAppServerRuntimeOptions;
91
- developerInstructions?: string;
92
- config?: JsonObject;
93
- finalConfigPatch?: JsonObject;
94
- nativeCodeModeEnabled?: boolean;
95
- nativeCodeModeOnlyEnabled?: boolean;
96
- userMcpServersEnabled?: boolean;
97
- mcpServersFingerprint?: string;
98
- mcpServersFingerprintEvaluated?: boolean;
99
- pluginThreadConfig?: CodexPluginThreadConfigProvider;
100
- contextEngineProjection?: CodexContextEngineThreadBootstrapProjection;
101
- }): Promise<CodexAppServerThreadLifecycleBinding> {
102
- const dynamicToolsFingerprint = fingerprintDynamicTools(params.dynamicTools);
103
- const contextEngineBinding = buildContextEngineBinding(
104
- params.params,
105
- params.contextEngineProjection,
106
- );
107
- const userMcpServersConfigPatch =
108
- params.userMcpServersEnabled === false
109
- ? undefined
110
- : buildCodexUserMcpServersThreadConfigPatch(params.params.config, {
111
- agentId: params.agentId ?? params.params.agentId,
112
- });
113
- const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
114
- let binding = await readCodexAppServerBinding(params.params.sessionFile, {
115
- authProfileStore: params.params.authProfileStore,
116
- agentDir: params.params.agentDir,
117
- config: params.params.config,
118
- });
119
- let preserveExistingBinding = false;
120
- let rotatedContextEngineBinding = false;
121
- let prebuiltPluginThreadConfig: CodexPluginThreadConfig | undefined;
122
- if (binding?.threadId && params.nativeCodeModeEnabled === false) {
123
- embeddedAgentLog.debug(
124
- "codex app-server native tool surface disabled for turn; starting transient thread",
125
- {
126
- threadId: binding.threadId,
127
- },
128
- );
129
- preserveExistingBinding = true;
130
- binding = undefined;
131
- }
132
- if (binding?.threadId && (binding.contextEngine || contextEngineBinding)) {
133
- if (
134
- !contextEngineBinding ||
135
- !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding)
136
- ) {
137
- embeddedAgentLog.debug(
138
- "codex app-server context-engine binding changed; starting a new thread",
139
- {
140
- threadId: binding.threadId,
141
- engineId: contextEngineBinding?.engineId,
142
- previousEngineId: binding.contextEngine?.engineId,
143
- epoch: contextEngineBinding?.projection?.epoch,
144
- previousEpoch: binding.contextEngine?.projection?.epoch,
145
- fingerprint: contextEngineBinding?.projection?.fingerprint,
146
- previousFingerprint: binding.contextEngine?.projection?.fingerprint,
147
- policyFingerprint: contextEngineBinding?.policyFingerprint,
148
- previousPolicyFingerprint: binding.contextEngine?.policyFingerprint,
149
- },
150
- );
151
- await clearCodexAppServerBinding(params.params.sessionFile);
152
- binding = undefined;
153
- rotatedContextEngineBinding = true;
154
- }
155
- }
156
- if (binding?.threadId && binding.userMcpServersFingerprint !== userMcpServersFingerprint) {
157
- embeddedAgentLog.debug("codex app-server user MCP config changed; starting a new thread", {
158
- threadId: binding.threadId,
159
- });
160
- await clearCodexAppServerBinding(params.params.sessionFile);
161
- binding = undefined;
162
- }
163
- if (
164
- binding?.threadId &&
165
- params.mcpServersFingerprintEvaluated === true &&
166
- binding.mcpServersFingerprint !== params.mcpServersFingerprint
167
- ) {
168
- embeddedAgentLog.debug("codex app-server MCP config changed; starting a new thread", {
169
- threadId: binding.threadId,
170
- });
171
- await clearCodexAppServerBinding(params.params.sessionFile);
172
- binding = undefined;
173
- }
174
- if (binding?.threadId) {
175
- let pluginBindingStale = isCodexPluginThreadBindingStale({
176
- codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
177
- bindingFingerprint: binding.pluginAppsFingerprint,
178
- bindingInputFingerprint: binding.pluginAppsInputFingerprint,
179
- currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
180
- hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext),
181
- });
182
- if (
183
- !pluginBindingStale &&
184
- shouldRecheckRecoverablePluginBinding({
185
- binding,
186
- pluginThreadConfig: params.pluginThreadConfig,
187
- })
188
- ) {
189
- try {
190
- prebuiltPluginThreadConfig = await params.pluginThreadConfig?.build();
191
- pluginBindingStale =
192
- prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
193
- } catch (error) {
194
- embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
195
- error,
196
- threadId: binding.threadId,
197
- });
198
- }
199
- }
200
- if (pluginBindingStale) {
201
- embeddedAgentLog.debug("codex app-server plugin app config changed; starting a new thread", {
202
- threadId: binding.threadId,
203
- });
204
- await clearCodexAppServerBinding(params.params.sessionFile);
205
- binding = undefined;
206
- }
207
- }
208
- if (
209
- binding?.threadId &&
210
- params.mcpServersFingerprintEvaluated === true &&
211
- binding.mcpServersFingerprint !== params.mcpServersFingerprint
212
- ) {
213
- embeddedAgentLog.debug("codex app-server MCP config changed; starting a new thread", {
214
- threadId: binding.threadId,
215
- });
216
- await clearCodexAppServerBinding(params.params.sessionFile);
217
- binding = undefined;
218
- }
219
- if (binding?.threadId) {
220
- // `/codex resume <thread>` writes a binding before the next turn can know
221
- // the dynamic tool catalog, so only invalidate fingerprints we actually have.
222
- if (
223
- binding.dynamicToolsFingerprint &&
224
- !areDynamicToolFingerprintsCompatible(
225
- binding.dynamicToolsFingerprint,
226
- dynamicToolsFingerprint,
227
- )
228
- ) {
229
- preserveExistingBinding = shouldStartTransientNoToolThread({
230
- previous: binding.dynamicToolsFingerprint,
231
- next: dynamicToolsFingerprint,
232
- });
233
- if (preserveExistingBinding) {
234
- embeddedAgentLog.debug(
235
- "codex app-server dynamic tools unavailable for turn; starting transient thread",
236
- {
237
- threadId: binding.threadId,
238
- },
239
- );
240
- } else {
241
- embeddedAgentLog.debug(
242
- "codex app-server dynamic tool catalog changed; starting a new thread",
243
- {
244
- threadId: binding.threadId,
245
- },
246
- );
247
- await clearCodexAppServerBinding(params.params.sessionFile);
248
- }
249
- } else {
250
- try {
251
- const authProfileId = params.params.authProfileId ?? binding.authProfileId;
252
- const resumeConfig = mergeCodexThreadConfigs(
253
- params.config,
254
- userMcpServersConfigPatch,
255
- params.finalConfigPatch,
256
- );
257
- const response = assertCodexThreadResumeResponse(
258
- await params.client.request(
259
- "thread/resume",
260
- buildThreadResumeParams(params.params, {
261
- threadId: binding.threadId,
262
- authProfileId,
263
- appServer: params.appServer,
264
- dynamicTools: params.dynamicTools,
265
- developerInstructions: params.developerInstructions,
266
- config: resumeConfig,
267
- nativeCodeModeEnabled: params.nativeCodeModeEnabled,
268
- nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
269
- }),
270
- ),
271
- );
272
- const boundAuthProfileId = authProfileId;
273
- const fallbackModelProvider = resolveCodexAppServerModelProvider({
274
- provider: params.params.provider,
275
- authProfileId: boundAuthProfileId,
276
- authProfileStore: params.params.authProfileStore,
277
- agentDir: params.params.agentDir,
278
- config: params.params.config,
279
- });
280
- const nextMcpServersFingerprint =
281
- params.mcpServersFingerprintEvaluated === true
282
- ? params.mcpServersFingerprint
283
- : binding.mcpServersFingerprint;
284
- await writeCodexAppServerBinding(
285
- params.params.sessionFile,
286
- {
287
- threadId: response.thread.id,
288
- cwd: params.cwd,
289
- authProfileId: boundAuthProfileId,
290
- model: params.params.modelId,
291
- modelProvider: response.modelProvider ?? fallbackModelProvider,
292
- dynamicToolsFingerprint,
293
- userMcpServersFingerprint,
294
- mcpServersFingerprint: nextMcpServersFingerprint,
295
- pluginAppsFingerprint: binding.pluginAppsFingerprint,
296
- pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
297
- pluginAppPolicyContext: binding.pluginAppPolicyContext,
298
- contextEngine: contextEngineBinding,
299
- createdAt: binding.createdAt,
300
- },
301
- {
302
- authProfileStore: params.params.authProfileStore,
303
- agentDir: params.params.agentDir,
304
- config: params.params.config,
305
- },
306
- );
307
- if (contextEngineBinding) {
308
- embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
309
- sessionId: params.params.sessionId,
310
- sessionKey: params.params.sessionKey,
311
- threadId: response.thread.id,
312
- engineId: contextEngineBinding.engineId,
313
- epoch: contextEngineBinding.projection?.epoch,
314
- fingerprint: contextEngineBinding.projection?.fingerprint,
315
- action: "resumed",
316
- });
317
- }
318
- return {
319
- ...binding,
320
- threadId: response.thread.id,
321
- cwd: params.cwd,
322
- authProfileId: boundAuthProfileId,
323
- model: params.params.modelId,
324
- modelProvider: response.modelProvider ?? fallbackModelProvider,
325
- dynamicToolsFingerprint,
326
- userMcpServersFingerprint,
327
- mcpServersFingerprint: nextMcpServersFingerprint,
328
- pluginAppsFingerprint: binding.pluginAppsFingerprint,
329
- pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
330
- pluginAppPolicyContext: binding.pluginAppPolicyContext,
331
- contextEngine: contextEngineBinding,
332
- lifecycle: { action: "resumed" },
333
- };
334
- } catch (error) {
335
- if (isCodexAppServerConnectionClosedError(error)) {
336
- throw error;
337
- }
338
- embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", {
339
- error,
340
- });
341
- await clearCodexAppServerBinding(params.params.sessionFile);
342
- }
343
- }
344
- }
345
-
346
- const pluginThreadConfig = params.pluginThreadConfig?.enabled
347
- ? (prebuiltPluginThreadConfig ?? (await params.pluginThreadConfig.build()))
348
- : undefined;
349
- const config = mergeCodexThreadConfigs(
350
- params.config,
351
- userMcpServersConfigPatch,
352
- pluginThreadConfig?.configPatch,
353
- params.finalConfigPatch,
354
- );
355
- const response = assertCodexThreadStartResponse(
356
- await params.client.request(
357
- "thread/start",
358
- buildThreadStartParams(params.params, {
359
- cwd: params.cwd,
360
- dynamicTools: params.dynamicTools,
361
- appServer: params.appServer,
362
- developerInstructions: params.developerInstructions,
363
- config,
364
- nativeCodeModeEnabled: params.nativeCodeModeEnabled,
365
- nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
366
- }),
367
- ),
368
- );
369
- const modelProvider = resolveCodexAppServerModelProvider({
370
- provider: params.params.provider,
371
- authProfileId: params.params.authProfileId,
372
- authProfileStore: params.params.authProfileStore,
373
- agentDir: params.params.agentDir,
374
- config: params.params.config,
375
- });
376
- const createdAt = new Date().toISOString();
377
- const nextMcpServersFingerprint =
378
- params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : undefined;
379
- if (!preserveExistingBinding) {
380
- await writeCodexAppServerBinding(
381
- params.params.sessionFile,
382
- {
383
- threadId: response.thread.id,
384
- cwd: params.cwd,
385
- authProfileId: params.params.authProfileId,
386
- model: response.model ?? params.params.modelId,
387
- modelProvider: response.modelProvider ?? modelProvider,
388
- dynamicToolsFingerprint,
389
- userMcpServersFingerprint,
390
- mcpServersFingerprint: nextMcpServersFingerprint,
391
- pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
392
- pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
393
- pluginAppPolicyContext: pluginThreadConfig?.policyContext,
394
- contextEngine: contextEngineBinding,
395
- createdAt,
396
- },
397
- {
398
- authProfileStore: params.params.authProfileStore,
399
- agentDir: params.params.agentDir,
400
- config: params.params.config,
401
- },
402
- );
403
- if (contextEngineBinding) {
404
- embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
405
- sessionId: params.params.sessionId,
406
- sessionKey: params.params.sessionKey,
407
- threadId: response.thread.id,
408
- engineId: contextEngineBinding.engineId,
409
- epoch: contextEngineBinding.projection?.epoch,
410
- fingerprint: contextEngineBinding.projection?.fingerprint,
411
- action: rotatedContextEngineBinding ? "rotated" : "started",
412
- });
413
- }
414
- }
415
- return {
416
- schemaVersion: 1,
417
- threadId: response.thread.id,
418
- sessionFile: params.params.sessionFile,
419
- cwd: params.cwd,
420
- authProfileId: params.params.authProfileId,
421
- model: response.model ?? params.params.modelId,
422
- modelProvider: response.modelProvider ?? modelProvider,
423
- dynamicToolsFingerprint,
424
- userMcpServersFingerprint,
425
- mcpServersFingerprint: nextMcpServersFingerprint,
426
- pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
427
- pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
428
- pluginAppPolicyContext: pluginThreadConfig?.policyContext,
429
- contextEngine: contextEngineBinding,
430
- createdAt,
431
- updatedAt: createdAt,
432
- lifecycle: {
433
- action: "started",
434
- ...(rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}),
435
- },
436
- };
437
- }
438
-
439
- export function buildContextEngineBinding(
440
- params: EmbeddedRunAttemptParams,
441
- projection?: CodexContextEngineThreadBootstrapProjection,
442
- ): CodexAppServerContextEngineBinding | undefined {
443
- const contextEngine = isActiveHarnessContextEngine(params.contextEngine)
444
- ? params.contextEngine
445
- : undefined;
446
- const engineId = contextEngine?.info?.id?.trim();
447
- if (!contextEngine || !engineId) {
448
- return undefined;
449
- }
450
- return {
451
- schemaVersion: 1,
452
- engineId,
453
- policyFingerprint: JSON.stringify({
454
- schemaVersion: 1,
455
- engineId,
456
- engineVersion: contextEngine.info.version,
457
- ownsCompaction: contextEngine.info.ownsCompaction === true,
458
- turnMaintenanceMode: contextEngine.info.turnMaintenanceMode,
459
- citationsMode: resolveContextEngineCitationsMode(params.config),
460
- contextTokenBudget: params.contextTokenBudget,
461
- projectionMaxChars: resolveCodexContextEngineProjectionMaxChars({
462
- contextTokenBudget: params.contextTokenBudget,
463
- reserveTokens: resolveCodexContextEngineProjectionReserveTokens({
464
- config: params.config,
465
- }),
466
- }),
467
- }),
468
- projection: projection ? buildContextEngineProjectionBinding(projection) : undefined,
469
- };
470
- }
471
-
472
- function buildContextEngineProjectionBinding(
473
- projection: CodexContextEngineThreadBootstrapProjection,
474
- ): CodexAppServerContextEngineProjectionBinding {
475
- return {
476
- schemaVersion: 1,
477
- mode: "thread_bootstrap",
478
- epoch: projection.epoch,
479
- fingerprint: projection.fingerprint,
480
- };
481
- }
482
-
483
- export function isContextEngineBindingCompatible(
484
- previous: CodexAppServerContextEngineBinding | undefined,
485
- next: CodexAppServerContextEngineBinding,
486
- ): boolean {
487
- return (
488
- previous?.schemaVersion === next.schemaVersion &&
489
- previous.engineId === next.engineId &&
490
- previous.policyFingerprint === next.policyFingerprint &&
491
- areContextEngineProjectionBindingsCompatible(previous.projection, next.projection)
492
- );
493
- }
494
-
495
- function areContextEngineProjectionBindingsCompatible(
496
- previous: CodexAppServerContextEngineProjectionBinding | undefined,
497
- next: CodexAppServerContextEngineProjectionBinding | undefined,
498
- ): boolean {
499
- if (!next) {
500
- return previous === undefined;
501
- }
502
- return (
503
- previous?.schemaVersion === next.schemaVersion &&
504
- previous.mode === next.mode &&
505
- previous.epoch === next.epoch &&
506
- previous.fingerprint === next.fingerprint
507
- );
508
- }
509
-
510
- function resolveContextEngineCitationsMode(config: unknown): JsonValue | undefined {
511
- const rootConfig = isUnknownRecord(config) ? config : undefined;
512
- const memoryConfig = isUnknownRecord(rootConfig?.memory) ? rootConfig.memory : undefined;
513
- const citations = memoryConfig?.citations;
514
- return isJsonConfigValue(citations) ? citations : undefined;
515
- }
516
-
517
- function isUnknownRecord(value: unknown): value is Record<string, unknown> {
518
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
519
- }
520
-
521
- function isJsonConfigValue(value: unknown): value is JsonValue {
522
- if (value === null || typeof value === "string" || typeof value === "boolean") {
523
- return true;
524
- }
525
- if (typeof value === "number") {
526
- return Number.isFinite(value);
527
- }
528
- if (Array.isArray(value)) {
529
- return value.every(isJsonConfigValue);
530
- }
531
- return isUnknownRecord(value) && Object.values(value).every(isJsonConfigValue);
532
- }
533
-
534
- function shouldRecheckRecoverablePluginBinding(params: {
535
- binding: CodexAppServerThreadBinding;
536
- pluginThreadConfig?: CodexPluginThreadConfigProvider;
537
- }): boolean {
538
- if (!params.pluginThreadConfig?.enabled) {
539
- return false;
540
- }
541
- if (
542
- !params.binding.pluginAppsFingerprint ||
543
- !params.binding.pluginAppsInputFingerprint ||
544
- params.binding.pluginAppsInputFingerprint !== params.pluginThreadConfig.inputFingerprint
545
- ) {
546
- return false;
547
- }
548
- const policyContext = params.binding.pluginAppPolicyContext;
549
- if (!policyContext) {
550
- return false;
551
- }
552
- const expectedPluginConfigKeys = params.pluginThreadConfig.enabledPluginConfigKeys ?? [];
553
- return Object.keys(policyContext.apps).length === 0 || expectedPluginConfigKeys.length > 0;
554
- }
555
-
556
- export function buildThreadStartParams(
557
- params: EmbeddedRunAttemptParams,
558
- options: {
559
- cwd: string;
560
- dynamicTools: CodexDynamicToolSpec[];
561
- appServer: CodexAppServerRuntimeOptions;
562
- developerInstructions?: string;
563
- config?: JsonObject;
564
- nativeCodeModeEnabled?: boolean;
565
- nativeCodeModeOnlyEnabled?: boolean;
566
- },
567
- ): CodexThreadStartParams {
568
- const modelProvider = resolveCodexAppServerModelProvider({
569
- provider: params.provider,
570
- authProfileId: params.authProfileId,
571
- authProfileStore: params.authProfileStore,
572
- agentDir: params.agentDir,
573
- config: params.config,
574
- });
575
- return {
576
- model: params.modelId,
577
- ...(modelProvider ? { modelProvider } : {}),
578
- cwd: options.cwd,
579
- approvalPolicy: options.appServer.approvalPolicy,
580
- approvalsReviewer: options.appServer.approvalsReviewer,
581
- sandbox: options.appServer.sandbox,
582
- ...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
583
- serviceName: "Klaw",
584
- config: buildCodexRuntimeThreadConfigForRun(params, options.config, {
585
- nativeCodeModeEnabled: options.nativeCodeModeEnabled,
586
- nativeCodeModeOnlyEnabled: options.nativeCodeModeOnlyEnabled,
587
- }),
588
- ...(options.nativeCodeModeEnabled === false ? { environments: [] } : {}),
589
- developerInstructions:
590
- options.developerInstructions ??
591
- buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
592
- dynamicTools: options.dynamicTools,
593
- experimentalRawEvents: true,
594
- persistExtendedHistory: true,
595
- };
596
- }
597
-
598
- export function buildThreadResumeParams(
599
- params: EmbeddedRunAttemptParams,
600
- options: {
601
- threadId: string;
602
- authProfileId?: string;
603
- appServer: CodexAppServerRuntimeOptions;
604
- dynamicTools?: CodexDynamicToolSpec[];
605
- developerInstructions?: string;
606
- config?: JsonObject;
607
- nativeCodeModeEnabled?: boolean;
608
- nativeCodeModeOnlyEnabled?: boolean;
609
- },
610
- ): CodexThreadResumeParams {
611
- const modelProvider = resolveCodexAppServerModelProvider({
612
- provider: params.provider,
613
- authProfileId: options.authProfileId ?? params.authProfileId,
614
- authProfileStore: params.authProfileStore,
615
- agentDir: params.agentDir,
616
- config: params.config,
617
- });
618
- return {
619
- threadId: options.threadId,
620
- model: params.modelId,
621
- ...(modelProvider ? { modelProvider } : {}),
622
- approvalPolicy: options.appServer.approvalPolicy,
623
- approvalsReviewer: options.appServer.approvalsReviewer,
624
- sandbox: options.appServer.sandbox,
625
- ...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
626
- config: buildCodexRuntimeThreadConfigForRun(params, options.config, {
627
- nativeCodeModeEnabled: options.nativeCodeModeEnabled,
628
- nativeCodeModeOnlyEnabled: options.nativeCodeModeOnlyEnabled,
629
- }),
630
- developerInstructions:
631
- options.developerInstructions ??
632
- buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
633
- persistExtendedHistory: true,
634
- };
635
- }
636
-
637
- export function buildCodexRuntimeThreadConfig(
638
- config: JsonObject | undefined,
639
- options: { nativeCodeModeEnabled?: boolean; nativeCodeModeOnlyEnabled?: boolean } = {},
640
- ): JsonObject {
641
- const codeModeConfig: JsonObject = {
642
- ...CODEX_CODE_MODE_THREAD_CONFIG,
643
- "features.code_mode_only": options.nativeCodeModeOnlyEnabled === true,
644
- };
645
- if (options.nativeCodeModeEnabled === false) {
646
- return (
647
- mergeCodexThreadConfigs(codeModeConfig, config, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG) ?? {
648
- ...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG,
649
- }
650
- );
651
- }
652
- if (options.nativeCodeModeOnlyEnabled === true) {
653
- return (
654
- mergeCodexThreadConfigs(codeModeConfig, config, {
655
- "features.code_mode_only": true,
656
- }) ?? {
657
- ...codeModeConfig,
658
- "features.code_mode_only": true,
659
- }
660
- );
661
- }
662
- return (
663
- mergeCodexThreadConfigs(codeModeConfig, config) ?? {
664
- ...codeModeConfig,
665
- }
666
- );
667
- }
668
-
669
- function buildCodexRuntimeThreadConfigForRun(
670
- params: EmbeddedRunAttemptParams,
671
- config: JsonObject | undefined,
672
- options: { nativeCodeModeEnabled?: boolean; nativeCodeModeOnlyEnabled?: boolean } = {},
673
- ): JsonObject {
674
- const runtimeConfig = buildCodexRuntimeThreadConfig(config, options);
675
- if (params.bootstrapContextMode !== "lightweight") {
676
- return runtimeConfig;
677
- }
678
- return (
679
- mergeCodexThreadConfigs(runtimeConfig, CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG) ?? {
680
- ...runtimeConfig,
681
- ...CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG,
682
- }
683
- );
684
- }
685
-
686
- export function buildTurnStartParams(
687
- params: EmbeddedRunAttemptParams,
688
- options: {
689
- threadId: string;
690
- cwd: string;
691
- appServer: CodexAppServerRuntimeOptions;
692
- promptText?: string;
693
- sandboxPolicy?: CodexSandboxPolicy;
694
- },
695
- ): CodexTurnStartParams {
696
- return {
697
- threadId: options.threadId,
698
- input: buildUserInput(params, options.promptText),
699
- cwd: options.cwd,
700
- approvalPolicy: options.appServer.approvalPolicy,
701
- approvalsReviewer: options.appServer.approvalsReviewer,
702
- sandboxPolicy:
703
- options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd),
704
- model: params.modelId,
705
- ...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
706
- effort: resolveReasoningEffort(params.thinkLevel, params.modelId),
707
- collaborationMode: buildTurnCollaborationMode(params),
708
- };
709
- }
710
-
711
- type CodexTurnCollaborationMode = NonNullable<CodexTurnStartParams["collaborationMode"]>;
712
-
713
- export function buildTurnCollaborationMode(
714
- params: EmbeddedRunAttemptParams,
715
- ): CodexTurnCollaborationMode {
716
- return {
717
- mode: "default",
718
- settings: {
719
- model: params.modelId,
720
- reasoning_effort: resolveReasoningEffort(params.thinkLevel, params.modelId),
721
- developer_instructions: buildTurnScopedCollaborationInstructions(params),
722
- },
723
- };
724
- }
725
-
726
- function buildTurnScopedCollaborationInstructions(params: EmbeddedRunAttemptParams): string | null {
727
- if (params.trigger === "cron") {
728
- return buildCronCollaborationInstructions();
729
- }
730
- if (params.trigger === "heartbeat") {
731
- return buildHeartbeatCollaborationInstructions();
732
- }
733
- return null;
734
- }
735
-
736
- function buildCronCollaborationInstructions(): string {
737
- return [
738
- "This is an Klaw cron automation turn. Apply these instructions only to this scheduled job; ordinary chat turns should stay in Codex Default mode.",
739
- "Execute the cron payload directly. If it asks you to run an exact command, run that command before doing any investigation, planning, memory review, or workspace bootstrap.",
740
- "Use context already provided by the runtime, but do not spend time loading or re-reading workspace bootstrap, memory, or project-doc files before executing the cron payload. Inspect those files only if the payload asks for them or the command fails and they are needed to diagnose it.",
741
- "Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration.",
742
- ].join("\n\n");
743
- }
744
-
745
- function buildHeartbeatCollaborationInstructions(): string {
746
- return [
747
- "This is an Klaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.",
748
- "When you are ready to end the heartbeat, prefer the structured `heartbeat_respond` tool so Klaw can record the wake outcome and notification decision. If `heartbeat_respond` is not already available and `tool_search` is available, search for `heartbeat_respond`, load it, then call it. Use `notify=false` when nothing should visibly interrupt the user.",
749
- CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY,
750
- ].join("\n\n");
751
- }
752
-
753
- export function codexDynamicToolsFingerprint(dynamicTools: CodexDynamicToolSpec[]): string {
754
- return fingerprintDynamicTools(dynamicTools);
755
- }
756
-
757
- export function areCodexDynamicToolFingerprintsCompatible(params: {
758
- previous?: string;
759
- next: string;
760
- }): boolean {
761
- return areDynamicToolFingerprintsCompatible(params.previous, params.next);
762
- }
763
-
764
- function fingerprintDynamicTools(dynamicTools: CodexDynamicToolSpec[]): string {
765
- return JSON.stringify(
766
- dynamicTools.map(fingerprintDynamicToolSpec).toSorted(compareJsonFingerprint),
767
- );
768
- }
769
-
770
- function fingerprintUserMcpServersConfigPatch(
771
- configPatch: JsonObject | undefined,
772
- ): string | undefined {
773
- return configPatch ? JSON.stringify(stabilizeJsonValue(configPatch)) : undefined;
774
- }
775
-
776
- function fingerprintDynamicToolSpec(tool: JsonValue): JsonValue {
777
- if (!isJsonObject(tool)) {
778
- return stabilizeJsonValue(tool);
779
- }
780
- const stable: JsonObject = {};
781
- for (const [key, child] of Object.entries(tool).toSorted(([left], [right]) =>
782
- left.localeCompare(right),
783
- )) {
784
- if (key === "description") {
785
- continue;
786
- }
787
- stable[key] = stabilizeJsonValue(child);
788
- }
789
- return stable;
790
- }
791
-
792
- function stabilizeJsonValue(value: JsonValue): JsonValue {
793
- if (Array.isArray(value)) {
794
- return value.map(stabilizeJsonValue);
795
- }
796
- if (!isJsonObject(value)) {
797
- return value;
798
- }
799
- const stable: JsonObject = {};
800
- for (const [key, child] of Object.entries(value).toSorted(([left], [right]) =>
801
- left.localeCompare(right),
802
- )) {
803
- stable[key] = stabilizeJsonValue(child);
804
- }
805
- return stable;
806
- }
807
-
808
- const EMPTY_DYNAMIC_TOOLS_FINGERPRINT = JSON.stringify([]);
809
-
810
- function areDynamicToolFingerprintsCompatible(previous: string | undefined, next: string): boolean {
811
- return !previous || previous === next;
812
- }
813
-
814
- function shouldStartTransientNoToolThread(params: {
815
- previous: string | undefined;
816
- next: string;
817
- }): boolean {
818
- return Boolean(
819
- params.previous &&
820
- params.previous !== EMPTY_DYNAMIC_TOOLS_FINGERPRINT &&
821
- params.next === EMPTY_DYNAMIC_TOOLS_FINGERPRINT,
822
- );
823
- }
824
-
825
- function compareJsonFingerprint(left: JsonValue, right: JsonValue): number {
826
- return JSON.stringify(left).localeCompare(JSON.stringify(right));
827
- }
828
-
829
- export function buildDeveloperInstructions(
830
- params: EmbeddedRunAttemptParams,
831
- options: { dynamicTools?: readonly CodexDynamicToolSpec[] } = {},
832
- ): string {
833
- const nativeCommandGuidance = listRegisteredPluginAgentPromptGuidance({
834
- surface: "codex_app_server",
835
- includeLegacyGlobalGuidance: false,
836
- }).join("\n");
837
- const sections = [
838
- "Running inside Klaw. Use Klaw dynamic tools for Klaw-owned messaging, cron, sessions, media, gateway, and nodes capabilities when available.",
839
- buildDeferredDynamicToolManifest(options.dynamicTools),
840
- "Use Codex native `spawn_agent` for Codex subagents. Use Klaw `sessions_spawn` only for Klaw or ACP delegation; if it is not already loaded, search for `sessions_spawn` in the `klaw` dynamic tool namespace before calling it.",
841
- buildVisibleReplyInstruction(params),
842
- nativeCommandGuidance,
843
- params.extraSystemPrompt,
844
- ];
845
- return sections.filter((section) => typeof section === "string" && section.trim()).join("\n\n");
846
- }
847
-
848
- function buildDeferredDynamicToolManifest(
849
- dynamicTools: readonly CodexDynamicToolSpec[] | undefined,
850
- ): string | undefined {
851
- const deferredToolNames = [
852
- ...new Set(
853
- (dynamicTools ?? [])
854
- .filter((tool) => tool.deferLoading === true)
855
- .map((tool) => tool.name.trim())
856
- .filter(Boolean),
857
- ),
858
- ].toSorted((left, right) => left.localeCompare(right));
859
- if (deferredToolNames.length === 0) {
860
- return undefined;
861
- }
862
- return `Deferred searchable Klaw dynamic tools available: ${deferredToolNames.join(", ")}. Use \`tool_search\` to load exact callable specs before use.`;
863
- }
864
-
865
- function buildVisibleReplyInstruction(params: EmbeddedRunAttemptParams): string {
866
- if (params.sourceReplyDeliveryMode === "message_tool_only") {
867
- return "Preserve channel/session context. Visible channel replies: use `message`, do not describe would-reply.";
868
- }
869
- return "Preserve channel/session context. Visible channel replies should use the active Codex delivery path; do not describe would-reply.";
870
- }
871
-
872
- function buildUserInput(
873
- params: EmbeddedRunAttemptParams,
874
- promptText: string = params.prompt,
875
- ): CodexUserInput[] {
876
- const imageInputs = (params.images ?? []).map((image): CodexUserInput => {
877
- const imageUrl = sanitizeInlineImageDataUrl(`data:${image.mimeType};base64,${image.data}`);
878
- return imageUrl
879
- ? { type: "image", url: imageUrl }
880
- : {
881
- type: "text",
882
- text: invalidInlineImageText("codex user input"),
883
- text_elements: [],
884
- };
885
- });
886
- return [{ type: "text", text: promptText, text_elements: [] }, ...imageInputs];
887
- }
888
-
889
- export function resolveCodexAppServerModelProvider(params: {
890
- provider: string;
891
- authProfileId?: string;
892
- authProfileStore?: CodexAppServerAuthProfileLookup["authProfileStore"];
893
- agentDir?: string;
894
- config?: CodexAppServerAuthProfileLookup["config"];
895
- }): string | undefined {
896
- const normalized = params.provider.trim();
897
- const normalizedLower = normalized.toLowerCase();
898
- if (!normalized || normalizedLower === "codex") {
899
- // `codex` is Klaw's virtual provider; let Codex app-server keep its
900
- // native provider/auth selection instead of forcing the legacy OpenAI path.
901
- return undefined;
902
- }
903
- if (
904
- isCodexAppServerNativeAuthProfile(params) &&
905
- (normalizedLower === "openai" || normalizedLower === "openai-codex")
906
- ) {
907
- // When Klaw is forwarding ChatGPT/Codex OAuth, `openai` is Codex's
908
- // native provider id, not a public OpenAI API-key choice. Omit the override
909
- // so app-server keeps its configured provider/auth pair for this session.
910
- return undefined;
911
- }
912
- return normalizedLower === "openai-codex" ? "openai" : normalized;
913
- }
914
-
915
- // Modern Codex models (gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.2) use the
916
- // none/low/medium/high/xhigh effort enum and reject "minimal". The CLI
917
- // defaults thinkLevel to "minimal", so without translation EVERY agent turn
918
- // on those models pays a wasted first request + retry-with-low fallback in
919
- // pi-embedded-runner. Map "minimal" -> "low" upfront for modern models so the
920
- // first request is accepted. Older Codex models still accept "minimal"
921
- // directly. (#71946)
922
- // Exported for unit-test coverage of the model-aware translation path.
923
- export function resolveReasoningEffort(
924
- thinkLevel: EmbeddedRunAttemptParams["thinkLevel"],
925
- modelId: string,
926
- ): "minimal" | "low" | "medium" | "high" | "xhigh" | null {
927
- if (thinkLevel === "minimal") {
928
- return isModernCodexModel(modelId) ? "low" : "minimal";
929
- }
930
- if (
931
- thinkLevel === "low" ||
932
- thinkLevel === "medium" ||
933
- thinkLevel === "high" ||
934
- thinkLevel === "xhigh"
935
- ) {
936
- return thinkLevel;
937
- }
938
- return null;
939
- }