@openclaw/codex 2026.8.1-beta.2 → 2026.8.1
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/dist/api.js +146 -0
- package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
- package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
- package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-CCQFs8lf.js} +15 -3
- package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-rq5tiSXk.js} +27 -26
- package/dist/{capabilities-CybGI1oz.js → capabilities-CGm_94eL.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-C54Yg3eA.js} +2 -2
- package/dist/{command-handlers-Db8pu_ds.js → command-handlers-BvOAHTrA.js} +111 -67
- package/dist/{compact-LspQaGfl.js → compact-Hd7SAc0N.js} +35 -30
- package/dist/{session-binding-DPxIvMgE.js → config-Cxw3Cl3k.js} +361 -1161
- package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
- package/dist/doctor-contract-api.js +128 -26
- package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-BFoUcRdx.js} +1341 -1464
- package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
- package/dist/elicitation-input-DMkeki8d.js +71 -0
- package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
- package/dist/{event-projector-usage-e5huIQWu.js → event-projector-usage-D3PHFFXh.js} +18 -18
- package/dist/harness-DVfUQBlQ.js +248 -0
- package/dist/harness.js +2 -226
- package/dist/index.js +405 -299
- package/dist/{isolated-completion-C58NbL40.js → isolated-completion-Cbbx_5Ed.js} +4 -4
- package/dist/launch-args-sXOAg0SX.js +84 -0
- package/dist/managed-binary-ZkGodW19.js +248 -0
- package/dist/media-understanding-provider.js +2 -2
- package/dist/model-catalog-o6Cgjoss.js +101 -0
- package/dist/{models-BFlpn1Ex.js → models-DgFWSuxA.js} +25 -28
- package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-CwFyoylz.js} +385 -94
- package/dist/node-exec-server.runtime-DXrqpw1c.js +195 -0
- package/dist/plugin-approval-roundtrip-D80UZiJ_.js +95 -0
- package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-mdmScYOj.js} +60 -55
- package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-CulOABwu.js} +164 -134
- package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-Bg5rpNxk.js} +203 -7
- package/dist/{rate-limits-DZXmOL8-.js → rate-limits-hFtzt2Up.js} +1 -1
- package/dist/{sandbox-guard-Cmwc9eIk.js → request-CCm4LaED.js} +207 -20
- package/dist/{run-attempt-P8BdxC1I.js → run-attempt-BwNQDH2u.js} +1506 -836
- package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-DeBvleJw.js} +4 -7
- package/dist/session-binding-q2WSTVmC.js +947 -0
- package/dist/{session-catalog-xwNi_SDM.js → session-catalog-C2Tae4rI.js} +1698 -1346
- package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
- package/dist/{session-cli-7hgXZDV7.js → session-cli-BuDjm5Ou.js} +20 -7
- package/dist/session-permission-policy-gLm40Vtq.js +160 -0
- package/dist/session-retirement-CdkI0bvb.js +90 -0
- package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-Bcy33kam.js} +2 -3
- package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-B8JJcxCi.js} +27 -22
- package/dist/{shared-client-kIBbGjk4.js → shared-client-kNkljY-R.js} +2023 -607
- package/dist/{side-question-9HAL26l9.js → side-question-GwD73tX6.js} +109 -57
- package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-Ss-uwnC4.js} +1208 -497
- package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-CeYBqg_4.js} +18 -8
- package/dist/{plugin-app-cache-key-C3H2CVp6.js → thread-resume-DF54hagX.js} +64 -6
- package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-CkEQIna-.js} +158 -41
- package/dist/transport-stdio-C7-s9TLG.js +708 -0
- package/dist/{upstream-prompt-provenance-ClRBije0.js → upstream-prompt-provenance-CFmsKwr9.js} +12 -14
- package/dist/{upstream-session-fork-CE-_LdnP.js → upstream-session-fork-BACGlOFa.js} +86 -88
- package/dist/{usage-CHlV0e-o.js → usage-C2l8Mils.js} +3 -3
- package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
- package/openclaw.plugin.json +50 -2
- package/package.json +9 -8
- package/dist/app-server-policy-zcBDc1JU.js +0 -49
- package/dist/incognito-session-ET_ufDH-.js +0 -2
- package/dist/request-BRl5_UfX.js +0 -157
- package/dist/session-retirement-DoMMJFKm.js +0 -36
- package/dist/thread-resume-CPrJNQVM.js +0 -30
- package/dist/transport-stdio-DIImwDVS.js +0 -99
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
4
|
-
import { a as
|
|
5
|
-
import { a as
|
|
6
|
-
import { t as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { E as resolveCodexPluginsPolicy, F as resolveCodexAppServerLocalHomeDir, M as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME, P as resolveCodexAppServerHomeDir, f as assertCodexModelBackedReviewerEffectiveConfig, j as CODEX_PLUGINS_MARKETPLACE_NAME, n as codexSandboxPolicyForTurn } from "./config-Cxw3Cl3k.js";
|
|
2
|
+
import { $ as CodexAppServerRpcError, D as CodexThreadStartRequestError, E as CodexThreadBindingConflictError, H as getCodexAppServerClientInstanceId, Ht as isJsonObject, J as isCodexAppServerOverloadError, Q as resolveCodexAppServerClientInstanceId, St as releaseCodexAppServerLiveThread, T as CodexRestrictedToolSurfaceAttestationError, Vt as flattenCodexDynamicToolFunctions, _ as retainSharedCodexAppServerClientByInstanceId, bt as isCodexAppServerLiveThreadClaimed, dt as resolveCodexToolAbortTerminalReason, gt as consumeCodexAppServerLiveThread, n as captureExclusiveSharedCodexAppServerClient, w as CodexAdoptedThreadActiveError, wt as unsubscribeCodexAppServerLiveThread } from "./shared-client-kNkljY-R.js";
|
|
3
|
+
import { a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-q2WSTVmC.js";
|
|
4
|
+
import { a as assertCodexThreadForkResponse, r as assertCodexThreadAcceptsDirectInput, s as assertCodexThreadStartResponse, t as CodexThreadDirectInputError } from "./protocol-validators-Bg5rpNxk.js";
|
|
5
|
+
import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, f as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-CVPvp9dL.js";
|
|
6
|
+
import { c as serializeCodexAppInventoryError, n as buildCodexAppServerConnectionFingerprint, s as defaultCodexAppInventoryCache, t as resumeCodexAppServerThread } from "./thread-resume-DF54hagX.js";
|
|
7
|
+
import { c as withExclusiveCodexAppServerThread, l as isIncognitoSessionKey } from "./thread-ownership-CeYBqg_4.js";
|
|
8
|
+
import { i as readCodexSessionMeta, n as codexCatalogHomeId } from "./session-catalog-home-id-DAKUrm3C.js";
|
|
9
|
+
import { d as projectBoundedCodexThreadHistory } from "./transcript-mirror-CkEQIna-.js";
|
|
9
10
|
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
|
|
10
11
|
import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
12
|
import fs from "node:fs/promises";
|
|
12
13
|
import path from "node:path";
|
|
13
|
-
import {
|
|
14
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
15
|
+
import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
16
|
+
import { SKILL_WORKSHOP_TOOL_NAME, buildCredentialSafetyPrompt, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
14
17
|
import * as crypto$1 from "node:crypto";
|
|
15
|
-
import crypto from "node:crypto";
|
|
18
|
+
import crypto, { createHash } from "node:crypto";
|
|
16
19
|
import os from "node:os";
|
|
20
|
+
import { isDeepStrictEqual } from "node:util";
|
|
21
|
+
import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
22
|
+
import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
23
|
+
import { z } from "zod";
|
|
17
24
|
import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
18
25
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
26
|
+
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
27
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
19
28
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
29
|
+
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
20
30
|
import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
21
31
|
import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
32
|
+
import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
|
|
33
|
+
import { buildCodexUserMcpServersThreadConfigPatchForRun } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
34
|
+
import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
|
|
35
|
+
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-flags";
|
|
25
36
|
//#region extensions/codex/src/app-server/plugin-inventory.ts
|
|
26
37
|
/**
|
|
27
38
|
* Reads Codex plugin marketplace state and app inventory to decide which
|
|
@@ -264,15 +275,39 @@ function resolveOwnedApps(params) {
|
|
|
264
275
|
enabled: false,
|
|
265
276
|
needsAuth: true
|
|
266
277
|
};
|
|
267
|
-
return {
|
|
278
|
+
return Object.assign({
|
|
268
279
|
id: app.id,
|
|
269
280
|
name: app.name,
|
|
270
281
|
accessible: info.isAccessible,
|
|
271
282
|
enabled: info.isEnabled,
|
|
272
283
|
needsAuth: !info.isAccessible
|
|
273
|
-
};
|
|
284
|
+
}, resolveOwnedAppReadOnlyToolConfigKeys(info));
|
|
274
285
|
}).toSorted((left, right) => left.id.localeCompare(right.id));
|
|
275
286
|
}
|
|
287
|
+
/** Returns the config keys that Codex metadata proves belong to read-only tools. */
|
|
288
|
+
function resolveOwnedAppReadOnlyToolConfigKeys(app) {
|
|
289
|
+
const appName = app.name.trim();
|
|
290
|
+
const appNameLower = appName.toLowerCase();
|
|
291
|
+
const tools = app.toolSummaries ?? [];
|
|
292
|
+
const writableToolConfigKeys = new Set(tools.filter((tool) => !tool.isReadOnly).flatMap((tool) => resolveAppToolConfigKeys({
|
|
293
|
+
appName,
|
|
294
|
+
appNameLower,
|
|
295
|
+
tool
|
|
296
|
+
})));
|
|
297
|
+
const keys = tools.filter((tool) => tool.isReadOnly).flatMap((tool) => resolveAppToolConfigKeys({
|
|
298
|
+
appName,
|
|
299
|
+
appNameLower,
|
|
300
|
+
tool
|
|
301
|
+
})).filter((key) => !writableToolConfigKeys.has(key));
|
|
302
|
+
return keys.length > 0 ? { readOnlyToolConfigKeys: Array.from(new Set(keys)).toSorted() } : {};
|
|
303
|
+
}
|
|
304
|
+
function resolveAppToolConfigKeys(params) {
|
|
305
|
+
const keys = [params.tool.name];
|
|
306
|
+
if (params.tool.title) keys.push(params.tool.title);
|
|
307
|
+
if (params.appName) keys.push(`${params.appName}_${params.tool.name}`);
|
|
308
|
+
if (params.appNameLower && params.appNameLower !== params.appName) keys.push(`${params.appNameLower}_${params.tool.name}`);
|
|
309
|
+
return keys;
|
|
310
|
+
}
|
|
276
311
|
function findPluginSummary(marketplace, pluginName) {
|
|
277
312
|
const exact = marketplace.plugins.find((plugin) => plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`);
|
|
278
313
|
if (exact) return exact;
|
|
@@ -451,12 +486,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
|
|
|
451
486
|
}
|
|
452
487
|
//#endregion
|
|
453
488
|
//#region extensions/codex/src/app-server/plugin-thread-app-admission.ts
|
|
489
|
+
function resolveCodexPluginThreadAppCacheKey(params) {
|
|
490
|
+
return params.threadId ? `${params.appCacheKey}:thread:${encodeURIComponent(params.threadId)}` : params.appCacheKey;
|
|
491
|
+
}
|
|
492
|
+
function createCodexPluginThreadAppInventoryRequest(params) {
|
|
493
|
+
return async (method, requestParams) => await params.request(method, (method === "app/installed" || method === "app/read") && params.threadId ? {
|
|
494
|
+
...requestParams,
|
|
495
|
+
threadId: params.threadId
|
|
496
|
+
} : requestParams);
|
|
497
|
+
}
|
|
454
498
|
async function refreshCodexPluginAppInventory(params, appCache, options = {}) {
|
|
455
499
|
if (!params.appCacheKey) return;
|
|
456
|
-
const request =
|
|
500
|
+
const request = createCodexPluginThreadAppInventoryRequest(params);
|
|
457
501
|
try {
|
|
458
502
|
return await appCache.refreshNow({
|
|
459
|
-
key: params
|
|
503
|
+
key: resolveCodexPluginThreadAppCacheKey(params),
|
|
460
504
|
request,
|
|
461
505
|
nowMs: params.nowMs,
|
|
462
506
|
forceRefetch: options.forceRefetch,
|
|
@@ -493,9 +537,9 @@ function normalizeCodexPluginOwnerName(name) {
|
|
|
493
537
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
494
538
|
}
|
|
495
539
|
async function readCodexThreadAdmissibleAccountApps(params, appCache) {
|
|
496
|
-
const request =
|
|
540
|
+
const request = createCodexPluginThreadAppInventoryRequest(params);
|
|
497
541
|
const cachedInventory = appCache.read({
|
|
498
|
-
key: params
|
|
542
|
+
key: resolveCodexPluginThreadAppCacheKey(params),
|
|
499
543
|
request,
|
|
500
544
|
nowMs: params.nowMs,
|
|
501
545
|
suppressRefresh: true
|
|
@@ -521,7 +565,8 @@ function toCodexPluginOwnedAccountApp(app) {
|
|
|
521
565
|
name: app.name,
|
|
522
566
|
accessible: app.isAccessible,
|
|
523
567
|
enabled: app.isEnabled,
|
|
524
|
-
needsAuth: !app.isAccessible
|
|
568
|
+
needsAuth: !app.isAccessible,
|
|
569
|
+
...resolveOwnedAppReadOnlyToolConfigKeys(app)
|
|
525
570
|
};
|
|
526
571
|
}
|
|
527
572
|
function resolveCodexThreadConfigAppsForRecord(params) {
|
|
@@ -610,6 +655,11 @@ function buildCodexPluginThreadConfigTimeoutFallback(params) {
|
|
|
610
655
|
/** Builds the Codex apps config patch and policy context for a native thread. */
|
|
611
656
|
async function buildCodexPluginThreadConfig(params) {
|
|
612
657
|
const appCache = params.appCache ?? defaultCodexAppInventoryCache;
|
|
658
|
+
const threadAppCacheKey = resolveCodexPluginThreadAppCacheKey(params);
|
|
659
|
+
const threadRequest = (method, requestParams) => params.request(method, (method === "app/installed" || method === "app/read") && params.threadId && isJsonObject(requestParams) ? {
|
|
660
|
+
...requestParams,
|
|
661
|
+
threadId: params.threadId
|
|
662
|
+
} : requestParams);
|
|
613
663
|
let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
|
|
614
664
|
pluginConfig: params.pluginConfig,
|
|
615
665
|
appCacheKey: params.appCacheKey
|
|
@@ -623,9 +673,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
623
673
|
let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
|
|
624
674
|
pluginConfig: params.pluginConfig,
|
|
625
675
|
policy,
|
|
626
|
-
request:
|
|
676
|
+
request: threadRequest,
|
|
627
677
|
appCache,
|
|
628
|
-
appCacheKey:
|
|
678
|
+
appCacheKey: threadAppCacheKey,
|
|
629
679
|
configCwd: params.configCwd,
|
|
630
680
|
metadataCache: params.metadataCache,
|
|
631
681
|
nowMs: params.nowMs,
|
|
@@ -641,9 +691,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
641
691
|
inventory = await readCodexPluginInventory({
|
|
642
692
|
pluginConfig: params.pluginConfig,
|
|
643
693
|
policy,
|
|
644
|
-
request:
|
|
694
|
+
request: threadRequest,
|
|
645
695
|
appCache,
|
|
646
|
-
appCacheKey:
|
|
696
|
+
appCacheKey: threadAppCacheKey,
|
|
647
697
|
configCwd: params.configCwd,
|
|
648
698
|
metadataCache: params.metadataCache,
|
|
649
699
|
nowMs: params.nowMs
|
|
@@ -659,9 +709,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
659
709
|
if (!record.activationRequired) continue;
|
|
660
710
|
const activation = await ensureCodexPluginActivation({
|
|
661
711
|
identity: record.policy,
|
|
662
|
-
request:
|
|
712
|
+
request: threadRequest,
|
|
663
713
|
appCache,
|
|
664
|
-
appCacheKey:
|
|
714
|
+
appCacheKey: threadAppCacheKey,
|
|
665
715
|
configCwd: params.configCwd,
|
|
666
716
|
metadataCache: params.metadataCache,
|
|
667
717
|
deferAppInventoryRefresh: true,
|
|
@@ -685,9 +735,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
685
735
|
inventory = await readCodexPluginInventory({
|
|
686
736
|
pluginConfig: params.pluginConfig,
|
|
687
737
|
policy,
|
|
688
|
-
request:
|
|
738
|
+
request: threadRequest,
|
|
689
739
|
appCache,
|
|
690
|
-
appCacheKey:
|
|
740
|
+
appCacheKey: threadAppCacheKey,
|
|
691
741
|
configCwd: params.configCwd,
|
|
692
742
|
metadataCache: params.metadataCache,
|
|
693
743
|
nowMs: params.nowMs
|
|
@@ -706,9 +756,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
706
756
|
inventory = await readCodexPluginInventory({
|
|
707
757
|
pluginConfig: params.pluginConfig,
|
|
708
758
|
policy,
|
|
709
|
-
request:
|
|
759
|
+
request: threadRequest,
|
|
710
760
|
appCache,
|
|
711
|
-
appCacheKey:
|
|
761
|
+
appCacheKey: threadAppCacheKey,
|
|
712
762
|
configCwd: params.configCwd,
|
|
713
763
|
metadataCache: params.metadataCache,
|
|
714
764
|
nowMs: params.nowMs
|
|
@@ -946,7 +996,7 @@ function readPersistedAppToolApprovalOverrideNames(config, app) {
|
|
|
946
996
|
const appConfig = isJsonObject(appsRoot) ? appsRoot[app.id] : void 0;
|
|
947
997
|
const tools = isJsonObject(appConfig) ? appConfig.tools : void 0;
|
|
948
998
|
if (!isJsonObject(tools)) return [];
|
|
949
|
-
return Object.entries(tools).filter(([, value]) => hasPersistedToolApprovalOverride(value)).map(([toolName]) => toolName).toSorted();
|
|
999
|
+
return Object.entries(tools).filter(([toolName]) => !app.readOnlyToolConfigKeys?.includes(toolName)).filter(([, value]) => hasPersistedToolApprovalOverride(value)).map(([toolName]) => toolName).toSorted();
|
|
950
1000
|
}
|
|
951
1001
|
function hasPersistedToolApprovalOverride(value) {
|
|
952
1002
|
return isJsonObject(value) && value.approval_mode !== void 0;
|
|
@@ -1063,6 +1113,32 @@ function resolveCodexContextEngineProjectionMaxChars(params) {
|
|
|
1063
1113
|
function resolveCodexContextEngineProjectionReserveTokens() {
|
|
1064
1114
|
return DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS;
|
|
1065
1115
|
}
|
|
1116
|
+
const CONTINUITY_PROJECTION_RESERVE_RATIO = .5;
|
|
1117
|
+
const CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN = 3;
|
|
1118
|
+
const CONTINUITY_MIN_CHARS_PER_TOKEN = .5;
|
|
1119
|
+
const CONTINUITY_MAX_CHARS_PER_TOKEN = CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
|
|
1120
|
+
const CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS = 5e4;
|
|
1121
|
+
/** Builds a calibration sample from a completed turn, or undefined if unusable. */
|
|
1122
|
+
function buildCodexContinuityCalibration(params) {
|
|
1123
|
+
if (!Number.isFinite(params.promptChars) || !Number.isFinite(params.inputTokens) || params.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || params.inputTokens <= 0) return;
|
|
1124
|
+
return {
|
|
1125
|
+
promptChars: Math.floor(params.promptChars),
|
|
1126
|
+
inputTokens: Math.floor(params.inputTokens)
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function resolveContinuityCharsPerToken(calibration) {
|
|
1130
|
+
if (!calibration || !Number.isFinite(calibration.promptChars) || !Number.isFinite(calibration.inputTokens) || calibration.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || calibration.inputTokens <= 0) return CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
|
|
1131
|
+
return Math.min(CONTINUITY_MAX_CHARS_PER_TOKEN, Math.max(CONTINUITY_MIN_CHARS_PER_TOKEN, calibration.promptChars / calibration.inputTokens));
|
|
1132
|
+
}
|
|
1133
|
+
/** Resolves rendered context size for no-engine continuity projections. */
|
|
1134
|
+
function resolveCodexContinuityProjectionMaxChars(params) {
|
|
1135
|
+
const contextTokenBudget = typeof params.contextTokenBudget === "number" && Number.isFinite(params.contextTokenBudget) ? Math.floor(params.contextTokenBudget) : void 0;
|
|
1136
|
+
if (!contextTokenBudget || contextTokenBudget <= 0) return DEFAULT_RENDERED_CONTEXT_CHARS;
|
|
1137
|
+
return normalizeRenderedContextMaxChars(resolveProjectionPromptBudgetTokens({
|
|
1138
|
+
contextTokenBudget,
|
|
1139
|
+
reserveTokens: Math.max(DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS, Math.floor(contextTokenBudget * CONTINUITY_PROJECTION_RESERVE_RATIO))
|
|
1140
|
+
}) * resolveContinuityCharsPerToken(params.calibration));
|
|
1141
|
+
}
|
|
1066
1142
|
/** Fits projected context prompts under Codex app-server turn/start text limits. */
|
|
1067
1143
|
function fitCodexProjectedContextForTurnStart(params) {
|
|
1068
1144
|
const maxChars = typeof params.maxChars === "number" && Number.isFinite(params.maxChars) ? Math.max(0, Math.floor(params.maxChars)) : CODEX_TURN_START_TEXT_INPUT_MAX_CHARS;
|
|
@@ -1328,7 +1404,6 @@ const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set
|
|
|
1328
1404
|
"write",
|
|
1329
1405
|
"edit",
|
|
1330
1406
|
"apply_patch",
|
|
1331
|
-
"update_plan",
|
|
1332
1407
|
...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
|
|
1333
1408
|
]);
|
|
1334
1409
|
const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
|
|
@@ -1561,10 +1636,6 @@ const nativeSkillIsolationByClient = /* @__PURE__ */ new WeakMap();
|
|
|
1561
1636
|
function isMissingPathError(error) {
|
|
1562
1637
|
return error.code === "ENOENT";
|
|
1563
1638
|
}
|
|
1564
|
-
function isPathWithin(root, candidate) {
|
|
1565
|
-
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
1566
|
-
return relative === "" || !path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== "..";
|
|
1567
|
-
}
|
|
1568
1639
|
async function canonicalizeExistingPath(candidate) {
|
|
1569
1640
|
try {
|
|
1570
1641
|
return await fs.realpath(candidate);
|
|
@@ -1590,12 +1661,12 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
|
|
|
1590
1661
|
const realDefaultCodexHome = await canonicalizeExistingPath(defaultCodexHome);
|
|
1591
1662
|
roots.push({
|
|
1592
1663
|
dir: path.join(defaultCodexHome, "skills"),
|
|
1593
|
-
onlyEscapedStateTargets:
|
|
1664
|
+
onlyEscapedStateTargets: isPathInside(realStateDir, realDefaultCodexHome)
|
|
1594
1665
|
});
|
|
1595
1666
|
}
|
|
1596
1667
|
const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
|
|
1597
1668
|
if (configuredCodexHome) {
|
|
1598
|
-
const stateOwned =
|
|
1669
|
+
const stateOwned = isPathInside(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
|
|
1599
1670
|
roots.push({
|
|
1600
1671
|
dir: path.join(configuredCodexHome, "skills"),
|
|
1601
1672
|
onlyEscapedStateTargets: stateOwned
|
|
@@ -1613,7 +1684,7 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
|
|
|
1613
1684
|
const recordSkillFile = async (filePath, onlyEscapedStateTargets) => {
|
|
1614
1685
|
try {
|
|
1615
1686
|
const skillRealPath = await fs.realpath(filePath);
|
|
1616
|
-
if (!onlyEscapedStateTargets || !
|
|
1687
|
+
if (!onlyEscapedStateTargets || !isPathInside(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
|
|
1617
1688
|
} catch (error) {
|
|
1618
1689
|
if (!isMissingPathError(error)) complete = false;
|
|
1619
1690
|
}
|
|
@@ -1753,32 +1824,63 @@ function applyCodexNativeSkillIsolation(config, isolation) {
|
|
|
1753
1824
|
};
|
|
1754
1825
|
}
|
|
1755
1826
|
//#endregion
|
|
1756
|
-
//#region extensions/codex/src/app-server/thread-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
var CodexAdoptedThreadActiveError = class extends Error {
|
|
1777
|
-
constructor() {
|
|
1778
|
-
super("Codex session became active in another runner; wait for it to finish before continuing");
|
|
1779
|
-
this.name = "CodexAdoptedThreadActiveError";
|
|
1827
|
+
//#region extensions/codex/src/app-server/managed-thread-store.ts
|
|
1828
|
+
const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
|
|
1829
|
+
const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
|
|
1830
|
+
const managedThreadSchema = z.object({
|
|
1831
|
+
version: z.literal(1),
|
|
1832
|
+
kind: z.literal("managed-thread"),
|
|
1833
|
+
sourceHomeId: z.string().min(1),
|
|
1834
|
+
threadId: z.string().min(1),
|
|
1835
|
+
rolloutPath: z.string().min(1).optional()
|
|
1836
|
+
});
|
|
1837
|
+
async function markStartedCodexManagedThread(store, params) {
|
|
1838
|
+
if (!store) return;
|
|
1839
|
+
try {
|
|
1840
|
+
await store.mark({
|
|
1841
|
+
sourceHomeId: params.sourceHomeId,
|
|
1842
|
+
threadId: params.threadId,
|
|
1843
|
+
...params.rolloutPath ? { rolloutPath: params.rolloutPath } : {}
|
|
1844
|
+
});
|
|
1845
|
+
} catch (error) {
|
|
1846
|
+
embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
|
|
1780
1847
|
}
|
|
1781
|
-
}
|
|
1848
|
+
}
|
|
1849
|
+
function managedThreadStoreKey(sourceHomeId, threadId) {
|
|
1850
|
+
return `sha256:${createHash("sha256").update("openclaw:codex-managed-thread:v1\0").update(sourceHomeId).update("\0").update(threadId).digest("hex")}`;
|
|
1851
|
+
}
|
|
1852
|
+
/** Durable ownership index for Codex threads created by OpenClaw. */
|
|
1853
|
+
function createCodexManagedThreadStore(state) {
|
|
1854
|
+
return {
|
|
1855
|
+
async mark(params) {
|
|
1856
|
+
try {
|
|
1857
|
+
const value = managedThreadSchema.parse({
|
|
1858
|
+
version: 1,
|
|
1859
|
+
kind: "managed-thread",
|
|
1860
|
+
sourceHomeId: params.sourceHomeId.trim(),
|
|
1861
|
+
threadId: params.threadId.trim(),
|
|
1862
|
+
...params.rolloutPath?.trim() ? { rolloutPath: params.rolloutPath.trim() } : {}
|
|
1863
|
+
});
|
|
1864
|
+
state.registerIfAbsent(managedThreadStoreKey(value.sourceHomeId, value.threadId), value);
|
|
1865
|
+
return true;
|
|
1866
|
+
} catch (error) {
|
|
1867
|
+
embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1870
|
+
},
|
|
1871
|
+
async snapshot() {
|
|
1872
|
+
const byHome = /* @__PURE__ */ new Map();
|
|
1873
|
+
for (const entry of state.entries()) {
|
|
1874
|
+
const parsed = managedThreadSchema.safeParse(entry.value);
|
|
1875
|
+
if (!parsed.success) continue;
|
|
1876
|
+
const ids = byHome.get(parsed.data.sourceHomeId) ?? /* @__PURE__ */ new Set();
|
|
1877
|
+
ids.add(parsed.data.threadId);
|
|
1878
|
+
byHome.set(parsed.data.sourceHomeId, ids);
|
|
1879
|
+
}
|
|
1880
|
+
return byHome;
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1782
1884
|
//#endregion
|
|
1783
1885
|
//#region extensions/codex/src/app-server/plugin-thread-attestation.ts
|
|
1784
1886
|
/**
|
|
@@ -1835,6 +1937,50 @@ async function discardUnattestedCodexPluginThread(params) {
|
|
|
1835
1937
|
}
|
|
1836
1938
|
}
|
|
1837
1939
|
//#endregion
|
|
1940
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-result.ts
|
|
1941
|
+
/** Materializes the public lifecycle result after a fresh thread is durably committed. */
|
|
1942
|
+
function buildStartedCodexThreadBinding(input) {
|
|
1943
|
+
const { context, params, response, startParams } = input;
|
|
1944
|
+
return {
|
|
1945
|
+
threadId: response.thread.id,
|
|
1946
|
+
...input.clientId ? { clientId: input.clientId } : {},
|
|
1947
|
+
cwd: params.cwd,
|
|
1948
|
+
...input.rolloutPath ? { rolloutPath: input.rolloutPath } : {},
|
|
1949
|
+
authProfileId: params.params.authProfileId,
|
|
1950
|
+
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
1951
|
+
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
1952
|
+
modelProvider: response.modelProvider ?? input.startModelProvider ?? input.modelProvider,
|
|
1953
|
+
dynamicToolsFingerprint: context.dynamicToolsFingerprint,
|
|
1954
|
+
dynamicToolsContainDeferred: context.dynamicToolsContainDeferred,
|
|
1955
|
+
nativeSkillIsolationFingerprint: context.nativeSkillIsolationFingerprint,
|
|
1956
|
+
userMcpServersFingerprint: context.userMcpServersFingerprint,
|
|
1957
|
+
mcpServersFingerprint: input.nextMcpServersFingerprint,
|
|
1958
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
1959
|
+
ringZeroConfigFingerprint: context.ringZeroConfigFingerprint,
|
|
1960
|
+
ringZeroClientInstanceId: context.ringZeroClientInstanceId,
|
|
1961
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1962
|
+
networkProxyConfigFingerprint: context.networkProxyConfigFingerprint,
|
|
1963
|
+
nativeHookRelayGeneration: input.finalConfigPatch.nativeHookRelayGeneration,
|
|
1964
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1965
|
+
pluginAppsFingerprint: input.pluginThreadConfig?.fingerprint,
|
|
1966
|
+
pluginAppsInputFingerprint: input.pluginThreadConfig?.inputFingerprint,
|
|
1967
|
+
pluginAppPolicyContext: input.pluginThreadConfig?.policyContext,
|
|
1968
|
+
contextEngine: context.contextEngineBinding,
|
|
1969
|
+
environmentSelectionFingerprint: context.environmentSelectionFingerprint,
|
|
1970
|
+
...!context.preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
|
|
1971
|
+
...startParams,
|
|
1972
|
+
model: response.model ?? startParams.model ?? null,
|
|
1973
|
+
requestedModel: startParams.model ?? null,
|
|
1974
|
+
modelProvider: input.bindingModelProvider ?? null,
|
|
1975
|
+
requestedModelProvider: startParams.modelProvider ?? input.bindingModelProvider ?? null
|
|
1976
|
+
}, params.params.authProfileId, context.dynamicToolsFingerprint) } : {},
|
|
1977
|
+
lifecycle: {
|
|
1978
|
+
action: "started",
|
|
1979
|
+
...context.rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
//#endregion
|
|
1838
1984
|
//#region extensions/codex/src/app-server/reasoning-effort.ts
|
|
1839
1985
|
const CODEX_REASONING_EFFORTS = [
|
|
1840
1986
|
"minimal",
|
|
@@ -1852,11 +1998,6 @@ const LEGACY_PRO_REASONING_EFFORTS = [
|
|
|
1852
1998
|
];
|
|
1853
1999
|
const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
|
|
1854
2000
|
const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
|
|
1855
|
-
function normalizeCodexReasoningEfforts(efforts) {
|
|
1856
|
-
if (!efforts) return [];
|
|
1857
|
-
const supported = new Set(efforts.map((effort) => effort.trim().toLowerCase()));
|
|
1858
|
-
return CODEX_REASONING_EFFORTS.filter((effort) => supported.has(effort));
|
|
1859
|
-
}
|
|
1860
2001
|
/** Read reasoning metadata after the Codex app-server route has been selected. */
|
|
1861
2002
|
function readCodexSupportedReasoningEfforts(compat) {
|
|
1862
2003
|
if (!compat || typeof compat !== "object" || Array.isArray(compat)) return;
|
|
@@ -1865,7 +2006,8 @@ function readCodexSupportedReasoningEfforts(compat) {
|
|
|
1865
2006
|
return efforts.filter((effort) => typeof effort === "string");
|
|
1866
2007
|
}
|
|
1867
2008
|
function resolveSupportedReasoningEffort(params) {
|
|
1868
|
-
const
|
|
2009
|
+
const declared = new Set(params.supportedReasoningEfforts.map((effort) => effort.trim().toLowerCase()));
|
|
2010
|
+
const supported = CODEX_REASONING_EFFORTS.filter((effort) => declared.has(effort));
|
|
1869
2011
|
if (supported.includes(params.requested)) return params.requested;
|
|
1870
2012
|
const fallbackEfforts = params.requested === "ultra" ? supported : supported.filter((effort) => effort !== "ultra");
|
|
1871
2013
|
const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
|
|
@@ -1873,7 +2015,8 @@ function resolveSupportedReasoningEffort(params) {
|
|
|
1873
2015
|
}
|
|
1874
2016
|
/** Resolve a turn effort from the selected model's provider-owned metadata. */
|
|
1875
2017
|
function resolveCodexAppServerReasoningEffort(params) {
|
|
1876
|
-
if (params.thinkLevel === "off"
|
|
2018
|
+
if (params.thinkLevel === "off") return params.supportedReasoningEfforts?.includes("none") ? "none" : null;
|
|
2019
|
+
if (params.thinkLevel === "adaptive") return null;
|
|
1877
2020
|
if (params.supportedReasoningEfforts) return resolveSupportedReasoningEffort({
|
|
1878
2021
|
requested: params.thinkLevel,
|
|
1879
2022
|
supportedReasoningEfforts: params.supportedReasoningEfforts
|
|
@@ -1963,12 +2106,22 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
|
|
|
1963
2106
|
});
|
|
1964
2107
|
}
|
|
1965
2108
|
//#endregion
|
|
2109
|
+
//#region extensions/codex/src/app-server/project-doc-thread-config.ts
|
|
2110
|
+
const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 128 * 1024;
|
|
2111
|
+
function buildCodexProjectDocThreadConfig(config) {
|
|
2112
|
+
const defaults = { project_doc_max_bytes: CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
|
|
2113
|
+
return mergeCodexThreadConfigs(defaults, config) ?? defaults;
|
|
2114
|
+
}
|
|
2115
|
+
//#endregion
|
|
1966
2116
|
//#region extensions/codex/src/app-server/thread-prompt.ts
|
|
1967
2117
|
function buildDeveloperInstructions(params, options = {}) {
|
|
1968
2118
|
const deferredToolNames = /* @__PURE__ */ new Set();
|
|
2119
|
+
let secretsToolName;
|
|
1969
2120
|
let hasSkillWorkshop = false;
|
|
1970
2121
|
let hasSessionsSpawn = false;
|
|
1971
2122
|
let hasSessionsYield = false;
|
|
2123
|
+
let hasSubagentsList = false;
|
|
2124
|
+
let hasSessionsSend = false;
|
|
1972
2125
|
let hasSeenDirectNamespace = false;
|
|
1973
2126
|
let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
|
|
1974
2127
|
for (const spec of options.dynamicTools ?? []) {
|
|
@@ -1977,9 +2130,12 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
1977
2130
|
for (const tool of spec.type === "namespace" ? spec.tools : [spec]) {
|
|
1978
2131
|
const name = tool.name.trim();
|
|
1979
2132
|
if (tool.deferLoading === true && name) deferredToolNames.add(name);
|
|
2133
|
+
if (name === "secrets" && params.disableTools !== true) secretsToolName ??= spec.type === "namespace" ? `${spec.name}.${name}` : name;
|
|
1980
2134
|
hasSkillWorkshop ||= name === SKILL_WORKSHOP_TOOL_NAME;
|
|
1981
2135
|
hasSessionsSpawn ||= name === "sessions_spawn";
|
|
1982
2136
|
hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
|
|
2137
|
+
hasSubagentsList ||= name === "subagents";
|
|
2138
|
+
hasSessionsSend ||= name === "sessions_send";
|
|
1983
2139
|
messageToolAvailable ||= name === "message";
|
|
1984
2140
|
}
|
|
1985
2141
|
}
|
|
@@ -1987,25 +2143,71 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
1987
2143
|
surface: "codex_app_server",
|
|
1988
2144
|
includeLegacyGlobalGuidance: false
|
|
1989
2145
|
}).join("\n");
|
|
1990
|
-
const
|
|
2146
|
+
const delegationGuidanceAvailable = params.disableTools !== true && params.delegationCapability !== "report_only" && !isMessageOnlyCodexSourceReply(params);
|
|
2147
|
+
const nativeDelegationAvailable = delegationGuidanceAvailable && !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) && !shouldDisableCodexToolSearchForModel(params.modelId);
|
|
1991
2148
|
const deferredToolDiscoveryGuidance = deferredToolNames.size > 0 || nativeDelegationAvailable ? "Deferred tools may be absent from the direct tool list. Use `tool_search` when directly callable. On code-mode-only models, use `exec` instead: filter `ALL_TOOLS` by name and description, then call the matching entry through `tools`." : void 0;
|
|
1992
2149
|
return [
|
|
1993
2150
|
"You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
|
|
1994
2151
|
deferredToolNames.size > 0 ? `Deferred searchable OpenClaw dynamic tools available: ${[...deferredToolNames].toSorted((left, right) => left.localeCompare(right)).join(", ")}.` : void 0,
|
|
1995
2152
|
deferredToolDiscoveryGuidance,
|
|
1996
2153
|
hasSkillWorkshop ? buildSkillWorkshopPromptSection().join("\n") : void 0,
|
|
1997
|
-
nativeDelegationAvailable ? `Use Codex native \`spawn_agent\` for Codex subagents. \`spawn_agent\` and the other native collaboration tools may be deferred.${hasSessionsSpawn ? " Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent
|
|
2154
|
+
nativeDelegationAvailable ? `Use Codex native \`spawn_agent\` for Codex subagents. \`spawn_agent\` and the other native collaboration tools may be deferred.${hasSessionsSpawn ? " Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent` on internal legwork." : ""}` : void 0,
|
|
1998
2155
|
hasSessionsYield && nativeDelegationAvailable ? "When a native child's result belongs in a later turn, end the current turn with `openclaw_direct.sessions_yield`; the completion arrives as the next model-visible input. Use native `wait_agent` only for an intentional same-turn wait when the immediate next step is blocked on the child. Never loop-poll for native child completion." : void 0,
|
|
1999
|
-
|
|
2000
|
-
|
|
2156
|
+
delegationGuidanceAvailable ? buildDelegationGuidanceSection({
|
|
2157
|
+
mode: resolveMainSessionDelegationMode({
|
|
2158
|
+
config: params.config,
|
|
2159
|
+
agentId: params.agentId,
|
|
2160
|
+
sessionKey: params.sessionKey
|
|
2161
|
+
}),
|
|
2162
|
+
isMinimal: params.promptMode === "minimal" || params.promptMode === "none",
|
|
2163
|
+
hiddenDelegationTool: nativeDelegationAvailable ? "native `spawn_agent`" : hasSessionsSpawn ? "`sessions_spawn`" : "",
|
|
2164
|
+
hasVisibleSessionSpawn: hasSessionsSpawn,
|
|
2165
|
+
hasSessionsYield,
|
|
2166
|
+
hasSubagentsList,
|
|
2167
|
+
hasSessionsSend
|
|
2168
|
+
}).join("\n") : void 0,
|
|
2169
|
+
buildHarnessVisibleReplyGuidance({
|
|
2170
|
+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
2171
|
+
messageToolAvailable
|
|
2172
|
+
}),
|
|
2173
|
+
buildCredentialSafetyPrompt(secretsToolName),
|
|
2001
2174
|
nativeCommandGuidance,
|
|
2002
2175
|
params.extraSystemPrompt
|
|
2003
2176
|
].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
|
|
2004
2177
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2178
|
+
//#endregion
|
|
2179
|
+
//#region extensions/codex/src/app-server/thread-shell-environment.ts
|
|
2180
|
+
/** Applies host-selected values and any required login-shell restriction last. */
|
|
2181
|
+
function applyCodexManagedShellEnvironment(config, environment, disableLoginShell = false) {
|
|
2182
|
+
if (!environment || Object.keys(environment).length === 0) return disableLoginShell ? {
|
|
2183
|
+
...config,
|
|
2184
|
+
allow_login_shell: false
|
|
2185
|
+
} : config;
|
|
2186
|
+
const current = isJsonObject(config.shell_environment_policy) ? config.shell_environment_policy : {};
|
|
2187
|
+
const currentSet = isJsonObject(current.set) ? current.set : {};
|
|
2188
|
+
const names = Object.keys(environment).toSorted();
|
|
2189
|
+
const includeOnly = Array.isArray(current.include_only) ? current.include_only.filter((entry) => typeof entry === "string") : [];
|
|
2190
|
+
const filters = isJsonObject(current.filters) ? current.filters : void 0;
|
|
2191
|
+
const hasIncludeFilter = filters && Object.values(filters).includes("include");
|
|
2192
|
+
const managedConfig = {
|
|
2193
|
+
...config,
|
|
2194
|
+
shell_environment_policy: {
|
|
2195
|
+
...current,
|
|
2196
|
+
experimental_use_profile: false,
|
|
2197
|
+
set: {
|
|
2198
|
+
...currentSet,
|
|
2199
|
+
...environment
|
|
2200
|
+
},
|
|
2201
|
+
...filters ? hasIncludeFilter ? { filters: {
|
|
2202
|
+
...filters,
|
|
2203
|
+
...Object.fromEntries(names.map((name) => [name, "include"]))
|
|
2204
|
+
} } : {} : includeOnly.length > 0 ? { include_only: [.../* @__PURE__ */ new Set([...includeOnly, ...names])] } : {}
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
return disableLoginShell ? {
|
|
2208
|
+
...managedConfig,
|
|
2209
|
+
allow_login_shell: false
|
|
2210
|
+
} : managedConfig;
|
|
2009
2211
|
}
|
|
2010
2212
|
//#endregion
|
|
2011
2213
|
//#region extensions/codex/src/app-server/web-search.ts
|
|
@@ -2068,20 +2270,23 @@ function resolveCodexWebSearchPlan(params) {
|
|
|
2068
2270
|
return {
|
|
2069
2271
|
kind: "native-hosted",
|
|
2070
2272
|
suppressManagedWebSearch: true,
|
|
2071
|
-
threadConfig: buildCodexNativeWebSearchThreadConfig(params.config)
|
|
2273
|
+
threadConfig: buildCodexNativeWebSearchThreadConfig(params.config),
|
|
2274
|
+
webFetchHostnameAllowlist: buildHostnameAllowlistPolicyFromSuffixAllowlist(nativeConfig?.allowedDomains)?.hostnameAllowlist
|
|
2072
2275
|
};
|
|
2073
2276
|
}
|
|
2074
2277
|
const CODEX_CODE_MODE_THREAD_CONFIG = {
|
|
2075
2278
|
"features.code_mode": true,
|
|
2076
2279
|
"features.code_mode_only": false,
|
|
2077
|
-
"features.apply_patch_streaming_events": true
|
|
2280
|
+
"features.apply_patch_streaming_events": true,
|
|
2281
|
+
suppress_unstable_features_warning: true
|
|
2078
2282
|
};
|
|
2079
2283
|
const CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG = { "features.goals": false };
|
|
2284
|
+
const CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG = { "tools.update_plan.enabled": false };
|
|
2080
2285
|
const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG = {
|
|
2081
2286
|
"features.code_mode": false,
|
|
2082
2287
|
"features.code_mode_only": false
|
|
2083
2288
|
};
|
|
2084
|
-
const
|
|
2289
|
+
const CODEX_NO_PROJECT_DOCS_CONFIG = { project_doc_max_bytes: 0 };
|
|
2085
2290
|
const CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG = { "features.multi_agent": false };
|
|
2086
2291
|
const CODEX_DELEGATION_DISABLED_THREAD_CONFIG = {
|
|
2087
2292
|
"agents.enabled": false,
|
|
@@ -2127,7 +2332,6 @@ const CODEX_RING_ZERO_THREAD_CONFIG = {
|
|
|
2127
2332
|
"skills.bundled.enabled": false,
|
|
2128
2333
|
"skills.include_instructions": false,
|
|
2129
2334
|
"tools.experimental_request_user_input.enabled": false,
|
|
2130
|
-
"tools.update_plan.enabled": false,
|
|
2131
2335
|
hooks: {
|
|
2132
2336
|
PreToolUse: [],
|
|
2133
2337
|
PermissionRequest: [],
|
|
@@ -2140,7 +2344,6 @@ const CODEX_RING_ZERO_THREAD_CONFIG = {
|
|
|
2140
2344
|
SubagentStop: [],
|
|
2141
2345
|
Stop: []
|
|
2142
2346
|
},
|
|
2143
|
-
project_doc_max_bytes: 0,
|
|
2144
2347
|
notify: [],
|
|
2145
2348
|
web_search: "disabled"
|
|
2146
2349
|
};
|
|
@@ -2153,6 +2356,7 @@ const CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES = /* @__PURE__ */ new Map([
|
|
|
2153
2356
|
["codex_hooks", "hooks"]
|
|
2154
2357
|
]);
|
|
2155
2358
|
const CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
2359
|
+
"packagedDefaults",
|
|
2156
2360
|
"mdm",
|
|
2157
2361
|
"system",
|
|
2158
2362
|
"enterpriseManaged",
|
|
@@ -2181,6 +2385,7 @@ function buildThreadStartParams(params, options) {
|
|
|
2181
2385
|
model: modelSelection.model,
|
|
2182
2386
|
...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
|
|
2183
2387
|
cwd: options.cwd,
|
|
2388
|
+
...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
|
|
2184
2389
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
2185
2390
|
approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
|
|
2186
2391
|
...codexThreadSandboxOrPermissions(options.appServer),
|
|
@@ -2196,7 +2401,9 @@ function buildThreadStartParams(params, options) {
|
|
|
2196
2401
|
webSearchAllowed: options.webSearchAllowed,
|
|
2197
2402
|
appServer: options.appServer,
|
|
2198
2403
|
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2199
|
-
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames
|
|
2404
|
+
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
|
|
2405
|
+
shellEnvironment: options.shellEnvironment,
|
|
2406
|
+
disableLoginShell: options.disableLoginShell
|
|
2200
2407
|
}),
|
|
2201
2408
|
...resolveCodexThreadEnvironmentSelection(options),
|
|
2202
2409
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
|
|
@@ -2222,6 +2429,8 @@ function buildThreadResumeParams(params, options) {
|
|
|
2222
2429
|
});
|
|
2223
2430
|
return {
|
|
2224
2431
|
threadId: options.threadId,
|
|
2432
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
2433
|
+
...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
|
|
2225
2434
|
excludeTurns: true,
|
|
2226
2435
|
initialTurnsPage: {
|
|
2227
2436
|
limit: 1,
|
|
@@ -2245,38 +2454,38 @@ function buildThreadResumeParams(params, options) {
|
|
|
2245
2454
|
webSearchAllowed: options.webSearchAllowed,
|
|
2246
2455
|
appServer: options.appServer,
|
|
2247
2456
|
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2248
|
-
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames
|
|
2457
|
+
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
|
|
2458
|
+
shellEnvironment: options.shellEnvironment,
|
|
2459
|
+
disableLoginShell: options.disableLoginShell
|
|
2249
2460
|
}),
|
|
2250
2461
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
2251
2462
|
};
|
|
2252
2463
|
}
|
|
2253
2464
|
function buildCodexRuntimeThreadConfig(config, options = {}) {
|
|
2465
|
+
const configured = buildCodexProjectDocThreadConfig(config);
|
|
2254
2466
|
const codeModeConfig = {
|
|
2255
2467
|
...CODEX_CODE_MODE_THREAD_CONFIG,
|
|
2256
2468
|
"features.code_mode_only": options.nativeCodeModeOnlyEnabled === true
|
|
2257
2469
|
};
|
|
2258
2470
|
if (options.nativeCodeModeEnabled === false) {
|
|
2259
|
-
const disabledConfig = mergeCodexThreadConfigs(
|
|
2471
|
+
const disabledConfig = expectDefined(mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG), "Codex disabled code mode config");
|
|
2260
2472
|
delete disabledConfig["features.apply_patch_streaming_events"];
|
|
2261
2473
|
return disabledConfig;
|
|
2262
2474
|
}
|
|
2263
|
-
if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig,
|
|
2264
|
-
|
|
2265
|
-
...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
|
|
2266
|
-
"features.code_mode_only": true
|
|
2267
|
-
}, options.directOnlyToolNamespaces);
|
|
2268
|
-
return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG) ?? {
|
|
2269
|
-
...codeModeConfig,
|
|
2270
|
-
...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG
|
|
2271
|
-
}, options.directOnlyToolNamespaces);
|
|
2475
|
+
if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(expectDefined(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }), "Codex code mode only config"), options.directOnlyToolNamespaces);
|
|
2476
|
+
return ensureDirectOnlyToolNamespaces(expectDefined(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG), "Codex code mode config"), options.directOnlyToolNamespaces);
|
|
2272
2477
|
}
|
|
2273
2478
|
function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
|
|
2274
2479
|
if (!requiredNamespaces?.length) return config;
|
|
2275
|
-
const
|
|
2276
|
-
const
|
|
2480
|
+
const feature = expectDefined(config["features.code_mode"], "Codex code mode config");
|
|
2481
|
+
const configured = isJsonObject(feature) ? feature : { enabled: feature };
|
|
2482
|
+
const namespaces = Array.isArray(configured.direct_only_tool_namespaces) ? configured.direct_only_tool_namespaces.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
2277
2483
|
return {
|
|
2278
2484
|
...config,
|
|
2279
|
-
"code_mode
|
|
2485
|
+
"features.code_mode": {
|
|
2486
|
+
...configured,
|
|
2487
|
+
direct_only_tool_namespaces: [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
|
|
2488
|
+
}
|
|
2280
2489
|
};
|
|
2281
2490
|
}
|
|
2282
2491
|
function resolveDirectOnlyToolNamespaces(dynamicTools) {
|
|
@@ -2286,13 +2495,10 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2286
2495
|
const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
|
|
2287
2496
|
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
|
|
2288
2497
|
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
|
|
2498
|
+
const restrictedTurnDisablesProjectDocs = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted && params.disableTools;
|
|
2289
2499
|
const configMcpServers = config?.mcp_servers;
|
|
2290
2500
|
if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
|
|
2291
2501
|
const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
|
|
2292
|
-
const restrictedRunConfig = restrictedToolSurface && isJsonObject(configMcpServers) ? {
|
|
2293
|
-
...config,
|
|
2294
|
-
mcp_servers: {}
|
|
2295
|
-
} : config;
|
|
2296
2502
|
const webSearchConfig = resolveCodexWebSearchPlan({
|
|
2297
2503
|
config: params.config,
|
|
2298
2504
|
disableTools: params.disableTools,
|
|
@@ -2300,19 +2506,20 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2300
2506
|
nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
|
|
2301
2507
|
webSearchAllowed: options.webSearchAllowed
|
|
2302
2508
|
}).threadConfig;
|
|
2303
|
-
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
...CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG
|
|
2309
|
-
};
|
|
2509
|
+
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(config, webSearchConfig), options);
|
|
2510
|
+
return applyCodexManagedShellEnvironment({
|
|
2511
|
+
...mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") ? { "features.image_generation": false } : void 0, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0, params.delegationCapability === "report_only" ? CODEX_DELEGATION_DISABLED_THREAD_CONFIG : void 0, messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true ? buildRestrictedToolConfigPatch(restrictedToolSurfaceMcpServerNames) : buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, restrictedToolSurfaceMcpServerNames), restrictedTurnDisablesProjectDocs ? CODEX_NO_PROJECT_DOCS_CONFIG : void 0, params.authoredContextTokenCap === void 0 ? void 0 : { model_context_window: params.authoredContextTokenCap }) ?? baseConfig,
|
|
2512
|
+
...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
|
|
2513
|
+
}, options.shellEnvironment, options.disableLoginShell);
|
|
2310
2514
|
}
|
|
2311
2515
|
function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
|
|
2312
2516
|
if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
|
|
2313
|
-
return
|
|
2517
|
+
return {
|
|
2518
|
+
...buildRestrictedToolConfigPatch(inheritedMcpServerNames),
|
|
2519
|
+
...CODEX_NO_PROJECT_DOCS_CONFIG
|
|
2520
|
+
};
|
|
2314
2521
|
}
|
|
2315
|
-
function
|
|
2522
|
+
function buildRestrictedToolConfigPatch(inheritedMcpServerNames) {
|
|
2316
2523
|
const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
|
|
2317
2524
|
return {
|
|
2318
2525
|
...CODEX_RING_ZERO_THREAD_CONFIG,
|
|
@@ -2336,12 +2543,12 @@ async function readCodexInheritedMcpServerNames(client, cwd, signal) {
|
|
|
2336
2543
|
if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
|
|
2337
2544
|
return Object.keys(configuredServers).toSorted();
|
|
2338
2545
|
}
|
|
2339
|
-
async function
|
|
2546
|
+
async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, options, signal) {
|
|
2340
2547
|
const response = await client.request("configRequirements/read", void 0, { signal });
|
|
2341
2548
|
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
|
|
2342
2549
|
if (response.requirements === null) return;
|
|
2343
2550
|
if (!isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
|
|
2344
|
-
for (const key of [
|
|
2551
|
+
if (options.restrictedToolSurface) for (const key of [
|
|
2345
2552
|
"hooks",
|
|
2346
2553
|
"managedHooks",
|
|
2347
2554
|
"managed_hooks"
|
|
@@ -2351,6 +2558,7 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
|
|
|
2351
2558
|
if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
|
|
2352
2559
|
if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex restricted tool surface cannot override managed hooks");
|
|
2353
2560
|
}
|
|
2561
|
+
const additionalDeniedFeatures = new Set(options.additionalDeniedFeatures);
|
|
2354
2562
|
for (const key of ["featureRequirements", "feature_requirements"]) {
|
|
2355
2563
|
const requirements = response.requirements[key];
|
|
2356
2564
|
if (requirements === void 0 || requirements === null) continue;
|
|
@@ -2358,7 +2566,8 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
|
|
|
2358
2566
|
for (const [feature, enabled] of Object.entries(requirements)) {
|
|
2359
2567
|
if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2360
2568
|
const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
|
|
2361
|
-
|
|
2569
|
+
const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
|
|
2570
|
+
if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
|
|
2362
2571
|
}
|
|
2363
2572
|
}
|
|
2364
2573
|
}
|
|
@@ -2408,6 +2617,14 @@ function resolveCodexThreadEnvironmentSelection(options) {
|
|
|
2408
2617
|
}
|
|
2409
2618
|
//#endregion
|
|
2410
2619
|
//#region extensions/codex/src/app-server/thread-lifecycle-io.ts
|
|
2620
|
+
function resolveCodexThreadAgentDir(params) {
|
|
2621
|
+
const agentId = resolveSessionAgentIdsStrict({
|
|
2622
|
+
config: params.params.config,
|
|
2623
|
+
sessionKey: params.params.sessionKey,
|
|
2624
|
+
agentId: params.agentId ?? params.params.agentId
|
|
2625
|
+
}).sessionAgentId;
|
|
2626
|
+
return params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
|
|
2627
|
+
}
|
|
2411
2628
|
function resolveCodexThreadRolloutPath(thread) {
|
|
2412
2629
|
const rolloutPath = thread.path?.trim();
|
|
2413
2630
|
if (!rolloutPath || !path.isAbsolute(rolloutPath) || path.extname(rolloutPath) !== ".jsonl" || !path.basename(rolloutPath).includes(thread.id)) return;
|
|
@@ -2416,6 +2633,8 @@ function resolveCodexThreadRolloutPath(thread) {
|
|
|
2416
2633
|
async function resumeExistingCodexThread(params, context) {
|
|
2417
2634
|
const { binding: resumeBinding, bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, clearCurrentBinding } = context;
|
|
2418
2635
|
let resumeReservation;
|
|
2636
|
+
let resumeResponseAccepted = false;
|
|
2637
|
+
const abandonClient = params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client));
|
|
2419
2638
|
try {
|
|
2420
2639
|
const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
2421
2640
|
const finalConfigPatch = context.prebuiltFinalConfigPatch ?? params.buildFinalConfigPatch?.({
|
|
@@ -2429,6 +2648,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2429
2648
|
const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
|
|
2430
2649
|
const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
2431
2650
|
threadId: resumeBinding.threadId,
|
|
2651
|
+
cwd: params.cwd,
|
|
2432
2652
|
authProfileId,
|
|
2433
2653
|
model: startModelSelection.model,
|
|
2434
2654
|
modelProvider: startModelProvider,
|
|
@@ -2442,29 +2662,35 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2442
2662
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2443
2663
|
webSearchAllowed: params.webSearchAllowed,
|
|
2444
2664
|
hostSystemAgentActive,
|
|
2445
|
-
restrictedToolSurfaceInheritedMcpServerNames
|
|
2665
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
2666
|
+
shellEnvironment: params.shellEnvironment,
|
|
2667
|
+
disableLoginShell: params.disableLoginShell
|
|
2446
2668
|
}));
|
|
2447
2669
|
const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
|
|
2448
2670
|
throwIfAborted();
|
|
2449
|
-
if (resumeBinding.preserveNativeModel === true) {
|
|
2450
|
-
const current = await lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
|
|
2451
|
-
threadId: resumeBinding.threadId,
|
|
2452
|
-
includeTurns: false
|
|
2453
|
-
}, { signal: params.signal }));
|
|
2454
|
-
throwIfAborted();
|
|
2455
|
-
if (current.thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
|
|
2456
|
-
}
|
|
2457
2671
|
resumeReservation = params.reserveResumeThread?.(resumeBinding.threadId);
|
|
2458
2672
|
const response = await lifecycleTiming.measure("thread-resume-request", () => resumeCodexAppServerThread({
|
|
2459
2673
|
client: params.client,
|
|
2460
|
-
abandonClient
|
|
2674
|
+
abandonClient,
|
|
2461
2675
|
request: resumeParams,
|
|
2462
|
-
signal: params.signal
|
|
2676
|
+
signal: params.signal,
|
|
2677
|
+
assertCurrent: context.assertResumeOwnership,
|
|
2678
|
+
isPrewriteOwnershipError: (error) => error instanceof CodexAdoptedThreadActiveError
|
|
2463
2679
|
}));
|
|
2680
|
+
resumeResponseAccepted = true;
|
|
2681
|
+
assertCodexThreadAcceptsDirectInput(response.thread);
|
|
2682
|
+
context.assertResumeConfiguration?.();
|
|
2683
|
+
if (resumeBinding.pendingResumeConfiguration) await attestCodexPluginThreadApps({
|
|
2684
|
+
client: params.client,
|
|
2685
|
+
threadId: response.thread.id,
|
|
2686
|
+
appIds: context.prebuiltPluginThreadConfig?.provisionalAppIds ?? [],
|
|
2687
|
+
signal: params.signal
|
|
2688
|
+
});
|
|
2464
2689
|
if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
|
|
2465
2690
|
await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, resumeParams.config, params.signal));
|
|
2466
2691
|
} catch (error) {
|
|
2467
|
-
|
|
2692
|
+
context.assertResumeOwnership?.();
|
|
2693
|
+
await abandonClient();
|
|
2468
2694
|
throw new CodexRestrictedToolSurfaceAttestationError(error);
|
|
2469
2695
|
}
|
|
2470
2696
|
throwIfAborted();
|
|
@@ -2472,6 +2698,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2472
2698
|
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
|
|
2473
2699
|
const resumePatch = {
|
|
2474
2700
|
clientId: resolveCodexAppServerClientInstanceId(params.client),
|
|
2701
|
+
pendingResumeConfiguration: void 0,
|
|
2475
2702
|
cwd: params.cwd,
|
|
2476
2703
|
rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
|
|
2477
2704
|
authProfileId: boundAuthProfileId,
|
|
@@ -2502,7 +2729,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2502
2729
|
kind: "patch",
|
|
2503
2730
|
threadId: resumeBinding.threadId,
|
|
2504
2731
|
patch: resumePatch
|
|
2505
|
-
}))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
|
|
2732
|
+
}, context.assertResumeConfiguration))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
|
|
2506
2733
|
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
2507
2734
|
sessionId: params.params.sessionId,
|
|
2508
2735
|
sessionKey: params.params.sessionKey,
|
|
@@ -2539,19 +2766,26 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2539
2766
|
};
|
|
2540
2767
|
} catch (error) {
|
|
2541
2768
|
resumeReservation?.release();
|
|
2542
|
-
if (
|
|
2769
|
+
if (!resumeResponseAccepted && (!(error instanceof CodexAppServerRpcError) || isCodexAppServerOverloadError(error))) throw error;
|
|
2543
2770
|
if (error instanceof CodexRestrictedToolSurfaceAttestationError) {
|
|
2544
|
-
await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
|
|
2771
|
+
if (!resumeBinding.pendingResumeConfiguration) await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
|
|
2545
2772
|
throw error;
|
|
2546
2773
|
}
|
|
2547
|
-
if (
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2774
|
+
if (resumeResponseAccepted) {
|
|
2775
|
+
if (!await unsubscribeCodexThreadBestEffort(params.client, {
|
|
2776
|
+
threadId: resumeBinding.threadId,
|
|
2777
|
+
timeoutMs: 5e3,
|
|
2778
|
+
assertCurrent: context.assertResumeOwnership
|
|
2779
|
+
}).catch(() => false)) {
|
|
2780
|
+
try {
|
|
2781
|
+
await abandonClient();
|
|
2782
|
+
} catch (abandonError) {
|
|
2783
|
+
throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume client could not be retired", { cause: abandonError });
|
|
2784
|
+
}
|
|
2785
|
+
throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
if (resumeBinding.pendingResumeConfiguration || error instanceof CodexThreadDirectInputError || params.signal?.aborted) throw error;
|
|
2555
2789
|
embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
|
|
2556
2790
|
await clearCurrentBinding("rotating a stale thread binding");
|
|
2557
2791
|
}
|
|
@@ -2579,7 +2813,9 @@ async function startFreshCodexThread(params, context) {
|
|
|
2579
2813
|
model: startModelSelection.model,
|
|
2580
2814
|
modelProvider: startModelProvider,
|
|
2581
2815
|
hostSystemAgentActive,
|
|
2582
|
-
restrictedToolSurfaceInheritedMcpServerNames
|
|
2816
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
2817
|
+
shellEnvironment: params.shellEnvironment,
|
|
2818
|
+
disableLoginShell: params.disableLoginShell
|
|
2583
2819
|
}));
|
|
2584
2820
|
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
2585
2821
|
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
@@ -2647,6 +2883,7 @@ async function startFreshCodexThread(params, context) {
|
|
|
2647
2883
|
cwd: params.cwd,
|
|
2648
2884
|
...rolloutPath ? { rolloutPath } : {},
|
|
2649
2885
|
authProfileId: params.params.authProfileId,
|
|
2886
|
+
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
2650
2887
|
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
2651
2888
|
modelProvider: bindingModelProvider,
|
|
2652
2889
|
dynamicToolsFingerprint,
|
|
@@ -2679,6 +2916,12 @@ async function startFreshCodexThread(params, context) {
|
|
|
2679
2916
|
throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after a binding replacement conflict", cause === void 0 ? void 0 : { cause });
|
|
2680
2917
|
}
|
|
2681
2918
|
};
|
|
2919
|
+
const managedSourceHomeId = codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, resolveCodexThreadAgentDir(params)));
|
|
2920
|
+
await lifecycleTiming.measure("thread-start-mark-managed", () => markStartedCodexManagedThread(params.bindingStore.managedThreads, {
|
|
2921
|
+
sourceHomeId: managedSourceHomeId,
|
|
2922
|
+
threadId: response.thread.id,
|
|
2923
|
+
...rolloutPath ? { rolloutPath } : {}
|
|
2924
|
+
}));
|
|
2682
2925
|
let committed;
|
|
2683
2926
|
try {
|
|
2684
2927
|
committed = await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, replacementPredecessor ? {
|
|
@@ -2716,241 +2959,54 @@ async function startFreshCodexThread(params, context) {
|
|
|
2716
2959
|
threadId: response.thread.id,
|
|
2717
2960
|
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
2718
2961
|
});
|
|
2719
|
-
return {
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
ringZeroConfigFingerprint,
|
|
2734
|
-
ringZeroClientInstanceId,
|
|
2735
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2736
|
-
networkProxyConfigFingerprint,
|
|
2737
|
-
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
2738
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
2739
|
-
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
2740
|
-
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
2741
|
-
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
2742
|
-
contextEngine: contextEngineBinding,
|
|
2743
|
-
environmentSelectionFingerprint,
|
|
2744
|
-
...!preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
|
|
2745
|
-
...startParams,
|
|
2746
|
-
model: response.model ?? startParams.model ?? null,
|
|
2747
|
-
requestedModel: startParams.model ?? null,
|
|
2748
|
-
modelProvider: bindingModelProvider ?? null,
|
|
2749
|
-
requestedModelProvider: startParams.modelProvider ?? bindingModelProvider ?? null
|
|
2750
|
-
}, params.params.authProfileId, dynamicToolsFingerprint) } : {},
|
|
2751
|
-
lifecycle: {
|
|
2752
|
-
action: "started",
|
|
2753
|
-
...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
|
|
2754
|
-
}
|
|
2755
|
-
};
|
|
2962
|
+
return buildStartedCodexThreadBinding({
|
|
2963
|
+
bindingModelProvider,
|
|
2964
|
+
clientId,
|
|
2965
|
+
context,
|
|
2966
|
+
finalConfigPatch,
|
|
2967
|
+
nextMcpServersFingerprint,
|
|
2968
|
+
params,
|
|
2969
|
+
pluginThreadConfig,
|
|
2970
|
+
response,
|
|
2971
|
+
rolloutPath,
|
|
2972
|
+
startModelProvider: requestModelProvider ?? startModelProvider,
|
|
2973
|
+
startParams,
|
|
2974
|
+
modelProvider
|
|
2975
|
+
});
|
|
2756
2976
|
}
|
|
2757
2977
|
//#endregion
|
|
2758
|
-
//#region extensions/codex/src/app-server/
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2978
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
|
|
2979
|
+
/** Preserves the caller's abort reason across thread ownership transitions. */
|
|
2980
|
+
function throwIfCodexThreadLifecycleAborted(signal) {
|
|
2981
|
+
if (!signal?.aborted) return;
|
|
2982
|
+
const reason = signal.reason;
|
|
2983
|
+
if (reason instanceof Error) throw reason;
|
|
2984
|
+
const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
|
|
2985
|
+
error.name = "AbortError";
|
|
2986
|
+
throw error;
|
|
2763
2987
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
return
|
|
2988
|
+
/** Releases consumed subscription ownership or retires an unsafe client. */
|
|
2989
|
+
async function releaseCodexConsumedLiveThread(options) {
|
|
2990
|
+
if (await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => unsubscribeCodexThreadBestEffort(options.client, {
|
|
2991
|
+
threadId: options.threadId,
|
|
2992
|
+
timeoutMs: 5e3,
|
|
2993
|
+
assertCurrent: options.assertCurrent
|
|
2994
|
+
}))) return;
|
|
2995
|
+
return await abandonCodexLiveThreadRelease(options, options.cause);
|
|
2772
2996
|
}
|
|
2773
|
-
function
|
|
2774
|
-
|
|
2775
|
-
|
|
2997
|
+
async function abandonCodexLiveThreadRelease(options, cause) {
|
|
2998
|
+
options.assertCurrent?.();
|
|
2999
|
+
await (options.abandonClient ?? (() => closeCodexStartupClientBestEffort(options.client)))();
|
|
3000
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${options.threadId}`, cause !== void 0 ? { cause } : void 0);
|
|
2776
3001
|
}
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
},
|
|
2786
|
-
mark() {},
|
|
2787
|
-
logSummary() {}
|
|
2788
|
-
};
|
|
2789
|
-
const now = options.now ?? Date.now;
|
|
2790
|
-
const startedAt = now();
|
|
2791
|
-
let didLog = false;
|
|
2792
|
-
const spans = [];
|
|
2793
|
-
const toMs = (value) => Math.max(0, Math.round(value));
|
|
2794
|
-
const record = (name, spanStartedAt) => {
|
|
2795
|
-
const currentAt = now();
|
|
2796
|
-
spans.push({
|
|
2797
|
-
name,
|
|
2798
|
-
durationMs: toMs(currentAt - spanStartedAt),
|
|
2799
|
-
elapsedMs: toMs(currentAt - startedAt)
|
|
2800
|
-
});
|
|
2801
|
-
};
|
|
2802
|
-
const snapshot = () => ({
|
|
2803
|
-
totalMs: toMs(now() - startedAt),
|
|
2804
|
-
spans: spans.slice()
|
|
2805
|
-
});
|
|
2806
|
-
return {
|
|
2807
|
-
async measure(name, run) {
|
|
2808
|
-
const spanStartedAt = now();
|
|
2809
|
-
try {
|
|
2810
|
-
return await run();
|
|
2811
|
-
} finally {
|
|
2812
|
-
record(name, spanStartedAt);
|
|
2813
|
-
}
|
|
2814
|
-
},
|
|
2815
|
-
measureSync(name, run) {
|
|
2816
|
-
const spanStartedAt = now();
|
|
2817
|
-
try {
|
|
2818
|
-
return run();
|
|
2819
|
-
} finally {
|
|
2820
|
-
record(name, spanStartedAt);
|
|
2821
|
-
}
|
|
2822
|
-
},
|
|
2823
|
-
mark(name) {
|
|
2824
|
-
record(name, now());
|
|
2825
|
-
},
|
|
2826
|
-
logSummary(params) {
|
|
2827
|
-
if (didLog) return;
|
|
2828
|
-
const summary = snapshot();
|
|
2829
|
-
const shouldWarn = shouldWarnCodexThreadLifecycleTimingSummary(summary, options);
|
|
2830
|
-
if (!shouldWarn && !log.isEnabled?.("trace")) return;
|
|
2831
|
-
didLog = true;
|
|
2832
|
-
const message = formatCodexThreadLifecycleTimingSummary({
|
|
2833
|
-
runId: params.runId,
|
|
2834
|
-
sessionId: params.sessionId,
|
|
2835
|
-
sessionKey: params.sessionKey,
|
|
2836
|
-
action: params.action,
|
|
2837
|
-
summary
|
|
2838
|
-
});
|
|
2839
|
-
const meta = {
|
|
2840
|
-
runId: params.runId,
|
|
2841
|
-
sessionId: params.sessionId,
|
|
2842
|
-
sessionKey: params.sessionKey,
|
|
2843
|
-
action: params.action,
|
|
2844
|
-
threadId: params.threadId,
|
|
2845
|
-
totalMs: summary.totalMs,
|
|
2846
|
-
spans: summary.spans
|
|
2847
|
-
};
|
|
2848
|
-
if (shouldWarn) log.warn(message, meta);
|
|
2849
|
-
else log.trace(message, meta);
|
|
2850
|
-
}
|
|
2851
|
-
};
|
|
2852
|
-
}
|
|
2853
|
-
//#endregion
|
|
2854
|
-
//#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
|
|
2855
|
-
async function prepareCodexThreadLifecyclePreflight(params) {
|
|
2856
|
-
const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
|
|
2857
|
-
...params.timing,
|
|
2858
|
-
enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
|
|
2859
|
-
});
|
|
2860
|
-
const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync("legacy-dynamic-tools-fingerprint", () => codexLegacyDynamicToolsFingerprint(params.dynamicTools));
|
|
2861
|
-
const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => hashCodexAppServerBindingFingerprint(legacyDynamicToolsFingerprint));
|
|
2862
|
-
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
2863
|
-
const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
2864
|
-
config: params.params.config,
|
|
2865
|
-
disableTools: params.params.disableTools,
|
|
2866
|
-
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
2867
|
-
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
2868
|
-
webSearchAllowed: params.webSearchAllowed
|
|
2869
|
-
})).threadConfig);
|
|
2870
|
-
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
2871
|
-
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
2872
|
-
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRuntime(params.params.config, {
|
|
2873
|
-
agentId: params.agentId ?? params.params.agentId,
|
|
2874
|
-
agentDir: params.params.agentDir,
|
|
2875
|
-
allowLiteralOAuthProjection: params.appServer.connectionClass !== "remote",
|
|
2876
|
-
toolOverrides: params.params.toolOverrides,
|
|
2877
|
-
onServerUnavailable: (serverName, error) => embeddedAgentLog.warn("skipping unavailable MCP OAuth server", {
|
|
2878
|
-
serverName,
|
|
2879
|
-
error: formatErrorMessage(error)
|
|
2880
|
-
})
|
|
2881
|
-
});
|
|
2882
|
-
const nativeSkillIsolation = await lifecycleTiming.measure("native-skill-isolation", () => resolveCodexNativeSkillIsolation({
|
|
2883
|
-
client: params.client,
|
|
2884
|
-
codexHome: params.appServer.start.env?.CODEX_HOME,
|
|
2885
|
-
cwd: params.cwd,
|
|
2886
|
-
home: params.appServer.start.env?.HOME,
|
|
2887
|
-
signal: params.signal,
|
|
2888
|
-
userProfile: params.appServer.start.env?.USERPROFILE
|
|
2889
|
-
}));
|
|
2890
|
-
const nativeSkillIsolationFingerprint = nativeSkillIsolation ? fingerprintJsonObject({
|
|
2891
|
-
version: 1,
|
|
2892
|
-
disabledUserSkillPaths: nativeSkillIsolation.disabledUserSkillPaths
|
|
2893
|
-
}) : void 0;
|
|
2894
|
-
const legacyUserMcpServersFingerprint = legacyFingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
2895
|
-
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
2896
|
-
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
2897
|
-
const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
|
|
2898
|
-
const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
|
|
2899
|
-
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
|
|
2900
|
-
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
|
|
2901
|
-
if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
|
|
2902
|
-
const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
|
|
2903
|
-
if (restrictedToolSurface) await lifecycleTiming.measure("restricted-tool-surface-config-requirements-read", () => assertCodexRestrictedToolSurfaceHasNoManagedHooks(params.client, params.signal));
|
|
2904
|
-
const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
|
|
2905
|
-
version: 1,
|
|
2906
|
-
baseInstructions: "",
|
|
2907
|
-
config: buildCodexRingZeroThreadConfigPatch(params.params, true, restrictedToolSurfaceInheritedMcpServerNames)
|
|
2908
|
-
}) : void 0;
|
|
2909
|
-
return {
|
|
2910
|
-
contextEngineBinding,
|
|
2911
|
-
dynamicToolsContainDeferred,
|
|
2912
|
-
dynamicToolsFingerprint,
|
|
2913
|
-
environmentSelectionFingerprint,
|
|
2914
|
-
hostSystemAgentActive,
|
|
2915
|
-
legacyDynamicToolsFingerprint,
|
|
2916
|
-
legacyUserMcpServersFingerprint,
|
|
2917
|
-
lifecycleTiming,
|
|
2918
|
-
nativeSkillIsolation,
|
|
2919
|
-
nativeSkillIsolationFingerprint,
|
|
2920
|
-
networkProxyConfigFingerprint,
|
|
2921
|
-
ringZeroActive,
|
|
2922
|
-
ringZeroClientInstanceId: ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0,
|
|
2923
|
-
ringZeroConfigFingerprint,
|
|
2924
|
-
restrictedToolSurface,
|
|
2925
|
-
restrictedToolSurfaceInheritedMcpServerNames,
|
|
2926
|
-
userMcpServersConfigPatch,
|
|
2927
|
-
userMcpServersFingerprint,
|
|
2928
|
-
webSearchThreadConfigFingerprint
|
|
2929
|
-
};
|
|
2930
|
-
}
|
|
2931
|
-
//#endregion
|
|
2932
|
-
//#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
|
|
2933
|
-
/** Preserves the caller's abort reason across thread ownership transitions. */
|
|
2934
|
-
function throwIfCodexThreadLifecycleAborted(signal) {
|
|
2935
|
-
if (!signal?.aborted) return;
|
|
2936
|
-
const reason = signal.reason;
|
|
2937
|
-
if (reason instanceof Error) throw reason;
|
|
2938
|
-
const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
|
|
2939
|
-
error.name = "AbortError";
|
|
2940
|
-
throw error;
|
|
2941
|
-
}
|
|
2942
|
-
/** Releases consumed subscription ownership or retires an unsafe client. */
|
|
2943
|
-
async function releaseCodexConsumedLiveThread(options) {
|
|
2944
|
-
if (await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => unsubscribeCodexThreadBestEffort(options.client, {
|
|
2945
|
-
threadId: options.threadId,
|
|
2946
|
-
timeoutMs: 5e3
|
|
2947
|
-
}))) return;
|
|
2948
|
-
await (options.abandonClient ?? (() => closeCodexStartupClientBestEffort(options.client)))();
|
|
2949
|
-
throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${options.threadId}`, options.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
2950
|
-
}
|
|
2951
|
-
/** Transfers retained-slot ownership before unconditionally releasing it. */
|
|
2952
|
-
async function releaseCodexRetainedLiveThread(options) {
|
|
2953
|
-
if (await consumeCodexAppServerLiveThread(options.client, options.threadId)) await releaseCodexConsumedLiveThread(options);
|
|
3002
|
+
/** Releases through the retained owner, preserving its guarded callback and rollback. */
|
|
3003
|
+
async function releaseCodexRetainedLiveThread(options) {
|
|
3004
|
+
try {
|
|
3005
|
+
return await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => releaseCodexAppServerLiveThread(options.client, options.threadId, options.assertCurrent));
|
|
3006
|
+
} catch (error) {
|
|
3007
|
+
if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
|
|
3008
|
+
return await abandonCodexLiveThreadRelease(options, error);
|
|
3009
|
+
}
|
|
2954
3010
|
}
|
|
2955
3011
|
/** Reuses one safely owned, fully matching subscription on its original client. */
|
|
2956
3012
|
async function tryReuseCodexLiveThread(options) {
|
|
@@ -2968,6 +3024,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
2968
3024
|
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
|
|
2969
3025
|
const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
2970
3026
|
threadId: binding.threadId,
|
|
3027
|
+
cwd: params.cwd,
|
|
2971
3028
|
authProfileId: resumeAuthProfileId,
|
|
2972
3029
|
model: startModelSelection.model,
|
|
2973
3030
|
modelProvider: startModelProvider,
|
|
@@ -2981,7 +3038,9 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
2981
3038
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2982
3039
|
webSearchAllowed: params.webSearchAllowed,
|
|
2983
3040
|
hostSystemAgentActive,
|
|
2984
|
-
restrictedToolSurfaceInheritedMcpServerNames
|
|
3041
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3042
|
+
shellEnvironment: params.shellEnvironment,
|
|
3043
|
+
disableLoginShell: params.disableLoginShell
|
|
2985
3044
|
}));
|
|
2986
3045
|
const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
|
|
2987
3046
|
...resumeParams,
|
|
@@ -3038,6 +3097,667 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3038
3097
|
}
|
|
3039
3098
|
}
|
|
3040
3099
|
//#endregion
|
|
3100
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-adoption.ts
|
|
3101
|
+
/** Passive refusal must precede releasing or acquiring any native subscription. */
|
|
3102
|
+
async function assertAdoptedCodexThreadResumeAllowed(params, threadId, context) {
|
|
3103
|
+
const { thread } = await context.lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
|
|
3104
|
+
threadId,
|
|
3105
|
+
includeTurns: false
|
|
3106
|
+
}, { signal: params.signal }));
|
|
3107
|
+
context.throwIfAborted();
|
|
3108
|
+
assertCodexThreadAcceptsDirectInput(thread);
|
|
3109
|
+
if (thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
|
|
3110
|
+
}
|
|
3111
|
+
/** Preserve attach's native-queue-before-binding-lease order when consuming pending intent. */
|
|
3112
|
+
async function withCodexThreadLifecycleBinding(params, run) {
|
|
3113
|
+
const identity = sessionBindingIdentity({
|
|
3114
|
+
sessionId: params.params.sessionId,
|
|
3115
|
+
sessionKey: params.params.sessionKey,
|
|
3116
|
+
agentId: params.agentId ?? params.params.agentId,
|
|
3117
|
+
config: params.params.config
|
|
3118
|
+
});
|
|
3119
|
+
const snapshot = await params.bindingStore.read(identity);
|
|
3120
|
+
const pendingThreadId = snapshot?.pendingResumeConfiguration ? snapshot.threadId : void 0;
|
|
3121
|
+
const runWithLease = () => params.bindingStore.withLease(identity, async () => {
|
|
3122
|
+
const binding = await params.bindingStore.read(identity);
|
|
3123
|
+
if (pendingThreadId && (binding?.threadId !== pendingThreadId || !binding.pendingResumeConfiguration)) throw new CodexThreadBindingConflictError(pendingThreadId, "acquiring a pending resume configuration");
|
|
3124
|
+
if (!pendingThreadId && binding?.pendingResumeConfiguration) throw new CodexThreadBindingConflictError(binding.threadId, "acquiring a pending resume configuration");
|
|
3125
|
+
return await run(identity, binding);
|
|
3126
|
+
});
|
|
3127
|
+
return pendingThreadId ? await withExclusiveCodexAppServerThread({
|
|
3128
|
+
bindingStore: params.bindingStore,
|
|
3129
|
+
identity,
|
|
3130
|
+
threadId: pendingThreadId,
|
|
3131
|
+
run: runWithLease
|
|
3132
|
+
}) : await runWithLease();
|
|
3133
|
+
}
|
|
3134
|
+
/** Completes manual attachment only under the native queue and exact binding lease. */
|
|
3135
|
+
async function resumePendingCodexThread(params, context) {
|
|
3136
|
+
const { binding, contextEngineBinding, lifecycleTiming, restrictedToolSurface } = context;
|
|
3137
|
+
if (isIncognitoSessionKey(params.params.sessionKey) || context.transientRestriction || !restrictedToolSurface && binding.nativeToolPolicyRestricted === true || (contextEngineBinding ? !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding) : binding.contextEngine !== void 0) || shouldRotateCodexGpt56MultiAgentBinding({
|
|
3138
|
+
bindingModel: binding.model,
|
|
3139
|
+
requestedModel: params.params.modelId
|
|
3140
|
+
})) throw new Error(`Cannot configure resumed Codex thread ${binding.threadId} under a transient or incompatible session policy. The thread is preserved; retry from its normal session or use /new for the current policy.`);
|
|
3141
|
+
const prebuiltPluginThreadConfig = params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
3142
|
+
const clientId = resolveCodexAppServerClientInstanceId(params.client);
|
|
3143
|
+
const configuration = await preparePendingCodexThreadResume(params, binding, context.dynamicToolsFingerprint, async (assertCurrent) => {
|
|
3144
|
+
const released = await context.releaseRetainedThread(binding.threadId, assertCurrent);
|
|
3145
|
+
assertCurrent();
|
|
3146
|
+
if (!released || binding.clientId && binding.clientId !== clientId) await releaseCodexConsumedLiveThread({
|
|
3147
|
+
client: params.client,
|
|
3148
|
+
abandonClient: params.abandonClient,
|
|
3149
|
+
lifecycleTiming,
|
|
3150
|
+
threadId: binding.threadId,
|
|
3151
|
+
assertCurrent
|
|
3152
|
+
});
|
|
3153
|
+
});
|
|
3154
|
+
try {
|
|
3155
|
+
const resumed = await resumeExistingCodexThread(params, {
|
|
3156
|
+
...context,
|
|
3157
|
+
prebuiltPluginThreadConfig,
|
|
3158
|
+
assertResumeConfiguration: configuration.assertConfigured,
|
|
3159
|
+
assertResumeOwnership: configuration.assertCurrent
|
|
3160
|
+
});
|
|
3161
|
+
if (!resumed) throw new Error(`Codex did not configure resumed thread ${binding.threadId}.`);
|
|
3162
|
+
return resumed;
|
|
3163
|
+
} finally {
|
|
3164
|
+
configuration.dispose();
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
/** Manual attachment is intent, never evidence that loaded native overrides took effect. */
|
|
3168
|
+
async function preparePendingCodexThreadResume(params, binding, dynamicToolsFingerprint, releaseSubscription) {
|
|
3169
|
+
const fail = (reason) => /* @__PURE__ */ new Error(`Cannot configure resumed Codex thread ${binding.threadId}: ${reason}. The thread is preserved; continue it in native Codex or use /new for the current OpenClaw tools.`);
|
|
3170
|
+
const agentDir = resolveCodexThreadAgentDir(params);
|
|
3171
|
+
const localHome = resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir);
|
|
3172
|
+
if (params.appServer.start.transport !== "stdio" || params.appServer.start.homeScope === "user" || path.resolve(localHome) !== resolveCodexAppServerHomeDir(agentDir) || binding.connectionScope === "supervision" || binding.preserveNativeModel === true) throw fail("configuration adoption requires an OpenClaw-owned local Codex home");
|
|
3173
|
+
if (isCodexAppServerLiveThreadClaimed(params.client, binding.threadId)) throw fail("the thread is claimed by active work; stop that run before resuming");
|
|
3174
|
+
const assertCurrent = captureExclusiveSharedCodexAppServerClient(params.client);
|
|
3175
|
+
const { thread } = await params.client.request("thread/read", {
|
|
3176
|
+
threadId: binding.threadId,
|
|
3177
|
+
includeTurns: false
|
|
3178
|
+
}, { signal: params.signal });
|
|
3179
|
+
assertCurrent();
|
|
3180
|
+
const statusType = thread.status?.type;
|
|
3181
|
+
if (thread.id !== binding.threadId || statusType !== "idle" && statusType !== "notLoaded") throw fail("the native thread is not idle; wait for its current run to finish");
|
|
3182
|
+
assertCodexThreadAcceptsDirectInput(thread);
|
|
3183
|
+
let unloaded = statusType === "notLoaded";
|
|
3184
|
+
const dispose = params.client.addNotificationHandler((notification) => {
|
|
3185
|
+
if (notification.method === "thread/status/changed" && isJsonObject(notification.params) && notification.params.threadId === binding.threadId && isJsonObject(notification.params.status) && notification.params.status.type === "notLoaded") unloaded = true;
|
|
3186
|
+
});
|
|
3187
|
+
try {
|
|
3188
|
+
const rolloutPath = thread.path ?? binding.rolloutPath;
|
|
3189
|
+
const metadata = rolloutPath ? await readCodexSessionMeta(path.join(localHome, "sessions"), rolloutPath, binding.threadId) : void 0;
|
|
3190
|
+
if (!metadata) throw fail("its native tool catalog could not be read from the selected Codex home");
|
|
3191
|
+
const recordedTools = metadata.dynamic_tools ?? [];
|
|
3192
|
+
if (!Array.isArray(recordedTools) || codexDynamicToolsFingerprint(recordedTools) !== dynamicToolsFingerprint) throw fail("its immutable native tool catalog does not match the current OpenClaw tools");
|
|
3193
|
+
assertCurrent();
|
|
3194
|
+
await releaseSubscription(assertCurrent);
|
|
3195
|
+
assertCurrent();
|
|
3196
|
+
return {
|
|
3197
|
+
assertConfigured: () => {
|
|
3198
|
+
assertCurrent();
|
|
3199
|
+
if (!unloaded) throw fail("Codex did not confirm unloading its previous configuration");
|
|
3200
|
+
},
|
|
3201
|
+
assertCurrent,
|
|
3202
|
+
dispose
|
|
3203
|
+
};
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
dispose();
|
|
3206
|
+
throw error;
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
//#endregion
|
|
3210
|
+
//#region extensions/codex/src/app-server/native-hook-relay-state.ts
|
|
3211
|
+
const pending = /* @__PURE__ */ new Set();
|
|
3212
|
+
/** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
|
|
3213
|
+
const nativeHookRelayUnregisterQueue = {
|
|
3214
|
+
add(entry) {
|
|
3215
|
+
pending.add(entry);
|
|
3216
|
+
},
|
|
3217
|
+
delete(entry) {
|
|
3218
|
+
return pending.delete(entry);
|
|
3219
|
+
},
|
|
3220
|
+
flush() {
|
|
3221
|
+
while (pending.size > 0) {
|
|
3222
|
+
const entry = pending.values().next().value;
|
|
3223
|
+
if (!entry) return;
|
|
3224
|
+
clearTimeout(entry.timeout);
|
|
3225
|
+
entry.unregister();
|
|
3226
|
+
}
|
|
3227
|
+
},
|
|
3228
|
+
clear() {
|
|
3229
|
+
for (const entry of pending) clearTimeout(entry.timeout);
|
|
3230
|
+
pending.clear();
|
|
3231
|
+
}
|
|
3232
|
+
};
|
|
3233
|
+
//#endregion
|
|
3234
|
+
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
3235
|
+
/**
|
|
3236
|
+
* Bridges Codex native hook callbacks into OpenClaw's native hook relay so
|
|
3237
|
+
* app-server tool events can still run OpenClaw policy and diagnostics.
|
|
3238
|
+
*/
|
|
3239
|
+
/** Codex hook events that can be registered through OpenClaw's native relay. */
|
|
3240
|
+
const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
|
3241
|
+
"pre_tool_use",
|
|
3242
|
+
"post_tool_use",
|
|
3243
|
+
"permission_request",
|
|
3244
|
+
"before_agent_finalize"
|
|
3245
|
+
];
|
|
3246
|
+
const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
|
|
3247
|
+
const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
|
|
3248
|
+
/** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
|
|
3249
|
+
const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
|
|
3250
|
+
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
|
|
3251
|
+
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
|
|
3252
|
+
const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
|
|
3253
|
+
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
|
|
3254
|
+
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
|
|
3255
|
+
const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
|
|
3256
|
+
const nativeHookPolicyByClient = /* @__PURE__ */ new WeakMap();
|
|
3257
|
+
const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
|
|
3258
|
+
exec: [
|
|
3259
|
+
"Bash",
|
|
3260
|
+
"exec",
|
|
3261
|
+
"exec_command"
|
|
3262
|
+
],
|
|
3263
|
+
apply_patch: [
|
|
3264
|
+
"apply_patch",
|
|
3265
|
+
"Write",
|
|
3266
|
+
"Edit"
|
|
3267
|
+
],
|
|
3268
|
+
spawn_agent: ["spawn_agent", "Agent"]
|
|
3269
|
+
};
|
|
3270
|
+
/** Enterprise managed-only policy silently drops the session-layer hooks that enforce OpenClaw. */
|
|
3271
|
+
async function assertCodexNativeHookRelayAllowed(client, signal) {
|
|
3272
|
+
let attestation = nativeHookPolicyByClient.get(client);
|
|
3273
|
+
if (!attestation) {
|
|
3274
|
+
attestation = client.request("configRequirements/read", void 0, { signal }).then((response) => {
|
|
3275
|
+
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid hook policy response");
|
|
3276
|
+
const requirements = response.requirements;
|
|
3277
|
+
if (requirements === null) return;
|
|
3278
|
+
if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid hook policy requirements");
|
|
3279
|
+
const managedOnly = requirements.allowManagedHooksOnly;
|
|
3280
|
+
if (managedOnly !== void 0 && managedOnly !== null && typeof managedOnly !== "boolean") throw new Error("Codex configRequirements/read returned invalid managed-only hook policy");
|
|
3281
|
+
if (managedOnly === true) throw new Error("Codex managed-only hooks disable the OpenClaw native hook relay; refusing unenforced execution");
|
|
3282
|
+
});
|
|
3283
|
+
nativeHookPolicyByClient.set(client, attestation);
|
|
3284
|
+
attestation.catch(() => {
|
|
3285
|
+
if (nativeHookPolicyByClient.get(client) === attestation) nativeHookPolicyByClient.delete(client);
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
await attestation;
|
|
3289
|
+
}
|
|
3290
|
+
/** Defers relay unregister so late native hook subprocesses can still resolve. */
|
|
3291
|
+
function scheduleCodexNativeHookRelayUnregister(params) {
|
|
3292
|
+
let pending;
|
|
3293
|
+
const unregister = () => {
|
|
3294
|
+
if (!pending) return;
|
|
3295
|
+
const current = pending;
|
|
3296
|
+
pending = void 0;
|
|
3297
|
+
if (!nativeHookRelayUnregisterQueue.delete(current)) return;
|
|
3298
|
+
params.relay.unregister();
|
|
3299
|
+
};
|
|
3300
|
+
const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
|
|
3301
|
+
pending = {
|
|
3302
|
+
timeout,
|
|
3303
|
+
unregister
|
|
3304
|
+
};
|
|
3305
|
+
nativeHookRelayUnregisterQueue.add(pending);
|
|
3306
|
+
timeout.unref();
|
|
3307
|
+
}
|
|
3308
|
+
/** Computes the delayed unregister window from Codex's hook timeout. */
|
|
3309
|
+
function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
|
|
3310
|
+
const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
|
|
3311
|
+
return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
|
|
3312
|
+
}
|
|
3313
|
+
/** Records a native pre-tool failure that Codex does not project as a tool item. */
|
|
3314
|
+
function emitCodexNativePreToolUseFailureDiagnostic(params) {
|
|
3315
|
+
emitTrustedDiagnosticEvent({
|
|
3316
|
+
type: "tool.execution.error",
|
|
3317
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
3318
|
+
sessionId: params.sessionId,
|
|
3319
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3320
|
+
runId: params.runId,
|
|
3321
|
+
toolName: params.failure.toolName,
|
|
3322
|
+
toolCallId: params.failure.toolCallId,
|
|
3323
|
+
durationMs: params.failure.durationMs,
|
|
3324
|
+
errorCategory: "before_tool_call",
|
|
3325
|
+
terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
|
|
3326
|
+
...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
/** Registers an OpenClaw native hook relay for a Codex app-server turn. */
|
|
3330
|
+
function createCodexNativeHookRelay(params) {
|
|
3331
|
+
if (params.options?.enabled === false) return;
|
|
3332
|
+
const directChildClaims = /* @__PURE__ */ new Map();
|
|
3333
|
+
const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
|
|
3334
|
+
let foregroundClosed = false;
|
|
3335
|
+
let successfulYieldRetentionAuthorized = false;
|
|
3336
|
+
const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
|
|
3337
|
+
const rejectPendingAdmissions = (reason) => {
|
|
3338
|
+
for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
|
|
3339
|
+
pendingDirectChildAdmissions.clear();
|
|
3340
|
+
};
|
|
3341
|
+
const relay = registerRetainedNativeHookRelayForBundledRuntime({
|
|
3342
|
+
provider: "codex",
|
|
3343
|
+
relayId: buildCodexNativeHookRelayId({
|
|
3344
|
+
agentId: params.agentId,
|
|
3345
|
+
sessionId: params.sessionId,
|
|
3346
|
+
sessionKey: params.sessionKey
|
|
3347
|
+
}),
|
|
3348
|
+
...params.generation ? { generation: params.generation } : {},
|
|
3349
|
+
...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
|
|
3350
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
3351
|
+
sessionId: params.sessionId,
|
|
3352
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3353
|
+
...params.config ? { config: params.config } : {},
|
|
3354
|
+
runId: params.runId,
|
|
3355
|
+
...params.channelId ? { channelId: params.channelId } : {},
|
|
3356
|
+
...params.requester ? { requester: params.requester } : {},
|
|
3357
|
+
...params.approvalContext ? { approvalContext: params.approvalContext } : {},
|
|
3358
|
+
allowedEvents: params.events,
|
|
3359
|
+
preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
|
|
3360
|
+
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
3361
|
+
explicitTtlMs: params.options?.ttlMs,
|
|
3362
|
+
attemptTimeoutMs: params.attemptTimeoutMs,
|
|
3363
|
+
startupTimeoutMs: params.startupTimeoutMs,
|
|
3364
|
+
turnStartTimeoutMs: params.turnStartTimeoutMs
|
|
3365
|
+
}),
|
|
3366
|
+
signal: params.signal,
|
|
3367
|
+
runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
|
|
3368
|
+
assertActive: params.hostCapabilities.assertActive,
|
|
3369
|
+
retention: {
|
|
3370
|
+
readClaim: readCodexNativeChildThreadId,
|
|
3371
|
+
shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
|
|
3372
|
+
allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
|
|
3373
|
+
awaitForegroundAdmission: (childThreadId) => {
|
|
3374
|
+
if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
|
|
3375
|
+
const existingClaim = directChildClaims.get(childThreadId);
|
|
3376
|
+
if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
|
|
3377
|
+
const existingPending = pendingDirectChildAdmissions.get(childThreadId);
|
|
3378
|
+
if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
|
|
3379
|
+
if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
|
|
3380
|
+
const { promise, resolve, reject } = createDeferred();
|
|
3381
|
+
pendingDirectChildAdmissions.set(childThreadId, {
|
|
3382
|
+
promise,
|
|
3383
|
+
resolve,
|
|
3384
|
+
reject
|
|
3385
|
+
});
|
|
3386
|
+
return promise.then((claim) => assertClaim(childThreadId, claim));
|
|
3387
|
+
},
|
|
3388
|
+
onDispose: () => {
|
|
3389
|
+
foregroundClosed = true;
|
|
3390
|
+
rejectPendingAdmissions("native hook relay registration closed");
|
|
3391
|
+
}
|
|
3392
|
+
},
|
|
3393
|
+
onPreToolUseFailure: params.onPreToolUseFailure,
|
|
3394
|
+
command: {
|
|
3395
|
+
nice: 10,
|
|
3396
|
+
timeoutMs: params.options?.gatewayTimeoutMs
|
|
3397
|
+
}
|
|
3398
|
+
});
|
|
3399
|
+
const unregister = () => {
|
|
3400
|
+
foregroundClosed = true;
|
|
3401
|
+
rejectPendingAdmissions("native hook relay foreground closed");
|
|
3402
|
+
relay.unregister();
|
|
3403
|
+
};
|
|
3404
|
+
return {
|
|
3405
|
+
...relay,
|
|
3406
|
+
unregister,
|
|
3407
|
+
authorizeRetentionAfterSuccessfulYield: () => {
|
|
3408
|
+
successfulYieldRetentionAuthorized = true;
|
|
3409
|
+
},
|
|
3410
|
+
hasClaimedDirectChild: () => directChildClaims.size > 0,
|
|
3411
|
+
rejectPendingDirectChild: (threadIdInput, reason) => {
|
|
3412
|
+
const threadId = threadIdInput.trim();
|
|
3413
|
+
const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
|
|
3414
|
+
if (!pending) return;
|
|
3415
|
+
pendingDirectChildAdmissions.delete(threadId);
|
|
3416
|
+
pending.reject(new Error(reason));
|
|
3417
|
+
},
|
|
3418
|
+
claimDirectChild: (threadIdInput) => {
|
|
3419
|
+
const threadId = threadIdInput.trim();
|
|
3420
|
+
if (!threadId) return () => void 0;
|
|
3421
|
+
if (directChildClaims.get(threadId)) return () => void 0;
|
|
3422
|
+
const claim = Symbol(threadId);
|
|
3423
|
+
directChildClaims.set(threadId, claim);
|
|
3424
|
+
const pending = pendingDirectChildAdmissions.get(threadId);
|
|
3425
|
+
pendingDirectChildAdmissions.delete(threadId);
|
|
3426
|
+
pending?.resolve(claim);
|
|
3427
|
+
let released = false;
|
|
3428
|
+
return () => {
|
|
3429
|
+
if (released) return;
|
|
3430
|
+
released = true;
|
|
3431
|
+
if (directChildClaims.get(threadId) !== claim) return;
|
|
3432
|
+
directChildClaims.delete(threadId);
|
|
3433
|
+
if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
function readCodexNativeChildThreadId(rawPayload) {
|
|
3439
|
+
if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
|
|
3440
|
+
return rawPayload.agent_id.trim() || void 0;
|
|
3441
|
+
}
|
|
3442
|
+
/** Selects the native hook events Codex should install for the current approval mode. */
|
|
3443
|
+
function resolveCodexNativeHookRelayEvents(params) {
|
|
3444
|
+
if (params.configuredEvents?.length) return params.configuredEvents;
|
|
3445
|
+
return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
|
|
3446
|
+
}
|
|
3447
|
+
/** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
|
|
3448
|
+
function resolveCodexNativeHookRelayTtlMs(params) {
|
|
3449
|
+
if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
|
|
3450
|
+
const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
|
|
3451
|
+
return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
|
|
3452
|
+
}
|
|
3453
|
+
/** Builds a stable relay id scoped to the agent and session identity. */
|
|
3454
|
+
function buildCodexNativeHookRelayId(params) {
|
|
3455
|
+
const hash = createHash("sha256");
|
|
3456
|
+
hash.update("openclaw:codex:native-hook-relay:v1");
|
|
3457
|
+
hash.update("\0");
|
|
3458
|
+
hash.update(params.agentId?.trim() || "");
|
|
3459
|
+
hash.update("\0");
|
|
3460
|
+
hash.update(params.sessionKey?.trim() || params.sessionId);
|
|
3461
|
+
return `codex-${hash.digest("hex").slice(0, 40)}`;
|
|
3462
|
+
}
|
|
3463
|
+
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
|
|
3464
|
+
pre_tool_use: "PreToolUse",
|
|
3465
|
+
post_tool_use: "PostToolUse",
|
|
3466
|
+
permission_request: "PermissionRequest",
|
|
3467
|
+
before_agent_finalize: "Stop"
|
|
3468
|
+
};
|
|
3469
|
+
const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
|
|
3470
|
+
pre_tool_use: "pre_tool_use",
|
|
3471
|
+
post_tool_use: "post_tool_use",
|
|
3472
|
+
permission_request: "permission_request",
|
|
3473
|
+
before_agent_finalize: "stop"
|
|
3474
|
+
};
|
|
3475
|
+
const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
|
|
3476
|
+
/** Builds the Codex config overlay that installs trusted command hooks for relay events. */
|
|
3477
|
+
function buildCodexNativeHookRelayConfig(params) {
|
|
3478
|
+
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
|
3479
|
+
const selectedEvents = new Set(events);
|
|
3480
|
+
const config = { "features.hooks": true };
|
|
3481
|
+
const hookState = {};
|
|
3482
|
+
for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
|
|
3483
|
+
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
|
3484
|
+
const selected = selectedEvents.has(event);
|
|
3485
|
+
const shouldRelay = params.relay.shouldRelayEvent(event);
|
|
3486
|
+
if (!selected || !shouldRelay) {
|
|
3487
|
+
if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
|
|
3488
|
+
if (params.clearOmittedEvents) for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = { enabled: false };
|
|
3489
|
+
continue;
|
|
3490
|
+
}
|
|
3491
|
+
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
3492
|
+
const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
|
|
3493
|
+
const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
|
|
3494
|
+
config[`hooks.${codexEvent}`] = [{
|
|
3495
|
+
...matcher ? { matcher } : {},
|
|
3496
|
+
hooks: [{
|
|
3497
|
+
type: "command",
|
|
3498
|
+
command,
|
|
3499
|
+
timeout,
|
|
3500
|
+
async: false,
|
|
3501
|
+
statusMessage: "OpenClaw native hook relay"
|
|
3502
|
+
}]
|
|
3503
|
+
}];
|
|
3504
|
+
const state = {
|
|
3505
|
+
enabled: true,
|
|
3506
|
+
trusted_hash: codexCommandHookTrustedHash({
|
|
3507
|
+
event,
|
|
3508
|
+
command,
|
|
3509
|
+
matcher,
|
|
3510
|
+
timeout,
|
|
3511
|
+
statusMessage: "OpenClaw native hook relay"
|
|
3512
|
+
})
|
|
3513
|
+
};
|
|
3514
|
+
for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
|
|
3515
|
+
}
|
|
3516
|
+
config["hooks.state"] = hookState;
|
|
3517
|
+
return config;
|
|
3518
|
+
}
|
|
3519
|
+
/** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
|
|
3520
|
+
function buildCodexNativeHookRelayDisabledConfig() {
|
|
3521
|
+
return {
|
|
3522
|
+
"features.hooks": false,
|
|
3523
|
+
"hooks.PreToolUse": [],
|
|
3524
|
+
"hooks.PostToolUse": [],
|
|
3525
|
+
"hooks.PermissionRequest": [],
|
|
3526
|
+
"hooks.Stop": []
|
|
3527
|
+
};
|
|
3528
|
+
}
|
|
3529
|
+
function normalizeHookTimeoutSec(value) {
|
|
3530
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
|
|
3531
|
+
}
|
|
3532
|
+
function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
|
|
3533
|
+
const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
|
|
3534
|
+
const parentMarginMs = Math.min(CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS, Math.max(CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS, Math.floor(parentTimeoutMs / 5)));
|
|
3535
|
+
return Math.max(1, parentTimeoutMs - parentMarginMs);
|
|
3536
|
+
}
|
|
3537
|
+
function buildCodexNativeToolMatcher(toolNames) {
|
|
3538
|
+
if (toolNames === void 0) return;
|
|
3539
|
+
if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
|
|
3540
|
+
const nativeNames = /* @__PURE__ */ new Set();
|
|
3541
|
+
let hasCustomToolName = false;
|
|
3542
|
+
for (const toolName of toolNames) {
|
|
3543
|
+
const canonicalToolName = toolName.trim();
|
|
3544
|
+
if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
|
|
3545
|
+
const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
|
|
3546
|
+
if (!nativeAliases) hasCustomToolName = true;
|
|
3547
|
+
for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
|
|
3548
|
+
}
|
|
3549
|
+
const sortedNames = Array.from(nativeNames).toSorted();
|
|
3550
|
+
if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
|
|
3551
|
+
return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
|
|
3552
|
+
}
|
|
3553
|
+
function codexCommandHookTrustedHash(params) {
|
|
3554
|
+
const identity = {
|
|
3555
|
+
event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
|
|
3556
|
+
...params.matcher ? { matcher: params.matcher } : {},
|
|
3557
|
+
hooks: [{
|
|
3558
|
+
async: false,
|
|
3559
|
+
command: params.command,
|
|
3560
|
+
statusMessage: params.statusMessage,
|
|
3561
|
+
timeout: params.timeout,
|
|
3562
|
+
type: "command"
|
|
3563
|
+
}]
|
|
3564
|
+
};
|
|
3565
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
|
|
3566
|
+
}
|
|
3567
|
+
function sortJsonValue(value) {
|
|
3568
|
+
if (!value || typeof value !== "object") return value;
|
|
3569
|
+
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
3570
|
+
const sorted = {};
|
|
3571
|
+
for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
|
|
3572
|
+
return sorted;
|
|
3573
|
+
}
|
|
3574
|
+
//#endregion
|
|
3575
|
+
//#region extensions/codex/src/app-server/profiler-flag.ts
|
|
3576
|
+
const PROFILER_FLAGS = ["profiler", "codex.profiler"];
|
|
3577
|
+
/** Checks the generic and Codex-specific profiler diagnostic flags. */
|
|
3578
|
+
function isCodexAppServerProfilerEnabled(config, env = process.env) {
|
|
3579
|
+
return PROFILER_FLAGS.some((flag) => isDiagnosticFlagEnabled(flag, config, env));
|
|
3580
|
+
}
|
|
3581
|
+
//#endregion
|
|
3582
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-timing.ts
|
|
3583
|
+
const CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS = 1e3;
|
|
3584
|
+
const CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS = 500;
|
|
3585
|
+
function shouldWarnCodexThreadLifecycleTimingSummary(summary, options = {}) {
|
|
3586
|
+
const totalThresholdMs = options.totalThresholdMs ?? CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS;
|
|
3587
|
+
const stageThresholdMs = options.stageThresholdMs ?? CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS;
|
|
3588
|
+
return summary.totalMs >= totalThresholdMs || summary.spans.some((span) => span.durationMs >= stageThresholdMs);
|
|
3589
|
+
}
|
|
3590
|
+
function formatCodexThreadLifecycleTimingSummary(params) {
|
|
3591
|
+
const spans = params.summary.spans.length > 0 ? params.summary.spans.map((span) => `${span.name}:${span.durationMs}ms@${span.elapsedMs}ms`).join(",") : "none";
|
|
3592
|
+
return `[trace:codex-app-server] thread lifecycle: runId=${params.runId} sessionId=${params.sessionId} sessionKey=${params.sessionKey ?? "unknown"} action=${params.action} totalMs=${params.summary.totalMs} stages=${spans}`;
|
|
3593
|
+
}
|
|
3594
|
+
function createCodexThreadLifecycleTimingTracker(options = {}) {
|
|
3595
|
+
const log = options.log ?? embeddedAgentLog;
|
|
3596
|
+
if (!options.enabled && log.isEnabled?.("trace") !== true) return {
|
|
3597
|
+
async measure(_name, run) {
|
|
3598
|
+
return await run();
|
|
3599
|
+
},
|
|
3600
|
+
measureSync(_name, run) {
|
|
3601
|
+
return run();
|
|
3602
|
+
},
|
|
3603
|
+
mark() {},
|
|
3604
|
+
logSummary() {}
|
|
3605
|
+
};
|
|
3606
|
+
const now = options.now ?? Date.now;
|
|
3607
|
+
const startedAt = now();
|
|
3608
|
+
let didLog = false;
|
|
3609
|
+
const spans = [];
|
|
3610
|
+
const toMs = (value) => Math.max(0, Math.round(value));
|
|
3611
|
+
const record = (name, spanStartedAt) => {
|
|
3612
|
+
const currentAt = now();
|
|
3613
|
+
spans.push({
|
|
3614
|
+
name,
|
|
3615
|
+
durationMs: toMs(currentAt - spanStartedAt),
|
|
3616
|
+
elapsedMs: toMs(currentAt - startedAt)
|
|
3617
|
+
});
|
|
3618
|
+
};
|
|
3619
|
+
const snapshot = () => ({
|
|
3620
|
+
totalMs: toMs(now() - startedAt),
|
|
3621
|
+
spans: spans.slice()
|
|
3622
|
+
});
|
|
3623
|
+
return {
|
|
3624
|
+
async measure(name, run) {
|
|
3625
|
+
const spanStartedAt = now();
|
|
3626
|
+
try {
|
|
3627
|
+
return await run();
|
|
3628
|
+
} finally {
|
|
3629
|
+
record(name, spanStartedAt);
|
|
3630
|
+
}
|
|
3631
|
+
},
|
|
3632
|
+
measureSync(name, run) {
|
|
3633
|
+
const spanStartedAt = now();
|
|
3634
|
+
try {
|
|
3635
|
+
return run();
|
|
3636
|
+
} finally {
|
|
3637
|
+
record(name, spanStartedAt);
|
|
3638
|
+
}
|
|
3639
|
+
},
|
|
3640
|
+
mark(name) {
|
|
3641
|
+
record(name, now());
|
|
3642
|
+
},
|
|
3643
|
+
logSummary(params) {
|
|
3644
|
+
if (didLog) return;
|
|
3645
|
+
const summary = snapshot();
|
|
3646
|
+
const shouldWarn = shouldWarnCodexThreadLifecycleTimingSummary(summary, options);
|
|
3647
|
+
if (!shouldWarn && !log.isEnabled?.("trace")) return;
|
|
3648
|
+
didLog = true;
|
|
3649
|
+
const message = formatCodexThreadLifecycleTimingSummary({
|
|
3650
|
+
runId: params.runId,
|
|
3651
|
+
sessionId: params.sessionId,
|
|
3652
|
+
sessionKey: params.sessionKey,
|
|
3653
|
+
action: params.action,
|
|
3654
|
+
summary
|
|
3655
|
+
});
|
|
3656
|
+
const meta = {
|
|
3657
|
+
runId: params.runId,
|
|
3658
|
+
sessionId: params.sessionId,
|
|
3659
|
+
sessionKey: params.sessionKey,
|
|
3660
|
+
action: params.action,
|
|
3661
|
+
threadId: params.threadId,
|
|
3662
|
+
totalMs: summary.totalMs,
|
|
3663
|
+
spans: summary.spans
|
|
3664
|
+
};
|
|
3665
|
+
if (shouldWarn) log.warn(message, meta);
|
|
3666
|
+
else log.trace(message, meta);
|
|
3667
|
+
}
|
|
3668
|
+
};
|
|
3669
|
+
}
|
|
3670
|
+
//#endregion
|
|
3671
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
|
|
3672
|
+
async function prepareCodexThreadLifecyclePreflight(params) {
|
|
3673
|
+
await assertCodexModelBackedReviewerEffectiveConfig({
|
|
3674
|
+
client: params.client,
|
|
3675
|
+
approvalsReviewer: params.appServer.approvalsReviewer,
|
|
3676
|
+
cwd: params.cwd,
|
|
3677
|
+
signal: params.signal
|
|
3678
|
+
});
|
|
3679
|
+
if (params.nativeHookRelayRequired) await assertCodexNativeHookRelayAllowed(params.client, params.signal);
|
|
3680
|
+
const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
|
|
3681
|
+
...params.timing,
|
|
3682
|
+
enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
|
|
3683
|
+
});
|
|
3684
|
+
const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync("legacy-dynamic-tools-fingerprint", () => codexLegacyDynamicToolsFingerprint(params.dynamicTools));
|
|
3685
|
+
const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => hashCodexAppServerBindingFingerprint(legacyDynamicToolsFingerprint));
|
|
3686
|
+
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
3687
|
+
const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
3688
|
+
config: params.params.config,
|
|
3689
|
+
disableTools: params.params.disableTools,
|
|
3690
|
+
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
3691
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
3692
|
+
webSearchAllowed: params.webSearchAllowed
|
|
3693
|
+
})).threadConfig);
|
|
3694
|
+
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
3695
|
+
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
3696
|
+
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRun({
|
|
3697
|
+
run: params.params,
|
|
3698
|
+
cwd: params.cwd,
|
|
3699
|
+
agentId: params.agentId ?? params.params.agentId,
|
|
3700
|
+
allowLiteralOAuthProjection: params.appServer.connectionClass !== "remote",
|
|
3701
|
+
warn: (message) => embeddedAgentLog.warn(message),
|
|
3702
|
+
onServerUnavailable: (serverName, error) => embeddedAgentLog.warn("skipping unavailable MCP OAuth server", {
|
|
3703
|
+
serverName,
|
|
3704
|
+
error: formatErrorMessage(error)
|
|
3705
|
+
})
|
|
3706
|
+
});
|
|
3707
|
+
const nativeSkillIsolation = await lifecycleTiming.measure("native-skill-isolation", () => resolveCodexNativeSkillIsolation({
|
|
3708
|
+
client: params.client,
|
|
3709
|
+
codexHome: params.appServer.start.env?.CODEX_HOME,
|
|
3710
|
+
cwd: params.cwd,
|
|
3711
|
+
home: params.appServer.start.env?.HOME,
|
|
3712
|
+
signal: params.signal,
|
|
3713
|
+
userProfile: params.appServer.start.env?.USERPROFILE
|
|
3714
|
+
}));
|
|
3715
|
+
const nativeSkillIsolationFingerprint = nativeSkillIsolation ? fingerprintJsonObject({
|
|
3716
|
+
version: 1,
|
|
3717
|
+
disabledUserSkillPaths: nativeSkillIsolation.disabledUserSkillPaths
|
|
3718
|
+
}) : void 0;
|
|
3719
|
+
const legacyUserMcpServersFingerprint = legacyFingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
3720
|
+
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
3721
|
+
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
3722
|
+
const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
|
|
3723
|
+
const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
|
|
3724
|
+
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
|
|
3725
|
+
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
|
|
3726
|
+
const imageGenerationDenied = params.params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") === true;
|
|
3727
|
+
if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
|
|
3728
|
+
const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
|
|
3729
|
+
if (restrictedToolSurface || imageGenerationDenied) await lifecycleTiming.measure("tool-policy-config-requirements-read", () => assertCodexManagedRequirementsDoNotOverrideToolPolicy(params.client, {
|
|
3730
|
+
restrictedToolSurface,
|
|
3731
|
+
additionalDeniedFeatures: imageGenerationDenied ? ["image_generation"] : void 0
|
|
3732
|
+
}, params.signal));
|
|
3733
|
+
const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
|
|
3734
|
+
version: 1,
|
|
3735
|
+
baseInstructions: "",
|
|
3736
|
+
config: buildCodexRingZeroThreadConfigPatch(params.params, true, restrictedToolSurfaceInheritedMcpServerNames)
|
|
3737
|
+
}) : void 0;
|
|
3738
|
+
return {
|
|
3739
|
+
contextEngineBinding,
|
|
3740
|
+
dynamicToolsContainDeferred,
|
|
3741
|
+
dynamicToolsFingerprint,
|
|
3742
|
+
environmentSelectionFingerprint,
|
|
3743
|
+
hostSystemAgentActive,
|
|
3744
|
+
legacyDynamicToolsFingerprint,
|
|
3745
|
+
legacyUserMcpServersFingerprint,
|
|
3746
|
+
lifecycleTiming,
|
|
3747
|
+
nativeSkillIsolation,
|
|
3748
|
+
nativeSkillIsolationFingerprint,
|
|
3749
|
+
networkProxyConfigFingerprint,
|
|
3750
|
+
ringZeroActive,
|
|
3751
|
+
ringZeroClientInstanceId: ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0,
|
|
3752
|
+
ringZeroConfigFingerprint,
|
|
3753
|
+
restrictedToolSurface,
|
|
3754
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3755
|
+
userMcpServersConfigPatch,
|
|
3756
|
+
userMcpServersFingerprint,
|
|
3757
|
+
webSearchThreadConfigFingerprint
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
//#endregion
|
|
3041
3761
|
//#region extensions/codex/src/app-server/thread-supervision.ts
|
|
3042
3762
|
async function materializePendingSupervisionBranch(params) {
|
|
3043
3763
|
let pending = params.binding.pendingSupervisionBranch;
|
|
@@ -3061,6 +3781,32 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3061
3781
|
});
|
|
3062
3782
|
let bindingCommitted = false;
|
|
3063
3783
|
let provisionalCleanupSafe = true;
|
|
3784
|
+
let cleanupExpected = pending;
|
|
3785
|
+
const trackPendingSupervisionArtifacts = async (cleanupThreadIds) => {
|
|
3786
|
+
const expected = pending;
|
|
3787
|
+
pending = withPendingSupervisionCleanup(pending, cleanupThreadIds);
|
|
3788
|
+
let updated;
|
|
3789
|
+
try {
|
|
3790
|
+
updated = await params.bindingStore.mutate(params.bindingIdentity, {
|
|
3791
|
+
kind: "patch-pending-supervision-branch",
|
|
3792
|
+
expected,
|
|
3793
|
+
pending
|
|
3794
|
+
});
|
|
3795
|
+
} catch (error) {
|
|
3796
|
+
try {
|
|
3797
|
+
const current = await params.bindingStore.read(params.bindingIdentity);
|
|
3798
|
+
if (matchesPendingSupervisionState(current, pending)) cleanupExpected = pending;
|
|
3799
|
+
else if (matchesPendingSupervisionState(current, expected)) cleanupExpected = expected;
|
|
3800
|
+
else throw new CodexThreadBindingConflictError(pending.sourceThreadId, "verifying supervised Codex cleanup tracking");
|
|
3801
|
+
} catch (verificationError) {
|
|
3802
|
+
provisionalCleanupSafe = false;
|
|
3803
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup tracking could not be verified: ${cleanupThreadIds.join(", ")}`, { cause: new AggregateError([error, verificationError], void 0, { cause: error }) });
|
|
3804
|
+
}
|
|
3805
|
+
throw error;
|
|
3806
|
+
}
|
|
3807
|
+
cleanupExpected = updated ? pending : void 0;
|
|
3808
|
+
if (!updated) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
|
|
3809
|
+
};
|
|
3064
3810
|
try {
|
|
3065
3811
|
const probeParams = buildPendingSupervisionProbeForkParams(params, pending);
|
|
3066
3812
|
const rawProbeResponse = await params.lifecycleTiming.measure("supervision-model-probe-fork", async () => {
|
|
@@ -3076,10 +3822,17 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3076
3822
|
sourceThreadId: pending.sourceThreadId,
|
|
3077
3823
|
role: "model probe"
|
|
3078
3824
|
});
|
|
3079
|
-
|
|
3825
|
+
let probeResponse;
|
|
3826
|
+
try {
|
|
3827
|
+
params.throwIfAborted();
|
|
3828
|
+
probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
|
|
3829
|
+
if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
|
|
3830
|
+
} finally {
|
|
3831
|
+
await unsubscribeCodexAppServerLiveThread(params.client, probeThreadId, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS).catch((cause) => {
|
|
3832
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Codex model probe subscription could not be released: ${probeThreadId}`, { cause });
|
|
3833
|
+
});
|
|
3834
|
+
}
|
|
3080
3835
|
params.throwIfAborted();
|
|
3081
|
-
const probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
|
|
3082
|
-
if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
|
|
3083
3836
|
const nativeModel = requireNonBlankSupervisionValue(probeResponse.model, "native model");
|
|
3084
3837
|
const nativeModelProvider = requireNativeSupervisionModelProvider({
|
|
3085
3838
|
responseModelProvider: probeResponse.modelProvider,
|
|
@@ -3102,7 +3855,9 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3102
3855
|
model: nativeModel,
|
|
3103
3856
|
modelProvider: nativeModelProvider,
|
|
3104
3857
|
hostSystemAgentActive: params.hostSystemAgentActive,
|
|
3105
|
-
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
|
|
3858
|
+
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
|
|
3859
|
+
shellEnvironment: params.shellEnvironment,
|
|
3860
|
+
disableLoginShell: params.disableLoginShell
|
|
3106
3861
|
});
|
|
3107
3862
|
assertExactSupervisionModelSelection(startParams, {
|
|
3108
3863
|
model: nativeModel,
|
|
@@ -3123,7 +3878,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3123
3878
|
otherThreadId: probeThreadId,
|
|
3124
3879
|
role: "canonical branch"
|
|
3125
3880
|
});
|
|
3126
|
-
|
|
3881
|
+
await trackPendingSupervisionArtifacts([finalThreadId]);
|
|
3127
3882
|
params.throwIfAborted();
|
|
3128
3883
|
assertExactSupervisionModelSelection(assertCodexThreadStartResponse(rawStartResponse), {
|
|
3129
3884
|
model: nativeModel,
|
|
@@ -3143,11 +3898,11 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3143
3898
|
client: params.client,
|
|
3144
3899
|
threadId: finalThreadId,
|
|
3145
3900
|
ephemeral: startParams.ephemeral === true
|
|
3146
|
-
})
|
|
3901
|
+
})) {
|
|
3147
3902
|
provisionalCleanupSafe = false;
|
|
3148
3903
|
throw new CodexAppServerUnsafeSubscriptionError("Codex supervised plugin app attestation cleanup failed", { cause: error });
|
|
3149
3904
|
}
|
|
3150
|
-
|
|
3905
|
+
await trackPendingSupervisionArtifacts([]);
|
|
3151
3906
|
throw error;
|
|
3152
3907
|
}
|
|
3153
3908
|
if (history.responseItems.length > 0) {
|
|
@@ -3157,8 +3912,6 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3157
3912
|
}, { signal: params.signal }));
|
|
3158
3913
|
params.throwIfAborted();
|
|
3159
3914
|
}
|
|
3160
|
-
if (!await archiveSupervisionArtifact(params.client, probeThreadId)) throw new Error(`Failed to archive temporary Codex model probe: ${probeThreadId}`);
|
|
3161
|
-
pending = await trackPendingSupervisionArtifacts(params, pending, [finalThreadId]);
|
|
3162
3915
|
const historyCoveredThrough = (/* @__PURE__ */ new Date()).toISOString();
|
|
3163
3916
|
const bindingModelProvider = params.normalizeBindingModelProvider(params.attempt.authProfileId, nativeModelProvider);
|
|
3164
3917
|
let committed = false;
|
|
@@ -3220,31 +3973,28 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3220
3973
|
};
|
|
3221
3974
|
} catch (error) {
|
|
3222
3975
|
if (bindingCommitted) throw error;
|
|
3223
|
-
if (error instanceof CodexThreadBindingConflictAfterCleanupError) throw error;
|
|
3224
3976
|
if (!provisionalCleanupSafe) {
|
|
3225
3977
|
await params.abandonClient();
|
|
3226
3978
|
throw error;
|
|
3227
3979
|
}
|
|
3228
3980
|
const cleanup = await cleanPendingSupervisionArtifacts(params.client, pending);
|
|
3981
|
+
const nextPending = withPendingSupervisionCleanup(pending, cleanup.remaining);
|
|
3229
3982
|
let cleanupStateError;
|
|
3230
|
-
if (
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
} catch (stateError) {
|
|
3239
|
-
cleanupStateError = stateError;
|
|
3240
|
-
}
|
|
3983
|
+
if (cleanupExpected && !isDeepStrictEqual(cleanupExpected, nextPending)) try {
|
|
3984
|
+
await params.bindingStore.mutate(params.bindingIdentity, {
|
|
3985
|
+
kind: "patch-pending-supervision-branch",
|
|
3986
|
+
expected: cleanupExpected,
|
|
3987
|
+
pending: nextPending
|
|
3988
|
+
});
|
|
3989
|
+
} catch (stateError) {
|
|
3990
|
+
cleanupStateError = stateError;
|
|
3241
3991
|
}
|
|
3242
3992
|
const unsafeCleanup = cleanup.remaining.length > 0 || isCodexAppServerUnsafeSubscriptionError(error);
|
|
3243
3993
|
if (unsafeCleanup) await params.abandonClient();
|
|
3244
3994
|
if (cleanupStateError) {
|
|
3245
|
-
const cause = new AggregateError([error, cleanupStateError]);
|
|
3995
|
+
const cause = new AggregateError([error, cleanupStateError], "Codex supervised branch cleanup state could not be recorded", { cause: error });
|
|
3246
3996
|
if (unsafeCleanup) throw new CodexAppServerUnsafeSubscriptionError("Codex supervised branch cleanup state could not be recorded", { cause });
|
|
3247
|
-
throw
|
|
3997
|
+
throw cause;
|
|
3248
3998
|
}
|
|
3249
3999
|
if (cleanup.remaining.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup remains pending: ${cleanup.remaining.join(", ")}`, { cause: error });
|
|
3250
4000
|
throw error;
|
|
@@ -3258,7 +4008,9 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
|
|
|
3258
4008
|
webSearchAllowed: params.webSearchAllowed,
|
|
3259
4009
|
appServer: params.appServer,
|
|
3260
4010
|
hostSystemAgentActive: params.hostSystemAgentActive,
|
|
3261
|
-
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
|
|
4011
|
+
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
|
|
4012
|
+
shellEnvironment: params.shellEnvironment,
|
|
4013
|
+
disableLoginShell: params.disableLoginShell
|
|
3262
4014
|
});
|
|
3263
4015
|
return {
|
|
3264
4016
|
threadId: pending.sourceThreadId,
|
|
@@ -3270,7 +4022,7 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
|
|
|
3270
4022
|
...params.appServer.serviceTier !== void 0 ? { serviceTier: params.appServer.serviceTier } : {},
|
|
3271
4023
|
config: runtimeConfig,
|
|
3272
4024
|
developerInstructions: params.developerInstructions ?? buildDeveloperInstructions(params.attempt, { dynamicTools: params.dynamicTools }),
|
|
3273
|
-
ephemeral:
|
|
4025
|
+
ephemeral: true,
|
|
3274
4026
|
threadSource: "appServer",
|
|
3275
4027
|
excludeTurns: true
|
|
3276
4028
|
};
|
|
@@ -3312,10 +4064,8 @@ function requireDistinctSupervisionThreadId(params) {
|
|
|
3312
4064
|
return threadId;
|
|
3313
4065
|
}
|
|
3314
4066
|
function readSupervisionResponseThreadId(value) {
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
if (!thread || typeof thread !== "object" || Array.isArray(thread)) return;
|
|
3318
|
-
return thread.id;
|
|
4067
|
+
const thread = isRecord(value) ? value.thread : void 0;
|
|
4068
|
+
return isRecord(thread) ? thread.id : void 0;
|
|
3319
4069
|
}
|
|
3320
4070
|
async function recoverPendingSupervisionArtifacts(params, pending) {
|
|
3321
4071
|
if (!pending.cleanupThreadIds?.length) return pending;
|
|
@@ -3338,20 +4088,6 @@ async function recoverPendingSupervisionArtifacts(params, pending) {
|
|
|
3338
4088
|
})) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "recovering a supervised Codex branch");
|
|
3339
4089
|
return next;
|
|
3340
4090
|
}
|
|
3341
|
-
async function trackPendingSupervisionArtifacts(params, pending, cleanupThreadIds) {
|
|
3342
|
-
const next = withPendingSupervisionCleanup(pending, cleanupThreadIds);
|
|
3343
|
-
if (!await params.bindingStore.mutate(params.bindingIdentity, {
|
|
3344
|
-
kind: "patch-pending-supervision-branch",
|
|
3345
|
-
expected: pending,
|
|
3346
|
-
pending: next
|
|
3347
|
-
})) {
|
|
3348
|
-
const cleanupFailed = [];
|
|
3349
|
-
for (const threadId of cleanupThreadIds) if (!await archiveSupervisionArtifact(params.client, threadId)) cleanupFailed.push(threadId);
|
|
3350
|
-
if (cleanupFailed.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch CAS cleanup failed: ${cleanupFailed.join(", ")}`);
|
|
3351
|
-
throw new CodexThreadBindingConflictAfterCleanupError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
|
|
3352
|
-
}
|
|
3353
|
-
return next;
|
|
3354
|
-
}
|
|
3355
4091
|
function withPendingSupervisionCleanup(pending, cleanupThreadIds) {
|
|
3356
4092
|
return {
|
|
3357
4093
|
sourceThreadId: pending.sourceThreadId,
|
|
@@ -3388,15 +4124,10 @@ async function archiveSupervisionArtifact(client, threadId) {
|
|
|
3388
4124
|
async function startOrResumeThread(params) {
|
|
3389
4125
|
const incognito = isIncognitoSessionKey(params.params.sessionKey);
|
|
3390
4126
|
const clientId = resolveCodexAppServerClientInstanceId(params.client);
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
config: params.params.config
|
|
3396
|
-
});
|
|
3397
|
-
return await params.bindingStore.withLease(bindingIdentity, async () => {
|
|
3398
|
-
const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = await prepareCodexThreadLifecyclePreflight(params);
|
|
3399
|
-
let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
|
|
4127
|
+
return await withCodexThreadLifecycleBinding(params, async (bindingIdentity, currentBinding) => {
|
|
4128
|
+
let binding = currentBinding;
|
|
4129
|
+
const preflight = await prepareCodexThreadLifecyclePreflight(params);
|
|
4130
|
+
const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = preflight;
|
|
3400
4131
|
let replacementPredecessor;
|
|
3401
4132
|
const initialBoundThreadId = binding?.threadId;
|
|
3402
4133
|
const initialBoundClientId = binding?.clientId;
|
|
@@ -3408,26 +4139,33 @@ async function startOrResumeThread(params) {
|
|
|
3408
4139
|
config: params.params.config
|
|
3409
4140
|
});
|
|
3410
4141
|
const throwIfAborted = () => throwIfCodexThreadLifecycleAborted(params.signal);
|
|
3411
|
-
const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId) => {
|
|
4142
|
+
const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId, assertCurrent) => {
|
|
3412
4143
|
if (ownerClientId && ownerClientId !== clientId) {
|
|
3413
4144
|
const previousClient = retainSharedCodexAppServerClientByInstanceId(ownerClientId);
|
|
3414
|
-
if (!previousClient) return;
|
|
4145
|
+
if (!previousClient) return false;
|
|
3415
4146
|
try {
|
|
3416
|
-
|
|
4147
|
+
const assertPrevious = assertCurrent ? captureExclusiveSharedCodexAppServerClient(previousClient.client) : void 0;
|
|
4148
|
+
if (isCodexAppServerLiveThreadClaimed(previousClient.client, threadId)) throw new Error(`Codex thread ${threadId} is claimed by active work; stop it first.`);
|
|
4149
|
+
return await releaseCodexRetainedLiveThread({
|
|
3417
4150
|
client: previousClient.client,
|
|
3418
4151
|
lifecycleTiming,
|
|
3419
|
-
threadId
|
|
4152
|
+
threadId,
|
|
4153
|
+
assertCurrent: assertCurrent ? () => {
|
|
4154
|
+
assertCurrent();
|
|
4155
|
+
assertPrevious?.();
|
|
4156
|
+
} : void 0
|
|
3420
4157
|
});
|
|
3421
4158
|
} finally {
|
|
3422
4159
|
previousClient.release();
|
|
3423
4160
|
}
|
|
3424
|
-
return;
|
|
3425
4161
|
}
|
|
3426
|
-
|
|
4162
|
+
if (isCodexAppServerLiveThreadClaimed(params.client, threadId)) throw new Error(`Codex thread ${threadId} is claimed by active work; stop it first.`);
|
|
4163
|
+
return await releaseCodexRetainedLiveThread({
|
|
3427
4164
|
client: params.client,
|
|
3428
4165
|
abandonClient: params.abandonClient,
|
|
3429
4166
|
lifecycleTiming,
|
|
3430
|
-
threadId
|
|
4167
|
+
threadId,
|
|
4168
|
+
assertCurrent
|
|
3431
4169
|
});
|
|
3432
4170
|
};
|
|
3433
4171
|
if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
|
|
@@ -3465,6 +4203,8 @@ async function startOrResumeThread(params) {
|
|
|
3465
4203
|
hostSystemAgentActive,
|
|
3466
4204
|
restrictedToolSurface,
|
|
3467
4205
|
restrictedToolSurfaceInheritedMcpServerNames,
|
|
4206
|
+
shellEnvironment: params.shellEnvironment,
|
|
4207
|
+
disableLoginShell: params.disableLoginShell,
|
|
3468
4208
|
environmentSelection: params.environmentSelection,
|
|
3469
4209
|
provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
|
|
3470
4210
|
signal: params.signal,
|
|
@@ -3475,6 +4215,7 @@ async function startOrResumeThread(params) {
|
|
|
3475
4215
|
cwd: params.cwd,
|
|
3476
4216
|
...clientId ? { clientId } : {},
|
|
3477
4217
|
authProfileId: void 0,
|
|
4218
|
+
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
3478
4219
|
preserveNativeModel: true,
|
|
3479
4220
|
dynamicToolsFingerprint,
|
|
3480
4221
|
dynamicToolsContainDeferred,
|
|
@@ -3506,6 +4247,36 @@ async function startOrResumeThread(params) {
|
|
|
3506
4247
|
})) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
3507
4248
|
binding = void 0;
|
|
3508
4249
|
};
|
|
4250
|
+
const resolveRequestContext = () => {
|
|
4251
|
+
const startModelSelection = resolveCodexAppServerThreadModelSelection({
|
|
4252
|
+
provider: params.params.provider,
|
|
4253
|
+
model: params.runtimeModelId ?? params.params.modelId,
|
|
4254
|
+
binding,
|
|
4255
|
+
authProfileId: params.params.authProfileId,
|
|
4256
|
+
authProfileStore: params.params.authProfileStore,
|
|
4257
|
+
agentDir: params.params.agentDir,
|
|
4258
|
+
config: params.params.config
|
|
4259
|
+
});
|
|
4260
|
+
return {
|
|
4261
|
+
...preflight,
|
|
4262
|
+
bindingIdentity,
|
|
4263
|
+
startModelSelection,
|
|
4264
|
+
startModelProvider: startModelSelection.modelProvider,
|
|
4265
|
+
normalizeBindingModelProvider,
|
|
4266
|
+
throwIfAborted
|
|
4267
|
+
};
|
|
4268
|
+
};
|
|
4269
|
+
const transientDelegationRestriction = params.params.delegationCapability === "report_only";
|
|
4270
|
+
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
4271
|
+
const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
|
|
4272
|
+
const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
|
|
4273
|
+
if (binding?.pendingResumeConfiguration) return await resumePendingCodexThread(params, {
|
|
4274
|
+
...resolveRequestContext(),
|
|
4275
|
+
binding,
|
|
4276
|
+
clearCurrentBinding,
|
|
4277
|
+
releaseRetainedThread: (threadId, assertCurrent) => releaseRetainedThread(threadId, initialBoundClientId, assertCurrent),
|
|
4278
|
+
transientRestriction: transientDelegationRestriction || transientNativeToolRestriction || transientWebSearchRestriction
|
|
4279
|
+
});
|
|
3509
4280
|
if (binding?.threadId && !restrictedToolSurface && binding.nativeToolPolicyRestricted === true) await clearCurrentBinding("rotating a host-policy-restricted thread binding");
|
|
3510
4281
|
if (binding?.threadId && binding.nativeSkillIsolationFingerprint !== nativeSkillIsolationFingerprint) {
|
|
3511
4282
|
embeddedAgentLog.debug("codex app-server native skill isolation changed; starting a new thread", { threadId: binding.threadId });
|
|
@@ -3539,24 +4310,12 @@ async function startOrResumeThread(params) {
|
|
|
3539
4310
|
await clearCurrentBinding("rotating a GPT-5.6 multi-agent thread binding");
|
|
3540
4311
|
binding = void 0;
|
|
3541
4312
|
}
|
|
3542
|
-
const
|
|
3543
|
-
|
|
3544
|
-
model: params.params.modelId,
|
|
3545
|
-
binding,
|
|
3546
|
-
authProfileId: params.params.authProfileId,
|
|
3547
|
-
authProfileStore: params.params.authProfileStore,
|
|
3548
|
-
agentDir: params.params.agentDir,
|
|
3549
|
-
config: params.params.config
|
|
3550
|
-
});
|
|
3551
|
-
const startModelProvider = startModelSelection.modelProvider;
|
|
3552
|
-
const transientDelegationRestriction = params.params.delegationCapability === "report_only";
|
|
4313
|
+
const requestContext = resolveRequestContext();
|
|
4314
|
+
const { startModelSelection, startModelProvider } = requestContext;
|
|
3553
4315
|
let preserveExistingBinding = transientDelegationRestriction || !ringZeroActive && params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
|
|
3554
4316
|
let rotatedContextEngineBinding = false;
|
|
3555
4317
|
let prebuiltPluginThreadConfig;
|
|
3556
4318
|
const webSearchBindingChanged = binding?.threadId && binding.webSearchThreadConfigFingerprint !== webSearchThreadConfigFingerprint;
|
|
3557
|
-
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
3558
|
-
const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
|
|
3559
|
-
const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
|
|
3560
4319
|
const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
|
|
3561
4320
|
const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
|
|
3562
4321
|
if (binding?.threadId && (params.configuredMcpOwnershipVersion === 1 && (binding.configuredMcpOwnershipVersion !== 1 || binding.dynamicToolsFingerprint === void 0 || binding.mcpServersFingerprint !== void 0 || binding.userMcpServersFingerprint !== void 0) || params.configuredMcpOwnershipVersion !== 1 && binding.configuredMcpOwnershipVersion === 1) && binding?.threadId) {
|
|
@@ -3727,31 +4486,11 @@ async function startOrResumeThread(params) {
|
|
|
3727
4486
|
userMcpServersConfigPatch
|
|
3728
4487
|
});
|
|
3729
4488
|
if (warmReuse.binding) return warmReuse.binding;
|
|
4489
|
+
if (binding.preserveNativeModel === true) await assertAdoptedCodexThreadResumeAllowed(params, binding.threadId, requestContext);
|
|
3730
4490
|
await releaseRetainedThread(binding.threadId);
|
|
3731
4491
|
const resumed = await resumeExistingCodexThread(params, {
|
|
4492
|
+
...requestContext,
|
|
3732
4493
|
binding,
|
|
3733
|
-
bindingIdentity,
|
|
3734
|
-
startModelSelection,
|
|
3735
|
-
startModelProvider,
|
|
3736
|
-
userMcpServersConfigPatch,
|
|
3737
|
-
dynamicToolsFingerprint,
|
|
3738
|
-
dynamicToolsContainDeferred,
|
|
3739
|
-
webSearchThreadConfigFingerprint,
|
|
3740
|
-
nativeSkillIsolationFingerprint,
|
|
3741
|
-
userMcpServersFingerprint,
|
|
3742
|
-
ringZeroConfigFingerprint,
|
|
3743
|
-
ringZeroClientInstanceId,
|
|
3744
|
-
networkProxyConfigFingerprint,
|
|
3745
|
-
contextEngineBinding,
|
|
3746
|
-
environmentSelectionFingerprint,
|
|
3747
|
-
hostSystemAgentActive,
|
|
3748
|
-
ringZeroActive,
|
|
3749
|
-
restrictedToolSurface,
|
|
3750
|
-
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3751
|
-
nativeSkillIsolation,
|
|
3752
|
-
lifecycleTiming,
|
|
3753
|
-
normalizeBindingModelProvider,
|
|
3754
|
-
throwIfAborted,
|
|
3755
4494
|
clearCurrentBinding,
|
|
3756
4495
|
prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
|
|
3757
4496
|
prebuiltPluginThreadConfig
|
|
@@ -3760,28 +4499,7 @@ async function startOrResumeThread(params) {
|
|
|
3760
4499
|
}
|
|
3761
4500
|
if (initialBoundThreadId && !preserveExistingBinding && !replacementPredecessor) await releaseRetainedThread(initialBoundThreadId);
|
|
3762
4501
|
const started = await startFreshCodexThread(params, {
|
|
3763
|
-
|
|
3764
|
-
startModelSelection,
|
|
3765
|
-
startModelProvider,
|
|
3766
|
-
userMcpServersConfigPatch,
|
|
3767
|
-
dynamicToolsFingerprint,
|
|
3768
|
-
dynamicToolsContainDeferred,
|
|
3769
|
-
webSearchThreadConfigFingerprint,
|
|
3770
|
-
nativeSkillIsolationFingerprint,
|
|
3771
|
-
userMcpServersFingerprint,
|
|
3772
|
-
ringZeroConfigFingerprint,
|
|
3773
|
-
ringZeroClientInstanceId,
|
|
3774
|
-
networkProxyConfigFingerprint,
|
|
3775
|
-
contextEngineBinding,
|
|
3776
|
-
environmentSelectionFingerprint,
|
|
3777
|
-
hostSystemAgentActive,
|
|
3778
|
-
ringZeroActive,
|
|
3779
|
-
restrictedToolSurface,
|
|
3780
|
-
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3781
|
-
nativeSkillIsolation,
|
|
3782
|
-
lifecycleTiming,
|
|
3783
|
-
normalizeBindingModelProvider,
|
|
3784
|
-
throwIfAborted,
|
|
4502
|
+
...requestContext,
|
|
3785
4503
|
prebuiltPluginThreadConfig,
|
|
3786
4504
|
preserveExistingBinding,
|
|
3787
4505
|
rotatedContextEngineBinding,
|
|
@@ -3855,9 +4573,10 @@ function buildTurnStartParams(params, options) {
|
|
|
3855
4573
|
input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
|
|
3856
4574
|
...additionalContext ? { additionalContext } : {},
|
|
3857
4575
|
cwd: options.cwd,
|
|
4576
|
+
...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
|
|
3858
4577
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
3859
4578
|
approvalsReviewer: options.appServer.approvalsReviewer,
|
|
3860
|
-
...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd, options.appServer.start?.args) },
|
|
4579
|
+
...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.appServer.sessionRoot ?? options.cwd, options.appServer.start?.args) },
|
|
3861
4580
|
...modelSelection ? {
|
|
3862
4581
|
model: modelSelection.model,
|
|
3863
4582
|
personality: CODEX_NATIVE_PERSONALITY_NONE
|
|
@@ -3887,7 +4606,6 @@ function buildTurnCollaborationMode(params, options = {}) {
|
|
|
3887
4606
|
function buildTurnScopedCollaborationInstructions(params, options = {}) {
|
|
3888
4607
|
const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
|
|
3889
4608
|
if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
|
|
3890
|
-
if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
|
|
3891
4609
|
if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
|
|
3892
4610
|
return null;
|
|
3893
4611
|
}
|
|
@@ -3914,15 +4632,8 @@ function buildCronCollaborationInstructions() {
|
|
|
3914
4632
|
"Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration."
|
|
3915
4633
|
].join("\n\n");
|
|
3916
4634
|
}
|
|
3917
|
-
function buildHeartbeatCollaborationInstructions() {
|
|
3918
|
-
return [
|
|
3919
|
-
"This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.",
|
|
3920
|
-
"When you are ready to end the heartbeat, prefer the structured `heartbeat_respond` tool so OpenClaw 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.",
|
|
3921
|
-
GPT5_HEARTBEAT_PROMPT_OVERLAY
|
|
3922
|
-
].join("\n\n");
|
|
3923
|
-
}
|
|
3924
4635
|
function joinPresentSections(...sections) {
|
|
3925
4636
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
3926
4637
|
}
|
|
3927
4638
|
//#endregion
|
|
3928
|
-
export {
|
|
4639
|
+
export { resolveCodexContinuityProjectionMaxChars as $, readCodexSupportedReasoningEfforts as A, isMessageOnlyCodexSourceReply as B, buildCodexProjectDocThreadConfig as C, resolveCodexAppServerThreadModelSelection as D, resolveCodexAppServerRequestModelSelection as E, codexDynamicToolsFingerprint as F, buildContextEngineBinding as G, normalizeCodexDynamicToolName as H, codexLegacyDynamicToolsFingerprint as I, fitCodexProjectedContextForTurnStart as J, isContextEngineBindingCompatible as K, filterCodexDynamicTools as L, CODEX_MANAGED_THREAD_NAMESPACE as M, createCodexManagedThreadStore as N, resolveCodexBindingModelProviderFallback as O, areCodexDynamicToolFingerprintsCompatible as P, resolveCodexContextEngineProjectionReserveTokens as Q, filterCodexDynamicToolsForDisabledNativeSurface as R, buildDeveloperInstructions as S, resolveCodexAppServerModelProvider as T, resolveCodexDynamicToolsLoading as U, isSystemAgentOnlyCodexDynamicToolAllowlist as V, resolveCodexDynamicToolsLoadingForRuntime as W, projectContextEngineAssemblyForCodex as X, neutralizeCodexExplicitMentionSigils as Y, resolveCodexContextEngineProjectionMaxChars as Z, buildCodexRingZeroThreadConfigPatch as _, isCodexAppServerProfilerEnabled as a, buildPluginAppPolicyContext as at, buildCodexNativeWebSearchThreadConfig as b, buildCodexNativeHookRelayConfig as c, ensureCodexPluginActivation as ct, emitCodexNativePreToolUseFailureDiagnostic as d, isOpenAiCuratedMarketplaceName as dt, buildCodexPluginAppsConfigPatchFromPolicyContext as et, resolveCodexNativeHookRelayEvents as f, pluginReadParams as ft, attestCodexRestrictedToolSurfaceMcpServersDisabled as g, assertCodexManagedRequirementsDoNotOverrideToolPolicy as h, startOrResumeThread as i, buildDisabledAppsConfigPatch as it, CODEX_MANAGED_THREAD_MAX_ENTRIES as j, resolveReasoningEffort as k, buildCodexNativeHookRelayDisabledConfig as l, refreshCodexPluginRuntimeState as lt, scheduleCodexNativeHookRelayUnregister as m, buildTurnStartParams as n, buildCodexPluginThreadConfigInputFingerprint as nt, CODEX_NATIVE_HOOK_RELAY_EVENTS as o, mergeCodexThreadConfigs as ot, resolveCodexNativeHookRelayTtlMs as p, resolveRecoverableCodexPluginConfigKeys as pt, buildCodexContinuityCalibration as q, buildCodexUserInput as r, buildCodexPluginThreadConfigTimeoutFallback as rt, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as s, shouldBuildCodexPluginThreadConfig as st, buildTurnCollaborationMode as t, buildCodexPluginThreadConfig as tt, createCodexNativeHookRelay as u, isOpenAiCuratedMarketplace as ut, buildCodexRuntimeThreadConfig as v, CODEX_NATIVE_PERSONALITY_NONE as w, resolveCodexWebSearchPlan as x, readCodexInheritedMcpServerNames as y, isForcedPrivateQaCodexRuntime as z };
|