@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,20 +1,20 @@
|
|
|
1
|
-
import { c as resolveCodexAppServerRuntimeOptions, d as withMcpElicitationsApprovalPolicy, i as isCodexAppServerApprovalPolicyAllowedByRequirements, s as readCodexPluginConfig, u as resolveCodexPluginsPolicy } from "./config-
|
|
1
|
+
import { c as resolveCodexAppServerRuntimeOptions, d as withMcpElicitationsApprovalPolicy, i as isCodexAppServerApprovalPolicyAllowedByRequirements, s as readCodexPluginConfig, u as resolveCodexPluginsPolicy } from "./config-B5pq6hEz.js";
|
|
2
2
|
import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-BGBspNmF.js";
|
|
3
3
|
import { t as isJsonObject } from "./protocol-C9UWI98H.js";
|
|
4
|
-
import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-
|
|
5
|
-
import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-
|
|
4
|
+
import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-6FkrXfaz.js";
|
|
5
|
+
import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-XPjLayGw.js";
|
|
6
6
|
import { d as resolveCodexUsageLimitResetAtMs, f as shouldRefreshCodexRateLimitsForUsageLimitMessage, r as formatCodexDisplayText, u as formatCodexUsageLimitErrorMessage } from "./command-formatters-BRW7_Nu7.js";
|
|
7
|
-
import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey,
|
|
8
|
-
import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-
|
|
9
|
-
import { a as defaultCodexAppInventoryCache } from "./plugin-activation-
|
|
10
|
-
import { S as
|
|
11
|
-
import { t as defaultCodexAppServerClientFactory } from "./client-factory-
|
|
12
|
-
import {
|
|
13
|
-
import { t as ensureCodexComputerUse } from "./computer-use-
|
|
7
|
+
import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey, f as resolveCodexAppServerHomeDir, l as resolveCodexAppServerAuthProfileId, s as refreshCodexAppServerAuthTokens, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-DlvmoLBJ.js";
|
|
8
|
+
import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-DqApZIgD.js";
|
|
9
|
+
import { a as defaultCodexAppInventoryCache } from "./plugin-activation-B49xb7pI.js";
|
|
10
|
+
import { C as isForcedPrivateQaCodexRuntime, S as filterCodexDynamicTools, T as resolveCodexDynamicToolsLoading, _ as projectContextEngineAssemblyForCodex, b as createCodexDynamicToolBridge, c as codexDynamicToolsFingerprint, f as startOrResumeThread, g as shouldBuildCodexPluginThreadConfig, h as mergeCodexThreadConfigs, i as buildDeveloperInstructions, l as isContextEngineBindingCompatible, m as buildCodexPluginThreadConfigInputFingerprint, p as buildCodexPluginThreadConfig, r as buildContextEngineBinding, s as buildTurnStartParams, t as areCodexDynamicToolFingerprintsCompatible, v as resolveCodexContextEngineProjectionMaxChars, w as normalizeCodexDynamicToolName, x as sanitizeCodexHistoryImagePayloads, y as resolveCodexContextEngineProjectionReserveTokens } from "./thread-lifecycle-BbriKhTq.js";
|
|
11
|
+
import { t as defaultCodexAppServerClientFactory } from "./client-factory-9L6Ie1dC.js";
|
|
12
|
+
import { a as handleCodexAppServerElicitationRequest, i as buildCodexNativeHookRelayDisabledConfig, n as CODEX_NATIVE_HOOK_RELAY_EVENTS, o as handleCodexAppServerApprovalRequest, r as buildCodexNativeHookRelayConfig, t as filterToolsForVisionInputs } from "./vision-tools-BX9YuTEK.js";
|
|
13
|
+
import { t as ensureCodexComputerUse } from "./computer-use-UJ3dxrXF.js";
|
|
14
14
|
import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-dvhq-4pi.js";
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
16
16
|
import nodeFs from "node:fs";
|
|
17
|
-
import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, hasBeforeToolCallPolicy, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, loadCodexBundleMcpThreadConfig, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveContextEngineOwnerPluginId, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds,
|
|
17
|
+
import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, hasBeforeToolCallPolicy, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, loadCodexBundleMcpThreadConfig, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveContextEngineOwnerPluginId, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockOptions, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
18
18
|
import fs from "node:fs/promises";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
@@ -324,7 +324,7 @@ async function readCodexMirroredSessionHistoryMessages(sessionFile) {
|
|
|
324
324
|
const firstEntry = entries[0];
|
|
325
325
|
if (firstEntry?.type !== "session" || typeof firstEntry.id !== "string") return;
|
|
326
326
|
migrateSessionEntries(entries);
|
|
327
|
-
return buildSessionContext(entries.filter((entry) => entry.type !== "session")).messages;
|
|
327
|
+
return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => entry.type !== "session")).messages, "codex mirrored history");
|
|
328
328
|
} catch (error) {
|
|
329
329
|
if (isMissingFileError(error)) return [];
|
|
330
330
|
return;
|
|
@@ -446,7 +446,7 @@ async function mirrorCodexAppServerTranscript(params) {
|
|
|
446
446
|
if (messages.length === 0) return;
|
|
447
447
|
const lock = await acquireSessionWriteLock({
|
|
448
448
|
sessionFile: params.sessionFile,
|
|
449
|
-
|
|
449
|
+
...resolveSessionWriteLockOptions(params.config)
|
|
450
450
|
});
|
|
451
451
|
try {
|
|
452
452
|
const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
|
|
@@ -990,6 +990,7 @@ var CodexAppServerEventProjector = class {
|
|
|
990
990
|
});
|
|
991
991
|
}
|
|
992
992
|
this.recordToolMeta(item);
|
|
993
|
+
this.emitSnapshotOnlyNativeToolProgress(item);
|
|
993
994
|
this.recordNativeToolTranscriptCall(item);
|
|
994
995
|
this.recordNativeToolTranscriptResult(item);
|
|
995
996
|
this.emitAfterToolCallObservation(item);
|
|
@@ -999,6 +1000,33 @@ var CodexAppServerEventProjector = class {
|
|
|
999
1000
|
this.activeCompactionItemIds.clear();
|
|
1000
1001
|
await this.maybeEndReasoning();
|
|
1001
1002
|
}
|
|
1003
|
+
emitSnapshotOnlyNativeToolProgress(item) {
|
|
1004
|
+
if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
1005
|
+
if (!this.activeItemIds.has(item.id)) {
|
|
1006
|
+
this.emitStandardItemEvent({
|
|
1007
|
+
phase: "start",
|
|
1008
|
+
item
|
|
1009
|
+
});
|
|
1010
|
+
this.emitNormalizedToolItemEvent({
|
|
1011
|
+
phase: "start",
|
|
1012
|
+
item
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
this.activeItemIds.delete(item.id);
|
|
1016
|
+
this.emitStandardItemEvent({
|
|
1017
|
+
phase: "end",
|
|
1018
|
+
item
|
|
1019
|
+
});
|
|
1020
|
+
this.emitNormalizedToolItemEvent({
|
|
1021
|
+
phase: "result",
|
|
1022
|
+
item
|
|
1023
|
+
});
|
|
1024
|
+
this.completedItemIds.add(item.id);
|
|
1025
|
+
}
|
|
1026
|
+
isCurrentTurnSnapshotItem(item) {
|
|
1027
|
+
const itemTurnId = readItemString(item, "turnId") ?? readItemString(item, "turn_id");
|
|
1028
|
+
return itemTurnId === void 0 || itemTurnId === this.turnId;
|
|
1029
|
+
}
|
|
1002
1030
|
handleOutputDelta(params, toolName) {
|
|
1003
1031
|
const itemId = readString$2(params, "itemId");
|
|
1004
1032
|
const delta = readString$2(params, "delta");
|
|
@@ -1128,6 +1156,13 @@ var CodexAppServerEventProjector = class {
|
|
|
1128
1156
|
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
1129
1157
|
const args = params.phase === "start" ? itemToolArgs(item) : void 0;
|
|
1130
1158
|
const status = params.phase === "result" ? itemStatus(item) : "running";
|
|
1159
|
+
this.recordToolTrajectoryEvent({
|
|
1160
|
+
phase: params.phase,
|
|
1161
|
+
item,
|
|
1162
|
+
name,
|
|
1163
|
+
args,
|
|
1164
|
+
status
|
|
1165
|
+
});
|
|
1131
1166
|
this.emitDiagnosticToolExecutionEvent({
|
|
1132
1167
|
phase: params.phase,
|
|
1133
1168
|
item,
|
|
@@ -1152,6 +1187,32 @@ var CodexAppServerEventProjector = class {
|
|
|
1152
1187
|
});
|
|
1153
1188
|
if (params.phase === "result") this.emitAfterToolCallObservation(item);
|
|
1154
1189
|
}
|
|
1190
|
+
recordToolTrajectoryEvent(params) {
|
|
1191
|
+
if (params.phase === "start") {
|
|
1192
|
+
this.options.trajectoryRecorder?.recordEvent("tool.call", {
|
|
1193
|
+
threadId: this.threadId,
|
|
1194
|
+
turnId: this.turnId,
|
|
1195
|
+
itemId: params.item.id,
|
|
1196
|
+
toolCallId: params.item.id,
|
|
1197
|
+
name: params.name,
|
|
1198
|
+
arguments: params.args
|
|
1199
|
+
});
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
const toolResult = itemToolResult(params.item).result;
|
|
1203
|
+
const output = itemOutputText(params.item);
|
|
1204
|
+
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
1205
|
+
threadId: this.threadId,
|
|
1206
|
+
turnId: this.turnId,
|
|
1207
|
+
itemId: params.item.id,
|
|
1208
|
+
toolCallId: params.item.id,
|
|
1209
|
+
name: params.name,
|
|
1210
|
+
status: params.status,
|
|
1211
|
+
isError: isNonSuccessItemStatus(params.status),
|
|
1212
|
+
...toolResult ? { result: toolResult } : {},
|
|
1213
|
+
...output ? { output } : {}
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1155
1216
|
emitDiagnosticToolExecutionEvent(params) {
|
|
1156
1217
|
const base = {
|
|
1157
1218
|
runId: this.params.runId,
|
|
@@ -1749,88 +1810,6 @@ function readTurn(value) {
|
|
|
1749
1810
|
return readCodexTurn(value);
|
|
1750
1811
|
}
|
|
1751
1812
|
//#endregion
|
|
1752
|
-
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
1753
|
-
const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
|
1754
|
-
"pre_tool_use",
|
|
1755
|
-
"post_tool_use",
|
|
1756
|
-
"permission_request",
|
|
1757
|
-
"before_agent_finalize"
|
|
1758
|
-
];
|
|
1759
|
-
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
|
|
1760
|
-
pre_tool_use: "PreToolUse",
|
|
1761
|
-
post_tool_use: "PostToolUse",
|
|
1762
|
-
permission_request: "PermissionRequest",
|
|
1763
|
-
before_agent_finalize: "Stop"
|
|
1764
|
-
};
|
|
1765
|
-
const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
|
|
1766
|
-
pre_tool_use: "pre_tool_use",
|
|
1767
|
-
post_tool_use: "post_tool_use",
|
|
1768
|
-
permission_request: "permission_request",
|
|
1769
|
-
before_agent_finalize: "stop"
|
|
1770
|
-
};
|
|
1771
|
-
const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
|
|
1772
|
-
function buildCodexNativeHookRelayConfig(params) {
|
|
1773
|
-
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
|
1774
|
-
const config = { "features.hooks": true };
|
|
1775
|
-
const hookState = {};
|
|
1776
|
-
for (const event of events) {
|
|
1777
|
-
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
|
1778
|
-
const command = params.relay.commandForEvent(event);
|
|
1779
|
-
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
1780
|
-
config[`hooks.${codexEvent}`] = [{ hooks: [{
|
|
1781
|
-
type: "command",
|
|
1782
|
-
command,
|
|
1783
|
-
timeout,
|
|
1784
|
-
async: false,
|
|
1785
|
-
statusMessage: "OpenClaw native hook relay"
|
|
1786
|
-
}] }];
|
|
1787
|
-
const state = {
|
|
1788
|
-
enabled: true,
|
|
1789
|
-
trusted_hash: codexCommandHookTrustedHash({
|
|
1790
|
-
event,
|
|
1791
|
-
command,
|
|
1792
|
-
timeout,
|
|
1793
|
-
statusMessage: "OpenClaw native hook relay"
|
|
1794
|
-
})
|
|
1795
|
-
};
|
|
1796
|
-
for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
|
|
1797
|
-
}
|
|
1798
|
-
config["hooks.state"] = hookState;
|
|
1799
|
-
return config;
|
|
1800
|
-
}
|
|
1801
|
-
function buildCodexNativeHookRelayDisabledConfig() {
|
|
1802
|
-
return {
|
|
1803
|
-
"features.hooks": false,
|
|
1804
|
-
"hooks.PreToolUse": [],
|
|
1805
|
-
"hooks.PostToolUse": [],
|
|
1806
|
-
"hooks.PermissionRequest": [],
|
|
1807
|
-
"hooks.Stop": []
|
|
1808
|
-
};
|
|
1809
|
-
}
|
|
1810
|
-
function normalizeHookTimeoutSec(value) {
|
|
1811
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : 5;
|
|
1812
|
-
}
|
|
1813
|
-
function codexCommandHookTrustedHash(params) {
|
|
1814
|
-
const identity = {
|
|
1815
|
-
event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
|
|
1816
|
-
hooks: [{
|
|
1817
|
-
async: false,
|
|
1818
|
-
command: params.command,
|
|
1819
|
-
statusMessage: params.statusMessage,
|
|
1820
|
-
timeout: params.timeout,
|
|
1821
|
-
type: "command"
|
|
1822
|
-
}]
|
|
1823
|
-
};
|
|
1824
|
-
return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
|
|
1825
|
-
}
|
|
1826
|
-
function sortJsonValue(value) {
|
|
1827
|
-
if (!value || typeof value !== "object") return value;
|
|
1828
|
-
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
1829
|
-
const sorted = {};
|
|
1830
|
-
for (const key of Object.keys(value).toSorted()) sorted[key] = sortJsonValue(value[key]);
|
|
1831
|
-
return sorted;
|
|
1832
|
-
}
|
|
1833
|
-
//#endregion
|
|
1834
1813
|
//#region extensions/codex/src/app-server/trajectory.ts
|
|
1835
1814
|
const SENSITIVE_FIELD_RE = /(?:authorization|cookie|credential|key|password|passwd|secret|token)/iu;
|
|
1836
1815
|
const PRIVATE_PAYLOAD_FIELD_RE = /(?:image|screenshot|attachment|fileData|dataUri)/iu;
|
|
@@ -2401,6 +2380,169 @@ function isCodexAppServerPolicyMode(value) {
|
|
|
2401
2380
|
function isCodexAppServerApprovalPolicy(value) {
|
|
2402
2381
|
return value === "never" || value === "on-request" || value === "on-failure" || value === "untrusted";
|
|
2403
2382
|
}
|
|
2383
|
+
const CODEX_APP_SERVER_NATIVE_THREAD_MAX_TOKENS = 7e4;
|
|
2384
|
+
const CODEX_APP_SERVER_BYTE_UNITS = {
|
|
2385
|
+
b: 1,
|
|
2386
|
+
k: 1024,
|
|
2387
|
+
kb: 1024,
|
|
2388
|
+
kib: 1024,
|
|
2389
|
+
m: 1024 * 1024,
|
|
2390
|
+
mb: 1024 * 1024,
|
|
2391
|
+
mib: 1024 * 1024,
|
|
2392
|
+
g: 1024 * 1024 * 1024,
|
|
2393
|
+
gb: 1024 * 1024 * 1024,
|
|
2394
|
+
gib: 1024 * 1024 * 1024,
|
|
2395
|
+
t: 1024 * 1024 * 1024 * 1024,
|
|
2396
|
+
tb: 1024 * 1024 * 1024 * 1024,
|
|
2397
|
+
tib: 1024 * 1024 * 1024 * 1024
|
|
2398
|
+
};
|
|
2399
|
+
function parseCodexAppServerByteLimit(value) {
|
|
2400
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
|
|
2401
|
+
if (typeof value !== "string") return;
|
|
2402
|
+
const match = value.trim().match(/^(\d+(?:\.\d+)?)\s*([a-z]+)?$/i);
|
|
2403
|
+
if (!match) return;
|
|
2404
|
+
const amount = Number(match[1]);
|
|
2405
|
+
if (!Number.isFinite(amount) || amount <= 0) return;
|
|
2406
|
+
const multiplier = CODEX_APP_SERVER_BYTE_UNITS[(match[2] ?? "b").toLowerCase()];
|
|
2407
|
+
if (multiplier === void 0) return;
|
|
2408
|
+
return Math.max(1, Math.round(amount * multiplier));
|
|
2409
|
+
}
|
|
2410
|
+
async function listCodexAppServerRolloutFilesForThread(agentDir, threadId, codexHome) {
|
|
2411
|
+
const resolvedAgentDir = path.resolve(agentDir);
|
|
2412
|
+
const resolvedCodexHome = codexHome?.trim() ? path.resolve(codexHome) : resolveCodexAppServerHomeDir(resolvedAgentDir);
|
|
2413
|
+
const roots = [
|
|
2414
|
+
path.join(resolvedCodexHome, "sessions"),
|
|
2415
|
+
path.join(resolveCodexAppServerHomeDir(resolvedAgentDir), "sessions"),
|
|
2416
|
+
path.join(resolvedAgentDir, "agent", "codex-home", "sessions"),
|
|
2417
|
+
path.join(path.dirname(resolvedAgentDir), "codex-home", "sessions")
|
|
2418
|
+
];
|
|
2419
|
+
const files = [];
|
|
2420
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2421
|
+
for (const root of roots) {
|
|
2422
|
+
if (visited.has(root)) continue;
|
|
2423
|
+
visited.add(root);
|
|
2424
|
+
const stack = [root];
|
|
2425
|
+
while (stack.length > 0) {
|
|
2426
|
+
const dir = stack.pop();
|
|
2427
|
+
if (!dir) continue;
|
|
2428
|
+
let entries;
|
|
2429
|
+
try {
|
|
2430
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
2431
|
+
} catch {
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
for (const entry of entries) {
|
|
2435
|
+
const file = path.join(dir, entry.name);
|
|
2436
|
+
if (entry.isDirectory()) {
|
|
2437
|
+
stack.push(file);
|
|
2438
|
+
continue;
|
|
2439
|
+
}
|
|
2440
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes(threadId)) continue;
|
|
2441
|
+
try {
|
|
2442
|
+
files.push({
|
|
2443
|
+
path: file,
|
|
2444
|
+
bytes: (await fs.stat(file)).size
|
|
2445
|
+
});
|
|
2446
|
+
} catch {}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
return files;
|
|
2451
|
+
}
|
|
2452
|
+
async function readCodexSessionRecordForSessionFile(sessionFile) {
|
|
2453
|
+
const sessionsFile = path.join(path.dirname(sessionFile), "sessions.json");
|
|
2454
|
+
let store;
|
|
2455
|
+
try {
|
|
2456
|
+
store = JSON.parse(await fs.readFile(sessionsFile, "utf8"));
|
|
2457
|
+
} catch {
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
if (!isJsonObject(store)) return;
|
|
2461
|
+
const resolvedSessionFile = path.resolve(sessionFile);
|
|
2462
|
+
for (const [sessionKey, record] of Object.entries(store)) {
|
|
2463
|
+
if (!isJsonObject(record) || typeof record.sessionFile !== "string") continue;
|
|
2464
|
+
if (path.resolve(record.sessionFile) !== resolvedSessionFile) continue;
|
|
2465
|
+
return {
|
|
2466
|
+
sessionKey,
|
|
2467
|
+
...record
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
async function readCodexAppServerRolloutTokenUsage(file) {
|
|
2472
|
+
let handle;
|
|
2473
|
+
try {
|
|
2474
|
+
handle = await fs.open(file, "r");
|
|
2475
|
+
} catch {
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
let totalTokens;
|
|
2479
|
+
try {
|
|
2480
|
+
for await (const line of handle.readLines()) {
|
|
2481
|
+
const lineTokens = readCodexAppServerRolloutTokenUsageLine(line);
|
|
2482
|
+
if (lineTokens !== void 0) totalTokens = lineTokens;
|
|
2483
|
+
}
|
|
2484
|
+
} finally {
|
|
2485
|
+
await handle.close();
|
|
2486
|
+
}
|
|
2487
|
+
return totalTokens;
|
|
2488
|
+
}
|
|
2489
|
+
function readCodexAppServerRolloutTokenUsageLine(line) {
|
|
2490
|
+
if (!line.trim()) return;
|
|
2491
|
+
try {
|
|
2492
|
+
const parsed = JSON.parse(line);
|
|
2493
|
+
const payload = isJsonObject(parsed) ? parsed.payload : void 0;
|
|
2494
|
+
const info = isJsonObject(payload) && payload.type === "token_count" && isJsonObject(payload.info) ? payload.info : void 0;
|
|
2495
|
+
if (!info) return;
|
|
2496
|
+
const usage = isJsonObject(info.last_token_usage) ? info.last_token_usage : isJsonObject(info.total_token_usage) ? info.total_token_usage : void 0;
|
|
2497
|
+
const value = usage?.total_tokens ?? usage?.totalTokens;
|
|
2498
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2499
|
+
} catch {
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function maxFiniteNumber(values) {
|
|
2504
|
+
const nums = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
2505
|
+
if (nums.length === 0) return;
|
|
2506
|
+
return Math.max(...nums);
|
|
2507
|
+
}
|
|
2508
|
+
async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
2509
|
+
const binding = params.binding;
|
|
2510
|
+
if (!binding?.threadId) return binding;
|
|
2511
|
+
if (params.config?.agents?.defaults?.compaction?.truncateAfterCompaction !== true) return binding;
|
|
2512
|
+
const sessionRecord = await readCodexSessionRecordForSessionFile(params.sessionFile);
|
|
2513
|
+
const maxBytes = parseCodexAppServerByteLimit(params.config?.agents?.defaults?.compaction?.maxActiveTranscriptBytes);
|
|
2514
|
+
const rolloutFiles = await listCodexAppServerRolloutFilesForThread(params.agentDir, binding.threadId, params.codexHome);
|
|
2515
|
+
if (maxBytes !== void 0) {
|
|
2516
|
+
const oversizedFiles = rolloutFiles.filter((file) => file.bytes >= maxBytes);
|
|
2517
|
+
if (oversizedFiles.length > 0) {
|
|
2518
|
+
embeddedAgentLog.warn("codex app-server native transcript exceeded active byte limit; starting a fresh thread", {
|
|
2519
|
+
threadId: binding.threadId,
|
|
2520
|
+
maxBytes,
|
|
2521
|
+
files: oversizedFiles.map((file) => ({
|
|
2522
|
+
path: file.path,
|
|
2523
|
+
bytes: file.bytes
|
|
2524
|
+
}))
|
|
2525
|
+
});
|
|
2526
|
+
await clearCodexAppServerBinding(params.sessionFile);
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
const nativeTokens = maxFiniteNumber(await Promise.all(rolloutFiles.map(async (file) => readCodexAppServerRolloutTokenUsage(file.path))));
|
|
2531
|
+
const sessionTokens = sessionRecord?.totalTokensFresh !== false && typeof sessionRecord?.totalTokens === "number" && Number.isFinite(sessionRecord.totalTokens) ? sessionRecord.totalTokens : void 0;
|
|
2532
|
+
const tokenCount = maxFiniteNumber([sessionTokens, nativeTokens]);
|
|
2533
|
+
if (tokenCount !== void 0 && tokenCount >= CODEX_APP_SERVER_NATIVE_THREAD_MAX_TOKENS) {
|
|
2534
|
+
embeddedAgentLog.warn("codex app-server native transcript exceeded active token limit; starting a fresh thread", {
|
|
2535
|
+
threadId: binding.threadId,
|
|
2536
|
+
maxTokens: CODEX_APP_SERVER_NATIVE_THREAD_MAX_TOKENS,
|
|
2537
|
+
sessionKey: sessionRecord?.sessionKey,
|
|
2538
|
+
sessionTokens,
|
|
2539
|
+
nativeTokens
|
|
2540
|
+
});
|
|
2541
|
+
await clearCodexAppServerBinding(params.sessionFile);
|
|
2542
|
+
return;
|
|
2543
|
+
}
|
|
2544
|
+
return binding;
|
|
2545
|
+
}
|
|
2404
2546
|
async function runCodexAppServerAttempt(params, options = {}) {
|
|
2405
2547
|
const attemptStartedAt = Date.now();
|
|
2406
2548
|
const attemptClientFactory = options.clientFactory ?? defaultCodexAppServerClientFactory;
|
|
@@ -2440,8 +2582,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2440
2582
|
agentId: params.agentId
|
|
2441
2583
|
});
|
|
2442
2584
|
const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
|
|
2443
|
-
|
|
2444
|
-
const
|
|
2585
|
+
let startupBinding = await readCodexAppServerBinding(params.sessionFile);
|
|
2586
|
+
const startupBindingAuthProfileId = startupBinding?.authProfileId;
|
|
2587
|
+
startupBinding = await rotateOversizedCodexAppServerStartupBinding({
|
|
2588
|
+
binding: startupBinding,
|
|
2589
|
+
sessionFile: params.sessionFile,
|
|
2590
|
+
agentDir,
|
|
2591
|
+
codexHome: appServer.start.env?.CODEX_HOME,
|
|
2592
|
+
config: params.config
|
|
2593
|
+
});
|
|
2594
|
+
const startupAuthProfileCandidate = params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId ?? startupBindingAuthProfileId;
|
|
2445
2595
|
const startupAuthProfileId = params.authProfileStore ? resolveCodexAppServerAuthProfileId({
|
|
2446
2596
|
authProfileId: startupAuthProfileCandidate,
|
|
2447
2597
|
store: params.authProfileStore,
|
|
@@ -2483,6 +2633,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2483
2633
|
});
|
|
2484
2634
|
for (const diagnostic of bundleMcpThreadConfig.diagnostics) embeddedAgentLog.warn(`bundle-mcp: ${diagnostic.pluginId}: ${diagnostic.message}`);
|
|
2485
2635
|
const activeContextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : void 0;
|
|
2636
|
+
const hookChannelId = resolveCodexAppServerHookChannelId(params, sandboxSessionKey);
|
|
2486
2637
|
let yieldDetected = false;
|
|
2487
2638
|
const toolBridge = createCodexDynamicToolBridge({
|
|
2488
2639
|
tools: await buildDynamicTools({
|
|
@@ -2499,14 +2650,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2499
2650
|
}
|
|
2500
2651
|
}),
|
|
2501
2652
|
signal: runAbortController.signal,
|
|
2502
|
-
loading: pluginConfig
|
|
2653
|
+
loading: resolveCodexDynamicToolsLoading(pluginConfig),
|
|
2503
2654
|
directToolNames: shouldForceMessageTool(params) ? ["message"] : [],
|
|
2504
2655
|
hookContext: {
|
|
2505
2656
|
agentId: sessionAgentId,
|
|
2506
2657
|
config: params.config,
|
|
2507
2658
|
sessionId: params.sessionId,
|
|
2508
2659
|
sessionKey: sandboxSessionKey,
|
|
2509
|
-
runId: params.runId
|
|
2660
|
+
runId: params.runId,
|
|
2661
|
+
channelId: hookChannelId
|
|
2510
2662
|
}
|
|
2511
2663
|
});
|
|
2512
2664
|
const hadSessionFile = await pathExists(activeSessionFile);
|
|
@@ -2524,7 +2676,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2524
2676
|
workspaceDir: params.workspaceDir,
|
|
2525
2677
|
messageProvider: params.messageProvider ?? void 0,
|
|
2526
2678
|
trigger: params.trigger,
|
|
2527
|
-
channelId:
|
|
2679
|
+
channelId: hookChannelId,
|
|
2528
2680
|
...hookContextWindowFields
|
|
2529
2681
|
};
|
|
2530
2682
|
const activeContextEnginePluginId = activeContextEngine ? resolveContextEngineOwnerPluginId(activeContextEngine) : void 0;
|
|
@@ -2644,7 +2796,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2644
2796
|
prePromptMessageCount = projection.prePromptMessageCount;
|
|
2645
2797
|
}
|
|
2646
2798
|
const buildPromptFromCurrentInputs = () => resolveAgentHarnessBeforePromptBuildResult({
|
|
2647
|
-
prompt:
|
|
2799
|
+
prompt: prependCurrentInboundContext(promptText, params.currentInboundContext),
|
|
2648
2800
|
developerInstructions,
|
|
2649
2801
|
messages: historyMessages,
|
|
2650
2802
|
ctx: hookContext
|
|
@@ -2688,16 +2840,18 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2688
2840
|
sessionKey: sandboxSessionKey,
|
|
2689
2841
|
config: params.config,
|
|
2690
2842
|
runId: params.runId,
|
|
2843
|
+
channelId: hookChannelId,
|
|
2691
2844
|
attemptTimeoutMs: params.timeoutMs,
|
|
2692
2845
|
startupTimeoutMs,
|
|
2693
2846
|
turnStartTimeoutMs: params.timeoutMs,
|
|
2694
2847
|
signal: runAbortController.signal
|
|
2695
2848
|
});
|
|
2696
|
-
const
|
|
2849
|
+
const nativeHookRelayConfig = nativeHookRelay ? buildCodexNativeHookRelayConfig({
|
|
2697
2850
|
relay: nativeHookRelay,
|
|
2698
2851
|
events: nativeHookRelayEvents,
|
|
2699
2852
|
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
|
|
2700
|
-
}) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0
|
|
2853
|
+
}) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0;
|
|
2854
|
+
const threadConfig = mergeCodexThreadConfigs(bundleMcpThreadConfig?.configPatch);
|
|
2701
2855
|
const pluginThreadConfigEnabled = shouldBuildCodexPluginThreadConfig(pluginConfig);
|
|
2702
2856
|
const pluginAppCacheKey = buildCodexPluginAppCacheKey({
|
|
2703
2857
|
appServer,
|
|
@@ -2740,6 +2894,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2740
2894
|
appServer: pluginAppServer,
|
|
2741
2895
|
developerInstructions: promptBuild.developerInstructions,
|
|
2742
2896
|
config: threadConfig,
|
|
2897
|
+
finalConfigPatch: nativeHookRelayConfig,
|
|
2743
2898
|
mcpServersFingerprint: bundleMcpThreadConfig.fingerprint,
|
|
2744
2899
|
mcpServersFingerprintEvaluated: bundleMcpThreadConfig.evaluated,
|
|
2745
2900
|
contextEngineProjection,
|
|
@@ -2826,9 +2981,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2826
2981
|
let userInputBridge;
|
|
2827
2982
|
let steeringQueue;
|
|
2828
2983
|
let completed = false;
|
|
2984
|
+
let terminalTurnNotificationQueued = false;
|
|
2829
2985
|
let timedOut = false;
|
|
2830
2986
|
let turnCompletionIdleTimedOut = false;
|
|
2831
2987
|
let turnCompletionIdleTimeoutMessage;
|
|
2988
|
+
let clientClosedPromptError;
|
|
2989
|
+
let clientClosedAbort = false;
|
|
2832
2990
|
let lifecycleStarted = false;
|
|
2833
2991
|
let lifecycleTerminalEmitted = false;
|
|
2834
2992
|
let resolveCompletion;
|
|
@@ -2842,6 +3000,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2842
3000
|
let turnCompletionIdleTimer;
|
|
2843
3001
|
let turnCompletionIdleWatchArmed = false;
|
|
2844
3002
|
let turnCompletionIdleWatchPinnedByTerminalError = false;
|
|
3003
|
+
let turnCompletionIdleTimeoutOverrideMs;
|
|
2845
3004
|
let turnAssistantCompletionIdleTimer;
|
|
2846
3005
|
let turnAssistantCompletionIdleWatchArmed = false;
|
|
2847
3006
|
let turnAssistantCompletionLastActivityAt = Date.now();
|
|
@@ -2953,8 +3112,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2953
3112
|
};
|
|
2954
3113
|
const fireTurnCompletionIdleTimeout = () => {
|
|
2955
3114
|
if (completed || runAbortController.signal.aborted || !turnCompletionIdleWatchArmed || activeAppServerTurnRequests > 0) return;
|
|
3115
|
+
const timeoutMs = turnCompletionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
|
|
2956
3116
|
const idleMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
|
|
2957
|
-
if (idleMs <
|
|
3117
|
+
if (idleMs < timeoutMs) {
|
|
2958
3118
|
scheduleTurnCompletionIdleWatch();
|
|
2959
3119
|
return;
|
|
2960
3120
|
}
|
|
@@ -2966,7 +3126,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2966
3126
|
threadId: thread.threadId,
|
|
2967
3127
|
turnId,
|
|
2968
3128
|
idleMs,
|
|
2969
|
-
timeoutMs
|
|
3129
|
+
timeoutMs,
|
|
2970
3130
|
lastActivityReason: turnCompletionLastActivityReason,
|
|
2971
3131
|
...turnCompletionLastActivityDetails
|
|
2972
3132
|
});
|
|
@@ -2974,7 +3134,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2974
3134
|
threadId: thread.threadId,
|
|
2975
3135
|
turnId,
|
|
2976
3136
|
idleMs,
|
|
2977
|
-
timeoutMs
|
|
3137
|
+
timeoutMs,
|
|
2978
3138
|
lastActivityReason: turnCompletionLastActivityReason,
|
|
2979
3139
|
...turnCompletionLastActivityDetails
|
|
2980
3140
|
});
|
|
@@ -3013,7 +3173,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3013
3173
|
clearTurnCompletionIdleTimer();
|
|
3014
3174
|
if (completed || runAbortController.signal.aborted || !turnCompletionIdleWatchArmed || activeAppServerTurnRequests > 0) return;
|
|
3015
3175
|
const elapsedMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
|
|
3016
|
-
const delayMs = Math.max(1, turnCompletionIdleTimeoutMs - elapsedMs);
|
|
3176
|
+
const delayMs = Math.max(1, (turnCompletionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs) - elapsedMs);
|
|
3017
3177
|
turnCompletionIdleTimer = setTimeout(fireTurnCompletionIdleTimeout, delayMs);
|
|
3018
3178
|
turnCompletionIdleTimer.unref?.();
|
|
3019
3179
|
}
|
|
@@ -3064,6 +3224,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3064
3224
|
turnCompletionLastActivityAt = Date.now();
|
|
3065
3225
|
turnCompletionLastActivityReason = reason;
|
|
3066
3226
|
turnCompletionLastActivityDetails = options?.details;
|
|
3227
|
+
turnCompletionIdleTimeoutOverrideMs = void 0;
|
|
3067
3228
|
if (options?.attemptProgress) {
|
|
3068
3229
|
turnAttemptLastProgressAt = turnCompletionLastActivityAt;
|
|
3069
3230
|
turnAttemptLastProgressReason = reason;
|
|
@@ -3092,6 +3253,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3092
3253
|
const disarmTurnCompletionIdleWatch = () => {
|
|
3093
3254
|
turnCompletionIdleWatchArmed = false;
|
|
3094
3255
|
turnCompletionIdleWatchPinnedByTerminalError = false;
|
|
3256
|
+
turnCompletionIdleTimeoutOverrideMs = void 0;
|
|
3095
3257
|
clearTurnCompletionIdleTimer();
|
|
3096
3258
|
};
|
|
3097
3259
|
const disarmTurnAssistantCompletionIdleWatch = () => {
|
|
@@ -3108,6 +3270,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3108
3270
|
const armTurnCompletionIdleWatch = (options) => {
|
|
3109
3271
|
turnCompletionIdleWatchArmed = true;
|
|
3110
3272
|
turnCompletionIdleWatchPinnedByTerminalError = options?.pinnedByTerminalError === true;
|
|
3273
|
+
turnCompletionIdleTimeoutOverrideMs = options?.timeoutMs !== void 0 ? Math.max(1, Math.floor(options.timeoutMs)) : void 0;
|
|
3111
3274
|
scheduleTurnCompletionIdleWatch();
|
|
3112
3275
|
};
|
|
3113
3276
|
const emitLifecycleStart = () => {
|
|
@@ -3162,6 +3325,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3162
3325
|
itemId: item.id
|
|
3163
3326
|
});
|
|
3164
3327
|
};
|
|
3328
|
+
const isTerminalTurnNotificationForTurn = (notification, notificationTurnId) => {
|
|
3329
|
+
if (!isTurnNotification(notification.params, thread.threadId, notificationTurnId)) return false;
|
|
3330
|
+
return notification.method === "turn/completed" || isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt });
|
|
3331
|
+
};
|
|
3165
3332
|
const handleNotification = async (notification) => {
|
|
3166
3333
|
userInputBridge?.handleNotification(notification);
|
|
3167
3334
|
if (!projector || !turnId) {
|
|
@@ -3183,6 +3350,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3183
3350
|
const rawToolOutputCompletion = isRawToolOutputCompletionNotification(notification);
|
|
3184
3351
|
if (isCurrentTurnNotification && (rawToolOutputCompletion || isNativeToolProgressNotification(notification))) turnCrossedToolHandoff = true;
|
|
3185
3352
|
const assistantCompletionCanRelease = isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff);
|
|
3353
|
+
const postToolRawAssistantCompletionNeedsTerminalGuard = isCurrentTurnNotification && turnCrossedToolHandoff && isRawAssistantCompletionNotification(notification) && activeTurnItemIds.size === 0;
|
|
3186
3354
|
const shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem = isCurrentTurnNotification && notification.method === "item/completed" && activeTurnItemIds.size === 0 && !trackedDynamicToolCompletion && !assistantCompletionCanRelease;
|
|
3187
3355
|
if (isCurrentTurnNotification && notification.method === "error") {
|
|
3188
3356
|
if (isRetryableErrorNotification(notification.params)) disarmTurnCompletionIdleWatch();
|
|
@@ -3190,13 +3358,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3190
3358
|
disarmTurnAssistantCompletionIdleWatch();
|
|
3191
3359
|
} else if (isTurnCompletion) disarmTurnAssistantCompletionIdleWatch();
|
|
3192
3360
|
else if (isCurrentTurnNotification && assistantCompletionCanRelease) armTurnAssistantCompletionIdleWatch(describeNotificationActivity(notification));
|
|
3361
|
+
else if (postToolRawAssistantCompletionNeedsTerminalGuard) armTurnCompletionIdleWatch({ timeoutMs: turnAssistantCompletionIdleTimeoutMs });
|
|
3193
3362
|
else if (unblockedAssistantCompletionRelease) armTurnAssistantCompletionIdleWatch(describeNotificationActivity(notification));
|
|
3194
3363
|
else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) armTurnCompletionIdleWatch();
|
|
3195
3364
|
else if (isCurrentTurnNotification && rawToolOutputCompletion) armTurnCompletionIdleWatch();
|
|
3196
3365
|
else if (isCurrentTurnNotification && shouldDisarmAssistantCompletionIdleWatch(notification)) disarmTurnAssistantCompletionIdleWatch();
|
|
3197
|
-
if (turnCompletionIdleWatchArmed && !turnCompletionIdleWatchPinnedByTerminalError && notification.method !== "turn/completed" && isCurrentTurnNotification && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) disarmTurnCompletionIdleWatch();
|
|
3366
|
+
if (turnCompletionIdleWatchArmed && !turnCompletionIdleWatchPinnedByTerminalError && notification.method !== "turn/completed" && isCurrentTurnNotification && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !postToolRawAssistantCompletionNeedsTerminalGuard && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) disarmTurnCompletionIdleWatch();
|
|
3198
3367
|
const isTurnAbortMarker = isCurrentTurnNotification && isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt });
|
|
3199
|
-
const isTurnTerminal =
|
|
3368
|
+
const isTurnTerminal = isTerminalTurnNotificationForTurn(notification, turnId);
|
|
3369
|
+
if (isTurnTerminal) terminalTurnNotificationQueued = true;
|
|
3200
3370
|
try {
|
|
3201
3371
|
await waitForCodexNotificationDispatchTurn();
|
|
3202
3372
|
await projector.handleNotification(notification);
|
|
@@ -3223,6 +3393,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3223
3393
|
pendingNotifications.push(notification);
|
|
3224
3394
|
return Promise.resolve();
|
|
3225
3395
|
}
|
|
3396
|
+
if (isTerminalTurnNotificationForTurn(notification, turnId)) terminalTurnNotificationQueued = true;
|
|
3226
3397
|
notificationQueue = notificationQueue.then(() => handleNotification(notification), () => handleNotification(notification));
|
|
3227
3398
|
return notificationQueue;
|
|
3228
3399
|
};
|
|
@@ -3280,6 +3451,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3280
3451
|
paramsForRun: params,
|
|
3281
3452
|
threadId: thread.threadId,
|
|
3282
3453
|
turnId,
|
|
3454
|
+
nativeHookRelay,
|
|
3283
3455
|
signal: runAbortController.signal
|
|
3284
3456
|
});
|
|
3285
3457
|
}
|
|
@@ -3375,6 +3547,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3375
3547
|
} else scheduleTurnProgressWatches();
|
|
3376
3548
|
}
|
|
3377
3549
|
});
|
|
3550
|
+
let closeCleanup;
|
|
3378
3551
|
const forceContextEngineCompactionForCodexOverflow = async (error) => {
|
|
3379
3552
|
if (!activeContextEngine?.info.ownsCompaction) return false;
|
|
3380
3553
|
embeddedAgentLog.warn("codex app-server context-engine turn overflowed; forcing context-engine compaction", {
|
|
@@ -3520,6 +3693,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3520
3693
|
signal: runAbortController.signal
|
|
3521
3694
|
});
|
|
3522
3695
|
const turnStartErrorMessage = usageLimitError?.message ?? formatErrorMessage(turnStartError);
|
|
3696
|
+
if (isInvalidCodexImagePayloadError(turnStartErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(activeSessionFile, {
|
|
3697
|
+
phase: "turn_start",
|
|
3698
|
+
threadId: thread.threadId,
|
|
3699
|
+
error: turnStartErrorMessage
|
|
3700
|
+
});
|
|
3523
3701
|
emitCodexAppServerEvent(params, {
|
|
3524
3702
|
stream: "codex_app_server.lifecycle",
|
|
3525
3703
|
data: {
|
|
@@ -3602,7 +3780,31 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3602
3780
|
prompt: promptBuild.prompt,
|
|
3603
3781
|
imagesCount: params.images?.length ?? 0
|
|
3604
3782
|
});
|
|
3605
|
-
projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, {
|
|
3783
|
+
projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, {
|
|
3784
|
+
nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && nativeHookRelay.shouldRelayEvent("post_tool_use"),
|
|
3785
|
+
trajectoryRecorder
|
|
3786
|
+
});
|
|
3787
|
+
if (isTerminalTurnStatus(turn.turn.status) || pendingNotifications.some((notification) => isTerminalTurnNotificationForTurn(notification, activeTurnId))) terminalTurnNotificationQueued = true;
|
|
3788
|
+
closeCleanup = client.addCloseHandler?.(() => {
|
|
3789
|
+
if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted) return;
|
|
3790
|
+
clientClosedPromptError = "codex app-server client closed before turn completed";
|
|
3791
|
+
trajectoryRecorder?.recordEvent("turn.client_closed", {
|
|
3792
|
+
threadId: thread.threadId,
|
|
3793
|
+
turnId: activeTurnId
|
|
3794
|
+
});
|
|
3795
|
+
embeddedAgentLog.warn("codex app-server client closed before turn completed", {
|
|
3796
|
+
threadId: thread.threadId,
|
|
3797
|
+
turnId: activeTurnId
|
|
3798
|
+
});
|
|
3799
|
+
clientClosedAbort = true;
|
|
3800
|
+
runAbortController.abort("client_closed");
|
|
3801
|
+
completed = true;
|
|
3802
|
+
clearTurnAttemptIdleTimer();
|
|
3803
|
+
clearTurnCompletionIdleTimer();
|
|
3804
|
+
clearTurnAssistantCompletionIdleTimer();
|
|
3805
|
+
clearTurnTerminalIdleTimer();
|
|
3806
|
+
resolveCompletion?.();
|
|
3807
|
+
});
|
|
3606
3808
|
emitLifecycleStart();
|
|
3607
3809
|
const activeProjector = projector;
|
|
3608
3810
|
turnTerminalIdleWatchArmed = true;
|
|
@@ -3655,9 +3857,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3655
3857
|
try {
|
|
3656
3858
|
await completion;
|
|
3657
3859
|
const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected });
|
|
3658
|
-
const finalAborted = result.aborted || runAbortController.signal.aborted;
|
|
3659
|
-
let finalPromptError = turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError;
|
|
3860
|
+
const finalAborted = result.aborted || runAbortController.signal.aborted && !clientClosedAbort;
|
|
3861
|
+
let finalPromptError = clientClosedPromptError ?? (turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError);
|
|
3660
3862
|
const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
|
|
3863
|
+
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(activeSessionFile, {
|
|
3864
|
+
phase: "turn_completed",
|
|
3865
|
+
threadId: thread.threadId,
|
|
3866
|
+
turnId: activeTurnId,
|
|
3867
|
+
error: finalPromptErrorMessage
|
|
3868
|
+
});
|
|
3661
3869
|
if (shouldRefreshCodexRateLimitsForUsageLimitMessage(finalPromptErrorMessage)) finalPromptError = await refreshCodexUsageLimitErrorMessage({
|
|
3662
3870
|
client,
|
|
3663
3871
|
source: {
|
|
@@ -3668,7 +3876,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3668
3876
|
timeoutMs: appServer.requestTimeoutMs,
|
|
3669
3877
|
signal: runAbortController.signal
|
|
3670
3878
|
});
|
|
3671
|
-
const finalPromptErrorSource = timedOut ? "prompt" : result.promptErrorSource;
|
|
3879
|
+
const finalPromptErrorSource = timedOut || clientClosedPromptError ? "prompt" : result.promptErrorSource;
|
|
3672
3880
|
recordCodexTrajectoryCompletion(trajectoryRecorder, {
|
|
3673
3881
|
attempt: params,
|
|
3674
3882
|
result,
|
|
@@ -3774,11 +3982,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3774
3982
|
error: "codex app-server run completed without lifecycle terminal event"
|
|
3775
3983
|
});
|
|
3776
3984
|
if (trajectoryRecorder && !trajectoryEndRecorded) trajectoryRecorder.recordEvent("session.ended", {
|
|
3777
|
-
status: timedOut || runAbortController.signal.aborted ? "interrupted" : "cleanup",
|
|
3985
|
+
status: timedOut || runAbortController.signal.aborted && !clientClosedAbort ? "interrupted" : "cleanup",
|
|
3778
3986
|
threadId: thread.threadId,
|
|
3779
3987
|
turnId: activeTurnId,
|
|
3780
3988
|
timedOut,
|
|
3781
|
-
aborted: runAbortController.signal.aborted
|
|
3989
|
+
aborted: runAbortController.signal.aborted && !clientClosedAbort
|
|
3782
3990
|
});
|
|
3783
3991
|
await runAgentCleanupStep({
|
|
3784
3992
|
runId: params.runId,
|
|
@@ -3797,6 +4005,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
3797
4005
|
clearTurnTerminalIdleTimer();
|
|
3798
4006
|
notificationCleanup();
|
|
3799
4007
|
requestCleanup();
|
|
4008
|
+
closeCleanup?.();
|
|
3800
4009
|
nativeHookRelay?.unregister();
|
|
3801
4010
|
runAbortController.signal.removeEventListener("abort", abortListener);
|
|
3802
4011
|
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
@@ -3956,6 +4165,7 @@ function createCodexNativeHookRelay(params) {
|
|
|
3956
4165
|
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3957
4166
|
...params.config ? { config: params.config } : {},
|
|
3958
4167
|
runId: params.runId,
|
|
4168
|
+
...params.channelId ? { channelId: params.channelId } : {},
|
|
3959
4169
|
allowedEvents: params.events,
|
|
3960
4170
|
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
3961
4171
|
explicitTtlMs: params.options?.ttlMs,
|
|
@@ -4019,6 +4229,15 @@ function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
|
|
|
4019
4229
|
runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0
|
|
4020
4230
|
};
|
|
4021
4231
|
}
|
|
4232
|
+
function resolveCodexAppServerHookChannelId(params, sandboxSessionKey) {
|
|
4233
|
+
return buildAgentHookContextChannelFields({
|
|
4234
|
+
sessionKey: sandboxSessionKey,
|
|
4235
|
+
messageChannel: params.messageChannel,
|
|
4236
|
+
messageProvider: params.messageProvider,
|
|
4237
|
+
currentChannelId: params.currentChannelId,
|
|
4238
|
+
messageTo: params.messageTo
|
|
4239
|
+
}).channelId;
|
|
4240
|
+
}
|
|
4022
4241
|
function buildOpenClawCodingToolsOptions(input) {
|
|
4023
4242
|
const { params } = input;
|
|
4024
4243
|
const modelHasVision = params.model.input?.includes("image") ?? false;
|
|
@@ -4045,7 +4264,7 @@ function buildOpenClawCodingToolsOptions(input) {
|
|
|
4045
4264
|
senderUsername: params.senderUsername,
|
|
4046
4265
|
senderE164: params.senderE164,
|
|
4047
4266
|
senderIsOwner: params.senderIsOwner,
|
|
4048
|
-
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding,
|
|
4267
|
+
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
|
|
4049
4268
|
...sessionKeys,
|
|
4050
4269
|
sessionId: params.sessionId,
|
|
4051
4270
|
runId: params.runId,
|
|
@@ -4066,6 +4285,7 @@ function buildOpenClawCodingToolsOptions(input) {
|
|
|
4066
4285
|
modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, void 0, { workspaceDir: input.effectiveWorkspace }),
|
|
4067
4286
|
suppressManagedWebSearch: false,
|
|
4068
4287
|
currentChannelId: params.currentChannelId,
|
|
4288
|
+
hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
|
|
4069
4289
|
currentThreadTs: params.currentThreadTs,
|
|
4070
4290
|
currentMessageId: params.currentMessageId,
|
|
4071
4291
|
replyToMode: params.replyToMode,
|
|
@@ -4324,6 +4544,23 @@ function readCodexErrorPayload(error) {
|
|
|
4324
4544
|
rateLimits
|
|
4325
4545
|
};
|
|
4326
4546
|
}
|
|
4547
|
+
function isInvalidCodexImagePayloadError(message) {
|
|
4548
|
+
if (typeof message !== "string" || !message.trim()) return false;
|
|
4549
|
+
const normalizedMessage = message.replace(/[_-]+/gu, " ");
|
|
4550
|
+
return /\b(?:invalid|malformed)\b[\s\S]{0,120}\b(?:image|image url|base64)\b/iu.test(normalizedMessage) || /\b(?:image|image url|base64)\b[\s\S]{0,120}\b(?:invalid|malformed)\b/iu.test(normalizedMessage);
|
|
4551
|
+
}
|
|
4552
|
+
async function clearCodexBindingAfterInvalidImagePayload(sessionFile, fields) {
|
|
4553
|
+
const currentBinding = await readCodexAppServerBinding(sessionFile);
|
|
4554
|
+
if (fields.threadId && currentBinding && currentBinding.threadId !== fields.threadId) {
|
|
4555
|
+
embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
|
|
4556
|
+
...fields,
|
|
4557
|
+
boundThreadId: currentBinding.threadId
|
|
4558
|
+
});
|
|
4559
|
+
return;
|
|
4560
|
+
}
|
|
4561
|
+
embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
|
|
4562
|
+
await clearCodexAppServerBinding(sessionFile);
|
|
4563
|
+
}
|
|
4327
4564
|
function describeNotificationActivity(notification) {
|
|
4328
4565
|
if (!isJsonObject(notification.params)) return { lastNotificationMethod: notification.method };
|
|
4329
4566
|
if (notification.method !== "rawResponseItem/completed") return { lastNotificationMethod: notification.method };
|
|
@@ -4692,7 +4929,7 @@ function codexExecutionToolName(item) {
|
|
|
4692
4929
|
function joinPresentSections(...sections) {
|
|
4693
4930
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
4694
4931
|
}
|
|
4695
|
-
function
|
|
4932
|
+
function prependCurrentInboundContext(prompt, context) {
|
|
4696
4933
|
const text = context?.text.trim();
|
|
4697
4934
|
return text ? [text, prompt].filter(Boolean).join("\n\n") : prompt;
|
|
4698
4935
|
}
|
|
@@ -4708,6 +4945,7 @@ function handleApprovalRequest(params) {
|
|
|
4708
4945
|
paramsForRun: params.paramsForRun,
|
|
4709
4946
|
threadId: params.threadId,
|
|
4710
4947
|
turnId: params.turnId,
|
|
4948
|
+
nativeHookRelay: params.nativeHookRelay,
|
|
4711
4949
|
signal: params.signal
|
|
4712
4950
|
});
|
|
4713
4951
|
}
|