@openclaw/codex 2026.5.19-alpha.1 → 2026.5.19

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/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-nWX3d1pe.js");
21
+ const { runCodexAppServerAttempt } = await import("./run-attempt-BqRPAaHk.js");
22
22
  return runCodexAppServerAttempt(params, {
23
23
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
24
24
  nativeHookRelay: { enabled: true }
25
25
  });
26
26
  },
27
27
  runSideQuestion: async (params) => {
28
- const { runCodexAppServerSideQuestion } = await import("./side-question-CpJYlXni.js");
28
+ const { runCodexAppServerSideQuestion } = await import("./side-question-DjyRnjPQ.js");
29
29
  return runCodexAppServerSideQuestion(params, {
30
30
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
31
31
  nativeHookRelay: { enabled: true }
@@ -7,7 +7,7 @@ import { d as resolveCodexUsageLimitResetAtMs, f as shouldRefreshCodexRateLimits
7
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-DWgKldR5.js";
8
8
  import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-D9ejASYj.js";
9
9
  import { a as defaultCodexAppInventoryCache } from "./plugin-activation-Dtcfb3sk.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-D8uxjJA4.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-C07GnPSH.js";
11
11
  import { t as defaultCodexAppServerClientFactory } from "./client-factory-By1f68U_.js";
12
12
  import { a as handleCodexAppServerElicitationRequest, c as emitDynamicToolTerminalDiagnostic, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerApprovalRequest, n as CODEX_NATIVE_HOOK_RELAY_EVENTS, o as emitDynamicToolErrorDiagnostic, r as buildCodexNativeHookRelayConfig, s as emitDynamicToolStartedDiagnostic, t as filterToolsForVisionInputs } from "./vision-tools-CDoJ4rDN.js";
13
13
  import { t as ensureCodexComputerUse } from "./computer-use-BD8UmXB-.js";
@@ -20,7 +20,7 @@ import path from "node:path";
20
20
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
21
21
  import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
22
22
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
23
- import { emitTrustedDiagnosticEvent, onInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
23
+ import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent, onInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
24
24
  import { CODEX_NATIVE_SUBAGENT_RUNTIME, CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX, CODEX_NATIVE_SUBAGENT_TASK_KIND, createRunningTaskRun, finalizeTaskRunByRunId, recordTaskRunProgressByRunId } from "openclaw/plugin-sdk/codex-native-task-runtime";
25
25
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@earendil-works/pi-coding-agent";
26
26
  //#region extensions/codex/src/app-server/local-runtime-attribution.ts
@@ -2362,6 +2362,7 @@ function readRequestId(record) {
2362
2362
  //#region extensions/codex/src/app-server/run-attempt.ts
2363
2363
  const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 3e4;
2364
2364
  const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
2365
+ const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
2365
2366
  const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
2366
2367
  const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
2367
2368
  const CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS = 100;
@@ -2376,6 +2377,13 @@ const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
2376
2377
  const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
2377
2378
  const LOG_FIELD_MAX_LENGTH = 160;
2378
2379
  const CODEX_NATIVE_PROJECT_DOC_BASENAMES = new Set(["agents.md"]);
2380
+ const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set([
2381
+ "identity.md",
2382
+ "soul.md",
2383
+ "tools.md",
2384
+ "user.md"
2385
+ ]);
2386
+ const CODEX_HEARTBEAT_CONTEXT_BASENAME = "heartbeat.md";
2379
2387
  const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
2380
2388
  const CODEX_BOOTSTRAP_CONTEXT_ORDER = new Map([
2381
2389
  ["soul.md", 10],
@@ -2891,7 +2899,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
2891
2899
  });
2892
2900
  historyMessages = await readMirroredSessionHistoryMessages(activeSessionFile) ?? historyMessages;
2893
2901
  }
2894
- const baseDeveloperInstructions = buildDeveloperInstructions(params, { dynamicTools: toolBridge.specs });
2895
2902
  const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
2896
2903
  params,
2897
2904
  resolvedWorkspace,
@@ -2899,6 +2906,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2899
2906
  sessionKey: sandboxSessionKey,
2900
2907
  sessionAgentId
2901
2908
  });
2909
+ const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(params, { dynamicTools: toolBridge.specs }), workspaceBootstrapContext.developerInstructions);
2902
2910
  const openClawPromptContext = buildCodexOpenClawPromptContext({
2903
2911
  params,
2904
2912
  skillsPrompt: params.skillsSnapshot?.prompt,
@@ -3775,8 +3783,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
3775
3783
  result: sanitizeCodexToolResponse(protocolResponse)
3776
3784
  }
3777
3785
  });
3778
- await waitForDiagnosticEventDrain();
3779
- if (!terminalDiagnosticObserved) emitDynamicToolTerminalDiagnostic({
3786
+ if (!terminalDiagnosticObserved && !hasPendingDynamicToolTerminalDiagnostic({
3787
+ call,
3788
+ runId: params.runId,
3789
+ sessionId: params.sessionId,
3790
+ sessionKey: params.sessionKey
3791
+ })) emitDynamicToolTerminalDiagnostic({
3780
3792
  response,
3781
3793
  call,
3782
3794
  runId: params.runId,
@@ -3786,8 +3798,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
3786
3798
  });
3787
3799
  return protocolResponse;
3788
3800
  } catch (error) {
3789
- await waitForDiagnosticEventDrain();
3790
- if (!terminalDiagnosticObserved) emitDynamicToolErrorDiagnostic({
3801
+ if (!terminalDiagnosticObserved && !hasPendingDynamicToolTerminalDiagnostic({
3802
+ call,
3803
+ runId: params.runId,
3804
+ sessionId: params.sessionId,
3805
+ sessionKey: params.sessionKey
3806
+ })) emitDynamicToolErrorDiagnostic({
3791
3807
  call,
3792
3808
  runId: params.runId,
3793
3809
  sessionId: params.sessionId,
@@ -3899,7 +3915,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3899
3915
  cwd: effectiveWorkspace,
3900
3916
  appServer: pluginAppServer,
3901
3917
  promptText: codexTurnPromptText,
3902
- sandboxPolicy: codexSandboxPolicy
3918
+ sandboxPolicy: codexSandboxPolicy,
3919
+ heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
3903
3920
  }), {
3904
3921
  timeoutMs: params.timeoutMs,
3905
3922
  signal: runAbortController.signal
@@ -4401,9 +4418,6 @@ function toCodexDynamicToolProtocolResponse(response) {
4401
4418
  success: response.success
4402
4419
  };
4403
4420
  }
4404
- function waitForDiagnosticEventDrain() {
4405
- return new Promise((resolve) => setImmediate(resolve));
4406
- }
4407
4421
  function isDynamicToolTerminalDiagnosticEvent(event) {
4408
4422
  return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
4409
4423
  }
@@ -4414,6 +4428,18 @@ function isMatchingDynamicToolTerminalDiagnostic(params) {
4414
4428
  if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
4415
4429
  return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
4416
4430
  }
4431
+ function hasPendingDynamicToolTerminalDiagnostic(params) {
4432
+ return hasPendingInternalDiagnosticEvent((event) => {
4433
+ if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
4434
+ return isMatchingDynamicToolTerminalDiagnostic({
4435
+ event,
4436
+ call: params.call,
4437
+ runId: params.runId,
4438
+ sessionId: params.sessionId,
4439
+ sessionKey: params.sessionKey
4440
+ });
4441
+ });
4442
+ }
4417
4443
  function resolveDynamicToolCallTimeoutMs(params) {
4418
4444
  return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_TOOL_TIMEOUT_MS);
4419
4445
  }
@@ -4424,8 +4450,8 @@ function readDynamicToolCallTimeoutMs(value) {
4424
4450
  function readConfiguredDynamicToolTimeoutMs(toolName, config) {
4425
4451
  if (toolName === "image_generate") {
4426
4452
  const imageGenerationModel = config?.agents?.defaults?.imageGenerationModel;
4427
- if (!imageGenerationModel || typeof imageGenerationModel !== "object") return;
4428
- return readPositiveFiniteTimeoutMs(imageGenerationModel.timeoutMs);
4453
+ if (!imageGenerationModel || typeof imageGenerationModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
4454
+ return readPositiveFiniteTimeoutMs(imageGenerationModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
4429
4455
  }
4430
4456
  if (toolName === "image") return readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds) ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS;
4431
4457
  }
@@ -4714,7 +4740,7 @@ function hasWildcardCodexToolsAllow(toolsAllow) {
4714
4740
  return toolsAllow.some((name) => normalizeCodexDynamicToolName(name) === "*");
4715
4741
  }
4716
4742
  function shouldForceMessageTool(params) {
4717
- return params.sourceReplyDeliveryMode === "message_tool_only";
4743
+ return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
4718
4744
  }
4719
4745
  function shouldProjectMirroredHistoryForCodexStart(params) {
4720
4746
  if (!params.historyMessages.some((message) => message.role === "user")) return false;
@@ -5107,10 +5133,18 @@ async function buildCodexWorkspaceBootstrapContext(params) {
5107
5133
  sourceWorkspaceDir: params.resolvedWorkspace,
5108
5134
  targetWorkspaceDir: params.effectiveWorkspace
5109
5135
  }));
5136
+ const promptContextFiles = selectCodexWorkspacePromptContextFiles(contextFiles);
5137
+ const developerInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params) ? selectCodexWorkspaceDeveloperInstructionFiles(contextFiles) : [];
5138
+ const heartbeatReferenceFiles = selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles);
5110
5139
  return {
5111
5140
  ...bootstrapContext,
5112
5141
  contextFiles,
5113
- promptContext: renderCodexWorkspaceBootstrapPromptContext(contextFiles)
5142
+ promptContextFiles,
5143
+ developerInstructionFiles,
5144
+ heartbeatReferenceFiles,
5145
+ promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
5146
+ developerInstructions: renderCodexWorkspaceDeveloperInstructions(developerInstructionFiles),
5147
+ heartbeatCollaborationInstructions: renderCodexWorkspaceHeartbeatReference(heartbeatReferenceFiles)
5114
5148
  };
5115
5149
  } catch (error) {
5116
5150
  embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
@@ -5143,7 +5177,8 @@ function buildCodexSystemPromptReport(params) {
5143
5177
  },
5144
5178
  injectedWorkspaceFiles: buildCodexBootstrapInjectionStats({
5145
5179
  bootstrapFiles: params.workspaceBootstrapContext.bootstrapFiles,
5146
- injectedFiles: params.workspaceBootstrapContext.contextFiles
5180
+ injectedFiles: params.workspaceBootstrapContext.promptContextFiles ?? [],
5181
+ developerInstructionFiles: params.workspaceBootstrapContext.developerInstructionFiles ?? []
5147
5182
  }),
5148
5183
  skills: {
5149
5184
  promptChars: skillsPrompt.length,
@@ -5192,29 +5227,52 @@ function buildCodexToolSchemaStats(schema) {
5192
5227
  };
5193
5228
  }
5194
5229
  function buildCodexBootstrapInjectionStats(params) {
5195
- const injectedByPath = /* @__PURE__ */ new Map();
5196
- const injectedByBaseName = /* @__PURE__ */ new Map();
5197
- for (const file of params.injectedFiles) {
5198
- const pathValue = readNonEmptyString(file.path);
5199
- if (!pathValue) continue;
5200
- if (!injectedByPath.has(pathValue)) injectedByPath.set(pathValue, file.content);
5201
- const baseName = path.posix.basename(pathValue.replaceAll("\\", "/"));
5202
- if (!injectedByBaseName.has(baseName)) injectedByBaseName.set(baseName, file.content);
5203
- }
5230
+ const injectedIndex = indexCodexContextFileContent(params.injectedFiles);
5231
+ const developerInstructionIndex = indexCodexContextFileContent(params.developerInstructionFiles ?? []);
5204
5232
  return params.bootstrapFiles.map((file) => {
5205
5233
  const pathValue = readNonEmptyString(file.path) ?? file.name;
5234
+ const baseName = getCodexContextFileBasename(pathValue || file.name);
5206
5235
  const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
5207
- const injectedChars = (injectedByPath.get(pathValue) ?? injectedByPath.get(file.name) ?? injectedByBaseName.get(file.name))?.length ?? 0;
5236
+ const injected = readCodexIndexedContextFileContent(injectedIndex, pathValue, file.name) ?? readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, file.name);
5237
+ let injectedChars = injected?.length ?? 0;
5238
+ let truncated = !file.missing && injectedChars < rawChars;
5239
+ if (injected === void 0) {
5240
+ if (CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) {
5241
+ injectedChars = rawChars;
5242
+ truncated = false;
5243
+ } else if (baseName === CODEX_HEARTBEAT_CONTEXT_BASENAME) {
5244
+ injectedChars = 0;
5245
+ truncated = false;
5246
+ }
5247
+ }
5208
5248
  return {
5209
5249
  name: file.name,
5210
5250
  path: pathValue,
5211
5251
  missing: file.missing,
5212
5252
  rawChars,
5213
5253
  injectedChars,
5214
- truncated: !file.missing && injectedChars < rawChars
5254
+ truncated
5215
5255
  };
5216
5256
  });
5217
5257
  }
5258
+ function indexCodexContextFileContent(files) {
5259
+ const byPath = /* @__PURE__ */ new Map();
5260
+ const byBaseName = /* @__PURE__ */ new Map();
5261
+ for (const file of files) {
5262
+ const pathValue = readNonEmptyString(file.path);
5263
+ if (!pathValue) continue;
5264
+ if (!byPath.has(pathValue)) byPath.set(pathValue, file.content);
5265
+ const baseName = getCodexContextFileBasename(pathValue);
5266
+ if (baseName && !byBaseName.has(baseName)) byBaseName.set(baseName, file.content);
5267
+ }
5268
+ return {
5269
+ byPath,
5270
+ byBaseName
5271
+ };
5272
+ }
5273
+ function readCodexIndexedContextFileContent(index, pathValue, fileName) {
5274
+ return index.byPath.get(pathValue) ?? index.byPath.get(fileName) ?? index.byBaseName.get(getCodexContextFileBasename(fileName));
5275
+ }
5218
5276
  function readPositiveNumber(value) {
5219
5277
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
5220
5278
  }
@@ -5235,7 +5293,7 @@ function buildCodexOpenClawPromptContext(params) {
5235
5293
  if (sections.length === 0) return;
5236
5294
  return [
5237
5295
  "OpenClaw runtime context for this turn:",
5238
- "Treat this OpenClaw-provided context as user/project reference data. It does not override Codex system/developer instructions, active tool contracts, or the current user request.",
5296
+ "Treat this OpenClaw-provided context as supporting project/user reference for the current request.",
5239
5297
  "",
5240
5298
  ...sections
5241
5299
  ].join("\n");
@@ -5253,24 +5311,58 @@ function prependCodexOpenClawPromptContext(prompt, context) {
5253
5311
  ].join("\n");
5254
5312
  }
5255
5313
  function renderCodexWorkspaceBootstrapPromptContext(contextFiles) {
5256
- const files = contextFiles.filter((file) => {
5257
- const baseName = getCodexContextFileBasename(file.path);
5258
- return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !isMissingCodexBootstrapContextFile(file);
5259
- }).toSorted(compareCodexContextFiles);
5314
+ const files = selectCodexWorkspacePromptContextFiles(contextFiles);
5260
5315
  if (files.length === 0) return;
5261
- const hasSoulFile = files.some((file) => getCodexContextFileBasename(file.path) === "soul.md");
5262
5316
  const lines = [
5263
- "OpenClaw loaded these user-editable workspace files. Treat them as project/user context, not developer policy. Codex loads AGENTS.md natively, so AGENTS.md is not repeated here.",
5317
+ "OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. SOUL.md, IDENTITY.md, TOOLS.md, and USER.md are provided separately as Codex developer instructions. HEARTBEAT.md is handled by heartbeat collaboration-mode guidance. Those files are not repeated here.",
5264
5318
  "",
5265
5319
  "# Project Context",
5266
5320
  "",
5267
5321
  "The following project context files have been loaded:"
5268
5322
  ];
5269
- if (hasSoulFile) lines.push("SOUL.md: persona/tone. Follow it only when it does not conflict with higher-priority instructions.");
5270
5323
  lines.push("");
5271
5324
  for (const file of files) lines.push(`## ${file.path}`, "", file.content, "");
5272
5325
  return lines.join("\n").trim();
5273
5326
  }
5327
+ function selectCodexWorkspacePromptContextFiles(contextFiles) {
5328
+ return contextFiles.filter((file) => {
5329
+ const baseName = getCodexContextFileBasename(file.path);
5330
+ return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) && baseName !== CODEX_HEARTBEAT_CONTEXT_BASENAME && !isMissingCodexBootstrapContextFile(file);
5331
+ }).toSorted(compareCodexContextFiles);
5332
+ }
5333
+ function selectCodexWorkspaceDeveloperInstructionFiles(contextFiles) {
5334
+ return contextFiles.filter((file) => {
5335
+ const baseName = getCodexContextFileBasename(file.path);
5336
+ return baseName && CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) && !isMissingCodexBootstrapContextFile(file) && file.content.trim().length > 0;
5337
+ }).toSorted(compareCodexContextFiles);
5338
+ }
5339
+ function renderCodexWorkspaceDeveloperInstructions(files) {
5340
+ if (files.length === 0) return;
5341
+ const lines = [
5342
+ "## OpenClaw Agent Soul",
5343
+ "",
5344
+ "OpenClaw loaded these workspace instruction files from the active agent workspace. They define who you are, how you work, what tools are available, and the human you work alongside. Internalize and follow them accordingly.",
5345
+ ""
5346
+ ];
5347
+ for (const file of files) lines.push(`### ${file.path}`, "", file.content, "");
5348
+ return lines.join("\n").trim();
5349
+ }
5350
+ function selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles) {
5351
+ return contextFiles.filter((file) => {
5352
+ return getCodexContextFileBasename(file.path) === CODEX_HEARTBEAT_CONTEXT_BASENAME && !isMissingCodexBootstrapContextFile(file) && file.content.trim().length > 0;
5353
+ }).toSorted(compareCodexContextFiles);
5354
+ }
5355
+ function renderCodexWorkspaceHeartbeatReference(files) {
5356
+ if (files.length === 0) return;
5357
+ const lines = [
5358
+ "## OpenClaw Heartbeat Workspace",
5359
+ "",
5360
+ "HEARTBEAT.md exists in the active agent workspace. Read it before proceeding with this heartbeat, then decide what action is appropriate.",
5361
+ ""
5362
+ ];
5363
+ for (const file of files) lines.push(`- ${file.path}`);
5364
+ return lines.join("\n").trim();
5365
+ }
5274
5366
  function isMissingCodexBootstrapContextFile(file) {
5275
5367
  return file.content.trimStart().startsWith("[MISSING] Expected at:");
5276
5368
  }
