@openclaw/codex 2026.5.16-beta.3 → 2026.5.16-beta.5
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-CA7amCZ8.js → client-6FkrXfaz.js} +1 -1
- package/dist/{client-factory-DwzPofwS.js → client-factory-9L6Ie1dC.js} +1 -1
- package/dist/{command-handlers-C4F8BChC.js → command-handlers-DcoFMn4e.js} +91 -37
- package/dist/{compact-96QPqIAu.js → compact-UKyG6Pw2.js} +3 -3
- package/dist/{computer-use-BX02ojP4.js → computer-use-UJ3dxrXF.js} +2 -2
- package/dist/{config-3ATInASk.js → config-B5pq6hEz.js} +7 -1
- package/dist/harness.js +9 -6
- package/dist/index.js +11 -8
- package/dist/media-understanding-provider.js +3 -3
- package/dist/{models-BZLMz9WS.js → models-C1zn2RN0.js} +1 -1
- package/dist/{node-cli-sessions-BLDFnUEF.js → node-cli-sessions-C9rlYMPw.js} +83 -35
- package/dist/{plugin-activation-BDU4e-NA.js → plugin-activation-B49xb7pI.js} +1 -1
- package/dist/provider.js +2 -2
- package/dist/{request-Czoa60Nt.js → request-XPjLayGw.js} +3 -3
- package/dist/{run-attempt-B3nk5oT1.js → run-attempt-Bssnu2uw.js} +355 -117
- package/dist/{session-binding-DbdVqMzW.js → session-binding-DqApZIgD.js} +1 -1
- package/dist/{shared-client-CnbrvEfV.js → shared-client-DlvmoLBJ.js} +70 -36
- package/dist/{side-question-D3nH-Jk1.js → side-question-DgVgwmjR.js} +107 -20
- package/dist/test-api.js +2 -2
- package/dist/{thread-lifecycle-D2sxRPdz.js → thread-lifecycle-BbriKhTq.js} +215 -17
- package/dist/{vision-tools-Cm_YicZb.js → vision-tools-BX9YuTEK.js} +203 -7
- package/package.json +5 -5
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { r as codexSandboxPolicyForTurn, u as resolveCodexPluginsPolicy } from "./config-
|
|
1
|
+
import { r as codexSandboxPolicyForTurn, u as resolveCodexPluginsPolicy } from "./config-B5pq6hEz.js";
|
|
2
2
|
import { n as assertCodexThreadResumeResponse, r as assertCodexThreadStartResponse } from "./protocol-validators-BGBspNmF.js";
|
|
3
3
|
import { t as isJsonObject } from "./protocol-C9UWI98H.js";
|
|
4
4
|
import { CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY, renderCodexPromptOverlay } from "./prompt-overlay.js";
|
|
5
5
|
import { isModernCodexModel } from "./provider.js";
|
|
6
|
-
import { i as isCodexAppServerConnectionClosedError } from "./client-
|
|
7
|
-
import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-
|
|
8
|
-
import { a as defaultCodexAppInventoryCache, r as readCodexPluginInventory, t as ensureCodexPluginActivation } from "./plugin-activation-
|
|
6
|
+
import { i as isCodexAppServerConnectionClosedError } from "./client-6FkrXfaz.js";
|
|
7
|
+
import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-DqApZIgD.js";
|
|
8
|
+
import { a as defaultCodexAppInventoryCache, r as readCodexPluginInventory, t as ensureCodexPluginActivation } from "./plugin-activation-B49xb7pI.js";
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
import { HEARTBEAT_RESPONSE_TOOL_NAME, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractToolResultMediaArtifact, filterToolResultMediaUrls, isActiveHarnessContextEngine, isMessagingTool, isMessagingToolSendAction, isToolWrappedWithBeforeToolCallHook, normalizeHeartbeatToolResponse, runAgentHarnessAfterToolCallHook, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
11
|
+
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
11
12
|
import { buildCodexUserMcpServersThreadConfigPatch } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
12
13
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
13
14
|
//#region extensions/codex/src/app-server/dynamic-tool-profile.ts
|
|
@@ -32,9 +33,15 @@ function normalizeCodexDynamicToolName(name) {
|
|
|
32
33
|
const normalized = name.trim().toLowerCase();
|
|
33
34
|
return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
|
|
34
35
|
}
|
|
35
|
-
function
|
|
36
|
+
function isForcedPrivateQaCodexRuntime(env = process.env) {
|
|
37
|
+
return env.OPENCLAW_BUILD_PRIVATE_QA === "1" && env.OPENCLAW_QA_FORCE_RUNTIME?.trim().toLowerCase() === "codex";
|
|
38
|
+
}
|
|
39
|
+
function resolveCodexDynamicToolsLoading(config, env = process.env) {
|
|
40
|
+
return isForcedPrivateQaCodexRuntime(env) ? "direct" : config.codexDynamicToolsLoading ?? "searchable";
|
|
41
|
+
}
|
|
42
|
+
function filterCodexDynamicTools(tools, config, env = process.env) {
|
|
36
43
|
const excludes = /* @__PURE__ */ new Set();
|
|
37
|
-
for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) excludes.add(name);
|
|
44
|
+
if (!isForcedPrivateQaCodexRuntime(env)) for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) excludes.add(name);
|
|
38
45
|
for (const name of config.codexDynamicToolsExclude ?? []) {
|
|
39
46
|
const trimmed = normalizeCodexDynamicToolName(name);
|
|
40
47
|
if (trimmed) excludes.add(trimmed);
|
|
@@ -42,11 +49,113 @@ function filterCodexDynamicTools(tools, config) {
|
|
|
42
49
|
return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(normalizeCodexDynamicToolName(tool.name)));
|
|
43
50
|
}
|
|
44
51
|
//#endregion
|
|
52
|
+
//#region extensions/codex/src/app-server/image-payload-sanitizer.ts
|
|
53
|
+
const DATA_URL_PREFIX = "data:";
|
|
54
|
+
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
|
|
55
|
+
function startsWithDataUrl(value) {
|
|
56
|
+
return value.slice(0, 5).toLowerCase() === DATA_URL_PREFIX;
|
|
57
|
+
}
|
|
58
|
+
function canonicalizeBase64(base64) {
|
|
59
|
+
let cleaned = "";
|
|
60
|
+
let padding = 0;
|
|
61
|
+
let sawPadding = false;
|
|
62
|
+
for (let i = 0; i < base64.length; i += 1) {
|
|
63
|
+
const code = base64.charCodeAt(i);
|
|
64
|
+
if (code <= 32) continue;
|
|
65
|
+
if (code === 61) {
|
|
66
|
+
padding += 1;
|
|
67
|
+
if (padding > 2) return;
|
|
68
|
+
sawPadding = true;
|
|
69
|
+
cleaned += "=";
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (sawPadding || !(code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 43 || code === 47)) return;
|
|
73
|
+
cleaned += base64[i];
|
|
74
|
+
}
|
|
75
|
+
if (!cleaned || cleaned.length % 4 !== 0) return;
|
|
76
|
+
return cleaned;
|
|
77
|
+
}
|
|
78
|
+
function sniffImageMime(buffer) {
|
|
79
|
+
if (buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10) return "image/png";
|
|
80
|
+
if (buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) return "image/jpeg";
|
|
81
|
+
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") return "image/webp";
|
|
82
|
+
if (buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")) return "image/gif";
|
|
83
|
+
}
|
|
84
|
+
function sanitizeInlineImageDataUrl(imageUrl) {
|
|
85
|
+
if (!startsWithDataUrl(imageUrl)) return imageUrl;
|
|
86
|
+
const commaIndex = imageUrl.indexOf(",");
|
|
87
|
+
if (commaIndex < 0) return;
|
|
88
|
+
const metadata = imageUrl.slice(5, commaIndex);
|
|
89
|
+
const payload = imageUrl.slice(commaIndex + 1);
|
|
90
|
+
const metadataParts = metadata.split(";").map((part) => part.trim());
|
|
91
|
+
if (!(metadataParts[0]?.toLowerCase())?.startsWith("image/")) return;
|
|
92
|
+
if (!metadataParts.slice(1).some((part) => part.toLowerCase() === "base64")) return;
|
|
93
|
+
const canonicalPayload = canonicalizeBase64(payload);
|
|
94
|
+
if (!canonicalPayload) return;
|
|
95
|
+
const sniffedMimeType = sniffImageMime(Buffer.from(canonicalPayload, "base64"));
|
|
96
|
+
if (!sniffedMimeType) return;
|
|
97
|
+
return `data:${sniffedMimeType};base64,${canonicalPayload}`;
|
|
98
|
+
}
|
|
99
|
+
function invalidInlineImageText(label) {
|
|
100
|
+
return `[${label}] ${IMAGE_OMITTED_TEXT}`;
|
|
101
|
+
}
|
|
102
|
+
function isRecord$1(value) {
|
|
103
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
104
|
+
}
|
|
105
|
+
function sanitizeImageContentRecord(record, label) {
|
|
106
|
+
if (record.type === "image" && typeof record.data === "string") {
|
|
107
|
+
const mimeType = typeof record.mimeType === "string" ? record.mimeType : "image/png";
|
|
108
|
+
const imageUrl = sanitizeInlineImageDataUrl(`data:${mimeType};base64,${record.data}`);
|
|
109
|
+
if (!imageUrl) return {
|
|
110
|
+
type: "text",
|
|
111
|
+
text: invalidInlineImageText(label)
|
|
112
|
+
};
|
|
113
|
+
const commaIndex = imageUrl.indexOf(",");
|
|
114
|
+
const mime = imageUrl.slice(5, commaIndex).split(";")[0] ?? mimeType;
|
|
115
|
+
return {
|
|
116
|
+
...record,
|
|
117
|
+
mimeType: mime,
|
|
118
|
+
data: imageUrl.slice(commaIndex + 1)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (record.type === "inputImage" && typeof record.imageUrl === "string") {
|
|
122
|
+
const imageUrl = sanitizeInlineImageDataUrl(record.imageUrl);
|
|
123
|
+
return imageUrl ? {
|
|
124
|
+
...record,
|
|
125
|
+
imageUrl
|
|
126
|
+
} : {
|
|
127
|
+
type: "inputText",
|
|
128
|
+
text: invalidInlineImageText(label)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (record.type === "input_image" && typeof record.image_url === "string") {
|
|
132
|
+
const imageUrl = sanitizeInlineImageDataUrl(record.image_url);
|
|
133
|
+
return imageUrl ? {
|
|
134
|
+
...record,
|
|
135
|
+
image_url: imageUrl
|
|
136
|
+
} : {
|
|
137
|
+
type: "input_text",
|
|
138
|
+
text: invalidInlineImageText(label)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function sanitizeCodexHistoryImagePayloads(value, label) {
|
|
143
|
+
if (Array.isArray(value)) return value.map((entry) => sanitizeCodexHistoryImagePayloads(entry, label));
|
|
144
|
+
if (!isRecord$1(value)) return value;
|
|
145
|
+
const imageRecord = sanitizeImageContentRecord(value, label);
|
|
146
|
+
if (imageRecord) return imageRecord;
|
|
147
|
+
const next = {};
|
|
148
|
+
for (const [key, child] of Object.entries(value)) next[key] = sanitizeCodexHistoryImagePayloads(child, label);
|
|
149
|
+
return next;
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
45
152
|
//#region extensions/codex/src/app-server/dynamic-tools.ts
|
|
46
153
|
const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
|
|
47
154
|
const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = new Set(["sessions_yield"]);
|
|
155
|
+
const DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 16e3;
|
|
48
156
|
function createCodexDynamicToolBridge(params) {
|
|
49
157
|
const toolResultHookContext = toToolResultHookContext(params.hookContext);
|
|
158
|
+
const toolResultMaxChars = resolveCodexDynamicToolResultMaxChars(params.hookContext);
|
|
50
159
|
const tools = params.tools.map((tool) => isToolWrappedWithBeforeToolCallHook(tool) ? tool : wrapToolWithBeforeToolCallHook(tool, params.hookContext));
|
|
51
160
|
const toolMap = new Map(tools.map((tool) => [tool.name, tool]));
|
|
52
161
|
const telemetry = {
|
|
@@ -120,12 +229,13 @@ function createCodexDynamicToolBridge(params) {
|
|
|
120
229
|
agentId: toolResultHookContext.agentId,
|
|
121
230
|
sessionId: toolResultHookContext.sessionId,
|
|
122
231
|
sessionKey: toolResultHookContext.sessionKey,
|
|
232
|
+
channelId: toolResultHookContext.channelId,
|
|
123
233
|
startArgs: args,
|
|
124
234
|
result,
|
|
125
235
|
startedAt
|
|
126
236
|
});
|
|
127
237
|
return {
|
|
128
|
-
contentItems: result.content
|
|
238
|
+
contentItems: convertToolContents(result.content, toolResultMaxChars),
|
|
129
239
|
success: !resultIsError
|
|
130
240
|
};
|
|
131
241
|
} catch (error) {
|
|
@@ -143,6 +253,7 @@ function createCodexDynamicToolBridge(params) {
|
|
|
143
253
|
agentId: toolResultHookContext.agentId,
|
|
144
254
|
sessionId: toolResultHookContext.sessionId,
|
|
145
255
|
sessionKey: toolResultHookContext.sessionKey,
|
|
256
|
+
channelId: toolResultHookContext.channelId,
|
|
146
257
|
startArgs: args,
|
|
147
258
|
error: error instanceof Error ? error.message : String(error),
|
|
148
259
|
startedAt
|
|
@@ -172,14 +283,34 @@ function createCodexDynamicToolSpec(params) {
|
|
|
172
283
|
};
|
|
173
284
|
}
|
|
174
285
|
function toToolResultHookContext(ctx) {
|
|
175
|
-
const { agentId, sessionId, sessionKey, runId } = ctx ?? {};
|
|
286
|
+
const { agentId, sessionId, sessionKey, runId, channelId } = ctx ?? {};
|
|
176
287
|
return {
|
|
177
288
|
...agentId && { agentId },
|
|
178
289
|
...sessionId && { sessionId },
|
|
179
290
|
...sessionKey && { sessionKey },
|
|
180
|
-
...runId && { runId }
|
|
291
|
+
...runId && { runId },
|
|
292
|
+
...channelId && { channelId }
|
|
181
293
|
};
|
|
182
294
|
}
|
|
295
|
+
function resolveCodexDynamicToolResultMaxChars(ctx) {
|
|
296
|
+
return resolveAgentContextLimitValue({
|
|
297
|
+
config: ctx?.config,
|
|
298
|
+
agentId: ctx?.agentId,
|
|
299
|
+
key: "toolResultMaxChars"
|
|
300
|
+
}) ?? DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
|
|
301
|
+
}
|
|
302
|
+
function resolveAgentContextLimitValue(params) {
|
|
303
|
+
const agents = readRecord(params.config?.agents);
|
|
304
|
+
const defaultValue = readPositiveInteger(readRecord(readRecord(agents?.defaults)?.contextLimits)?.[params.key]);
|
|
305
|
+
if (!params.agentId) return defaultValue;
|
|
306
|
+
const list = agents?.list;
|
|
307
|
+
if (!Array.isArray(list)) return defaultValue;
|
|
308
|
+
const normalizedAgentId = normalizeAgentId(params.agentId);
|
|
309
|
+
return readPositiveInteger(readRecord(readRecord(list.find((entry) => {
|
|
310
|
+
const entryId = readRecord(entry)?.id;
|
|
311
|
+
return typeof entryId === "string" && normalizeAgentId(entryId) === normalizedAgentId;
|
|
312
|
+
}))?.contextLimits)?.[params.key]) ?? defaultValue;
|
|
313
|
+
}
|
|
183
314
|
function composeAbortSignals(...signals) {
|
|
184
315
|
const activeSignals = signals.filter((signal) => Boolean(signal));
|
|
185
316
|
if (activeSignals.length === 0) return new AbortController().signal;
|
|
@@ -261,6 +392,13 @@ function extractInternalSourceReplyPayload(details) {
|
|
|
261
392
|
function isRecord(value) {
|
|
262
393
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
263
394
|
}
|
|
395
|
+
function readRecord(value) {
|
|
396
|
+
return isRecord(value) ? value : void 0;
|
|
397
|
+
}
|
|
398
|
+
function readPositiveInteger(value) {
|
|
399
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return;
|
|
400
|
+
return Math.floor(value);
|
|
401
|
+
}
|
|
264
402
|
function isToolResultError(result) {
|
|
265
403
|
const details = result.details;
|
|
266
404
|
if (!isRecord(details)) return false;
|
|
@@ -270,14 +408,66 @@ function isToolResultError(result) {
|
|
|
270
408
|
const status = details.status.trim().toLowerCase();
|
|
271
409
|
return status !== "" && status !== "0" && status !== "ok" && status !== "success" && status !== "completed" && status !== "recorded" && status !== "running";
|
|
272
410
|
}
|
|
411
|
+
function normalizeToolResultMaxChars(maxChars) {
|
|
412
|
+
return typeof maxChars === "number" && Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
|
|
413
|
+
}
|
|
414
|
+
function convertToolContents(content, toolResultMaxChars = DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS) {
|
|
415
|
+
const maxChars = normalizeToolResultMaxChars(toolResultMaxChars);
|
|
416
|
+
const totalTextChars = content.reduce((total, item) => total + (item.type === "text" ? item.text.length : 0), 0);
|
|
417
|
+
if (totalTextChars <= maxChars) return content.flatMap(convertToolContent);
|
|
418
|
+
const noticeText = `...(OpenClaw truncated dynamic tool result: original ${totalTextChars} chars, showing ${maxChars}; rerun with narrower args.)`;
|
|
419
|
+
const notice = `\n${noticeText}`;
|
|
420
|
+
let remainingTextBudget = Math.max(0, maxChars - notice.length);
|
|
421
|
+
let appendedNotice = false;
|
|
422
|
+
const output = [];
|
|
423
|
+
for (const item of content) {
|
|
424
|
+
if (item.type !== "text") {
|
|
425
|
+
output.push(...convertToolContent(item));
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (appendedNotice) continue;
|
|
429
|
+
if (notice.length >= maxChars) {
|
|
430
|
+
output.push({
|
|
431
|
+
type: "inputText",
|
|
432
|
+
text: noticeText.slice(0, maxChars)
|
|
433
|
+
});
|
|
434
|
+
appendedNotice = true;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const sliceLength = Math.min(item.text.length, remainingTextBudget);
|
|
438
|
+
remainingTextBudget -= sliceLength;
|
|
439
|
+
const shouldAppendNotice = remainingTextBudget <= 0;
|
|
440
|
+
const text = item.text.slice(0, sliceLength);
|
|
441
|
+
if (shouldAppendNotice) {
|
|
442
|
+
output.push({
|
|
443
|
+
type: "inputText",
|
|
444
|
+
text: `${text.trimEnd()}${notice}`.slice(0, maxChars)
|
|
445
|
+
});
|
|
446
|
+
appendedNotice = true;
|
|
447
|
+
} else if (text.length > 0) output.push({
|
|
448
|
+
type: "inputText",
|
|
449
|
+
text
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
if (!appendedNotice) output.push({
|
|
453
|
+
type: "inputText",
|
|
454
|
+
text: noticeText.slice(0, maxChars)
|
|
455
|
+
});
|
|
456
|
+
return output;
|
|
457
|
+
}
|
|
273
458
|
function convertToolContent(content) {
|
|
274
459
|
if (content.type === "text") return [{
|
|
275
460
|
type: "inputText",
|
|
276
461
|
text: content.text
|
|
277
462
|
}];
|
|
463
|
+
const imageUrl = sanitizeInlineImageDataUrl(`data:${content.mimeType};base64,${content.data}`);
|
|
464
|
+
if (!imageUrl) return [{
|
|
465
|
+
type: "inputText",
|
|
466
|
+
text: invalidInlineImageText("codex dynamic tool")
|
|
467
|
+
}];
|
|
278
468
|
return [{
|
|
279
469
|
type: "inputImage",
|
|
280
|
-
imageUrl
|
|
470
|
+
imageUrl
|
|
281
471
|
}];
|
|
282
472
|
}
|
|
283
473
|
function toJsonValue(value) {
|
|
@@ -872,7 +1062,7 @@ async function startOrResumeThread(params) {
|
|
|
872
1062
|
}
|
|
873
1063
|
} else try {
|
|
874
1064
|
const authProfileId = params.params.authProfileId ?? binding.authProfileId;
|
|
875
|
-
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch);
|
|
1065
|
+
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, params.finalConfigPatch);
|
|
876
1066
|
const response = assertCodexThreadResumeResponse(await params.client.request("thread/resume", buildThreadResumeParams(params.params, {
|
|
877
1067
|
threadId: binding.threadId,
|
|
878
1068
|
authProfileId,
|
|
@@ -939,7 +1129,7 @@ async function startOrResumeThread(params) {
|
|
|
939
1129
|
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
940
1130
|
}
|
|
941
1131
|
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await params.pluginThreadConfig.build() : void 0;
|
|
942
|
-
const config = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch);
|
|
1132
|
+
const config = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch, params.finalConfigPatch);
|
|
943
1133
|
const response = assertCodexThreadStartResponse(await params.client.request("thread/start", buildThreadStartParams(params.params, {
|
|
944
1134
|
cwd: params.cwd,
|
|
945
1135
|
dynamicTools: params.dynamicTools,
|
|
@@ -1238,14 +1428,22 @@ function renderCodexRuntimePromptOverlay(params) {
|
|
|
1238
1428
|
].filter((section) => typeof section === "string" && section.trim().length > 0).join("\n\n");
|
|
1239
1429
|
}
|
|
1240
1430
|
function buildUserInput(params, promptText = params.prompt) {
|
|
1431
|
+
const imageInputs = (params.images ?? []).map((image) => {
|
|
1432
|
+
const imageUrl = sanitizeInlineImageDataUrl(`data:${image.mimeType};base64,${image.data}`);
|
|
1433
|
+
return imageUrl ? {
|
|
1434
|
+
type: "image",
|
|
1435
|
+
url: imageUrl
|
|
1436
|
+
} : {
|
|
1437
|
+
type: "text",
|
|
1438
|
+
text: invalidInlineImageText("codex user input"),
|
|
1439
|
+
text_elements: []
|
|
1440
|
+
};
|
|
1441
|
+
});
|
|
1241
1442
|
return [{
|
|
1242
1443
|
type: "text",
|
|
1243
1444
|
text: promptText,
|
|
1244
1445
|
text_elements: []
|
|
1245
|
-
}, ...
|
|
1246
|
-
type: "image",
|
|
1247
|
-
url: `data:${image.mimeType};base64,${image.data}`
|
|
1248
|
-
}))];
|
|
1446
|
+
}, ...imageInputs];
|
|
1249
1447
|
}
|
|
1250
1448
|
function resolveCodexAppServerModelProvider(params) {
|
|
1251
1449
|
const normalized = params.provider.trim();
|
|
@@ -1260,4 +1458,4 @@ function resolveReasoningEffort(thinkLevel, modelId) {
|
|
|
1260
1458
|
return null;
|
|
1261
1459
|
}
|
|
1262
1460
|
//#endregion
|
|
1263
|
-
export {
|
|
1461
|
+
export { isForcedPrivateQaCodexRuntime as C, filterCodexDynamicTools as S, resolveCodexDynamicToolsLoading as T, projectContextEngineAssemblyForCodex as _, buildThreadResumeParams as a, createCodexDynamicToolBridge as b, codexDynamicToolsFingerprint as c, resolveReasoningEffort as d, startOrResumeThread as f, shouldBuildCodexPluginThreadConfig as g, mergeCodexThreadConfigs as h, buildDeveloperInstructions as i, isContextEngineBindingCompatible as l, buildCodexPluginThreadConfigInputFingerprint as m, buildCodexRuntimeThreadConfig as n, buildThreadStartParams as o, buildCodexPluginThreadConfig as p, buildContextEngineBinding as r, buildTurnStartParams as s, areCodexDynamicToolFingerprintsCompatible as t, resolveCodexAppServerModelProvider as u, resolveCodexContextEngineProjectionMaxChars as v, normalizeCodexDynamicToolName as w, sanitizeCodexHistoryImagePayloads as x, resolveCodexContextEngineProjectionReserveTokens as y };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as isJsonObject } from "./protocol-C9UWI98H.js";
|
|
2
2
|
import { r as formatCodexDisplayText } from "./command-formatters-BRW7_Nu7.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { buildAgentHookContextChannelFields, callGatewayTool, embeddedAgentLog, formatApprovalDisplayPath, hasNativeHookRelayInvocation, invokeNativeHookRelay, runBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
4
5
|
//#region extensions/codex/src/app-server/plugin-approval-roundtrip.ts
|
|
5
6
|
const DEFAULT_CODEX_APPROVAL_TIMEOUT_MS = 12e4;
|
|
6
7
|
const MAX_PLUGIN_APPROVAL_TITLE_LENGTH = 80;
|
|
@@ -89,9 +90,10 @@ async function handleCodexAppServerApprovalRequest(params) {
|
|
|
89
90
|
requestParams,
|
|
90
91
|
paramsForRun: params.paramsForRun,
|
|
91
92
|
context,
|
|
93
|
+
nativeHookRelay: params.nativeHookRelay,
|
|
92
94
|
signal: params.signal
|
|
93
95
|
});
|
|
94
|
-
if (policyOutcome?.
|
|
96
|
+
if (policyOutcome?.outcome === "denied") {
|
|
95
97
|
emitApprovalEvent(params.paramsForRun, {
|
|
96
98
|
phase: "resolved",
|
|
97
99
|
kind: context.kind,
|
|
@@ -221,6 +223,26 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
|
|
|
221
223
|
const policyRequest = buildOpenClawToolPolicyRequest(params.method, params.requestParams);
|
|
222
224
|
if (!policyRequest) return;
|
|
223
225
|
const cwd = readString$1(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
|
|
226
|
+
const nativeRelayOutcome = await runNativeRelayToolPolicyForApprovalRequest({
|
|
227
|
+
method: params.method,
|
|
228
|
+
requestParams: params.requestParams,
|
|
229
|
+
context: params.context,
|
|
230
|
+
policyRequest,
|
|
231
|
+
nativeHookRelay: params.nativeHookRelay,
|
|
232
|
+
cwd
|
|
233
|
+
});
|
|
234
|
+
if (nativeRelayOutcome?.blocked) return {
|
|
235
|
+
outcome: "denied",
|
|
236
|
+
reason: nativeRelayOutcome.reason
|
|
237
|
+
};
|
|
238
|
+
if (nativeRelayOutcome?.handled) return { outcome: "no-decision" };
|
|
239
|
+
const hookChannelId = buildAgentHookContextChannelFields({
|
|
240
|
+
sessionKey: params.paramsForRun.sessionKey,
|
|
241
|
+
messageChannel: params.paramsForRun.messageChannel,
|
|
242
|
+
messageProvider: params.paramsForRun.messageProvider,
|
|
243
|
+
currentChannelId: params.paramsForRun.currentChannelId,
|
|
244
|
+
messageTo: params.paramsForRun.messageTo
|
|
245
|
+
}).channelId;
|
|
224
246
|
const outcome = await runBeforeToolCallHook({
|
|
225
247
|
toolName: policyRequest.toolName,
|
|
226
248
|
params: policyRequest.params,
|
|
@@ -234,23 +256,108 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
|
|
|
234
256
|
...params.paramsForRun.sessionKey ? { sessionKey: params.paramsForRun.sessionKey } : {},
|
|
235
257
|
...params.paramsForRun.sessionId ? { sessionId: params.paramsForRun.sessionId } : {},
|
|
236
258
|
...params.paramsForRun.runId ? { runId: params.paramsForRun.runId } : {},
|
|
237
|
-
...
|
|
259
|
+
...hookChannelId ? { channelId: hookChannelId } : {}
|
|
238
260
|
}
|
|
239
261
|
});
|
|
240
262
|
if (outcome.blocked) return {
|
|
241
|
-
|
|
263
|
+
outcome: "denied",
|
|
242
264
|
reason: outcome.reason
|
|
243
265
|
};
|
|
244
266
|
if ("params" in outcome && toolPolicyParamsWereRewritten(policyRequest.params, outcome.params)) return {
|
|
245
|
-
|
|
267
|
+
outcome: "denied",
|
|
246
268
|
reason: "OpenClaw tool policy rewrote Codex app-server approval params; refusing original request."
|
|
247
269
|
};
|
|
248
270
|
}
|
|
271
|
+
async function runNativeRelayToolPolicyForApprovalRequest(params) {
|
|
272
|
+
if (params.method !== "item/commandExecution/requestApproval" || !params.nativeHookRelay?.allowedEvents.includes("pre_tool_use")) return;
|
|
273
|
+
const payload = buildNativeRelayPreToolUsePayload({
|
|
274
|
+
requestParams: params.requestParams,
|
|
275
|
+
policyRequest: params.policyRequest,
|
|
276
|
+
context: params.context,
|
|
277
|
+
cwd: params.cwd
|
|
278
|
+
});
|
|
279
|
+
if (!payload) return;
|
|
280
|
+
if (hasNativeHookRelayInvocation({
|
|
281
|
+
relayId: params.nativeHookRelay.relayId,
|
|
282
|
+
event: "pre_tool_use",
|
|
283
|
+
toolUseId: params.context.itemId
|
|
284
|
+
})) return { handled: true };
|
|
285
|
+
try {
|
|
286
|
+
const decision = readNativeRelayPreToolUseDecision(await invokeNativeHookRelay({
|
|
287
|
+
provider: "codex",
|
|
288
|
+
relayId: params.nativeHookRelay.relayId,
|
|
289
|
+
event: "pre_tool_use",
|
|
290
|
+
rawPayload: payload
|
|
291
|
+
}));
|
|
292
|
+
if (decision.blocked) return {
|
|
293
|
+
handled: true,
|
|
294
|
+
blocked: true,
|
|
295
|
+
reason: decision.reason
|
|
296
|
+
};
|
|
297
|
+
return { handled: true };
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
handled: true,
|
|
301
|
+
blocked: true,
|
|
302
|
+
reason: `OpenClaw native hook relay unavailable for Codex app-server approval: ${formatCodexDisplayText(formatErrorMessage$1(error))}`
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function buildNativeRelayPreToolUsePayload(params) {
|
|
307
|
+
const command = readString$1(params.policyRequest.params, "command");
|
|
308
|
+
if (!command) return;
|
|
309
|
+
const turnId = readString$1(params.requestParams, "turnId");
|
|
310
|
+
return {
|
|
311
|
+
hook_event_name: "PreToolUse",
|
|
312
|
+
openclaw_approval_mode: "report",
|
|
313
|
+
tool_name: "exec_command",
|
|
314
|
+
...params.context.itemId ? { tool_use_id: params.context.itemId } : {},
|
|
315
|
+
...params.cwd ? { cwd: params.cwd } : {},
|
|
316
|
+
...turnId ? { turn_id: turnId } : {},
|
|
317
|
+
tool_input: {
|
|
318
|
+
...params.policyRequest.params,
|
|
319
|
+
command,
|
|
320
|
+
cmd: command
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function readNativeRelayPreToolUseDecision(response) {
|
|
325
|
+
if (!response || response.exitCode !== 0) return {
|
|
326
|
+
blocked: true,
|
|
327
|
+
reason: sanitizeRelayDecisionReason(response?.stderr) || sanitizeRelayDecisionReason(response?.stdout) || "OpenClaw native hook relay failed for Codex app-server approval."
|
|
328
|
+
};
|
|
329
|
+
const stdout = response.stdout?.trim();
|
|
330
|
+
if (!stdout) return { blocked: false };
|
|
331
|
+
const parsed = parseRelayJsonResponse(stdout);
|
|
332
|
+
const output = isJsonObject(parsed?.hookSpecificOutput) ? parsed.hookSpecificOutput : void 0;
|
|
333
|
+
if (output?.permissionDecision === "deny") return {
|
|
334
|
+
blocked: true,
|
|
335
|
+
reason: readString$1(output, "permissionDecisionReason") || "OpenClaw native hook policy denied Codex app-server approval."
|
|
336
|
+
};
|
|
337
|
+
return {
|
|
338
|
+
blocked: true,
|
|
339
|
+
reason: output ? "OpenClaw native hook relay returned a non-deny Codex app-server approval decision." : "OpenClaw native hook relay returned an unreadable Codex app-server approval result."
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function parseRelayJsonResponse(text) {
|
|
343
|
+
try {
|
|
344
|
+
const parsed = JSON.parse(text);
|
|
345
|
+
return isJsonObject(parsed) ? parsed : void 0;
|
|
346
|
+
} catch {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function sanitizeRelayDecisionReason(value) {
|
|
351
|
+
return sanitizeApprovalPreview(value ? {
|
|
352
|
+
value,
|
|
353
|
+
clipped: false
|
|
354
|
+
} : void 0, 240).text;
|
|
355
|
+
}
|
|
249
356
|
function buildOpenClawToolPolicyRequest(method, requestParams) {
|
|
250
357
|
if (method === "item/commandExecution/requestApproval") {
|
|
251
358
|
const command = readPolicyCommand(requestParams);
|
|
252
359
|
return {
|
|
253
|
-
toolName: "
|
|
360
|
+
toolName: "exec",
|
|
254
361
|
params: {
|
|
255
362
|
...command ? { command } : {},
|
|
256
363
|
...readString$1(requestParams, "cwd") ? { cwd: readString$1(requestParams, "cwd") } : {},
|
|
@@ -1120,10 +1227,99 @@ function readFirstString(record, keys) {
|
|
|
1120
1227
|
}
|
|
1121
1228
|
}
|
|
1122
1229
|
//#endregion
|
|
1230
|
+
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
1231
|
+
const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
|
1232
|
+
"pre_tool_use",
|
|
1233
|
+
"post_tool_use",
|
|
1234
|
+
"permission_request",
|
|
1235
|
+
"before_agent_finalize"
|
|
1236
|
+
];
|
|
1237
|
+
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
|
|
1238
|
+
pre_tool_use: "PreToolUse",
|
|
1239
|
+
post_tool_use: "PostToolUse",
|
|
1240
|
+
permission_request: "PermissionRequest",
|
|
1241
|
+
before_agent_finalize: "Stop"
|
|
1242
|
+
};
|
|
1243
|
+
const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
|
|
1244
|
+
pre_tool_use: "pre_tool_use",
|
|
1245
|
+
post_tool_use: "post_tool_use",
|
|
1246
|
+
permission_request: "permission_request",
|
|
1247
|
+
before_agent_finalize: "stop"
|
|
1248
|
+
};
|
|
1249
|
+
const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
|
|
1250
|
+
function buildCodexNativeHookRelayConfig(params) {
|
|
1251
|
+
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
|
1252
|
+
const selectedEvents = new Set(events);
|
|
1253
|
+
const config = { "features.hooks": true };
|
|
1254
|
+
const hookState = {};
|
|
1255
|
+
for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
|
|
1256
|
+
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
|
1257
|
+
const selected = selectedEvents.has(event);
|
|
1258
|
+
if (!selected || !params.relay.shouldRelayEvent(event)) {
|
|
1259
|
+
if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
|
|
1260
|
+
if (params.clearOmittedEvents) for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = { enabled: false };
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
const command = params.relay.commandForEvent(event);
|
|
1264
|
+
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
1265
|
+
config[`hooks.${codexEvent}`] = [{ hooks: [{
|
|
1266
|
+
type: "command",
|
|
1267
|
+
command,
|
|
1268
|
+
timeout,
|
|
1269
|
+
async: false,
|
|
1270
|
+
statusMessage: "OpenClaw native hook relay"
|
|
1271
|
+
}] }];
|
|
1272
|
+
const state = {
|
|
1273
|
+
enabled: true,
|
|
1274
|
+
trusted_hash: codexCommandHookTrustedHash({
|
|
1275
|
+
event,
|
|
1276
|
+
command,
|
|
1277
|
+
timeout,
|
|
1278
|
+
statusMessage: "OpenClaw native hook relay"
|
|
1279
|
+
})
|
|
1280
|
+
};
|
|
1281
|
+
for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
|
|
1282
|
+
}
|
|
1283
|
+
config["hooks.state"] = hookState;
|
|
1284
|
+
return config;
|
|
1285
|
+
}
|
|
1286
|
+
function buildCodexNativeHookRelayDisabledConfig() {
|
|
1287
|
+
return {
|
|
1288
|
+
"features.hooks": false,
|
|
1289
|
+
"hooks.PreToolUse": [],
|
|
1290
|
+
"hooks.PostToolUse": [],
|
|
1291
|
+
"hooks.PermissionRequest": [],
|
|
1292
|
+
"hooks.Stop": []
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
function normalizeHookTimeoutSec(value) {
|
|
1296
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : 5;
|
|
1297
|
+
}
|
|
1298
|
+
function codexCommandHookTrustedHash(params) {
|
|
1299
|
+
const identity = {
|
|
1300
|
+
event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
|
|
1301
|
+
hooks: [{
|
|
1302
|
+
async: false,
|
|
1303
|
+
command: params.command,
|
|
1304
|
+
statusMessage: params.statusMessage,
|
|
1305
|
+
timeout: params.timeout,
|
|
1306
|
+
type: "command"
|
|
1307
|
+
}]
|
|
1308
|
+
};
|
|
1309
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
|
|
1310
|
+
}
|
|
1311
|
+
function sortJsonValue(value) {
|
|
1312
|
+
if (!value || typeof value !== "object") return value;
|
|
1313
|
+
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
1314
|
+
const sorted = {};
|
|
1315
|
+
for (const key of Object.keys(value).toSorted()) sorted[key] = sortJsonValue(value[key]);
|
|
1316
|
+
return sorted;
|
|
1317
|
+
}
|
|
1318
|
+
//#endregion
|
|
1123
1319
|
//#region extensions/codex/src/app-server/vision-tools.ts
|
|
1124
1320
|
function filterToolsForVisionInputs(tools, params) {
|
|
1125
1321
|
if (!params.modelHasVision || !params.hasInboundImages) return tools;
|
|
1126
1322
|
return tools.filter((tool) => tool.name !== "image");
|
|
1127
1323
|
}
|
|
1128
1324
|
//#endregion
|
|
1129
|
-
export { handleCodexAppServerElicitationRequest as n, handleCodexAppServerApprovalRequest as r, filterToolsForVisionInputs as t };
|
|
1325
|
+
export { handleCodexAppServerElicitationRequest as a, buildCodexNativeHookRelayDisabledConfig as i, CODEX_NATIVE_HOOK_RELAY_EVENTS as n, handleCodexAppServerApprovalRequest as o, buildCodexNativeHookRelayConfig as r, filterToolsForVisionInputs as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/codex",
|
|
3
|
-
"version": "2026.5.16-beta.
|
|
3
|
+
"version": "2026.5.16-beta.5",
|
|
4
4
|
"description": "OpenClaw Codex harness and model provider plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@earendil-works/pi-coding-agent": "0.74.
|
|
11
|
+
"@earendil-works/pi-coding-agent": "0.74.1",
|
|
12
12
|
"@openai/codex": "0.130.0",
|
|
13
13
|
"ajv": "8.20.0",
|
|
14
14
|
"ws": "8.20.1",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"minHostVersion": ">=2026.5.1-beta.1"
|
|
28
28
|
},
|
|
29
29
|
"compat": {
|
|
30
|
-
"pluginApi": ">=2026.5.16-beta.
|
|
30
|
+
"pluginApi": ">=2026.5.16-beta.5"
|
|
31
31
|
},
|
|
32
32
|
"build": {
|
|
33
|
-
"openclawVersion": "2026.5.16-beta.
|
|
33
|
+
"openclawVersion": "2026.5.16-beta.5"
|
|
34
34
|
},
|
|
35
35
|
"release": {
|
|
36
36
|
"publishToClawHub": true,
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"openclaw.plugin.json"
|
|
46
46
|
],
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"openclaw": ">=2026.5.16-beta.
|
|
48
|
+
"openclaw": ">=2026.5.16-beta.5"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"openclaw": {
|