@openclaw/codex 2026.5.16-beta.2 → 2026.5.16-beta.3

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.
@@ -1,5 +1,5 @@
1
1
  import { a as isCodexFastServiceTier, c as resolveCodexAppServerRuntimeOptions } from "./config-3ATInASk.js";
2
- import { n as listCodexAppServerModels, t as listAllCodexAppServerModels } from "./models-BHS-8DS_.js";
2
+ import { n as listCodexAppServerModels, t as listAllCodexAppServerModels } from "./models-BZLMz9WS.js";
3
3
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
4
4
  import { i as describeControlFailure, r as CODEX_CONTROL_METHODS, t as requestCodexAppServerJson } from "./request-Czoa60Nt.js";
5
5
  import { a as formatComputerUseStatus, c as formatThreads, i as formatCodexStatus, l as readString$1, n as formatAccount, o as formatList, p as summarizeCodexAccountUsage, r as formatCodexDisplayText, s as formatModels, t as buildHelp } from "./command-formatters-BRW7_Nu7.js";
package/dist/harness.js CHANGED
@@ -18,14 +18,14 @@ function createCodexAppServerAgentHarness(options) {
18
18
  };
19
19
  },
20
20
  runAttempt: async (params) => {
21
- const { runCodexAppServerAttempt } = await import("./run-attempt-B6gN6CrO.js");
21
+ const { runCodexAppServerAttempt } = await import("./run-attempt-B3nk5oT1.js");
22
22
  return runCodexAppServerAttempt(params, {
23
23
  pluginConfig: options?.pluginConfig,
24
24
  nativeHookRelay: { enabled: true }
25
25
  });
26
26
  },
27
27
  runSideQuestion: async (params) => {
28
- const { runCodexAppServerSideQuestion } = await import("./side-question-CFcVH8fj.js");
28
+ const { runCodexAppServerSideQuestion } = await import("./side-question-D3nH-Jk1.js");
29
29
  return runCodexAppServerSideQuestion(params, { pluginConfig: options?.pluginConfig });
30
30
  },
31
31
  compact: async (params) => {
package/dist/index.js CHANGED
@@ -38,7 +38,7 @@ async function handleCodexCommand(ctx, options = {}) {
38
38
  }
39
39
  }
40
40
  async function loadDefaultCodexSubcommandHandler() {
41
- const { handleCodexSubcommand } = await import("./command-handlers-D6UDzndR.js");
41
+ const { handleCodexSubcommand } = await import("./command-handlers-C4F8BChC.js");
42
42
  return handleCodexSubcommand;
43
43
  }
44
44
  //#endregion
@@ -1,7 +1,7 @@
1
1
  import { CODEX_PROVIDER_ID, FALLBACK_CODEX_MODELS } from "./provider-catalog.js";
2
2
  import { c as resolveCodexAppServerRuntimeOptions } from "./config-3ATInASk.js";
3
- import { i as assertCodexTurnStartResponse, l as readCodexTurnCompletedNotification, o as readCodexErrorNotification, r as assertCodexThreadStartResponse } from "./protocol-validators-CSY0BFBo.js";
4
- import { i as readModelListResult } from "./models-BHS-8DS_.js";
3
+ import { i as assertCodexTurnStartResponse, l as readCodexTurnCompletedNotification, o as readCodexErrorNotification, r as assertCodexThreadStartResponse } from "./protocol-validators-BGBspNmF.js";
4
+ import { i as readModelListResult } from "./models-BZLMz9WS.js";
5
5
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
6
6
  import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
7
7
  //#region extensions/codex/media-understanding-provider.ts
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
- import { s as readCodexModelListResponse } from "./protocol-validators-CSY0BFBo.js";
2
+ import { s as readCodexModelListResponse } from "./protocol-validators-BGBspNmF.js";
3
3
  //#region extensions/codex/src/app-server/models.ts
