@openclaw/codex 2026.5.7 → 2026.5.9-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/{client-BGbqC7jk.js → client-DYkCXAze.js} +6 -6
- package/dist/{client-factory--cll1Fba.js → client-factory-D3-EhEwN.js} +1 -1
- package/dist/{command-handlers-DiH-D13x.js → command-handlers-De-V2-4K.js} +7 -7
- package/dist/{compact-DcR5aTxd.js → compact-BaeaEEoQ.js} +3 -3
- package/dist/config-BZiM7rhH.js +498 -0
- package/dist/{conversation-binding-CtHkMJfG.js → conversation-binding-BjanzgDh.js} +10 -8
- package/dist/harness.js +4 -4
- package/dist/index.js +406 -33
- package/dist/media-understanding-provider.js +4 -4
- package/dist/{models-CkowdYbm.js → models-nfvRggjT.js} +2 -2
- package/dist/plugin-activation-D0uXssSN.js +389 -0
- package/dist/{protocol-validators-Dky2yV4W.js → protocol-validators-CbqWfY5M.js} +147 -14
- package/dist/provider.js +2 -2
- package/dist/{rate-limit-cache-t6ebYmfS.js → rate-limit-cache-BZWGqXPP.js} +3 -17
- package/dist/{command-formatters-PiJcdUbu.js → request-CaW8DCAa.js} +16 -2
- package/dist/{run-attempt-BaT_FcTv.js → run-attempt-DCCThMaw.js} +388 -132
- package/dist/{session-binding-DuJYTJQy.js → session-binding-C_HDlZx8.js} +44 -5
- package/dist/{shared-client-Dfk3Enm-.js → shared-client-pLCOzMaZ.js} +77 -22
- package/dist/test-api.js +7 -4
- package/dist/{thread-lifecycle-CzllX4PU.js → thread-lifecycle-unEEsXe-.js} +326 -15
- package/openclaw.plugin.json +79 -3
- package/package.json +7 -8
- package/dist/config-ByrA30No.js +0 -242
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-
|
|
1
|
+
import { l as resolveCodexPluginsPolicy, o as readCodexPluginConfig, s as resolveCodexAppServerRuntimeOptions, u as withMcpElicitationsApprovalPolicy } from "./config-BZiM7rhH.js";
|
|
2
|
+
import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-CbqWfY5M.js";
|
|
3
3
|
import { t as isJsonObject } from "./protocol-C9UWI98H.js";
|
|
4
|
-
import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-
|
|
5
|
-
import {
|
|
6
|
-
import { a as refreshCodexAppServerAuthTokens, o as
|
|
7
|
-
import { i as readCodexAppServerBinding } from "./session-binding-
|
|
8
|
-
import {
|
|
9
|
-
import { n as
|
|
10
|
-
import { n as
|
|
4
|
+
import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-DYkCXAze.js";
|
|
5
|
+
import { d as formatCodexUsageLimitErrorMessage, i as formatCodexDisplayText } from "./request-CaW8DCAa.js";
|
|
6
|
+
import { a as refreshCodexAppServerAuthTokens, c as resolveCodexAppServerAuthProfileIdForAgent, l as resolveCodexAppServerEnvApiKeyCacheKey, o as resolveCodexAppServerAuthAccountCacheKey, s as resolveCodexAppServerAuthProfileId, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerHomeDir } from "./shared-client-pLCOzMaZ.js";
|
|
7
|
+
import { i as readCodexAppServerBinding } from "./session-binding-C_HDlZx8.js";
|
|
8
|
+
import { i as defaultCodexAppInventoryCache, r as buildCodexAppInventoryCacheKey } from "./plugin-activation-D0uXssSN.js";
|
|
9
|
+
import { a as buildTurnStartParams, c as buildCodexPluginThreadConfig, d as createCodexDynamicToolBridge, f as applyCodexDynamicToolProfile, l as buildCodexPluginThreadConfigInputFingerprint, n as buildDeveloperInstructions, o as codexDynamicToolsFingerprint, p as normalizeCodexDynamicToolName, s as startOrResumeThread, t as areCodexDynamicToolFingerprintsCompatible, u as shouldBuildCodexPluginThreadConfig } from "./thread-lifecycle-unEEsXe-.js";
|
|
10
|
+
import { n as defaultCodexAppServerClientFactory } from "./client-factory-D3-EhEwN.js";
|
|
11
|
+
import { n as rememberCodexRateLimits, r as ensureCodexComputerUse, t as readRecentCodexRateLimits } from "./rate-limit-cache-BZWGqXPP.js";
|
|
11
12
|
import { createHash } from "node:crypto";
|
|
12
|
-
import
|
|
13
|
+
import nodeFs from "node:fs";
|
|
14
|
+
import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatApprovalDisplayPath, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockAcquireTimeoutMs, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
13
15
|
import fs from "node:fs/promises";
|
|
14
16
|
import path from "node:path";
|
|
15
|
-
import
|
|
17
|
+
import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
|
+
import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
|
|
19
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
20
|
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
17
21
|
import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@mariozechner/pi-coding-agent";
|
|
18
22
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/text-runtime";
|
|
19
|
-
import { resolveUserPath as resolveUserPath$1 } from "openclaw/plugin-sdk/agent-harness";
|
|
20
23
|
//#region extensions/codex/src/app-server/plugin-approval-roundtrip.ts
|
|
21
24
|
const DEFAULT_CODEX_APPROVAL_TIMEOUT_MS = 12e4;
|
|
22
25
|
const MAX_PLUGIN_APPROVAL_TITLE_LENGTH = 80;
|
|
@@ -92,7 +95,7 @@ const INVISIBLE_FORMATTING_CONTROL_RE$1 = new RegExp(String.raw`[\u00ad\u034f\u0
|
|
|
92
95
|
const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE$1 = new RegExp(String.raw`(?:\u001b\][^\u001b\u009c\u0007]*|\u009d[^\u001b\u009c\u0007]*|\u001b\[[0-?]*[ -/]*|\u009b[0-?]*[ -/]*|\u001b)$`);
|
|
93
96
|
async function handleCodexAppServerApprovalRequest(params) {
|
|
94
97
|
const requestParams = isJsonObject(params.requestParams) ? params.requestParams : void 0;
|
|
95
|
-
if (!matchesCurrentTurn
|
|
98
|
+
if (!matchesCurrentTurn(requestParams, params.threadId, params.turnId)) return;
|
|
96
99
|
if (!isSupportedAppServerApprovalMethod(params.method)) return unsupportedApprovalResponse();
|
|
97
100
|
const context = buildApprovalContext({
|
|
98
101
|
method: params.method,
|
|
@@ -176,7 +179,7 @@ function buildApprovalResponse(method, requestParams, outcome) {
|
|
|
176
179
|
}
|
|
177
180
|
return unsupportedApprovalResponse();
|
|
178
181
|
}
|
|
179
|
-
function matchesCurrentTurn
|
|
182
|
+
function matchesCurrentTurn(requestParams, threadId, turnId) {
|
|
180
183
|
if (!requestParams) return false;
|
|
181
184
|
const requestThreadId = readString$4(requestParams, "threadId") ?? readString$4(requestParams, "conversationId");
|
|
182
185
|
const requestTurnId = readString$4(requestParams, "turnId");
|
|
@@ -636,6 +639,29 @@ const MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY = "connector_name";
|
|
|
636
639
|
const MCP_TOOL_APPROVAL_TOOL_TITLE_KEY = "tool_title";
|
|
637
640
|
const MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY = "tool_description";
|
|
638
641
|
const MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY = "tool_params_display";
|
|
642
|
+
const PLUGIN_APP_ID_META_KEYS = [
|
|
643
|
+
"app_id",
|
|
644
|
+
"appId",
|
|
645
|
+
"codex_app_id",
|
|
646
|
+
"codexAppId"
|
|
647
|
+
];
|
|
648
|
+
const PLUGIN_NAME_META_KEYS = [
|
|
649
|
+
"plugin_name",
|
|
650
|
+
"pluginName",
|
|
651
|
+
"codex_plugin_name",
|
|
652
|
+
"codexPluginName"
|
|
653
|
+
];
|
|
654
|
+
const PLUGIN_CONFIG_KEY_META_KEYS = [
|
|
655
|
+
"config_key",
|
|
656
|
+
"configKey",
|
|
657
|
+
"codex_config_key"
|
|
658
|
+
];
|
|
659
|
+
const PLUGIN_MARKETPLACE_NAME_META_KEYS = [
|
|
660
|
+
"marketplace_name",
|
|
661
|
+
"marketplaceName",
|
|
662
|
+
"codex_marketplace_name",
|
|
663
|
+
"codexMarketplaceName"
|
|
664
|
+
];
|
|
639
665
|
const MAX_DISPLAY_PARAM_ENTRIES = 8;
|
|
640
666
|
const MAX_DISPLAY_PARAM_VALUE_LENGTH = 120;
|
|
641
667
|
const MAX_DISPLAY_VALUE_ARRAY_ITEMS = 8;
|
|
@@ -649,7 +675,24 @@ const INVISIBLE_FORMATTING_CONTROL_RE = new RegExp(String.raw`[\u00ad\u034f\u061
|
|
|
649
675
|
const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE = new RegExp(String.raw`(?:\u001b\][^\u001b\u009c\u0007]*|\u009d[^\u001b\u009c\u0007]*|\u001b\[[0-?]*[ -/]*|\u009b[0-?]*[ -/]*|\u001b)$`);
|
|
650
676
|
async function handleCodexAppServerElicitationRequest(params) {
|
|
651
677
|
const requestParams = isJsonObject(params.requestParams) ? params.requestParams : void 0;
|
|
652
|
-
if (!
|
|
678
|
+
if (!requestParams) return;
|
|
679
|
+
if (!matchesCurrentThread(requestParams, params.threadId)) return;
|
|
680
|
+
if (turnIdMismatches(requestParams, params.turnId)) return;
|
|
681
|
+
const pluginResolution = resolvePluginElicitation({
|
|
682
|
+
requestParams,
|
|
683
|
+
pluginAppPolicyContext: params.pluginAppPolicyContext
|
|
684
|
+
});
|
|
685
|
+
if (pluginResolution.kind !== "not_plugin") {
|
|
686
|
+
if (pluginResolution.kind === "decline") {
|
|
687
|
+
logPluginElicitationDecline(pluginResolution.reason, requestParams);
|
|
688
|
+
return declineElicitationResponse();
|
|
689
|
+
}
|
|
690
|
+
if (!hasExactTurnId(requestParams, params.turnId)) {
|
|
691
|
+
logPluginElicitationDecline("missing_active_turn", requestParams);
|
|
692
|
+
return declineElicitationResponse();
|
|
693
|
+
}
|
|
694
|
+
return buildPluginPolicyElicitationResponse(pluginResolution.entry, requestParams);
|
|
695
|
+
}
|
|
653
696
|
const approvalPrompt = readBridgeableApprovalElicitation(requestParams);
|
|
654
697
|
if (!approvalPrompt) return;
|
|
655
698
|
const outcome = await requestPluginApprovalOutcome({
|
|
@@ -660,12 +703,113 @@ async function handleCodexAppServerElicitationRequest(params) {
|
|
|
660
703
|
});
|
|
661
704
|
return buildElicitationResponse(approvalPrompt.requestedSchema, approvalPrompt.meta, outcome);
|
|
662
705
|
}
|
|
663
|
-
function
|
|
706
|
+
function matchesCurrentThread(requestParams, threadId) {
|
|
664
707
|
if (!requestParams) return false;
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
708
|
+
return readString$3(requestParams, "threadId") === threadId;
|
|
709
|
+
}
|
|
710
|
+
function turnIdMismatches(requestParams, turnId) {
|
|
711
|
+
const rawTurnId = requestParams?.turnId;
|
|
712
|
+
return rawTurnId !== null && rawTurnId !== void 0 && rawTurnId !== turnId;
|
|
713
|
+
}
|
|
714
|
+
function hasExactTurnId(requestParams, turnId) {
|
|
715
|
+
return requestParams?.turnId === turnId;
|
|
716
|
+
}
|
|
717
|
+
function resolvePluginElicitation(params) {
|
|
718
|
+
const requestParams = params.requestParams;
|
|
719
|
+
if (!requestParams) return { kind: "not_plugin" };
|
|
720
|
+
const meta = isJsonObject(requestParams._meta) ? requestParams._meta : {};
|
|
721
|
+
const context = params.pluginAppPolicyContext;
|
|
722
|
+
const entries = context ? Object.values(context.apps) : [];
|
|
723
|
+
const appId = readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ?? readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
|
|
724
|
+
if (appId) {
|
|
725
|
+
if (!context) return {
|
|
726
|
+
kind: "decline",
|
|
727
|
+
reason: "missing_policy_context"
|
|
728
|
+
};
|
|
729
|
+
const entry = context.apps[appId];
|
|
730
|
+
return uniquePluginMatch(entry ? [entry] : [], "app_id");
|
|
731
|
+
}
|
|
732
|
+
const serverName = readString$3(requestParams, "serverName");
|
|
733
|
+
if (serverName && context) {
|
|
734
|
+
const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
|
|
735
|
+
if (matches.length > 0) return uniquePluginMatch(matches, "server_name");
|
|
736
|
+
}
|
|
737
|
+
const metadataResolution = resolvePluginStableMetadataMatch({
|
|
738
|
+
meta,
|
|
739
|
+
requestParams,
|
|
740
|
+
entries,
|
|
741
|
+
context
|
|
742
|
+
});
|
|
743
|
+
if (metadataResolution.kind !== "not_plugin") return metadataResolution;
|
|
744
|
+
if (context && hasDisplayNameOnlyPluginMatch(meta, entries)) return {
|
|
745
|
+
kind: "decline",
|
|
746
|
+
reason: "display_name_only"
|
|
747
|
+
};
|
|
748
|
+
return { kind: "not_plugin" };
|
|
749
|
+
}
|
|
750
|
+
function resolvePluginStableMetadataMatch(params) {
|
|
751
|
+
const pluginName = readFirstString(params.meta, PLUGIN_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_NAME_META_KEYS);
|
|
752
|
+
const configKey = readFirstString(params.meta, PLUGIN_CONFIG_KEY_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_CONFIG_KEY_META_KEYS);
|
|
753
|
+
const marketplaceName = readFirstString(params.meta, PLUGIN_MARKETPLACE_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_MARKETPLACE_NAME_META_KEYS);
|
|
754
|
+
if (!pluginName && !configKey) return { kind: "not_plugin" };
|
|
755
|
+
if (!params.context) return {
|
|
756
|
+
kind: "decline",
|
|
757
|
+
reason: "missing_policy_context"
|
|
758
|
+
};
|
|
759
|
+
return uniquePluginMatch(params.entries.filter((entry) => {
|
|
760
|
+
if (marketplaceName && entry.marketplaceName !== marketplaceName) return false;
|
|
761
|
+
if (pluginName && entry.pluginName !== pluginName) return false;
|
|
762
|
+
if (configKey && entry.configKey !== configKey) return false;
|
|
763
|
+
return true;
|
|
764
|
+
}), "metadata");
|
|
765
|
+
}
|
|
766
|
+
function uniquePluginMatch(matches, source) {
|
|
767
|
+
if (matches.length === 1 && matches[0]) return {
|
|
768
|
+
kind: "matched",
|
|
769
|
+
entry: matches[0]
|
|
770
|
+
};
|
|
771
|
+
return {
|
|
772
|
+
kind: "decline",
|
|
773
|
+
reason: matches.length === 0 ? `${source}_not_enabled` : `${source}_ambiguous`
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function hasDisplayNameOnlyPluginMatch(meta, entries) {
|
|
777
|
+
const connectorName = readString$3(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
|
|
778
|
+
if (!connectorName) return false;
|
|
779
|
+
const normalized = normalizePluginIdentityText(connectorName);
|
|
780
|
+
return entries.some((entry) => normalizePluginIdentityText(entry.pluginName) === normalized || normalizePluginIdentityText(entry.configKey) === normalized);
|
|
781
|
+
}
|
|
782
|
+
function normalizePluginIdentityText(value) {
|
|
783
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
784
|
+
}
|
|
785
|
+
function buildPluginPolicyElicitationResponse(entry, requestParams) {
|
|
786
|
+
if (!entry.allowDestructiveActions) {
|
|
787
|
+
logPluginElicitationDecline("destructive_actions_disabled", requestParams);
|
|
788
|
+
return declineElicitationResponse();
|
|
789
|
+
}
|
|
790
|
+
if (readString$3(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) {
|
|
791
|
+
logPluginElicitationDecline("unsupported_schema", requestParams);
|
|
792
|
+
return declineElicitationResponse();
|
|
793
|
+
}
|
|
794
|
+
const meta = isJsonObject(requestParams._meta) ? requestParams._meta : {};
|
|
795
|
+
const response = buildElicitationResponse(requestParams.requestedSchema, meta, "approved-once");
|
|
796
|
+
if (isJsonObject(response) && response.action === "accept") return response;
|
|
797
|
+
logPluginElicitationDecline("unmappable_schema", requestParams);
|
|
798
|
+
return declineElicitationResponse();
|
|
799
|
+
}
|
|
800
|
+
function declineElicitationResponse() {
|
|
801
|
+
return {
|
|
802
|
+
action: "decline",
|
|
803
|
+
content: null,
|
|
804
|
+
_meta: null
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function logPluginElicitationDecline(reason, requestParams) {
|
|
808
|
+
embeddedAgentLog.debug("codex plugin elicitation declined", {
|
|
809
|
+
reason,
|
|
810
|
+
serverName: readString$3(requestParams, "serverName"),
|
|
811
|
+
mode: readString$3(requestParams, "mode")
|
|
812
|
+
});
|
|
669
813
|
}
|
|
670
814
|
function readBridgeableApprovalElicitation(requestParams) {
|
|
671
815
|
if (!requestParams || readString$3(requestParams, "mode") !== "form" || !isJsonObject(requestParams._meta) || requestParams._meta[MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND || !isJsonObject(requestParams.requestedSchema)) return;
|
|
@@ -944,6 +1088,12 @@ function readString$3(record, key) {
|
|
|
944
1088
|
const value = record?.[key];
|
|
945
1089
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
946
1090
|
}
|
|
1091
|
+
function readFirstString(record, keys) {
|
|
1092
|
+
for (const key of keys) {
|
|
1093
|
+
const value = readString$3(record, key);
|
|
1094
|
+
if (value) return value;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
947
1097
|
//#endregion
|
|
948
1098
|
//#region extensions/codex/src/app-server/session-history.ts
|
|
949
1099
|
function isMissingFileError(error) {
|
|
@@ -1813,13 +1963,13 @@ function readNumber(record, key) {
|
|
|
1813
1963
|
const value = record[key];
|
|
1814
1964
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1815
1965
|
}
|
|
1816
|
-
function readBoolean(record, key) {
|
|
1966
|
+
function readBoolean$1(record, key) {
|
|
1817
1967
|
const value = record[key];
|
|
1818
1968
|
return typeof value === "boolean" ? value : void 0;
|
|
1819
1969
|
}
|
|
1820
1970
|
function readBooleanAlias(record, keys) {
|
|
1821
1971
|
for (const key of keys) {
|
|
1822
|
-
const value = readBoolean(record, key);
|
|
1972
|
+
const value = readBoolean$1(record, key);
|
|
1823
1973
|
if (value !== void 0) return value;
|
|
1824
1974
|
}
|
|
1825
1975
|
}
|
|
@@ -2111,62 +2261,17 @@ const JWT_VALUE_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]
|
|
|
2111
2261
|
const COOKIE_PAIR_RE = /\b([A-Za-z][A-Za-z0-9_.-]{1,64})=([A-Za-z0-9+/._~%=-]{16,})(?=;|\s|$)/gu;
|
|
2112
2262
|
const TRAJECTORY_RUNTIME_FILE_MAX_BYTES = 50 * 1024 * 1024;
|
|
2113
2263
|
const TRAJECTORY_RUNTIME_EVENT_MAX_BYTES = 256 * 1024;
|
|
2114
|
-
function resolveCodexTrajectoryAppendFlags(constants = nodeFs.constants) {
|
|
2115
|
-
const noFollow = constants.O_NOFOLLOW;
|
|
2116
|
-
return constants.O_CREAT | constants.O_APPEND | constants.O_WRONLY | (typeof noFollow === "number" ? noFollow : 0);
|
|
2117
|
-
}
|
|
2118
2264
|
function resolveCodexTrajectoryPointerFlags(constants = nodeFs.constants) {
|
|
2119
2265
|
const noFollow = constants.O_NOFOLLOW;
|
|
2120
2266
|
return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY | (typeof noFollow === "number" ? noFollow : 0);
|
|
2121
2267
|
}
|
|
2122
|
-
async function assertNoSymlinkParents(filePath) {
|
|
2123
|
-
const resolvedDir = path.resolve(path.dirname(filePath));
|
|
2124
|
-
const parsed = path.parse(resolvedDir);
|
|
2125
|
-
const relativeParts = path.relative(parsed.root, resolvedDir).split(path.sep).filter(Boolean);
|
|
2126
|
-
let current = parsed.root;
|
|
2127
|
-
for (const part of relativeParts) {
|
|
2128
|
-
current = path.join(current, part);
|
|
2129
|
-
const stat = await fs.lstat(current);
|
|
2130
|
-
if (stat.isSymbolicLink()) {
|
|
2131
|
-
if (path.dirname(current) === parsed.root) continue;
|
|
2132
|
-
throw new Error(`Refusing to write trajectory under symlinked directory: ${current}`);
|
|
2133
|
-
}
|
|
2134
|
-
if (!stat.isDirectory()) throw new Error(`Refusing to write trajectory under non-directory: ${current}`);
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
function verifyStableOpenedTrajectoryFile(params) {
|
|
2138
|
-
if (!params.postOpenStat.isFile()) throw new Error(`Refusing to write trajectory to non-file: ${params.filePath}`);
|
|
2139
|
-
if (params.postOpenStat.nlink > 1) throw new Error(`Refusing to write trajectory to hardlinked file: ${params.filePath}`);
|
|
2140
|
-
const pre = params.preOpenStat;
|
|
2141
|
-
if (pre && (pre.dev !== params.postOpenStat.dev || pre.ino !== params.postOpenStat.ino)) throw new Error(`Refusing to write trajectory after file changed: ${params.filePath}`);
|
|
2142
|
-
}
|
|
2143
2268
|
async function safeAppendTrajectoryFile(filePath, line) {
|
|
2144
|
-
await
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
preOpenStat = stat;
|
|
2151
|
-
} catch (err) {
|
|
2152
|
-
if (err.code !== "ENOENT") throw err;
|
|
2153
|
-
}
|
|
2154
|
-
const lineBytes = Buffer.byteLength(line, "utf8");
|
|
2155
|
-
if ((preOpenStat?.size ?? 0) + lineBytes > TRAJECTORY_RUNTIME_FILE_MAX_BYTES) return;
|
|
2156
|
-
const handle = await fs.open(filePath, resolveCodexTrajectoryAppendFlags(), 384);
|
|
2157
|
-
try {
|
|
2158
|
-
const stat = await handle.stat();
|
|
2159
|
-
verifyStableOpenedTrajectoryFile({
|
|
2160
|
-
preOpenStat,
|
|
2161
|
-
postOpenStat: stat,
|
|
2162
|
-
filePath
|
|
2163
|
-
});
|
|
2164
|
-
if (stat.size + lineBytes > TRAJECTORY_RUNTIME_FILE_MAX_BYTES) return;
|
|
2165
|
-
await handle.chmod(384);
|
|
2166
|
-
await handle.appendFile(line, "utf8");
|
|
2167
|
-
} finally {
|
|
2168
|
-
await handle.close();
|
|
2169
|
-
}
|
|
2269
|
+
await appendRegularFile({
|
|
2270
|
+
filePath,
|
|
2271
|
+
content: line,
|
|
2272
|
+
maxFileBytes: TRAJECTORY_RUNTIME_FILE_MAX_BYTES,
|
|
2273
|
+
rejectSymlinkParents: true
|
|
2274
|
+
});
|
|
2170
2275
|
}
|
|
2171
2276
|
function boundedTrajectoryLine(event) {
|
|
2172
2277
|
const line = JSON.stringify(event);
|
|
@@ -2289,7 +2394,7 @@ function parseTrajectoryEnabled(env) {
|
|
|
2289
2394
|
}
|
|
2290
2395
|
function resolveTrajectoryFilePath(params) {
|
|
2291
2396
|
const dirOverride = params.env.OPENCLAW_TRAJECTORY_DIR?.trim();
|
|
2292
|
-
if (dirOverride) return resolveContainedPath(resolveUserPath
|
|
2397
|
+
if (dirOverride) return resolveContainedPath(resolveUserPath(dirOverride), `${safeTrajectorySessionFileName(params.sessionId)}.jsonl`);
|
|
2293
2398
|
return params.sessionFile.endsWith(".jsonl") ? `${params.sessionFile.slice(0, -6)}.trajectory.jsonl` : `${params.sessionFile}.trajectory.jsonl`;
|
|
2294
2399
|
}
|
|
2295
2400
|
function safeTrajectorySessionFileName(sessionId) {
|
|
@@ -2529,8 +2634,11 @@ function filterToolsForVisionInputs(tools, params) {
|
|
|
2529
2634
|
//#region extensions/codex/src/app-server/run-attempt.ts
|
|
2530
2635
|
const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 3e4;
|
|
2531
2636
|
const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
|
|
2637
|
+
const CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS = 100;
|
|
2532
2638
|
const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 6e4;
|
|
2533
2639
|
const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
|
|
2640
|
+
const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
|
|
2641
|
+
const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
|
|
2534
2642
|
const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
|
|
2535
2643
|
const LOG_FIELD_MAX_LENGTH = 160;
|
|
2536
2644
|
const CODEX_NATIVE_PROJECT_DOC_BASENAMES = new Set(["agents.md"]);
|
|
@@ -2544,7 +2652,12 @@ const CODEX_BOOTSTRAP_CONTEXT_ORDER = new Map([
|
|
|
2544
2652
|
["memory.md", 60],
|
|
2545
2653
|
["heartbeat.md", 70]
|
|
2546
2654
|
]);
|
|
2547
|
-
|
|
2655
|
+
const testClientFactoryStorage = new AsyncLocalStorage();
|
|
2656
|
+
const clientFactory = defaultCodexAppServerClientFactory;
|
|
2657
|
+
let openClawCodingToolsFactoryForTests;
|
|
2658
|
+
function resolveCodexAppServerClientFactory() {
|
|
2659
|
+
return testClientFactoryStorage.getStore() ?? clientFactory;
|
|
2660
|
+
}
|
|
2548
2661
|
function emitCodexAppServerEvent(params, event) {
|
|
2549
2662
|
try {
|
|
2550
2663
|
emitAgentEvent({
|
|
@@ -2680,11 +2793,42 @@ function toCodexTextInput(text) {
|
|
|
2680
2793
|
text_elements: []
|
|
2681
2794
|
};
|
|
2682
2795
|
}
|
|
2796
|
+
function resolveCodexPluginAppCacheEndpoint(appServer) {
|
|
2797
|
+
return JSON.stringify({
|
|
2798
|
+
transport: appServer.start.transport,
|
|
2799
|
+
command: appServer.start.command,
|
|
2800
|
+
args: appServer.start.args,
|
|
2801
|
+
url: appServer.start.url ?? null,
|
|
2802
|
+
credentialFingerprint: fingerprintCodexPluginAppCacheCredentials(appServer.start)
|
|
2803
|
+
});
|
|
2804
|
+
}
|
|
2805
|
+
function fingerprintCodexPluginAppCacheCredentials(startOptions) {
|
|
2806
|
+
const authToken = startOptions.authToken ?? "";
|
|
2807
|
+
const headers = Object.entries(startOptions.headers).map(([key, value]) => [key.toLowerCase(), value]).toSorted(([left], [right]) => left.localeCompare(right));
|
|
2808
|
+
if (!authToken && headers.length === 0) return null;
|
|
2809
|
+
const hash = createHash("sha256");
|
|
2810
|
+
hash.update("openclaw:codex:plugin-app-cache-credentials:v1");
|
|
2811
|
+
hash.update("\0");
|
|
2812
|
+
hash.update(authToken);
|
|
2813
|
+
for (const [key, value] of headers) {
|
|
2814
|
+
hash.update("\0");
|
|
2815
|
+
hash.update(key);
|
|
2816
|
+
hash.update("\0");
|
|
2817
|
+
hash.update(value);
|
|
2818
|
+
}
|
|
2819
|
+
return `sha256:${hash.digest("hex")}`;
|
|
2820
|
+
}
|
|
2821
|
+
function resolveCodexPluginAppCacheCodexHome(appServer, agentDir) {
|
|
2822
|
+
const configuredCodexHome = appServer.start.env?.CODEX_HOME?.trim();
|
|
2823
|
+
if (configuredCodexHome) return configuredCodexHome;
|
|
2824
|
+
return appServer.start.transport === "stdio" ? resolveCodexAppServerHomeDir(agentDir) : void 0;
|
|
2825
|
+
}
|
|
2683
2826
|
async function runCodexAppServerAttempt(params, options = {}) {
|
|
2684
2827
|
const attemptStartedAt = Date.now();
|
|
2685
|
-
const attemptClientFactory =
|
|
2828
|
+
const attemptClientFactory = resolveCodexAppServerClientFactory();
|
|
2686
2829
|
const pluginConfig = readCodexPluginConfig(options.pluginConfig);
|
|
2687
2830
|
const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
2831
|
+
let pluginAppServer = appServer;
|
|
2688
2832
|
const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
|
|
2689
2833
|
configuredEvents: options.nativeHookRelay?.events,
|
|
2690
2834
|
appServer
|
|
@@ -2710,7 +2854,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2710
2854
|
config: params.config,
|
|
2711
2855
|
agentId: params.agentId
|
|
2712
2856
|
});
|
|
2713
|
-
const agentDir = params.agentDir ??
|
|
2857
|
+
const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId);
|
|
2714
2858
|
const startupBinding = await readCodexAppServerBinding(params.sessionFile);
|
|
2715
2859
|
const startupAuthProfileCandidate = params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId;
|
|
2716
2860
|
const startupAuthProfileId = params.authProfileStore ? resolveCodexAppServerAuthProfileId({
|
|
@@ -2727,6 +2871,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2727
2871
|
sessionKey: sandboxSessionKey,
|
|
2728
2872
|
...startupAuthProfileId ? { authProfileId: startupAuthProfileId } : {}
|
|
2729
2873
|
};
|
|
2874
|
+
const startupAuthAccountCacheKey = await resolveCodexAppServerAuthAccountCacheKey({
|
|
2875
|
+
authProfileId: startupAuthProfileId,
|
|
2876
|
+
authProfileStore: params.authProfileStore,
|
|
2877
|
+
agentDir,
|
|
2878
|
+
config: params.config
|
|
2879
|
+
});
|
|
2880
|
+
const startupEnvApiKeyCacheKey = startupAuthProfileId ? void 0 : resolveCodexAppServerEnvApiKeyCacheKey({ startOptions: appServer.start });
|
|
2730
2881
|
const activeContextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : void 0;
|
|
2731
2882
|
let yieldDetected = false;
|
|
2732
2883
|
const toolBridge = createCodexDynamicToolBridge({
|
|
@@ -2744,6 +2895,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2744
2895
|
}
|
|
2745
2896
|
}),
|
|
2746
2897
|
signal: runAbortController.signal,
|
|
2898
|
+
loading: pluginConfig.codexDynamicToolsLoading ?? "searchable",
|
|
2899
|
+
directToolNames: shouldForceMessageTool(params) ? ["message"] : [],
|
|
2747
2900
|
hookContext: {
|
|
2748
2901
|
agentId: sessionAgentId,
|
|
2749
2902
|
config: params.config,
|
|
@@ -2752,7 +2905,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2752
2905
|
runId: params.runId
|
|
2753
2906
|
}
|
|
2754
2907
|
});
|
|
2755
|
-
const hadSessionFile = await
|
|
2908
|
+
const hadSessionFile = await pathExists(params.sessionFile);
|
|
2756
2909
|
let historyMessages = await readMirroredSessionHistoryMessages(params.sessionFile) ?? [];
|
|
2757
2910
|
const hookContext = {
|
|
2758
2911
|
runId: params.runId,
|
|
@@ -2784,8 +2937,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2784
2937
|
historyMessages = await readMirroredSessionHistoryMessages(params.sessionFile) ?? historyMessages;
|
|
2785
2938
|
}
|
|
2786
2939
|
const baseDeveloperInstructions = buildDeveloperInstructions(params);
|
|
2940
|
+
const workspaceBootstrapInstructions = await buildCodexWorkspaceBootstrapInstructions({
|
|
2941
|
+
params,
|
|
2942
|
+
resolvedWorkspace,
|
|
2943
|
+
effectiveWorkspace,
|
|
2944
|
+
sessionKey: sandboxSessionKey,
|
|
2945
|
+
sessionAgentId
|
|
2946
|
+
});
|
|
2787
2947
|
let promptText = params.prompt;
|
|
2788
|
-
let developerInstructions = baseDeveloperInstructions;
|
|
2948
|
+
let developerInstructions = joinPresentSections(baseDeveloperInstructions, workspaceBootstrapInstructions);
|
|
2789
2949
|
let prePromptMessageCount = historyMessages.length;
|
|
2790
2950
|
if (activeContextEngine) try {
|
|
2791
2951
|
const assembled = await assembleHarnessContextEngine({
|
|
@@ -2807,7 +2967,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2807
2967
|
systemPromptAddition: assembled.systemPromptAddition
|
|
2808
2968
|
});
|
|
2809
2969
|
promptText = projection.promptText;
|
|
2810
|
-
developerInstructions = joinPresentSections(baseDeveloperInstructions, projection.developerInstructionAddition);
|
|
2970
|
+
developerInstructions = joinPresentSections(baseDeveloperInstructions, workspaceBootstrapInstructions, projection.developerInstructionAddition);
|
|
2811
2971
|
prePromptMessageCount = projection.prePromptMessageCount;
|
|
2812
2972
|
} catch (assembleErr) {
|
|
2813
2973
|
embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
|
|
@@ -2831,13 +2991,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2831
2991
|
messages: historyMessages,
|
|
2832
2992
|
ctx: hookContext
|
|
2833
2993
|
});
|
|
2834
|
-
const workspaceBootstrapInstructions = await buildCodexWorkspaceBootstrapInstructions({
|
|
2835
|
-
params,
|
|
2836
|
-
resolvedWorkspace,
|
|
2837
|
-
effectiveWorkspace,
|
|
2838
|
-
sessionKey: sandboxSessionKey,
|
|
2839
|
-
sessionAgentId
|
|
2840
|
-
});
|
|
2841
2994
|
const trajectoryRecorder = createCodexTrajectoryRecorder({
|
|
2842
2995
|
attempt: params,
|
|
2843
2996
|
cwd: effectiveWorkspace,
|
|
@@ -2850,6 +3003,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2850
3003
|
let trajectoryEndRecorded = false;
|
|
2851
3004
|
let nativeHookRelay;
|
|
2852
3005
|
let startupClientForCleanup;
|
|
3006
|
+
const startupTimeoutMs = resolveCodexStartupTimeoutMs({
|
|
3007
|
+
timeoutMs: params.timeoutMs,
|
|
3008
|
+
timeoutFloorMs: options.startupTimeoutFloorMs
|
|
3009
|
+
});
|
|
2853
3010
|
try {
|
|
2854
3011
|
emitCodexAppServerEvent(params, {
|
|
2855
3012
|
stream: "codex_app_server.lifecycle",
|
|
@@ -2863,16 +3020,36 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2863
3020
|
sessionKey: sandboxSessionKey,
|
|
2864
3021
|
config: params.config,
|
|
2865
3022
|
runId: params.runId,
|
|
3023
|
+
attemptTimeoutMs: params.timeoutMs,
|
|
3024
|
+
startupTimeoutMs,
|
|
3025
|
+
turnStartTimeoutMs: params.timeoutMs,
|
|
2866
3026
|
signal: runAbortController.signal
|
|
2867
3027
|
});
|
|
2868
|
-
const threadConfig =
|
|
3028
|
+
const threadConfig = nativeHookRelay ? buildCodexNativeHookRelayConfig({
|
|
2869
3029
|
relay: nativeHookRelay,
|
|
2870
3030
|
events: nativeHookRelayEvents,
|
|
2871
3031
|
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
|
|
2872
|
-
}) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0
|
|
3032
|
+
}) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0;
|
|
3033
|
+
const pluginThreadConfigEnabled = shouldBuildCodexPluginThreadConfig(pluginConfig);
|
|
3034
|
+
const pluginAppCacheKey = buildCodexAppInventoryCacheKey({
|
|
3035
|
+
codexHome: resolveCodexPluginAppCacheCodexHome(appServer, agentDir),
|
|
3036
|
+
endpoint: resolveCodexPluginAppCacheEndpoint(appServer),
|
|
3037
|
+
authProfileId: startupAuthProfileId,
|
|
3038
|
+
accountId: startupAuthAccountCacheKey,
|
|
3039
|
+
envApiKeyFingerprint: startupEnvApiKeyCacheKey
|
|
3040
|
+
});
|
|
3041
|
+
const pluginThreadConfigInputFingerprint = pluginThreadConfigEnabled ? buildCodexPluginThreadConfigInputFingerprint({
|
|
3042
|
+
pluginConfig,
|
|
3043
|
+
appCacheKey: pluginAppCacheKey
|
|
3044
|
+
}) : void 0;
|
|
3045
|
+
const resolvedPluginPolicy = pluginThreadConfigEnabled ? resolveCodexPluginsPolicy(pluginConfig) : void 0;
|
|
3046
|
+
const enabledPluginConfigKeys = resolvedPluginPolicy ? resolvedPluginPolicy.pluginPolicies.filter((plugin) => plugin.enabled).map((plugin) => plugin.configKey).toSorted() : void 0;
|
|
3047
|
+
pluginAppServer = resolvedPluginPolicy?.enabled === true ? {
|
|
3048
|
+
...appServer,
|
|
3049
|
+
approvalPolicy: withMcpElicitationsApprovalPolicy(appServer.approvalPolicy)
|
|
3050
|
+
} : appServer;
|
|
2873
3051
|
({client, thread} = await withCodexStartupTimeout({
|
|
2874
|
-
timeoutMs:
|
|
2875
|
-
timeoutFloorMs: options.startupTimeoutFloorMs,
|
|
3052
|
+
timeoutMs: startupTimeoutMs,
|
|
2876
3053
|
signal: runAbortController.signal,
|
|
2877
3054
|
operation: async () => {
|
|
2878
3055
|
let attemptedClient;
|
|
@@ -2893,9 +3070,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2893
3070
|
params: runtimeParams,
|
|
2894
3071
|
cwd: effectiveWorkspace,
|
|
2895
3072
|
dynamicTools: toolBridge.specs,
|
|
2896
|
-
appServer,
|
|
3073
|
+
appServer: pluginAppServer,
|
|
2897
3074
|
developerInstructions: promptBuild.developerInstructions,
|
|
2898
|
-
config: threadConfig
|
|
3075
|
+
config: threadConfig,
|
|
3076
|
+
pluginThreadConfig: pluginThreadConfigEnabled ? {
|
|
3077
|
+
enabled: true,
|
|
3078
|
+
inputFingerprint: pluginThreadConfigInputFingerprint,
|
|
3079
|
+
enabledPluginConfigKeys,
|
|
3080
|
+
build: () => buildCodexPluginThreadConfig({
|
|
3081
|
+
pluginConfig,
|
|
3082
|
+
request: (method, requestParams) => startupClient.request(method, requestParams, {
|
|
3083
|
+
timeoutMs: appServer.requestTimeoutMs,
|
|
3084
|
+
signal: runAbortController.signal
|
|
3085
|
+
}),
|
|
3086
|
+
appCache: defaultCodexAppInventoryCache,
|
|
3087
|
+
appCacheKey: pluginAppCacheKey
|
|
3088
|
+
})
|
|
3089
|
+
} : void 0
|
|
2899
3090
|
})
|
|
2900
3091
|
};
|
|
2901
3092
|
};
|
|
@@ -2971,14 +3162,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2971
3162
|
resolveCompletion = resolve;
|
|
2972
3163
|
});
|
|
2973
3164
|
let notificationQueue = Promise.resolve();
|
|
2974
|
-
const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs);
|
|
3165
|
+
const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
|
|
2975
3166
|
const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs);
|
|
2976
3167
|
let turnCompletionIdleTimer;
|
|
2977
3168
|
let turnCompletionIdleWatchArmed = false;
|
|
3169
|
+
let turnCompletionIdleWatchPinnedByTerminalError = false;
|
|
2978
3170
|
let turnTerminalIdleTimer;
|
|
2979
3171
|
let turnTerminalIdleWatchArmed = false;
|
|
2980
3172
|
let turnCompletionLastActivityAt = Date.now();
|
|
2981
3173
|
let turnCompletionLastActivityReason = "startup";
|
|
3174
|
+
let turnCompletionLastActivityDetails;
|
|
2982
3175
|
let activeAppServerTurnRequests = 0;
|
|
2983
3176
|
const clearTurnCompletionIdleTimer = () => {
|
|
2984
3177
|
if (turnCompletionIdleTimer) {
|
|
@@ -3008,14 +3201,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3008
3201
|
turnId,
|
|
3009
3202
|
idleMs,
|
|
3010
3203
|
timeoutMs: turnCompletionIdleTimeoutMs,
|
|
3011
|
-
lastActivityReason: turnCompletionLastActivityReason
|
|
3204
|
+
lastActivityReason: turnCompletionLastActivityReason,
|
|
3205
|
+
...turnCompletionLastActivityDetails
|
|
3012
3206
|
});
|
|
3013
3207
|
embeddedAgentLog.warn("codex app-server turn idle timed out waiting for completion", {
|
|
3014
3208
|
threadId: thread.threadId,
|
|
3015
3209
|
turnId,
|
|
3016
3210
|
idleMs,
|
|
3017
3211
|
timeoutMs: turnCompletionIdleTimeoutMs,
|
|
3018
|
-
lastActivityReason: turnCompletionLastActivityReason
|
|
3212
|
+
lastActivityReason: turnCompletionLastActivityReason,
|
|
3213
|
+
...turnCompletionLastActivityDetails
|
|
3019
3214
|
});
|
|
3020
3215
|
runAbortController.abort("turn_completion_idle_timeout");
|
|
3021
3216
|
};
|
|
@@ -3035,14 +3230,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3035
3230
|
turnId,
|
|
3036
3231
|
idleMs,
|
|
3037
3232
|
timeoutMs: turnTerminalIdleTimeoutMs,
|
|
3038
|
-
lastActivityReason: turnCompletionLastActivityReason
|
|
3233
|
+
lastActivityReason: turnCompletionLastActivityReason,
|
|
3234
|
+
...turnCompletionLastActivityDetails
|
|
3039
3235
|
});
|
|
3040
3236
|
embeddedAgentLog.warn("codex app-server turn idle timed out waiting for terminal event", {
|
|
3041
3237
|
threadId: thread.threadId,
|
|
3042
3238
|
turnId,
|
|
3043
3239
|
idleMs,
|
|
3044
3240
|
timeoutMs: turnTerminalIdleTimeoutMs,
|
|
3045
|
-
lastActivityReason: turnCompletionLastActivityReason
|
|
3241
|
+
lastActivityReason: turnCompletionLastActivityReason,
|
|
3242
|
+
...turnCompletionLastActivityDetails
|
|
3046
3243
|
});
|
|
3047
3244
|
runAbortController.abort("turn_terminal_idle_timeout");
|
|
3048
3245
|
};
|
|
@@ -3065,6 +3262,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3065
3262
|
const touchTurnCompletionActivity = (reason, options) => {
|
|
3066
3263
|
turnCompletionLastActivityAt = Date.now();
|
|
3067
3264
|
turnCompletionLastActivityReason = reason;
|
|
3265
|
+
turnCompletionLastActivityDetails = options?.details;
|
|
3068
3266
|
emitTrustedDiagnosticEvent({
|
|
3069
3267
|
type: "run.progress",
|
|
3070
3268
|
runId: params.runId,
|
|
@@ -3072,10 +3270,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3072
3270
|
sessionKey: params.sessionKey,
|
|
3073
3271
|
reason: `codex_app_server:${reason}`
|
|
3074
3272
|
});
|
|
3075
|
-
if (options?.arm)
|
|
3273
|
+
if (options?.arm) {
|
|
3274
|
+
turnCompletionIdleWatchArmed = true;
|
|
3275
|
+
turnCompletionIdleWatchPinnedByTerminalError = false;
|
|
3276
|
+
}
|
|
3076
3277
|
scheduleTurnCompletionIdleWatch();
|
|
3077
3278
|
scheduleTurnTerminalIdleWatch();
|
|
3078
3279
|
};
|
|
3280
|
+
const disarmTurnCompletionIdleWatch = () => {
|
|
3281
|
+
turnCompletionIdleWatchArmed = false;
|
|
3282
|
+
turnCompletionIdleWatchPinnedByTerminalError = false;
|
|
3283
|
+
clearTurnCompletionIdleTimer();
|
|
3284
|
+
};
|
|
3285
|
+
const armTurnCompletionIdleWatch = (options) => {
|
|
3286
|
+
turnCompletionIdleWatchArmed = true;
|
|
3287
|
+
turnCompletionIdleWatchPinnedByTerminalError = options?.pinnedByTerminalError === true;
|
|
3288
|
+
scheduleTurnCompletionIdleWatch();
|
|
3289
|
+
};
|
|
3079
3290
|
const emitLifecycleStart = () => {
|
|
3080
3291
|
emitCodexAppServerEvent(params, {
|
|
3081
3292
|
stream: "lifecycle",
|
|
@@ -3099,13 +3310,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3099
3310
|
lifecycleTerminalEmitted = true;
|
|
3100
3311
|
};
|
|
3101
3312
|
const handleNotification = async (notification) => {
|
|
3102
|
-
touchTurnCompletionActivity(`notification:${notification.method}`);
|
|
3103
3313
|
userInputBridge?.handleNotification(notification);
|
|
3104
3314
|
if (!projector || !turnId) {
|
|
3105
3315
|
pendingNotifications.push(notification);
|
|
3106
3316
|
return;
|
|
3107
3317
|
}
|
|
3108
|
-
const
|
|
3318
|
+
const isCurrentTurnNotification = isTurnNotification(notification.params, thread.threadId, turnId);
|
|
3319
|
+
if (isCurrentTurnNotification) touchTurnCompletionActivity(`notification:${notification.method}`, { details: describeNotificationActivity(notification) });
|
|
3320
|
+
if (isCurrentTurnNotification && notification.method === "error") if (isRetryableErrorNotification(notification.params)) disarmTurnCompletionIdleWatch();
|
|
3321
|
+
else armTurnCompletionIdleWatch({ pinnedByTerminalError: true });
|
|
3322
|
+
else if (turnCompletionIdleWatchArmed && !turnCompletionIdleWatchPinnedByTerminalError && notification.method !== "turn/completed" && isCurrentTurnNotification) disarmTurnCompletionIdleWatch();
|
|
3323
|
+
const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
|
|
3109
3324
|
try {
|
|
3110
3325
|
await projector.handleNotification(notification);
|
|
3111
3326
|
} catch (error) {
|
|
@@ -3136,7 +3351,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3136
3351
|
try {
|
|
3137
3352
|
if (request.method === "account/chatgptAuthTokens/refresh") return refreshCodexAppServerAuthTokens({
|
|
3138
3353
|
agentDir,
|
|
3139
|
-
authProfileId: startupAuthProfileId
|
|
3354
|
+
authProfileId: startupAuthProfileId,
|
|
3355
|
+
config: params.config
|
|
3140
3356
|
});
|
|
3141
3357
|
if (!turnId) return;
|
|
3142
3358
|
if (request.method === "mcpServer/elicitation/request") {
|
|
@@ -3146,6 +3362,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3146
3362
|
paramsForRun: params,
|
|
3147
3363
|
threadId: thread.threadId,
|
|
3148
3364
|
turnId,
|
|
3365
|
+
pluginAppPolicyContext: thread.pluginAppPolicyContext,
|
|
3149
3366
|
signal: runAbortController.signal
|
|
3150
3367
|
});
|
|
3151
3368
|
}
|
|
@@ -3265,7 +3482,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3265
3482
|
turn = assertCodexTurnStartResponse(await client.request("turn/start", buildTurnStartParams(params, {
|
|
3266
3483
|
threadId: thread.threadId,
|
|
3267
3484
|
cwd: effectiveWorkspace,
|
|
3268
|
-
appServer,
|
|
3485
|
+
appServer: pluginAppServer,
|
|
3269
3486
|
promptText: promptBuild.prompt
|
|
3270
3487
|
}), {
|
|
3271
3488
|
timeoutMs: params.timeoutMs,
|
|
@@ -3593,7 +3810,12 @@ function createCodexNativeHookRelay(params) {
|
|
|
3593
3810
|
...params.config ? { config: params.config } : {},
|
|
3594
3811
|
runId: params.runId,
|
|
3595
3812
|
allowedEvents: params.events,
|
|
3596
|
-
ttlMs:
|
|
3813
|
+
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
3814
|
+
explicitTtlMs: params.options?.ttlMs,
|
|
3815
|
+
attemptTimeoutMs: params.attemptTimeoutMs,
|
|
3816
|
+
startupTimeoutMs: params.startupTimeoutMs,
|
|
3817
|
+
turnStartTimeoutMs: params.turnStartTimeoutMs
|
|
3818
|
+
}),
|
|
3597
3819
|
signal: params.signal,
|
|
3598
3820
|
command: { timeoutMs: params.options?.gatewayTimeoutMs }
|
|
3599
3821
|
});
|
|
@@ -3602,6 +3824,11 @@ function resolveCodexNativeHookRelayEvents(params) {
|
|
|
3602
3824
|
if (params.configuredEvents?.length) return params.configuredEvents;
|
|
3603
3825
|
return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
|
|
3604
3826
|
}
|
|
3827
|
+
function resolveCodexNativeHookRelayTtlMs(params) {
|
|
3828
|
+
if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
|
|
3829
|
+
const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
|
|
3830
|
+
return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
|
|
3831
|
+
}
|
|
3605
3832
|
function buildCodexNativeHookRelayId(params) {
|
|
3606
3833
|
const hash = createHash("sha256");
|
|
3607
3834
|
hash.update("openclaw:codex:native-hook-relay:v1");
|
|
@@ -3616,13 +3843,20 @@ function interruptCodexTurnBestEffort(client, params) {
|
|
|
3616
3843
|
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
3617
3844
|
});
|
|
3618
3845
|
}
|
|
3846
|
+
function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
|
|
3847
|
+
return {
|
|
3848
|
+
sessionKey: sandboxSessionKey,
|
|
3849
|
+
runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3619
3852
|
async function buildDynamicTools(input) {
|
|
3620
3853
|
const { params } = input;
|
|
3621
3854
|
if (params.disableTools || !supportsModelTools(params.model)) return [];
|
|
3622
3855
|
const modelHasVision = params.model.input?.includes("image") ?? false;
|
|
3623
|
-
const agentDir = params.agentDir ??
|
|
3624
|
-
const
|
|
3625
|
-
const
|
|
3856
|
+
const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
|
|
3857
|
+
const createOpenClawCodingTools = openClawCodingToolsFactoryForTests ?? (await import("openclaw/plugin-sdk/agent-harness")).createOpenClawCodingTools;
|
|
3858
|
+
const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
|
|
3859
|
+
const filteredTools = filterCodexDynamicToolsForAllowlist(filterToolsForVisionInputs(applyCodexDynamicToolProfile(createOpenClawCodingTools({
|
|
3626
3860
|
agentId: input.sessionAgentId,
|
|
3627
3861
|
...buildEmbeddedAttemptToolRunContext(params),
|
|
3628
3862
|
exec: {
|
|
@@ -3644,8 +3878,7 @@ async function buildDynamicTools(input) {
|
|
|
3644
3878
|
senderE164: params.senderE164,
|
|
3645
3879
|
senderIsOwner: params.senderIsOwner,
|
|
3646
3880
|
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding,
|
|
3647
|
-
|
|
3648
|
-
runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
|
|
3881
|
+
...sessionKeys,
|
|
3649
3882
|
sessionId: params.sessionId,
|
|
3650
3883
|
runId: params.runId,
|
|
3651
3884
|
agentDir,
|
|
@@ -3670,8 +3903,9 @@ async function buildDynamicTools(input) {
|
|
|
3670
3903
|
hasRepliedRef: params.hasRepliedRef,
|
|
3671
3904
|
modelHasVision,
|
|
3672
3905
|
requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
|
|
3906
|
+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
3673
3907
|
disableMessageTool: params.disableMessageTool,
|
|
3674
|
-
forceMessageTool: params
|
|
3908
|
+
forceMessageTool: shouldForceMessageTool(params),
|
|
3675
3909
|
enableHeartbeatTool: params.trigger === "heartbeat",
|
|
3676
3910
|
forceHeartbeatTool: params.trigger === "heartbeat",
|
|
3677
3911
|
onYield: (message) => {
|
|
@@ -3688,8 +3922,7 @@ async function buildDynamicTools(input) {
|
|
|
3688
3922
|
}), input.pluginConfig), {
|
|
3689
3923
|
modelHasVision,
|
|
3690
3924
|
hasInboundImages: (params.images?.length ?? 0) > 0
|
|
3691
|
-
});
|
|
3692
|
-
const filteredTools = params.toolsAllow && params.toolsAllow.length > 0 ? visionFilteredTools.filter((tool) => params.toolsAllow?.includes(tool.name)) : visionFilteredTools;
|
|
3925
|
+
}), params.toolsAllow);
|
|
3693
3926
|
return normalizeAgentRuntimeTools({
|
|
3694
3927
|
runtimePlan: params.runtimePlan,
|
|
3695
3928
|
tools: filteredTools,
|
|
@@ -3702,6 +3935,14 @@ async function buildDynamicTools(input) {
|
|
|
3702
3935
|
model: params.model
|
|
3703
3936
|
});
|
|
3704
3937
|
}
|
|
3938
|
+
function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
|
|
3939
|
+
if (!toolsAllow || toolsAllow.length === 0) return tools;
|
|
3940
|
+
const allowSet = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)).filter(Boolean));
|
|
3941
|
+
return tools.filter((tool) => allowSet.has(normalizeCodexDynamicToolName(tool.name)));
|
|
3942
|
+
}
|
|
3943
|
+
function shouldForceMessageTool(params) {
|
|
3944
|
+
return params.sourceReplyDeliveryMode === "message_tool_only";
|
|
3945
|
+
}
|
|
3705
3946
|
function shouldProjectMirroredHistoryForCodexStart(params) {
|
|
3706
3947
|
if (!params.historyMessages.some((message) => message.role === "user")) return false;
|
|
3707
3948
|
if (!params.startupBinding?.threadId) return true;
|
|
@@ -3723,10 +3964,9 @@ async function withCodexStartupTimeout(params) {
|
|
|
3723
3964
|
}
|
|
3724
3965
|
reject(error);
|
|
3725
3966
|
};
|
|
3726
|
-
const timeoutMs = Math.max(params.timeoutFloorMs ?? 100, params.timeoutMs);
|
|
3727
3967
|
timeout = setTimeout(() => {
|
|
3728
3968
|
rejectOnce(/* @__PURE__ */ new Error("codex app-server startup timed out"));
|
|
3729
|
-
}, timeoutMs);
|
|
3969
|
+
}, params.timeoutMs);
|
|
3730
3970
|
const abortListener = () => rejectOnce(/* @__PURE__ */ new Error("codex app-server startup aborted"));
|
|
3731
3971
|
params.signal.addEventListener("abort", abortListener, { once: true });
|
|
3732
3972
|
abortCleanup = () => params.signal.removeEventListener("abort", abortListener);
|
|
@@ -3736,6 +3976,9 @@ async function withCodexStartupTimeout(params) {
|
|
|
3736
3976
|
abortCleanup?.();
|
|
3737
3977
|
}
|
|
3738
3978
|
}
|
|
3979
|
+
function resolveCodexStartupTimeoutMs(params) {
|
|
3980
|
+
return Math.max(params.timeoutFloorMs ?? CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS, params.timeoutMs);
|
|
3981
|
+
}
|
|
3739
3982
|
function resolveCodexTurnCompletionIdleTimeoutMs(value) {
|
|
3740
3983
|
if (value === void 0) return CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS;
|
|
3741
3984
|
if (!Number.isFinite(value)) return CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS;
|
|
@@ -3791,10 +4034,37 @@ function readCodexErrorPayload(error) {
|
|
|
3791
4034
|
rateLimits: nestedError.rateLimits ?? data.rateLimits
|
|
3792
4035
|
};
|
|
3793
4036
|
}
|
|
4037
|
+
function describeNotificationActivity(notification) {
|
|
4038
|
+
if (!isJsonObject(notification.params)) return { lastNotificationMethod: notification.method };
|
|
4039
|
+
if (notification.method !== "rawResponseItem/completed") return { lastNotificationMethod: notification.method };
|
|
4040
|
+
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
4041
|
+
if (!item) return { lastNotificationMethod: notification.method };
|
|
4042
|
+
return {
|
|
4043
|
+
lastNotificationMethod: notification.method,
|
|
4044
|
+
lastNotificationItemId: readString(item, "id"),
|
|
4045
|
+
lastNotificationItemType: readString(item, "type"),
|
|
4046
|
+
lastNotificationItemRole: readString(item, "role"),
|
|
4047
|
+
lastAssistantTextPreview: readRawAssistantTextPreview(item)
|
|
4048
|
+
};
|
|
4049
|
+
}
|
|
4050
|
+
function readRawAssistantTextPreview(item) {
|
|
4051
|
+
if (readString(item, "role") !== "assistant" || !Array.isArray(item.content)) return;
|
|
4052
|
+
const text = item.content.flatMap((content) => {
|
|
4053
|
+
if (!isJsonObject(content)) return [];
|
|
4054
|
+
const contentText = readString(content, "text");
|
|
4055
|
+
return contentText ? [contentText] : [];
|
|
4056
|
+
}).join("\n").trim();
|
|
4057
|
+
if (!text) return;
|
|
4058
|
+
return text.length > 240 ? `${text.slice(0, 237)}...` : text;
|
|
4059
|
+
}
|
|
3794
4060
|
function isTurnNotification(value, threadId, turnId) {
|
|
3795
4061
|
if (!isJsonObject(value)) return false;
|
|
3796
4062
|
return readString(value, "threadId") === threadId && readNotificationTurnId(value) === turnId;
|
|
3797
4063
|
}
|
|
4064
|
+
function isRetryableErrorNotification(value) {
|
|
4065
|
+
if (!isJsonObject(value)) return false;
|
|
4066
|
+
return readBoolean(value, "willRetry") === true || readBoolean(value, "will_retry") === true;
|
|
4067
|
+
}
|
|
3798
4068
|
function isTerminalTurnStatus(status) {
|
|
3799
4069
|
return status === "completed" || status === "interrupted" || status === "failed";
|
|
3800
4070
|
}
|
|
@@ -3809,6 +4079,10 @@ function readString(record, key) {
|
|
|
3809
4079
|
const value = record[key];
|
|
3810
4080
|
return typeof value === "string" ? value : void 0;
|
|
3811
4081
|
}
|
|
4082
|
+
function readBoolean(record, key) {
|
|
4083
|
+
const value = record[key];
|
|
4084
|
+
return typeof value === "boolean" ? value : void 0;
|
|
4085
|
+
}
|
|
3812
4086
|
async function readMirroredSessionHistoryMessages(sessionFile) {
|
|
3813
4087
|
const messages = await readCodexMirroredSessionHistoryMessages(sessionFile);
|
|
3814
4088
|
if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile });
|
|
@@ -3855,12 +4129,6 @@ function renderCodexWorkspaceBootstrapInstructions(contextFiles) {
|
|
|
3855
4129
|
for (const file of files) lines.push(`## ${file.path}`, "", file.content, "");
|
|
3856
4130
|
return lines.join("\n").trim();
|
|
3857
4131
|
}
|
|
3858
|
-
function mergeCodexConfigInstructions(config, instructions) {
|
|
3859
|
-
if (!instructions?.trim()) return config;
|
|
3860
|
-
const merged = { ...config };
|
|
3861
|
-
merged.instructions = joinPresentSections(typeof merged.instructions === "string" ? merged.instructions.trim() : void 0, instructions);
|
|
3862
|
-
return merged;
|
|
3863
|
-
}
|
|
3864
4132
|
function remapCodexContextFilePath(params) {
|
|
3865
4133
|
const relativePath = path.relative(params.sourceWorkspaceDir, params.file.path);
|
|
3866
4134
|
if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath) || params.sourceWorkspaceDir === params.targetWorkspaceDir) return params.file;
|
|
@@ -3900,15 +4168,6 @@ async function mirrorTranscriptBestEffort(params) {
|
|
|
3900
4168
|
embeddedAgentLog.warn("failed to mirror codex app-server transcript", { error });
|
|
3901
4169
|
}
|
|
3902
4170
|
}
|
|
3903
|
-
async function fileExists(filePath) {
|
|
3904
|
-
try {
|
|
3905
|
-
await fs.stat(filePath);
|
|
3906
|
-
return true;
|
|
3907
|
-
} catch (error) {
|
|
3908
|
-
if (error.code === "ENOENT") return false;
|
|
3909
|
-
throw error;
|
|
3910
|
-
}
|
|
3911
|
-
}
|
|
3912
4171
|
function isNonEmptyString(value) {
|
|
3913
4172
|
return typeof value === "string" && value.length > 0;
|
|
3914
4173
|
}
|
|
@@ -3925,8 +4184,5 @@ function handleApprovalRequest(params) {
|
|
|
3925
4184
|
signal: params.signal
|
|
3926
4185
|
});
|
|
3927
4186
|
}
|
|
3928
|
-
({ ...createCodexAppServerClientFactoryTestHooks((factory) => {
|
|
3929
|
-
clientFactory = factory;
|
|
3930
|
-
}) });
|
|
3931
4187
|
//#endregion
|
|
3932
4188
|
export { runCodexAppServerAttempt };
|