@openclaw/codex 2026.8.1-beta.3 → 2026.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +4 -5
- package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
- package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
- package/dist/{binding-connection-DAvZwp96.js → binding-connection-CCQFs8lf.js} +2 -2
- package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-rq5tiSXk.js} +13 -18
- package/dist/{capabilities-CH3K57v4.js → capabilities-CGm_94eL.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-D0nZ3qtd.js → command-formatters-C54Yg3eA.js} +2 -2
- package/dist/{command-handlers-DC86lgT4.js → command-handlers-BvOAHTrA.js} +89 -62
- package/dist/{compact-COqgBRny.js → compact-Hd7SAc0N.js} +8 -9
- package/dist/{config-CpNeo5AE.js → config-Cxw3Cl3k.js} +346 -286
- package/dist/doctor-contract-api.js +115 -14
- package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BFoUcRdx.js} +147 -903
- package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
- package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
- package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-D3PHFFXh.js} +3 -3
- package/dist/{harness-DoKQcF8n.js → harness-DVfUQBlQ.js} +42 -52
- package/dist/harness.js +1 -1
- package/dist/index.js +278 -295
- package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-Cbbx_5Ed.js} +4 -4
- package/dist/launch-args-sXOAg0SX.js +84 -0
- package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
- package/dist/media-understanding-provider.js +1 -1
- package/dist/model-catalog-o6Cgjoss.js +101 -0
- package/dist/{models-CZw1wClT.js → models-DgFWSuxA.js} +25 -28
- package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-CwFyoylz.js} +207 -30
- package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DXrqpw1c.js} +8 -5
- package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-D80UZiJ_.js} +1 -1
- package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-mdmScYOj.js} +30 -39
- package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-CulOABwu.js} +162 -135
- package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
- package/dist/{rate-limits-BI1dMHDG.js → rate-limits-hFtzt2Up.js} +1 -1
- package/dist/{sandbox-guard-C1qEeZ5C.js → request-CCm4LaED.js} +183 -2
- package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BwNQDH2u.js} +599 -361
- package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-DeBvleJw.js} +1 -1
- package/dist/{session-binding-BTrLf9Qm.js → session-binding-q2WSTVmC.js} +195 -122
- package/dist/{session-catalog-ezP_UcRM.js → session-catalog-C2Tae4rI.js} +229 -122
- package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
- package/dist/{session-cli-B1hKvfkI.js → session-cli-BuDjm5Ou.js} +1 -1
- package/dist/session-permission-policy-gLm40Vtq.js +160 -0
- package/dist/session-retirement-CdkI0bvb.js +90 -0
- package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-Bcy33kam.js} +2 -3
- package/dist/{settled-turn-finalizer-Dx1onIqg.js → settled-turn-finalizer-B8JJcxCi.js} +7 -6
- package/dist/{shared-client-Dw-rzii0.js → shared-client-kNkljY-R.js} +1720 -786
- package/dist/{side-question-DbXkJZzg.js → side-question-GwD73tX6.js} +69 -39
- package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-Ss-uwnC4.js} +852 -359
- package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-CeYBqg_4.js} +18 -8
- package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-DF54hagX.js} +52 -6
- package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-CkEQIna-.js} +112 -13
- package/dist/transport-stdio-C7-s9TLG.js +708 -0
- package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-BACGlOFa.js} +78 -86
- package/dist/{usage-9dKJCgcy.js → usage-C2l8Mils.js} +3 -3
- package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
- package/openclaw.plugin.json +19 -1
- package/package.json +8 -8
- package/dist/app-server-policy-bg5QPSVP.js +0 -25
- package/dist/command-rpc-xUIIonsO.js +0 -90
- package/dist/incognito-session-ET_ufDH-.js +0 -2
- package/dist/model-catalog-CmGoqVKB.js +0 -48
- package/dist/request-DSK0W6YI.js +0 -157
- package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
- package/dist/session-retirement-CE4iMiLh.js +0 -36
- package/dist/thread-resume-CdJk2Ehf.js +0 -30
- package/dist/transport-stdio-DhoGy33Z.js +0 -99
|
@@ -1,818 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { C as isCodexRemoteExecPlacementSandbox, T as readCodexPluginConfig } from "./config-Cxw3Cl3k.js";
|
|
2
|
+
import { Bt as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, Ht as isJsonObject, dt as resolveCodexToolAbortTerminalReason, et as createCodexElicitationResponse, ft as createFailedDynamicToolResponse, h as releaseLeasedSharedCodexAppServerClient, mt as withDynamicToolTranscriptDetails, pt as withDynamicToolExecutionState } from "./shared-client-kNkljY-R.js";
|
|
3
|
+
import { d as readCodexTurn } from "./protocol-validators-Bg5rpNxk.js";
|
|
4
|
+
import { D as itemStatus, I as readItem, b as auditNativeToolTerminalStatus, g as isCodexNotificationForTurn, x as auditNativeToolUnfinishedStatus, y as auditNativeToolName } from "./attempt-client-cleanup-CVPvp9dL.js";
|
|
5
|
+
import { H as normalizeCodexDynamicToolName, L as filterCodexDynamicTools, R as filterCodexDynamicToolsForDisabledNativeSurface, V as isSystemAgentOnlyCodexDynamicToolAllowlist, d as emitCodexNativePreToolUseFailureDiagnostic, x as resolveCodexWebSearchPlan, z as isForcedPrivateQaCodexRuntime } from "./thread-lifecycle-Ss-uwnC4.js";
|
|
6
|
+
import { l as resolveCodexNativeExecutionPolicy, u as resolveCodexNodeExecToolOverrides } from "./request-CCm4LaED.js";
|
|
6
7
|
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
|
|
7
|
-
import { r as formatCodexDisplayText } from "./command-formatters-
|
|
8
|
-
import { a as
|
|
9
|
-
import { a as sanitizeCodexApprovalVisibleText, c as waitForPluginApprovalDecision, i as requestPluginApproval, n as codexApprovalTimeoutText, o as stripDanglingCodexApprovalTerminalSequence, r as mapExecDecisionToOutcome, s as truncateCodexApprovalDisplayText, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-C75Yiiwc.js";
|
|
8
|
+
import { r as formatCodexDisplayText } from "./command-formatters-C54Yg3eA.js";
|
|
9
|
+
import { a as sanitizeCodexApprovalVisibleText, c as waitForPluginApprovalDecision, i as requestPluginApproval, n as codexApprovalTimeoutText, o as stripDanglingCodexApprovalTerminalSequence, r as mapExecDecisionToOutcome, s as truncateCodexApprovalDisplayText, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-D80UZiJ_.js";
|
|
10
10
|
import { asNonArrayRecord, asOptionalRecord, isRecord, normalizeOptionalString, normalizeTrimmedStringList, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
11
|
import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
|
|
12
12
|
import path, { posix } from "node:path";
|
|
13
13
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
14
|
-
import {
|
|
14
|
+
import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
|
|
15
15
|
import { HEARTBEAT_RESPONSE_TOOL_NAME, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatToolExecutionErrorMessage, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, getPluginToolSideEffectOwnerKey, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isHostScopedAgentToolActive, isMessagingTool, isMessagingToolSendAction, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, projectRuntimeToolInputSchema, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, resolveToolResultFailureKind, runAgentHarnessAfterToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
16
16
|
import { createHash, randomUUID } from "node:crypto";
|
|
17
17
|
import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
|
-
import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
|
|
19
18
|
import { formatErrorMessage as formatErrorMessage$1, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
19
|
+
import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
|
|
20
20
|
import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
21
21
|
import { spawn } from "node:child_process";
|
|
22
|
+
import { once } from "node:events";
|
|
22
23
|
import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
23
24
|
import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, estimateToolResultTextChars, resolveLiveToolResultMaxChars, sliceToolResultTextToBudget, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
24
|
-
import {
|
|
25
|
+
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
25
26
|
import { isIP } from "node:net";
|
|
26
27
|
import { WebSocketServer } from "ws";
|
|
27
28
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
28
29
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
29
30
|
import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
30
31
|
import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
31
|
-
import { runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
32
|
-
import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
33
32
|
import { buildRemoteCommand, isToolAllowed, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
|
|
33
|
+
import { pinExecToolTarget, resolveCodexScheduledToolProjectionFactory, runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
34
34
|
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
|
|
35
|
-
import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
|
|
36
35
|
import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
|
|
37
36
|
import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
|
|
38
|
-
//#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
|
|
39
|
-
/** Retains the host-owned app preview without adding it to Codex's response payload. */
|
|
40
|
-
function withDynamicToolTranscriptDetails(response, details) {
|
|
41
|
-
if (details === void 0) return response;
|
|
42
|
-
Object.defineProperty(response, "transcriptDetails", {
|
|
43
|
-
configurable: true,
|
|
44
|
-
enumerable: false,
|
|
45
|
-
value: details
|
|
46
|
-
});
|
|
47
|
-
return response;
|
|
48
|
-
}
|
|
49
|
-
function withDynamicToolTerminalResolution(response, terminalResolution) {
|
|
50
|
-
if (terminalResolution) {
|
|
51
|
-
Object.defineProperties(response, {
|
|
52
|
-
terminalResolution: {
|
|
53
|
-
configurable: true,
|
|
54
|
-
enumerable: false,
|
|
55
|
-
value: terminalResolution
|
|
56
|
-
},
|
|
57
|
-
executionStarted: {
|
|
58
|
-
configurable: true,
|
|
59
|
-
enumerable: false,
|
|
60
|
-
value: terminalResolution.executionStarted
|
|
61
|
-
},
|
|
62
|
-
...terminalResolution.executedArguments ? { executedArguments: {
|
|
63
|
-
configurable: true,
|
|
64
|
-
enumerable: false,
|
|
65
|
-
value: terminalResolution.executedArguments
|
|
66
|
-
} } : {}
|
|
67
|
-
});
|
|
68
|
-
withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
|
|
69
|
-
}
|
|
70
|
-
return response;
|
|
71
|
-
}
|
|
72
|
-
function withDynamicToolExecutionState(response, state) {
|
|
73
|
-
Object.defineProperties(response, {
|
|
74
|
-
executedArguments: {
|
|
75
|
-
configurable: true,
|
|
76
|
-
enumerable: false,
|
|
77
|
-
value: state.executedArguments
|
|
78
|
-
},
|
|
79
|
-
executionStarted: {
|
|
80
|
-
configurable: true,
|
|
81
|
-
enumerable: false,
|
|
82
|
-
value: state.executionStarted
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
|
|
86
|
-
}
|
|
87
|
-
function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
|
|
88
|
-
if (!sideEffectEvidence) {
|
|
89
|
-
delete response.sideEffectEvidence;
|
|
90
|
-
return response;
|
|
91
|
-
}
|
|
92
|
-
Object.defineProperty(response, "sideEffectEvidence", {
|
|
93
|
-
configurable: true,
|
|
94
|
-
enumerable: false,
|
|
95
|
-
value: true
|
|
96
|
-
});
|
|
97
|
-
return response;
|
|
98
|
-
}
|
|
99
|
-
function createFailedDynamicToolResponse(message, options) {
|
|
100
|
-
const response = {
|
|
101
|
-
contentItems: [{
|
|
102
|
-
type: "inputText",
|
|
103
|
-
text: message
|
|
104
|
-
}],
|
|
105
|
-
success: false
|
|
106
|
-
};
|
|
107
|
-
Object.defineProperties(response, {
|
|
108
|
-
diagnosticTerminalReason: {
|
|
109
|
-
configurable: true,
|
|
110
|
-
enumerable: false,
|
|
111
|
-
value: options?.terminalReason ?? "failed"
|
|
112
|
-
},
|
|
113
|
-
diagnosticTerminalType: {
|
|
114
|
-
configurable: true,
|
|
115
|
-
enumerable: false,
|
|
116
|
-
value: "error"
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
|
|
120
|
-
configurable: true,
|
|
121
|
-
enumerable: false,
|
|
122
|
-
value: options.executionStarted
|
|
123
|
-
});
|
|
124
|
-
if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
|
|
125
|
-
configurable: true,
|
|
126
|
-
enumerable: false,
|
|
127
|
-
value: options.executedArguments
|
|
128
|
-
});
|
|
129
|
-
return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
|
|
130
|
-
}
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
|
|
133
|
-
/** Leaf helper shared by native and dynamic tool diagnostics. */
|
|
134
|
-
const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
|
|
135
|
-
"codex_startup_timeout",
|
|
136
|
-
"turn_completion_idle_timeout",
|
|
137
|
-
"turn_progress_idle_timeout",
|
|
138
|
-
"turn_terminal_idle_timeout"
|
|
139
|
-
]);
|
|
140
|
-
/** Preserves timeout provenance when an enclosing run aborts an active tool. */
|
|
141
|
-
function resolveCodexToolAbortTerminalReason(signal) {
|
|
142
|
-
try {
|
|
143
|
-
const reason = signal.reason;
|
|
144
|
-
if (typeof reason === "string") {
|
|
145
|
-
if (CODEX_TIMEOUT_ABORT_REASONS.has(reason)) return "timed_out";
|
|
146
|
-
return reason === "client_closed" ? "failed" : "cancelled";
|
|
147
|
-
}
|
|
148
|
-
if (reason && typeof reason === "object") {
|
|
149
|
-
const record = reason;
|
|
150
|
-
if (record.name === "TimeoutError" || record.reason === "timeout") return "timed_out";
|
|
151
|
-
}
|
|
152
|
-
} catch {
|
|
153
|
-
return "cancelled";
|
|
154
|
-
}
|
|
155
|
-
return "cancelled";
|
|
156
|
-
}
|
|
157
|
-
//#endregion
|
|
158
|
-
//#region extensions/codex/src/app-server/dynamic-tool-execution.ts
|
|
159
|
-
/**
|
|
160
|
-
* Timeout, terminal-release, and diagnostic helpers for Codex dynamic tool
|
|
161
|
-
* calls.
|
|
162
|
-
*/
|
|
163
|
-
/** Default timeout for Codex dynamic tool calls. */
|
|
164
|
-
const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 9e4;
|
|
165
|
-
/** Hard cap for per-call Codex dynamic tool timeout overrides. */
|
|
166
|
-
const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
|
|
167
|
-
const CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS = 3e4;
|
|
168
|
-
const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
|
|
169
|
-
const CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS = 3e4;
|
|
170
|
-
const CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS = 3e4;
|
|
171
|
-
/** Timeout for image-understanding style dynamic tool calls. */
|
|
172
|
-
const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
|
|
173
|
-
/** Timeout for message-delivery dynamic tool calls. */
|
|
174
|
-
const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 6e5;
|
|
175
|
-
/** Outer default for collector waits: full swarm budget plus completion grace. */
|
|
176
|
-
const CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS = 63e4;
|
|
177
|
-
const LOG_FIELD_MAX_LENGTH = 160;
|
|
178
|
-
function normalizeLogField(value) {
|
|
179
|
-
if (typeof value !== "string") return;
|
|
180
|
-
const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
|
|
181
|
-
if (!normalized) return;
|
|
182
|
-
return normalized.length > LOG_FIELD_MAX_LENGTH ? `${truncateUtf16Safe(normalized, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
|
|
183
|
-
}
|
|
184
|
-
function readNumericTimeoutMs(value) {
|
|
185
|
-
if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
|
|
186
|
-
if (typeof value === "string") {
|
|
187
|
-
const parsed = parseStrictNonNegativeInteger(value);
|
|
188
|
-
if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
function formatDynamicToolTimeoutDetails(params) {
|
|
192
|
-
const tool = normalizeLogField(params.call.tool) ?? "unknown";
|
|
193
|
-
const baseMeta = {
|
|
194
|
-
tool: params.call.tool,
|
|
195
|
-
toolCallId: params.call.callId,
|
|
196
|
-
threadId: params.call.threadId,
|
|
197
|
-
turnId: params.call.turnId,
|
|
198
|
-
timeoutMs: params.timeoutMs,
|
|
199
|
-
timeoutKind: "codex_dynamic_tool_rpc"
|
|
200
|
-
};
|
|
201
|
-
if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
|
|
202
|
-
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
|
|
203
|
-
consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
|
|
204
|
-
meta: baseMeta
|
|
205
|
-
};
|
|
206
|
-
const action = normalizeLogField(params.call.arguments.action);
|
|
207
|
-
const sessionId = normalizeLogField(params.call.arguments.sessionId);
|
|
208
|
-
const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
|
|
209
|
-
const actionPart = action ? ` action=${action}` : "";
|
|
210
|
-
const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
|
|
211
|
-
const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
|
|
212
|
-
const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
|
|
213
|
-
const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
|
|
214
|
-
return {
|
|
215
|
-
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
|
|
216
|
-
consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
|
|
217
|
-
meta: {
|
|
218
|
-
...baseMeta,
|
|
219
|
-
processAction: action,
|
|
220
|
-
processSessionId: sessionId,
|
|
221
|
-
processRequestedTimeoutMs: requestedTimeoutMs
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Runs a dynamic tool call with run-abort and per-call timeout handling,
|
|
227
|
-
* returning a Codex protocol response instead of throwing.
|
|
228
|
-
*/
|
|
229
|
-
async function handleDynamicToolCallWithTimeout(params) {
|
|
230
|
-
let didNotifyAgentToolResult = false;
|
|
231
|
-
const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
|
|
232
|
-
const finalizeTerminal = (response) => {
|
|
233
|
-
const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
|
|
234
|
-
const ownerKey = params.toolBridge.sideEffectOwnerKeyForTool?.(params.call.tool);
|
|
235
|
-
const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
|
|
236
|
-
const terminalResolution = params.observeToolTerminal?.({
|
|
237
|
-
toolCallId: params.call.callId,
|
|
238
|
-
toolName: params.call.tool,
|
|
239
|
-
arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
|
|
240
|
-
...params.toolMeta ? { meta: params.toolMeta } : {},
|
|
241
|
-
...ownerKey ? { ownerMutation: { ownerKey } } : {},
|
|
242
|
-
...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
|
|
243
|
-
outcome: response.success ? "success" : "failure",
|
|
244
|
-
...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
|
|
245
|
-
});
|
|
246
|
-
return withDynamicToolTerminalResolution(response, terminalResolution);
|
|
247
|
-
};
|
|
248
|
-
const createFailedAfterPossibleDispatch = (message, terminalReason) => {
|
|
249
|
-
const response = createFailedDynamicToolResponse(message, {
|
|
250
|
-
executionStarted: true,
|
|
251
|
-
sideEffectEvidence: true,
|
|
252
|
-
terminalReason
|
|
253
|
-
});
|
|
254
|
-
conservativeRaceResponses.add(response);
|
|
255
|
-
return response;
|
|
256
|
-
};
|
|
257
|
-
const notifyAgentToolResult = (event) => {
|
|
258
|
-
if (didNotifyAgentToolResult) return;
|
|
259
|
-
didNotifyAgentToolResult = true;
|
|
260
|
-
try {
|
|
261
|
-
params.onAgentToolResult?.(event);
|
|
262
|
-
} catch (error) {
|
|
263
|
-
embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
const notifyFailedToolResult = (message, terminalReason = "failed") => {
|
|
267
|
-
notifyAgentToolResult({
|
|
268
|
-
toolName: params.call.tool,
|
|
269
|
-
result: {
|
|
270
|
-
content: [{
|
|
271
|
-
type: "text",
|
|
272
|
-
text: message
|
|
273
|
-
}],
|
|
274
|
-
details: {
|
|
275
|
-
status: terminalReason,
|
|
276
|
-
error: message
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
isError: true
|
|
280
|
-
});
|
|
281
|
-
};
|
|
282
|
-
if (params.signal.aborted) {
|
|
283
|
-
const message = "OpenClaw dynamic tool call aborted before execution.";
|
|
284
|
-
const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
|
|
285
|
-
params.onFallbackSelected?.();
|
|
286
|
-
notifyFailedToolResult(message, terminalReason);
|
|
287
|
-
return finalizeTerminal(createFailedDynamicToolResponse(message, {
|
|
288
|
-
executionStarted: false,
|
|
289
|
-
terminalReason
|
|
290
|
-
}));
|
|
291
|
-
}
|
|
292
|
-
const controller = new AbortController();
|
|
293
|
-
let timeout;
|
|
294
|
-
let timedOut = false;
|
|
295
|
-
let resolveAbort;
|
|
296
|
-
const abortFromRun = () => {
|
|
297
|
-
const message = "OpenClaw dynamic tool call aborted.";
|
|
298
|
-
const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
|
|
299
|
-
params.onFallbackSelected?.();
|
|
300
|
-
controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
|
|
301
|
-
notifyFailedToolResult(message, terminalReason);
|
|
302
|
-
resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
|
|
303
|
-
};
|
|
304
|
-
const abortPromise = new Promise((resolve) => {
|
|
305
|
-
resolveAbort = resolve;
|
|
306
|
-
});
|
|
307
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
308
|
-
const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
|
|
309
|
-
timeout = setTimeout(() => {
|
|
310
|
-
timedOut = true;
|
|
311
|
-
const timeoutDetails = formatDynamicToolTimeoutDetails({
|
|
312
|
-
call: params.call,
|
|
313
|
-
timeoutMs
|
|
314
|
-
});
|
|
315
|
-
params.onFallbackSelected?.();
|
|
316
|
-
controller.abort(new Error(timeoutDetails.responseMessage));
|
|
317
|
-
params.onTimeout?.();
|
|
318
|
-
embeddedAgentLog.warn("codex dynamic tool call timed out", {
|
|
319
|
-
...timeoutDetails.meta,
|
|
320
|
-
consoleMessage: timeoutDetails.consoleMessage
|
|
321
|
-
});
|
|
322
|
-
notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
|
|
323
|
-
resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
|
|
324
|
-
}, timeoutMs);
|
|
325
|
-
timeout.unref?.();
|
|
326
|
-
});
|
|
327
|
-
try {
|
|
328
|
-
params.signal.addEventListener("abort", abortFromRun, { once: true });
|
|
329
|
-
if (params.signal.aborted) abortFromRun();
|
|
330
|
-
const response = await Promise.race([
|
|
331
|
-
params.toolBridge.handleToolCall(params.call, {
|
|
332
|
-
signal: controller.signal,
|
|
333
|
-
onAgentToolResult: notifyAgentToolResult,
|
|
334
|
-
toolCallOrdinal: params.toolCallOrdinal,
|
|
335
|
-
retainExecutionSnapshot: true
|
|
336
|
-
}),
|
|
337
|
-
abortPromise,
|
|
338
|
-
timeoutPromise
|
|
339
|
-
]);
|
|
340
|
-
if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
|
|
341
|
-
return finalizeTerminal(response);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
|
|
344
|
-
const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
|
|
345
|
-
notifyFailedToolResult(message, terminalReason);
|
|
346
|
-
return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
|
|
347
|
-
} finally {
|
|
348
|
-
if (timeout) clearTimeout(timeout);
|
|
349
|
-
params.signal.removeEventListener("abort", abortFromRun);
|
|
350
|
-
resolveAbort = void 0;
|
|
351
|
-
if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
function readDynamicToolResponseText(response) {
|
|
355
|
-
return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
|
|
356
|
-
}
|
|
357
|
-
/** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
|
|
358
|
-
function toCodexDynamicToolProtocolResponse(response) {
|
|
359
|
-
return {
|
|
360
|
-
contentItems: response.contentItems,
|
|
361
|
-
success: response.success
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
/** Adds async-started progress details when a tool result continues out of band. */
|
|
365
|
-
function toCodexDynamicToolProgressResponse(response, protocolResponse) {
|
|
366
|
-
const transcriptDetails = isJsonObject(response.transcriptDetails) ? response.transcriptDetails : void 0;
|
|
367
|
-
const mcpAppPreview = isJsonObject(transcriptDetails?.mcpAppPreview) ? transcriptDetails.mcpAppPreview : void 0;
|
|
368
|
-
const progressDetails = mcpAppPreview ? { mcpAppPreview } : void 0;
|
|
369
|
-
if (response.asyncStarted !== true && progressDetails === void 0) return protocolResponse;
|
|
370
|
-
return {
|
|
371
|
-
...protocolResponse,
|
|
372
|
-
...progressDetails ? { details: progressDetails } : {},
|
|
373
|
-
...response.asyncStarted === true ? { details: {
|
|
374
|
-
...progressDetails,
|
|
375
|
-
async: true,
|
|
376
|
-
status: "started"
|
|
377
|
-
} } : {}
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
/** Decides whether a terminal dynamic tool response can release the Codex turn. */
|
|
381
|
-
function shouldReleaseTurnAfterTerminalDynamicTool(state) {
|
|
382
|
-
return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
|
|
383
|
-
}
|
|
384
|
-
/** Returns true when a non-async result should block terminal-release shortcuts. */
|
|
385
|
-
function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
|
|
386
|
-
return response.asyncStarted !== true;
|
|
387
|
-
}
|
|
388
|
-
/** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
|
|
389
|
-
function resolveTerminalDynamicToolBatchAction(state) {
|
|
390
|
-
if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
|
|
391
|
-
if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
|
|
392
|
-
if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
|
|
393
|
-
return "idle";
|
|
394
|
-
}
|
|
395
|
-
/** Returns true for diagnostic events that terminate a dynamic tool call. */
|
|
396
|
-
function isDynamicToolTerminalDiagnosticEvent(event) {
|
|
397
|
-
return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
|
|
398
|
-
}
|
|
399
|
-
/** Matches terminal diagnostics to a specific dynamic tool call id/name. */
|
|
400
|
-
function isMatchingDynamicToolTerminalDiagnostic(params) {
|
|
401
|
-
if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
|
|
402
|
-
if (params.runId !== void 0) return params.event.runId === params.runId;
|
|
403
|
-
if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
|
|
404
|
-
if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
|
|
405
|
-
return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
|
|
406
|
-
}
|
|
407
|
-
/** Checks pending diagnostics for a terminal event matching a tool call. */
|
|
408
|
-
function hasPendingDynamicToolTerminalDiagnostic(params) {
|
|
409
|
-
return hasPendingInternalDiagnosticEvent((event) => {
|
|
410
|
-
if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
|
|
411
|
-
return isMatchingDynamicToolTerminalDiagnostic({
|
|
412
|
-
event,
|
|
413
|
-
call: params.call,
|
|
414
|
-
runId: params.runId,
|
|
415
|
-
sessionId: params.sessionId,
|
|
416
|
-
sessionKey: params.sessionKey
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
/** Resolves per-tool timeout, applying media/message defaults and hard caps. */
|
|
421
|
-
function resolveDynamicToolCallTimeoutMs(params) {
|
|
422
|
-
if (params.call.tool === "computer") return clampDynamicToolTimeoutMs(readComputerToolTimeoutMs(params.call.arguments));
|
|
423
|
-
if (params.call.tool === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
|
|
424
|
-
if (params.call.tool === "agents_wait") {
|
|
425
|
-
const requestedMs = readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS;
|
|
426
|
-
return Math.max(1, Math.min(63e4, Math.floor(requestedMs)));
|
|
427
|
-
}
|
|
428
|
-
return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_TOOL_TIMEOUT_MS);
|
|
429
|
-
}
|
|
430
|
-
function readComputerToolTimeoutMs(value) {
|
|
431
|
-
const args = isJsonObject(value) ? value : void 0;
|
|
432
|
-
const action = typeof args?.action === "string" ? args.action : void 0;
|
|
433
|
-
const gatewayTimeoutMs = readPositiveFiniteTimeoutMs(args?.timeoutMs) ?? CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS;
|
|
434
|
-
const gatewayCallCount = action === "screenshot" || action === "wait" ? 3 : 4;
|
|
435
|
-
return (action === "wait" || action === "hold_key" ? Math.max(0, Number(args?.duration) || 0) * 1e3 : 0) + gatewayCallCount * gatewayTimeoutMs + CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS;
|
|
436
|
-
}
|
|
437
|
-
function readDynamicToolCallTimeoutMs(value) {
|
|
438
|
-
if (!isJsonObject(value)) return;
|
|
439
|
-
const timeoutMs = readPositiveFiniteTimeoutMs(value.timeoutMs);
|
|
440
|
-
if (timeoutMs !== void 0) return timeoutMs;
|
|
441
|
-
const timeoutSecondsMs = readDynamicToolTimeoutSecondsAsMs(value.timeoutSeconds);
|
|
442
|
-
return timeoutSecondsMs === void 0 ? void 0 : addTimerTimeoutGraceMs(timeoutSecondsMs, CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS);
|
|
443
|
-
}
|
|
444
|
-
function readConfiguredDynamicToolTimeoutMs(toolName, config) {
|
|
445
|
-
if (toolName === "image_generate") {
|
|
446
|
-
const imageModel = config?.agents?.defaults?.mediaModels?.image;
|
|
447
|
-
if (!imageModel || typeof imageModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
448
|
-
return readPositiveFiniteTimeoutMs(imageModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
449
|
-
}
|
|
450
|
-
if (toolName === "view_image") {
|
|
451
|
-
const candidates = (config?.tools?.media?.models ?? []).filter((entry) => !entry.capabilities || entry.capabilities.includes("image"));
|
|
452
|
-
const capabilityTimeoutMs = readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds);
|
|
453
|
-
return Math.max(capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS, ...candidates.map((entry) => readTimeoutSecondsAsMs(entry.timeoutSeconds) ?? capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS));
|
|
454
|
-
}
|
|
455
|
-
if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
|
|
456
|
-
}
|
|
457
|
-
function readTimeoutSecondsAsMs(value) {
|
|
458
|
-
const seconds = readPositiveFiniteTimeoutMs(value);
|
|
459
|
-
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
460
|
-
}
|
|
461
|
-
function readDynamicToolTimeoutSecondsAsMs(value) {
|
|
462
|
-
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return;
|
|
463
|
-
return value * 1e3;
|
|
464
|
-
}
|
|
465
|
-
function readPositiveFiniteTimeoutMs(value) {
|
|
466
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
467
|
-
}
|
|
468
|
-
function clampDynamicToolTimeoutMs(timeoutMs) {
|
|
469
|
-
return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
|
|
470
|
-
}
|
|
471
|
-
//#endregion
|
|
472
|
-
//#region extensions/codex/src/app-server/native-hook-relay-state.ts
|
|
473
|
-
const pending = /* @__PURE__ */ new Set();
|
|
474
|
-
/** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
|
|
475
|
-
const nativeHookRelayUnregisterQueue = {
|
|
476
|
-
add(entry) {
|
|
477
|
-
pending.add(entry);
|
|
478
|
-
},
|
|
479
|
-
delete(entry) {
|
|
480
|
-
return pending.delete(entry);
|
|
481
|
-
},
|
|
482
|
-
flush() {
|
|
483
|
-
while (pending.size > 0) {
|
|
484
|
-
const entry = pending.values().next().value;
|
|
485
|
-
if (!entry) return;
|
|
486
|
-
clearTimeout(entry.timeout);
|
|
487
|
-
entry.unregister();
|
|
488
|
-
}
|
|
489
|
-
},
|
|
490
|
-
clear() {
|
|
491
|
-
for (const entry of pending) clearTimeout(entry.timeout);
|
|
492
|
-
pending.clear();
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
//#endregion
|
|
496
|
-
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
497
|
-
/**
|
|
498
|
-
* Bridges Codex native hook callbacks into OpenClaw's native hook relay so
|
|
499
|
-
* app-server tool events can still run OpenClaw policy and diagnostics.
|
|
500
|
-
*/
|
|
501
|
-
/** Codex hook events that can be registered through OpenClaw's native relay. */
|
|
502
|
-
const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
|
503
|
-
"pre_tool_use",
|
|
504
|
-
"post_tool_use",
|
|
505
|
-
"permission_request",
|
|
506
|
-
"before_agent_finalize"
|
|
507
|
-
];
|
|
508
|
-
const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
|
|
509
|
-
const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
|
|
510
|
-
/** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
|
|
511
|
-
const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
|
|
512
|
-
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
|
|
513
|
-
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
|
|
514
|
-
const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
|
|
515
|
-
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
|
|
516
|
-
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
|
|
517
|
-
const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
|
|
518
|
-
const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
|
|
519
|
-
exec: [
|
|
520
|
-
"Bash",
|
|
521
|
-
"exec",
|
|
522
|
-
"exec_command"
|
|
523
|
-
],
|
|
524
|
-
apply_patch: [
|
|
525
|
-
"apply_patch",
|
|
526
|
-
"Write",
|
|
527
|
-
"Edit"
|
|
528
|
-
],
|
|
529
|
-
spawn_agent: ["spawn_agent", "Agent"]
|
|
530
|
-
};
|
|
531
|
-
/** Defers relay unregister so late native hook subprocesses can still resolve. */
|
|
532
|
-
function scheduleCodexNativeHookRelayUnregister(params) {
|
|
533
|
-
let pending;
|
|
534
|
-
const unregister = () => {
|
|
535
|
-
if (!pending) return;
|
|
536
|
-
const current = pending;
|
|
537
|
-
pending = void 0;
|
|
538
|
-
if (!nativeHookRelayUnregisterQueue.delete(current)) return;
|
|
539
|
-
params.relay.unregister();
|
|
540
|
-
};
|
|
541
|
-
const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
|
|
542
|
-
pending = {
|
|
543
|
-
timeout,
|
|
544
|
-
unregister
|
|
545
|
-
};
|
|
546
|
-
nativeHookRelayUnregisterQueue.add(pending);
|
|
547
|
-
timeout.unref();
|
|
548
|
-
}
|
|
549
|
-
/** Computes the delayed unregister window from Codex's hook timeout. */
|
|
550
|
-
function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
|
|
551
|
-
const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
|
|
552
|
-
return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
|
|
553
|
-
}
|
|
554
|
-
/** Records a native pre-tool failure that Codex does not project as a tool item. */
|
|
555
|
-
function emitCodexNativePreToolUseFailureDiagnostic(params) {
|
|
556
|
-
emitTrustedDiagnosticEvent({
|
|
557
|
-
type: "tool.execution.error",
|
|
558
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
559
|
-
sessionId: params.sessionId,
|
|
560
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
561
|
-
runId: params.runId,
|
|
562
|
-
toolName: params.failure.toolName,
|
|
563
|
-
toolCallId: params.failure.toolCallId,
|
|
564
|
-
durationMs: params.failure.durationMs,
|
|
565
|
-
errorCategory: "before_tool_call",
|
|
566
|
-
terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
|
|
567
|
-
...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
/** Registers an OpenClaw native hook relay for a Codex app-server turn. */
|
|
571
|
-
function createCodexNativeHookRelay(params) {
|
|
572
|
-
if (params.options?.enabled === false) return;
|
|
573
|
-
const directChildClaims = /* @__PURE__ */ new Map();
|
|
574
|
-
const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
|
|
575
|
-
let foregroundClosed = false;
|
|
576
|
-
let successfulYieldRetentionAuthorized = false;
|
|
577
|
-
const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
|
|
578
|
-
const rejectPendingAdmissions = (reason) => {
|
|
579
|
-
for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
|
|
580
|
-
pendingDirectChildAdmissions.clear();
|
|
581
|
-
};
|
|
582
|
-
const relay = registerRetainedNativeHookRelayForBundledRuntime({
|
|
583
|
-
provider: "codex",
|
|
584
|
-
relayId: buildCodexNativeHookRelayId({
|
|
585
|
-
agentId: params.agentId,
|
|
586
|
-
sessionId: params.sessionId,
|
|
587
|
-
sessionKey: params.sessionKey
|
|
588
|
-
}),
|
|
589
|
-
...params.generation ? { generation: params.generation } : {},
|
|
590
|
-
...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
|
|
591
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
592
|
-
sessionId: params.sessionId,
|
|
593
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
594
|
-
...params.config ? { config: params.config } : {},
|
|
595
|
-
runId: params.runId,
|
|
596
|
-
...params.channelId ? { channelId: params.channelId } : {},
|
|
597
|
-
...params.requester ? { requester: params.requester } : {},
|
|
598
|
-
...params.approvalContext ? { approvalContext: params.approvalContext } : {},
|
|
599
|
-
allowedEvents: params.events,
|
|
600
|
-
preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
|
|
601
|
-
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
602
|
-
explicitTtlMs: params.options?.ttlMs,
|
|
603
|
-
attemptTimeoutMs: params.attemptTimeoutMs,
|
|
604
|
-
startupTimeoutMs: params.startupTimeoutMs,
|
|
605
|
-
turnStartTimeoutMs: params.turnStartTimeoutMs
|
|
606
|
-
}),
|
|
607
|
-
signal: params.signal,
|
|
608
|
-
runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
|
|
609
|
-
assertActive: params.hostCapabilities.assertActive,
|
|
610
|
-
retention: {
|
|
611
|
-
readClaim: readCodexNativeChildThreadId,
|
|
612
|
-
shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
|
|
613
|
-
allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
|
|
614
|
-
awaitForegroundAdmission: (childThreadId) => {
|
|
615
|
-
if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
|
|
616
|
-
const existingClaim = directChildClaims.get(childThreadId);
|
|
617
|
-
if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
|
|
618
|
-
const existingPending = pendingDirectChildAdmissions.get(childThreadId);
|
|
619
|
-
if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
|
|
620
|
-
if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
|
|
621
|
-
const { promise, resolve, reject } = createDeferred();
|
|
622
|
-
pendingDirectChildAdmissions.set(childThreadId, {
|
|
623
|
-
promise,
|
|
624
|
-
resolve,
|
|
625
|
-
reject
|
|
626
|
-
});
|
|
627
|
-
return promise.then((claim) => assertClaim(childThreadId, claim));
|
|
628
|
-
},
|
|
629
|
-
onDispose: () => {
|
|
630
|
-
foregroundClosed = true;
|
|
631
|
-
rejectPendingAdmissions("native hook relay registration closed");
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
onPreToolUseFailure: params.onPreToolUseFailure,
|
|
635
|
-
command: {
|
|
636
|
-
nice: 10,
|
|
637
|
-
timeoutMs: params.options?.gatewayTimeoutMs
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
const unregister = () => {
|
|
641
|
-
foregroundClosed = true;
|
|
642
|
-
rejectPendingAdmissions("native hook relay foreground closed");
|
|
643
|
-
relay.unregister();
|
|
644
|
-
};
|
|
645
|
-
return {
|
|
646
|
-
...relay,
|
|
647
|
-
unregister,
|
|
648
|
-
authorizeRetentionAfterSuccessfulYield: () => {
|
|
649
|
-
successfulYieldRetentionAuthorized = true;
|
|
650
|
-
},
|
|
651
|
-
hasClaimedDirectChild: () => directChildClaims.size > 0,
|
|
652
|
-
rejectPendingDirectChild: (threadIdInput, reason) => {
|
|
653
|
-
const threadId = threadIdInput.trim();
|
|
654
|
-
const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
|
|
655
|
-
if (!pending) return;
|
|
656
|
-
pendingDirectChildAdmissions.delete(threadId);
|
|
657
|
-
pending.reject(new Error(reason));
|
|
658
|
-
},
|
|
659
|
-
claimDirectChild: (threadIdInput) => {
|
|
660
|
-
const threadId = threadIdInput.trim();
|
|
661
|
-
if (!threadId) return () => void 0;
|
|
662
|
-
if (directChildClaims.get(threadId)) return () => void 0;
|
|
663
|
-
const claim = Symbol(threadId);
|
|
664
|
-
directChildClaims.set(threadId, claim);
|
|
665
|
-
const pending = pendingDirectChildAdmissions.get(threadId);
|
|
666
|
-
pendingDirectChildAdmissions.delete(threadId);
|
|
667
|
-
pending?.resolve(claim);
|
|
668
|
-
let released = false;
|
|
669
|
-
return () => {
|
|
670
|
-
if (released) return;
|
|
671
|
-
released = true;
|
|
672
|
-
if (directChildClaims.get(threadId) !== claim) return;
|
|
673
|
-
directChildClaims.delete(threadId);
|
|
674
|
-
if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function readCodexNativeChildThreadId(rawPayload) {
|
|
680
|
-
if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
|
|
681
|
-
return rawPayload.agent_id.trim() || void 0;
|
|
682
|
-
}
|
|
683
|
-
/** Selects the native hook events Codex should install for the current approval mode. */
|
|
684
|
-
function resolveCodexNativeHookRelayEvents(params) {
|
|
685
|
-
if (params.configuredEvents?.length) return params.configuredEvents;
|
|
686
|
-
return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
|
|
687
|
-
}
|
|
688
|
-
/** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
|
|
689
|
-
function resolveCodexNativeHookRelayTtlMs(params) {
|
|
690
|
-
if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
|
|
691
|
-
const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
|
|
692
|
-
return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
|
|
693
|
-
}
|
|
694
|
-
/** Builds a stable relay id scoped to the agent and session identity. */
|
|
695
|
-
function buildCodexNativeHookRelayId(params) {
|
|
696
|
-
const hash = createHash("sha256");
|
|
697
|
-
hash.update("openclaw:codex:native-hook-relay:v1");
|
|
698
|
-
hash.update("\0");
|
|
699
|
-
hash.update(params.agentId?.trim() || "");
|
|
700
|
-
hash.update("\0");
|
|
701
|
-
hash.update(params.sessionKey?.trim() || params.sessionId);
|
|
702
|
-
return `codex-${hash.digest("hex").slice(0, 40)}`;
|
|
703
|
-
}
|
|
704
|
-
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
|
|
705
|
-
pre_tool_use: "PreToolUse",
|
|
706
|
-
post_tool_use: "PostToolUse",
|
|
707
|
-
permission_request: "PermissionRequest",
|
|
708
|
-
before_agent_finalize: "Stop"
|
|
709
|
-
};
|
|
710
|
-
const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
|
|
711
|
-
pre_tool_use: "pre_tool_use",
|
|
712
|
-
post_tool_use: "post_tool_use",
|
|
713
|
-
permission_request: "permission_request",
|
|
714
|
-
before_agent_finalize: "stop"
|
|
715
|
-
};
|
|
716
|
-
const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
|
|
717
|
-
/** Builds the Codex config overlay that installs trusted command hooks for relay events. */
|
|
718
|
-
function buildCodexNativeHookRelayConfig(params) {
|
|
719
|
-
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
|
720
|
-
const selectedEvents = new Set(events);
|
|
721
|
-
const config = { "features.hooks": true };
|
|
722
|
-
const hookState = {};
|
|
723
|
-
for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
|
|
724
|
-
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
|
725
|
-
const selected = selectedEvents.has(event);
|
|
726
|
-
const shouldRelay = params.relay.shouldRelayEvent(event);
|
|
727
|
-
if (!selected || !shouldRelay) {
|
|
728
|
-
if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
|
|
729
|
-
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 };
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
|
-
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
733
|
-
const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
|
|
734
|
-
const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
|
|
735
|
-
config[`hooks.${codexEvent}`] = [{
|
|
736
|
-
...matcher ? { matcher } : {},
|
|
737
|
-
hooks: [{
|
|
738
|
-
type: "command",
|
|
739
|
-
command,
|
|
740
|
-
timeout,
|
|
741
|
-
async: false,
|
|
742
|
-
statusMessage: "OpenClaw native hook relay"
|
|
743
|
-
}]
|
|
744
|
-
}];
|
|
745
|
-
const state = {
|
|
746
|
-
enabled: true,
|
|
747
|
-
trusted_hash: codexCommandHookTrustedHash({
|
|
748
|
-
event,
|
|
749
|
-
command,
|
|
750
|
-
matcher,
|
|
751
|
-
timeout,
|
|
752
|
-
statusMessage: "OpenClaw native hook relay"
|
|
753
|
-
})
|
|
754
|
-
};
|
|
755
|
-
for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
|
|
756
|
-
}
|
|
757
|
-
config["hooks.state"] = hookState;
|
|
758
|
-
return config;
|
|
759
|
-
}
|
|
760
|
-
/** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
|
|
761
|
-
function buildCodexNativeHookRelayDisabledConfig() {
|
|
762
|
-
return {
|
|
763
|
-
"features.hooks": false,
|
|
764
|
-
"hooks.PreToolUse": [],
|
|
765
|
-
"hooks.PostToolUse": [],
|
|
766
|
-
"hooks.PermissionRequest": [],
|
|
767
|
-
"hooks.Stop": []
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
function normalizeHookTimeoutSec(value) {
|
|
771
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
|
|
772
|
-
}
|
|
773
|
-
function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
|
|
774
|
-
const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
|
|
775
|
-
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)));
|
|
776
|
-
return Math.max(1, parentTimeoutMs - parentMarginMs);
|
|
777
|
-
}
|
|
778
|
-
function buildCodexNativeToolMatcher(toolNames) {
|
|
779
|
-
if (toolNames === void 0) return;
|
|
780
|
-
if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
|
|
781
|
-
const nativeNames = /* @__PURE__ */ new Set();
|
|
782
|
-
let hasCustomToolName = false;
|
|
783
|
-
for (const toolName of toolNames) {
|
|
784
|
-
const canonicalToolName = toolName.trim();
|
|
785
|
-
if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
|
|
786
|
-
const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
|
|
787
|
-
if (!nativeAliases) hasCustomToolName = true;
|
|
788
|
-
for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
|
|
789
|
-
}
|
|
790
|
-
const sortedNames = Array.from(nativeNames).toSorted();
|
|
791
|
-
if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
|
|
792
|
-
return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
|
|
793
|
-
}
|
|
794
|
-
function codexCommandHookTrustedHash(params) {
|
|
795
|
-
const identity = {
|
|
796
|
-
event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
|
|
797
|
-
...params.matcher ? { matcher: params.matcher } : {},
|
|
798
|
-
hooks: [{
|
|
799
|
-
async: false,
|
|
800
|
-
command: params.command,
|
|
801
|
-
statusMessage: params.statusMessage,
|
|
802
|
-
timeout: params.timeout,
|
|
803
|
-
type: "command"
|
|
804
|
-
}]
|
|
805
|
-
};
|
|
806
|
-
return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
|
|
807
|
-
}
|
|
808
|
-
function sortJsonValue(value) {
|
|
809
|
-
if (!value || typeof value !== "object") return value;
|
|
810
|
-
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
811
|
-
const sorted = {};
|
|
812
|
-
for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
|
|
813
|
-
return sorted;
|
|
814
|
-
}
|
|
815
|
-
//#endregion
|
|
816
37
|
//#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
|
|
817
38
|
/** Projects metadata-only lifecycle diagnostics for native tool items. */
|
|
818
39
|
var CodexNativeToolLifecycleProjector = class {
|
|
@@ -1326,83 +547,47 @@ const dynamicToolBuildState = {};
|
|
|
1326
547
|
const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
|
|
1327
548
|
const CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME = "gateway_exec";
|
|
1328
549
|
const CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME = "gateway_process";
|
|
1329
|
-
const CODEX_EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([
|
|
1330
|
-
"host",
|
|
1331
|
-
"security",
|
|
1332
|
-
"ask"
|
|
1333
|
-
]);
|
|
1334
|
-
const CODEX_NODE_EXEC_PARAMETER_NAMES = /* @__PURE__ */ new Set([
|
|
1335
|
-
"command",
|
|
1336
|
-
"workdir",
|
|
1337
|
-
"env",
|
|
1338
|
-
"timeoutSeconds",
|
|
1339
|
-
"node"
|
|
1340
|
-
]);
|
|
1341
550
|
const PROCESS_FOLLOWUP_TEXT = "Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.";
|
|
1342
551
|
/** Returns true when plugin config explicitly removes any named dynamic tool. */
|
|
1343
552
|
function isCodexDynamicToolExcluded(config, names) {
|
|
1344
553
|
const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
|
|
1345
554
|
return (config.codexDynamicToolsExclude ?? []).some((name) => normalizedNames.has(normalizeCodexDynamicToolName(name)));
|
|
1346
555
|
}
|
|
1347
|
-
function
|
|
1348
|
-
const pinnedNode =
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
execute: async (toolCallId, args, signal, onUpdate) => {
|
|
1360
|
-
const result = await execTool.execute(toolCallId, pinExecDynamicToolArgs(args, params.host, pinnedNode), signal, onUpdate);
|
|
1361
|
-
return {
|
|
1362
|
-
...result,
|
|
1363
|
-
content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, followupText) }) : item)
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
556
|
+
function createNodeExecAliasDynamicTool(execTool, node) {
|
|
557
|
+
const pinnedNode = node?.trim();
|
|
558
|
+
const pinnedTool = pinExecToolTarget(execTool, {
|
|
559
|
+
host: "node",
|
|
560
|
+
...pinnedNode ? { node: pinnedNode } : {}
|
|
561
|
+
});
|
|
562
|
+
const execute = async (toolCallId, args, signal, onUpdate) => {
|
|
563
|
+
const result = await pinnedTool.execute(toolCallId, args, signal, onUpdate);
|
|
564
|
+
return {
|
|
565
|
+
...result,
|
|
566
|
+
content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, "Remote-node background follow-up is unavailable. Wait for the command to complete.") }) : item)
|
|
567
|
+
};
|
|
1366
568
|
};
|
|
1367
|
-
}
|
|
1368
|
-
function createGatewayProcessAliasDynamicTool(processTool) {
|
|
1369
569
|
return {
|
|
1370
|
-
...
|
|
1371
|
-
name:
|
|
1372
|
-
description: "
|
|
570
|
+
...pinnedTool,
|
|
571
|
+
name: CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME,
|
|
572
|
+
description: pinnedNode ? "Run a shell command to completion on the OpenClaw configured remote node for this session. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Remote-node background follow-up is unavailable. Use Codex's native shell for local app-server work." : "Run a shell command to completion on an OpenClaw remote node. Select the node by name or id when multiple nodes are available. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Remote-node background follow-up is unavailable. Use Codex's native shell for local app-server work.",
|
|
573
|
+
execute
|
|
1373
574
|
};
|
|
1374
575
|
}
|
|
1375
|
-
function
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
host
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
return {
|
|
1384
|
-
...nodeArgs,
|
|
1385
|
-
host,
|
|
1386
|
-
...node ? { node } : {}
|
|
1387
|
-
};
|
|
576
|
+
function createGatewayExecProjection(createProjection, execTool, params) {
|
|
577
|
+
return createProjection(execTool, {
|
|
578
|
+
kind: "exec",
|
|
579
|
+
name: CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME,
|
|
580
|
+
description: "Run a shell command through OpenClaw on the Gateway host for OpenClaw-managed Gateway environment access, including Secret Store agent-readable environment values and protected egress sentinels. Native Codex shell remains preferred for ordinary local work. This tool always uses OpenClaw host=gateway internally and follows Gateway exec approval and allowlist policy.",
|
|
581
|
+
followupText: params.processAliasAvailable ? "Use gateway_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up." : "Background session follow-up is unavailable because gateway_process is not exposed. Rerun without background=true and set yieldMs high enough to wait for completion.",
|
|
582
|
+
...params.ask ? { ask: params.ask } : {}
|
|
583
|
+
});
|
|
1388
584
|
}
|
|
1389
|
-
function
|
|
1390
|
-
return
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
const rawProperties = schema.properties;
|
|
1396
|
-
if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters;
|
|
1397
|
-
const includeParameter = (name) => nodeOnly ? CODEX_NODE_EXEC_PARAMETER_NAMES.has(name) && !(hideNode && name === "node") : !CODEX_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(hideNode && normalizeCodexDynamicToolName(name) === "node");
|
|
1398
|
-
const nextProperties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => includeParameter(name)));
|
|
1399
|
-
const rawRequired = schema.required;
|
|
1400
|
-
const nextRequired = Array.isArray(rawRequired) ? rawRequired.filter((name) => typeof name !== "string" || includeParameter(name)) : rawRequired;
|
|
1401
|
-
return {
|
|
1402
|
-
...schema,
|
|
1403
|
-
properties: nextProperties,
|
|
1404
|
-
...Array.isArray(rawRequired) ? { required: nextRequired } : {}
|
|
1405
|
-
};
|
|
585
|
+
function createGatewayProcessProjection(createProjection, processTool) {
|
|
586
|
+
return createProjection(processTool, {
|
|
587
|
+
kind: "process",
|
|
588
|
+
name: CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
|
|
589
|
+
description: "Manage background shell sessions in the existing per-session OpenClaw process scope: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use for gateway_exec follow-up; use native Codex shell session handling for ordinary local work."
|
|
590
|
+
});
|
|
1406
591
|
}
|
|
1407
592
|
//#endregion
|
|
1408
593
|
//#region extensions/codex/src/app-server/vision-tools.ts
|
|
@@ -1430,10 +615,10 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
|
|
|
1430
615
|
"apply_patch"
|
|
1431
616
|
];
|
|
1432
617
|
const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = /* @__PURE__ */ new Set(["read", "write"]);
|
|
1433
|
-
/** Keeps
|
|
618
|
+
/** Keeps node filesystem and process ownership on its native exec-server. */
|
|
1434
619
|
function resolveCodexNodePlacementToolConstructionPlan(sandbox, nativeToolSurfaceEnabled) {
|
|
1435
620
|
if (!isCodexRemoteExecPlacementSandbox(sandbox) || sandbox?.backendId !== "node" || !("placementNodeId" in sandbox) || typeof sandbox.placementNodeId !== "string" || !sandbox.placementNodeId) return;
|
|
1436
|
-
if (!nativeToolSurfaceEnabled) throw new Error("Codex
|
|
621
|
+
if (!nativeToolSurfaceEnabled) throw new Error("Codex node execution requires its native exec-server tool surface; adjust the session tool policy and start a fresh attempt.");
|
|
1437
622
|
return {
|
|
1438
623
|
includeBaseCodingTools: false,
|
|
1439
624
|
includeShellTools: false,
|
|
@@ -1518,6 +703,10 @@ async function buildDynamicTools(input) {
|
|
|
1518
703
|
...params,
|
|
1519
704
|
disableMessageTool: false
|
|
1520
705
|
} : params;
|
|
706
|
+
const toolRunContext = buildEmbeddedAttemptToolRunContext({
|
|
707
|
+
...params,
|
|
708
|
+
forceMessageTool: shouldForceMessageTool(messagePolicyParams)
|
|
709
|
+
});
|
|
1521
710
|
if (params.disableTools) {
|
|
1522
711
|
input.onWebSearchPolicyResolved?.(false);
|
|
1523
712
|
return [];
|
|
@@ -1547,16 +736,17 @@ async function buildDynamicTools(input) {
|
|
|
1547
736
|
const toolConstructionPlan = resolveCodexNodePlacementToolConstructionPlan(input.sandbox, input.nativeToolSurfaceEnabled);
|
|
1548
737
|
const options = {
|
|
1549
738
|
agentId: input.sessionAgentId,
|
|
1550
|
-
...
|
|
739
|
+
...toolRunContext,
|
|
1551
740
|
exec: {
|
|
1552
741
|
...params.execOverrides,
|
|
742
|
+
...input.sessionPermissionPolicy ? { mode: input.sessionPermissionPolicy.execMode } : {},
|
|
1553
743
|
...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
|
|
1554
744
|
config: params.config,
|
|
1555
745
|
elevated: params.bashElevated
|
|
1556
746
|
},
|
|
1557
|
-
sessionPermissionPolicy:
|
|
1558
|
-
mode:
|
|
1559
|
-
root:
|
|
747
|
+
sessionPermissionPolicy: input.sessionPermissionPolicy ? {
|
|
748
|
+
mode: input.sessionPermissionPolicy.mode,
|
|
749
|
+
root: input.sessionPermissionPolicy.root
|
|
1560
750
|
} : void 0,
|
|
1561
751
|
sandbox: input.sandbox,
|
|
1562
752
|
...toolConstructionPlan ? { toolConstructionPlan } : {},
|
|
@@ -1595,6 +785,7 @@ async function buildDynamicTools(input) {
|
|
|
1595
785
|
resolvedWorkspace: input.resolvedWorkspace
|
|
1596
786
|
}),
|
|
1597
787
|
config: params.config,
|
|
788
|
+
githubPublicationAvailable: params.githubPublicationAvailable,
|
|
1598
789
|
authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
|
|
1599
790
|
abortSignal: input.runAbortController.signal,
|
|
1600
791
|
emitBeforeToolCallDiagnostics: false,
|
|
@@ -1616,6 +807,7 @@ async function buildDynamicTools(input) {
|
|
|
1616
807
|
hasRepliedRef: params.hasRepliedRef,
|
|
1617
808
|
modelHasVision,
|
|
1618
809
|
computerContextEpoch: input.computerContextEpoch,
|
|
810
|
+
registerRunCleanup: input.registerRunCleanup,
|
|
1619
811
|
requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
|
|
1620
812
|
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
1621
813
|
taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
|
|
@@ -1698,7 +890,7 @@ async function buildDynamicTools(input) {
|
|
|
1698
890
|
};
|
|
1699
891
|
const transientWebSearchRestriction = !webSearchPolicy.allowed && webSearchPolicy.persistentAllowed || isCodexMemoryFlushRun(params);
|
|
1700
892
|
input.onPersistentWebSearchPolicyResolved?.(webSearchPresent || persistentCodexWebSearchSurface && transientWebSearchRestriction && webSearchPolicy.persistentAllowed);
|
|
1701
|
-
const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools,
|
|
893
|
+
const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, toolRunContext.runtimeToolAllowlist);
|
|
1702
894
|
toolBuildStages.mark("allowlist-filter");
|
|
1703
895
|
const normalizedTools = normalizeAgentRuntimeTools({
|
|
1704
896
|
runtimePlan: input.ignoreRuntimePlan ? void 0 : params.runtimePlan,
|
|
@@ -1759,23 +951,15 @@ function addGatewayShellDynamicToolsIfAvailable(filteredTools, allTools, input,
|
|
|
1759
951
|
if (!execTool || execExcluded || existingNames.has("gateway_exec")) return filteredTools;
|
|
1760
952
|
const processExcluded = isCodexDynamicToolExcluded(input.pluginConfig, ["process", CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME]);
|
|
1761
953
|
const processAliasAvailable = Boolean(processTool && !processExcluded && !existingNames.has("gateway_process"));
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1764
|
-
|
|
954
|
+
const createProjection = resolveCodexScheduledToolProjectionFactory(input.params.hostCapabilities);
|
|
955
|
+
if (!createProjection) return filteredTools;
|
|
956
|
+
const toolsToAppend = [createGatewayExecProjection(createProjection, execTool, {
|
|
957
|
+
processAliasAvailable,
|
|
958
|
+
...input.sessionPermissionPolicy?.mode === "guarded" ? { ask: "always" } : {}
|
|
1765
959
|
})];
|
|
1766
|
-
if (processAliasAvailable && processTool) toolsToAppend.push(
|
|
960
|
+
if (processAliasAvailable && processTool) toolsToAppend.push(createGatewayProcessProjection(createProjection, processTool));
|
|
1767
961
|
return [...filteredTools, ...toolsToAppend];
|
|
1768
962
|
}
|
|
1769
|
-
/** Preserves delivery-critical tools when a narrow allowlist would otherwise hide them. */
|
|
1770
|
-
function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
|
|
1771
|
-
if (toolsAllow === void 0 || hasWildcardCodexToolsAllow(toolsAllow)) return toolsAllow;
|
|
1772
|
-
const forcedToolNames = shouldForceMessageTool(params) ? ["message"] : [];
|
|
1773
|
-
if (forcedToolNames.length === 0) return toolsAllow;
|
|
1774
|
-
if (toolsAllow.length === 0) return forcedToolNames;
|
|
1775
|
-
const normalized = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)));
|
|
1776
|
-
const missingToolNames = forcedToolNames.filter((toolName) => !normalized.has(normalizeCodexDynamicToolName(toolName)));
|
|
1777
|
-
return missingToolNames.length === 0 ? toolsAllow : [...toolsAllow, ...missingToolNames];
|
|
1778
|
-
}
|
|
1779
963
|
/** Decides whether Codex native code mode can own shell/file tools for this turn. */
|
|
1780
964
|
function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options = {}) {
|
|
1781
965
|
if (params.pluginHarnessToolPolicyRestricted === true) return false;
|
|
@@ -1786,7 +970,7 @@ function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options =
|
|
|
1786
970
|
runtimeSessionKey: options.runtimeSessionKey,
|
|
1787
971
|
sandbox
|
|
1788
972
|
})) return false;
|
|
1789
|
-
const toolsAllow =
|
|
973
|
+
const toolsAllow = params.toolsAllow;
|
|
1790
974
|
if (toolsAllow === void 0) return canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
|
|
1791
975
|
return hasWildcardCodexToolsAllow(toolsAllow) && canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
|
|
1792
976
|
}
|
|
@@ -1916,10 +1100,7 @@ function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePo
|
|
|
1916
1100
|
if (!nodeExecIsDefault && !nodeExecAvailableFromAuto) return filteredTools;
|
|
1917
1101
|
const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
|
|
1918
1102
|
if (!execTool) return filteredTools;
|
|
1919
|
-
if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return [...filteredTools,
|
|
1920
|
-
host: "node",
|
|
1921
|
-
node: nodePolicy.node
|
|
1922
|
-
})];
|
|
1103
|
+
if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return [...filteredTools, createNodeExecAliasDynamicTool(execTool, nodePolicy.node)];
|
|
1923
1104
|
return filteredTools;
|
|
1924
1105
|
}
|
|
1925
1106
|
function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
|
|
@@ -1986,7 +1167,7 @@ const nodeExecServerTextDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
|
1986
1167
|
/** Produces the bounded, redacted terminal failure shared by pending and claimed node leases. */
|
|
1987
1168
|
function createCodexNodeExecServerDisconnectError(reason, cause) {
|
|
1988
1169
|
const detail = cause === void 0 ? "" : `: ${truncateUtf16Safe(redactSensitiveText(formatErrorMessage$1(cause), { mode: "tools" }), CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS)}`;
|
|
1989
|
-
return /* @__PURE__ */ new Error(`Codex
|
|
1170
|
+
return /* @__PURE__ */ new Error(`Codex execution node disconnected; start a fresh attempt. (${reason}${detail})`);
|
|
1990
1171
|
}
|
|
1991
1172
|
/** Relays one authorized, single-use Codex exec-server channel without interpreting its protocol. */
|
|
1992
1173
|
async function startCodexNodeExecServerRelay(params) {
|
|
@@ -2013,7 +1194,7 @@ async function startCodexNodeExecServerRelay(params) {
|
|
|
2013
1194
|
if (!closed && !params.lease.closed) params.lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(reason, cause));
|
|
2014
1195
|
closeBoth(code, reason);
|
|
2015
1196
|
};
|
|
2016
|
-
params.lease.onChannelClosed = ({ failed, error }) => failUnexpectedly(failed ? 1011 : 1001, failed ? "execution
|
|
1197
|
+
params.lease.onChannelClosed = ({ failed, error }) => failUnexpectedly(failed ? 1011 : 1001, failed ? "execution node failed" : "execution node disconnected", error);
|
|
2017
1198
|
socket.once("close", () => failUnexpectedly(1001, "execution socket closed"));
|
|
2018
1199
|
socket.once("error", () => failUnexpectedly(1011, "execution socket failed"));
|
|
2019
1200
|
let toNode = Promise.resolve();
|
|
@@ -2217,7 +1398,7 @@ function createCredentialedCodexNodeHttpRejection(request) {
|
|
|
2217
1398
|
id: request.id,
|
|
2218
1399
|
error: {
|
|
2219
1400
|
code: -32602,
|
|
2220
|
-
message: "Authenticated remote HTTP is unavailable on
|
|
1401
|
+
message: "Authenticated remote HTTP is unavailable on execution nodes; run on Gateway or use an intentionally credential-free endpoint."
|
|
2221
1402
|
}
|
|
2222
1403
|
}));
|
|
2223
1404
|
}
|
|
@@ -3012,6 +2193,8 @@ async function httpRequest(execServer, notifications, params) {
|
|
|
3012
2193
|
const record = requireObject(params, "http/request params");
|
|
3013
2194
|
const requestId = requireString(record.requestId, "requestId");
|
|
3014
2195
|
const url = requireString(record.url, "url");
|
|
2196
|
+
const redirectPolicy = record.redirectPolicy ?? "follow";
|
|
2197
|
+
if (redirectPolicy !== "follow" && redirectPolicy !== "stop") throw new Error("http/request redirectPolicy must be follow or stop");
|
|
3015
2198
|
assertSandboxHttpRequestTargetAllowed(url);
|
|
3016
2199
|
const request = {
|
|
3017
2200
|
method: requireString(record.method, "method"),
|
|
@@ -3019,6 +2202,7 @@ async function httpRequest(execServer, notifications, params) {
|
|
|
3019
2202
|
headers: readHttpHeaders(record.headers),
|
|
3020
2203
|
bodyBase64: typeof record.bodyBase64 === "string" ? record.bodyBase64 : void 0,
|
|
3021
2204
|
timeoutMs: typeof record.timeoutMs === "number" && record.timeoutMs > 0 ? Math.floor(record.timeoutMs) : void 0,
|
|
2205
|
+
redirectPolicy,
|
|
3022
2206
|
streamResponse: record.streamResponse === true
|
|
3023
2207
|
};
|
|
3024
2208
|
if (request.streamResponse) return await runStreamingSandboxHttpRequest(execServer, notifications, requestId, request);
|
|
@@ -3315,9 +2499,40 @@ def assert_url_allowed(url):
|
|
|
3315
2499
|
PINNED_ADDRESSES[hostname] = sorted(addresses)
|
|
3316
2500
|
|
|
3317
2501
|
class GuardedRedirectHandler(urllib.request.HTTPRedirectHandler):
|
|
2502
|
+
def __init__(self, redirect_policy):
|
|
2503
|
+
self.redirect_policy = redirect_policy
|
|
2504
|
+
|
|
3318
2505
|
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
2506
|
+
if self.redirect_policy == "stop":
|
|
2507
|
+
return None
|
|
3319
2508
|
assert_url_allowed(newurl)
|
|
3320
|
-
|
|
2509
|
+
method = req.get_method()
|
|
2510
|
+
drop_body = code == 303 or (code in (301, 302) and method == "POST")
|
|
2511
|
+
next_headers = dict(req.headers)
|
|
2512
|
+
if drop_body:
|
|
2513
|
+
if method != "HEAD":
|
|
2514
|
+
method = "GET"
|
|
2515
|
+
for name in ("content-type", "content-length", "content-encoding", "transfer-encoding"):
|
|
2516
|
+
next_headers.pop(name.capitalize(), None)
|
|
2517
|
+
redirected = urllib.request.Request(
|
|
2518
|
+
newurl,
|
|
2519
|
+
data=None if drop_body else req.data,
|
|
2520
|
+
headers=next_headers,
|
|
2521
|
+
method=method,
|
|
2522
|
+
origin_req_host=req.origin_req_host,
|
|
2523
|
+
unverifiable=True,
|
|
2524
|
+
)
|
|
2525
|
+
previous = urllib.parse.urlsplit(req.full_url)
|
|
2526
|
+
target = urllib.parse.urlsplit(newurl)
|
|
2527
|
+
previous_port = previous.port or (443 if previous.scheme == "https" else 80)
|
|
2528
|
+
target_port = target.port or (443 if target.scheme == "https" else 80)
|
|
2529
|
+
if (previous.scheme, previous.hostname, previous_port) != (
|
|
2530
|
+
target.scheme, target.hostname, target_port
|
|
2531
|
+
):
|
|
2532
|
+
# Match Codex's route-aware client: cross-origin hops never inherit secrets.
|
|
2533
|
+
for name in ("authorization", "cookie", "proxy-authorization", "www-authenticate", "cookie2"):
|
|
2534
|
+
redirected.remove_header(name.capitalize())
|
|
2535
|
+
return redirected
|
|
3321
2536
|
|
|
3322
2537
|
def pinned_getaddrinfo(original_getaddrinfo):
|
|
3323
2538
|
def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
|
|
@@ -3373,7 +2588,8 @@ def main():
|
|
|
3373
2588
|
timeout = None
|
|
3374
2589
|
if isinstance(timeout_ms, (int, float)) and timeout_ms > 0:
|
|
3375
2590
|
timeout = timeout_ms / 1000
|
|
3376
|
-
|
|
2591
|
+
redirect_handler = GuardedRedirectHandler(input_data.get("redirectPolicy", "follow"))
|
|
2592
|
+
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), redirect_handler)
|
|
3377
2593
|
original_getaddrinfo = socket.getaddrinfo
|
|
3378
2594
|
socket.getaddrinfo = pinned_getaddrinfo(original_getaddrinfo)
|
|
3379
2595
|
try:
|
|
@@ -3406,6 +2622,7 @@ async function startProcess(execServer, processes, notify, params) {
|
|
|
3406
2622
|
const argv = requireStringArray(record.argv, "argv");
|
|
3407
2623
|
const cwd = resolveExecServerPath(requireString(record.cwd, "cwd"), "process cwd");
|
|
3408
2624
|
rejectUnsupportedArg0(record.arg0);
|
|
2625
|
+
assertSupportedProcessSandbox(execServer, record);
|
|
3409
2626
|
const env = readProcessEnv(record);
|
|
3410
2627
|
const managed = {
|
|
3411
2628
|
processId,
|
|
@@ -3450,7 +2667,18 @@ async function startProcess(execServer, processes, notify, params) {
|
|
|
3450
2667
|
} finally {
|
|
3451
2668
|
if (managed.startPromise === startPromise) managed.startPromise = void 0;
|
|
3452
2669
|
}
|
|
3453
|
-
return {
|
|
2670
|
+
return {
|
|
2671
|
+
processId,
|
|
2672
|
+
sandboxType: "none"
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
function assertSupportedProcessSandbox(execServer, record) {
|
|
2676
|
+
if (record.networkProxy !== void 0 && record.networkProxy !== null) throw new Error("Codex sandbox exec-server network proxy launch is not supported.");
|
|
2677
|
+
if (record.enforceManagedNetwork === true || record.managedNetwork !== void 0 && record.managedNetwork !== null) throw new Error("Codex managed network restrictions cannot be enforced by the sandbox backend.");
|
|
2678
|
+
if (resolveFsSandboxPolicy(execServer, record)?.unrestricted === false) throw new Error("Codex process filesystem sandbox restrictions cannot be enforced by the backend.");
|
|
2679
|
+
if (record.sandbox === void 0 || record.sandbox === null) return;
|
|
2680
|
+
if (requireObject(requireObject(record.sandbox, "process sandbox context").permissions, "process sandbox permissions").network !== "restricted") return;
|
|
2681
|
+
if (!execServer.networkIsolated) throw new Error("Codex network restrictions cannot be enforced by the sandbox backend.");
|
|
3454
2682
|
}
|
|
3455
2683
|
async function runProcess(execServer, managed, params) {
|
|
3456
2684
|
const backend = execServer.backend;
|
|
@@ -3767,7 +2995,7 @@ async function ensureCodexSandboxExecServerEnvironment(params) {
|
|
|
3767
2995
|
if (!params.sandbox?.enabled) return;
|
|
3768
2996
|
const placementNodeId = readCodexPlacementNodeId(params.sandbox);
|
|
3769
2997
|
if (!params.sandbox.backend && !placementNodeId) return;
|
|
3770
|
-
if (placementNodeId && !params.runtime) throw new Error("Codex
|
|
2998
|
+
if (placementNodeId && !params.runtime) throw new Error("Codex node execution requires its active plugin runtime.");
|
|
3771
2999
|
if (!canExposeLocalExecServerToAppServer(params.appServerStartOptions)) throw new Error("OpenClaw Codex exec-server uses a local loopback URL and cannot be registered with a remote Codex app-server.");
|
|
3772
3000
|
const { server: execServer, nodeLease } = await acquireOpenClawExecServer({
|
|
3773
3001
|
sandbox: params.sandbox,
|
|
@@ -3833,7 +3061,7 @@ async function acquireOpenClawExecServer(params) {
|
|
|
3833
3061
|
if (!("node" in server)) return { server };
|
|
3834
3062
|
if (!runtime || !signal) {
|
|
3835
3063
|
await releaseOpenClawExecServer(server);
|
|
3836
|
-
throw new Error("Codex
|
|
3064
|
+
throw new Error("Codex node execution requires an active runtime and attempt.");
|
|
3837
3065
|
}
|
|
3838
3066
|
try {
|
|
3839
3067
|
const placementIdentity = readCodexPlacementWorkspaceIdentity(sandbox);
|
|
@@ -3852,7 +3080,7 @@ async function acquireOpenClawExecServer(params) {
|
|
|
3852
3080
|
});
|
|
3853
3081
|
if (signal.aborted || server.closed || sandboxExecServerRegistry.servers.get(key) !== promise) {
|
|
3854
3082
|
channel.close();
|
|
3855
|
-
throw new Error("Codex
|
|
3083
|
+
throw new Error("Codex node execution retired before its channel was ready.");
|
|
3856
3084
|
}
|
|
3857
3085
|
const nodeLease = {
|
|
3858
3086
|
id: randomUUID(),
|
|
@@ -3936,7 +3164,8 @@ async function startOpenClawExecServer(sandbox) {
|
|
|
3936
3164
|
} : {
|
|
3937
3165
|
...common,
|
|
3938
3166
|
backend: connection.backend,
|
|
3939
|
-
fsBridge: connection.fsBridge
|
|
3167
|
+
fsBridge: connection.fsBridge,
|
|
3168
|
+
networkIsolated: (connection.backend.id === "docker" || connection.backend.id === "podman") && sandbox.docker.network.trim().toLowerCase() === "none"
|
|
3940
3169
|
};
|
|
3941
3170
|
server.on("connection", (socket, request) => {
|
|
3942
3171
|
socket.on("error", handleExecServerSocketError);
|
|
@@ -3977,7 +3206,7 @@ function readCodexPlacementNodeId(sandbox) {
|
|
|
3977
3206
|
return sandbox.placementNodeId;
|
|
3978
3207
|
}
|
|
3979
3208
|
function readCodexPlacementWorkspaceIdentity(sandbox) {
|
|
3980
|
-
if (!("placementEnvironmentId" in sandbox) || typeof sandbox.placementEnvironmentId !== "string" || !sandbox.placementEnvironmentId || sandbox.placementEnvironmentId.trim() !== sandbox.placementEnvironmentId || !("placementSessionId" in sandbox) || typeof sandbox.placementSessionId !== "string" || !sandbox.placementSessionId || sandbox.placementSessionId.trim() !== sandbox.placementSessionId || !("placementOwnerEpoch" in sandbox) || typeof sandbox.placementOwnerEpoch !== "number" || !Number.isSafeInteger(sandbox.placementOwnerEpoch) || sandbox.placementOwnerEpoch < 1 || !sandbox.sessionKey || sandbox.sessionKey.trim() !== sandbox.sessionKey) throw new Error("Codex
|
|
3209
|
+
if (!("placementEnvironmentId" in sandbox) || typeof sandbox.placementEnvironmentId !== "string" || !sandbox.placementEnvironmentId || sandbox.placementEnvironmentId.trim() !== sandbox.placementEnvironmentId || !("placementSessionId" in sandbox) || typeof sandbox.placementSessionId !== "string" || !sandbox.placementSessionId || sandbox.placementSessionId.trim() !== sandbox.placementSessionId || !("placementOwnerEpoch" in sandbox) || typeof sandbox.placementOwnerEpoch !== "number" || !Number.isSafeInteger(sandbox.placementOwnerEpoch) || sandbox.placementOwnerEpoch < 1 || !sandbox.sessionKey || sandbox.sessionKey.trim() !== sandbox.sessionKey) throw new Error("Codex node execution requires its exact placement workspace identity.");
|
|
3981
3210
|
return {
|
|
3982
3211
|
environmentId: sandbox.placementEnvironmentId,
|
|
3983
3212
|
sessionId: sandbox.placementSessionId,
|
|
@@ -4018,7 +3247,7 @@ function handleClosedCodexNodeExecServerLease(execServer, lease, result) {
|
|
|
4018
3247
|
return;
|
|
4019
3248
|
}
|
|
4020
3249
|
try {
|
|
4021
|
-
lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(result.failed ? "execution
|
|
3250
|
+
lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(result.failed ? "execution node failed" : "execution node disconnected", result.error));
|
|
4022
3251
|
} finally {
|
|
4023
3252
|
closeCodexNodeExecServerLease(execServer, lease);
|
|
4024
3253
|
}
|
|
@@ -4156,7 +3385,7 @@ async function handleCodexAppServerApprovalRequest(params) {
|
|
|
4156
3385
|
return buildApprovalResponse(params.method, context.requestParams, resolvedOutcome);
|
|
4157
3386
|
};
|
|
4158
3387
|
try {
|
|
4159
|
-
if (params.method === "item/commandExecution/requestApproval") {
|
|
3388
|
+
if (params.method === "item/commandExecution/requestApproval" && !readNetworkApprovalContext(requestParams)) {
|
|
4160
3389
|
const command = readPolicyCommand(requestParams);
|
|
4161
3390
|
const cwd = readStringField(requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
|
|
4162
3391
|
const prepareMutableFileApproval = params.paramsForRun.hostCapabilities.prepareMutableFileApproval;
|
|
@@ -4183,7 +3412,7 @@ async function handleCodexAppServerApprovalRequest(params) {
|
|
|
4183
3412
|
return await resolvePolicyApproval("denied", policyOutcome.reason);
|
|
4184
3413
|
}
|
|
4185
3414
|
if (policyOutcome?.outcome === "approved-once" || policyOutcome?.outcome === "approved-session") return await resolvePolicyApproval(policyOutcome.outcome);
|
|
4186
|
-
if (CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method) && params.autoApprove === true) return await resolvePolicyApproval("approved-session", "Codex app-server approval auto-approved by runtime policy.");
|
|
3415
|
+
if (CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method) && !readNetworkApprovalContext(requestParams) && params.autoApprove === true) return await resolvePolicyApproval("approved-session", "Codex app-server approval auto-approved by runtime policy.");
|
|
4187
3416
|
const requestResult = await requestPluginApproval({
|
|
4188
3417
|
hostCapabilities: params.paramsForRun.hostCapabilities,
|
|
4189
3418
|
title: context.title,
|
|
@@ -4299,18 +3528,19 @@ function buildApprovalContext(params) {
|
|
|
4299
3528
|
const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
|
|
4300
3529
|
const command = commandPreview.text;
|
|
4301
3530
|
const reason = reasonPreview.text;
|
|
3531
|
+
const networkApproval = params.method === "item/commandExecution/requestApproval" ? readNetworkApprovalContext(params.requestParams) : void 0;
|
|
4302
3532
|
const approvalKind = params.method.includes("commandExecution") ? "command" : params.method.includes("fileChange") ? "file-change" : params.method.includes("permissions") ? "permissions" : "other";
|
|
4303
3533
|
const kind = approvalKind === "command" ? "exec" : approvalKind === "other" ? "unknown" : "plugin";
|
|
4304
3534
|
const permissionLines = params.method === "item/permissions/requestApproval" ? describeRequestedPermissions(params.requestParams) : [];
|
|
4305
|
-
const title = kind === "exec" ? "Codex app-server command approval" : params.method === "item/permissions/requestApproval" ? "Codex app-server permission approval" : kind === "plugin" ? "Codex app-server file approval" : "Codex app-server approval";
|
|
4306
|
-
const subject = permissionLines[0] ?? (command ? `Command: ${formatApprovalPreviewSubject(command, commandPreview.omitted)}` : commandPreview.omitted ? `Command: ${APPROVAL_PREVIEW_OMITTED}` : reason ? `Reason: ${formatApprovalPreviewSubject(reason, reasonPreview.omitted)}` : reasonPreview.omitted ? `Reason: ${APPROVAL_PREVIEW_OMITTED}` : `Request method: ${params.method}`);
|
|
3535
|
+
const title = networkApproval ? "Codex app-server network approval" : kind === "exec" ? "Codex app-server command approval" : params.method === "item/permissions/requestApproval" ? "Codex app-server permission approval" : kind === "plugin" ? "Codex app-server file approval" : "Codex app-server approval";
|
|
3536
|
+
const subject = (networkApproval ? `Network: ${sanitizePermissionScalar(networkApproval.protocol)}://${sanitizePermissionHostValue(networkApproval.host)}` : void 0) ?? permissionLines[0] ?? (command ? `Command: ${formatApprovalPreviewSubject(command, commandPreview.omitted)}` : commandPreview.omitted ? `Command: ${APPROVAL_PREVIEW_OMITTED}` : reason ? `Reason: ${formatApprovalPreviewSubject(reason, reasonPreview.omitted)}` : reasonPreview.omitted ? `Reason: ${APPROVAL_PREVIEW_OMITTED}` : `Request method: ${params.method}`);
|
|
4307
3537
|
return {
|
|
4308
3538
|
approvalKind,
|
|
4309
3539
|
kind,
|
|
4310
3540
|
title,
|
|
4311
3541
|
description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [subject, ...commandDetailLines].join("\n"),
|
|
4312
3542
|
severity: kind === "exec" ? "warning" : "info",
|
|
4313
|
-
toolName: kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
|
|
3543
|
+
toolName: networkApproval ? "codex_network_approval" : kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
|
|
4314
3544
|
itemId,
|
|
4315
3545
|
approvalId,
|
|
4316
3546
|
requestParams: params.requestParams,
|
|
@@ -4482,6 +3712,10 @@ function sanitizeRelayDecisionReason(value) {
|
|
|
4482
3712
|
}
|
|
4483
3713
|
function buildOpenClawToolPolicyRequest(method, requestParams) {
|
|
4484
3714
|
if (method === "item/commandExecution/requestApproval") {
|
|
3715
|
+
if (readNetworkApprovalContext(requestParams)) return {
|
|
3716
|
+
toolName: "codex_network_approval",
|
|
3717
|
+
params: { approval: requestParams ?? {} }
|
|
3718
|
+
};
|
|
4485
3719
|
const command = readPolicyCommand(requestParams);
|
|
4486
3720
|
return {
|
|
4487
3721
|
toolName: "exec",
|
|
@@ -4769,6 +4003,15 @@ function readPolicyCommand(record) {
|
|
|
4769
4003
|
const actionCommands = readCommandActions(record);
|
|
4770
4004
|
if (actionCommands.length > 0) return actionCommands.join(" && ");
|
|
4771
4005
|
}
|
|
4006
|
+
function readNetworkApprovalContext(record) {
|
|
4007
|
+
const context = isJsonObject(record?.networkApprovalContext) ? record.networkApprovalContext : void 0;
|
|
4008
|
+
const host = readStringField(context, "host");
|
|
4009
|
+
const protocol = readStringField(context, "protocol");
|
|
4010
|
+
return host && protocol ? {
|
|
4011
|
+
host,
|
|
4012
|
+
protocol
|
|
4013
|
+
} : void 0;
|
|
4014
|
+
}
|
|
4772
4015
|
function readCommandActions(record) {
|
|
4773
4016
|
const actions = record?.commandActions;
|
|
4774
4017
|
if (!Array.isArray(actions)) return [];
|
|
@@ -4958,6 +4201,7 @@ async function routeCodexAppServerElicitationRequest(params) {
|
|
|
4958
4201
|
if (!requestParams || readNonBlankStringField(requestParams, "threadId") !== params.threadId) return { kind: "not-mine" };
|
|
4959
4202
|
const requestTurnId = requestParams.turnId;
|
|
4960
4203
|
if (requestTurnId !== null && requestTurnId !== void 0 && requestTurnId !== params.turnId) return { kind: "not-mine" };
|
|
4204
|
+
if (!((isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : void 0)?.[MCP_TOOL_APPROVAL_KIND_KEY] === MCP_TOOL_APPROVAL_KIND || params.computerUseMcpServerName !== void 0 && readNonBlankStringField(requestParams, "serverName") === params.computerUseMcpServerName)) return { kind: "not-mine" };
|
|
4961
4205
|
const pluginResolution = resolvePluginElicitation({
|
|
4962
4206
|
requestParams,
|
|
4963
4207
|
pluginAppPolicyContext: params.pluginAppPolicyContext
|
|
@@ -4983,7 +4227,7 @@ async function routeCodexAppServerElicitationRequest(params) {
|
|
|
4983
4227
|
}));
|
|
4984
4228
|
}
|
|
4985
4229
|
const approvalPrompt = readComputerUseApprovalElicitation(requestParams, params.computerUseMcpServerName) ?? readBridgeableApprovalElicitation(requestParams);
|
|
4986
|
-
if (!approvalPrompt) return
|
|
4230
|
+
if (!approvalPrompt) return handled(createCodexElicitationResponse("decline"));
|
|
4987
4231
|
return handled(buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
|
|
4988
4232
|
paramsForRun: params.paramsForRun,
|
|
4989
4233
|
title: approvalPrompt.title,
|
|
@@ -6526,4 +5770,4 @@ function isCronAddAction(args) {
|
|
|
6526
5770
|
return typeof action === "string" && action.trim().toLowerCase() === "add";
|
|
6527
5771
|
}
|
|
6528
5772
|
//#endregion
|
|
6529
|
-
export {
|
|
5773
|
+
export { shouldRequireCodexSandboxExecServerEnvironment as C, CodexNativeToolLifecycleProjector as D, readBoundedCodexRemoteWorkspaceFile as E, shouldEnableCodexAppServerNativeToolSurface as S, filterCodexVisionTools as T, resolveCodexAppServerHookChannelId as _, emitDynamicToolStartedDiagnostic as a, resolveCodexNodePlacementToolConstructionPlan as b, resolveCodexProviderWebSearchSupport as c, releaseCodexSandboxExecServerEnvironment as d, buildDynamicTools as f, resolveCodexAppServerExecutionCwd as g, formatCodexDynamicToolBuildStageSummary as h, emitDynamicToolErrorDiagnostic as i, resolveCodexProviderWebSearchSupportForClient as l, disableCodexPluginThreadConfig as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, createCodexDynamicToolBuildStageTracker as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, ensureCodexSandboxExecServerEnvironment as u, resolveCodexExternalSandboxPolicyForOpenClawSandbox as v, shouldWarnCodexDynamicToolBuildStageSummary as w, resolveCodexSandboxEnvironmentSelection as x, resolveCodexMessageToolProvider as y };
|