4
4
  var models_exports = /* @__PURE__ */ __exportAll({
5
5
  listAllCodexAppServerModels: () => listAllCodexAppServerModels,
@@ -318,6 +318,12 @@ var ThreadResumeResponse_default = {
318
318
  "anyOf": [{ "$ref": "#/definitions/PermissionProfile" }, { "type": "null" }]
319
319
  },
320
320
  "reasoningEffort": { "anyOf": [{ "$ref": "#/definitions/ReasoningEffort" }, { "type": "null" }] },
321
+ "runtimeWorkspaceRoots": {
322
+ "description": "Thread-scoped runtime workspace roots used to materialize `:workspace_roots`.",
323
+ "default": [],
324
+ "type": "array",
325
+ "items": { "$ref": "#/definitions/AbsolutePathBuf" }
326
+ },
321
327
  "sandbox": {
322
328
  "description": "Legacy sandbox policy retained for compatibility. Experimental clients should prefer `permissionProfile` when they need exact runtime permissions.",
323
329
  "allOf": [{ "$ref": "#/definitions/SandboxPolicy" }]
@@ -342,29 +348,9 @@ var ThreadResumeResponse_default = {
342
348
  "id": {
343
349
  "description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
344
350
  "type": "string"
345
- },
346
- "modifications": {
347
- "description": "Bounded user-requested modifications applied on top of the named profile, if any.",
348
- "default": [],
349
- "type": "array",
350
- "items": { "$ref": "#/definitions/ActivePermissionProfileModification" }
351
351
  }
352
352
  }
353
353
  },
354
- "ActivePermissionProfileModification": { "oneOf": [{
355
- "description": "Additional concrete directory that should be writable.",
356
- "type": "object",
357
- "required": ["path", "type"],
358
- "properties": {
359
- "path": { "$ref": "#/definitions/AbsolutePathBuf" },
360
- "type": {
361
- "type": "string",
362
- "enum": ["additionalWritableRoot"],
363
- "title": "AdditionalWritableRootActivePermissionProfileModificationType"
364
- }
365
- },
366
- "title": "AdditionalWritableRootActivePermissionProfileModification"
367
- }] },
368
354
  "AgentPath": { "type": "string" },
369
355
  "ApprovalsReviewer": {
370
356
  "description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
@@ -2035,6 +2021,12 @@ var ThreadStartResponse_default = {
2035
2021
  "anyOf": [{ "$ref": "#/definitions/PermissionProfile" }, { "type": "null" }]
2036
2022
  },
2037
2023
  "reasoningEffort": { "anyOf": [{ "$ref": "#/definitions/ReasoningEffort" }, { "type": "null" }] },
2024
+ "runtimeWorkspaceRoots": {
2025
+ "description": "Thread-scoped runtime workspace roots used to materialize `:workspace_roots`.",
2026
+ "default": [],
2027
+ "type": "array",
2028
+ "items": { "$ref": "#/definitions/AbsolutePathBuf" }
2029
+ },
2038
2030
  "sandbox": {
2039
2031
  "description": "Legacy sandbox policy retained for compatibility. Experimental clients should prefer `permissionProfile` when they need exact runtime permissions.",
2040
2032
  "allOf": [{ "$ref": "#/definitions/SandboxPolicy" }]
@@ -2059,29 +2051,9 @@ var ThreadStartResponse_default = {
2059
2051
  "id": {
2060
2052
  "description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
2061
2053
  "type": "string"
2062
- },
2063
- "modifications": {
2064
- "description": "Bounded user-requested modifications applied on top of the named profile, if any.",
2065
- "default": [],
2066
- "type": "array",
2067
- "items": { "$ref": "#/definitions/ActivePermissionProfileModification" }
2068
2054
  }
2069
2055
  }
2070
2056
  },
2071
- "ActivePermissionProfileModification": { "oneOf": [{
2072
- "description": "Additional concrete directory that should be writable.",
2073
- "type": "object",
2074
- "required": ["path", "type"],
2075
- "properties": {
2076
- "path": { "$ref": "#/definitions/AbsolutePathBuf" },
2077
- "type": {
2078
- "type": "string",
2079
- "enum": ["additionalWritableRoot"],
2080
- "title": "AdditionalWritableRootActivePermissionProfileModificationType"
2081
- }
2082
- },
2083
- "title": "AdditionalWritableRootActivePermissionProfileModification"
2084
- }] },
2085
2057
  "AgentPath": { "type": "string" },
2086
2058
  "ApprovalsReviewer": {
2087
2059
  "description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
package/dist/provider.js CHANGED
@@ -123,7 +123,7 @@ async function listModelsBestEffort(params) {
123
123
  }
124
124
  }
125
125
  async function listCodexAppServerModelsLazy(options) {
126
- const { listCodexAppServerModels } = await import("./models-BHS-8DS_.js").then((n) => n.r);
126
+ const { listCodexAppServerModels } = await import("./models-BZLMz9WS.js").then((n) => n.r);
127
127
  return listCodexAppServerModels(options);
128
128
  }
129
129
  function normalizeTimeoutMs(value) {
@@ -1,5 +1,5 @@
1
1
  import { c as resolveCodexAppServerRuntimeOptions, d as withMcpElicitationsApprovalPolicy, i as isCodexAppServerApprovalPolicyAllowedByRequirements, s as readCodexPluginConfig, u as resolveCodexPluginsPolicy } from "./config-3ATInASk.js";
2
- import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-CSY0BFBo.js";
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
4
  import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-CA7amCZ8.js";
5
5
  import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-Czoa60Nt.js";
@@ -7,7 +7,7 @@ import { d as resolveCodexUsageLimitResetAtMs, f as shouldRefreshCodexRateLimits
7
7
  import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey, l as resolveCodexAppServerAuthProfileId, n as clearSharedCodexAppServerClientIfCurrent, s as refreshCodexAppServerAuthTokens, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-CnbrvEfV.js";
8
8
  import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-DbdVqMzW.js";
9
9
  import { a as defaultCodexAppInventoryCache } from "./plugin-activation-BDU4e-NA.js";
10
- import { S as normalizeCodexDynamicToolName, _ 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, x as filterCodexDynamicTools, y as resolveCodexContextEngineProjectionReserveTokens } from "./thread-lifecycle-BMK4m_PL.js";
10
+ import { S as normalizeCodexDynamicToolName, _ 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, x as filterCodexDynamicTools, y as resolveCodexContextEngineProjectionReserveTokens } from "./thread-lifecycle-D2sxRPdz.js";
11
11
  import { t as defaultCodexAppServerClientFactory } from "./client-factory-DwzPofwS.js";
12
12
  import { n as handleCodexAppServerElicitationRequest, r as handleCodexAppServerApprovalRequest, t as filterToolsForVisionInputs } from "./vision-tools-Cm_YicZb.js";
13
13
  import { t as ensureCodexComputerUse } from "./computer-use-BX02ojP4.js";
@@ -23,6 +23,26 @@ import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugi
23
23
  import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
24
24
  import { createRunningTaskRun, finalizeTaskRunByRunId, recordTaskRunProgressByRunId } from "openclaw/plugin-sdk/codex-native-task-runtime";
25
25
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@earendil-works/pi-coding-agent";
26
+ //#region extensions/codex/src/app-server/local-runtime-attribution.ts
27
+ const OPENAI_PROVIDER_ID = "openai";
28
+ const OPENAI_RESPONSES_API = "openai-responses";
29
+ const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
30
+ const OPENAI_CODEX_RESPONSES_API = "openai-codex-responses";
31
+ function normalizeRuntimeId(value) {
32
+ return value?.trim().toLowerCase() ?? "";
33
+ }
34
+ function resolveCodexLocalRuntimeAttribution(params) {
35
+ const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
36
+ 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 {
37
+ provider: OPENAI_CODEX_PROVIDER_ID,
38
+ api: OPENAI_CODEX_RESPONSES_API
39
+ };
40
+ return {
41
+ provider: params.provider,
42
+ api: params.model.api
43
+ };
44
+ }
45
+ //#endregion
26
46
  //#region extensions/codex/src/app-server/native-subagent-task-mirror.ts
27
47
  const CODEX_NATIVE_SUBAGENT_RUNTIME = "subagent";
28
48
  const CODEX_NATIVE_SUBAGENT_TASK_KIND = "codex-native";
@@ -860,6 +880,7 @@ var CodexAppServerEventProjector = class {
860
880
  data: {
861
881
  phase: "end",
862
882
  backend: "codex-app-server",
883
+ completed: true,
863
884
  threadId: this.threadId,
864
885
  turnId: this.turnId,
865
886
  itemId
@@ -1017,8 +1038,14 @@ var CodexAppServerEventProjector = class {
1017
1038
  const text = extractRawAssistantText(item);
1018
1039
  if (!text) return;
1019
1040
  const itemId = readString$2(item, "id") ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
1041
+ const phase = readString$2(item, "phase");
1042
+ if (phase) this.assistantPhaseByItem.set(itemId, phase);
1020
1043
  this.rememberAssistantItem(itemId);
1021
1044
  this.assistantTextByItem.set(itemId, text);
1045
+ if (phase === "commentary") this.emitCommentaryProgress({
1046
+ itemId,
1047
+ text
1048
+ });
1022
1049
  }
1023
1050
  recordNativeGeneratedMedia(item) {
1024
1051
  if (item?.type !== "imageGeneration") return;
@@ -1327,6 +1354,7 @@ var CodexAppServerEventProjector = class {
1327
1354
  return await readCodexMirroredSessionHistoryMessages(this.params.sessionFile) ?? [];
1328
1355
  }
1329
1356
  createAssistantMessage(text) {
1357
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
1330
1358
  const usage = this.tokenUsage ? {
1331
1359
  input: this.tokenUsage.input ?? 0,
1332
1360
  output: this.tokenUsage.output ?? 0,
@@ -1341,8 +1369,8 @@ var CodexAppServerEventProjector = class {
1341
1369
  type: "text",
1342
1370
  text
1343
1371
  }],
1344
- api: this.params.model.api ?? "openai-codex-responses",
1345
- provider: this.params.provider,
1372
+ api: attribution.api ?? "openai-codex-responses",
1373
+ provider: attribution.provider,
1346
1374
  model: this.params.modelId,
1347
1375
  usage,
1348
1376
  stopReason: this.aborted ? "aborted" : this.promptError ? "error" : "stop",
@@ -1351,14 +1379,15 @@ var CodexAppServerEventProjector = class {
1351
1379
  };
1352
1380
  }
1353
1381
  createAssistantMirrorMessage(title, text) {
1382
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
1354
1383
  return {
1355
1384
  role: "assistant",
1356
1385
  content: [{
1357
1386
  type: "text",
1358
1387
  text: `${title}:\n${text}`
1359
1388
  }],
1360
- api: this.params.model.api ?? "openai-codex-responses",
1361
- provider: this.params.provider,
1389
+ api: attribution.api ?? "openai-codex-responses",
1390
+ provider: attribution.provider,
1362
1391
  model: this.params.modelId,
1363
1392
  usage: ZERO_USAGE,
1364
1393
  stopReason: "stop",
@@ -1367,6 +1396,7 @@ var CodexAppServerEventProjector = class {
1367
1396
  }
1368
1397
  createToolCallMessage(params) {
1369
1398
  const args = normalizeToolTranscriptArguments(params.arguments);
1399
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
1370
1400
  return {
1371
1401
  role: "assistant",
1372
1402
  content: [{
@@ -1376,8 +1406,8 @@ var CodexAppServerEventProjector = class {
1376
1406
  arguments: args,
1377
1407
  input: args
1378
1408
  }],
1379
- api: this.params.model.api ?? "openai-codex-responses",
1380
- provider: this.params.provider,
1409
+ api: attribution.api ?? "openai-codex-responses",
1410
+ provider: attribution.provider,
1381
1411
  model: this.params.modelId,
1382
1412
  usage: ZERO_USAGE,
1383
1413
  stopReason: "toolUse",
@@ -1882,6 +1912,7 @@ function createCodexTrajectoryRecorder(params) {
1882
1912
  });
1883
1913
  let queue = Promise.resolve();
1884
1914
  let seq = 0;
1915
+ const attribution = resolveCodexLocalRuntimeAttribution(params.attempt);
1885
1916
  return {
1886
1917
  filePath,
1887
1918
  recordEvent: (type, data) => {
@@ -1898,9 +1929,9 @@ function createCodexTrajectoryRecorder(params) {
1898
1929
  sessionKey: params.attempt.sessionKey,
1899
1930
  runId: params.attempt.runId,
1900
1931
  workspaceDir: params.cwd,
1901
- provider: params.attempt.provider,
1932
+ provider: attribution.provider,
1902
1933
  modelId: params.attempt.modelId,
1903
- modelApi: params.attempt.model.api,
1934
+ modelApi: attribution.api,
1904
1935
  data: data ? sanitizeValue(data) : void 0
1905
1936
  });
1906
1937
  if (!line) return;
@@ -2186,6 +2217,7 @@ const CODEX_TURN_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS = 1e4;
2186
2217
  const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
2187
2218
  const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
2188
2219
  const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
2220
+ const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
2189
2221
  const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
2190
2222
  const LOG_FIELD_MAX_LENGTH = 160;
2191
2223
  const CODEX_NATIVE_PROJECT_DOC_BASENAMES = new Set(["agents.md"]);
@@ -2814,14 +2846,22 @@ async function runCodexAppServerAttempt(params, options = {}) {
2814
2846
  let turnAssistantCompletionIdleWatchArmed = false;
2815
2847
  let turnAssistantCompletionLastActivityAt = Date.now();
2816
2848
  let turnAssistantCompletionLastActivityDetails;
2849
+ const turnAttemptIdleTimeoutMs = Math.max(100, Math.floor(params.timeoutMs));
2850
+ let turnAttemptIdleTimer;
2851
+ let turnAttemptIdleWatchArmed = false;
2817
2852
  let turnTerminalIdleTimer;
2818
2853
  let turnTerminalIdleWatchArmed = false;
2819
2854
  let turnCompletionLastActivityAt = Date.now();
2820
2855
  let turnCompletionLastActivityReason = "startup";
2821
2856
  let turnCompletionLastActivityDetails;
2857
+ let turnAttemptLastProgressAt = Date.now();
2858
+ let turnAttemptLastProgressReason = "startup";
2859
+ let turnAttemptLastProgressDetails;
2860
+ let nativeHookRelayLastRenewedAt = 0;
2822
2861
  let activeAppServerTurnRequests = 0;
2823
2862
  const activeOpenClawDynamicToolCallIds = /* @__PURE__ */ new Set();
2824
2863
  const activeTurnItemIds = /* @__PURE__ */ new Set();
2864
+ let turnCrossedToolHandoff = false;
2825
2865
  const clearTurnCompletionIdleTimer = () => {
2826
2866
  if (turnCompletionIdleTimer) {
2827
2867
  clearTimeout(turnCompletionIdleTimer);
@@ -2840,6 +2880,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
2840
2880
  turnAssistantCompletionIdleTimer = void 0;
2841
2881
  }
2842
2882
  };
2883
+ const clearTurnAttemptIdleTimer = () => {
2884
+ if (turnAttemptIdleTimer) {
2885
+ clearTimeout(turnAttemptIdleTimer);
2886
+ turnAttemptIdleTimer = void 0;
2887
+ }
2888
+ };
2843
2889
  const fireTurnAssistantCompletionIdleRelease = () => {
2844
2890
  if (completed || runAbortController.signal.aborted || !turnAssistantCompletionIdleWatchArmed) return;
2845
2891
  if (activeAppServerTurnRequests > 0 || activeTurnItemIds.size > 0) {
@@ -2876,6 +2922,35 @@ async function runCodexAppServerAttempt(params, options = {}) {
2876
2922
  completed = true;
2877
2923
  resolveCompletion?.();
2878
2924
  };
2925
+ const fireTurnAttemptIdleTimeout = () => {
2926
+ if (completed || runAbortController.signal.aborted || !turnAttemptIdleWatchArmed) return;
2927
+ const idleMs = Math.max(0, Date.now() - turnAttemptLastProgressAt);
2928
+ if (idleMs < turnAttemptIdleTimeoutMs) {
2929
+ scheduleTurnAttemptIdleWatch();
2930
+ return;
2931
+ }
2932
+ timedOut = true;
2933
+ turnCompletionIdleTimedOut = true;
2934
+ turnCompletionIdleTimeoutMessage = "codex app-server turn idle timed out waiting for turn/completed";
2935
+ projector?.markTimedOut();
2936
+ trajectoryRecorder?.recordEvent("turn.progress_idle_timeout", {
2937
+ threadId: thread.threadId,
2938
+ turnId,
2939
+ idleMs,
2940
+ timeoutMs: turnAttemptIdleTimeoutMs,
2941
+ lastActivityReason: turnAttemptLastProgressReason,
2942
+ ...turnAttemptLastProgressDetails
2943
+ });
2944
+ embeddedAgentLog.warn("codex app-server turn idle timed out waiting for progress", {
2945
+ threadId: thread.threadId,
2946
+ turnId,
2947
+ idleMs,
2948
+ timeoutMs: turnAttemptIdleTimeoutMs,
2949
+ lastActivityReason: turnAttemptLastProgressReason,
2950
+ ...turnAttemptLastProgressDetails
2951
+ });
2952
+ runAbortController.abort("turn_progress_idle_timeout");
2953
+ };
2879
2954
  const fireTurnCompletionIdleTimeout = () => {
2880
2955
  if (completed || runAbortController.signal.aborted || !turnCompletionIdleWatchArmed || activeAppServerTurnRequests > 0) return;
2881
2956
  const idleMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
@@ -2950,6 +3025,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
2950
3025
  turnAssistantCompletionIdleTimer = setTimeout(fireTurnAssistantCompletionIdleRelease, delayMs);
2951
3026
  turnAssistantCompletionIdleTimer.unref?.();
2952
3027
  }
3028
+ function scheduleTurnAttemptIdleWatch() {
3029
+ clearTurnAttemptIdleTimer();
3030
+ if (completed || runAbortController.signal.aborted || !turnAttemptIdleWatchArmed) return;
3031
+ const elapsedMs = Math.max(0, Date.now() - turnAttemptLastProgressAt);
3032
+ const delayMs = Math.max(1, turnAttemptIdleTimeoutMs - elapsedMs);
3033
+ turnAttemptIdleTimer = setTimeout(fireTurnAttemptIdleTimeout, delayMs);
3034
+ turnAttemptIdleTimer.unref?.();
3035
+ }
2953
3036
  function scheduleTurnTerminalIdleWatch() {
2954
3037
  clearTurnTerminalIdleTimer();
2955
3038
  if (completed || runAbortController.signal.aborted || !turnTerminalIdleWatchArmed || activeAppServerTurnRequests > 0) return;
@@ -2958,10 +3041,41 @@ async function runCodexAppServerAttempt(params, options = {}) {
2958
3041
  turnTerminalIdleTimer = setTimeout(fireTurnTerminalIdleTimeout, delayMs);
2959
3042
  turnTerminalIdleTimer.unref?.();
2960
3043
  }
3044
+ function scheduleTurnProgressWatches() {
3045
+ scheduleTurnAttemptIdleWatch();
3046
+ scheduleTurnCompletionIdleWatch();
3047
+ scheduleTurnTerminalIdleWatch();
3048
+ }
3049
+ const renewNativeHookRelayForTurnProgress = () => {
3050
+ if (!nativeHookRelay || options.nativeHookRelay?.ttlMs !== void 0) return;
3051
+ const now = Date.now();
3052
+ const renewsRecently = now - nativeHookRelayLastRenewedAt < CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS;
3053
+ const expiresSoon = now >= nativeHookRelay.expiresAtMs - CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
3054
+ if (renewsRecently && !expiresSoon) return;
3055
+ nativeHookRelayLastRenewedAt = now;
3056
+ nativeHookRelay.renew(resolveCodexNativeHookRelayTtlMs({
3057
+ explicitTtlMs: void 0,
3058
+ attemptTimeoutMs: turnAttemptIdleTimeoutMs,
3059
+ startupTimeoutMs,
3060
+ turnStartTimeoutMs: params.timeoutMs
3061
+ }));
3062
+ };
2961
3063
  const touchTurnCompletionActivity = (reason, options) => {
2962
3064
  turnCompletionLastActivityAt = Date.now();
2963
3065
  turnCompletionLastActivityReason = reason;
2964
3066
  turnCompletionLastActivityDetails = options?.details;
3067
+ if (options?.attemptProgress) {
3068
+ turnAttemptLastProgressAt = turnCompletionLastActivityAt;
3069
+ turnAttemptLastProgressReason = reason;
3070
+ turnAttemptLastProgressDetails = options.details;
3071
+ renewNativeHookRelayForTurnProgress();
3072
+ params.onRunProgress?.({
3073
+ reason,
3074
+ provider: params.provider,
3075
+ model: params.modelId,
3076
+ backend: "codex-app-server"
3077
+ });
3078
+ }
2965
3079
  emitTrustedDiagnosticEvent({
2966
3080
  type: "run.progress",
2967
3081
  runId: params.runId,
@@ -2973,8 +3087,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2973
3087
  turnCompletionIdleWatchArmed = true;
2974
3088
  turnCompletionIdleWatchPinnedByTerminalError = false;
2975
3089
  }
2976
- scheduleTurnCompletionIdleWatch();
2977
- scheduleTurnTerminalIdleWatch();
3090
+ scheduleTurnProgressWatches();
2978
3091
  };
2979
3092
  const disarmTurnCompletionIdleWatch = () => {
2980
3093
  turnCompletionIdleWatchArmed = false;
@@ -3058,20 +3171,25 @@ async function runCodexAppServerAttempt(params, options = {}) {
3058
3171
  const isCurrentTurnNotification = isTurnNotification(notification.params, thread.threadId, turnId);
3059
3172
  const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
3060
3173
  if (isCurrentTurnNotification) {
3061
- touchTurnCompletionActivity(`notification:${notification.method}`, { details: describeNotificationActivity(notification) });
3174
+ touchTurnCompletionActivity(`notification:${notification.method}`, {
3175
+ details: describeNotificationActivity(notification),
3176
+ attemptProgress: true
3177
+ });
3062
3178
  reportCodexExecutionNotification(notification);
3063
3179
  }
3064
3180
  if (isCurrentTurnNotification) updateActiveTurnItemIds(notification, activeTurnItemIds);
3065
3181
  const unblockedAssistantCompletionRelease = isCurrentTurnNotification && turnAssistantCompletionIdleWatchArmed && notification.method === "item/completed" && activeTurnItemIds.size === 0;
3066
3182
  const trackedDynamicToolCompletion = isTrackedOpenClawDynamicToolCompletionNotification(notification, activeOpenClawDynamicToolCallIds);
3067
3183
  const rawToolOutputCompletion = isRawToolOutputCompletionNotification(notification);
3068
- const shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem = isCurrentTurnNotification && notification.method === "item/completed" && activeTurnItemIds.size === 0 && !trackedDynamicToolCompletion && !isCompletedAssistantNotification(notification);
3184
+ if (isCurrentTurnNotification && (rawToolOutputCompletion || isNativeToolProgressNotification(notification))) turnCrossedToolHandoff = true;
3185
+ const assistantCompletionCanRelease = isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff);
3186
+ const shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem = isCurrentTurnNotification && notification.method === "item/completed" && activeTurnItemIds.size === 0 && !trackedDynamicToolCompletion && !assistantCompletionCanRelease;
3069
3187
  if (isCurrentTurnNotification && notification.method === "error") {
3070
3188
  if (isRetryableErrorNotification(notification.params)) disarmTurnCompletionIdleWatch();
3071
3189
  else armTurnCompletionIdleWatch({ pinnedByTerminalError: true });
3072
3190
  disarmTurnAssistantCompletionIdleWatch();
3073
3191
  } else if (isTurnCompletion) disarmTurnAssistantCompletionIdleWatch();
3074
- else if (isCurrentTurnNotification && isCompletedAssistantNotification(notification)) armTurnAssistantCompletionIdleWatch(describeNotificationActivity(notification));
3192
+ else if (isCurrentTurnNotification && assistantCompletionCanRelease) armTurnAssistantCompletionIdleWatch(describeNotificationActivity(notification));
3075
3193
  else if (unblockedAssistantCompletionRelease) armTurnAssistantCompletionIdleWatch(describeNotificationActivity(notification));
3076
3194
  else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) armTurnCompletionIdleWatch();
3077
3195
  else if (isCurrentTurnNotification && rawToolOutputCompletion) armTurnCompletionIdleWatch();
@@ -3110,11 +3228,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
3110
3228
  };
3111
3229
  const notificationCleanup = client.addNotificationHandler(enqueueNotification);
3112
3230
  const requestCleanup = client.addRequestHandler(async (request) => {
3113
- activeAppServerTurnRequests += 1;
3114
- clearTurnCompletionIdleTimer();
3115
- disarmTurnAssistantCompletionIdleWatch();
3116
- touchTurnCompletionActivity(`request:${request.method}`);
3117
3231
  let armCompletionWatchOnResponse = false;
3232
+ let requestCountsAsTurnActivity = false;
3233
+ const markCurrentTurnRequestProgress = () => {
3234
+ activeAppServerTurnRequests += 1;
3235
+ clearTurnCompletionIdleTimer();
3236
+ disarmTurnAssistantCompletionIdleWatch();
3237
+ requestCountsAsTurnActivity = true;
3238
+ touchTurnCompletionActivity(`request:${request.method}:start`, { attemptProgress: true });
3239
+ };
3118
3240
  try {
3119
3241
  if (request.method === "account/chatgptAuthTokens/refresh") return refreshCodexAppServerAuthTokens({
3120
3242
  agentDir,
@@ -3123,7 +3245,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
3123
3245
  });
3124
3246
  if (!turnId) return;
3125
3247
  if (request.method === "mcpServer/elicitation/request") {
3126
- armCompletionWatchOnResponse = true;
3248
+ if (isCurrentThreadOptionalTurnRequestParams(request.params, thread.threadId, turnId)) {
3249
+ armCompletionWatchOnResponse = true;
3250
+ markCurrentTurnRequestProgress();
3251
+ }
3127
3252
  return handleCodexAppServerElicitationRequest({
3128
3253
  requestParams: request.params,
3129
3254
  paramsForRun: params,
@@ -3134,7 +3259,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
3134
3259
  });
3135
3260
  }
3136
3261
  if (request.method === "item/tool/requestUserInput") {
3137
- armCompletionWatchOnResponse = true;
3262
+ if (isCurrentThreadTurnRequestParams(request.params, thread.threadId, turnId)) {
3263
+ armCompletionWatchOnResponse = true;
3264
+ markCurrentTurnRequestProgress();
3265
+ }
3138
3266
  return userInputBridge?.handleRequest({
3139
3267
  id: request.id,
3140
3268
  params: request.params
@@ -3142,7 +3270,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
3142
3270
  }
3143
3271
  if (request.method !== "item/tool/call") {
3144
3272
  if (isCodexAppServerApprovalRequest(request.method)) {
3145
- armCompletionWatchOnResponse = true;
3273
+ if (isCurrentApprovalTurnRequestParams(request.params, thread.threadId, turnId)) {
3274
+ armCompletionWatchOnResponse = true;
3275
+ markCurrentTurnRequestProgress();
3276
+ }
3146
3277
  return handleApprovalRequest({
3147
3278
  method: request.method,
3148
3279
  params: request.params,
@@ -3157,6 +3288,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3157
3288
  const call = readDynamicToolCallParams(request.params);
3158
3289
  if (!call || call.threadId !== thread.threadId || call.turnId !== turnId) return;
3159
3290
  armCompletionWatchOnResponse = true;
3291
+ markCurrentTurnRequestProgress();
3292
+ turnCrossedToolHandoff = true;
3160
3293
  activeOpenClawDynamicToolCallIds.add(call.callId);
3161
3294
  trajectoryRecorder?.recordEvent("tool.call", {
3162
3295
  threadId: call.threadId,
@@ -3233,8 +3366,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
3233
3366
  });
3234
3367
  return response;
3235
3368
  } finally {
3236
- activeAppServerTurnRequests = Math.max(0, activeAppServerTurnRequests - 1);
3237
- touchTurnCompletionActivity(`request:${request.method}:response`, { arm: armCompletionWatchOnResponse });
3369
+ if (requestCountsAsTurnActivity) {
3370
+ activeAppServerTurnRequests = Math.max(0, activeAppServerTurnRequests - 1);
3371
+ touchTurnCompletionActivity(`request:${request.method}:response`, {
3372
+ arm: armCompletionWatchOnResponse,
3373
+ attemptProgress: true
3374
+ });
3375
+ } else scheduleTurnProgressWatches();
3238
3376
  }
3239
3377
  });
3240
3378
  const forceContextEngineCompactionForCodexOverflow = async (error) => {
@@ -3495,11 +3633,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
3495
3633
  abort: () => runAbortController.abort("aborted")
3496
3634
  };
3497
3635
  setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
3498
- const timeout = setTimeout(() => {
3499
- timedOut = true;
3500
- projector?.markTimedOut();
3501
- runAbortController.abort("timeout");
3502
- }, Math.max(100, params.timeoutMs));
3636
+ turnAttemptIdleWatchArmed = true;
3637
+ turnTerminalIdleWatchArmed = true;
3638
+ touchTurnCompletionActivity("turn:start", { attemptProgress: true });
3503
3639
  const abortListener = () => {
3504
3640
  const shouldRetireClient = timedOut;
3505
3641
  interruptCodexTurnBestEffort(client, {
@@ -3655,7 +3791,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
3655
3791
  });
3656
3792
  if (!timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
3657
3793
  userInputBridge?.cancelPending();
3658
- clearTimeout(timeout);
3794
+ clearTurnAttemptIdleTimer();
3659
3795
  clearTurnCompletionIdleTimer();
3660
3796
  clearTurnAssistantCompletionIdleTimer();
3661
3797
  clearTurnTerminalIdleTimer();
@@ -4217,6 +4353,10 @@ function isCompletedAssistantNotification(notification) {
4217
4353
  const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
4218
4354
  return Boolean(item && readString(item, "type") === "agentMessage" && readString(item, "phase") !== "commentary");
4219
4355
  }
4356
+ function isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff) {
4357
+ if (isCompletedAssistantNotification(notification)) return true;
4358
+ return !turnCrossedToolHandoff && isRawAssistantCompletionNotification(notification);
4359
+ }
4220
4360
  function shouldDisarmAssistantCompletionIdleWatch(notification) {
4221
4361
  if (!isJsonObject(notification.params)) return false;
4222
4362
  if (notification.method === "item/started") return true;
@@ -4241,6 +4381,23 @@ function isRawToolOutputCompletionNotification(notification) {
4241
4381
  const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
4242
4382
  return item ? readString(item, "type") === "custom_tool_call_output" : false;
4243
4383
  }
4384
+ function isNativeToolProgressNotification(notification) {
4385
+ if (notification.method !== "item/started" && notification.method !== "item/completed" && notification.method !== "item/updated") return false;
4386
+ if (!isJsonObject(notification.params)) return false;
4387
+ const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
4388
+ switch (item ? readString(item, "type") : void 0) {
4389
+ case "commandExecution":
4390
+ case "fileChange":
4391
+ case "mcpToolCall":
4392
+ case "webSearch": return true;
4393
+ default: return false;
4394
+ }
4395
+ }
4396
+ function isRawAssistantCompletionNotification(notification) {
4397
+ if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
4398
+ const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
4399
+ return Boolean(item && readString(item, "type") === "message" && readString(item, "role") === "assistant" && readString(item, "phase") !== "commentary" && readRawAssistantTextPreview(item));
4400
+ }
4244
4401
  function readRawAssistantTextPreview(item) {
4245
4402
  if (readString(item, "role") !== "assistant" || !Array.isArray(item.content)) return;
4246
4403
  const text = item.content.flatMap((content) => {
@@ -4255,6 +4412,19 @@ function isTurnNotification(value, threadId, turnId) {
4255
4412
  if (!isJsonObject(value)) return false;
4256
4413
  return readString(value, "threadId") === threadId && readNotificationTurnId(value) === turnId;
4257
4414
  }
4415
+ function isCurrentThreadTurnRequestParams(value, threadId, turnId) {
4416
+ if (!isJsonObject(value)) return false;
4417
+ return readString(value, "threadId") === threadId && readString(value, "turnId") === turnId;
4418
+ }
4419
+ function isCurrentApprovalTurnRequestParams(value, threadId, turnId) {
4420
+ if (!isJsonObject(value)) return false;
4421
+ return (readString(value, "threadId") ?? readString(value, "conversationId")) === threadId && readString(value, "turnId") === turnId;
4422
+ }
4423
+ function isCurrentThreadOptionalTurnRequestParams(value, threadId, turnId) {
4424
+ if (!isJsonObject(value) || readString(value, "threadId") !== threadId) return false;
4425
+ const requestTurnId = value.turnId;
4426
+ return requestTurnId === null || requestTurnId === void 0 || requestTurnId === turnId;
4427
+ }
4258
4428
  function isRetryableErrorNotification(value) {
4259
4429
  if (!isJsonObject(value)) return false;
4260
4430
  return readBoolean(value, "willRetry") === true || readBoolean(value, "will_retry") === true;
@@ -1,11 +1,11 @@
1
1
  import { c as resolveCodexAppServerRuntimeOptions, s as readCodexPluginConfig } from "./config-3ATInASk.js";
2
- import { a as readCodexDynamicToolCallParams, i as assertCodexTurnStartResponse, l as readCodexTurnCompletedNotification, t as assertCodexThreadForkResponse } from "./protocol-validators-CSY0BFBo.js";
2
+ import { a as readCodexDynamicToolCallParams, i as assertCodexTurnStartResponse, l as readCodexTurnCompletedNotification, t as assertCodexThreadForkResponse } from "./protocol-validators-BGBspNmF.js";
3
3
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
4
4
  import { r as isCodexAppServerApprovalRequest } from "./client-CA7amCZ8.js";
5
5
  import { u as formatCodexUsageLimitErrorMessage } from "./command-formatters-BRW7_Nu7.js";
6
6
  import { i as getSharedCodexAppServerClient, s as refreshCodexAppServerAuthTokens } from "./shared-client-CnbrvEfV.js";
7
7
  import { i as readCodexAppServerBinding } from "./session-binding-DbdVqMzW.js";
8
- import { b as createCodexDynamicToolBridge, d as resolveReasoningEffort, n as buildCodexRuntimeThreadConfig, u as resolveCodexAppServerModelProvider, x as filterCodexDynamicTools } from "./thread-lifecycle-BMK4m_PL.js";
8
+ import { b as createCodexDynamicToolBridge, d as resolveReasoningEffort, n as buildCodexRuntimeThreadConfig, u as resolveCodexAppServerModelProvider, x as filterCodexDynamicTools } from "./thread-lifecycle-D2sxRPdz.js";
9
9
  import { n as handleCodexAppServerElicitationRequest, r as handleCodexAppServerApprovalRequest, t as filterToolsForVisionInputs } from "./vision-tools-Cm_YicZb.js";
10
10
  import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-dvhq-4pi.js";
11
11
  import { embeddedAgentLog, formatErrorMessage, resolveAgentDir, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
package/dist/test-api.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { c as resolveCodexAppServerRuntimeOptions } from "./config-3ATInASk.js";
2
- import { a as buildThreadResumeParams, b as createCodexDynamicToolBridge, i as buildDeveloperInstructions, o as buildThreadStartParams, s as buildTurnStartParams, x as filterCodexDynamicTools } from "./thread-lifecycle-BMK4m_PL.js";
2
+ import { a as buildThreadResumeParams, b as createCodexDynamicToolBridge, i as buildDeveloperInstructions, o as buildThreadStartParams, s as buildTurnStartParams, x as filterCodexDynamicTools } from "./thread-lifecycle-D2sxRPdz.js";
3
3
  //#region extensions/codex/test-api.ts
4
4
  function resolveCodexPromptSnapshotAppServerOptions(pluginConfig) {
5
5
  return resolveCodexAppServerRuntimeOptions({
@@ -1,5 +1,5 @@
1
1
  import { r as codexSandboxPolicyForTurn, u as resolveCodexPluginsPolicy } from "./config-3ATInASk.js";
2
- import { n as assertCodexThreadResumeResponse, r as assertCodexThreadStartResponse } from "./protocol-validators-CSY0BFBo.js";
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";
@@ -19,10 +19,10 @@ const CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES = [
19
19
  "exec",
20
20
  "process",
21
21
  "update_plan",
22
- "tool_search_code",
23
- "tool_search",
22
+ "tool_call",
24
23
  "tool_describe",
25
- "tool_call"
24
+ "tool_search",
25
+ "tool_search_code"
26
26
  ];
27
27
  const DYNAMIC_TOOL_NAME_ALIASES = {
28
28
  bash: "exec",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/codex",
3
- "version": "2026.5.16-beta.2",
3
+ "version": "2026.5.16-beta.3",
4
4
  "description": "OpenClaw Codex harness and model provider plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,10 +27,10 @@
27
27
  "minHostVersion": ">=2026.5.1-beta.1"
28
28
  },
29
29
  "compat": {
30
- "pluginApi": ">=2026.5.16-beta.2"
30
+ "pluginApi": ">=2026.5.16-beta.3"
31
31
  },
32
32
  "build": {
33
- "openclawVersion": "2026.5.16-beta.2"
33
+ "openclawVersion": "2026.5.16-beta.3"
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.2"
48
+ "openclaw": ">=2026.5.16-beta.3"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "openclaw": {