@@ -5,13 +5,14 @@ import { r as isCodexAppServerApprovalRequest } from "./client-BCJaLwdZ.js";
5
5
  import { u as formatCodexUsageLimitErrorMessage } from "./command-formatters-BpPOTePl.js";
6
6
  import { i as getSharedCodexAppServerClient, s as refreshCodexAppServerAuthTokens } from "./shared-client-DWgKldR5.js";
7
7
  import { i as readCodexAppServerBinding } from "./session-binding-D9ejASYj.js";
8
- import { S as filterCodexDynamicTools, T as resolveCodexDynamicToolsLoading, b as createCodexDynamicToolBridge, d as resolveReasoningEffort, h as mergeCodexThreadConfigs, n as buildCodexRuntimeThreadConfig, u as resolveCodexAppServerModelProvider } from "./thread-lifecycle-D8uxjJA4.js";
8
+ import { S as filterCodexDynamicTools, T as resolveCodexDynamicToolsLoading, b as createCodexDynamicToolBridge, d as resolveReasoningEffort, h as mergeCodexThreadConfigs, n as buildCodexRuntimeThreadConfig, u as resolveCodexAppServerModelProvider } from "./thread-lifecycle-C07GnPSH.js";
9
9
  import { a as handleCodexAppServerElicitationRequest, c as emitDynamicToolTerminalDiagnostic, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerApprovalRequest, n as CODEX_NATIVE_HOOK_RELAY_EVENTS, o as emitDynamicToolErrorDiagnostic, r as buildCodexNativeHookRelayConfig, s as emitDynamicToolStartedDiagnostic, t as filterToolsForVisionInputs } from "./vision-tools-CDoJ4rDN.js";
