@openclaw/codex 2026.8.2 → 2026.9.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 +3 -3
- package/dist/{attempt-client-cleanup-CpVfwctT.js → attempt-client-cleanup-DYHcRQAa.js} +6 -43
- package/dist/attempt-context-DmQUNWwT.js +634 -0
- package/dist/{binding-connection-CF7BmrDk.js → binding-connection-E6uDsPlx.js} +2 -2
- package/dist/{bounded-turn-DRC3hcjm.js → bounded-turn-wzJrwKKe.js} +8 -9
- package/dist/{capabilities-D6m_TdVy.js → capabilities-dIjNVCYH.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters--UToCokS.js → command-formatters-CLHhEB1Q.js} +3 -3
- package/dist/{command-handlers-CLFx2Ypa.js → command-handlers-CTGb2csu.js} +54 -55
- package/dist/{compact-5xqW1oCh.js → compact-_t0HQrUS.js} +33 -25
- package/dist/{config-CcYL77O6.js → config-BhmfC1s6.js} +12 -8
- package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
- package/dist/doctor-contract-api.js +31 -4
- package/dist/{dynamic-tools-BmeXjwNZ.js → dynamic-tools-BU9tmlYm.js} +448 -457
- package/dist/{effective-mcp-catalog-XXQKgK1U.js → effective-mcp-catalog-DzYBW4h1.js} +2 -2
- package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-DHRFXKbS.js} +4 -4
- package/dist/{event-projector-usage-B2r5vhWH.js → event-projector-usage-C5mcrGjp.js} +9 -137
- package/dist/{harness-BgeB7LnD.js → harness-DXVZ5II5.js} +13 -13
- package/dist/harness.js +1 -1
- package/dist/index.js +323 -313
- package/dist/{isolated-completion-CxTus3vZ.js → isolated-completion-kg9kv4iH.js} +4 -4
- package/dist/{launch-args-sXOAg0SX.js → launch-args-RXQwn8zV.js} +7 -1
- package/dist/{managed-binary-ZkGodW19.js → managed-binary-4-Vjz5GH.js} +14 -9
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{model-catalog-DULBcniB.js → model-catalog-CUM68Hkl.js} +11 -11
- package/dist/{models-DaWfETQk.js → models-sYnEc0gd.js} +3 -3
- package/dist/{native-subagent-monitor-vXYUZH37.js → native-subagent-monitor-CnH147jH.js} +3 -3
- package/dist/{node-exec-server.runtime-DirKVZ5h.js → node-exec-server.runtime-CstNHCmC.js} +2 -2
- package/dist/{plugin-approval-roundtrip-CCWQGlAI.js → plugin-approval-roundtrip-CNe-Xrjx.js} +29 -3
- package/dist/{plugin-marketplace-discovery-BXjIFEiw.js → plugin-marketplace-discovery-CfFlKqDl.js} +6 -6
- package/dist/{plugin-metadata-cache-DQQm4gWT.js → plugin-metadata-cache-BFrPhb2c.js} +5 -5
- package/dist/provider-capabilities-DW5p4k9w.js +101 -0
- package/dist/{rate-limits-BqHEHXQJ.js → rate-limits-CawQJfUS.js} +1 -1
- package/dist/{request-BWxweEdK.js → request-DYAI5FQE.js} +18 -6
- package/dist/{run-attempt-BVLE4Gbi.js → run-attempt-D1hJO4fX.js} +483 -1865
- package/dist/{runtime-artifact-D9pYp4zL.js → runtime-artifact-C3jVimFn.js} +19 -12
- package/dist/{session-binding-D0zinCP1.js → session-binding-BHuTCRZQ.js} +8 -10
- package/dist/{session-catalog-eMhVm0Ql.js → session-catalog-yyvTWFOw.js} +138 -116
- package/dist/{session-cli-B2lyjJA0.js → session-cli-CmFEXMAM.js} +1 -1
- package/dist/session-initialization-BeWzjGiR.js +74 -0
- package/dist/{session-permission-policy-ScnAyG90.js → session-permission-policy-DeWVTCMj.js} +2 -2
- package/dist/{session-retirement-CtUtUTr9.js → session-retirement-BujDjXVF.js} +16 -9
- package/dist/{session-upstream-marker-gqrYIRHk.js → session-upstream-marker-C351-5Qw.js} +18 -2
- package/dist/{settled-turn-context-Bk3IUwod.js → settled-turn-context-D3SNWtUn.js} +6 -28
- package/dist/{settled-turn-finalizer-BXLN9PfJ.js → settled-turn-finalizer-BVJx4yoc.js} +5 -5
- package/dist/{shared-client-B1tT5U6r.js → shared-client-DxcfF9KX.js} +2748 -101
- package/dist/{side-question-CZ-mCw-t.js → side-question-BTZAII4l.js} +97 -63
- package/dist/{thread-lifecycle-CGGKP720.js → thread-lifecycle-DAcEJ_Om.js} +2039 -1487
- package/dist/{thread-ownership-RkaoZCmQ.js → thread-ownership-zGokd8i1.js} +10 -9
- package/dist/thread-policy-g4KbJPzp.js +54 -0
- package/dist/{thread-resume-BFt1-ZDa.js → thread-resume-C7pyVKeW.js} +15 -7
- package/dist/{transcript-mirror-1ep4XjzF.js → transcript-mirror-DTUxFWa_.js} +47 -10
- package/dist/{transport-stdio-NT_Dpd9V.js → transport-stdio-CyPldczm.js} +3 -1
- package/dist/upstream-session-fork-CYlBIGVt.js +966 -0
- package/dist/{usage-DUhd58gI.js → usage-Bj-rrzkR.js} +3 -3
- package/dist/{web-search-provider.runtime-BEmlE7ov.js → web-search-provider.runtime-D2FWSK4w.js} +3 -3
- package/openclaw.plugin.json +0 -30
- package/package.json +5 -5
- package/dist/protocol-validators-Bg5rpNxk.js +0 -2542
- package/dist/session-catalog-home-id-Dt2GiG5o.js +0 -112
- package/dist/upstream-session-fork-Dc8BFExw.js +0 -312
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { l as resolveCodexNativeExecutionPolicy, u as resolveCodexNodeExecToolOverrides } from "./request-BWxweEdK.js";
|
|
1
|
+
import { $t as isJsonObject, Qt as flattenCodexDynamicToolFunctions, Zt as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, _t as resolveCodexToolAbortTerminalReason, bt as withDynamicToolTranscriptDetails, it as readCodexTurn, ot as createCodexElicitationResponse, vt as createFailedDynamicToolResponse, yt as withDynamicToolExecutionState } from "./shared-client-DxcfF9KX.js";
|
|
2
|
+
import { E as readCodexPluginConfig, w as isCodexRemoteExecPlacementSandbox } from "./config-BhmfC1s6.js";
|
|
3
|
+
import { E as itemStatus, I as readItem, b as auditNativeToolUnfinishedStatus, h as isCodexNotificationForTurn, v as auditNativeToolName, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-DYHcRQAa.js";
|
|
4
|
+
import { B as emitCodexNativePreToolUseFailureDiagnostic, S as resolveCodexWebSearchPlan, at as filterCodexDynamicTools, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, ot as filterCodexDynamicToolsForDisabledNativeSurface, st as isForcedPrivateQaCodexRuntime, ut as normalizeCodexDynamicToolName } from "./thread-lifecycle-DAcEJ_Om.js";
|
|
5
|
+
import { l as resolveCodexNativeExecutionPolicy, u as resolveCodexNodeExecToolOverrides } from "./request-DYAI5FQE.js";
|
|
7
6
|
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
|
|
8
|
-
import {
|
|
9
|
-
import { a as
|
|
7
|
+
import { a as formatCodexDisplayText } from "./command-formatters-CLHhEB1Q.js";
|
|
8
|
+
import { a as requestPluginApprovalOutcome, c as truncateCodexApprovalDisplayText, i as requestPluginApproval, l as waitForPluginApprovalDecision, n as codexApprovalTimeoutText, o as sanitizeCodexApprovalVisibleText, r as mapExecDecisionToOutcome, s as stripDanglingCodexApprovalTerminalSequence, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-CNe-Xrjx.js";
|
|
9
|
+
import { a as CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME, c as createNodeExecAliasDynamicTool, i as CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME, l as isCodexDynamicToolExcluded, o as createGatewayExecProjection, r as CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME, s as createGatewayProcessProjection } from "./provider-capabilities-DW5p4k9w.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
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
|
+
import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
16
17
|
import { createHash, randomUUID } from "node:crypto";
|
|
17
18
|
import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
19
|
import { formatErrorMessage as formatErrorMessage$1, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
|
|
@@ -28,15 +29,17 @@ import { WebSocketServer } from "ws";
|
|
|
28
29
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
29
30
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
30
31
|
import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
31
|
-
import {
|
|
32
|
+
import { formatMcpCodexApprovalRemedy, requiresMcpCodexToolApproval, resolveCodexScheduledToolProjectionFactory, resolveProjectedMcpCodexToolApprovalMode, runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
32
33
|
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
35
|
import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
|
|
36
|
-
import { getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
|
|
37
|
-
import {
|
|
36
|
+
import { consumeTrustedToolNoStartError, getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
|
|
37
|
+
import { normalizeOpenAIStrictCompatSchema } from "openclaw/plugin-sdk/provider-tools";
|
|
38
38
|
//#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
|
|
39
|
-
|
|
39
|
+
function isMcpToolCallItemNotification(method, params) {
|
|
40
|
+
return (method === "item/started" || method === "item/completed") && isJsonObject(params.item) && params.item.type === "mcpToolCall";
|
|
41
|
+
}
|
|
42
|
+
/** Owns native item lifetimes for diagnostics and MCP approval correlation. */
|
|
40
43
|
var CodexNativeToolLifecycleProjector = class {
|
|
41
44
|
constructor(context, threadId, turnId, options = {}) {
|
|
42
45
|
this.context = context;
|
|
@@ -50,13 +53,39 @@ var CodexNativeToolLifecycleProjector = class {
|
|
|
50
53
|
this.approvalFailureDispositionByItem = /* @__PURE__ */ new Map();
|
|
51
54
|
this.preToolUseFailureByItem = /* @__PURE__ */ new Map();
|
|
52
55
|
this.finalized = false;
|
|
56
|
+
this.turnCompleted = false;
|
|
57
|
+
this.pendingMcpNotifications = 0;
|
|
58
|
+
}
|
|
59
|
+
getActiveMcpToolCall(serverName) {
|
|
60
|
+
if (this.finalized || this.turnCompleted || this.pendingMcpNotifications > 0 || this.options.runAbortSignal?.aborted) return;
|
|
61
|
+
let candidate;
|
|
62
|
+
for (const { mcpToolCall } of this.activeItems.values()) {
|
|
63
|
+
if (mcpToolCall?.server !== serverName) continue;
|
|
64
|
+
if (candidate) return;
|
|
65
|
+
candidate = mcpToolCall;
|
|
66
|
+
}
|
|
67
|
+
if (!candidate || candidate.status !== "inProgress" || typeof candidate.server !== "string" || !candidate.server.trim() || typeof candidate.tool !== "string" || !candidate.tool.trim() || candidate.arguments === void 0 || candidate.appContext != null || candidate.pluginId != null) return;
|
|
68
|
+
return {
|
|
69
|
+
id: candidate.id,
|
|
70
|
+
server: candidate.server,
|
|
71
|
+
tool: candidate.tool,
|
|
72
|
+
arguments: candidate.arguments
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
recordMcpToolCallReceipt(notification) {
|
|
76
|
+
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
77
|
+
if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
|
|
78
|
+
if (isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications += 1;
|
|
79
|
+
else if (notification.method === "turn/completed" && readCodexTurn(params.turn)?.id === this.turnId) this.turnCompleted = true;
|
|
53
80
|
}
|
|
54
81
|
handleNotification(notification) {
|
|
55
82
|
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
56
83
|
if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
|
|
84
|
+
if (this.pendingMcpNotifications > 0 && isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications -= 1;
|
|
57
85
|
if (notification.method === "turn/completed") {
|
|
58
86
|
const turn = readCodexTurn(params.turn);
|
|
59
87
|
if (!turn || turn.id !== this.turnId) return;
|
|
88
|
+
this.turnCompleted = true;
|
|
60
89
|
for (const item of turn.items ?? []) this.recordSnapshotItem(item);
|
|
61
90
|
return;
|
|
62
91
|
}
|
|
@@ -75,10 +104,10 @@ var CodexNativeToolLifecycleProjector = class {
|
|
|
75
104
|
});
|
|
76
105
|
}
|
|
77
106
|
recordItem(params) {
|
|
78
|
-
const toolName = auditNativeToolName(params.item);
|
|
107
|
+
const toolName = auditNativeToolName(params.item) ?? (params.item.type === "mcpToolCall" ? params.item.type : void 0);
|
|
79
108
|
if (!toolName || this.completedItemIds.has(params.item.id)) return;
|
|
80
109
|
if (params.phase === "start") {
|
|
81
|
-
this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs);
|
|
110
|
+
this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs, params.item.type === "mcpToolCall" ? params.item : void 0);
|
|
82
111
|
return;
|
|
83
112
|
}
|
|
84
113
|
if (params.item.type === "webSearch") {
|
|
@@ -204,12 +233,13 @@ var CodexNativeToolLifecycleProjector = class {
|
|
|
204
233
|
item
|
|
205
234
|
});
|
|
206
235
|
}
|
|
207
|
-
recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs) {
|
|
236
|
+
recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs, mcpToolCall) {
|
|
208
237
|
if (this.activeItems.has(toolCallId)) return;
|
|
209
238
|
this.startedAtByItem.set(toolCallId, sourceTimestampMs ?? Date.now());
|
|
210
239
|
this.activeItems.set(toolCallId, {
|
|
211
240
|
toolName,
|
|
212
|
-
unfinishedStatus
|
|
241
|
+
unfinishedStatus,
|
|
242
|
+
mcpToolCall
|
|
213
243
|
});
|
|
214
244
|
emitTrustedDiagnosticEvent({
|
|
215
245
|
type: "tool.execution.started",
|
|
@@ -544,53 +574,6 @@ function mapMessageMediaValues(args, mapValue) {
|
|
|
544
574
|
/** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
|
|
545
575
|
const dynamicToolBuildState = {};
|
|
546
576
|
//#endregion
|
|
547
|
-
//#region extensions/codex/src/app-server/shell-dynamic-tools.ts
|
|
548
|
-
const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
|
|
549
|
-
const CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME = "gateway_exec";
|
|
550
|
-
const CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME = "gateway_process";
|
|
551
|
-
const PROCESS_FOLLOWUP_TEXT = "Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.";
|
|
552
|
-
/** Returns true when plugin config explicitly removes any named dynamic tool. */
|
|
553
|
-
function isCodexDynamicToolExcluded(config, names) {
|
|
554
|
-
const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
|
|
555
|
-
return (config.codexDynamicToolsExclude ?? []).some((name) => normalizedNames.has(normalizeCodexDynamicToolName(name)));
|
|
556
|
-
}
|
|
557
|
-
function createNodeExecAliasDynamicTool(execTool, node) {
|
|
558
|
-
const pinnedNode = node?.trim();
|
|
559
|
-
const pinnedTool = pinExecToolTarget(execTool, {
|
|
560
|
-
host: "node",
|
|
561
|
-
...pinnedNode ? { node: pinnedNode } : {}
|
|
562
|
-
});
|
|
563
|
-
const execute = async (toolCallId, args, signal, onUpdate) => {
|
|
564
|
-
const result = await pinnedTool.execute(toolCallId, args, signal, onUpdate);
|
|
565
|
-
return {
|
|
566
|
-
...result,
|
|
567
|
-
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)
|
|
568
|
-
};
|
|
569
|
-
};
|
|
570
|
-
return {
|
|
571
|
-
...pinnedTool,
|
|
572
|
-
name: CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME,
|
|
573
|
-
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.",
|
|
574
|
-
execute
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
function createGatewayExecProjection(createProjection, execTool, params) {
|
|
578
|
-
return createProjection(execTool, {
|
|
579
|
-
kind: "exec",
|
|
580
|
-
name: CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME,
|
|
581
|
-
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.",
|
|
582
|
-
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.",
|
|
583
|
-
...params.ask ? { ask: params.ask } : {}
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
function createGatewayProcessProjection(createProjection, processTool) {
|
|
587
|
-
return createProjection(processTool, {
|
|
588
|
-
kind: "process",
|
|
589
|
-
name: CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
|
|
590
|
-
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."
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
//#endregion
|
|
594
577
|
//#region extensions/codex/src/app-server/vision-tools.ts
|
|
595
578
|
/**
|
|
596
579
|
* Codex's enabled native surface includes its stable view_image loader. Keep
|
|
@@ -616,6 +599,15 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
|
|
|
616
599
|
"apply_patch"
|
|
617
600
|
];
|
|
618
601
|
const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = /* @__PURE__ */ new Set(["read", "write"]);
|
|
602
|
+
const CODEX_DISABLED_NATIVE_SHELL_DYNAMIC_TOOLS = /* @__PURE__ */ new Set([
|
|
603
|
+
"exec",
|
|
604
|
+
"process",
|
|
605
|
+
"sandbox_exec",
|
|
606
|
+
"sandbox_process",
|
|
607
|
+
CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME,
|
|
608
|
+
CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
|
|
609
|
+
CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME
|
|
610
|
+
]);
|
|
619
611
|
/** Keeps node filesystem and process ownership on its native exec-server. */
|
|
620
612
|
function resolveCodexNodePlacementToolConstructionPlan(sandbox, nativeToolSurfaceEnabled) {
|
|
621
613
|
if (!isCodexRemoteExecPlacementSandbox(sandbox) || sandbox?.backendId !== "node" || !("placementNodeId" in sandbox) || typeof sandbox.placementNodeId !== "string" || !sandbox.placementNodeId) return;
|
|
@@ -726,112 +718,114 @@ async function buildDynamicTools(input) {
|
|
|
726
718
|
nativeProviderWebSearchSupport: input.nativeProviderWebSearchSupport
|
|
727
719
|
});
|
|
728
720
|
const webFetchHostnameAllowlistRef = {};
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
|
|
739
|
-
config: params.config,
|
|
740
|
-
elevated: params.bashElevated
|
|
741
|
-
},
|
|
742
|
-
sessionPermissionPolicy: input.sessionPermissionPolicy ? {
|
|
743
|
-
mode: input.sessionPermissionPolicy.mode,
|
|
744
|
-
root: input.sessionPermissionPolicy.root
|
|
745
|
-
} : void 0,
|
|
746
|
-
sandbox: input.sandbox,
|
|
747
|
-
...toolConstructionPlan ? { toolConstructionPlan } : {},
|
|
748
|
-
messageProvider: resolveCodexMessageToolProvider(params),
|
|
749
|
-
toolPolicyMessageProvider: params.messageProvider ?? params.messageChannel,
|
|
750
|
-
clientCaps: params.clientCaps,
|
|
751
|
-
chatType: params.chatType,
|
|
752
|
-
agentAccountId: params.agentAccountId,
|
|
753
|
-
messageTo: params.messageTo,
|
|
754
|
-
messageThreadId: params.messageThreadId,
|
|
755
|
-
nativeChannelId: params.chatId,
|
|
756
|
-
messageActionTurnCapability: params.messageActionTurnCapability,
|
|
757
|
-
groupId: params.groupId,
|
|
758
|
-
groupChannel: params.groupChannel,
|
|
759
|
-
groupSpace: params.groupSpace,
|
|
760
|
-
spawnedBy: params.spawnedBy,
|
|
761
|
-
senderId: params.senderId,
|
|
762
|
-
senderName: params.senderName,
|
|
763
|
-
senderUsername: params.senderUsername,
|
|
764
|
-
senderE164: params.senderE164,
|
|
765
|
-
senderIsOwner: params.senderIsOwner,
|
|
766
|
-
inputProvenance: params.inputProvenance,
|
|
767
|
-
trustedInternalHandoff: params.trustedInternalHandoff,
|
|
768
|
-
scheduledToolPolicy: params.scheduledToolPolicy,
|
|
769
|
-
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
|
|
770
|
-
sessionKey: input.sandboxSessionKey,
|
|
771
|
-
runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
|
|
772
|
-
sessionId: params.sessionId,
|
|
773
|
-
runId: params.runId,
|
|
774
|
-
approvalReviewerDeviceId: params.approvalReviewerDeviceId,
|
|
775
|
-
agentDir,
|
|
776
|
-
preparedModelRuntime: params.preparedModelRuntime,
|
|
777
|
-
cwd: input.effectiveCwd ?? input.effectiveWorkspace,
|
|
778
|
-
workspaceDir: input.effectiveWorkspace,
|
|
779
|
-
spawnWorkspaceDir: input.effectiveCwd && input.effectiveCwd !== input.effectiveWorkspace ? input.resolvedWorkspace : resolveAttemptSpawnWorkspaceDir({
|
|
780
|
-
sandbox: input.sandbox,
|
|
781
|
-
resolvedWorkspace: input.resolvedWorkspace
|
|
782
|
-
}),
|
|
721
|
+
const toolConstructionPlan = resolveCodexNodePlacementToolConstructionPlan(input.sandbox, input.nativeToolSurfaceEnabled);
|
|
722
|
+
const options = {
|
|
723
|
+
agentId: input.sessionAgentId,
|
|
724
|
+
policyAgentId: input.policyAgentId,
|
|
725
|
+
...toolRunContext,
|
|
726
|
+
exec: {
|
|
727
|
+
...params.execOverrides,
|
|
728
|
+
...input.sessionPermissionPolicy ? { mode: input.sessionPermissionPolicy.execMode } : {},
|
|
729
|
+
...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
|
|
783
730
|
config: params.config,
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
731
|
+
elevated: params.bashElevated
|
|
732
|
+
},
|
|
733
|
+
sessionPermissionPolicy: input.sessionPermissionPolicy ? {
|
|
734
|
+
mode: input.sessionPermissionPolicy.mode,
|
|
735
|
+
root: input.sessionPermissionPolicy.root
|
|
736
|
+
} : void 0,
|
|
737
|
+
sandbox: input.sandbox,
|
|
738
|
+
...toolConstructionPlan ? { toolConstructionPlan } : {},
|
|
739
|
+
messageProvider: resolveCodexMessageToolProvider(params),
|
|
740
|
+
toolPolicyMessageProvider: params.messageProvider ?? params.messageChannel,
|
|
741
|
+
clientCaps: params.clientCaps,
|
|
742
|
+
chatType: params.chatType,
|
|
743
|
+
agentAccountId: params.agentAccountId,
|
|
744
|
+
messageTo: params.messageTo,
|
|
745
|
+
messageThreadId: params.messageThreadId,
|
|
746
|
+
nativeChannelId: params.chatId,
|
|
747
|
+
messageActionTurnCapability: params.messageActionTurnCapability,
|
|
748
|
+
groupId: params.groupId,
|
|
749
|
+
groupChannel: params.groupChannel,
|
|
750
|
+
groupSpace: params.groupSpace,
|
|
751
|
+
spawnedBy: params.spawnedBy,
|
|
752
|
+
senderId: params.senderId,
|
|
753
|
+
senderName: params.senderName,
|
|
754
|
+
senderUsername: params.senderUsername,
|
|
755
|
+
senderE164: params.senderE164,
|
|
756
|
+
senderIsOwner: params.senderIsOwner,
|
|
757
|
+
inputProvenance: params.inputProvenance,
|
|
758
|
+
trustedInternalHandoff: params.trustedInternalHandoff,
|
|
759
|
+
scheduledToolPolicy: params.scheduledToolPolicy,
|
|
760
|
+
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
|
|
761
|
+
sessionKey: input.sandboxSessionKey,
|
|
762
|
+
runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
|
|
763
|
+
sessionId: params.sessionId,
|
|
764
|
+
runId: params.runId,
|
|
765
|
+
approvalReviewerDeviceId: params.approvalReviewerDeviceId,
|
|
766
|
+
agentDir,
|
|
767
|
+
preparedModelRuntime: params.preparedModelRuntime,
|
|
768
|
+
cwd: input.effectiveCwd ?? input.effectiveWorkspace,
|
|
769
|
+
workspaceDir: input.effectiveWorkspace,
|
|
770
|
+
spawnWorkspaceDir: input.effectiveCwd && input.effectiveCwd !== input.effectiveWorkspace ? input.resolvedWorkspace : resolveAttemptSpawnWorkspaceDir({
|
|
771
|
+
sandbox: input.sandbox,
|
|
772
|
+
resolvedWorkspace: input.resolvedWorkspace
|
|
773
|
+
}),
|
|
774
|
+
config: params.config,
|
|
775
|
+
skillsSnapshot: params.skillsSnapshot,
|
|
776
|
+
...params.skillLibraryAuthoring ? { skillWorkshop: { libraryAuthoring: params.skillLibraryAuthoring } } : {},
|
|
777
|
+
githubPublicationAvailable: params.githubPublicationAvailable,
|
|
778
|
+
authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
|
|
779
|
+
abortSignal: input.runAbortController.signal,
|
|
780
|
+
emitBeforeToolCallDiagnostics: false,
|
|
781
|
+
modelProvider: params.model.provider,
|
|
782
|
+
modelId: params.modelId,
|
|
783
|
+
modelCompat: params.model.compat && typeof params.model.compat === "object" ? params.model.compat : void 0,
|
|
784
|
+
modelApi: params.model.api,
|
|
785
|
+
modelContextWindowTokens: params.model.contextWindow,
|
|
786
|
+
delegationCapability: params.delegationCapability,
|
|
787
|
+
modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
|
|
788
|
+
suppressManagedWebSearch: false,
|
|
789
|
+
webFetchHostnameAllowlistRef,
|
|
790
|
+
currentChannelId: params.currentChannelId,
|
|
791
|
+
currentMessagingTarget: params.currentMessagingTarget,
|
|
792
|
+
hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
|
|
793
|
+
currentThreadTs: params.currentThreadTs,
|
|
794
|
+
currentMessageId: params.currentMessageId,
|
|
795
|
+
replyToMode: params.replyToMode,
|
|
796
|
+
hasRepliedRef: params.hasRepliedRef,
|
|
797
|
+
modelHasVision,
|
|
798
|
+
computerContextEpoch: input.computerContextEpoch,
|
|
799
|
+
registerRunCleanup: input.registerRunCleanup,
|
|
800
|
+
requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
|
|
801
|
+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
802
|
+
taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
|
|
803
|
+
disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
|
|
804
|
+
forceMessageTool: shouldForceMessageTool(messagePolicyParams),
|
|
805
|
+
enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
|
|
806
|
+
forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
|
|
807
|
+
onYield: (message, acknowledgment) => {
|
|
808
|
+
input.onYieldDetected(acknowledgment);
|
|
809
|
+
input.onCodexAppServerEvent?.({
|
|
810
|
+
stream: "codex_app_server.tool",
|
|
811
|
+
data: {
|
|
812
|
+
name: "sessions_yield",
|
|
813
|
+
message
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
},
|
|
817
|
+
claimYieldCompletion: input.claimYieldCompletion,
|
|
818
|
+
recordToolPrepStage: (name) => {
|
|
819
|
+
toolBuildStages.mark(name);
|
|
820
|
+
},
|
|
821
|
+
onToolOutcome: params.onToolOutcome,
|
|
822
|
+
isTurnTainted: params.isTurnTainted,
|
|
823
|
+
allocateToolOutcomeOrdinal: params.allocateToolOutcomeOrdinal,
|
|
824
|
+
cronCreatorToolAllowlistRef: input.cronCreatorToolAllowlistRef,
|
|
825
|
+
cronCreatorToolAllowlistCaptureRef: input.cronCreatorToolAllowlistCaptureRef,
|
|
826
|
+
cronCreatorAuthorityUnavailableReason: input.cronCreatorAuthorityUnavailableReason
|
|
827
|
+
};
|
|
828
|
+
const buildOpenClawCodingTools = () => {
|
|
835
829
|
const bindingOptions = { cwd: input.effectiveCwd ?? input.effectiveWorkspace };
|
|
836
830
|
if (injectedOpenClawCodingToolsFactory) return params.hostCapabilities.bindToolSurface(injectedOpenClawCodingToolsFactory(options), bindingOptions);
|
|
837
831
|
const createToolSurface = params.hostCapabilities.createToolSurface;
|
|
@@ -851,7 +845,7 @@ async function buildDynamicTools(input) {
|
|
|
851
845
|
const readableAllTools = [...readableAllToolProjection.tools];
|
|
852
846
|
const normallyProfiledTools = input.nativeToolSurfaceEnabled === false ? filterCodexDynamicToolsForDisabledNativeSurface(readableAllTools, input.pluginConfig, { preserveShell: shouldKeepOpenClawShellDynamicTools(input, nativeExecutionPolicy) }) : filterCodexDynamicTools(readableAllTools, input.pluginConfig);
|
|
853
847
|
const profileFilteredTools = (input.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) ? preserveRingZeroSystemAgentTool(readableAllTools, normallyProfiledTools) : normallyProfiledTools;
|
|
854
|
-
const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addGatewayShellDynamicToolsIfAvailable(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy);
|
|
848
|
+
const codexFilteredTools = await addNodeShellDynamicToolsIfNeeded(addGatewayShellDynamicToolsIfAvailable(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy);
|
|
855
849
|
toolBuildStages.mark("codex-filtering");
|
|
856
850
|
const visionFilteredTools = filterCodexVisionTools(codexFilteredTools, {
|
|
857
851
|
modelHasVision,
|
|
@@ -905,7 +899,7 @@ async function buildDynamicTools(input) {
|
|
|
905
899
|
const webSearchAllowed = normalizedTools.some((tool) => tool.name === "web_search");
|
|
906
900
|
webFetchHostnameAllowlistRef.value = webSearchAllowed ? webSearchPlan.webFetchHostnameAllowlist : void 0;
|
|
907
901
|
input.onWebSearchPolicyResolved?.(webSearchAllowed);
|
|
908
|
-
const exposedTools = webSearchPlan.suppressManagedWebSearch ? normalizedTools.filter((tool) => tool.name !== "web_search") : normalizedTools;
|
|
902
|
+
const exposedTools = placeDisabledNativeShellToolsInDirectNamespace(webSearchPlan.suppressManagedWebSearch ? normalizedTools.filter((tool) => tool.name !== "web_search") : normalizedTools, input.nativeToolSurfaceEnabled);
|
|
909
903
|
if (preNormalizationDiagnostics.length > 0) embeddedAgentLog.warn(`codex app-server quarantined ${preNormalizationDiagnostics.length} unsupported runtime tool schema${preNormalizationDiagnostics.length === 1 ? "" : "s"} before dynamic tool registration`, {
|
|
910
904
|
runId: params.runId,
|
|
911
905
|
sessionId: params.sessionId,
|
|
@@ -1078,19 +1072,25 @@ function isSandboxShellDynamicToolExcluded(config) {
|
|
|
1078
1072
|
"sandbox_process"
|
|
1079
1073
|
]);
|
|
1080
1074
|
}
|
|
1081
|
-
function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePolicy) {
|
|
1075
|
+
async function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePolicy) {
|
|
1082
1076
|
if (isCodexMemoryFlushRun(input.params)) return filteredTools;
|
|
1083
1077
|
const nodeExecIsDefault = nodePolicy.effectiveExecHost === "node";
|
|
1084
1078
|
const nodeExecAvailableFromAuto = nodePolicy.requestedExecHost === "auto" && nodePolicy.effectiveExecHost === "gateway";
|
|
1085
1079
|
if (!nodeExecIsDefault && !nodeExecAvailableFromAuto) return filteredTools;
|
|
1086
1080
|
const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
|
|
1087
|
-
if (!execTool) return filteredTools;
|
|
1088
|
-
|
|
1089
|
-
return filteredTools;
|
|
1081
|
+
if (!execTool || isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) || filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return filteredTools;
|
|
1082
|
+
const nodeExec = await createNodeExecAliasDynamicTool(execTool, nodePolicy.node, input.runAbortController.signal, input.nodeExecAvailability);
|
|
1083
|
+
return nodeExec ? [...filteredTools, nodeExec] : filteredTools;
|
|
1090
1084
|
}
|
|
1091
1085
|
function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
|
|
1092
1086
|
return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
|
|
1093
1087
|
}
|
|
1088
|
+
/** Keeps replacement shell tools direct even when model metadata mandates Codex Code Mode. */
|
|
1089
|
+
function placeDisabledNativeShellToolsInDirectNamespace(tools, nativeToolSurfaceEnabled) {
|
|
1090
|
+
if (nativeToolSurfaceEnabled !== false) return tools;
|
|
1091
|
+
for (const tool of tools) if (CODEX_DISABLED_NATIVE_SHELL_DYNAMIC_TOOLS.has(normalizeCodexDynamicToolName(tool.name))) tool.catalogMode = "direct-only";
|
|
1092
|
+
return tools;
|
|
1093
|
+
}
|
|
1094
1094
|
/** Applies a normalized tool allowlist while preserving shell aliases for exec/process. */
|
|
1095
1095
|
function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
|
|
1096
1096
|
if (!toolsAllow) return tools;
|
|
@@ -3257,52 +3257,6 @@ async function handleMessage(session, data) {
|
|
|
3257
3257
|
await session.handleRequest(parseRequest(buffer.toString("utf8")));
|
|
3258
3258
|
}
|
|
3259
3259
|
//#endregion
|
|
3260
|
-
//#region extensions/codex/src/app-server/provider-capabilities.ts
|
|
3261
|
-
function resolveOverriddenProviderWebSearchSupport(modelProviderOverride) {
|
|
3262
|
-
const provider = modelProviderOverride?.trim().toLowerCase();
|
|
3263
|
-
if (!provider) return;
|
|
3264
|
-
return provider === "openai" ? "supported" : "unsupported";
|
|
3265
|
-
}
|
|
3266
|
-
async function readConfiguredProviderWebSearchSupport(params) {
|
|
3267
|
-
return (await params.client.request("modelProvider/capabilities/read", {}, {
|
|
3268
|
-
timeoutMs: params.timeoutMs,
|
|
3269
|
-
signal: params.signal
|
|
3270
|
-
})).webSearch ? "supported" : "unsupported";
|
|
3271
|
-
}
|
|
3272
|
-
async function resolveCodexProviderWebSearchSupportForClient(params) {
|
|
3273
|
-
const overrideSupport = resolveOverriddenProviderWebSearchSupport(params.modelProviderOverride);
|
|
3274
|
-
if (overrideSupport) return overrideSupport;
|
|
3275
|
-
try {
|
|
3276
|
-
return await readConfiguredProviderWebSearchSupport(params);
|
|
3277
|
-
} catch {
|
|
3278
|
-
return "unknown";
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
async function resolveCodexProviderWebSearchSupport(params) {
|
|
3282
|
-
const overrideSupport = resolveOverriddenProviderWebSearchSupport(params.modelProviderOverride);
|
|
3283
|
-
if (overrideSupport) return overrideSupport;
|
|
3284
|
-
let client;
|
|
3285
|
-
try {
|
|
3286
|
-
client = await params.clientFactory({
|
|
3287
|
-
startOptions: params.appServer.start,
|
|
3288
|
-
...params.preparedAuth ? { preparedAuth: params.preparedAuth } : { authProfileId: params.authProfileId },
|
|
3289
|
-
agentDir: params.agentDir,
|
|
3290
|
-
config: params.config,
|
|
3291
|
-
timeoutMs: params.appServer.requestTimeoutMs
|
|
3292
|
-
});
|
|
3293
|
-
return await resolveCodexProviderWebSearchSupportForClient({
|
|
3294
|
-
client,
|
|
3295
|
-
timeoutMs: params.appServer.requestTimeoutMs,
|
|
3296
|
-
modelProviderOverride: params.modelProviderOverride,
|
|
3297
|
-
signal: params.signal
|
|
3298
|
-
});
|
|
3299
|
-
} catch {
|
|
3300
|
-
return "unknown";
|
|
3301
|
-
} finally {
|
|
3302
|
-
if (client) releaseLeasedSharedCodexAppServerClient(client);
|
|
3303
|
-
}
|
|
3304
|
-
}
|
|
3305
|
-
//#endregion
|
|
3306
3260
|
//#region extensions/codex/src/app-server/approval-bridge.ts
|
|
3307
3261
|
/**
|
|
3308
3262
|
* Bridges Codex app-server approval requests into OpenClaw policy hooks and
|
|
@@ -4211,16 +4165,63 @@ async function routeCodexAppServerElicitationRequest(params) {
|
|
|
4211
4165
|
signal: params.signal
|
|
4212
4166
|
}));
|
|
4213
4167
|
}
|
|
4214
|
-
const
|
|
4168
|
+
const computerUsePrompt = readComputerUseApprovalElicitation(requestParams, params.computerUseMcpServerName);
|
|
4169
|
+
const approvalPrompt = computerUsePrompt ?? readBridgeableApprovalElicitation(requestParams);
|
|
4215
4170
|
if (!approvalPrompt) return handled(createCodexElicitationResponse("decline"));
|
|
4171
|
+
let persistence;
|
|
4172
|
+
if (!computerUsePrompt) {
|
|
4173
|
+
const serverName = readNonBlankStringField(requestParams, "serverName");
|
|
4174
|
+
const server = serverName ? params.paramsForRun.config?.mcp?.servers?.[serverName] : void 0;
|
|
4175
|
+
const mode = serverName ? resolveProjectedMcpCodexToolApprovalMode(serverName, server ?? {}, params.projectedMcpServers?.[serverName]) : void 0;
|
|
4176
|
+
if (!requiresMcpCodexToolApproval({
|
|
4177
|
+
mode,
|
|
4178
|
+
fullPermission: params.autoApproveMcpTools
|
|
4179
|
+
})) {
|
|
4180
|
+
params.paramsForRun.hostCapabilities.assertActive();
|
|
4181
|
+
return handled(buildElicitationResponse(approvalPrompt, "approved-once"));
|
|
4182
|
+
}
|
|
4183
|
+
if (mode === "prompt") approvalPrompt.allowedDecisions = ["allow-once", "deny"];
|
|
4184
|
+
else if (serverName && serverName !== CODEX_APPS_SERVER_NAME && Object.hasOwn(params.paramsForRun.config?.mcp?.servers ?? {}, serverName) && requestTurnId === params.turnId && readPersistHints(approvalPrompt.meta, "explicit").includes("always")) {
|
|
4185
|
+
const resolveItem = () => {
|
|
4186
|
+
const item = params.getActiveMcpToolCall?.(serverName);
|
|
4187
|
+
return item?.server === serverName && matchesMcpApprovalDisplay(item, approvalPrompt.meta) ? item : void 0;
|
|
4188
|
+
};
|
|
4189
|
+
const item = resolveItem();
|
|
4190
|
+
if (item) persistence = {
|
|
4191
|
+
mcpTool: {
|
|
4192
|
+
server: serverName,
|
|
4193
|
+
tool: item.tool
|
|
4194
|
+
},
|
|
4195
|
+
toolCallId: item.id,
|
|
4196
|
+
isMcpToolApprovalActive: () => {
|
|
4197
|
+
const current = resolveItem();
|
|
4198
|
+
return current?.id === item.id && current.tool === item.tool;
|
|
4199
|
+
}
|
|
4200
|
+
};
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4216
4203
|
return handled(buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
|
|
4217
|
-
|
|
4204
|
+
hostCapabilities: params.paramsForRun.hostCapabilities,
|
|
4218
4205
|
title: approvalPrompt.title,
|
|
4219
4206
|
description: approvalPrompt.description,
|
|
4220
4207
|
allowedDecisions: approvalPrompt.allowedDecisions,
|
|
4208
|
+
toolName: "codex_mcp_tool_approval",
|
|
4209
|
+
...persistence,
|
|
4221
4210
|
signal: params.signal
|
|
4222
4211
|
})));
|
|
4223
4212
|
}
|
|
4213
|
+
function matchesMcpApprovalDisplay(item, meta) {
|
|
4214
|
+
if (!Object.hasOwn(meta, MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY)) return true;
|
|
4215
|
+
const display = meta[MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY];
|
|
4216
|
+
if (!Array.isArray(display)) return false;
|
|
4217
|
+
const args = item.arguments;
|
|
4218
|
+
return display.every((param) => {
|
|
4219
|
+
if (!isJsonObject(param) || typeof param.name !== "string" || !isJsonObject(args)) return false;
|
|
4220
|
+
if (!Object.hasOwn(args, param.name)) return false;
|
|
4221
|
+
const value = args[param.name];
|
|
4222
|
+
return typeof param.value !== "string" || param.value === (typeof value === "string" ? value : JSON.stringify(value));
|
|
4223
|
+
});
|
|
4224
|
+
}
|
|
4224
4225
|
function handled(response) {
|
|
4225
4226
|
return {
|
|
4226
4227
|
kind: "handled",
|
|
@@ -4336,14 +4337,14 @@ async function buildPluginPolicyElicitationResponse(params) {
|
|
|
4336
4337
|
const response = buildElicitationResponse(approvalPrompt, "approved-once");
|
|
4337
4338
|
if (response.action === "accept") {
|
|
4338
4339
|
if (mode === "allow") return response;
|
|
4339
|
-
|
|
4340
|
-
|
|
4340
|
+
return buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
|
|
4341
|
+
hostCapabilities: params.paramsForRun.hostCapabilities,
|
|
4341
4342
|
title: approvalPrompt.title,
|
|
4342
4343
|
description: approvalPrompt.description,
|
|
4343
4344
|
allowedDecisions: allowedPluginPolicyApprovalDecisions(mode, approvalPrompt),
|
|
4345
|
+
toolName: "codex_mcp_tool_approval",
|
|
4344
4346
|
signal: params.signal
|
|
4345
|
-
});
|
|
4346
|
-
return buildElicitationResponse(approvalPrompt, mode === "ask" && outcome === "approved-session" ? "approved-once" : outcome);
|
|
4347
|
+
}));
|
|
4347
4348
|
}
|
|
4348
4349
|
logPluginElicitationDecline("unmappable_schema", params.requestParams);
|
|
4349
4350
|
return createCodexElicitationResponse("decline");
|
|
@@ -4378,15 +4379,16 @@ function readPluginApprovalElicitation(entry, requestParams) {
|
|
|
4378
4379
|
allowedDecisions: buildApprovalAllowedDecisions(requestedSchema, meta)
|
|
4379
4380
|
};
|
|
4380
4381
|
}
|
|
4381
|
-
function buildApprovalAllowedDecisions(requestedSchema, meta) {
|
|
4382
|
-
return canMapPersistentApproval(requestedSchema, meta) ? [
|
|
4382
|
+
function buildApprovalAllowedDecisions(requestedSchema, meta, allowSession = false) {
|
|
4383
|
+
return canMapPersistentApproval(requestedSchema, meta, allowSession) ? [
|
|
4383
4384
|
"allow-once",
|
|
4384
4385
|
"allow-always",
|
|
4385
4386
|
"deny"
|
|
4386
4387
|
] : ["allow-once", "deny"];
|
|
4387
4388
|
}
|
|
4388
|
-
function canMapPersistentApproval(requestedSchema, meta) {
|
|
4389
|
+
function canMapPersistentApproval(requestedSchema, meta, allowSession) {
|
|
4389
4390
|
const persistHints = readPersistHints(meta, "explicit");
|
|
4391
|
+
if (allowSession) return choosePersistHint(persistHints) !== void 0;
|
|
4390
4392
|
if (persistHints.length > 0) return persistHints.includes("always");
|
|
4391
4393
|
const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
|
|
4392
4394
|
return Object.entries(properties).some(([name, value]) => {
|
|
@@ -4411,16 +4413,20 @@ function readBridgeableApprovalElicitation(requestParams) {
|
|
|
4411
4413
|
const requestedSchema = requestParams.requestedSchema;
|
|
4412
4414
|
if (readNonBlankStringField(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
4413
4415
|
const title = sanitizeDisplayText(readNonBlankStringField(requestParams, "message") ?? "") || "Codex MCP tool approval";
|
|
4416
|
+
const serverName = readNonBlankStringField(requestParams, "serverName");
|
|
4414
4417
|
return {
|
|
4415
4418
|
title,
|
|
4416
4419
|
description: buildApprovalDescription({
|
|
4417
4420
|
title,
|
|
4418
4421
|
meta: requestParams["_meta"],
|
|
4419
4422
|
requestedSchema,
|
|
4420
|
-
serverName: sanitizeOptionalDisplayText(
|
|
4423
|
+
serverName: sanitizeOptionalDisplayText(serverName),
|
|
4424
|
+
remedy: serverName ? formatMcpCodexApprovalRemedy(serverName) : void 0
|
|
4421
4425
|
}),
|
|
4422
4426
|
requestedSchema,
|
|
4423
|
-
meta: requestParams["_meta"]
|
|
4427
|
+
meta: requestParams["_meta"],
|
|
4428
|
+
persistHintsMode: "explicit",
|
|
4429
|
+
allowedDecisions: buildApprovalAllowedDecisions(requestedSchema, requestParams["_meta"], true)
|
|
4424
4430
|
};
|
|
4425
4431
|
}
|
|
4426
4432
|
function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
|
|
@@ -4450,6 +4456,7 @@ function buildApprovalDescription(params) {
|
|
|
4450
4456
|
connectorName && `App: ${connectorName}`,
|
|
4451
4457
|
toolTitle && `Tool: ${toolTitle}`,
|
|
4452
4458
|
params.serverName && `MCP server: ${params.serverName}`,
|
|
4459
|
+
params.remedy,
|
|
4453
4460
|
toolDescription
|
|
4454
4461
|
].filter((line) => Boolean(line));
|
|
4455
4462
|
const paramLines = readDisplayParamLines(params.meta);
|
|
@@ -4530,36 +4537,10 @@ function sanitizeDisplayText(value) {
|
|
|
4530
4537
|
const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
|
|
4531
4538
|
return clipped && escaped ? `${escaped}...` : escaped;
|
|
4532
4539
|
}
|
|
4533
|
-
async function requestPluginApprovalOutcome(params) {
|
|
4534
|
-
try {
|
|
4535
|
-
const requestResult = await requestPluginApproval({
|
|
4536
|
-
hostCapabilities: params.paramsForRun.hostCapabilities,
|
|
4537
|
-
signal: params.signal,
|
|
4538
|
-
title: params.title,
|
|
4539
|
-
description: params.description,
|
|
4540
|
-
severity: "warning",
|
|
4541
|
-
toolName: "codex_mcp_tool_approval",
|
|
4542
|
-
allowedDecisions: params.allowedDecisions
|
|
4543
|
-
});
|
|
4544
|
-
const approvalId = requestResult?.id;
|
|
4545
|
-
if (!approvalId) return "unavailable";
|
|
4546
|
-
const approvalResult = approvalRequestExplicitlyUnavailable(requestResult) ? void 0 : await waitForPluginApprovalDecision({
|
|
4547
|
-
hostCapabilities: params.paramsForRun.hostCapabilities,
|
|
4548
|
-
approvalId,
|
|
4549
|
-
signal: params.signal
|
|
4550
|
-
});
|
|
4551
|
-
if (params.signal?.aborted) return "cancelled";
|
|
4552
|
-
if (approvalResult?.terminalReason === "timeout") return "timed-out";
|
|
4553
|
-
return mapExecDecisionToOutcome(approvalResult?.decision);
|
|
4554
|
-
} catch {
|
|
4555
|
-
return params.signal?.aborted ? "cancelled" : "denied";
|
|
4556
|
-
}
|
|
4557
|
-
}
|
|
4558
4540
|
function buildElicitationResponse(approvalPrompt, outcome) {
|
|
4559
4541
|
const { requestedSchema, meta } = approvalPrompt;
|
|
4560
4542
|
if (outcome === "cancelled") return createCodexElicitationResponse("cancel");
|
|
4561
|
-
if (outcome === "timed-out"
|
|
4562
|
-
if (outcome === "denied" || outcome === "unavailable") return createCodexElicitationResponse("decline");
|
|
4543
|
+
if (outcome === "timed-out" || outcome === "denied" || outcome === "unavailable") return createCodexElicitationResponse("decline");
|
|
4563
4544
|
const content = buildAcceptedContent(approvalPrompt, outcome);
|
|
4564
4545
|
if (!content && !hasNoSchemaProperties(requestedSchema)) {
|
|
4565
4546
|
embeddedAgentLog.warn("codex MCP approval elicitation approved without a mappable response", {
|
|
@@ -4586,7 +4567,7 @@ function buildAcceptedContent(approvalPrompt, outcome) {
|
|
|
4586
4567
|
schema,
|
|
4587
4568
|
required: required.has(name)
|
|
4588
4569
|
};
|
|
4589
|
-
const next = readApprovalFieldValue(property, outcome) ?? readPersistFieldValue(property, meta, outcome, approvalPrompt.persistHintsMode ?? "legacy") ?? readFallbackFieldValue(property, outcome);
|
|
4570
|
+
const next = readApprovalFieldValue(property, outcome, choosePersistHint(readPersistHints(meta, approvalPrompt.persistHintsMode))) ?? readPersistFieldValue(property, meta, outcome, approvalPrompt.persistHintsMode ?? "legacy") ?? readFallbackFieldValue(property, outcome);
|
|
4590
4571
|
if (next === void 0) {
|
|
4591
4572
|
if (isApprovalField(property)) sawApprovalField = true;
|
|
4592
4573
|
if (property.required) return;
|
|
@@ -4597,15 +4578,14 @@ function buildAcceptedContent(approvalPrompt, outcome) {
|
|
|
4597
4578
|
}
|
|
4598
4579
|
return sawApprovalField ? content : void 0;
|
|
4599
4580
|
}
|
|
4600
|
-
function readApprovalFieldValue(property, outcome) {
|
|
4581
|
+
function readApprovalFieldValue(property, outcome, persist) {
|
|
4601
4582
|
if (!isApprovalField(property)) return;
|
|
4602
4583
|
if (readNonBlankStringField(property.schema, "type") === "boolean") return true;
|
|
4603
4584
|
const options = readEnumOptions(property.schema);
|
|
4604
4585
|
if (options.length === 0) return;
|
|
4605
|
-
const sessionChoice = options.find((option) => isSessionApprovalOption(option));
|
|
4606
4586
|
const acceptChoice = options.find((option) => isPositiveApprovalOption(option));
|
|
4607
|
-
if (outcome === "approved-session") return
|
|
4608
|
-
return acceptChoice?.value
|
|
4587
|
+
if (outcome === "approved-session") return options.find((option) => isPersistentApprovalOption(option, persist))?.value ?? acceptChoice?.value;
|
|
4588
|
+
return acceptChoice?.value;
|
|
4609
4589
|
}
|
|
4610
4590
|
function readPersistFieldValue(property, meta, outcome, persistHintsMode) {
|
|
4611
4591
|
if (!isPersistField(property) || outcome !== "approved-session") return;
|
|
@@ -4684,9 +4664,9 @@ function isPositiveApprovalOption(option) {
|
|
|
4684
4664
|
const haystack = `${option.value} ${option.label}`.toLowerCase();
|
|
4685
4665
|
return /\b(allow|approve|accept|yes|continue|proceed|true)\b/.test(haystack);
|
|
4686
4666
|
}
|
|
4687
|
-
function
|
|
4667
|
+
function isPersistentApprovalOption(option, persist) {
|
|
4688
4668
|
const haystack = `${option.value} ${option.label}`.toLowerCase();
|
|
4689
|
-
return /\b(
|
|
4669
|
+
return (persist === "always" ? /\b(always|persistent)\b|\bdon't ask me again\b/.test(haystack) : persist === "session" && /\bsession\b/.test(haystack)) && /\b(allow|approve|accept)\b/.test(haystack);
|
|
4690
4670
|
}
|
|
4691
4671
|
function readNonBlankStringField(record, key) {
|
|
4692
4672
|
const value = record?.[key];
|
|
@@ -4699,6 +4679,191 @@ function readFirstString$1(record, keys) {
|
|
|
4699
4679
|
}
|
|
4700
4680
|
}
|
|
4701
4681
|
//#endregion
|
|
4682
|
+
//#region extensions/codex/src/app-server/dynamic-tool-catalog.ts
|
|
4683
|
+
/** Namespace attached to OpenClaw-owned dynamic tools exposed to Codex. */
|
|
4684
|
+
const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
|
|
4685
|
+
const CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS = 128;
|
|
4686
|
+
const CODEX_DYNAMIC_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/u;
|
|
4687
|
+
const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
4688
|
+
"agents_list",
|
|
4689
|
+
"progress_card",
|
|
4690
|
+
"sessions_spawn",
|
|
4691
|
+
"sessions_yield"
|
|
4692
|
+
]);
|
|
4693
|
+
function createCodexDynamicToolSpecs(params) {
|
|
4694
|
+
const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
|
|
4695
|
+
const specs = [];
|
|
4696
|
+
const namespaceTools = [];
|
|
4697
|
+
const directOnlyNamespaceTools = [];
|
|
4698
|
+
const entries = params.loading === "direct" ? params.entries : params.entries.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
4699
|
+
for (const entry of entries) {
|
|
4700
|
+
const functionSpec = createCodexDynamicToolFunctionSpec({ entry });
|
|
4701
|
+
if (entry.name === "openclaw" && directToolNames.has(entry.name)) {
|
|
4702
|
+
specs.push(functionSpec);
|
|
4703
|
+
continue;
|
|
4704
|
+
}
|
|
4705
|
+
if (entry.tool.catalogMode === "direct-only") {
|
|
4706
|
+
directOnlyNamespaceTools.push(functionSpec);
|
|
4707
|
+
continue;
|
|
4708
|
+
}
|
|
4709
|
+
if (params.loading === "direct" || directToolNames.has(entry.name)) {
|
|
4710
|
+
specs.push(functionSpec);
|
|
4711
|
+
continue;
|
|
4712
|
+
}
|
|
4713
|
+
namespaceTools.push({
|
|
4714
|
+
...functionSpec,
|
|
4715
|
+
deferLoading: true
|
|
4716
|
+
});
|
|
4717
|
+
}
|
|
4718
|
+
if (namespaceTools.length > 0) specs.push({
|
|
4719
|
+
type: "namespace",
|
|
4720
|
+
name: CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
|
|
4721
|
+
description: "",
|
|
4722
|
+
tools: namespaceTools
|
|
4723
|
+
});
|
|
4724
|
+
if (directOnlyNamespaceTools.length > 0) specs.push({
|
|
4725
|
+
type: "namespace",
|
|
4726
|
+
name: CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE,
|
|
4727
|
+
description: "",
|
|
4728
|
+
tools: directOnlyNamespaceTools
|
|
4729
|
+
});
|
|
4730
|
+
return specs;
|
|
4731
|
+
}
|
|
4732
|
+
function createCodexDynamicToolFunctionSpec(params) {
|
|
4733
|
+
return {
|
|
4734
|
+
type: "function",
|
|
4735
|
+
name: params.entry.name,
|
|
4736
|
+
description: params.entry.description,
|
|
4737
|
+
inputSchema: params.entry.inputSchema
|
|
4738
|
+
};
|
|
4739
|
+
}
|
|
4740
|
+
function projectCodexDynamicTools(tools) {
|
|
4741
|
+
const projectedTools = [];
|
|
4742
|
+
const quarantinedTools = [];
|
|
4743
|
+
let length;
|
|
4744
|
+
try {
|
|
4745
|
+
length = tools.length;
|
|
4746
|
+
} catch {
|
|
4747
|
+
return {
|
|
4748
|
+
tools: [],
|
|
4749
|
+
quarantinedTools: [{
|
|
4750
|
+
tool: "tool[0]",
|
|
4751
|
+
violations: ["tool[0] is unreadable"]
|
|
4752
|
+
}]
|
|
4753
|
+
};
|
|
4754
|
+
}
|
|
4755
|
+
for (let toolIndex = 0; toolIndex < length; toolIndex += 1) {
|
|
4756
|
+
let tool;
|
|
4757
|
+
try {
|
|
4758
|
+
tool = tools[toolIndex];
|
|
4759
|
+
} catch {
|
|
4760
|
+
quarantinedTools.push({
|
|
4761
|
+
tool: `tool[${toolIndex}]`,
|
|
4762
|
+
violations: [`tool[${toolIndex}] is unreadable`]
|
|
4763
|
+
});
|
|
4764
|
+
continue;
|
|
4765
|
+
}
|
|
4766
|
+
const descriptor = readCodexDynamicToolDescriptor(tool, toolIndex);
|
|
4767
|
+
if (!descriptor.ok) {
|
|
4768
|
+
quarantinedTools.push(descriptor.diagnostic);
|
|
4769
|
+
continue;
|
|
4770
|
+
}
|
|
4771
|
+
const normalizedParameters = normalizeOpenAIStrictCompatSchema(descriptor.parameters);
|
|
4772
|
+
const projection = projectRuntimeToolInputSchema(normalizedParameters ?? descriptor.parameters, `${descriptor.name}.inputSchema`);
|
|
4773
|
+
if (projection.violations.length > 0) {
|
|
4774
|
+
quarantinedTools.push({
|
|
4775
|
+
tool: descriptor.name,
|
|
4776
|
+
violations: projection.violations
|
|
4777
|
+
});
|
|
4778
|
+
continue;
|
|
4779
|
+
}
|
|
4780
|
+
if (!isRecord(projection.schema)) {
|
|
4781
|
+
quarantinedTools.push({
|
|
4782
|
+
tool: descriptor.name,
|
|
4783
|
+
violations: [`${descriptor.name}.inputSchema must be a JSON object schema`]
|
|
4784
|
+
});
|
|
4785
|
+
continue;
|
|
4786
|
+
}
|
|
4787
|
+
projectedTools.push({
|
|
4788
|
+
tool,
|
|
4789
|
+
name: descriptor.name,
|
|
4790
|
+
description: descriptor.description,
|
|
4791
|
+
inputSchema: projection.schema
|
|
4792
|
+
});
|
|
4793
|
+
}
|
|
4794
|
+
return {
|
|
4795
|
+
tools: projectedTools,
|
|
4796
|
+
quarantinedTools
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
function readCodexDynamicToolDescriptor(tool, toolIndex) {
|
|
4800
|
+
const fallbackName = `tool[${toolIndex}]`;
|
|
4801
|
+
let name;
|
|
4802
|
+
try {
|
|
4803
|
+
const rawName = tool.name;
|
|
4804
|
+
if (typeof rawName !== "string" || !rawName) return {
|
|
4805
|
+
ok: false,
|
|
4806
|
+
diagnostic: {
|
|
4807
|
+
tool: fallbackName,
|
|
4808
|
+
violations: [`${fallbackName}.name must be a non-empty string`]
|
|
4809
|
+
}
|
|
4810
|
+
};
|
|
4811
|
+
const trimmedName = rawName.trim();
|
|
4812
|
+
let nameViolation;
|
|
4813
|
+
if (!trimmedName) nameViolation = `${rawName}.name must not be empty`;
|
|
4814
|
+
else if (trimmedName !== rawName) nameViolation = `${rawName}.name must not have leading or trailing whitespace`;
|
|
4815
|
+
else if (!CODEX_DYNAMIC_TOOL_NAME_PATTERN.test(rawName)) nameViolation = `${rawName}.name must match ^[a-zA-Z0-9_-]+$`;
|
|
4816
|
+
else if (rawName.length > CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS) nameViolation = `${rawName}.name must be at most ${CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS} characters`;
|
|
4817
|
+
else if (rawName === "mcp" || rawName.startsWith("mcp__")) nameViolation = `${rawName}.name is reserved by Codex app-server`;
|
|
4818
|
+
if (nameViolation) return {
|
|
4819
|
+
ok: false,
|
|
4820
|
+
diagnostic: {
|
|
4821
|
+
tool: rawName,
|
|
4822
|
+
violations: [nameViolation]
|
|
4823
|
+
}
|
|
4824
|
+
};
|
|
4825
|
+
name = rawName;
|
|
4826
|
+
} catch {
|
|
4827
|
+
return {
|
|
4828
|
+
ok: false,
|
|
4829
|
+
diagnostic: {
|
|
4830
|
+
tool: fallbackName,
|
|
4831
|
+
violations: [`${fallbackName}.name is unreadable`]
|
|
4832
|
+
}
|
|
4833
|
+
};
|
|
4834
|
+
}
|
|
4835
|
+
let description;
|
|
4836
|
+
try {
|
|
4837
|
+
description = typeof tool.description === "string" ? tool.description : "";
|
|
4838
|
+
} catch {
|
|
4839
|
+
return {
|
|
4840
|
+
ok: false,
|
|
4841
|
+
diagnostic: {
|
|
4842
|
+
tool: name,
|
|
4843
|
+
violations: [`${name}.description is unreadable`]
|
|
4844
|
+
}
|
|
4845
|
+
};
|
|
4846
|
+
}
|
|
4847
|
+
let parameters;
|
|
4848
|
+
try {
|
|
4849
|
+
parameters = tool.parameters;
|
|
4850
|
+
} catch {
|
|
4851
|
+
return {
|
|
4852
|
+
ok: false,
|
|
4853
|
+
diagnostic: {
|
|
4854
|
+
tool: name,
|
|
4855
|
+
violations: [`${name}.inputSchema is unreadable`]
|
|
4856
|
+
}
|
|
4857
|
+
};
|
|
4858
|
+
}
|
|
4859
|
+
return {
|
|
4860
|
+
ok: true,
|
|
4861
|
+
name,
|
|
4862
|
+
description,
|
|
4863
|
+
parameters
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
//#endregion
|
|
4702
4867
|
//#region extensions/codex/src/app-server/dynamic-tools.ts
|
|
4703
4868
|
/**
|
|
4704
4869
|
* Bridges OpenClaw runtime tools into Codex app-server dynamic tool specs and
|
|
@@ -4862,15 +5027,6 @@ function normalizeAcceptedSessionSpawn(result) {
|
|
|
4862
5027
|
childSessionKey
|
|
4863
5028
|
} : null;
|
|
4864
5029
|
}
|
|
4865
|
-
/** Namespace attached to OpenClaw-owned dynamic tools exposed to Codex. */
|
|
4866
|
-
const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
|
|
4867
|
-
const CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS = 128;
|
|
4868
|
-
const CODEX_DYNAMIC_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/u;
|
|
4869
|
-
const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
4870
|
-
"agents_list",
|
|
4871
|
-
"sessions_spawn",
|
|
4872
|
-
"sessions_yield"
|
|
4873
|
-
]);
|
|
4874
5030
|
const EXPLICIT_MESSAGE_PROVIDER_KEYS = ["channel", "provider"];
|
|
4875
5031
|
const EXPLICIT_MESSAGE_TARGET_KEYS = [
|
|
4876
5032
|
"target",
|
|
@@ -4910,11 +5066,13 @@ function createCodexDynamicToolBridge(params) {
|
|
|
4910
5066
|
const toolResultMaxChars = typeof contextWindowTokens === "number" && Number.isFinite(contextWindowTokens) && contextWindowTokens > 0 ? Math.max(1, resolveLiveToolResultMaxChars({ contextWindowTokens })) : DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;
|
|
4911
5067
|
const availableProjection = projectCodexExecutableDynamicToolSurface(params.tools, params.hookContext);
|
|
4912
5068
|
const registeredProjection = params.registeredTools ? projectCodexDynamicTools(params.registeredTools) : availableProjection;
|
|
4913
|
-
const
|
|
5069
|
+
const inheritedSpecs = params.registeredSpecs ? structuredClone([...params.registeredSpecs]) : void 0;
|
|
5070
|
+
const inheritedNames = inheritedSpecs ? new Set(flattenCodexDynamicToolFunctions(inheritedSpecs).map((tool) => tool.name)) : void 0;
|
|
5071
|
+
const availableTools = availableProjection.tools.filter((entry) => !inheritedNames || inheritedNames.has(entry.name));
|
|
4914
5072
|
const quarantinedAvailableToolNames = new Set(availableProjection.quarantinedTools.map((tool) => tool.tool));
|
|
4915
5073
|
const registeredSpecTools = (params.registeredTools ? registeredProjection.tools : availableTools).filter((entry) => !quarantinedAvailableToolNames.has(entry.name));
|
|
4916
5074
|
const toolMap = new Map(availableTools.map((entry) => [entry.name, entry]));
|
|
4917
|
-
const registeredToolNames = new Set(registeredSpecTools.map((entry) => entry.name));
|
|
5075
|
+
const registeredToolNames = inheritedNames ?? new Set(registeredSpecTools.map((entry) => entry.name));
|
|
4918
5076
|
const quarantinedTools = dedupeQuarantinedDynamicTools([...availableProjection.quarantinedTools, ...registeredProjection.quarantinedTools]);
|
|
4919
5077
|
warnQuarantinedDynamicTools({
|
|
4920
5078
|
tools: quarantinedTools,
|
|
@@ -4947,16 +5105,23 @@ function createCodexDynamicToolBridge(params) {
|
|
|
4947
5105
|
};
|
|
4948
5106
|
const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
|
|
4949
5107
|
const executionSnapshotStates = /* @__PURE__ */ new Map();
|
|
4950
|
-
const directToolNames =
|
|
5108
|
+
const directToolNames = params.directToolNames;
|
|
4951
5109
|
let readRemoteWorkspaceFile;
|
|
4952
5110
|
return {
|
|
4953
5111
|
availableTools: availableTools.map((entry) => entry.tool),
|
|
4954
|
-
availableSpecs:
|
|
5112
|
+
availableSpecs: inheritedSpecs ? inheritedSpecs.flatMap((spec) => {
|
|
5113
|
+
if (spec.type === "function") return toolMap.has(spec.name) ? [spec] : [];
|
|
5114
|
+
const tools = spec.tools.filter((tool) => toolMap.has(tool.name));
|
|
5115
|
+
return tools.length ? [{
|
|
5116
|
+
...spec,
|
|
5117
|
+
tools
|
|
5118
|
+
}] : [];
|
|
5119
|
+
}) : createCodexDynamicToolSpecs({
|
|
4955
5120
|
entries: availableTools,
|
|
4956
5121
|
loading: params.loading ?? "searchable",
|
|
4957
5122
|
directToolNames
|
|
4958
5123
|
}),
|
|
4959
|
-
specs: createCodexDynamicToolSpecs({
|
|
5124
|
+
specs: inheritedSpecs ?? createCodexDynamicToolSpecs({
|
|
4960
5125
|
entries: registeredSpecTools,
|
|
4961
5126
|
loading: params.loading ?? "searchable",
|
|
4962
5127
|
directToolNames
|
|
@@ -5171,6 +5336,8 @@ function createCodexDynamicToolBridge(params) {
|
|
|
5171
5336
|
sideEffectEvidence: !replaySafe
|
|
5172
5337
|
});
|
|
5173
5338
|
} catch (error) {
|
|
5339
|
+
const trustedNoStart = consumeTrustedToolNoStartError(error);
|
|
5340
|
+
executionPrevented ||= trustedNoStart;
|
|
5174
5341
|
captureExecutionBoundary();
|
|
5175
5342
|
if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateComputerFrame(params.computerContextEpoch);
|
|
5176
5343
|
const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
|
|
@@ -5281,182 +5448,6 @@ function wrapProjectedCodexDynamicTools(tools, hookContext) {
|
|
|
5281
5448
|
quarantinedTools
|
|
5282
5449
|
};
|
|
5283
5450
|
}
|
|
5284
|
-
function createCodexDynamicToolSpecs(params) {
|
|
5285
|
-
const specs = [];
|
|
5286
|
-
const namespaceTools = [];
|
|
5287
|
-
const directOnlyNamespaceTools = [];
|
|
5288
|
-
const entries = params.loading === "direct" ? params.entries : params.entries.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
5289
|
-
for (const entry of entries) {
|
|
5290
|
-
const functionSpec = createCodexDynamicToolFunctionSpec({ entry });
|
|
5291
|
-
if (entry.name === "openclaw" && params.directToolNames.has(entry.name)) {
|
|
5292
|
-
specs.push(functionSpec);
|
|
5293
|
-
continue;
|
|
5294
|
-
}
|
|
5295
|
-
if (entry.tool.catalogMode === "direct-only") {
|
|
5296
|
-
directOnlyNamespaceTools.push(functionSpec);
|
|
5297
|
-
continue;
|
|
5298
|
-
}
|
|
5299
|
-
if (params.loading === "direct" || params.directToolNames.has(entry.name)) {
|
|
5300
|
-
specs.push(functionSpec);
|
|
5301
|
-
continue;
|
|
5302
|
-
}
|
|
5303
|
-
namespaceTools.push({
|
|
5304
|
-
...functionSpec,
|
|
5305
|
-
deferLoading: true
|
|
5306
|
-
});
|
|
5307
|
-
}
|
|
5308
|
-
if (namespaceTools.length > 0) specs.push({
|
|
5309
|
-
type: "namespace",
|
|
5310
|
-
name: CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
|
|
5311
|
-
description: "",
|
|
5312
|
-
tools: namespaceTools
|
|
5313
|
-
});
|
|
5314
|
-
if (directOnlyNamespaceTools.length > 0) specs.push({
|
|
5315
|
-
type: "namespace",
|
|
5316
|
-
name: CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE,
|
|
5317
|
-
description: "",
|
|
5318
|
-
tools: directOnlyNamespaceTools
|
|
5319
|
-
});
|
|
5320
|
-
return specs;
|
|
5321
|
-
}
|
|
5322
|
-
function createCodexDynamicToolFunctionSpec(params) {
|
|
5323
|
-
return {
|
|
5324
|
-
type: "function",
|
|
5325
|
-
name: params.entry.name,
|
|
5326
|
-
description: params.entry.description,
|
|
5327
|
-
inputSchema: params.entry.inputSchema
|
|
5328
|
-
};
|
|
5329
|
-
}
|
|
5330
|
-
function projectCodexDynamicTools(tools) {
|
|
5331
|
-
const projectedTools = [];
|
|
5332
|
-
const quarantinedTools = [];
|
|
5333
|
-
let length;
|
|
5334
|
-
try {
|
|
5335
|
-
length = tools.length;
|
|
5336
|
-
} catch {
|
|
5337
|
-
return {
|
|
5338
|
-
tools: [],
|
|
5339
|
-
quarantinedTools: [{
|
|
5340
|
-
tool: "tool[0]",
|
|
5341
|
-
violations: ["tool[0] is unreadable"]
|
|
5342
|
-
}]
|
|
5343
|
-
};
|
|
5344
|
-
}
|
|
5345
|
-
for (let toolIndex = 0; toolIndex < length; toolIndex += 1) {
|
|
5346
|
-
let tool;
|
|
5347
|
-
try {
|
|
5348
|
-
tool = tools[toolIndex];
|
|
5349
|
-
} catch {
|
|
5350
|
-
quarantinedTools.push({
|
|
5351
|
-
tool: `tool[${toolIndex}]`,
|
|
5352
|
-
violations: [`tool[${toolIndex}] is unreadable`]
|
|
5353
|
-
});
|
|
5354
|
-
continue;
|
|
5355
|
-
}
|
|
5356
|
-
const descriptor = readCodexDynamicToolDescriptor(tool, toolIndex);
|
|
5357
|
-
if (!descriptor.ok) {
|
|
5358
|
-
quarantinedTools.push(descriptor.diagnostic);
|
|
5359
|
-
continue;
|
|
5360
|
-
}
|
|
5361
|
-
const normalizedParameters = normalizeOpenAIToolSchemas({
|
|
5362
|
-
provider: "openai",
|
|
5363
|
-
modelApi: "openai-chatgpt-responses",
|
|
5364
|
-
tools: [{ parameters: descriptor.parameters }]
|
|
5365
|
-
})[0]?.parameters;
|
|
5366
|
-
const projection = projectRuntimeToolInputSchema(normalizedParameters ?? descriptor.parameters, `${descriptor.name}.inputSchema`);
|
|
5367
|
-
if (projection.violations.length > 0) {
|
|
5368
|
-
quarantinedTools.push({
|
|
5369
|
-
tool: descriptor.name,
|
|
5370
|
-
violations: projection.violations
|
|
5371
|
-
});
|
|
5372
|
-
continue;
|
|
5373
|
-
}
|
|
5374
|
-
if (!isRecord(projection.schema)) {
|
|
5375
|
-
quarantinedTools.push({
|
|
5376
|
-
tool: descriptor.name,
|
|
5377
|
-
violations: [`${descriptor.name}.inputSchema must be a JSON object schema`]
|
|
5378
|
-
});
|
|
5379
|
-
continue;
|
|
5380
|
-
}
|
|
5381
|
-
projectedTools.push({
|
|
5382
|
-
tool,
|
|
5383
|
-
name: descriptor.name,
|
|
5384
|
-
description: descriptor.description,
|
|
5385
|
-
inputSchema: projection.schema
|
|
5386
|
-
});
|
|
5387
|
-
}
|
|
5388
|
-
return {
|
|
5389
|
-
tools: projectedTools,
|
|
5390
|
-
quarantinedTools
|
|
5391
|
-
};
|
|
5392
|
-
}
|
|
5393
|
-
function readCodexDynamicToolDescriptor(tool, toolIndex) {
|
|
5394
|
-
const fallbackName = `tool[${toolIndex}]`;
|
|
5395
|
-
let name;
|
|
5396
|
-
try {
|
|
5397
|
-
const rawName = tool.name;
|
|
5398
|
-
if (typeof rawName !== "string" || !rawName) return {
|
|
5399
|
-
ok: false,
|
|
5400
|
-
diagnostic: {
|
|
5401
|
-
tool: fallbackName,
|
|
5402
|
-
violations: [`${fallbackName}.name must be a non-empty string`]
|
|
5403
|
-
}
|
|
5404
|
-
};
|
|
5405
|
-
const trimmedName = rawName.trim();
|
|
5406
|
-
let nameViolation;
|
|
5407
|
-
if (!trimmedName) nameViolation = `${rawName}.name must not be empty`;
|
|
5408
|
-
else if (trimmedName !== rawName) nameViolation = `${rawName}.name must not have leading or trailing whitespace`;
|
|
5409
|
-
else if (!CODEX_DYNAMIC_TOOL_NAME_PATTERN.test(rawName)) nameViolation = `${rawName}.name must match ^[a-zA-Z0-9_-]+$`;
|
|
5410
|
-
else if (rawName.length > CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS) nameViolation = `${rawName}.name must be at most ${CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS} characters`;
|
|
5411
|
-
else if (rawName === "mcp" || rawName.startsWith("mcp__")) nameViolation = `${rawName}.name is reserved by Codex app-server`;
|
|
5412
|
-
if (nameViolation) return {
|
|
5413
|
-
ok: false,
|
|
5414
|
-
diagnostic: {
|
|
5415
|
-
tool: rawName,
|
|
5416
|
-
violations: [nameViolation]
|
|
5417
|
-
}
|
|
5418
|
-
};
|
|
5419
|
-
name = rawName;
|
|
5420
|
-
} catch {
|
|
5421
|
-
return {
|
|
5422
|
-
ok: false,
|
|
5423
|
-
diagnostic: {
|
|
5424
|
-
tool: fallbackName,
|
|
5425
|
-
violations: [`${fallbackName}.name is unreadable`]
|
|
5426
|
-
}
|
|
5427
|
-
};
|
|
5428
|
-
}
|
|
5429
|
-
let description;
|
|
5430
|
-
try {
|
|
5431
|
-
description = typeof tool.description === "string" ? tool.description : "";
|
|
5432
|
-
} catch {
|
|
5433
|
-
return {
|
|
5434
|
-
ok: false,
|
|
5435
|
-
diagnostic: {
|
|
5436
|
-
tool: name,
|
|
5437
|
-
violations: [`${name}.description is unreadable`]
|
|
5438
|
-
}
|
|
5439
|
-
};
|
|
5440
|
-
}
|
|
5441
|
-
let parameters;
|
|
5442
|
-
try {
|
|
5443
|
-
parameters = tool.parameters;
|
|
5444
|
-
} catch {
|
|
5445
|
-
return {
|
|
5446
|
-
ok: false,
|
|
5447
|
-
diagnostic: {
|
|
5448
|
-
tool: name,
|
|
5449
|
-
violations: [`${name}.inputSchema is unreadable`]
|
|
5450
|
-
}
|
|
5451
|
-
};
|
|
5452
|
-
}
|
|
5453
|
-
return {
|
|
5454
|
-
ok: true,
|
|
5455
|
-
name,
|
|
5456
|
-
description,
|
|
5457
|
-
parameters
|
|
5458
|
-
};
|
|
5459
|
-
}
|
|
5460
5451
|
function warnQuarantinedDynamicTools(params) {
|
|
5461
5452
|
if (params.tools.length === 0) return;
|
|
5462
5453
|
const unique = /* @__PURE__ */ new Map();
|
|
@@ -5767,4 +5758,4 @@ function isCronAddAction(args) {
|
|
|
5767
5758
|
return typeof action === "string" && action.trim().toLowerCase() === "add";
|
|
5768
5759
|
}
|
|
5769
5760
|
//#endregion
|
|
5770
|
-
export {
|
|
5761
|
+
export { filterCodexVisionTools as C, shouldWarnCodexDynamicToolBuildStageSummary as S, CodexNativeToolLifecycleProjector as T, resolveCodexMessageToolProvider as _, emitDynamicToolStartedDiagnostic as a, shouldEnableCodexAppServerNativeToolSurface as b, ensureCodexSandboxExecServerEnvironment as c, createCodexDynamicToolBuildStageTracker as d, disableCodexPluginThreadConfig as f, resolveCodexExternalSandboxPolicyForOpenClawSandbox as g, resolveCodexAppServerHookChannelId as h, emitDynamicToolErrorDiagnostic as i, releaseCodexSandboxExecServerEnvironment as l, resolveCodexAppServerExecutionCwd as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, formatCodexDynamicToolBuildStageSummary as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, buildDynamicTools as u, resolveCodexNodePlacementToolConstructionPlan as v, readBoundedCodexRemoteWorkspaceFile as w, shouldRequireCodexSandboxExecServerEnvironment as x, resolveCodexSandboxEnvironmentSelection as y };
|