@openclaw/codex 2026.8.1-beta.2 → 2026.9.1-beta.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 +147 -0
- package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-bfsmcQ6m.js} +21 -2
- package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-BMvPX73o.js} +15 -3
- package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-B7iBUcuP.js} +22 -16
- package/dist/{capabilities-CybGI1oz.js → capabilities-BRMh0aIq.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-DEsIufkt.js} +2 -2
- package/dist/{command-handlers-Db8pu_ds.js → command-handlers-D_-1_qRp.js} +65 -37
- package/dist/command-rpc-CCeCkf_v.js +90 -0
- package/dist/{compact-LspQaGfl.js → compact-BglNFZrQ.js} +35 -29
- package/dist/{session-binding-DPxIvMgE.js → config-giW8X0QA.js} +108 -914
- package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
- package/dist/doctor-contract-api.js +23 -21
- package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-Dnlfa--w.js} +1345 -1424
- package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-CTeGrnfO.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-Bk1I90Bg.js} +18 -18
- package/dist/harness-Csz0F-DK.js +258 -0
- package/dist/harness.js +2 -226
- package/dist/index.js +262 -181
- package/dist/{isolated-completion-C58NbL40.js → isolated-completion-Cv6FqygM.js} +4 -4
- package/dist/managed-binary-DFYK4DWq.js +248 -0
- package/dist/media-understanding-provider.js +2 -2
- package/dist/model-catalog-i9rNBthX.js +48 -0
- package/dist/{models-BFlpn1Ex.js → models-TS3HBU-q.js} +4 -3
- package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-4Mfq2HDs.js} +354 -83
- package/dist/node-exec-server.runtime-BpWqJO1V.js +192 -0
- package/dist/{plugin-app-cache-key-C3H2CVp6.js → plugin-app-cache-key-C1CAdfcP.js} +19 -4
- package/dist/plugin-approval-roundtrip-bUm777tR.js +95 -0
- package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-B9DvaAG-.js} +59 -54
- package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-CTCT3w5L.js} +164 -134
- package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-VeJxxJ0U.js} +80 -5
- package/dist/{rate-limits-DZXmOL8-.js → rate-limits-C5_D8Gmb.js} +1 -1
- package/dist/{request-BRl5_UfX.js → request-DXXGHko0.js} +2 -2
- package/dist/{run-attempt-P8BdxC1I.js → run-attempt-DYc0QCBF.js} +1276 -765
- package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-BvyT1uyd.js} +4 -7
- package/dist/{sandbox-guard-Cmwc9eIk.js → sandbox-guard-kuWTn9C5.js} +25 -19
- package/dist/session-binding-Bcynfd-_.js +874 -0
- package/dist/{session-catalog-xwNi_SDM.js → session-catalog-DaLKVtpH.js} +1534 -1288
- package/dist/session-catalog-home-id-BNwAJ9D7.js +18 -0
- package/dist/{session-cli-7hgXZDV7.js → session-cli-DMc4oS2w.js} +20 -7
- package/dist/session-permission-policy-BrNH1bjy.js +160 -0
- package/dist/{session-retirement-DoMMJFKm.js → session-retirement-CcNC3YSY.js} +3 -3
- package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-BIkusb95.js} +1 -1
- package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-Dc3ApjU4.js} +24 -20
- package/dist/{shared-client-kIBbGjk4.js → shared-client-DKz23hHY.js} +1632 -372
- package/dist/{side-question-9HAL26l9.js → side-question-DjA_jsNe.js} +105 -54
- package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-L7oEkTlq.js} +1147 -114
- package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-DZeuKH1j.js} +2 -2
- package/dist/{thread-resume-CPrJNQVM.js → thread-resume-BE9qNEW0.js} +3 -3
- package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-DsCMWBca.js} +137 -38
- package/dist/{transport-stdio-DIImwDVS.js → transport-stdio-DhoGy33Z.js} +2 -2
- 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-DlaHxSLC.js} +12 -6
- package/dist/{usage-CHlV0e-o.js → usage-ROIaW3wo.js} +3 -3
- package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-ClaNPV7N.js} +3 -3
- package/openclaw.plugin.json +49 -2
- package/package.json +9 -8
- package/dist/app-server-policy-zcBDc1JU.js +0 -49
|
@@ -1,27 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
4
|
-
import { a as
|
|
5
|
-
import { a as
|
|
1
|
+
import { _ as CODEX_PLUGINS_MARKETPLACE_NAME, b as assertCodexModelBackedReviewerEffectiveConfig, g as resolveCodexPluginsPolicy, n as codexSandboxPolicyForTurn, v as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME } from "./config-giW8X0QA.js";
|
|
2
|
+
import { G as CodexAppServerRpcError, J as consumeCodexAppServerLiveThread, L as isCodexAppServerConnectionClosedError, P as getCodexAppServerClientInstanceId, St as resolveCodexAppServerLocalHomeDir, U as resolveCodexAppServerClientInstanceId, _t as flattenCodexDynamicToolFunctions, c as isCodexAppServerStartSelectionChangedError, h as retainSharedCodexAppServerClientByInstanceId, vt as isJsonObject } from "./shared-client-DKz23hHY.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-Bcynfd-_.js";
|
|
4
|
+
import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, d as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-bfsmcQ6m.js";
|
|
5
|
+
import { a as assertCodexThreadStartResponse, r as assertCodexThreadForkResponse } from "./protocol-validators-VeJxxJ0U.js";
|
|
6
|
+
import { o as defaultCodexAppInventoryCache, s as serializeCodexAppInventoryError, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-C1CAdfcP.js";
|
|
6
7
|
import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { n as codexCatalogHomeId } from "./session-catalog-home-id-BNwAJ9D7.js";
|
|
9
|
+
import { t as resumeCodexAppServerThread } from "./thread-resume-BE9qNEW0.js";
|
|
10
|
+
import { u as projectBoundedCodexThreadHistory } from "./transcript-mirror-DsCMWBca.js";
|
|
9
11
|
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
|
|
10
12
|
import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
13
|
import fs from "node:fs/promises";
|
|
12
14
|
import path from "node:path";
|
|
13
|
-
import {
|
|
15
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
16
|
+
import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
17
|
+
import { SKILL_WORKSHOP_TOOL_NAME, TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, formatToolExecutionErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode, resolveToolExecutionErrorKind } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
14
18
|
import * as crypto$1 from "node:crypto";
|
|
15
|
-
import crypto from "node:crypto";
|
|
19
|
+
import crypto, { createHash } from "node:crypto";
|
|
16
20
|
import os from "node:os";
|
|
21
|
+
import { resolveAgentDir as resolveAgentDir$1, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
22
|
+
import { z } from "zod";
|
|
17
23
|
import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
18
24
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
19
25
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
26
|
+
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
20
27
|
import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
21
28
|
import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
|
|
29
|
+
import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
|
|
22
30
|
import { buildCodexUserMcpServersThreadConfigPatchForRuntime } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
23
|
-
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
24
|
-
import {
|
|
31
|
+
import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent, isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
32
|
+
import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
|
|
25
33
|
//#region extensions/codex/src/app-server/plugin-inventory.ts
|
|
26
34
|
/**
|
|
27
35
|
* Reads Codex plugin marketplace state and app inventory to decide which
|
|
@@ -451,12 +459,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
|
|
|
451
459
|
}
|
|
452
460
|
//#endregion
|
|
453
461
|
//#region extensions/codex/src/app-server/plugin-thread-app-admission.ts
|
|
462
|
+
function resolveCodexPluginThreadAppCacheKey(params) {
|
|
463
|
+
return params.threadId ? `${params.appCacheKey}:thread:${encodeURIComponent(params.threadId)}` : params.appCacheKey;
|
|
464
|
+
}
|
|
465
|
+
function createCodexPluginThreadAppInventoryRequest(params) {
|
|
466
|
+
return async (method, requestParams) => await params.request(method, (method === "app/installed" || method === "app/read") && params.threadId ? {
|
|
467
|
+
...requestParams,
|
|
468
|
+
threadId: params.threadId
|
|
469
|
+
} : requestParams);
|
|
470
|
+
}
|
|
454
471
|
async function refreshCodexPluginAppInventory(params, appCache, options = {}) {
|
|
455
472
|
if (!params.appCacheKey) return;
|
|
456
|
-
const request =
|
|
473
|
+
const request = createCodexPluginThreadAppInventoryRequest(params);
|
|
457
474
|
try {
|
|
458
475
|
return await appCache.refreshNow({
|
|
459
|
-
key: params
|
|
476
|
+
key: resolveCodexPluginThreadAppCacheKey(params),
|
|
460
477
|
request,
|
|
461
478
|
nowMs: params.nowMs,
|
|
462
479
|
forceRefetch: options.forceRefetch,
|
|
@@ -493,9 +510,9 @@ function normalizeCodexPluginOwnerName(name) {
|
|
|
493
510
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
494
511
|
}
|
|
495
512
|
async function readCodexThreadAdmissibleAccountApps(params, appCache) {
|
|
496
|
-
const request =
|
|
513
|
+
const request = createCodexPluginThreadAppInventoryRequest(params);
|
|
497
514
|
const cachedInventory = appCache.read({
|
|
498
|
-
key: params
|
|
515
|
+
key: resolveCodexPluginThreadAppCacheKey(params),
|
|
499
516
|
request,
|
|
500
517
|
nowMs: params.nowMs,
|
|
501
518
|
suppressRefresh: true
|
|
@@ -610,6 +627,11 @@ function buildCodexPluginThreadConfigTimeoutFallback(params) {
|
|
|
610
627
|
/** Builds the Codex apps config patch and policy context for a native thread. */
|
|
611
628
|
async function buildCodexPluginThreadConfig(params) {
|
|
612
629
|
const appCache = params.appCache ?? defaultCodexAppInventoryCache;
|
|
630
|
+
const threadAppCacheKey = resolveCodexPluginThreadAppCacheKey(params);
|
|
631
|
+
const threadRequest = (method, requestParams) => params.request(method, (method === "app/installed" || method === "app/read") && params.threadId && isJsonObject(requestParams) ? {
|
|
632
|
+
...requestParams,
|
|
633
|
+
threadId: params.threadId
|
|
634
|
+
} : requestParams);
|
|
613
635
|
let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
|
|
614
636
|
pluginConfig: params.pluginConfig,
|
|
615
637
|
appCacheKey: params.appCacheKey
|
|
@@ -623,9 +645,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
623
645
|
let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
|
|
624
646
|
pluginConfig: params.pluginConfig,
|
|
625
647
|
policy,
|
|
626
|
-
request:
|
|
648
|
+
request: threadRequest,
|
|
627
649
|
appCache,
|
|
628
|
-
appCacheKey:
|
|
650
|
+
appCacheKey: threadAppCacheKey,
|
|
629
651
|
configCwd: params.configCwd,
|
|
630
652
|
metadataCache: params.metadataCache,
|
|
631
653
|
nowMs: params.nowMs,
|
|
@@ -641,9 +663,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
641
663
|
inventory = await readCodexPluginInventory({
|
|
642
664
|
pluginConfig: params.pluginConfig,
|
|
643
665
|
policy,
|
|
644
|
-
request:
|
|
666
|
+
request: threadRequest,
|
|
645
667
|
appCache,
|
|
646
|
-
appCacheKey:
|
|
668
|
+
appCacheKey: threadAppCacheKey,
|
|
647
669
|
configCwd: params.configCwd,
|
|
648
670
|
metadataCache: params.metadataCache,
|
|
649
671
|
nowMs: params.nowMs
|
|
@@ -659,9 +681,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
659
681
|
if (!record.activationRequired) continue;
|
|
660
682
|
const activation = await ensureCodexPluginActivation({
|
|
661
683
|
identity: record.policy,
|
|
662
|
-
request:
|
|
684
|
+
request: threadRequest,
|
|
663
685
|
appCache,
|
|
664
|
-
appCacheKey:
|
|
686
|
+
appCacheKey: threadAppCacheKey,
|
|
665
687
|
configCwd: params.configCwd,
|
|
666
688
|
metadataCache: params.metadataCache,
|
|
667
689
|
deferAppInventoryRefresh: true,
|
|
@@ -685,9 +707,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
685
707
|
inventory = await readCodexPluginInventory({
|
|
686
708
|
pluginConfig: params.pluginConfig,
|
|
687
709
|
policy,
|
|
688
|
-
request:
|
|
710
|
+
request: threadRequest,
|
|
689
711
|
appCache,
|
|
690
|
-
appCacheKey:
|
|
712
|
+
appCacheKey: threadAppCacheKey,
|
|
691
713
|
configCwd: params.configCwd,
|
|
692
714
|
metadataCache: params.metadataCache,
|
|
693
715
|
nowMs: params.nowMs
|
|
@@ -706,9 +728,9 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
706
728
|
inventory = await readCodexPluginInventory({
|
|
707
729
|
pluginConfig: params.pluginConfig,
|
|
708
730
|
policy,
|
|
709
|
-
request:
|
|
731
|
+
request: threadRequest,
|
|
710
732
|
appCache,
|
|
711
|
-
appCacheKey:
|
|
733
|
+
appCacheKey: threadAppCacheKey,
|
|
712
734
|
configCwd: params.configCwd,
|
|
713
735
|
metadataCache: params.metadataCache,
|
|
714
736
|
nowMs: params.nowMs
|
|
@@ -1063,6 +1085,32 @@ function resolveCodexContextEngineProjectionMaxChars(params) {
|
|
|
1063
1085
|
function resolveCodexContextEngineProjectionReserveTokens() {
|
|
1064
1086
|
return DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS;
|
|
1065
1087
|
}
|
|
1088
|
+
const CONTINUITY_PROJECTION_RESERVE_RATIO = .5;
|
|
1089
|
+
const CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN = 3;
|
|
1090
|
+
const CONTINUITY_MIN_CHARS_PER_TOKEN = .5;
|
|
1091
|
+
const CONTINUITY_MAX_CHARS_PER_TOKEN = CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
|
|
1092
|
+
const CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS = 5e4;
|
|
1093
|
+
/** Builds a calibration sample from a completed turn, or undefined if unusable. */
|
|
1094
|
+
function buildCodexContinuityCalibration(params) {
|
|
1095
|
+
if (!Number.isFinite(params.promptChars) || !Number.isFinite(params.inputTokens) || params.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || params.inputTokens <= 0) return;
|
|
1096
|
+
return {
|
|
1097
|
+
promptChars: Math.floor(params.promptChars),
|
|
1098
|
+
inputTokens: Math.floor(params.inputTokens)
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
function resolveContinuityCharsPerToken(calibration) {
|
|
1102
|
+
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;
|
|
1103
|
+
return Math.min(CONTINUITY_MAX_CHARS_PER_TOKEN, Math.max(CONTINUITY_MIN_CHARS_PER_TOKEN, calibration.promptChars / calibration.inputTokens));
|
|
1104
|
+
}
|
|
1105
|
+
/** Resolves rendered context size for no-engine continuity projections. */
|
|
1106
|
+
function resolveCodexContinuityProjectionMaxChars(params) {
|
|
1107
|
+
const contextTokenBudget = typeof params.contextTokenBudget === "number" && Number.isFinite(params.contextTokenBudget) ? Math.floor(params.contextTokenBudget) : void 0;
|
|
1108
|
+
if (!contextTokenBudget || contextTokenBudget <= 0) return DEFAULT_RENDERED_CONTEXT_CHARS;
|
|
1109
|
+
return normalizeRenderedContextMaxChars(resolveProjectionPromptBudgetTokens({
|
|
1110
|
+
contextTokenBudget,
|
|
1111
|
+
reserveTokens: Math.max(DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS, Math.floor(contextTokenBudget * CONTINUITY_PROJECTION_RESERVE_RATIO))
|
|
1112
|
+
}) * resolveContinuityCharsPerToken(params.calibration));
|
|
1113
|
+
}
|
|
1066
1114
|
/** Fits projected context prompts under Codex app-server turn/start text limits. */
|
|
1067
1115
|
function fitCodexProjectedContextForTurnStart(params) {
|
|
1068
1116
|
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 +1376,6 @@ const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set
|
|
|
1328
1376
|
"write",
|
|
1329
1377
|
"edit",
|
|
1330
1378
|
"apply_patch",
|
|
1331
|
-
"update_plan",
|
|
1332
1379
|
...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
|
|
1333
1380
|
]);
|
|
1334
1381
|
const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
|
|
@@ -1561,10 +1608,6 @@ const nativeSkillIsolationByClient = /* @__PURE__ */ new WeakMap();
|
|
|
1561
1608
|
function isMissingPathError(error) {
|
|
1562
1609
|
return error.code === "ENOENT";
|
|
1563
1610
|
}
|
|
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
1611
|
async function canonicalizeExistingPath(candidate) {
|
|
1569
1612
|
try {
|
|
1570
1613
|
return await fs.realpath(candidate);
|
|
@@ -1590,12 +1633,12 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
|
|
|
1590
1633
|
const realDefaultCodexHome = await canonicalizeExistingPath(defaultCodexHome);
|
|
1591
1634
|
roots.push({
|
|
1592
1635
|
dir: path.join(defaultCodexHome, "skills"),
|
|
1593
|
-
onlyEscapedStateTargets:
|
|
1636
|
+
onlyEscapedStateTargets: isPathInside(realStateDir, realDefaultCodexHome)
|
|
1594
1637
|
});
|
|
1595
1638
|
}
|
|
1596
1639
|
const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
|
|
1597
1640
|
if (configuredCodexHome) {
|
|
1598
|
-
const stateOwned =
|
|
1641
|
+
const stateOwned = isPathInside(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
|
|
1599
1642
|
roots.push({
|
|
1600
1643
|
dir: path.join(configuredCodexHome, "skills"),
|
|
1601
1644
|
onlyEscapedStateTargets: stateOwned
|
|
@@ -1613,7 +1656,7 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
|
|
|
1613
1656
|
const recordSkillFile = async (filePath, onlyEscapedStateTargets) => {
|
|
1614
1657
|
try {
|
|
1615
1658
|
const skillRealPath = await fs.realpath(filePath);
|
|
1616
|
-
if (!onlyEscapedStateTargets || !
|
|
1659
|
+
if (!onlyEscapedStateTargets || !isPathInside(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
|
|
1617
1660
|
} catch (error) {
|
|
1618
1661
|
if (!isMissingPathError(error)) complete = false;
|
|
1619
1662
|
}
|
|
@@ -1756,7 +1799,7 @@ function applyCodexNativeSkillIsolation(config, isolation) {
|
|
|
1756
1799
|
//#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
|
|
1757
1800
|
var CodexThreadStartRequestError = class extends Error {
|
|
1758
1801
|
constructor(cause) {
|
|
1759
|
-
super(formatErrorMessage(cause)
|
|
1802
|
+
super(`thread/start: ${formatErrorMessage(cause)}`, { cause });
|
|
1760
1803
|
this.name = "CodexThreadStartRequestError";
|
|
1761
1804
|
}
|
|
1762
1805
|
};
|
|
@@ -1780,6 +1823,64 @@ var CodexAdoptedThreadActiveError = class extends Error {
|
|
|
1780
1823
|
}
|
|
1781
1824
|
};
|
|
1782
1825
|
//#endregion
|
|
1826
|
+
//#region extensions/codex/src/app-server/managed-thread-store.ts
|
|
1827
|
+
const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
|
|
1828
|
+
const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
|
|
1829
|
+
const managedThreadSchema = z.object({
|
|
1830
|
+
version: z.literal(1),
|
|
1831
|
+
kind: z.literal("managed-thread"),
|
|
1832
|
+
sourceHomeId: z.string().min(1),
|
|
1833
|
+
threadId: z.string().min(1),
|
|
1834
|
+
rolloutPath: z.string().min(1).optional()
|
|
1835
|
+
});
|
|
1836
|
+
async function markStartedCodexManagedThread(store, params) {
|
|
1837
|
+
if (!store) return;
|
|
1838
|
+
try {
|
|
1839
|
+
await store.mark({
|
|
1840
|
+
sourceHomeId: params.sourceHomeId,
|
|
1841
|
+
threadId: params.threadId,
|
|
1842
|
+
...params.rolloutPath ? { rolloutPath: params.rolloutPath } : {}
|
|
1843
|
+
});
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
function managedThreadStoreKey(sourceHomeId, threadId) {
|
|
1849
|
+
return `sha256:${createHash("sha256").update("openclaw:codex-managed-thread:v1\0").update(sourceHomeId).update("\0").update(threadId).digest("hex")}`;
|
|
1850
|
+
}
|
|
1851
|
+
/** Durable ownership index for Codex threads created by OpenClaw. */
|
|
1852
|
+
function createCodexManagedThreadStore(state) {
|
|
1853
|
+
return {
|
|
1854
|
+
async mark(params) {
|
|
1855
|
+
try {
|
|
1856
|
+
const value = managedThreadSchema.parse({
|
|
1857
|
+
version: 1,
|
|
1858
|
+
kind: "managed-thread",
|
|
1859
|
+
sourceHomeId: params.sourceHomeId.trim(),
|
|
1860
|
+
threadId: params.threadId.trim(),
|
|
1861
|
+
...params.rolloutPath?.trim() ? { rolloutPath: params.rolloutPath.trim() } : {}
|
|
1862
|
+
});
|
|
1863
|
+
state.registerIfAbsent(managedThreadStoreKey(value.sourceHomeId, value.threadId), value);
|
|
1864
|
+
return true;
|
|
1865
|
+
} catch (error) {
|
|
1866
|
+
embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
|
|
1867
|
+
return false;
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
async snapshot() {
|
|
1871
|
+
const byHome = /* @__PURE__ */ new Map();
|
|
1872
|
+
for (const entry of state.entries()) {
|
|
1873
|
+
const parsed = managedThreadSchema.safeParse(entry.value);
|
|
1874
|
+
if (!parsed.success) continue;
|
|
1875
|
+
const ids = byHome.get(parsed.data.sourceHomeId) ?? /* @__PURE__ */ new Set();
|
|
1876
|
+
ids.add(parsed.data.threadId);
|
|
1877
|
+
byHome.set(parsed.data.sourceHomeId, ids);
|
|
1878
|
+
}
|
|
1879
|
+
return byHome;
|
|
1880
|
+
}
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
//#endregion
|
|
1783
1884
|
//#region extensions/codex/src/app-server/plugin-thread-attestation.ts
|
|
1784
1885
|
/**
|
|
1785
1886
|
* Confirms admitted plugin and account apps against their actual Codex thread before
|
|
@@ -1835,6 +1936,50 @@ async function discardUnattestedCodexPluginThread(params) {
|
|
|
1835
1936
|
}
|
|
1836
1937
|
}
|
|
1837
1938
|
//#endregion
|
|
1939
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-result.ts
|
|
1940
|
+
/** Materializes the public lifecycle result after a fresh thread is durably committed. */
|
|
1941
|
+
function buildStartedCodexThreadBinding(input) {
|
|
1942
|
+
const { context, params, response, startParams } = input;
|
|
1943
|
+
return {
|
|
1944
|
+
threadId: response.thread.id,
|
|
1945
|
+
...input.clientId ? { clientId: input.clientId } : {},
|
|
1946
|
+
cwd: params.cwd,
|
|
1947
|
+
...input.rolloutPath ? { rolloutPath: input.rolloutPath } : {},
|
|
1948
|
+
authProfileId: params.params.authProfileId,
|
|
1949
|
+
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
1950
|
+
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
1951
|
+
modelProvider: response.modelProvider ?? input.startModelProvider ?? input.modelProvider,
|
|
1952
|
+
dynamicToolsFingerprint: context.dynamicToolsFingerprint,
|
|
1953
|
+
dynamicToolsContainDeferred: context.dynamicToolsContainDeferred,
|
|
1954
|
+
nativeSkillIsolationFingerprint: context.nativeSkillIsolationFingerprint,
|
|
1955
|
+
userMcpServersFingerprint: context.userMcpServersFingerprint,
|
|
1956
|
+
mcpServersFingerprint: input.nextMcpServersFingerprint,
|
|
1957
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
1958
|
+
ringZeroConfigFingerprint: context.ringZeroConfigFingerprint,
|
|
1959
|
+
ringZeroClientInstanceId: context.ringZeroClientInstanceId,
|
|
1960
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1961
|
+
networkProxyConfigFingerprint: context.networkProxyConfigFingerprint,
|
|
1962
|
+
nativeHookRelayGeneration: input.finalConfigPatch.nativeHookRelayGeneration,
|
|
1963
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1964
|
+
pluginAppsFingerprint: input.pluginThreadConfig?.fingerprint,
|
|
1965
|
+
pluginAppsInputFingerprint: input.pluginThreadConfig?.inputFingerprint,
|
|
1966
|
+
pluginAppPolicyContext: input.pluginThreadConfig?.policyContext,
|
|
1967
|
+
contextEngine: context.contextEngineBinding,
|
|
1968
|
+
environmentSelectionFingerprint: context.environmentSelectionFingerprint,
|
|
1969
|
+
...!context.preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
|
|
1970
|
+
...startParams,
|
|
1971
|
+
model: response.model ?? startParams.model ?? null,
|
|
1972
|
+
requestedModel: startParams.model ?? null,
|
|
1973
|
+
modelProvider: input.bindingModelProvider ?? null,
|
|
1974
|
+
requestedModelProvider: startParams.modelProvider ?? input.bindingModelProvider ?? null
|
|
1975
|
+
}, params.params.authProfileId, context.dynamicToolsFingerprint) } : {},
|
|
1976
|
+
lifecycle: {
|
|
1977
|
+
action: "started",
|
|
1978
|
+
...context.rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
//#endregion
|
|
1838
1983
|
//#region extensions/codex/src/app-server/reasoning-effort.ts
|
|
1839
1984
|
const CODEX_REASONING_EFFORTS = [
|
|
1840
1985
|
"minimal",
|
|
@@ -1963,12 +2108,21 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
|
|
|
1963
2108
|
});
|
|
1964
2109
|
}
|
|
1965
2110
|
//#endregion
|
|
2111
|
+
//#region extensions/codex/src/app-server/project-doc-thread-config.ts
|
|
2112
|
+
const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 128 * 1024;
|
|
2113
|
+
function buildCodexProjectDocThreadConfig(config) {
|
|
2114
|
+
const defaults = { project_doc_max_bytes: CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
|
|
2115
|
+
return mergeCodexThreadConfigs(defaults, config) ?? defaults;
|
|
2116
|
+
}
|
|
2117
|
+
//#endregion
|
|
1966
2118
|
//#region extensions/codex/src/app-server/thread-prompt.ts
|
|
1967
2119
|
function buildDeveloperInstructions(params, options = {}) {
|
|
1968
2120
|
const deferredToolNames = /* @__PURE__ */ new Set();
|
|
1969
2121
|
let hasSkillWorkshop = false;
|
|
1970
2122
|
let hasSessionsSpawn = false;
|
|
1971
2123
|
let hasSessionsYield = false;
|
|
2124
|
+
let hasSubagentsList = false;
|
|
2125
|
+
let hasSessionsSend = false;
|
|
1972
2126
|
let hasSeenDirectNamespace = false;
|
|
1973
2127
|
let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
|
|
1974
2128
|
for (const spec of options.dynamicTools ?? []) {
|
|
@@ -1980,6 +2134,8 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
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
|
-
|
|
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
|
+
}),
|
|
2000
2173
|
TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT,
|
|
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,29 +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 = mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
|
|
2472
|
+
...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG,
|
|
2473
|
+
...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
|
|
2474
|
+
...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
|
|
2475
|
+
};
|
|
2260
2476
|
delete disabledConfig["features.apply_patch_streaming_events"];
|
|
2261
2477
|
return disabledConfig;
|
|
2262
2478
|
}
|
|
2263
|
-
if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig,
|
|
2479
|
+
if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
|
|
2264
2480
|
...codeModeConfig,
|
|
2265
2481
|
...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
|
|
2482
|
+
...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG,
|
|
2266
2483
|
"features.code_mode_only": true
|
|
2267
2484
|
}, options.directOnlyToolNamespaces);
|
|
2268
|
-
return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig,
|
|
2485
|
+
return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
|
|
2269
2486
|
...codeModeConfig,
|
|
2270
|
-
...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG
|
|
2487
|
+
...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
|
|
2488
|
+
...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
|
|
2271
2489
|
}, options.directOnlyToolNamespaces);
|
|
2272
2490
|
}
|
|
2273
2491
|
function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
|
|
@@ -2286,6 +2504,7 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2286
2504
|
const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
|
|
2287
2505
|
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
|
|
2288
2506
|
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
|
|
2507
|
+
const restrictedTurnDisablesProjectDocs = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted && params.disableTools;
|
|
2289
2508
|
const configMcpServers = config?.mcp_servers;
|
|
2290
2509
|
if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
|
|
2291
2510
|
const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
|
|
@@ -2301,18 +2520,19 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2301
2520
|
webSearchAllowed: options.webSearchAllowed
|
|
2302
2521
|
}).threadConfig;
|
|
2303
2522
|
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(restrictedRunConfig, webSearchConfig), options);
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
...CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG
|
|
2309
|
-
};
|
|
2523
|
+
return applyCodexManagedShellEnvironment({
|
|
2524
|
+
...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,
|
|
2525
|
+
...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
|
|
2526
|
+
}, options.shellEnvironment, options.disableLoginShell);
|
|
2310
2527
|
}
|
|
2311
2528
|
function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
|
|
2312
2529
|
if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
|
|
2313
|
-
return
|
|
2530
|
+
return {
|
|
2531
|
+
...buildRestrictedToolConfigPatch(inheritedMcpServerNames),
|
|
2532
|
+
...CODEX_NO_PROJECT_DOCS_CONFIG
|
|
2533
|
+
};
|
|
2314
2534
|
}
|
|
2315
|
-
function
|
|
2535
|
+
function buildRestrictedToolConfigPatch(inheritedMcpServerNames) {
|
|
2316
2536
|
const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
|
|
2317
2537
|
return {
|
|
2318
2538
|
...CODEX_RING_ZERO_THREAD_CONFIG,
|
|
@@ -2336,12 +2556,12 @@ async function readCodexInheritedMcpServerNames(client, cwd, signal) {
|
|
|
2336
2556
|
if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
|
|
2337
2557
|
return Object.keys(configuredServers).toSorted();
|
|
2338
2558
|
}
|
|
2339
|
-
async function
|
|
2559
|
+
async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, options, signal) {
|
|
2340
2560
|
const response = await client.request("configRequirements/read", void 0, { signal });
|
|
2341
2561
|
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
|
|
2342
2562
|
if (response.requirements === null) return;
|
|
2343
2563
|
if (!isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
|
|
2344
|
-
for (const key of [
|
|
2564
|
+
if (options.restrictedToolSurface) for (const key of [
|
|
2345
2565
|
"hooks",
|
|
2346
2566
|
"managedHooks",
|
|
2347
2567
|
"managed_hooks"
|
|
@@ -2351,6 +2571,7 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
|
|
|
2351
2571
|
if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
|
|
2352
2572
|
if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex restricted tool surface cannot override managed hooks");
|
|
2353
2573
|
}
|
|
2574
|
+
const additionalDeniedFeatures = new Set(options.additionalDeniedFeatures);
|
|
2354
2575
|
for (const key of ["featureRequirements", "feature_requirements"]) {
|
|
2355
2576
|
const requirements = response.requirements[key];
|
|
2356
2577
|
if (requirements === void 0 || requirements === null) continue;
|
|
@@ -2358,7 +2579,8 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
|
|
|
2358
2579
|
for (const [feature, enabled] of Object.entries(requirements)) {
|
|
2359
2580
|
if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2360
2581
|
const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
|
|
2361
|
-
|
|
2582
|
+
const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
|
|
2583
|
+
if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
|
|
2362
2584
|
}
|
|
2363
2585
|
}
|
|
2364
2586
|
}
|
|
@@ -2408,6 +2630,15 @@ function resolveCodexThreadEnvironmentSelection(options) {
|
|
|
2408
2630
|
}
|
|
2409
2631
|
//#endregion
|
|
2410
2632
|
//#region extensions/codex/src/app-server/thread-lifecycle-io.ts
|
|
2633
|
+
function resolveManagedThreadSourceHomeId(params) {
|
|
2634
|
+
const agentId = resolveSessionAgentIds$1({
|
|
2635
|
+
config: params.params.config,
|
|
2636
|
+
sessionKey: params.params.sessionKey,
|
|
2637
|
+
agentId: params.agentId ?? params.params.agentId
|
|
2638
|
+
}).sessionAgentId;
|
|
2639
|
+
const agentDir = params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
|
|
2640
|
+
return codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir));
|
|
2641
|
+
}
|
|
2411
2642
|
function resolveCodexThreadRolloutPath(thread) {
|
|
2412
2643
|
const rolloutPath = thread.path?.trim();
|
|
2413
2644
|
if (!rolloutPath || !path.isAbsolute(rolloutPath) || path.extname(rolloutPath) !== ".jsonl" || !path.basename(rolloutPath).includes(thread.id)) return;
|
|
@@ -2429,6 +2660,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2429
2660
|
const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
|
|
2430
2661
|
const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
2431
2662
|
threadId: resumeBinding.threadId,
|
|
2663
|
+
cwd: params.cwd,
|
|
2432
2664
|
authProfileId,
|
|
2433
2665
|
model: startModelSelection.model,
|
|
2434
2666
|
modelProvider: startModelProvider,
|
|
@@ -2442,7 +2674,9 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2442
2674
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2443
2675
|
webSearchAllowed: params.webSearchAllowed,
|
|
2444
2676
|
hostSystemAgentActive,
|
|
2445
|
-
restrictedToolSurfaceInheritedMcpServerNames
|
|
2677
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
2678
|
+
shellEnvironment: params.shellEnvironment,
|
|
2679
|
+
disableLoginShell: params.disableLoginShell
|
|
2446
2680
|
}));
|
|
2447
2681
|
const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
|
|
2448
2682
|
throwIfAborted();
|
|
@@ -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 = resolveManagedThreadSourceHomeId(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,44 +2959,820 @@ async function startFreshCodexThread(params, context) {
|
|
|
2716
2959
|
threadId: response.thread.id,
|
|
2717
2960
|
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
2718
2961
|
});
|
|
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
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
//#endregion
|
|
2978
|
+
//#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
|
|
2979
|
+
/** Retains the host-owned app preview without adding it to Codex's response payload. */
|
|
2980
|
+
function withDynamicToolTranscriptDetails(response, details) {
|
|
2981
|
+
if (details === void 0) return response;
|
|
2982
|
+
Object.defineProperty(response, "transcriptDetails", {
|
|
2983
|
+
configurable: true,
|
|
2984
|
+
enumerable: false,
|
|
2985
|
+
value: details
|
|
2986
|
+
});
|
|
2987
|
+
return response;
|
|
2988
|
+
}
|
|
2989
|
+
function withDynamicToolTerminalResolution(response, terminalResolution) {
|
|
2990
|
+
if (terminalResolution) {
|
|
2991
|
+
Object.defineProperties(response, {
|
|
2992
|
+
terminalResolution: {
|
|
2993
|
+
configurable: true,
|
|
2994
|
+
enumerable: false,
|
|
2995
|
+
value: terminalResolution
|
|
2996
|
+
},
|
|
2997
|
+
executionStarted: {
|
|
2998
|
+
configurable: true,
|
|
2999
|
+
enumerable: false,
|
|
3000
|
+
value: terminalResolution.executionStarted
|
|
3001
|
+
},
|
|
3002
|
+
...terminalResolution.executedArguments ? { executedArguments: {
|
|
3003
|
+
configurable: true,
|
|
3004
|
+
enumerable: false,
|
|
3005
|
+
value: terminalResolution.executedArguments
|
|
3006
|
+
} } : {}
|
|
3007
|
+
});
|
|
3008
|
+
withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
|
|
3009
|
+
}
|
|
3010
|
+
return response;
|
|
3011
|
+
}
|
|
3012
|
+
function withDynamicToolExecutionState(response, state) {
|
|
3013
|
+
Object.defineProperties(response, {
|
|
3014
|
+
executedArguments: {
|
|
3015
|
+
configurable: true,
|
|
3016
|
+
enumerable: false,
|
|
3017
|
+
value: state.executedArguments
|
|
3018
|
+
},
|
|
3019
|
+
executionStarted: {
|
|
3020
|
+
configurable: true,
|
|
3021
|
+
enumerable: false,
|
|
3022
|
+
value: state.executionStarted
|
|
3023
|
+
}
|
|
3024
|
+
});
|
|
3025
|
+
return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
|
|
3026
|
+
}
|
|
3027
|
+
function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
|
|
3028
|
+
if (!sideEffectEvidence) {
|
|
3029
|
+
delete response.sideEffectEvidence;
|
|
3030
|
+
return response;
|
|
3031
|
+
}
|
|
3032
|
+
Object.defineProperty(response, "sideEffectEvidence", {
|
|
3033
|
+
configurable: true,
|
|
3034
|
+
enumerable: false,
|
|
3035
|
+
value: true
|
|
3036
|
+
});
|
|
3037
|
+
return response;
|
|
3038
|
+
}
|
|
3039
|
+
function createFailedDynamicToolResponse(message, options) {
|
|
3040
|
+
const response = {
|
|
3041
|
+
contentItems: [{
|
|
3042
|
+
type: "inputText",
|
|
3043
|
+
text: message
|
|
3044
|
+
}],
|
|
3045
|
+
success: false
|
|
3046
|
+
};
|
|
3047
|
+
Object.defineProperties(response, {
|
|
3048
|
+
diagnosticTerminalReason: {
|
|
3049
|
+
configurable: true,
|
|
3050
|
+
enumerable: false,
|
|
3051
|
+
value: options?.terminalReason ?? "failed"
|
|
3052
|
+
},
|
|
3053
|
+
diagnosticTerminalType: {
|
|
3054
|
+
configurable: true,
|
|
3055
|
+
enumerable: false,
|
|
3056
|
+
value: "error"
|
|
3057
|
+
}
|
|
3058
|
+
});
|
|
3059
|
+
if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
|
|
3060
|
+
configurable: true,
|
|
3061
|
+
enumerable: false,
|
|
3062
|
+
value: options.executionStarted
|
|
3063
|
+
});
|
|
3064
|
+
if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
|
|
3065
|
+
configurable: true,
|
|
3066
|
+
enumerable: false,
|
|
3067
|
+
value: options.executedArguments
|
|
3068
|
+
});
|
|
3069
|
+
return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
|
|
3070
|
+
}
|
|
3071
|
+
//#endregion
|
|
3072
|
+
//#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
|
|
3073
|
+
/** Leaf helper shared by native and dynamic tool diagnostics. */
|
|
3074
|
+
const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
|
|
3075
|
+
"codex_startup_timeout",
|
|
3076
|
+
"turn_completion_idle_timeout",
|
|
3077
|
+
"turn_progress_idle_timeout",
|
|
3078
|
+
"turn_terminal_idle_timeout"
|
|
3079
|
+
]);
|
|
3080
|
+
/** Preserves timeout provenance when an enclosing run aborts an active tool. */
|
|
3081
|
+
function resolveCodexToolAbortTerminalReason(signal) {
|
|
3082
|
+
try {
|
|
3083
|
+
const reason = signal.reason;
|
|
3084
|
+
if (typeof reason === "string") {
|
|
3085
|
+
if (CODEX_TIMEOUT_ABORT_REASONS.has(reason)) return "timed_out";
|
|
3086
|
+
return reason === "client_closed" ? "failed" : "cancelled";
|
|
3087
|
+
}
|
|
3088
|
+
if (reason && typeof reason === "object") {
|
|
3089
|
+
const record = reason;
|
|
3090
|
+
if (record.name === "TimeoutError" || record.reason === "timeout") return "timed_out";
|
|
3091
|
+
}
|
|
3092
|
+
} catch {
|
|
3093
|
+
return "cancelled";
|
|
3094
|
+
}
|
|
3095
|
+
return "cancelled";
|
|
3096
|
+
}
|
|
3097
|
+
//#endregion
|
|
3098
|
+
//#region extensions/codex/src/app-server/dynamic-tool-execution.ts
|
|
3099
|
+
/**
|
|
3100
|
+
* Timeout, terminal-release, and diagnostic helpers for Codex dynamic tool
|
|
3101
|
+
* calls.
|
|
3102
|
+
*/
|
|
3103
|
+
/** Default timeout for Codex dynamic tool calls. */
|
|
3104
|
+
const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 9e4;
|
|
3105
|
+
/** Hard cap for per-call Codex dynamic tool timeout overrides. */
|
|
3106
|
+
const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
|
|
3107
|
+
const CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS = 3e4;
|
|
3108
|
+
const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
|
|
3109
|
+
const CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS = 3e4;
|
|
3110
|
+
const CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS = 3e4;
|
|
3111
|
+
/** Timeout for image-understanding style dynamic tool calls. */
|
|
3112
|
+
const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
|
|
3113
|
+
/** Timeout for message-delivery dynamic tool calls. */
|
|
3114
|
+
const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 6e5;
|
|
3115
|
+
/** Outer default for collector waits: full swarm budget plus completion grace. */
|
|
3116
|
+
const CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS = 63e4;
|
|
3117
|
+
const LOG_FIELD_MAX_LENGTH = 160;
|
|
3118
|
+
function normalizeLogField(value) {
|
|
3119
|
+
if (typeof value !== "string") return;
|
|
3120
|
+
const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
|
|
3121
|
+
if (!normalized) return;
|
|
3122
|
+
return normalized.length > LOG_FIELD_MAX_LENGTH ? `${truncateUtf16Safe(normalized, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
|
|
3123
|
+
}
|
|
3124
|
+
function readNumericTimeoutMs(value) {
|
|
3125
|
+
if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
|
|
3126
|
+
if (typeof value === "string") {
|
|
3127
|
+
const parsed = parseStrictNonNegativeInteger(value);
|
|
3128
|
+
if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
function formatDynamicToolTimeoutDetails(params) {
|
|
3132
|
+
const tool = normalizeLogField(params.call.tool) ?? "unknown";
|
|
3133
|
+
const baseMeta = {
|
|
3134
|
+
tool: params.call.tool,
|
|
3135
|
+
toolCallId: params.call.callId,
|
|
3136
|
+
threadId: params.call.threadId,
|
|
3137
|
+
turnId: params.call.turnId,
|
|
3138
|
+
timeoutMs: params.timeoutMs,
|
|
3139
|
+
timeoutKind: "codex_dynamic_tool_rpc"
|
|
3140
|
+
};
|
|
3141
|
+
if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
|
|
3142
|
+
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
|
|
3143
|
+
consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
|
|
3144
|
+
meta: baseMeta
|
|
3145
|
+
};
|
|
3146
|
+
const action = normalizeLogField(params.call.arguments.action);
|
|
3147
|
+
const sessionId = normalizeLogField(params.call.arguments.sessionId);
|
|
3148
|
+
const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
|
|
3149
|
+
const actionPart = action ? ` action=${action}` : "";
|
|
3150
|
+
const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
|
|
3151
|
+
const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
|
|
3152
|
+
const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
|
|
3153
|
+
const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
|
|
2719
3154
|
return {
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
dynamicToolsFingerprint,
|
|
2728
|
-
dynamicToolsContainDeferred,
|
|
2729
|
-
nativeSkillIsolationFingerprint,
|
|
2730
|
-
userMcpServersFingerprint,
|
|
2731
|
-
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2732
|
-
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
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 } : {}
|
|
3155
|
+
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
|
|
3156
|
+
consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
|
|
3157
|
+
meta: {
|
|
3158
|
+
...baseMeta,
|
|
3159
|
+
processAction: action,
|
|
3160
|
+
processSessionId: sessionId,
|
|
3161
|
+
processRequestedTimeoutMs: requestedTimeoutMs
|
|
2754
3162
|
}
|
|
2755
3163
|
};
|
|
2756
3164
|
}
|
|
3165
|
+
/**
|
|
3166
|
+
* Runs a dynamic tool call with run-abort and per-call timeout handling,
|
|
3167
|
+
* returning a Codex protocol response instead of throwing.
|
|
3168
|
+
*/
|
|
3169
|
+
async function handleDynamicToolCallWithTimeout(params) {
|
|
3170
|
+
let didNotifyAgentToolResult = false;
|
|
3171
|
+
const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
|
|
3172
|
+
const finalizeTerminal = (response) => {
|
|
3173
|
+
const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
|
|
3174
|
+
const ownerKey = params.toolBridge.sideEffectOwnerKeyForTool?.(params.call.tool);
|
|
3175
|
+
const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
|
|
3176
|
+
const terminalResolution = params.observeToolTerminal?.({
|
|
3177
|
+
toolCallId: params.call.callId,
|
|
3178
|
+
toolName: params.call.tool,
|
|
3179
|
+
arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
|
|
3180
|
+
...params.toolMeta ? { meta: params.toolMeta } : {},
|
|
3181
|
+
...ownerKey ? { ownerMutation: { ownerKey } } : {},
|
|
3182
|
+
...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
|
|
3183
|
+
outcome: response.success ? "success" : "failure",
|
|
3184
|
+
...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
|
|
3185
|
+
});
|
|
3186
|
+
return withDynamicToolTerminalResolution(response, terminalResolution);
|
|
3187
|
+
};
|
|
3188
|
+
const createFailedAfterPossibleDispatch = (message, terminalReason) => {
|
|
3189
|
+
const response = createFailedDynamicToolResponse(message, {
|
|
3190
|
+
executionStarted: true,
|
|
3191
|
+
sideEffectEvidence: true,
|
|
3192
|
+
terminalReason
|
|
3193
|
+
});
|
|
3194
|
+
conservativeRaceResponses.add(response);
|
|
3195
|
+
return response;
|
|
3196
|
+
};
|
|
3197
|
+
const notifyAgentToolResult = (event) => {
|
|
3198
|
+
if (didNotifyAgentToolResult) return;
|
|
3199
|
+
didNotifyAgentToolResult = true;
|
|
3200
|
+
try {
|
|
3201
|
+
params.onAgentToolResult?.(event);
|
|
3202
|
+
} catch (error) {
|
|
3203
|
+
embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
|
|
3204
|
+
}
|
|
3205
|
+
};
|
|
3206
|
+
const notifyFailedToolResult = (message, terminalReason = "failed") => {
|
|
3207
|
+
notifyAgentToolResult({
|
|
3208
|
+
toolName: params.call.tool,
|
|
3209
|
+
result: {
|
|
3210
|
+
content: [{
|
|
3211
|
+
type: "text",
|
|
3212
|
+
text: message
|
|
3213
|
+
}],
|
|
3214
|
+
details: {
|
|
3215
|
+
status: terminalReason,
|
|
3216
|
+
error: message
|
|
3217
|
+
}
|
|
3218
|
+
},
|
|
3219
|
+
isError: true
|
|
3220
|
+
});
|
|
3221
|
+
};
|
|
3222
|
+
if (params.signal.aborted) {
|
|
3223
|
+
const message = "OpenClaw dynamic tool call aborted before execution.";
|
|
3224
|
+
const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
|
|
3225
|
+
params.onFallbackSelected?.();
|
|
3226
|
+
notifyFailedToolResult(message, terminalReason);
|
|
3227
|
+
return finalizeTerminal(createFailedDynamicToolResponse(message, {
|
|
3228
|
+
executionStarted: false,
|
|
3229
|
+
terminalReason
|
|
3230
|
+
}));
|
|
3231
|
+
}
|
|
3232
|
+
const controller = new AbortController();
|
|
3233
|
+
let timeout;
|
|
3234
|
+
let timedOut = false;
|
|
3235
|
+
let resolveAbort;
|
|
3236
|
+
const abortFromRun = () => {
|
|
3237
|
+
const message = "OpenClaw dynamic tool call aborted.";
|
|
3238
|
+
const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
|
|
3239
|
+
params.onFallbackSelected?.();
|
|
3240
|
+
controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
|
|
3241
|
+
notifyFailedToolResult(message, terminalReason);
|
|
3242
|
+
resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
|
|
3243
|
+
};
|
|
3244
|
+
const abortPromise = new Promise((resolve) => {
|
|
3245
|
+
resolveAbort = resolve;
|
|
3246
|
+
});
|
|
3247
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
3248
|
+
const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
|
|
3249
|
+
timeout = setTimeout(() => {
|
|
3250
|
+
timedOut = true;
|
|
3251
|
+
const timeoutDetails = formatDynamicToolTimeoutDetails({
|
|
3252
|
+
call: params.call,
|
|
3253
|
+
timeoutMs
|
|
3254
|
+
});
|
|
3255
|
+
params.onFallbackSelected?.();
|
|
3256
|
+
controller.abort(new Error(timeoutDetails.responseMessage));
|
|
3257
|
+
params.onTimeout?.();
|
|
3258
|
+
embeddedAgentLog.warn("codex dynamic tool call timed out", {
|
|
3259
|
+
...timeoutDetails.meta,
|
|
3260
|
+
consoleMessage: timeoutDetails.consoleMessage
|
|
3261
|
+
});
|
|
3262
|
+
notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
|
|
3263
|
+
resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
|
|
3264
|
+
}, timeoutMs);
|
|
3265
|
+
timeout.unref?.();
|
|
3266
|
+
});
|
|
3267
|
+
try {
|
|
3268
|
+
params.signal.addEventListener("abort", abortFromRun, { once: true });
|
|
3269
|
+
if (params.signal.aborted) abortFromRun();
|
|
3270
|
+
const response = await Promise.race([
|
|
3271
|
+
params.toolBridge.handleToolCall(params.call, {
|
|
3272
|
+
signal: controller.signal,
|
|
3273
|
+
onAgentToolResult: notifyAgentToolResult,
|
|
3274
|
+
toolCallOrdinal: params.toolCallOrdinal,
|
|
3275
|
+
retainExecutionSnapshot: true
|
|
3276
|
+
}),
|
|
3277
|
+
abortPromise,
|
|
3278
|
+
timeoutPromise
|
|
3279
|
+
]);
|
|
3280
|
+
if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
|
|
3281
|
+
return finalizeTerminal(response);
|
|
3282
|
+
} catch (error) {
|
|
3283
|
+
const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
|
|
3284
|
+
const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
|
|
3285
|
+
notifyFailedToolResult(message, terminalReason);
|
|
3286
|
+
return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
|
|
3287
|
+
} finally {
|
|
3288
|
+
if (timeout) clearTimeout(timeout);
|
|
3289
|
+
params.signal.removeEventListener("abort", abortFromRun);
|
|
3290
|
+
resolveAbort = void 0;
|
|
3291
|
+
if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
function readDynamicToolResponseText(response) {
|
|
3295
|
+
return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
|
|
3296
|
+
}
|
|
3297
|
+
/** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
|
|
3298
|
+
function toCodexDynamicToolProtocolResponse(response) {
|
|
3299
|
+
return {
|
|
3300
|
+
contentItems: response.contentItems,
|
|
3301
|
+
success: response.success
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
/** Adds async-started progress details when a tool result continues out of band. */
|
|
3305
|
+
function toCodexDynamicToolProgressResponse(response, protocolResponse) {
|
|
3306
|
+
const transcriptDetails = isJsonObject(response.transcriptDetails) ? response.transcriptDetails : void 0;
|
|
3307
|
+
const mcpAppPreview = isJsonObject(transcriptDetails?.mcpAppPreview) ? transcriptDetails.mcpAppPreview : void 0;
|
|
3308
|
+
const progressDetails = mcpAppPreview ? { mcpAppPreview } : void 0;
|
|
3309
|
+
if (response.asyncStarted !== true && progressDetails === void 0) return protocolResponse;
|
|
3310
|
+
return {
|
|
3311
|
+
...protocolResponse,
|
|
3312
|
+
...progressDetails ? { details: progressDetails } : {},
|
|
3313
|
+
...response.asyncStarted === true ? { details: {
|
|
3314
|
+
...progressDetails,
|
|
3315
|
+
async: true,
|
|
3316
|
+
status: "started"
|
|
3317
|
+
} } : {}
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
/** Decides whether a terminal dynamic tool response can release the Codex turn. */
|
|
3321
|
+
function shouldReleaseTurnAfterTerminalDynamicTool(state) {
|
|
3322
|
+
return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
|
|
3323
|
+
}
|
|
3324
|
+
/** Returns true when a non-async result should block terminal-release shortcuts. */
|
|
3325
|
+
function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
|
|
3326
|
+
return response.asyncStarted !== true;
|
|
3327
|
+
}
|
|
3328
|
+
/** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
|
|
3329
|
+
function resolveTerminalDynamicToolBatchAction(state) {
|
|
3330
|
+
if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
|
|
3331
|
+
if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
|
|
3332
|
+
if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
|
|
3333
|
+
return "idle";
|
|
3334
|
+
}
|
|
3335
|
+
/** Returns true for diagnostic events that terminate a dynamic tool call. */
|
|
3336
|
+
function isDynamicToolTerminalDiagnosticEvent(event) {
|
|
3337
|
+
return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
|
|
3338
|
+
}
|
|
3339
|
+
/** Matches terminal diagnostics to a specific dynamic tool call id/name. */
|
|
3340
|
+
function isMatchingDynamicToolTerminalDiagnostic(params) {
|
|
3341
|
+
if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
|
|
3342
|
+
if (params.runId !== void 0) return params.event.runId === params.runId;
|
|
3343
|
+
if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
|
|
3344
|
+
if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
|
|
3345
|
+
return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
|
|
3346
|
+
}
|
|
3347
|
+
/** Checks pending diagnostics for a terminal event matching a tool call. */
|
|
3348
|
+
function hasPendingDynamicToolTerminalDiagnostic(params) {
|
|
3349
|
+
return hasPendingInternalDiagnosticEvent((event) => {
|
|
3350
|
+
if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
|
|
3351
|
+
return isMatchingDynamicToolTerminalDiagnostic({
|
|
3352
|
+
event,
|
|
3353
|
+
call: params.call,
|
|
3354
|
+
runId: params.runId,
|
|
3355
|
+
sessionId: params.sessionId,
|
|
3356
|
+
sessionKey: params.sessionKey
|
|
3357
|
+
});
|
|
3358
|
+
});
|
|
3359
|
+
}
|
|
3360
|
+
/** Resolves per-tool timeout, applying media/message defaults and hard caps. */
|
|
3361
|
+
function resolveDynamicToolCallTimeoutMs(params) {
|
|
3362
|
+
if (params.call.tool === "computer") return clampDynamicToolTimeoutMs(readComputerToolTimeoutMs(params.call.arguments));
|
|
3363
|
+
if (params.call.tool === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
|
|
3364
|
+
if (params.call.tool === "agents_wait") {
|
|
3365
|
+
const requestedMs = readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS;
|
|
3366
|
+
return Math.max(1, Math.min(63e4, Math.floor(requestedMs)));
|
|
3367
|
+
}
|
|
3368
|
+
return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_TOOL_TIMEOUT_MS);
|
|
3369
|
+
}
|
|
3370
|
+
function readComputerToolTimeoutMs(value) {
|
|
3371
|
+
const args = isJsonObject(value) ? value : void 0;
|
|
3372
|
+
const action = typeof args?.action === "string" ? args.action : void 0;
|
|
3373
|
+
const gatewayTimeoutMs = readPositiveFiniteTimeoutMs(args?.timeoutMs) ?? CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS;
|
|
3374
|
+
const gatewayCallCount = action === "screenshot" || action === "wait" ? 3 : 4;
|
|
3375
|
+
return (action === "wait" || action === "hold_key" ? Math.max(0, Number(args?.duration) || 0) * 1e3 : 0) + gatewayCallCount * gatewayTimeoutMs + CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS;
|
|
3376
|
+
}
|
|
3377
|
+
function readDynamicToolCallTimeoutMs(value) {
|
|
3378
|
+
if (!isJsonObject(value)) return;
|
|
3379
|
+
const timeoutMs = readPositiveFiniteTimeoutMs(value.timeoutMs);
|
|
3380
|
+
if (timeoutMs !== void 0) return timeoutMs;
|
|
3381
|
+
const timeoutSecondsMs = readDynamicToolTimeoutSecondsAsMs(value.timeoutSeconds);
|
|
3382
|
+
return timeoutSecondsMs === void 0 ? void 0 : addTimerTimeoutGraceMs(timeoutSecondsMs, CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS);
|
|
3383
|
+
}
|
|
3384
|
+
function readConfiguredDynamicToolTimeoutMs(toolName, config) {
|
|
3385
|
+
if (toolName === "image_generate") {
|
|
3386
|
+
const imageModel = config?.agents?.defaults?.mediaModels?.image;
|
|
3387
|
+
if (!imageModel || typeof imageModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
3388
|
+
return readPositiveFiniteTimeoutMs(imageModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
3389
|
+
}
|
|
3390
|
+
if (toolName === "view_image") {
|
|
3391
|
+
const candidates = (config?.tools?.media?.models ?? []).filter((entry) => !entry.capabilities || entry.capabilities.includes("image"));
|
|
3392
|
+
const capabilityTimeoutMs = readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds);
|
|
3393
|
+
return Math.max(capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS, ...candidates.map((entry) => readTimeoutSecondsAsMs(entry.timeoutSeconds) ?? capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS));
|
|
3394
|
+
}
|
|
3395
|
+
if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
|
|
3396
|
+
}
|
|
3397
|
+
function readTimeoutSecondsAsMs(value) {
|
|
3398
|
+
const seconds = readPositiveFiniteTimeoutMs(value);
|
|
3399
|
+
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
3400
|
+
}
|
|
3401
|
+
function readDynamicToolTimeoutSecondsAsMs(value) {
|
|
3402
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return;
|
|
3403
|
+
return value * 1e3;
|
|
3404
|
+
}
|
|
3405
|
+
function readPositiveFiniteTimeoutMs(value) {
|
|
3406
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
3407
|
+
}
|
|
3408
|
+
function clampDynamicToolTimeoutMs(timeoutMs) {
|
|
3409
|
+
return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
|
|
3410
|
+
}
|
|
3411
|
+
//#endregion
|
|
3412
|
+
//#region extensions/codex/src/app-server/native-hook-relay-state.ts
|
|
3413
|
+
const pending = /* @__PURE__ */ new Set();
|
|
3414
|
+
/** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
|
|
3415
|
+
const nativeHookRelayUnregisterQueue = {
|
|
3416
|
+
add(entry) {
|
|
3417
|
+
pending.add(entry);
|
|
3418
|
+
},
|
|
3419
|
+
delete(entry) {
|
|
3420
|
+
return pending.delete(entry);
|
|
3421
|
+
},
|
|
3422
|
+
flush() {
|
|
3423
|
+
while (pending.size > 0) {
|
|
3424
|
+
const entry = pending.values().next().value;
|
|
3425
|
+
if (!entry) return;
|
|
3426
|
+
clearTimeout(entry.timeout);
|
|
3427
|
+
entry.unregister();
|
|
3428
|
+
}
|
|
3429
|
+
},
|
|
3430
|
+
clear() {
|
|
3431
|
+
for (const entry of pending) clearTimeout(entry.timeout);
|
|
3432
|
+
pending.clear();
|
|
3433
|
+
}
|
|
3434
|
+
};
|
|
3435
|
+
//#endregion
|
|
3436
|
+
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
3437
|
+
/**
|
|
3438
|
+
* Bridges Codex native hook callbacks into OpenClaw's native hook relay so
|
|
3439
|
+
* app-server tool events can still run OpenClaw policy and diagnostics.
|
|
3440
|
+
*/
|
|
3441
|
+
/** Codex hook events that can be registered through OpenClaw's native relay. */
|
|
3442
|
+
const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
|
3443
|
+
"pre_tool_use",
|
|
3444
|
+
"post_tool_use",
|
|
3445
|
+
"permission_request",
|
|
3446
|
+
"before_agent_finalize"
|
|
3447
|
+
];
|
|
3448
|
+
const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
|
|
3449
|
+
const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
|
|
3450
|
+
/** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
|
|
3451
|
+
const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
|
|
3452
|
+
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
|
|
3453
|
+
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
|
|
3454
|
+
const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
|
|
3455
|
+
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
|
|
3456
|
+
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
|
|
3457
|
+
const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
|
|
3458
|
+
const nativeHookPolicyByClient = /* @__PURE__ */ new WeakMap();
|
|
3459
|
+
const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
|
|
3460
|
+
exec: [
|
|
3461
|
+
"Bash",
|
|
3462
|
+
"exec",
|
|
3463
|
+
"exec_command"
|
|
3464
|
+
],
|
|
3465
|
+
apply_patch: [
|
|
3466
|
+
"apply_patch",
|
|
3467
|
+
"Write",
|
|
3468
|
+
"Edit"
|
|
3469
|
+
],
|
|
3470
|
+
spawn_agent: ["spawn_agent", "Agent"]
|
|
3471
|
+
};
|
|
3472
|
+
/** Enterprise managed-only policy silently drops the session-layer hooks that enforce OpenClaw. */
|
|
3473
|
+
async function assertCodexNativeHookRelayAllowed(client, signal) {
|
|
3474
|
+
let attestation = nativeHookPolicyByClient.get(client);
|
|
3475
|
+
if (!attestation) {
|
|
3476
|
+
attestation = client.request("configRequirements/read", void 0, { signal }).then((response) => {
|
|
3477
|
+
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid hook policy response");
|
|
3478
|
+
const requirements = response.requirements;
|
|
3479
|
+
if (requirements === null) return;
|
|
3480
|
+
if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid hook policy requirements");
|
|
3481
|
+
const managedOnly = requirements.allowManagedHooksOnly;
|
|
3482
|
+
if (managedOnly !== void 0 && managedOnly !== null && typeof managedOnly !== "boolean") throw new Error("Codex configRequirements/read returned invalid managed-only hook policy");
|
|
3483
|
+
if (managedOnly === true) throw new Error("Codex managed-only hooks disable the OpenClaw native hook relay; refusing unenforced execution");
|
|
3484
|
+
});
|
|
3485
|
+
nativeHookPolicyByClient.set(client, attestation);
|
|
3486
|
+
attestation.catch(() => {
|
|
3487
|
+
if (nativeHookPolicyByClient.get(client) === attestation) nativeHookPolicyByClient.delete(client);
|
|
3488
|
+
});
|
|
3489
|
+
}
|
|
3490
|
+
await attestation;
|
|
3491
|
+
}
|
|
3492
|
+
/** Defers relay unregister so late native hook subprocesses can still resolve. */
|
|
3493
|
+
function scheduleCodexNativeHookRelayUnregister(params) {
|
|
3494
|
+
let pending;
|
|
3495
|
+
const unregister = () => {
|
|
3496
|
+
if (!pending) return;
|
|
3497
|
+
const current = pending;
|
|
3498
|
+
pending = void 0;
|
|
3499
|
+
if (!nativeHookRelayUnregisterQueue.delete(current)) return;
|
|
3500
|
+
params.relay.unregister();
|
|
3501
|
+
};
|
|
3502
|
+
const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
|
|
3503
|
+
pending = {
|
|
3504
|
+
timeout,
|
|
3505
|
+
unregister
|
|
3506
|
+
};
|
|
3507
|
+
nativeHookRelayUnregisterQueue.add(pending);
|
|
3508
|
+
timeout.unref();
|
|
3509
|
+
}
|
|
3510
|
+
/** Computes the delayed unregister window from Codex's hook timeout. */
|
|
3511
|
+
function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
|
|
3512
|
+
const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
|
|
3513
|
+
return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
|
|
3514
|
+
}
|
|
3515
|
+
/** Records a native pre-tool failure that Codex does not project as a tool item. */
|
|
3516
|
+
function emitCodexNativePreToolUseFailureDiagnostic(params) {
|
|
3517
|
+
emitTrustedDiagnosticEvent({
|
|
3518
|
+
type: "tool.execution.error",
|
|
3519
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
3520
|
+
sessionId: params.sessionId,
|
|
3521
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3522
|
+
runId: params.runId,
|
|
3523
|
+
toolName: params.failure.toolName,
|
|
3524
|
+
toolCallId: params.failure.toolCallId,
|
|
3525
|
+
durationMs: params.failure.durationMs,
|
|
3526
|
+
errorCategory: "before_tool_call",
|
|
3527
|
+
terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
|
|
3528
|
+
...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
|
|
3529
|
+
});
|
|
3530
|
+
}
|
|
3531
|
+
/** Registers an OpenClaw native hook relay for a Codex app-server turn. */
|
|
3532
|
+
function createCodexNativeHookRelay(params) {
|
|
3533
|
+
if (params.options?.enabled === false) return;
|
|
3534
|
+
const directChildClaims = /* @__PURE__ */ new Map();
|
|
3535
|
+
const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
|
|
3536
|
+
let foregroundClosed = false;
|
|
3537
|
+
let successfulYieldRetentionAuthorized = false;
|
|
3538
|
+
const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
|
|
3539
|
+
const rejectPendingAdmissions = (reason) => {
|
|
3540
|
+
for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
|
|
3541
|
+
pendingDirectChildAdmissions.clear();
|
|
3542
|
+
};
|
|
3543
|
+
const relay = registerRetainedNativeHookRelayForBundledRuntime({
|
|
3544
|
+
provider: "codex",
|
|
3545
|
+
relayId: buildCodexNativeHookRelayId({
|
|
3546
|
+
agentId: params.agentId,
|
|
3547
|
+
sessionId: params.sessionId,
|
|
3548
|
+
sessionKey: params.sessionKey
|
|
3549
|
+
}),
|
|
3550
|
+
...params.generation ? { generation: params.generation } : {},
|
|
3551
|
+
...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
|
|
3552
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
3553
|
+
sessionId: params.sessionId,
|
|
3554
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3555
|
+
...params.config ? { config: params.config } : {},
|
|
3556
|
+
runId: params.runId,
|
|
3557
|
+
...params.channelId ? { channelId: params.channelId } : {},
|
|
3558
|
+
...params.requester ? { requester: params.requester } : {},
|
|
3559
|
+
...params.approvalContext ? { approvalContext: params.approvalContext } : {},
|
|
3560
|
+
allowedEvents: params.events,
|
|
3561
|
+
preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
|
|
3562
|
+
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
3563
|
+
explicitTtlMs: params.options?.ttlMs,
|
|
3564
|
+
attemptTimeoutMs: params.attemptTimeoutMs,
|
|
3565
|
+
startupTimeoutMs: params.startupTimeoutMs,
|
|
3566
|
+
turnStartTimeoutMs: params.turnStartTimeoutMs
|
|
3567
|
+
}),
|
|
3568
|
+
signal: params.signal,
|
|
3569
|
+
runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
|
|
3570
|
+
assertActive: params.hostCapabilities.assertActive,
|
|
3571
|
+
retention: {
|
|
3572
|
+
readClaim: readCodexNativeChildThreadId,
|
|
3573
|
+
shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
|
|
3574
|
+
allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
|
|
3575
|
+
awaitForegroundAdmission: (childThreadId) => {
|
|
3576
|
+
if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
|
|
3577
|
+
const existingClaim = directChildClaims.get(childThreadId);
|
|
3578
|
+
if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
|
|
3579
|
+
const existingPending = pendingDirectChildAdmissions.get(childThreadId);
|
|
3580
|
+
if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
|
|
3581
|
+
if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
|
|
3582
|
+
const { promise, resolve, reject } = createDeferred();
|
|
3583
|
+
pendingDirectChildAdmissions.set(childThreadId, {
|
|
3584
|
+
promise,
|
|
3585
|
+
resolve,
|
|
3586
|
+
reject
|
|
3587
|
+
});
|
|
3588
|
+
return promise.then((claim) => assertClaim(childThreadId, claim));
|
|
3589
|
+
},
|
|
3590
|
+
onDispose: () => {
|
|
3591
|
+
foregroundClosed = true;
|
|
3592
|
+
rejectPendingAdmissions("native hook relay registration closed");
|
|
3593
|
+
}
|
|
3594
|
+
},
|
|
3595
|
+
onPreToolUseFailure: params.onPreToolUseFailure,
|
|
3596
|
+
command: {
|
|
3597
|
+
nice: 10,
|
|
3598
|
+
timeoutMs: params.options?.gatewayTimeoutMs
|
|
3599
|
+
}
|
|
3600
|
+
});
|
|
3601
|
+
const unregister = () => {
|
|
3602
|
+
foregroundClosed = true;
|
|
3603
|
+
rejectPendingAdmissions("native hook relay foreground closed");
|
|
3604
|
+
relay.unregister();
|
|
3605
|
+
};
|
|
3606
|
+
return {
|
|
3607
|
+
...relay,
|
|
3608
|
+
unregister,
|
|
3609
|
+
authorizeRetentionAfterSuccessfulYield: () => {
|
|
3610
|
+
successfulYieldRetentionAuthorized = true;
|
|
3611
|
+
},
|
|
3612
|
+
hasClaimedDirectChild: () => directChildClaims.size > 0,
|
|
3613
|
+
rejectPendingDirectChild: (threadIdInput, reason) => {
|
|
3614
|
+
const threadId = threadIdInput.trim();
|
|
3615
|
+
const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
|
|
3616
|
+
if (!pending) return;
|
|
3617
|
+
pendingDirectChildAdmissions.delete(threadId);
|
|
3618
|
+
pending.reject(new Error(reason));
|
|
3619
|
+
},
|
|
3620
|
+
claimDirectChild: (threadIdInput) => {
|
|
3621
|
+
const threadId = threadIdInput.trim();
|
|
3622
|
+
if (!threadId) return () => void 0;
|
|
3623
|
+
if (directChildClaims.get(threadId)) return () => void 0;
|
|
3624
|
+
const claim = Symbol(threadId);
|
|
3625
|
+
directChildClaims.set(threadId, claim);
|
|
3626
|
+
const pending = pendingDirectChildAdmissions.get(threadId);
|
|
3627
|
+
pendingDirectChildAdmissions.delete(threadId);
|
|
3628
|
+
pending?.resolve(claim);
|
|
3629
|
+
let released = false;
|
|
3630
|
+
return () => {
|
|
3631
|
+
if (released) return;
|
|
3632
|
+
released = true;
|
|
3633
|
+
if (directChildClaims.get(threadId) !== claim) return;
|
|
3634
|
+
directChildClaims.delete(threadId);
|
|
3635
|
+
if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
function readCodexNativeChildThreadId(rawPayload) {
|
|
3641
|
+
if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
|
|
3642
|
+
return rawPayload.agent_id.trim() || void 0;
|
|
3643
|
+
}
|
|
3644
|
+
/** Selects the native hook events Codex should install for the current approval mode. */
|
|
3645
|
+
function resolveCodexNativeHookRelayEvents(params) {
|
|
3646
|
+
if (params.configuredEvents?.length) return params.configuredEvents;
|
|
3647
|
+
return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
|
|
3648
|
+
}
|
|
3649
|
+
/** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
|
|
3650
|
+
function resolveCodexNativeHookRelayTtlMs(params) {
|
|
3651
|
+
if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
|
|
3652
|
+
const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
|
|
3653
|
+
return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
|
|
3654
|
+
}
|
|
3655
|
+
/** Builds a stable relay id scoped to the agent and session identity. */
|
|
3656
|
+
function buildCodexNativeHookRelayId(params) {
|
|
3657
|
+
const hash = createHash("sha256");
|
|
3658
|
+
hash.update("openclaw:codex:native-hook-relay:v1");
|
|
3659
|
+
hash.update("\0");
|
|
3660
|
+
hash.update(params.agentId?.trim() || "");
|
|
3661
|
+
hash.update("\0");
|
|
3662
|
+
hash.update(params.sessionKey?.trim() || params.sessionId);
|
|
3663
|
+
return `codex-${hash.digest("hex").slice(0, 40)}`;
|
|
3664
|
+
}
|
|
3665
|
+
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
|
|
3666
|
+
pre_tool_use: "PreToolUse",
|
|
3667
|
+
post_tool_use: "PostToolUse",
|
|
3668
|
+
permission_request: "PermissionRequest",
|
|
3669
|
+
before_agent_finalize: "Stop"
|
|
3670
|
+
};
|
|
3671
|
+
const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
|
|
3672
|
+
pre_tool_use: "pre_tool_use",
|
|
3673
|
+
post_tool_use: "post_tool_use",
|
|
3674
|
+
permission_request: "permission_request",
|
|
3675
|
+
before_agent_finalize: "stop"
|
|
3676
|
+
};
|
|
3677
|
+
const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
|
|
3678
|
+
/** Builds the Codex config overlay that installs trusted command hooks for relay events. */
|
|
3679
|
+
function buildCodexNativeHookRelayConfig(params) {
|
|
3680
|
+
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
|
3681
|
+
const selectedEvents = new Set(events);
|
|
3682
|
+
const config = { "features.hooks": true };
|
|
3683
|
+
const hookState = {};
|
|
3684
|
+
for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
|
|
3685
|
+
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
|
3686
|
+
const selected = selectedEvents.has(event);
|
|
3687
|
+
const shouldRelay = params.relay.shouldRelayEvent(event);
|
|
3688
|
+
if (!selected || !shouldRelay) {
|
|
3689
|
+
if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
|
|
3690
|
+
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 };
|
|
3691
|
+
continue;
|
|
3692
|
+
}
|
|
3693
|
+
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
3694
|
+
const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
|
|
3695
|
+
const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
|
|
3696
|
+
config[`hooks.${codexEvent}`] = [{
|
|
3697
|
+
...matcher ? { matcher } : {},
|
|
3698
|
+
hooks: [{
|
|
3699
|
+
type: "command",
|
|
3700
|
+
command,
|
|
3701
|
+
timeout,
|
|
3702
|
+
async: false,
|
|
3703
|
+
statusMessage: "OpenClaw native hook relay"
|
|
3704
|
+
}]
|
|
3705
|
+
}];
|
|
3706
|
+
const state = {
|
|
3707
|
+
enabled: true,
|
|
3708
|
+
trusted_hash: codexCommandHookTrustedHash({
|
|
3709
|
+
event,
|
|
3710
|
+
command,
|
|
3711
|
+
matcher,
|
|
3712
|
+
timeout,
|
|
3713
|
+
statusMessage: "OpenClaw native hook relay"
|
|
3714
|
+
})
|
|
3715
|
+
};
|
|
3716
|
+
for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
|
|
3717
|
+
}
|
|
3718
|
+
config["hooks.state"] = hookState;
|
|
3719
|
+
return config;
|
|
3720
|
+
}
|
|
3721
|
+
/** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
|
|
3722
|
+
function buildCodexNativeHookRelayDisabledConfig() {
|
|
3723
|
+
return {
|
|
3724
|
+
"features.hooks": false,
|
|
3725
|
+
"hooks.PreToolUse": [],
|
|
3726
|
+
"hooks.PostToolUse": [],
|
|
3727
|
+
"hooks.PermissionRequest": [],
|
|
3728
|
+
"hooks.Stop": []
|
|
3729
|
+
};
|
|
3730
|
+
}
|
|
3731
|
+
function normalizeHookTimeoutSec(value) {
|
|
3732
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
|
|
3733
|
+
}
|
|
3734
|
+
function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
|
|
3735
|
+
const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
|
|
3736
|
+
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)));
|
|
3737
|
+
return Math.max(1, parentTimeoutMs - parentMarginMs);
|
|
3738
|
+
}
|
|
3739
|
+
function buildCodexNativeToolMatcher(toolNames) {
|
|
3740
|
+
if (toolNames === void 0) return;
|
|
3741
|
+
if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
|
|
3742
|
+
const nativeNames = /* @__PURE__ */ new Set();
|
|
3743
|
+
let hasCustomToolName = false;
|
|
3744
|
+
for (const toolName of toolNames) {
|
|
3745
|
+
const canonicalToolName = toolName.trim();
|
|
3746
|
+
if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
|
|
3747
|
+
const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
|
|
3748
|
+
if (!nativeAliases) hasCustomToolName = true;
|
|
3749
|
+
for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
|
|
3750
|
+
}
|
|
3751
|
+
const sortedNames = Array.from(nativeNames).toSorted();
|
|
3752
|
+
if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
|
|
3753
|
+
return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
|
|
3754
|
+
}
|
|
3755
|
+
function codexCommandHookTrustedHash(params) {
|
|
3756
|
+
const identity = {
|
|
3757
|
+
event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
|
|
3758
|
+
...params.matcher ? { matcher: params.matcher } : {},
|
|
3759
|
+
hooks: [{
|
|
3760
|
+
async: false,
|
|
3761
|
+
command: params.command,
|
|
3762
|
+
statusMessage: params.statusMessage,
|
|
3763
|
+
timeout: params.timeout,
|
|
3764
|
+
type: "command"
|
|
3765
|
+
}]
|
|
3766
|
+
};
|
|
3767
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
|
|
3768
|
+
}
|
|
3769
|
+
function sortJsonValue(value) {
|
|
3770
|
+
if (!value || typeof value !== "object") return value;
|
|
3771
|
+
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
3772
|
+
const sorted = {};
|
|
3773
|
+
for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
|
|
3774
|
+
return sorted;
|
|
3775
|
+
}
|
|
2757
3776
|
//#endregion
|
|
2758
3777
|
//#region extensions/codex/src/app-server/profiler-flag.ts
|
|
2759
3778
|
const PROFILER_FLAGS = ["profiler", "codex.profiler"];
|
|
@@ -2853,6 +3872,13 @@ function createCodexThreadLifecycleTimingTracker(options = {}) {
|
|
|
2853
3872
|
//#endregion
|
|
2854
3873
|
//#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
|
|
2855
3874
|
async function prepareCodexThreadLifecyclePreflight(params) {
|
|
3875
|
+
await assertCodexModelBackedReviewerEffectiveConfig({
|
|
3876
|
+
client: params.client,
|
|
3877
|
+
approvalsReviewer: params.appServer.approvalsReviewer,
|
|
3878
|
+
cwd: params.cwd,
|
|
3879
|
+
signal: params.signal
|
|
3880
|
+
});
|
|
3881
|
+
if (params.nativeHookRelayRequired) await assertCodexNativeHookRelayAllowed(params.client, params.signal);
|
|
2856
3882
|
const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
|
|
2857
3883
|
...params.timing,
|
|
2858
3884
|
enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
|
|
@@ -2898,9 +3924,13 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
2898
3924
|
const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
|
|
2899
3925
|
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
|
|
2900
3926
|
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
|
|
3927
|
+
const imageGenerationDenied = params.params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") === true;
|
|
2901
3928
|
if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
|
|
2902
3929
|
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("
|
|
3930
|
+
if (restrictedToolSurface || imageGenerationDenied) await lifecycleTiming.measure("tool-policy-config-requirements-read", () => assertCodexManagedRequirementsDoNotOverrideToolPolicy(params.client, {
|
|
3931
|
+
restrictedToolSurface,
|
|
3932
|
+
additionalDeniedFeatures: imageGenerationDenied ? ["image_generation"] : void 0
|
|
3933
|
+
}, params.signal));
|
|
2904
3934
|
const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
|
|
2905
3935
|
version: 1,
|
|
2906
3936
|
baseInstructions: "",
|
|
@@ -2968,6 +3998,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
2968
3998
|
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
|
|
2969
3999
|
const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
2970
4000
|
threadId: binding.threadId,
|
|
4001
|
+
cwd: params.cwd,
|
|
2971
4002
|
authProfileId: resumeAuthProfileId,
|
|
2972
4003
|
model: startModelSelection.model,
|
|
2973
4004
|
modelProvider: startModelProvider,
|
|
@@ -2981,7 +4012,9 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
2981
4012
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2982
4013
|
webSearchAllowed: params.webSearchAllowed,
|
|
2983
4014
|
hostSystemAgentActive,
|
|
2984
|
-
restrictedToolSurfaceInheritedMcpServerNames
|
|
4015
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
4016
|
+
shellEnvironment: params.shellEnvironment,
|
|
4017
|
+
disableLoginShell: params.disableLoginShell
|
|
2985
4018
|
}));
|
|
2986
4019
|
const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
|
|
2987
4020
|
...resumeParams,
|
|
@@ -3102,7 +4135,9 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3102
4135
|
model: nativeModel,
|
|
3103
4136
|
modelProvider: nativeModelProvider,
|
|
3104
4137
|
hostSystemAgentActive: params.hostSystemAgentActive,
|
|
3105
|
-
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
|
|
4138
|
+
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
|
|
4139
|
+
shellEnvironment: params.shellEnvironment,
|
|
4140
|
+
disableLoginShell: params.disableLoginShell
|
|
3106
4141
|
});
|
|
3107
4142
|
assertExactSupervisionModelSelection(startParams, {
|
|
3108
4143
|
model: nativeModel,
|
|
@@ -3258,7 +4293,9 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
|
|
|
3258
4293
|
webSearchAllowed: params.webSearchAllowed,
|
|
3259
4294
|
appServer: params.appServer,
|
|
3260
4295
|
hostSystemAgentActive: params.hostSystemAgentActive,
|
|
3261
|
-
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
|
|
4296
|
+
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
|
|
4297
|
+
shellEnvironment: params.shellEnvironment,
|
|
4298
|
+
disableLoginShell: params.disableLoginShell
|
|
3262
4299
|
});
|
|
3263
4300
|
return {
|
|
3264
4301
|
threadId: pending.sourceThreadId,
|
|
@@ -3465,6 +4502,8 @@ async function startOrResumeThread(params) {
|
|
|
3465
4502
|
hostSystemAgentActive,
|
|
3466
4503
|
restrictedToolSurface,
|
|
3467
4504
|
restrictedToolSurfaceInheritedMcpServerNames,
|
|
4505
|
+
shellEnvironment: params.shellEnvironment,
|
|
4506
|
+
disableLoginShell: params.disableLoginShell,
|
|
3468
4507
|
environmentSelection: params.environmentSelection,
|
|
3469
4508
|
provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
|
|
3470
4509
|
signal: params.signal,
|
|
@@ -3475,6 +4514,7 @@ async function startOrResumeThread(params) {
|
|
|
3475
4514
|
cwd: params.cwd,
|
|
3476
4515
|
...clientId ? { clientId } : {},
|
|
3477
4516
|
authProfileId: void 0,
|
|
4517
|
+
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
3478
4518
|
preserveNativeModel: true,
|
|
3479
4519
|
dynamicToolsFingerprint,
|
|
3480
4520
|
dynamicToolsContainDeferred,
|
|
@@ -3541,7 +4581,7 @@ async function startOrResumeThread(params) {
|
|
|
3541
4581
|
}
|
|
3542
4582
|
const startModelSelection = resolveCodexAppServerThreadModelSelection({
|
|
3543
4583
|
provider: params.params.provider,
|
|
3544
|
-
model: params.params.modelId,
|
|
4584
|
+
model: params.runtimeModelId ?? params.params.modelId,
|
|
3545
4585
|
binding,
|
|
3546
4586
|
authProfileId: params.params.authProfileId,
|
|
3547
4587
|
authProfileStore: params.params.authProfileStore,
|
|
@@ -3855,9 +4895,10 @@ function buildTurnStartParams(params, options) {
|
|
|
3855
4895
|
input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
|
|
3856
4896
|
...additionalContext ? { additionalContext } : {},
|
|
3857
4897
|
cwd: options.cwd,
|
|
4898
|
+
...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
|
|
3858
4899
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
3859
4900
|
approvalsReviewer: options.appServer.approvalsReviewer,
|
|
3860
|
-
...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd, options.appServer.start?.args) },
|
|
4901
|
+
...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.appServer.sessionRoot ?? options.cwd, options.appServer.start?.args) },
|
|
3861
4902
|
...modelSelection ? {
|
|
3862
4903
|
model: modelSelection.model,
|
|
3863
4904
|
personality: CODEX_NATIVE_PERSONALITY_NONE
|
|
@@ -3887,7 +4928,6 @@ function buildTurnCollaborationMode(params, options = {}) {
|
|
|
3887
4928
|
function buildTurnScopedCollaborationInstructions(params, options = {}) {
|
|
3888
4929
|
const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
|
|
3889
4930
|
if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
|
|
3890
|
-
if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
|
|
3891
4931
|
if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
|
|
3892
4932
|
return null;
|
|
3893
4933
|
}
|
|
@@ -3914,15 +4954,8 @@ function buildCronCollaborationInstructions() {
|
|
|
3914
4954
|
"Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration."
|
|
3915
4955
|
].join("\n\n");
|
|
3916
4956
|
}
|
|
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
4957
|
function joinPresentSections(...sections) {
|
|
3925
4958
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
3926
4959
|
}
|
|
3927
4960
|
//#endregion
|
|
3928
|
-
export {
|
|
4961
|
+
export { isForcedPrivateQaCodexRuntime as $, attestCodexRestrictedToolSurfaceMcpServersDisabled as A, resolveCodexAppServerRequestModelSelection as B, toCodexDynamicToolProgressResponse as C, refreshCodexPluginRuntimeState as Ct, withDynamicToolExecutionState as D, resolveRecoverableCodexPluginConfigKeys as Dt, createFailedDynamicToolResponse as E, pluginReadParams as Et, resolveCodexWebSearchPlan as F, CODEX_MANAGED_THREAD_MAX_ENTRIES as G, resolveCodexBindingModelProviderFallback as H, buildDeveloperInstructions as I, areCodexDynamicToolFingerprintsCompatible as J, CODEX_MANAGED_THREAD_NAMESPACE as K, buildCodexProjectDocThreadConfig as L, buildCodexRuntimeThreadConfig as M, readCodexInheritedMcpServerNames as N, withDynamicToolTranscriptDetails as O, buildCodexNativeWebSearchThreadConfig as P, filterCodexDynamicToolsForDisabledNativeSurface as Q, CODEX_NATIVE_PERSONALITY_NONE as R, shouldReleaseTurnAfterTerminalDynamicTool as S, ensureCodexPluginActivation as St, resolveCodexToolAbortTerminalReason as T, isOpenAiCuratedMarketplaceName as Tt, resolveReasoningEffort as U, resolveCodexAppServerThreadModelSelection as V, readCodexSupportedReasoningEfforts as W, codexLegacyDynamicToolsFingerprint as X, codexDynamicToolsFingerprint as Y, filterCodexDynamicTools as Z, isDynamicToolTerminalDiagnosticEvent as _, buildCodexPluginThreadConfigTimeoutFallback as _t, isCodexAppServerProfilerEnabled as a, buildContextEngineBinding as at, resolveTerminalDynamicToolBatchAction as b, mergeCodexThreadConfigs as bt, buildCodexNativeHookRelayConfig as c, fitCodexProjectedContextForTurnStart as ct, emitCodexNativePreToolUseFailureDiagnostic as d, resolveCodexContextEngineProjectionMaxChars as dt, isMessageOnlyCodexSourceReply as et, resolveCodexNativeHookRelayEvents as f, resolveCodexContextEngineProjectionReserveTokens as ft, hasPendingDynamicToolTerminalDiagnostic as g, buildCodexPluginThreadConfigInputFingerprint as gt, handleDynamicToolCallWithTimeout as h, buildCodexPluginThreadConfig as ht, startOrResumeThread as i, resolveCodexDynamicToolsLoadingForRuntime as it, buildCodexRingZeroThreadConfigPatch as j, assertCodexManagedRequirementsDoNotOverrideToolPolicy as k, buildCodexNativeHookRelayDisabledConfig as l, neutralizeCodexExplicitMentionSigils as lt, scheduleCodexNativeHookRelayUnregister as m, buildCodexPluginAppsConfigPatchFromPolicyContext as mt, buildTurnStartParams as n, normalizeCodexDynamicToolName as nt, CODEX_NATIVE_HOOK_RELAY_EVENTS as o, isContextEngineBindingCompatible as ot, resolveCodexNativeHookRelayTtlMs as p, resolveCodexContinuityProjectionMaxChars as pt, createCodexManagedThreadStore as q, buildCodexUserInput as r, resolveCodexDynamicToolsLoading as rt, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as s, buildCodexContinuityCalibration as st, buildTurnCollaborationMode as t, isSystemAgentOnlyCodexDynamicToolAllowlist as tt, createCodexNativeHookRelay as u, projectContextEngineAssemblyForCodex as ut, isMatchingDynamicToolTerminalDiagnostic as v, buildDisabledAppsConfigPatch as vt, toCodexDynamicToolProtocolResponse as w, isOpenAiCuratedMarketplace as wt, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as x, shouldBuildCodexPluginThreadConfig as xt, resolveDynamicToolCallTimeoutMs as y, buildPluginAppPolicyContext as yt, resolveCodexAppServerModelProvider as z };
|