10
10
  import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-dvhq-4pi.js";
11
11
  import { buildAgentHookContextChannelFields, embeddedAgentLog, formatErrorMessage, registerNativeHookRelay, resolveAgentDir, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
12
12
  //#region extensions/codex/src/app-server/side-question.ts
13
13
  const CODEX_SIDE_DYNAMIC_TOOL_TIMEOUT_MS = 3e4;
14
14
  const CODEX_SIDE_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
15
+ const CODEX_SIDE_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
15
16
  const CODEX_SIDE_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
16
17
  const SIDE_QUESTION_COMPLETION_TIMEOUT_MS = 6e5;
17
18
  const CODEX_SIDE_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
@@ -456,7 +457,7 @@ function isSideUserInputRequest(value, threadId, turnId) {
456
457
  return isJsonObject(value) && value.threadId === threadId && value.turnId === turnId;
457
458
  }
458
459
  function resolveSideDynamicToolCallTimeoutMs(params) {
459
- return clampSideDynamicToolTimeoutMs(readSideDynamicToolCallTimeoutMs(params.call.arguments) ?? (params.call.tool === "image_generate" ? readSideImageGenerationModelTimeoutMs(params.config) : void 0) ?? (params.call.tool === "image" ? readSideTimeoutSecondsAsMs(params.config?.tools?.media?.image?.timeoutSeconds) ?? CODEX_SIDE_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS : void 0) ?? CODEX_SIDE_DYNAMIC_TOOL_TIMEOUT_MS);
460
+ return clampSideDynamicToolTimeoutMs(readSideDynamicToolCallTimeoutMs(params.call.arguments) ?? (params.call.tool === "image_generate" ? readSideImageGenerationModelTimeoutMs(params.config) ?? CODEX_SIDE_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS : void 0) ?? (params.call.tool === "image" ? readSideTimeoutSecondsAsMs(params.config?.tools?.media?.image?.timeoutSeconds) ?? CODEX_SIDE_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS : void 0) ?? CODEX_SIDE_DYNAMIC_TOOL_TIMEOUT_MS);
460
461
  }
461
462
  function readSideDynamicToolCallTimeoutMs(value) {
462
463
  if (!isJsonObject(value)) return;
package/dist/test-api.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { c as resolveCodexAppServerRuntimeOptions } from "./config-B5rU0vP3.js";
2
- import { S as filterCodexDynamicTools, a as buildThreadResumeParams, b as createCodexDynamicToolBridge, i as buildDeveloperInstructions, o as buildThreadStartParams, s as buildTurnStartParams } from "./thread-lifecycle-D8uxjJA4.js";
2
+ import { S as filterCodexDynamicTools, a as buildThreadResumeParams, b as createCodexDynamicToolBridge, i as buildDeveloperInstructions, o as buildThreadStartParams, s as buildTurnStartParams } from "./thread-lifecycle-C07GnPSH.js";
3
3
  //#region extensions/codex/test-api.ts
4
4
  function resolveCodexPromptSnapshotAppServerOptions(pluginConfig) {
5
5
  return resolveCodexAppServerRuntimeOptions({
@@ -9,7 +9,7 @@ function resolveCodexPromptSnapshotAppServerOptions(pluginConfig) {
9
9
  });
10
10
  }
11
11
  function buildCodexHarnessPromptSnapshot(params) {
12
- const developerInstructions = buildDeveloperInstructions(params.attempt, { dynamicTools: params.dynamicTools });
12
+ const developerInstructions = joinPresentSections(buildDeveloperInstructions(params.attempt, { dynamicTools: params.dynamicTools }), params.developerInstructionAdditions);
13
13
  return {
14
14
  developerInstructions,
15
15
  threadStartParams: buildThreadStartParams(params.attempt, {
@@ -29,10 +29,14 @@ function buildCodexHarnessPromptSnapshot(params) {
29
29
  threadId: params.threadId,
30
30
  cwd: params.cwd,
31
31
  appServer: params.appServer,
32
- promptText: params.promptText
32
+ promptText: params.promptText,
33
+ heartbeatCollaborationInstructions: params.heartbeatCollaborationInstructions
33
34
  })
34
35
  };
35
36
  }
37
+ function joinPresentSections(...sections) {
38
+ return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
39
+ }
36
40
  function createCodexDynamicToolSpecsForPromptSnapshot(params) {
37
41
  return createCodexDynamicToolBridge({
38
42
  tools: filterCodexDynamicTools(params.tools, params.pluginConfig ?? {}),
@@ -1433,22 +1433,22 @@ function buildTurnStartParams(params, options) {
1433
1433
  model: params.modelId,
1434
1434
  ...options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {},
1435
1435
  effort: resolveReasoningEffort(params.thinkLevel, params.modelId),
1436
- collaborationMode: buildTurnCollaborationMode(params)
1436
+ collaborationMode: buildTurnCollaborationMode(params, { heartbeatCollaborationInstructions: options.heartbeatCollaborationInstructions })
1437
1437
  };
1438
1438
  }
1439
- function buildTurnCollaborationMode(params) {
1439
+ function buildTurnCollaborationMode(params, options = {}) {
1440
1440
  return {
1441
1441
  mode: "default",
1442
1442
  settings: {
1443
1443
  model: params.modelId,
1444
1444
  reasoning_effort: resolveReasoningEffort(params.thinkLevel, params.modelId),
1445
- developer_instructions: buildTurnScopedCollaborationInstructions(params)
1445
+ developer_instructions: buildTurnScopedCollaborationInstructions(params, options)
1446
1446
  }
1447
1447
  };
1448
1448
  }
1449
- function buildTurnScopedCollaborationInstructions(params) {
1449
+ function buildTurnScopedCollaborationInstructions(params, options = {}) {
1450
1450
  if (params.trigger === "cron") return buildCronCollaborationInstructions();
1451
- if (params.trigger === "heartbeat") return buildHeartbeatCollaborationInstructions();
1451
+ if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), options.heartbeatCollaborationInstructions);
1452
1452
  return null;
1453
1453
  }
1454
1454
  function buildCronCollaborationInstructions() {
@@ -1466,6 +1466,9 @@ function buildHeartbeatCollaborationInstructions() {
1466
1466
  CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY
1467
1467
  ].join("\n\n");
1468
1468
  }
1469
+ function joinPresentSections(...sections) {
1470
+ return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
1471
+ }
1469
1472
  function codexDynamicToolsFingerprint(dynamicTools) {
1470
1473
  return fingerprintDynamicTools(dynamicTools);
1471
1474
  }
@@ -1510,10 +1513,10 @@ function buildDeveloperInstructions(params, options = {}) {
1510
1513
  includeLegacyGlobalGuidance: false
1511
1514
  }).join("\n");
1512
1515
  return [
1513
- "Running inside OpenClaw. Use OpenClaw dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes capabilities when available.",
1516
+ "You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
1514
1517
  buildDeferredDynamicToolManifest(options.dynamicTools),
1515
- "Use Codex native `spawn_agent` for Codex subagents. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation; if it is not already loaded, search for `sessions_spawn` in the `openclaw` dynamic tool namespace before calling it.",
1516
- buildVisibleReplyInstruction(params),
1518
+ "Use Codex native `spawn_agent` for Codex subagents. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation.",
1519
+ buildVisibleReplyInstruction(params, options.dynamicTools),
1517
1520
  nativeCommandGuidance,
1518
1521
  params.extraSystemPrompt
1519
1522
  ].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
@@ -1523,9 +1526,15 @@ function buildDeferredDynamicToolManifest(dynamicTools) {
1523
1526
  if (deferredToolNames.length === 0) return;
1524
1527
  return `Deferred searchable OpenClaw dynamic tools available: ${deferredToolNames.join(", ")}. Use \`tool_search\` to load exact callable specs before use.`;
1525
1528
  }
1526
- function buildVisibleReplyInstruction(params) {
1527
- if (params.sourceReplyDeliveryMode === "message_tool_only") return "Preserve channel/session context. Visible channel replies: use `message`, do not describe would-reply.";
1528
- return "Preserve channel/session context. Visible channel replies should use the active Codex delivery path; do not describe would-reply.";
1529
+ function buildVisibleReplyInstruction(params, dynamicTools) {
1530
+ const messageToolAvailable = dynamicTools ? dynamicTools.some((tool) => tool.name.trim() === "message") : params.disableMessageTool !== true;
1531
+ if (params.sourceReplyDeliveryMode === "message_tool_only" && messageToolAvailable) return [
1532
+ "Preserve channel/session context.",
1533
+ "If this turn needs visible output in the current channel, call `message` with `action=\"send\"` before ending the turn.",
1534
+ "Do not rely on normal final assistant text for visible delivery; final text is private to OpenClaw/Codex in this mode.",
1535
+ "If no visible channel response is needed, do not call `message(action=\"send\")`."
1536
+ ].join(" ");
1537
+ return "To send a visible reply, use the active Codex delivery path.";
1529
1538
  }
1530
1539
  function buildUserInput(params, promptText = params.prompt) {
1531
1540
  const imageInputs = (params.images ?? []).map((image) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/codex",
3
- "version": "2026.5.19-alpha.1",
3
+ "version": "2026.5.19",
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.19-alpha.1"
30
+ "pluginApi": ">=2026.5.19"
31
31
  },
32
32
  "build": {
33
- "openclawVersion": "2026.5.19-alpha.1"
33
+ "openclawVersion": "2026.5.19"
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.19-alpha.1"
48
+ "openclaw": ">=2026.5.19"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "openclaw": {