@openclaw/codex 2026.6.2-beta.1 → 2026.6.5-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{client-factory-BS9nYX8K.js → client-factory-Bt49r45B.js} +2 -1
- package/dist/{client-BFxKzFnH.js → client-kMCtlApt.js} +42 -0
- package/dist/{command-handlers-BaBMyAFy.js → command-handlers-DMn2M7W7.js} +9 -9
- package/dist/{compact-CVPc2Rag.js → compact-CwnPeYnM.js} +11 -4
- package/dist/{computer-use-Dhz6SrFx.js → computer-use-ClweWaMz.js} +11 -1
- package/dist/{conversation-binding-CMaXGYAc.js → conversation-binding-CzpvaBs1.js} +7 -7
- package/dist/doctor-contract-api.js +5 -0
- package/dist/harness.js +9 -5
- package/dist/index.js +8 -6
- package/dist/media-understanding-provider.js +13 -5
- package/dist/{models-D8i1zWEu.js → models-DXorTaja.js} +9 -2
- package/dist/{native-hook-relay-CBp3nIGk.js → native-hook-relay-DZ3Oon0b.js} +61 -3
- package/dist/{notification-correlation-Bg-AlEEy.js → notification-correlation-o8quHmTK.js} +30 -1
- package/dist/prompt-overlay.js +7 -0
- package/dist/{protocol-validators-DIt7cXIp.js → protocol-validators-CIpP8IJ2.js} +13 -0
- package/dist/provider-catalog.js +8 -0
- package/dist/provider-discovery.js +1 -0
- package/dist/provider.js +16 -1
- package/dist/{rate-limit-cache-N66I-Rd7.js → rate-limit-cache-C7qmZ0Jh.js} +2 -0
- package/dist/{request-nYrsFNU2.js → request-D64BfplD.js} +16 -4
- package/dist/{run-attempt-Wo9uasL_.js → run-attempt-CWfXFq9Q.js} +383 -27
- package/dist/{sandbox-guard-DMCJlzmz.js → sandbox-guard-C-Yv9uwY.js} +5 -0
- package/dist/{session-binding-D8DxeEbf.js → session-binding-BgTv_YGm.js} +11 -0
- package/dist/{shared-client-B31-oqN-.js → shared-client-xytpSKD0.js} +31 -2
- package/dist/{side-question-DbYSPUnj.js → side-question-BEpALo9c.js} +9 -9
- package/dist/{thread-lifecycle-BOYYMjx6.js → thread-lifecycle-BJsazZ8j.js} +50 -6
- package/npm-shrinkwrap.json +2 -2
- package/package.json +4 -4
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { c as readCodexPluginConfig, d as resolveCodexPluginsPolicy, f as resolveOpenClawExecPolicyForCodexAppServer, i as isCodexAppServerApprovalPolicyAllowedByRequirements, l as resolveCodexAppServerRuntimeOptions, m as withMcpElicitationsApprovalPolicy, o as isCodexSandboxExecServerEnabled, p as shouldAutoApproveCodexAppServerApprovals, u as resolveCodexComputerUseConfig } from "./config-BT6SLiE6.js";
|
|
2
|
-
import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-
|
|
3
|
-
import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, l as isJsonObject, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-
|
|
4
|
-
import { A as projectContextEngineAssemblyForCodex, C as sanitizeCodexHistoryImagePayloads, D as normalizeCodexDynamicToolName$1, E as isForcedPrivateQaCodexRuntime, M as resolveCodexContextEngineProjectionReserveTokens, T as filterCodexDynamicTools, _ as shouldBuildCodexPluginThreadConfig, a as buildDeveloperInstructions, c as codexDynamicToolsFingerprint, f as startOrResumeThread, g as mergeCodexThreadConfigs, h as buildCodexPluginThreadConfigInputFingerprint, i as buildContextEngineBinding, j as resolveCodexContextEngineProjectionMaxChars, k as resolveCodexDynamicToolsLoadingForModel, l as isContextEngineBindingCompatible, m as buildCodexPluginThreadConfig, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, p as isCodexAppServerProfilerEnabled, s as buildTurnStartParams, x as defaultCodexAppInventoryCache } from "./thread-lifecycle-
|
|
5
|
-
import { a as readCodexAppServerBinding, n as clearCodexAppServerBindingForThread, s as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-
|
|
6
|
-
import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-
|
|
7
|
-
import { _ as shouldRefreshCodexRateLimitsForUsageLimitMessage, g as resolveCodexUsageLimitResetAtMs, h as formatCodexUsageLimitErrorMessage, i as readCodexNotificationTurnId, n as isCodexNotificationForTurn, r as readCodexNotificationThreadId, s as formatCodexDisplayText, t as describeCodexNotificationCorrelation } from "./notification-correlation-
|
|
8
|
-
import { d as resolveCodexAppServerAuthAccountCacheKey, f as resolveCodexAppServerAuthProfileId, h as resolveCodexAppServerHomeDir, m as resolveCodexAppServerFallbackApiKeyCacheKey, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as releaseLeasedSharedCodexAppServerClient, p as resolveCodexAppServerAuthProfileIdForAgent, s as retireSharedCodexAppServerClientIfCurrent, t as clearSharedCodexAppServerClientIfCurrent, u as refreshCodexAppServerAuthTokens } from "./shared-client-
|
|
9
|
-
import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-
|
|
10
|
-
import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-
|
|
11
|
-
import { S as withCodexStartupTimeout, _ as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, a as createCodexNativeHookRelay, b as resolveCodexTurnCompletionIdleTimeoutMs, c as scheduleCodexNativeHookRelayUnregister, d as emitDynamicToolErrorDiagnostic, f as emitDynamicToolStartedDiagnostic, g as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, h as handleCodexAppServerApprovalRequest, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerElicitationRequest, m as filterToolsForVisionInputs, n as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, o as resolveCodexNativeHookRelayEvents, p as emitDynamicToolTerminalDiagnostic, r as buildCodexNativeHookRelayConfig, s as resolveCodexNativeHookRelayTtlMs, u as createCodexDynamicToolBridge, v as resolveCodexStartupTimeoutMs, x as resolveCodexTurnTerminalIdleTimeoutMs, y as resolveCodexTurnAssistantCompletionIdleTimeoutMs } from "./native-hook-relay-
|
|
12
|
-
import { t as ensureCodexComputerUse } from "./computer-use-
|
|
13
|
-
import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-
|
|
2
|
+
import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-CIpP8IJ2.js";
|
|
3
|
+
import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, l as isJsonObject, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-kMCtlApt.js";
|
|
4
|
+
import { A as projectContextEngineAssemblyForCodex, C as sanitizeCodexHistoryImagePayloads, D as normalizeCodexDynamicToolName$1, E as isForcedPrivateQaCodexRuntime, M as resolveCodexContextEngineProjectionReserveTokens, T as filterCodexDynamicTools, _ as shouldBuildCodexPluginThreadConfig, a as buildDeveloperInstructions, c as codexDynamicToolsFingerprint, f as startOrResumeThread, g as mergeCodexThreadConfigs, h as buildCodexPluginThreadConfigInputFingerprint, i as buildContextEngineBinding, j as resolveCodexContextEngineProjectionMaxChars, k as resolveCodexDynamicToolsLoadingForModel, l as isContextEngineBindingCompatible, m as buildCodexPluginThreadConfig, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, p as isCodexAppServerProfilerEnabled, s as buildTurnStartParams, x as defaultCodexAppInventoryCache } from "./thread-lifecycle-BJsazZ8j.js";
|
|
5
|
+
import { a as readCodexAppServerBinding, n as clearCodexAppServerBindingForThread, s as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-BgTv_YGm.js";
|
|
6
|
+
import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-D64BfplD.js";
|
|
7
|
+
import { _ as shouldRefreshCodexRateLimitsForUsageLimitMessage, g as resolveCodexUsageLimitResetAtMs, h as formatCodexUsageLimitErrorMessage, i as readCodexNotificationTurnId, n as isCodexNotificationForTurn, r as readCodexNotificationThreadId, s as formatCodexDisplayText, t as describeCodexNotificationCorrelation } from "./notification-correlation-o8quHmTK.js";
|
|
8
|
+
import { d as resolveCodexAppServerAuthAccountCacheKey, f as resolveCodexAppServerAuthProfileId, h as resolveCodexAppServerHomeDir, m as resolveCodexAppServerFallbackApiKeyCacheKey, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as releaseLeasedSharedCodexAppServerClient, p as resolveCodexAppServerAuthProfileIdForAgent, s as retireSharedCodexAppServerClientIfCurrent, t as clearSharedCodexAppServerClientIfCurrent, u as refreshCodexAppServerAuthTokens } from "./shared-client-xytpSKD0.js";
|
|
9
|
+
import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-C-Yv9uwY.js";
|
|
10
|
+
import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-Bt49r45B.js";
|
|
11
|
+
import { S as withCodexStartupTimeout, _ as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, a as createCodexNativeHookRelay, b as resolveCodexTurnCompletionIdleTimeoutMs, c as scheduleCodexNativeHookRelayUnregister, d as emitDynamicToolErrorDiagnostic, f as emitDynamicToolStartedDiagnostic, g as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, h as handleCodexAppServerApprovalRequest, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerElicitationRequest, m as filterToolsForVisionInputs, n as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, o as resolveCodexNativeHookRelayEvents, p as emitDynamicToolTerminalDiagnostic, r as buildCodexNativeHookRelayConfig, s as resolveCodexNativeHookRelayTtlMs, u as createCodexDynamicToolBridge, v as resolveCodexStartupTimeoutMs, x as resolveCodexTurnTerminalIdleTimeoutMs, y as resolveCodexTurnAssistantCompletionIdleTimeoutMs } from "./native-hook-relay-DZ3Oon0b.js";
|
|
12
|
+
import { t as ensureCodexComputerUse } from "./computer-use-ClweWaMz.js";
|
|
13
|
+
import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-C7qmZ0Jh.js";
|
|
14
14
|
import { asDateTimestampMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
15
15
|
import { createHash, randomUUID } from "node:crypto";
|
|
16
16
|
import fsSync from "node:fs";
|
|
@@ -33,6 +33,10 @@ import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generat
|
|
|
33
33
|
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
|
|
34
34
|
import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
|
|
35
35
|
//#region extensions/codex/src/app-server/app-server-policy.ts
|
|
36
|
+
/**
|
|
37
|
+
* Promotes implicit `never` approval policy to `untrusted` only when runtime
|
|
38
|
+
* requirements allow OpenClaw to handle tool approvals.
|
|
39
|
+
*/
|
|
36
40
|
function resolveCodexAppServerForOpenClawToolPolicy(params) {
|
|
37
41
|
if (!params.shouldPromote || !params.canUseUntrustedApprovalPolicy || params.appServer.approvalPolicy !== "never") return params.appServer;
|
|
38
42
|
const explicitMode = params.execPolicy?.mode === "full" || params.pluginConfig.appServer?.mode !== void 0 || isCodexAppServerPolicyMode(params.env.OPENCLAW_CODEX_APP_SERVER_MODE);
|
|
@@ -51,8 +55,14 @@ function isCodexAppServerApprovalPolicy(value) {
|
|
|
51
55
|
}
|
|
52
56
|
//#endregion
|
|
53
57
|
//#region extensions/codex/src/app-server/attempt-client-cleanup.ts
|
|
58
|
+
/**
|
|
59
|
+
* Best-effort cleanup helpers for timed-out or aborted Codex app-server turns.
|
|
60
|
+
*/
|
|
61
|
+
/** Timeout for best-effort app-server turn interruption during cleanup. */
|
|
54
62
|
const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
|
|
63
|
+
/** Timeout for best-effort thread unsubscribe during cleanup. */
|
|
55
64
|
const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
|
|
65
|
+
/** Sends a turn interrupt without blocking abort cleanup on app-server errors. */
|
|
56
66
|
function interruptCodexTurnBestEffort(client, params) {
|
|
57
67
|
const requestOptions = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? { timeoutMs: params.timeoutMs } : void 0;
|
|
58
68
|
const requestParams = {
|
|
@@ -68,6 +78,7 @@ function interruptCodexTurnBestEffort(client, params) {
|
|
|
68
78
|
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
69
79
|
}
|
|
70
80
|
}
|
|
81
|
+
/** Unsubscribes from a thread while swallowing cleanup-only failures. */
|
|
71
82
|
async function unsubscribeCodexThreadBestEffort(client, params) {
|
|
72
83
|
try {
|
|
73
84
|
await client.request("thread/unsubscribe", { threadId: params.threadId }, { timeoutMs: params.timeoutMs });
|
|
@@ -78,6 +89,10 @@ async function unsubscribeCodexThreadBestEffort(client, params) {
|
|
|
78
89
|
});
|
|
79
90
|
}
|
|
80
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Retires the shared client after a timed-out turn so later runs do not reuse a
|
|
94
|
+
* potentially wedged app-server connection.
|
|
95
|
+
*/
|
|
81
96
|
async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
|
|
82
97
|
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
83
98
|
const detachedSharedClient = Boolean(retiredSharedClient);
|
|
@@ -115,9 +130,14 @@ async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
|
|
|
115
130
|
}
|
|
116
131
|
//#endregion
|
|
117
132
|
//#region extensions/codex/src/app-server/session-history.ts
|
|
133
|
+
/**
|
|
134
|
+
* Reads OpenClaw session history for Codex transcript mirroring and sanitizes
|
|
135
|
+
* image payloads before replaying messages into the app-server projector.
|
|
136
|
+
*/
|
|
118
137
|
function isMissingFileError(error) {
|
|
119
138
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
120
139
|
}
|
|
140
|
+
/** Returns sanitized session-context messages for a Codex mirrored session file. */
|
|
121
141
|
async function readCodexMirroredSessionHistoryMessages(sessionFile) {
|
|
122
142
|
try {
|
|
123
143
|
const entries = parseSessionEntries(await fs.readFile(sessionFile, "utf-8"));
|
|
@@ -132,6 +152,10 @@ async function readCodexMirroredSessionHistoryMessages(sessionFile) {
|
|
|
132
152
|
}
|
|
133
153
|
//#endregion
|
|
134
154
|
//#region extensions/codex/src/app-server/attempt-context.ts
|
|
155
|
+
/**
|
|
156
|
+
* Builds Codex app-server prompt context, workspace bootstrap injections,
|
|
157
|
+
* system-prompt reports, and context-engine projection decisions.
|
|
158
|
+
*/
|
|
135
159
|
const CODEX_NATIVE_PROJECT_DOC_BASENAMES = new Set(["agents.md"]);
|
|
136
160
|
const CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set(["tools.md"]);
|
|
137
161
|
const CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set([
|
|
@@ -152,11 +176,13 @@ const CODEX_BOOTSTRAP_CONTEXT_ORDER = new Map([
|
|
|
152
176
|
["memory.md", 60],
|
|
153
177
|
["heartbeat.md", 70]
|
|
154
178
|
]);
|
|
179
|
+
/** Reads mirrored Codex session history for harness hooks. */
|
|
155
180
|
async function readMirroredSessionHistoryMessages(sessionFile) {
|
|
156
181
|
const messages = await readCodexMirroredSessionHistoryMessages(sessionFile);
|
|
157
182
|
if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile });
|
|
158
183
|
return messages;
|
|
159
184
|
}
|
|
185
|
+
/** Reads a valid thread-bootstrap projection request from context-engine output. */
|
|
160
186
|
function readContextEngineThreadBootstrapProjection(projection) {
|
|
161
187
|
if (projection?.mode !== "thread_bootstrap") return;
|
|
162
188
|
const epoch = projection.epoch?.trim();
|
|
@@ -171,6 +197,10 @@ function readContextEngineThreadBootstrapProjection(projection) {
|
|
|
171
197
|
...fingerprint ? { fingerprint } : {}
|
|
172
198
|
};
|
|
173
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Decides whether an existing Codex thread can reuse its context-engine
|
|
202
|
+
* bootstrap projection or must be reprojected.
|
|
203
|
+
*/
|
|
174
204
|
function resolveContextEngineBootstrapProjectionDecision(params) {
|
|
175
205
|
const bindingProjection = params.startupBinding?.contextEngine?.projection;
|
|
176
206
|
if (!params.startupBinding?.threadId || !bindingProjection) return {
|
|
@@ -196,6 +226,10 @@ function resolveContextEngineBootstrapProjectionDecision(params) {
|
|
|
196
226
|
reason: "matching-thread-bootstrap-binding"
|
|
197
227
|
};
|
|
198
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Loads workspace bootstrap files and partitions them into Codex-native prompt,
|
|
231
|
+
* developer-instruction, heartbeat, and memory-tool contexts.
|
|
232
|
+
*/
|
|
199
233
|
async function buildCodexWorkspaceBootstrapContext(params) {
|
|
200
234
|
try {
|
|
201
235
|
const memoryToolsAvailable = params.memoryToolNames.length > 0 && canRouteCodexWorkspaceMemoryThroughTools({
|
|
@@ -269,6 +303,10 @@ async function buildCodexWorkspaceBootstrapContext(params) {
|
|
|
269
303
|
};
|
|
270
304
|
}
|
|
271
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Builds the prompt-size, bootstrap-file, skill, and tool-schema accounting
|
|
308
|
+
* report for a Codex run.
|
|
309
|
+
*/
|
|
272
310
|
function buildCodexSystemPromptReport(params) {
|
|
273
311
|
const toolEntries = params.tools.map(buildCodexToolReportEntry);
|
|
274
312
|
const schemaChars = toolEntries.reduce((sum, tool) => sum + tool.schemaChars, 0);
|
|
@@ -427,6 +465,9 @@ function readPositiveNumber(value) {
|
|
|
427
465
|
function readNonEmptyString$1(value) {
|
|
428
466
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
429
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Builds OpenClaw-provided workspace prompt context for the current Codex turn.
|
|
470
|
+
*/
|
|
430
471
|
function buildCodexOpenClawPromptContext(params) {
|
|
431
472
|
if (!shouldInjectCodexOpenClawPromptContext(params.params)) return;
|
|
432
473
|
const sections = [params.workspacePromptContext?.trim() ? [
|
|
@@ -445,6 +486,7 @@ function buildCodexOpenClawPromptContext(params) {
|
|
|
445
486
|
function shouldInjectCodexOpenClawPromptContext(params) {
|
|
446
487
|
return !(params.bootstrapContextMode === "lightweight" && params.bootstrapContextRunKind === "cron");
|
|
447
488
|
}
|
|
489
|
+
/** Renders loaded OpenClaw skill prompts as Codex collaboration instructions. */
|
|
448
490
|
function renderCodexSkillsCollaborationInstructions(params) {
|
|
449
491
|
if (!shouldInjectCodexOpenClawPromptContext(params.attempt)) return;
|
|
450
492
|
return params.skillsPrompt?.trim() ? [
|
|
@@ -453,6 +495,10 @@ function renderCodexSkillsCollaborationInstructions(params) {
|
|
|
453
495
|
params.skillsPrompt.trim()
|
|
454
496
|
].join("\n") : void 0;
|
|
455
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Prepends OpenClaw context while preserving leading delivery metadata as
|
|
500
|
+
* routing guidance instead of user request text.
|
|
501
|
+
*/
|
|
456
502
|
function prependCodexOpenClawPromptContext(prompt, context, options = {}) {
|
|
457
503
|
const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
|
|
458
504
|
if (!context?.trim() && (!deliveryHint || options.preservePromptWithoutContext)) return prompt;
|
|
@@ -567,6 +613,10 @@ function selectCodexWorkspaceMemoryReferenceFiles(params) {
|
|
|
567
613
|
}) && !file.missing && (file.content ?? "").trim().length > 0;
|
|
568
614
|
}).toSorted(compareCodexBootstrapFiles);
|
|
569
615
|
}
|
|
616
|
+
/**
|
|
617
|
+
* Renders a memory-file reference that points Codex at memory tools instead of
|
|
618
|
+
* embedding MEMORY.md contents.
|
|
619
|
+
*/
|
|
570
620
|
function renderCodexWorkspaceMemoryReference(params) {
|
|
571
621
|
if (params.files.length === 0) return;
|
|
572
622
|
const lines = [
|
|
@@ -578,6 +628,7 @@ function renderCodexWorkspaceMemoryReference(params) {
|
|
|
578
628
|
for (const file of params.files) lines.push(`- ${file.path}`);
|
|
579
629
|
return lines.join("\n").trim();
|
|
580
630
|
}
|
|
631
|
+
/** Lists available memory tool names understood by Codex workspace memory routing. */
|
|
581
632
|
function getCodexWorkspaceMemoryToolNames(tools) {
|
|
582
633
|
const availableToolNames = new Set(tools.map((tool) => normalizeCodexDynamicToolName(tool.name)));
|
|
583
634
|
return Array.from(CODEX_MEMORY_TOOL_NAMES).filter((name) => availableToolNames.has(name));
|
|
@@ -616,6 +667,10 @@ function isCodexWorkspaceRootMemoryPath(params) {
|
|
|
616
667
|
function isSameCodexWorkspacePath(left, right) {
|
|
617
668
|
return path.resolve(left) === path.resolve(right);
|
|
618
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* Remaps bootstrap file paths from the resolved workspace to the effective Codex
|
|
672
|
+
* workspace while preserving platform path separators.
|
|
673
|
+
*/
|
|
619
674
|
function remapCodexContextFilePath(params) {
|
|
620
675
|
const relativePath = path.relative(params.sourceWorkspaceDir, params.file.path);
|
|
621
676
|
if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath) || params.sourceWorkspaceDir === params.targetWorkspaceDir) return params.file;
|
|
@@ -657,9 +712,15 @@ function isNonEmptyString$2(value) {
|
|
|
657
712
|
}
|
|
658
713
|
//#endregion
|
|
659
714
|
//#region extensions/codex/src/app-server/attempt-diagnostics.ts
|
|
715
|
+
/**
|
|
716
|
+
* Diagnostic helpers for Codex app-server model calls and plugin-thread config
|
|
717
|
+
* eligibility.
|
|
718
|
+
*/
|
|
719
|
+
/** Reads a tool schema field in either app-server or OpenClaw naming. */
|
|
660
720
|
function readCodexDiagnosticToolParameters(tool) {
|
|
661
721
|
return tool.inputSchema ?? tool.parameters;
|
|
662
722
|
}
|
|
723
|
+
/** Builds compact diagnostic tool definitions for trusted private telemetry. */
|
|
663
724
|
function buildCodexDiagnosticToolDefinitions(tools) {
|
|
664
725
|
return tools.map((tool) => ({
|
|
665
726
|
name: tool.name,
|
|
@@ -667,6 +728,7 @@ function buildCodexDiagnosticToolDefinitions(tools) {
|
|
|
667
728
|
parameters: readCodexDiagnosticToolParameters(tool)
|
|
668
729
|
}));
|
|
669
730
|
}
|
|
731
|
+
/** Returns the serialized UTF-8 byte length for a JSON-compatible value. */
|
|
670
732
|
function utf8JsonByteLength(value) {
|
|
671
733
|
try {
|
|
672
734
|
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
@@ -674,6 +736,7 @@ function utf8JsonByteLength(value) {
|
|
|
674
736
|
return;
|
|
675
737
|
}
|
|
676
738
|
}
|
|
739
|
+
/** Builds a short namespaced fingerprint for sensitive log values. */
|
|
677
740
|
function fingerprintCodexLogValue(namespace, value) {
|
|
678
741
|
const hash = createHash("sha256");
|
|
679
742
|
hash.update(namespace);
|
|
@@ -681,6 +744,10 @@ function fingerprintCodexLogValue(namespace, value) {
|
|
|
681
744
|
hash.update(value);
|
|
682
745
|
return `sha256:${hash.digest("hex").slice(0, 16)}`;
|
|
683
746
|
}
|
|
747
|
+
/**
|
|
748
|
+
* Builds redacted diagnostics explaining whether plugin thread config was
|
|
749
|
+
* eligible for a Codex app-server attempt.
|
|
750
|
+
*/
|
|
684
751
|
function buildCodexPluginThreadConfigEligibilityLogData(params) {
|
|
685
752
|
return {
|
|
686
753
|
sessionId: params.sessionId,
|
|
@@ -696,6 +763,10 @@ function buildCodexPluginThreadConfigEligibilityLogData(params) {
|
|
|
696
763
|
appServerCommandSource: params.appServer.start.commandSource
|
|
697
764
|
};
|
|
698
765
|
}
|
|
766
|
+
/**
|
|
767
|
+
* Creates lifecycle emitters for trusted model-call diagnostics with optional
|
|
768
|
+
* private payload capture.
|
|
769
|
+
*/
|
|
699
770
|
function createCodexModelCallDiagnosticEmitter(params) {
|
|
700
771
|
const now = params.now ?? (() => Date.now());
|
|
701
772
|
const toolDefinitions = params.capture.toolDefinitions ? buildCodexDiagnosticToolDefinitions(params.tools) : void 0;
|
|
@@ -756,6 +827,7 @@ function createCodexModelCallDiagnosticEmitter(params) {
|
|
|
756
827
|
}
|
|
757
828
|
};
|
|
758
829
|
}
|
|
830
|
+
/** Classifies model-call failures into timeout/abort buckets for diagnostics. */
|
|
759
831
|
function classifyCodexModelCallFailureKind(params) {
|
|
760
832
|
if (params.timedOut || params.turnCompletionIdleTimedOut) return "timeout";
|
|
761
833
|
const errorMessage = params.error ? params.formatError(params.error).toLowerCase() : "";
|
|
@@ -765,10 +837,14 @@ function classifyCodexModelCallFailureKind(params) {
|
|
|
765
837
|
}
|
|
766
838
|
//#endregion
|
|
767
839
|
//#region extensions/codex/src/app-server/attempt-notifications.ts
|
|
840
|
+
/**
|
|
841
|
+
* Predicates and readers for Codex app-server notification envelopes.
|
|
842
|
+
*/
|
|
768
843
|
const CODEX_TURN_ABORT_MARKER_START = "<turn_aborted>";
|
|
769
844
|
const CODEX_TURN_ABORT_MARKER_END = "</turn_aborted>";
|
|
770
845
|
const CODEX_INTERRUPTED_USER_GUIDANCE = "The user interrupted the previous turn on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.";
|
|
771
846
|
const CODEX_INTERRUPTED_DEVELOPER_GUIDANCE = "The previous turn was interrupted on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.";
|
|
847
|
+
/** Builds compact activity metadata for watchdog and diagnostic updates. */
|
|
772
848
|
function describeNotificationActivity(notification) {
|
|
773
849
|
if (!isJsonObject(notification.params)) return { lastNotificationMethod: notification.method };
|
|
774
850
|
if (notification.method !== "rawResponseItem/completed") return { lastNotificationMethod: notification.method };
|
|
@@ -782,6 +858,7 @@ function describeNotificationActivity(notification) {
|
|
|
782
858
|
lastAssistantTextPreview: readRawAssistantTextPreview(item)
|
|
783
859
|
};
|
|
784
860
|
}
|
|
861
|
+
/** Tracks active app-server item ids from item start/completion notifications. */
|
|
785
862
|
function updateActiveTurnItemIds(notification, activeItemIds) {
|
|
786
863
|
if (notification.method !== "item/started" && notification.method !== "item/completed") return;
|
|
787
864
|
const itemId = readNotificationItemId(notification);
|
|
@@ -798,36 +875,43 @@ function isCompletedAssistantNotification(notification) {
|
|
|
798
875
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
799
876
|
return Boolean(item && readString$6(item, "type") === "agentMessage" && readString$6(item, "phase") !== "commentary");
|
|
800
877
|
}
|
|
878
|
+
/** Returns true for completed app-server reasoning items. */
|
|
801
879
|
function isReasoningItemCompletionNotification(notification) {
|
|
802
880
|
if (!isJsonObject(notification.params) || notification.method !== "item/completed") return false;
|
|
803
881
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
804
882
|
return item ? readString$6(item, "type") === "reasoning" : false;
|
|
805
883
|
}
|
|
884
|
+
/** Returns true for completed assistant commentary items. */
|
|
806
885
|
function isAssistantCommentaryCompletionNotification(notification) {
|
|
807
886
|
if (!isJsonObject(notification.params) || notification.method !== "item/completed") return false;
|
|
808
887
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
809
888
|
return Boolean(item && readString$6(item, "type") === "agentMessage" && readString$6(item, "phase") === "commentary");
|
|
810
889
|
}
|
|
890
|
+
/** Returns true for completed raw response reasoning items. */
|
|
811
891
|
function isRawReasoningCompletionNotification(notification) {
|
|
812
892
|
if (!isJsonObject(notification.params) || notification.method !== "rawResponseItem/completed") return false;
|
|
813
893
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
814
894
|
return item ? readString$6(item, "type") === "reasoning" : false;
|
|
815
895
|
}
|
|
896
|
+
/** Returns true when assistant completion can release the short idle watch. */
|
|
816
897
|
function isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff) {
|
|
817
898
|
if (isCompletedAssistantNotification(notification)) return true;
|
|
818
899
|
return !turnCrossedToolHandoff && isRawAssistantCompletionNotification(notification);
|
|
819
900
|
}
|
|
901
|
+
/** Returns true when a notification proves assistant output is still active. */
|
|
820
902
|
function shouldDisarmAssistantCompletionIdleWatch(notification) {
|
|
821
903
|
if (!isJsonObject(notification.params)) return false;
|
|
822
904
|
if (notification.method === "item/started") return true;
|
|
823
905
|
if (notification.method === "item/agentMessage/delta") return true;
|
|
824
906
|
return false;
|
|
825
907
|
}
|
|
908
|
+
/** Reads an item id from supported notification envelope shapes. */
|
|
826
909
|
function readNotificationItemId(notification) {
|
|
827
910
|
if (!isJsonObject(notification.params)) return;
|
|
828
911
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
829
912
|
return (item ? readString$6(item, "id") : void 0) ?? readString$6(notification.params, "itemId") ?? readString$6(notification.params, "id");
|
|
830
913
|
}
|
|
914
|
+
/** Detects completion for an OpenClaw dynamic tool result still awaited by Codex. */
|
|
831
915
|
function isPendingOpenClawDynamicToolCompletionNotification(notification, pendingOpenClawDynamicToolCompletionIds) {
|
|
832
916
|
if (notification.method !== "item/completed" || !isJsonObject(notification.params)) return false;
|
|
833
917
|
const itemId = readNotificationItemId(notification);
|
|
@@ -836,11 +920,13 @@ function isPendingOpenClawDynamicToolCompletionNotification(notification, pendin
|
|
|
836
920
|
const itemType = item ? readString$6(item, "type") : void 0;
|
|
837
921
|
return itemType === void 0 || itemType === "dynamicToolCall";
|
|
838
922
|
}
|
|
923
|
+
/** Returns true for raw response tool-output completion notifications. */
|
|
839
924
|
function isRawToolOutputCompletionNotification(notification) {
|
|
840
925
|
if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
|
|
841
926
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
842
927
|
return item ? readString$6(item, "type") === "custom_tool_call_output" : false;
|
|
843
928
|
}
|
|
929
|
+
/** Returns true for progress on Codex-native tool item types. */
|
|
844
930
|
function isNativeToolProgressNotification(notification) {
|
|
845
931
|
if (notification.method !== "item/started" && notification.method !== "item/completed" && notification.method !== "item/updated") return false;
|
|
846
932
|
if (!isJsonObject(notification.params)) return false;
|
|
@@ -853,17 +939,21 @@ function isNativeToolProgressNotification(notification) {
|
|
|
853
939
|
default: return false;
|
|
854
940
|
}
|
|
855
941
|
}
|
|
942
|
+
/** Returns true for raw native response stream delta events. */
|
|
856
943
|
function isNativeResponseStreamDeltaNotification(notification) {
|
|
857
944
|
return notification.method.startsWith("response.") && notification.method.endsWith(".delta");
|
|
858
945
|
}
|
|
946
|
+
/** Returns true for file-change patch update notifications. */
|
|
859
947
|
function isFileChangePatchUpdatedNotification(notification) {
|
|
860
948
|
return notification.method === "item/fileChange/patchUpdated" && isJsonObject(notification.params);
|
|
861
949
|
}
|
|
950
|
+
/** Returns true for raw assistant message progress with readable text. */
|
|
862
951
|
function isRawAssistantProgressNotification(notification) {
|
|
863
952
|
if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
|
|
864
953
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
865
954
|
return Boolean(item && readString$6(item, "type") === "message" && readString$6(item, "role") === "assistant" && readRawAssistantTextPreview(item));
|
|
866
955
|
}
|
|
956
|
+
/** Returns true for raw assistant completion outside commentary phase. */
|
|
867
957
|
function isRawAssistantCompletionNotification(notification) {
|
|
868
958
|
if (!isRawAssistantProgressNotification(notification) || !isJsonObject(notification.params)) return false;
|
|
869
959
|
const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
|
|
@@ -879,34 +969,45 @@ function readRawAssistantTextPreview(item) {
|
|
|
879
969
|
if (!text) return;
|
|
880
970
|
return text.length > 240 ? `${text.slice(0, 237)}...` : text;
|
|
881
971
|
}
|
|
972
|
+
/** Returns true when notification params correlate to a specific thread/turn. */
|
|
882
973
|
function isTurnNotification(value, threadId, turnId) {
|
|
883
974
|
return isCodexNotificationForTurn(value, threadId, turnId);
|
|
884
975
|
}
|
|
976
|
+
/** Returns true when a correlated notification belongs to another active run. */
|
|
885
977
|
function isCodexNotificationOutsideActiveRun(correlation) {
|
|
886
978
|
if (!Boolean(correlation.threadId || correlation.nestedTurnThreadId)) return false;
|
|
887
979
|
if (!correlation.matchesActiveThread) return true;
|
|
888
980
|
return Boolean(correlation.turnId || correlation.nestedTurnId) && correlation.matchesActiveTurn === false;
|
|
889
981
|
}
|
|
982
|
+
/** Checks request params that must contain the current thread and turn ids. */
|
|
890
983
|
function isCurrentThreadTurnRequestParams(value, threadId, turnId) {
|
|
891
984
|
if (!isJsonObject(value)) return false;
|
|
892
985
|
return readString$6(value, "threadId") === threadId && readString$6(value, "turnId") === turnId;
|
|
893
986
|
}
|
|
987
|
+
/** Checks approval request params, accepting `conversationId` as thread id. */
|
|
894
988
|
function isCurrentApprovalTurnRequestParams(value, threadId, turnId) {
|
|
895
989
|
if (!isJsonObject(value)) return false;
|
|
896
990
|
return (readString$6(value, "threadId") ?? readString$6(value, "conversationId")) === threadId && readString$6(value, "turnId") === turnId;
|
|
897
991
|
}
|
|
992
|
+
/** Checks request params where `turnId` may be omitted or null for the thread. */
|
|
898
993
|
function isCurrentThreadOptionalTurnRequestParams(value, threadId, turnId) {
|
|
899
994
|
if (!isJsonObject(value) || readString$6(value, "threadId") !== threadId) return false;
|
|
900
995
|
const requestTurnId = value.turnId;
|
|
901
996
|
return requestTurnId === null || requestTurnId === void 0 || requestTurnId === turnId;
|
|
902
997
|
}
|
|
998
|
+
/** Returns true for app-server error notifications that will retry. */
|
|
903
999
|
function isRetryableErrorNotification(value) {
|
|
904
1000
|
if (!isJsonObject(value)) return false;
|
|
905
1001
|
return readBoolean$1(value, "willRetry") === true || readBoolean$1(value, "will_retry") === true;
|
|
906
1002
|
}
|
|
1003
|
+
/** Returns true for terminal app-server thread status strings. */
|
|
907
1004
|
function isTerminalTurnStatus(status) {
|
|
908
1005
|
return status === "completed" || status === "interrupted" || status === "failed";
|
|
909
1006
|
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Detects Codex's synthetic interrupted-turn marker while ignoring the current
|
|
1009
|
+
* user prompt echoed through raw response events.
|
|
1010
|
+
*/
|
|
910
1011
|
function isCodexTurnAbortMarkerNotification(notification, options = {}) {
|
|
911
1012
|
if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
|
|
912
1013
|
const item = notification.params.item;
|
|
@@ -940,11 +1041,13 @@ function readString$6(record, key) {
|
|
|
940
1041
|
function readBoolean$1(record, key) {
|
|
941
1042
|
return asBoolean(record[key]);
|
|
942
1043
|
}
|
|
1044
|
+
/** Reads a typed Codex item from notification params when id/type are present. */
|
|
943
1045
|
function readCodexNotificationItem(params) {
|
|
944
1046
|
if (!isJsonObject(params) || !isJsonObject(params.item)) return;
|
|
945
1047
|
const item = params.item;
|
|
946
1048
|
return typeof item.id === "string" && typeof item.type === "string" ? item : void 0;
|
|
947
1049
|
}
|
|
1050
|
+
/** Maps Codex item types to the tool name shown in execution progress. */
|
|
948
1051
|
function codexExecutionToolName(item) {
|
|
949
1052
|
if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
|
|
950
1053
|
if (item.type === "mcpToolCall" && typeof item.tool === "string") {
|
|
@@ -960,6 +1063,10 @@ function isNonEmptyString$1(value) {
|
|
|
960
1063
|
}
|
|
961
1064
|
//#endregion
|
|
962
1065
|
//#region extensions/codex/src/app-server/attempt-notification-state.ts
|
|
1066
|
+
/**
|
|
1067
|
+
* State machine for Codex app-server turn notifications and idle-watch updates.
|
|
1068
|
+
*/
|
|
1069
|
+
/** Emits coarse execution phases exactly once from app-server notifications. */
|
|
963
1070
|
function reportCodexExecutionNotification(params) {
|
|
964
1071
|
const { notification } = params;
|
|
965
1072
|
if (notification.method === "turn/started") {
|
|
@@ -980,10 +1087,15 @@ function reportCodexExecutionNotification(params) {
|
|
|
980
1087
|
itemId: item.id
|
|
981
1088
|
});
|
|
982
1089
|
}
|
|
1090
|
+
/** Returns true when a notification ends the current app-server turn. */
|
|
983
1091
|
function isTerminalCodexTurnNotificationForTurn(params) {
|
|
984
1092
|
if (!isTurnNotification(params.notification.params, params.threadId, params.turnId)) return false;
|
|
985
1093
|
return params.notification.method === "turn/completed" || isCodexTurnAbortMarkerNotification(params.notification, { currentPromptTexts: params.currentPromptTexts });
|
|
986
1094
|
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Applies one notification to active item tracking, idle watches, and terminal
|
|
1097
|
+
* turn state.
|
|
1098
|
+
*/
|
|
987
1099
|
function applyCodexTurnNotificationState(params) {
|
|
988
1100
|
const { notification, turnWatches } = params;
|
|
989
1101
|
const isCurrentTurnNotification = isTurnNotification(notification.params, params.threadId, params.turnId);
|
|
@@ -1058,30 +1170,34 @@ function applyCodexTurnNotificationState(params) {
|
|
|
1058
1170
|
//#region extensions/codex/src/app-server/attempt-results.ts
|
|
1059
1171
|
const CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_USER_MESSAGE = "Codex stopped before confirming the turn was complete. The response may be incomplete; retry if needed.";
|
|
1060
1172
|
const CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_SIDE_EFFECT_USER_MESSAGE = "Codex stopped before confirming the turn was complete. Some work may already have been performed; verify the current state before retrying.";
|
|
1173
|
+
/** Joins terminal assistant text blocks into the final attempt answer. */
|
|
1061
1174
|
function collectTerminalAssistantText(result) {
|
|
1062
1175
|
return result.assistantTexts.join("\n\n").trim();
|
|
1063
1176
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1177
|
+
/**
|
|
1178
|
+
* Builds the user-facing timeout outcome when Codex stops without a terminal
|
|
1179
|
+
* turn event.
|
|
1180
|
+
*/
|
|
1067
1181
|
function buildCodexAppServerPromptTimeoutOutcome(params) {
|
|
1068
|
-
|
|
1182
|
+
if (!params.turnCompletionIdleTimedOut) return;
|
|
1183
|
+
if (params.turnWatchTimeoutKind !== void 0 && params.turnWatchTimeoutKind !== "completion") return;
|
|
1069
1184
|
const replayBlockedReason = resolveCodexAppServerReplayBlockedReason(params.result);
|
|
1070
|
-
if (!params.turnCompletionIdleTimedOut || params.result.itemLifecycle.completedCount === 0 && !completionIdleTimeoutHadPotentialSideEffects && replayBlockedReason === void 0) return;
|
|
1071
1185
|
return {
|
|
1072
|
-
message:
|
|
1073
|
-
...
|
|
1186
|
+
message: replayBlockedReason === "tool_activity" || replayBlockedReason === "potential_side_effect" || replayBlockedReason === "active_item" ? CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_SIDE_EFFECT_USER_MESSAGE : CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_USER_MESSAGE,
|
|
1187
|
+
...replayBlockedReason ? {
|
|
1074
1188
|
replayInvalid: true,
|
|
1075
1189
|
livenessState: "abandoned"
|
|
1076
1190
|
} : {}
|
|
1077
1191
|
};
|
|
1078
1192
|
}
|
|
1193
|
+
/** Explains why an incomplete app-server turn cannot be safely replayed. */
|
|
1079
1194
|
function resolveCodexAppServerReplayBlockedReason(result) {
|
|
1080
1195
|
if (result.replayMetadata.hadPotentialSideEffects) return "potential_side_effect";
|
|
1081
1196
|
if (result.assistantTexts.some((text) => text.trim().length > 0)) return "assistant_output";
|
|
1082
1197
|
if (result.toolMetas.length > 0 || result.clientToolCalls || result.lastToolError || result.didSendDeterministicApprovalPrompt) return "tool_activity";
|
|
1083
1198
|
if (result.itemLifecycle.startedCount > 0 || result.itemLifecycle.activeCount > 0) return "active_item";
|
|
1084
1199
|
}
|
|
1200
|
+
/** Builds an attempt result for failures before the app-server turn starts. */
|
|
1085
1201
|
function buildCodexTurnStartFailureResult(params) {
|
|
1086
1202
|
return {
|
|
1087
1203
|
aborted: false,
|
|
@@ -1115,6 +1231,7 @@ function buildCodexTurnStartFailureResult(params) {
|
|
|
1115
1231
|
systemPromptReport: params.systemPromptReport
|
|
1116
1232
|
};
|
|
1117
1233
|
}
|
|
1234
|
+
/** Detects app-server errors caused by invalid image payload data. */
|
|
1118
1235
|
function isInvalidCodexImagePayloadError(message) {
|
|
1119
1236
|
if (typeof message !== "string" || !message.trim()) return false;
|
|
1120
1237
|
const normalizedMessage = message.replace(/[_-]+/gu, " ");
|
|
@@ -1122,6 +1239,11 @@ function isInvalidCodexImagePayloadError(message) {
|
|
|
1122
1239
|
}
|
|
1123
1240
|
//#endregion
|
|
1124
1241
|
//#region extensions/codex/src/app-server/dynamic-tool-build.ts
|
|
1242
|
+
/**
|
|
1243
|
+
* Builds the Codex app-server dynamic tool list for one turn, including
|
|
1244
|
+
* OpenClaw-owned tools, Codex native-tool fallback rules, sandbox shell shims,
|
|
1245
|
+
* and provider allowlist normalization.
|
|
1246
|
+
*/
|
|
1125
1247
|
const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
|
|
1126
1248
|
"exec",
|
|
1127
1249
|
"process",
|
|
@@ -1131,12 +1253,14 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
|
|
|
1131
1253
|
"apply_patch"
|
|
1132
1254
|
];
|
|
1133
1255
|
const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = new Set(["read", "write"]);
|
|
1256
|
+
/** Splits sandbox and run session keys so tool calls can bind to both scopes when needed. */
|
|
1134
1257
|
function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
|
|
1135
1258
|
return {
|
|
1136
1259
|
sessionKey: sandboxSessionKey,
|
|
1137
1260
|
runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0
|
|
1138
1261
|
};
|
|
1139
1262
|
}
|
|
1263
|
+
/** Resolves the channel id that hook events should target for this Codex app-server turn. */
|
|
1140
1264
|
function resolveCodexAppServerHookChannelId(params, sandboxSessionKey) {
|
|
1141
1265
|
return buildAgentHookContextChannelFields({
|
|
1142
1266
|
sessionKey: sandboxSessionKey,
|
|
@@ -1148,6 +1272,7 @@ function resolveCodexAppServerHookChannelId(params, sandboxSessionKey) {
|
|
|
1148
1272
|
}
|
|
1149
1273
|
const CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS = 1e3;
|
|
1150
1274
|
const CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS = 500;
|
|
1275
|
+
/** Creates cheap optional timing instrumentation for the dynamic-tool hot path. */
|
|
1151
1276
|
function createCodexDynamicToolBuildStageTracker(options = {}) {
|
|
1152
1277
|
if (!options.enabled) return {
|
|
1153
1278
|
mark() {},
|
|
@@ -1180,12 +1305,15 @@ function createCodexDynamicToolBuildStageTracker(options = {}) {
|
|
|
1180
1305
|
}
|
|
1181
1306
|
};
|
|
1182
1307
|
}
|
|
1308
|
+
/** Returns true when dynamic-tool construction is slow enough to warrant a warning log. */
|
|
1183
1309
|
function shouldWarnCodexDynamicToolBuildStageSummary(summary) {
|
|
1184
1310
|
return summary.totalMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS || summary.stages.some((stage) => stage.durationMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS);
|
|
1185
1311
|
}
|
|
1312
|
+
/** Formats per-stage timings into the compact form used by Codex app-server logs. */
|
|
1186
1313
|
function formatCodexDynamicToolBuildStageSummary(summary) {
|
|
1187
1314
|
return summary.stages.length > 0 ? summary.stages.map((stage) => `${stage.name}:${stage.durationMs}ms@${stage.elapsedMs}ms`).join(",") : "none";
|
|
1188
1315
|
}
|
|
1316
|
+
/** Builds, filters, and normalizes Codex-compatible runtime tools for a single turn. */
|
|
1189
1317
|
async function buildDynamicTools(input) {
|
|
1190
1318
|
const { params } = input;
|
|
1191
1319
|
if (params.disableTools || !supportsModelTools(params.model)) return [];
|
|
@@ -1323,6 +1451,7 @@ async function buildDynamicTools(input) {
|
|
|
1323
1451
|
}
|
|
1324
1452
|
return normalizedTools;
|
|
1325
1453
|
}
|
|
1454
|
+
/** Preserves delivery-critical tools when a narrow allowlist would otherwise hide them. */
|
|
1326
1455
|
function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
|
|
1327
1456
|
if (toolsAllow === void 0 || hasWildcardCodexToolsAllow(toolsAllow)) return toolsAllow;
|
|
1328
1457
|
const forcedToolNames = shouldForceMessageTool(params) ? ["message"] : [];
|
|
@@ -1332,6 +1461,7 @@ function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
|
|
|
1332
1461
|
const missingToolNames = forcedToolNames.filter((toolName) => !normalized.has(normalizeCodexDynamicToolName$1(toolName)));
|
|
1333
1462
|
return missingToolNames.length === 0 ? toolsAllow : [...toolsAllow, ...missingToolNames];
|
|
1334
1463
|
}
|
|
1464
|
+
/** Decides whether Codex native code mode can own shell/file tools for this turn. */
|
|
1335
1465
|
function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options = {}) {
|
|
1336
1466
|
if (isCodexMemoryFlushRun(params)) return false;
|
|
1337
1467
|
if (isCodexNativeExecutionBlockedByNodeExecHost(params, {
|
|
@@ -1343,6 +1473,7 @@ function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options =
|
|
|
1343
1473
|
if (toolsAllow === void 0) return canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
|
|
1344
1474
|
return hasWildcardCodexToolsAllow(toolsAllow) && canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
|
|
1345
1475
|
}
|
|
1476
|
+
/** Returns true when OpenClaw policy requires the Node-owned exec/process tools instead. */
|
|
1346
1477
|
function isCodexNativeExecutionBlockedByNodeExecHost(params, options = {}) {
|
|
1347
1478
|
return !resolveCodexNativeExecutionPolicy({
|
|
1348
1479
|
config: params.config,
|
|
@@ -1371,15 +1502,19 @@ function isCodexMemoryFlushRun(params) {
|
|
|
1371
1502
|
function filterCodexMemoryFlushDynamicTools(tools) {
|
|
1372
1503
|
return tools.filter((tool) => CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW.has(normalizeCodexDynamicToolName$1(tool.name)));
|
|
1373
1504
|
}
|
|
1505
|
+
/** Requires a Codex sandbox environment only when native tools must run inside OpenClaw sandboxing. */
|
|
1374
1506
|
function shouldRequireCodexSandboxExecServerEnvironment(params) {
|
|
1375
1507
|
return Boolean(params.sandbox?.enabled && params.nativeToolSurfaceEnabled && params.sandboxExecServerEnabled);
|
|
1376
1508
|
}
|
|
1509
|
+
/** Selects the sandbox exec-server environment passed through the Codex app-server protocol. */
|
|
1377
1510
|
function resolveCodexSandboxEnvironmentSelection(environment, nativeToolSurfaceEnabled) {
|
|
1378
1511
|
return environment && nativeToolSurfaceEnabled ? [environment] : void 0;
|
|
1379
1512
|
}
|
|
1513
|
+
/** Chooses the cwd visible to Codex native execution after sandbox exec-server setup. */
|
|
1380
1514
|
function resolveCodexAppServerExecutionCwd(params) {
|
|
1381
1515
|
return params.environment && params.nativeToolSurfaceEnabled ? params.environment.cwd : params.effectiveCwd;
|
|
1382
1516
|
}
|
|
1517
|
+
/** Converts OpenClaw sandbox networking into Codex's external-sandbox policy shape. */
|
|
1383
1518
|
function resolveCodexExternalSandboxPolicyForOpenClawSandbox(sandbox) {
|
|
1384
1519
|
return {
|
|
1385
1520
|
type: "externalSandbox",
|
|
@@ -1391,6 +1526,7 @@ function codexNetworkAccessForOpenClawSandbox(sandbox) {
|
|
|
1391
1526
|
const network = sandbox?.docker?.network?.trim().toLowerCase();
|
|
1392
1527
|
return Boolean(network && network !== "none");
|
|
1393
1528
|
}
|
|
1529
|
+
/** Returns a Codex config copy with app-server Codex plugin loading disabled for thread tools. */
|
|
1394
1530
|
function disableCodexPluginThreadConfig(pluginConfig) {
|
|
1395
1531
|
const config = readCodexPluginConfig(pluginConfig);
|
|
1396
1532
|
return {
|
|
@@ -1401,6 +1537,7 @@ function disableCodexPluginThreadConfig(pluginConfig) {
|
|
|
1401
1537
|
}
|
|
1402
1538
|
};
|
|
1403
1539
|
}
|
|
1540
|
+
/** Adds sandbox_exec/process aliases when native Code Mode cannot directly honor the sandbox. */
|
|
1404
1541
|
function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input) {
|
|
1405
1542
|
if (!shouldExposeSandboxExecDynamicTool(input) || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
|
|
1406
1543
|
const execTool = allTools.find((tool) => normalizeCodexDynamicToolName$1(tool.name) === "exec");
|
|
@@ -1471,6 +1608,7 @@ function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input) {
|
|
|
1471
1608
|
}
|
|
1472
1609
|
return next;
|
|
1473
1610
|
}
|
|
1611
|
+
/** Applies a normalized tool allowlist while preserving sandbox shell aliases for exec/process. */
|
|
1474
1612
|
function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
|
|
1475
1613
|
if (!toolsAllow) return tools;
|
|
1476
1614
|
if (toolsAllow.length === 0) return [];
|
|
@@ -1481,46 +1619,57 @@ function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
|
|
|
1481
1619
|
return allowSet.has(normalized) || normalized === "sandbox_exec" && allowSet.has("exec") || normalized === "sandbox_process" && (allowSet.has("exec") || allowSet.has("process"));
|
|
1482
1620
|
});
|
|
1483
1621
|
}
|
|
1622
|
+
/** Detects the wildcard allowlist marker after Codex tool-name normalization. */
|
|
1484
1623
|
function hasWildcardCodexToolsAllow(toolsAllow) {
|
|
1485
1624
|
return toolsAllow.some((name) => normalizeCodexDynamicToolName$1(name) === "*");
|
|
1486
1625
|
}
|
|
1626
|
+
/** Forces message delivery through the message tool when the source channel requires it. */
|
|
1487
1627
|
function shouldForceMessageTool(params) {
|
|
1488
1628
|
return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
|
|
1489
1629
|
}
|
|
1490
1630
|
//#endregion
|
|
1491
1631
|
//#region extensions/codex/src/app-server/sandbox-exec-server/json-rpc.ts
|
|
1632
|
+
/** JSON-RPC error code used when a sandbox exec-server method is unknown. */
|
|
1492
1633
|
const JSON_RPC_NOT_FOUND = -32004;
|
|
1634
|
+
/** Protocol-level error carrying the JSON-RPC error code to send to the client. */
|
|
1493
1635
|
var JsonRpcProtocolError = class extends Error {
|
|
1494
1636
|
constructor(code, message) {
|
|
1495
1637
|
super(message);
|
|
1496
1638
|
this.code = code;
|
|
1497
1639
|
}
|
|
1498
1640
|
};
|
|
1641
|
+
/** Parses raw WebSocket data into a JSON-RPC request object. */
|
|
1499
1642
|
function parseRequest(data) {
|
|
1500
1643
|
const text = (Array.isArray(data) ? Buffer.concat(data) : Buffer.isBuffer(data) ? data : Buffer.from(data)).toString("utf8");
|
|
1501
1644
|
return requireObject(JSON.parse(text), "JSON-RPC request");
|
|
1502
1645
|
}
|
|
1646
|
+
/** Validates that a JSON value is a non-array object. */
|
|
1503
1647
|
function requireObject(value, label) {
|
|
1504
1648
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
|
|
1505
1649
|
return value;
|
|
1506
1650
|
}
|
|
1651
|
+
/** Validates a non-empty string JSON-RPC parameter. */
|
|
1507
1652
|
function requireString(value, label) {
|
|
1508
1653
|
if (typeof value !== "string" || !value) throw new Error(`${label} must be a non-empty string.`);
|
|
1509
1654
|
return value;
|
|
1510
1655
|
}
|
|
1656
|
+
/** Validates a base64 payload parameter as a string; decoding happens at call sites. */
|
|
1511
1657
|
function requireBase64String(value, label) {
|
|
1512
1658
|
if (typeof value !== "string") throw new Error(`${label} must be a string.`);
|
|
1513
1659
|
return value;
|
|
1514
1660
|
}
|
|
1661
|
+
/** Validates a finite numeric JSON-RPC parameter. */
|
|
1515
1662
|
function requireNumber(value, label) {
|
|
1516
1663
|
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${label} must be a finite number.`);
|
|
1517
1664
|
return value;
|
|
1518
1665
|
}
|
|
1666
|
+
/** Validates a non-empty string-array JSON-RPC parameter. */
|
|
1519
1667
|
function requireStringArray(value, label) {
|
|
1520
1668
|
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error(`${label} must be a string array.`);
|
|
1521
1669
|
if (value.length === 0) throw new Error(`${label} must not be empty.`);
|
|
1522
1670
|
return value;
|
|
1523
1671
|
}
|
|
1672
|
+
/** Reads HTTP headers from JSON-RPC params, defaulting to an empty header list. */
|
|
1524
1673
|
function readHttpHeaders(value) {
|
|
1525
1674
|
if (!Array.isArray(value)) return [];
|
|
1526
1675
|
return value.map((entry, index) => {
|
|
@@ -1531,6 +1680,7 @@ function readHttpHeaders(value) {
|
|
|
1531
1680
|
};
|
|
1532
1681
|
});
|
|
1533
1682
|
}
|
|
1683
|
+
/** Sends a JSON-RPC success response over the WebSocket. */
|
|
1534
1684
|
function sendResult(socket, id, result) {
|
|
1535
1685
|
socket.send(JSON.stringify({
|
|
1536
1686
|
jsonrpc: "2.0",
|
|
@@ -1538,6 +1688,7 @@ function sendResult(socket, id, result) {
|
|
|
1538
1688
|
result: result === void 0 ? {} : result
|
|
1539
1689
|
}));
|
|
1540
1690
|
}
|
|
1691
|
+
/** Sends a JSON-RPC error response over the WebSocket. */
|
|
1541
1692
|
function sendError(socket, id, code, message) {
|
|
1542
1693
|
socket.send(JSON.stringify({
|
|
1543
1694
|
jsonrpc: "2.0",
|
|
@@ -1550,9 +1701,15 @@ function sendError(socket, id, code, message) {
|
|
|
1550
1701
|
}
|
|
1551
1702
|
//#endregion
|
|
1552
1703
|
//#region extensions/codex/src/app-server/sandbox-exec-server/fs-policy.ts
|
|
1704
|
+
/**
|
|
1705
|
+
* Resolves Codex filesystem sandbox policy payloads into OpenClaw path/glob
|
|
1706
|
+
* checks for sandbox exec-server filesystem operations.
|
|
1707
|
+
*/
|
|
1708
|
+
/** Resolves request-local sandbox policy and asserts each requested path has the needed access. */
|
|
1553
1709
|
function assertFsSandboxAccess(execServer, record, requests) {
|
|
1554
1710
|
assertResolvedFsSandboxAccess(resolveFsSandboxPolicy(execServer, record), requests);
|
|
1555
1711
|
}
|
|
1712
|
+
/** Parses a Codex managed filesystem sandbox context into normalized access entries. */
|
|
1556
1713
|
function resolveFsSandboxPolicy(execServer, record) {
|
|
1557
1714
|
if (record.sandbox === void 0 || record.sandbox === null) return;
|
|
1558
1715
|
const sandbox = requireObject(record.sandbox, "fs sandbox context");
|
|
@@ -1633,6 +1790,7 @@ function resolveFsSpecialPath(value, cwd) {
|
|
|
1633
1790
|
if (kind === "slash_tmp" || kind === "tmpdir") return "/tmp";
|
|
1634
1791
|
throw new Error(`Unsupported Codex fs sandbox special path: ${kind}`);
|
|
1635
1792
|
}
|
|
1793
|
+
/** Asserts access against an already resolved filesystem sandbox policy. */
|
|
1636
1794
|
function assertResolvedFsSandboxAccess(policy, requests) {
|
|
1637
1795
|
if (!policy?.unrestricted && policy) for (const request of requests) {
|
|
1638
1796
|
const access = resolveFsAccess(policy, request.path);
|
|
@@ -1655,6 +1813,7 @@ function resolveFsAccess(policy, rawPath) {
|
|
|
1655
1813
|
}
|
|
1656
1814
|
return selected?.access ?? "none";
|
|
1657
1815
|
}
|
|
1816
|
+
/** Rejects recursive writes/removes that would cross protected read-only descendants. */
|
|
1658
1817
|
function assertNoReadOnlyDescendant(policy, rawPath, operation) {
|
|
1659
1818
|
if (!policy || policy.unrestricted) return;
|
|
1660
1819
|
const target = normalizeSandboxAbsolutePath(rawPath, "fs path");
|
|
@@ -1669,11 +1828,13 @@ function assertNoReadOnlyDescendant(policy, rawPath, operation) {
|
|
|
1669
1828
|
throw new Error(`Codex fs sandbox denied recursive ${operation} of ${rawPath} because ${protectedPath} is not writable.`);
|
|
1670
1829
|
}
|
|
1671
1830
|
}
|
|
1831
|
+
/** Normalizes and validates an absolute POSIX path inside the sandbox namespace. */
|
|
1672
1832
|
function normalizeSandboxAbsolutePath(rawPath, label) {
|
|
1673
1833
|
if (!rawPath || rawPath.includes("\0") || !rawPath.startsWith("/")) throw new Error(`${label} must be an absolute sandbox path.`);
|
|
1674
1834
|
const normalized = posix.normalize(rawPath);
|
|
1675
1835
|
return normalized === "//" ? "/" : normalized;
|
|
1676
1836
|
}
|
|
1837
|
+
/** Returns true when target is root itself or a descendant of root. */
|
|
1677
1838
|
function pathContains(root, target) {
|
|
1678
1839
|
return root === "/" || target === root || target.startsWith(`${root}/`);
|
|
1679
1840
|
}
|
|
@@ -1751,17 +1912,20 @@ function sandboxGlobLiteralPrefix(pattern) {
|
|
|
1751
1912
|
if (slash <= 0) return "/";
|
|
1752
1913
|
return normalizeSandboxAbsolutePath(prefix.slice(0, slash), "fs sandbox glob prefix");
|
|
1753
1914
|
}
|
|
1915
|
+
/** Safely joins a single directory entry name onto a sandbox parent path. */
|
|
1754
1916
|
function joinSandboxChildPath(parent, child) {
|
|
1755
1917
|
if (!child || child === "." || child === ".." || child.includes("/") || child.includes("\0")) throw new Error(`Invalid sandbox directory entry name: ${child}`);
|
|
1756
1918
|
return parent.endsWith("/") ? `${parent}${child}` : `${parent}/${child}`;
|
|
1757
1919
|
}
|
|
1758
1920
|
//#endregion
|
|
1759
1921
|
//#region extensions/codex/src/app-server/sandbox-exec-server/runtime.ts
|
|
1922
|
+
/** Returns the configured sandbox backend or fails the current JSON-RPC request. */
|
|
1760
1923
|
function requireBackend(execServer) {
|
|
1761
1924
|
const backend = execServer.sandbox.backend;
|
|
1762
1925
|
if (!backend) throw new Error("OpenClaw sandbox backend is unavailable.");
|
|
1763
1926
|
return backend;
|
|
1764
1927
|
}
|
|
1928
|
+
/** Returns the configured filesystem bridge or fails the current JSON-RPC request. */
|
|
1765
1929
|
function requireFsBridge(execServer) {
|
|
1766
1930
|
const fsBridge = execServer.sandbox.fsBridge;
|
|
1767
1931
|
if (!fsBridge) throw new Error("Sandbox filesystem bridge is unavailable.");
|
|
@@ -1769,7 +1933,12 @@ function requireFsBridge(execServer) {
|
|
|
1769
1933
|
}
|
|
1770
1934
|
//#endregion
|
|
1771
1935
|
//#region extensions/codex/src/app-server/sandbox-exec-server/filesystem.ts
|
|
1936
|
+
/**
|
|
1937
|
+
* Implements filesystem JSON-RPC handlers for the Codex sandbox exec-server
|
|
1938
|
+
* with OpenClaw sandbox policy checks before every bridge operation.
|
|
1939
|
+
*/
|
|
1772
1940
|
const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES = 512 * 1024 * 1024;
|
|
1941
|
+
/** Reads a sandbox file as base64 after read-policy and size checks. */
|
|
1773
1942
|
async function readFile(execServer, params) {
|
|
1774
1943
|
const record = requireObject(params, "fs/readFile params");
|
|
1775
1944
|
const filePath = requireString(record.path, "path");
|
|
@@ -1783,6 +1952,7 @@ async function readFile(execServer, params) {
|
|
|
1783
1952
|
if (stat.type === "file" && stat.size > CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES) throw new Error(`file is too large to read through Codex sandbox exec-server: ${stat.size} bytes`);
|
|
1784
1953
|
return { dataBase64: (await fsBridge.readFile({ filePath })).toString("base64") };
|
|
1785
1954
|
}
|
|
1955
|
+
/** Writes base64 data to an existing sandbox directory after write-policy checks. */
|
|
1786
1956
|
async function writeFile(execServer, params) {
|
|
1787
1957
|
const record = requireObject(params, "fs/writeFile params");
|
|
1788
1958
|
const filePath = requireString(record.path, "path");
|
|
@@ -1798,6 +1968,7 @@ async function writeFile(execServer, params) {
|
|
|
1798
1968
|
mkdir: false
|
|
1799
1969
|
});
|
|
1800
1970
|
}
|
|
1971
|
+
/** Creates a sandbox directory, respecting recursive and parent-directory semantics. */
|
|
1801
1972
|
async function createDirectory(execServer, params) {
|
|
1802
1973
|
const record = requireObject(params, "fs/createDirectory params");
|
|
1803
1974
|
const filePath = requireString(record.path, "path");
|
|
@@ -1812,6 +1983,7 @@ async function createDirectory(execServer, params) {
|
|
|
1812
1983
|
}
|
|
1813
1984
|
await fsBridge.mkdirp({ filePath });
|
|
1814
1985
|
}
|
|
1986
|
+
/** Returns normalized metadata for a sandbox path. */
|
|
1815
1987
|
async function getMetadata(execServer, params) {
|
|
1816
1988
|
const record = requireObject(params, "fs/getMetadata params");
|
|
1817
1989
|
const filePath = requireString(record.path, "path");
|
|
@@ -1823,6 +1995,7 @@ async function getMetadata(execServer, params) {
|
|
|
1823
1995
|
if (!stat) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "file not found");
|
|
1824
1996
|
return metadataResponse(stat);
|
|
1825
1997
|
}
|
|
1998
|
+
/** Lists sandbox directory entries visible under the resolved filesystem policy. */
|
|
1826
1999
|
async function readDirectory(execServer, params) {
|
|
1827
2000
|
const record = requireObject(params, "fs/readDirectory params");
|
|
1828
2001
|
return { entries: await listDirectoryEntries(execServer, requireString(record.path, "path"), resolveFsSandboxPolicy(execServer, record)) };
|
|
@@ -1854,6 +2027,7 @@ async function listDirectoryEntries(execServer, filePath, fsSandboxPolicy) {
|
|
|
1854
2027
|
};
|
|
1855
2028
|
});
|
|
1856
2029
|
}
|
|
2030
|
+
/** Removes a sandbox path after rejecting writes outside policy or under read-only descendants. */
|
|
1857
2031
|
async function removePath(execServer, params) {
|
|
1858
2032
|
const record = requireObject(params, "fs/remove params");
|
|
1859
2033
|
const filePath = requireString(record.path, "path");
|
|
@@ -1869,6 +2043,7 @@ async function removePath(execServer, params) {
|
|
|
1869
2043
|
force: record.force !== false
|
|
1870
2044
|
});
|
|
1871
2045
|
}
|
|
2046
|
+
/** Copies sandbox files or recursive directories while enforcing source and destination policy. */
|
|
1872
2047
|
async function copyPath(execServer, params) {
|
|
1873
2048
|
const record = requireObject(params, "fs/copy params");
|
|
1874
2049
|
const sourcePath = requireString(record.sourcePath ?? record.source, "sourcePath");
|
|
@@ -1933,7 +2108,13 @@ function metadataResponse(stat) {
|
|
|
1933
2108
|
}
|
|
1934
2109
|
//#endregion
|
|
1935
2110
|
//#region extensions/codex/src/app-server/sandbox-exec-server/http.ts
|
|
2111
|
+
/**
|
|
2112
|
+
* Implements sandboxed HTTP requests for Codex native tools by routing network
|
|
2113
|
+
* access through the active OpenClaw sandbox backend.
|
|
2114
|
+
*/
|
|
2115
|
+
/** Maximum JSON-line size accepted from the streaming HTTP helper process. */
|
|
1936
2116
|
const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 256 * 1024;
|
|
2117
|
+
/** Handles one sandbox HTTP JSON-RPC request, optionally streaming response body deltas. */
|
|
1937
2118
|
async function httpRequest(execServer, socket, params) {
|
|
1938
2119
|
const record = requireObject(params, "http/request params");
|
|
1939
2120
|
const requestId = requireString(record.requestId, "requestId");
|
|
@@ -2182,9 +2363,14 @@ function sendHttpBodyDelta(socket, params) {
|
|
|
2182
2363
|
}
|
|
2183
2364
|
//#endregion
|
|
2184
2365
|
//#region extensions/codex/src/app-server/sandbox-exec-server/processes.ts
|
|
2366
|
+
/**
|
|
2367
|
+
* Manages subprocess lifecycle, streaming output buffers, stdin writes, and
|
|
2368
|
+
* termination for Codex sandbox exec-server process RPCs.
|
|
2369
|
+
*/
|
|
2185
2370
|
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2186
2371
|
const RETAINED_PROCESS_OUTPUT_BYTES = 1024 * 1024;
|
|
2187
2372
|
const CLOSED_PROCESS_EVICTION_MS = 6e4;
|
|
2373
|
+
/** Starts a sandbox-backed process and registers it in the connection-local process table. */
|
|
2188
2374
|
async function startProcess(execServer, processes, socket, params) {
|
|
2189
2375
|
const record = requireObject(params, "process/start params");
|
|
2190
2376
|
const processId = requireString(record.processId, "processId");
|
|
@@ -2365,6 +2551,7 @@ function limitProcessChunks(chunks, maxBytes) {
|
|
|
2365
2551
|
}
|
|
2366
2552
|
return retained;
|
|
2367
2553
|
}
|
|
2554
|
+
/** Reads buffered process output, optionally waiting for new output or process close. */
|
|
2368
2555
|
async function readProcess(processes, params) {
|
|
2369
2556
|
const record = requireObject(params, "process/read params");
|
|
2370
2557
|
const managed = requireProcess(processes, requireString(record.processId, "processId"));
|
|
@@ -2382,6 +2569,7 @@ async function readProcess(processes, params) {
|
|
|
2382
2569
|
failure: managed.failure
|
|
2383
2570
|
};
|
|
2384
2571
|
}
|
|
2572
|
+
/** Writes base64 stdin data to a running process when stdin is still open. */
|
|
2385
2573
|
function writeProcess(processes, params) {
|
|
2386
2574
|
const record = requireObject(params, "process/write params");
|
|
2387
2575
|
const processId = requireString(record.processId, "processId");
|
|
@@ -2392,6 +2580,7 @@ function writeProcess(processes, params) {
|
|
|
2392
2580
|
managed.child.stdin.write(chunk);
|
|
2393
2581
|
return { status: "accepted" };
|
|
2394
2582
|
}
|
|
2583
|
+
/** Requests process termination and reports whether it was running at call time. */
|
|
2395
2584
|
function terminateProcess(processes, params) {
|
|
2396
2585
|
const processId = requireString(requireObject(params, "process/terminate params").processId, "processId");
|
|
2397
2586
|
const managed = processes.get(processId);
|
|
@@ -2471,7 +2660,12 @@ function readStringList(value) {
|
|
|
2471
2660
|
}
|
|
2472
2661
|
//#endregion
|
|
2473
2662
|
//#region extensions/codex/src/app-server/sandbox-exec-server.ts
|
|
2663
|
+
/**
|
|
2664
|
+
* Hosts the local OpenClaw sandbox exec-server that Codex app-server native
|
|
2665
|
+
* execution can register as an external environment.
|
|
2666
|
+
*/
|
|
2474
2667
|
const SANDBOX_EXEC_SERVERS = /* @__PURE__ */ new Map();
|
|
2668
|
+
/** Starts or reuses a sandbox exec-server and registers it with Codex app-server. */
|
|
2475
2669
|
async function ensureCodexSandboxExecServerEnvironment(params) {
|
|
2476
2670
|
if (!params.sandbox?.enabled || !params.sandbox.backend) return;
|
|
2477
2671
|
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.");
|
|
@@ -2498,6 +2692,7 @@ async function ensureCodexSandboxExecServerEnvironment(params) {
|
|
|
2498
2692
|
cwd: params.sandbox.containerWorkdir
|
|
2499
2693
|
};
|
|
2500
2694
|
}
|
|
2695
|
+
/** Releases the sandbox exec-server lease associated with a sandbox runtime. */
|
|
2501
2696
|
async function releaseCodexSandboxExecServerEnvironment(sandbox) {
|
|
2502
2697
|
if (!sandbox?.enabled) return;
|
|
2503
2698
|
const server = await SANDBOX_EXEC_SERVERS.get(sandbox.runtimeId)?.catch(() => void 0);
|
|
@@ -2659,7 +2854,16 @@ async function dispatchRequest(execServer, processes, socket, request) {
|
|
|
2659
2854
|
}
|
|
2660
2855
|
//#endregion
|
|
2661
2856
|
//#region extensions/codex/src/app-server/attempt-startup.ts
|
|
2857
|
+
/**
|
|
2858
|
+
* Startup orchestration for Codex app-server attempts, including shared-client
|
|
2859
|
+
* leasing, plugin thread config, sandbox execution environment, and thread
|
|
2860
|
+
* lifecycle binding.
|
|
2861
|
+
*/
|
|
2662
2862
|
const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
|
|
2863
|
+
/**
|
|
2864
|
+
* Starts or resumes the Codex app-server thread and returns the resources the
|
|
2865
|
+
* run loop must later release.
|
|
2866
|
+
*/
|
|
2663
2867
|
async function startCodexAttemptThread(params) {
|
|
2664
2868
|
let pluginAppServer = params.appServer;
|
|
2665
2869
|
let releaseSharedClientLease;
|
|
@@ -2984,7 +3188,15 @@ function shouldClearSharedClientAfterStartupFailure(params) {
|
|
|
2984
3188
|
}
|
|
2985
3189
|
//#endregion
|
|
2986
3190
|
//#region extensions/codex/src/app-server/attempt-steering.ts
|
|
3191
|
+
/**
|
|
3192
|
+
* Debounced steering queue for forwarding user text to an active Codex
|
|
3193
|
+
* app-server turn.
|
|
3194
|
+
*/
|
|
2987
3195
|
const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
|
|
3196
|
+
/**
|
|
3197
|
+
* Creates a queue that batches steer text while still serializing app-server
|
|
3198
|
+
* `turn/steer` requests.
|
|
3199
|
+
*/
|
|
2988
3200
|
function createCodexSteeringQueue(params) {
|
|
2989
3201
|
let batchedTexts = [];
|
|
2990
3202
|
let batchTimer;
|
|
@@ -3055,9 +3267,11 @@ function createCodexSteeringQueue(params) {
|
|
|
3055
3267
|
}
|
|
3056
3268
|
};
|
|
3057
3269
|
}
|
|
3270
|
+
/** Normalizes steer debounce milliseconds, preserving explicit zero. */
|
|
3058
3271
|
function normalizeCodexSteerDebounceMs(value) {
|
|
3059
3272
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : CODEX_STEER_ALL_DEBOUNCE_MS;
|
|
3060
3273
|
}
|
|
3274
|
+
/** Converts plain text into the Codex app-server user-input shape. */
|
|
3061
3275
|
function toCodexTextInput(text) {
|
|
3062
3276
|
return {
|
|
3063
3277
|
type: "text",
|
|
@@ -3067,6 +3281,14 @@ function toCodexTextInput(text) {
|
|
|
3067
3281
|
}
|
|
3068
3282
|
//#endregion
|
|
3069
3283
|
//#region extensions/codex/src/app-server/attempt-turn-watches.ts
|
|
3284
|
+
/**
|
|
3285
|
+
* Idle-watch controller for Codex app-server turn progress, completion, and
|
|
3286
|
+
* terminal-event gaps.
|
|
3287
|
+
*/
|
|
3288
|
+
/**
|
|
3289
|
+
* Creates a controller that arms/disarms timers as Codex app-server
|
|
3290
|
+
* notifications and tool handoffs progress.
|
|
3291
|
+
*/
|
|
3070
3292
|
function createCodexAttemptTurnWatchController(params) {
|
|
3071
3293
|
let completionIdleTimer;
|
|
3072
3294
|
let completionIdleWatchArmed = false;
|
|
@@ -3258,12 +3480,21 @@ function createCodexAttemptTurnWatchController(params) {
|
|
|
3258
3480
|
scheduleCompletionIdleWatch();
|
|
3259
3481
|
return;
|
|
3260
3482
|
}
|
|
3483
|
+
const details = {
|
|
3484
|
+
...completionLastActivityDetails,
|
|
3485
|
+
activeAppServerTurnRequests: params.getActiveAppServerTurnRequests(),
|
|
3486
|
+
activeTurnItemCount: params.getActiveTurnItemCount(),
|
|
3487
|
+
terminalTurnNotificationQueued: params.isTerminalTurnNotificationQueued(),
|
|
3488
|
+
completionIdleWatchArmed,
|
|
3489
|
+
assistantCompletionIdleWatchArmed,
|
|
3490
|
+
terminalIdleWatchArmed
|
|
3491
|
+
};
|
|
3261
3492
|
const timeout = {
|
|
3262
3493
|
kind: "completion",
|
|
3263
3494
|
idleMs,
|
|
3264
3495
|
timeoutMs,
|
|
3265
3496
|
lastActivityReason: completionLastActivityReason,
|
|
3266
|
-
details
|
|
3497
|
+
details
|
|
3267
3498
|
};
|
|
3268
3499
|
params.onTimeout(timeout);
|
|
3269
3500
|
params.onMarkTimedOut();
|
|
@@ -3385,8 +3616,10 @@ function createCodexAttemptTurnWatchController(params) {
|
|
|
3385
3616
|
clearAllTimers
|
|
3386
3617
|
};
|
|
3387
3618
|
}
|
|
3619
|
+
/** Hard cap for per-call Codex dynamic tool timeout overrides. */
|
|
3388
3620
|
const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
|
|
3389
3621
|
const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
|
|
3622
|
+
/** Timeout for message-delivery dynamic tool calls. */
|
|
3390
3623
|
const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 12e4;
|
|
3391
3624
|
const LOG_FIELD_MAX_LENGTH = 160;
|
|
3392
3625
|
function normalizeLogField(value) {
|
|
@@ -3436,6 +3669,10 @@ function formatDynamicToolTimeoutDetails(params) {
|
|
|
3436
3669
|
}
|
|
3437
3670
|
};
|
|
3438
3671
|
}
|
|
3672
|
+
/**
|
|
3673
|
+
* Runs a dynamic tool call with run-abort and per-call timeout handling,
|
|
3674
|
+
* returning a Codex protocol response instead of throwing.
|
|
3675
|
+
*/
|
|
3439
3676
|
async function handleDynamicToolCallWithTimeout(params) {
|
|
3440
3677
|
if (params.signal.aborted) return failedDynamicToolResponse("OpenClaw dynamic tool call aborted before execution.");
|
|
3441
3678
|
const controller = new AbortController();
|
|
@@ -3505,12 +3742,14 @@ function failedDynamicToolResponse(message, options) {
|
|
|
3505
3742
|
});
|
|
3506
3743
|
return response;
|
|
3507
3744
|
}
|
|
3745
|
+
/** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
|
|
3508
3746
|
function toCodexDynamicToolProtocolResponse(response) {
|
|
3509
3747
|
return {
|
|
3510
3748
|
contentItems: response.contentItems,
|
|
3511
3749
|
success: response.success
|
|
3512
3750
|
};
|
|
3513
3751
|
}
|
|
3752
|
+
/** Adds async-started progress details when a tool result continues out of band. */
|
|
3514
3753
|
function toCodexDynamicToolProgressResponse(response, protocolResponse) {
|
|
3515
3754
|
if (response.asyncStarted !== true) return protocolResponse;
|
|
3516
3755
|
return {
|
|
@@ -3521,21 +3760,26 @@ function toCodexDynamicToolProgressResponse(response, protocolResponse) {
|
|
|
3521
3760
|
}
|
|
3522
3761
|
};
|
|
3523
3762
|
}
|
|
3763
|
+
/** Decides whether a terminal dynamic tool response can release the Codex turn. */
|
|
3524
3764
|
function shouldReleaseTurnAfterTerminalDynamicTool(state) {
|
|
3525
3765
|
return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
|
|
3526
3766
|
}
|
|
3767
|
+
/** Returns true when a non-async result should block terminal-release shortcuts. */
|
|
3527
3768
|
function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
|
|
3528
3769
|
return response.asyncStarted !== true;
|
|
3529
3770
|
}
|
|
3771
|
+
/** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
|
|
3530
3772
|
function resolveTerminalDynamicToolBatchAction(state) {
|
|
3531
3773
|
if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
|
|
3532
3774
|
if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
|
|
3533
3775
|
if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
|
|
3534
3776
|
return "idle";
|
|
3535
3777
|
}
|
|
3778
|
+
/** Returns true for diagnostic events that terminate a dynamic tool call. */
|
|
3536
3779
|
function isDynamicToolTerminalDiagnosticEvent(event) {
|
|
3537
3780
|
return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
|
|
3538
3781
|
}
|
|
3782
|
+
/** Matches terminal diagnostics to a specific dynamic tool call id/name. */
|
|
3539
3783
|
function isMatchingDynamicToolTerminalDiagnostic(params) {
|
|
3540
3784
|
if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
|
|
3541
3785
|
if (params.runId !== void 0) return params.event.runId === params.runId;
|
|
@@ -3543,6 +3787,7 @@ function isMatchingDynamicToolTerminalDiagnostic(params) {
|
|
|
3543
3787
|
if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
|
|
3544
3788
|
return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
|
|
3545
3789
|
}
|
|
3790
|
+
/** Checks pending diagnostics for a terminal event matching a tool call. */
|
|
3546
3791
|
function hasPendingDynamicToolTerminalDiagnostic(params) {
|
|
3547
3792
|
return hasPendingInternalDiagnosticEvent((event) => {
|
|
3548
3793
|
if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
|
|
@@ -3555,6 +3800,7 @@ function hasPendingDynamicToolTerminalDiagnostic(params) {
|
|
|
3555
3800
|
});
|
|
3556
3801
|
});
|
|
3557
3802
|
}
|
|
3803
|
+
/** Resolves per-tool timeout, applying media/message defaults and hard caps. */
|
|
3558
3804
|
function resolveDynamicToolCallTimeoutMs(params) {
|
|
3559
3805
|
return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? 9e4);
|
|
3560
3806
|
}
|
|
@@ -3589,6 +3835,7 @@ const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
|
|
|
3589
3835
|
function normalizeRuntimeId(value) {
|
|
3590
3836
|
return value?.trim().toLowerCase() ?? "";
|
|
3591
3837
|
}
|
|
3838
|
+
/** Maps local Codex runtime plans onto the provider/api pair exposed to event projection. */
|
|
3592
3839
|
function resolveCodexLocalRuntimeAttribution(params) {
|
|
3593
3840
|
const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
|
|
3594
3841
|
if (normalizeRuntimeId(params.runtimePlan?.observability.harnessId) === "codex" && authProfileProvider !== OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.provider) === OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.api) === OPENAI_RESPONSES_API) return {
|
|
@@ -3602,9 +3849,15 @@ function resolveCodexLocalRuntimeAttribution(params) {
|
|
|
3602
3849
|
}
|
|
3603
3850
|
//#endregion
|
|
3604
3851
|
//#region extensions/codex/src/app-server/tool-progress-normalization.ts
|
|
3852
|
+
/**
|
|
3853
|
+
* Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
|
|
3854
|
+
* emitted into OpenClaw events or logs.
|
|
3855
|
+
*/
|
|
3856
|
+
/** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
|
|
3605
3857
|
function resolveCodexToolProgressDetailMode(value) {
|
|
3606
3858
|
return value === "raw" ? "raw" : "explain";
|
|
3607
3859
|
}
|
|
3860
|
+
/** Recursively redacts sensitive strings and handles circular values in event payloads. */
|
|
3608
3861
|
function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
3609
3862
|
if (typeof value === "string") return redactToolPayloadText(value);
|
|
3610
3863
|
if (Array.isArray(value)) {
|
|
@@ -3621,16 +3874,20 @@ function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet(
|
|
|
3621
3874
|
}
|
|
3622
3875
|
return value;
|
|
3623
3876
|
}
|
|
3877
|
+
/** Sanitizes a record-shaped Codex agent event payload. */
|
|
3624
3878
|
function sanitizeCodexAgentEventRecord(value) {
|
|
3625
3879
|
return sanitizeCodexAgentEventValue(value);
|
|
3626
3880
|
}
|
|
3881
|
+
/** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
|
|
3627
3882
|
function sanitizeCodexToolArguments(value) {
|
|
3628
3883
|
if (!isJsonObject(value)) return;
|
|
3629
3884
|
return sanitizeCodexAgentEventRecord(value);
|
|
3630
3885
|
}
|
|
3886
|
+
/** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
|
|
3631
3887
|
function sanitizeCodexToolResponse(response) {
|
|
3632
3888
|
return sanitizeCodexAgentEventRecord(response);
|
|
3633
3889
|
}
|
|
3890
|
+
/** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
|
|
3634
3891
|
function inferCodexDynamicToolMeta(call, detailMode) {
|
|
3635
3892
|
return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
|
|
3636
3893
|
}
|
|
@@ -3994,6 +4251,10 @@ var CodexAppServerEventProjector = class {
|
|
|
3994
4251
|
getCompletedTurnStatus() {
|
|
3995
4252
|
return this.completedTurn?.status;
|
|
3996
4253
|
}
|
|
4254
|
+
hasCompletedTerminalAssistantText() {
|
|
4255
|
+
const finalItem = this.resolveFinalAssistantTextItem();
|
|
4256
|
+
return finalItem !== void 0 && this.completedItemIds.has(finalItem.itemId);
|
|
4257
|
+
}
|
|
3997
4258
|
async handleNotification(notification) {
|
|
3998
4259
|
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
3999
4260
|
if (!params) return;
|
|
@@ -5023,12 +5284,18 @@ var CodexAppServerEventProjector = class {
|
|
|
5023
5284
|
return finalText ? [finalText] : [];
|
|
5024
5285
|
}
|
|
5025
5286
|
resolveFinalAssistantText() {
|
|
5287
|
+
return this.resolveFinalAssistantTextItem()?.text;
|
|
5288
|
+
}
|
|
5289
|
+
resolveFinalAssistantTextItem() {
|
|
5026
5290
|
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5027
5291
|
const itemId = this.assistantItemOrder[i];
|
|
5028
5292
|
if (!itemId) continue;
|
|
5029
5293
|
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
5030
5294
|
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5031
|
-
if (text && !this.toolProgressTexts.has(text)) return
|
|
5295
|
+
if (text && !this.toolProgressTexts.has(text)) return {
|
|
5296
|
+
itemId,
|
|
5297
|
+
text
|
|
5298
|
+
};
|
|
5032
5299
|
}
|
|
5033
5300
|
}
|
|
5034
5301
|
rememberAssistantItem(itemId) {
|
|
@@ -5546,6 +5813,7 @@ function readTurn(value) {
|
|
|
5546
5813
|
//#region extensions/codex/src/app-server/native-subagent-notification.ts
|
|
5547
5814
|
const CODEX_SUBAGENT_NOTIFICATION_START = "<subagent_notification>";
|
|
5548
5815
|
const CODEX_SUBAGENT_NOTIFICATION_END = "</subagent_notification>";
|
|
5816
|
+
/** Extracts trusted subagent completion payloads from a Codex server notification. */
|
|
5549
5817
|
function extractCodexNativeSubagentCompletions(notification) {
|
|
5550
5818
|
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
5551
5819
|
if (!params) return [];
|
|
@@ -5556,6 +5824,7 @@ function extractCodexNativeSubagentCompletions(notification) {
|
|
|
5556
5824
|
const author = readTrustedInterAgentCommunicationAuthor(item);
|
|
5557
5825
|
return extractCodexNativeSubagentCompletionsFromText(text).filter((completion) => completion.agentPath === author);
|
|
5558
5826
|
}
|
|
5827
|
+
/** Parses one or more tagged subagent completion payloads from commentary text. */
|
|
5559
5828
|
function extractCodexNativeSubagentCompletionsFromText(text) {
|
|
5560
5829
|
const completions = [];
|
|
5561
5830
|
let cursor = 0;
|
|
@@ -5656,11 +5925,19 @@ function normalizeStatusKey(value) {
|
|
|
5656
5925
|
}
|
|
5657
5926
|
//#endregion
|
|
5658
5927
|
//#region extensions/codex/src/app-server/native-subagent-task-ids.ts
|
|
5928
|
+
/**
|
|
5929
|
+
* Shared identifiers for representing Codex native subagents as OpenClaw task
|
|
5930
|
+
* runtime rows.
|
|
5931
|
+
*/
|
|
5932
|
+
/** Task runtime namespace for Codex native subagent task rows. */
|
|
5659
5933
|
const CODEX_NATIVE_SUBAGENT_RUNTIME = "subagent";
|
|
5934
|
+
/** Task kind used to distinguish native Codex subagents from other subagent runtimes. */
|
|
5660
5935
|
const CODEX_NATIVE_SUBAGENT_TASK_KIND = "codex-native";
|
|
5936
|
+
/** Run id prefix for task rows keyed by Codex child thread ids. */
|
|
5661
5937
|
const CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX = "codex-thread:";
|
|
5662
5938
|
//#endregion
|
|
5663
5939
|
//#region extensions/codex/src/app-server/native-subagent-task-mirror.ts
|
|
5940
|
+
/** Projects Codex thread and collab-agent notifications into task lifecycle updates. */
|
|
5664
5941
|
var CodexNativeSubagentTaskMirror = class {
|
|
5665
5942
|
constructor(params, runtime) {
|
|
5666
5943
|
this.params = params;
|
|
@@ -5875,9 +6152,11 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5875
6152
|
});
|
|
5876
6153
|
}
|
|
5877
6154
|
};
|
|
6155
|
+
/** Converts a Codex child thread id into the OpenClaw task-runtime run id. */
|
|
5878
6156
|
function codexNativeSubagentRunId(threadId) {
|
|
5879
6157
|
return `${CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX}${threadId.trim()}`;
|
|
5880
6158
|
}
|
|
6159
|
+
/** Reads a subagent thread-spawn source only when it belongs to the expected parent thread. */
|
|
5881
6160
|
function readSubagentThreadSpawnSource(source, parentThreadId) {
|
|
5882
6161
|
if (!source || typeof source !== "object" || !("subAgent" in source)) return;
|
|
5883
6162
|
const subAgent = source.subAgent;
|
|
@@ -5970,6 +6249,10 @@ function trimOptional(value) {
|
|
|
5970
6249
|
}
|
|
5971
6250
|
//#endregion
|
|
5972
6251
|
//#region extensions/codex/src/app-server/native-subagent-monitor.ts
|
|
6252
|
+
/**
|
|
6253
|
+
* Monitors Codex native subagent threads and mirrors their lifecycle/completion
|
|
6254
|
+
* into OpenClaw task runtime records for parent sessions.
|
|
6255
|
+
*/
|
|
5973
6256
|
const DEFAULT_TRANSCRIPT_POLL_DELAYS_MS = [
|
|
5974
6257
|
2e3,
|
|
5975
6258
|
5e3,
|
|
@@ -5995,6 +6278,7 @@ const defaultRuntime = {
|
|
|
5995
6278
|
deliverAgentHarnessTaskCompletion
|
|
5996
6279
|
};
|
|
5997
6280
|
const monitors = /* @__PURE__ */ new WeakMap();
|
|
6281
|
+
/** Registers or updates the monitor bound to a Codex app-server client. */
|
|
5998
6282
|
function registerCodexNativeSubagentMonitor(params) {
|
|
5999
6283
|
let monitor = monitors.get(params.client);
|
|
6000
6284
|
if (!monitor) {
|
|
@@ -6008,6 +6292,7 @@ function registerCodexNativeSubagentMonitor(params) {
|
|
|
6008
6292
|
agentId: params.agentId
|
|
6009
6293
|
});
|
|
6010
6294
|
}
|
|
6295
|
+
/** Tracks native subagent thread notifications, transcript completions, and task delivery. */
|
|
6011
6296
|
var CodexNativeSubagentMonitor = class {
|
|
6012
6297
|
constructor(client, runtime = defaultRuntime, options = {}) {
|
|
6013
6298
|
this.runtime = runtime;
|
|
@@ -6805,6 +7090,7 @@ function minFiniteNumber(values) {
|
|
|
6805
7090
|
function hasContextEngineThreadBootstrapProjection(binding) {
|
|
6806
7091
|
return binding.contextEngine?.projection?.mode === "thread_bootstrap";
|
|
6807
7092
|
}
|
|
7093
|
+
/** Clears and drops a binding when the native Codex thread is too large to resume safely. */
|
|
6808
7094
|
async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
6809
7095
|
const binding = params.binding;
|
|
6810
7096
|
if (!binding?.threadId) return binding;
|
|
@@ -6871,6 +7157,10 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
|
6871
7157
|
}
|
|
6872
7158
|
//#endregion
|
|
6873
7159
|
//#region extensions/codex/src/app-server/trajectory.ts
|
|
7160
|
+
/**
|
|
7161
|
+
* Records optional Codex runtime trajectory sidecars with bounded, redacted
|
|
7162
|
+
* context and completion events.
|
|
7163
|
+
*/
|
|
6874
7164
|
const SENSITIVE_FIELD_RE = /(?:authorization|cookie|credential|key|password|passwd|secret|token)/iu;
|
|
6875
7165
|
const PRIVATE_PAYLOAD_FIELD_RE = /(?:image|screenshot|attachment|fileData|dataUri)/iu;
|
|
6876
7166
|
const AUTHORIZATION_VALUE_RE = /\b(Bearer|Basic)\s+[A-Za-z0-9+/._~=-]{8,}/giu;
|
|
@@ -6878,6 +7168,7 @@ const JWT_VALUE_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]
|
|
|
6878
7168
|
const COOKIE_PAIR_RE = /\b([A-Za-z][A-Za-z0-9_.-]{1,64})=([A-Za-z0-9+/._~%=-]{16,})(?=;|\s|$)/gu;
|
|
6879
7169
|
const TRAJECTORY_RUNTIME_FILE_MAX_BYTES = 50 * 1024 * 1024;
|
|
6880
7170
|
const TRAJECTORY_RUNTIME_EVENT_MAX_BYTES = 256 * 1024;
|
|
7171
|
+
/** Resolves secure create/truncate flags for trajectory pointer files. */
|
|
6881
7172
|
function resolveCodexTrajectoryPointerFlags(constants = fsSync.constants) {
|
|
6882
7173
|
const noFollow = constants.O_NOFOLLOW;
|
|
6883
7174
|
return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY | (typeof noFollow === "number" ? noFollow : 0);
|
|
@@ -6932,6 +7223,7 @@ function writeTrajectoryPointerBestEffort(params) {
|
|
|
6932
7223
|
}
|
|
6933
7224
|
} catch {}
|
|
6934
7225
|
}
|
|
7226
|
+
/** Creates a trajectory recorder when trajectory capture is enabled for the environment. */
|
|
6935
7227
|
function createCodexTrajectoryRecorder(params) {
|
|
6936
7228
|
const env = params.env ?? process.env;
|
|
6937
7229
|
if (!parseTrajectoryEnabled(env)) return null;
|
|
@@ -6981,6 +7273,7 @@ function createCodexTrajectoryRecorder(params) {
|
|
|
6981
7273
|
}
|
|
6982
7274
|
};
|
|
6983
7275
|
}
|
|
7276
|
+
/** Records compiled prompt/tool context at the start of a Codex runtime attempt. */
|
|
6984
7277
|
function recordCodexTrajectoryContext(recorder, params) {
|
|
6985
7278
|
if (!recorder) return;
|
|
6986
7279
|
recorder.recordEvent("context.compiled", {
|
|
@@ -6990,6 +7283,7 @@ function recordCodexTrajectoryContext(recorder, params) {
|
|
|
6990
7283
|
tools: toTrajectoryToolDefinitions(params.tools)
|
|
6991
7284
|
});
|
|
6992
7285
|
}
|
|
7286
|
+
/** Records final Codex model completion metadata and assistant snapshots. */
|
|
6993
7287
|
function recordCodexTrajectoryCompletion(recorder, params) {
|
|
6994
7288
|
if (!recorder) return;
|
|
6995
7289
|
recorder.recordEvent("model.completed", {
|
|
@@ -7059,6 +7353,7 @@ function sanitizeValue(value, depth = 0, key = "") {
|
|
|
7059
7353
|
function redactSensitiveString(value) {
|
|
7060
7354
|
return value.replace(AUTHORIZATION_VALUE_RE, "$1 <redacted>").replace(JWT_VALUE_RE, "<redacted-jwt>").replace(COOKIE_PAIR_RE, "$1=<redacted>");
|
|
7061
7355
|
}
|
|
7356
|
+
/** Converts arbitrary prompt errors into trajectory-safe text. */
|
|
7062
7357
|
function normalizeCodexTrajectoryError(value) {
|
|
7063
7358
|
if (!value) return null;
|
|
7064
7359
|
if (value instanceof Error) return value.message;
|
|
@@ -7071,7 +7366,12 @@ function normalizeCodexTrajectoryError(value) {
|
|
|
7071
7366
|
}
|
|
7072
7367
|
//#endregion
|
|
7073
7368
|
//#region extensions/codex/src/app-server/usage-limit-error.ts
|
|
7369
|
+
/**
|
|
7370
|
+
* Enriches Codex usage-limit failures with current rate-limit information and
|
|
7371
|
+
* marks blocked auth profiles when Codex exposes a reset time.
|
|
7372
|
+
*/
|
|
7074
7373
|
const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
|
|
7374
|
+
/** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
|
|
7075
7375
|
async function markCodexAuthProfileBlockedFromRateLimits(params) {
|
|
7076
7376
|
const authProfileId = params.authProfileId?.trim();
|
|
7077
7377
|
if (!authProfileId || !params.params.authProfileStore) return;
|
|
@@ -7094,6 +7394,7 @@ async function markCodexAuthProfileBlockedFromRateLimits(params) {
|
|
|
7094
7394
|
});
|
|
7095
7395
|
}
|
|
7096
7396
|
}
|
|
7397
|
+
/** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
|
|
7097
7398
|
async function formatCodexTurnStartUsageLimitError(params) {
|
|
7098
7399
|
return refreshCodexUsageLimitError({
|
|
7099
7400
|
client: params.client,
|
|
@@ -7102,6 +7403,7 @@ async function formatCodexTurnStartUsageLimitError(params) {
|
|
|
7102
7403
|
signal: params.signal
|
|
7103
7404
|
});
|
|
7104
7405
|
}
|
|
7406
|
+
/** Refreshes a generic prompt usage-limit message into a reset-aware message. */
|
|
7105
7407
|
async function refreshCodexUsageLimitPromptError(params) {
|
|
7106
7408
|
if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
|
|
7107
7409
|
return (await refreshCodexUsageLimitError({
|
|
@@ -7211,6 +7513,11 @@ function readString$1(record, key) {
|
|
|
7211
7513
|
}
|
|
7212
7514
|
//#endregion
|
|
7213
7515
|
//#region extensions/codex/src/app-server/user-input-bridge.ts
|
|
7516
|
+
/**
|
|
7517
|
+
* Bridges Codex item/tool user-input requests to OpenClaw messaging prompts and
|
|
7518
|
+
* turns replies into app-server answer payloads.
|
|
7519
|
+
*/
|
|
7520
|
+
/** Creates a per-turn bridge for pending Codex user-input requests. */
|
|
7214
7521
|
function createCodexUserInputBridge(params) {
|
|
7215
7522
|
let pending;
|
|
7216
7523
|
const resolvePending = (value) => {
|
|
@@ -8051,6 +8358,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8051
8358
|
let timedOut = false;
|
|
8052
8359
|
let turnCompletionIdleTimedOut = false;
|
|
8053
8360
|
let turnWatchTimeoutKind;
|
|
8361
|
+
let turnWatchTimeoutIdleMs;
|
|
8362
|
+
let turnWatchTimeoutMs;
|
|
8363
|
+
let turnWatchTimeoutLastActivityReason;
|
|
8364
|
+
let turnWatchTimeoutDetails;
|
|
8054
8365
|
let turnCompletionIdleTimeoutMessage;
|
|
8055
8366
|
let clientClosedPromptError;
|
|
8056
8367
|
let clientClosedAbort = false;
|
|
@@ -8111,6 +8422,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8111
8422
|
timedOut = true;
|
|
8112
8423
|
turnCompletionIdleTimedOut = true;
|
|
8113
8424
|
turnWatchTimeoutKind = timeout.kind;
|
|
8425
|
+
turnWatchTimeoutIdleMs = timeout.idleMs;
|
|
8426
|
+
turnWatchTimeoutMs = timeout.timeoutMs;
|
|
8427
|
+
turnWatchTimeoutLastActivityReason = timeout.lastActivityReason;
|
|
8428
|
+
turnWatchTimeoutDetails = timeout.details;
|
|
8114
8429
|
turnCompletionIdleTimeoutMessage = "codex app-server turn idle timed out waiting for turn/completed";
|
|
8115
8430
|
},
|
|
8116
8431
|
onMarkTimedOut: () => projectorRef.current?.markTimedOut(),
|
|
@@ -8905,7 +9220,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8905
9220
|
await notificationQueue;
|
|
8906
9221
|
const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected });
|
|
8907
9222
|
const finalAborted = result.aborted || runAbortController.signal.aborted && !clientClosedAbort;
|
|
8908
|
-
|
|
9223
|
+
const canUseCompletedAssistantTextAfterClientClose = activeProjector.hasCompletedTerminalAssistantText() && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0;
|
|
9224
|
+
const clientClosedPromptErrorForFinal = clientClosedPromptError && canUseCompletedAssistantTextAfterClientClose ? void 0 : clientClosedPromptError;
|
|
9225
|
+
let finalPromptError = clientClosedPromptErrorForFinal ?? (turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError);
|
|
8909
9226
|
const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
|
|
8910
9227
|
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(activeSessionFile, {
|
|
8911
9228
|
phase: "turn_completed",
|
|
@@ -8934,13 +9251,20 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8934
9251
|
signal: runAbortController.signal
|
|
8935
9252
|
});
|
|
8936
9253
|
if (refreshedUsageLimitPromptError) finalPromptError = refreshedUsageLimitPromptError;
|
|
8937
|
-
const finalPromptErrorSource = timedOut ||
|
|
8938
|
-
const codexAppServerFailureKind =
|
|
9254
|
+
const finalPromptErrorSource = timedOut || clientClosedPromptErrorForFinal ? "prompt" : result.promptErrorSource;
|
|
9255
|
+
const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : turnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
|
|
8939
9256
|
const codexAppServerReplayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
|
|
8940
9257
|
const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome({
|
|
8941
9258
|
result,
|
|
8942
|
-
turnCompletionIdleTimedOut
|
|
9259
|
+
turnCompletionIdleTimedOut,
|
|
9260
|
+
turnWatchTimeoutKind
|
|
8943
9261
|
});
|
|
9262
|
+
const codexAppServerFailureDiagnostics = codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind === "completion" ? buildCodexAppServerTimeoutDiagnostics({
|
|
9263
|
+
idleMs: turnWatchTimeoutIdleMs,
|
|
9264
|
+
timeoutMs: turnWatchTimeoutMs,
|
|
9265
|
+
lastActivityReason: turnWatchTimeoutLastActivityReason,
|
|
9266
|
+
details: turnWatchTimeoutDetails
|
|
9267
|
+
}) : void 0;
|
|
8944
9268
|
const modelCallFailureKind = classifyCodexModelCallFailureKind({
|
|
8945
9269
|
error: finalPromptError,
|
|
8946
9270
|
timedOut,
|
|
@@ -9020,7 +9344,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9020
9344
|
}),
|
|
9021
9345
|
runMaintenance: runHarnessContextEngineMaintenance,
|
|
9022
9346
|
config: params.config,
|
|
9023
|
-
warn: (message) => embeddedAgentLog.warn(message)
|
|
9347
|
+
warn: (message) => embeddedAgentLog.warn(message),
|
|
9348
|
+
isHeartbeat: params.bootstrapContextRunKind === "heartbeat"
|
|
9024
9349
|
});
|
|
9025
9350
|
}
|
|
9026
9351
|
runAgentHarnessLlmOutputHook({
|
|
@@ -9070,7 +9395,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9070
9395
|
threadId: thread.threadId,
|
|
9071
9396
|
turnId: activeTurnId,
|
|
9072
9397
|
replaySafe: codexAppServerReplayBlockedReason === void 0,
|
|
9073
|
-
...codexAppServerReplayBlockedReason ? { replayBlockedReason: codexAppServerReplayBlockedReason } : {}
|
|
9398
|
+
...codexAppServerReplayBlockedReason ? { replayBlockedReason: codexAppServerReplayBlockedReason } : {},
|
|
9399
|
+
...codexAppServerFailureDiagnostics ? { diagnostics: codexAppServerFailureDiagnostics } : {}
|
|
9074
9400
|
} } : {},
|
|
9075
9401
|
...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
|
|
9076
9402
|
systemPromptReport
|
|
@@ -9179,6 +9505,36 @@ function waitForCodexNotificationDispatchTurn() {
|
|
|
9179
9505
|
setImmediate(resolve);
|
|
9180
9506
|
});
|
|
9181
9507
|
}
|
|
9508
|
+
function buildCodexAppServerTimeoutDiagnostics(params) {
|
|
9509
|
+
const readString = (key) => {
|
|
9510
|
+
const value = params.details?.[key];
|
|
9511
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
9512
|
+
};
|
|
9513
|
+
const readNumber = (key) => {
|
|
9514
|
+
const value = params.details?.[key];
|
|
9515
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
9516
|
+
};
|
|
9517
|
+
const readBoolean = (key) => {
|
|
9518
|
+
const value = params.details?.[key];
|
|
9519
|
+
return typeof value === "boolean" ? value : void 0;
|
|
9520
|
+
};
|
|
9521
|
+
return {
|
|
9522
|
+
...params.idleMs !== void 0 ? { idleMs: params.idleMs } : {},
|
|
9523
|
+
...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
|
|
9524
|
+
...params.lastActivityReason ? { lastActivityReason: params.lastActivityReason } : {},
|
|
9525
|
+
...readString("lastNotificationMethod") ? { lastNotificationMethod: readString("lastNotificationMethod") } : {},
|
|
9526
|
+
...readString("lastNotificationItemId") ? { lastNotificationItemId: readString("lastNotificationItemId") } : {},
|
|
9527
|
+
...readString("lastNotificationItemType") ? { lastNotificationItemType: readString("lastNotificationItemType") } : {},
|
|
9528
|
+
...readString("lastNotificationItemRole") ? { lastNotificationItemRole: readString("lastNotificationItemRole") } : {},
|
|
9529
|
+
...readString("lastAssistantTextPreview") ? { lastAssistantTextPreview: readString("lastAssistantTextPreview") } : {},
|
|
9530
|
+
...readNumber("activeAppServerTurnRequests") !== void 0 ? { activeAppServerTurnRequests: readNumber("activeAppServerTurnRequests") } : {},
|
|
9531
|
+
...readNumber("activeTurnItemCount") !== void 0 ? { activeTurnItemCount: readNumber("activeTurnItemCount") } : {},
|
|
9532
|
+
...readBoolean("terminalTurnNotificationQueued") !== void 0 ? { terminalTurnNotificationQueued: readBoolean("terminalTurnNotificationQueued") } : {},
|
|
9533
|
+
...readBoolean("completionIdleWatchArmed") !== void 0 ? { completionIdleWatchArmed: readBoolean("completionIdleWatchArmed") } : {},
|
|
9534
|
+
...readBoolean("assistantCompletionIdleWatchArmed") !== void 0 ? { assistantCompletionIdleWatchArmed: readBoolean("assistantCompletionIdleWatchArmed") } : {},
|
|
9535
|
+
...readBoolean("terminalIdleWatchArmed") !== void 0 ? { terminalIdleWatchArmed: readBoolean("terminalIdleWatchArmed") } : {}
|
|
9536
|
+
};
|
|
9537
|
+
}
|
|
9182
9538
|
function handleApprovalRequest(params) {
|
|
9183
9539
|
return handleCodexAppServerApprovalRequest({
|
|
9184
9540
|
method: params.method,
|