@openclaw/codex 2026.9.1-beta.1 → 2026.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/api.js +8 -9
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-Bp1ZOnk2.js} +105 -75
  3. package/dist/attempt-context-CSazkWns.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-DxGJRYbY.js} +53 -57
  6. package/dist/{capabilities-BRMh0aIq.js → capabilities-B1mgvpP8.js} +2 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-DEsIufkt.js → command-formatters-DSbGMXA0.js} +5 -4
  9. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-DOJvGv6-.js} +243 -148
  10. package/dist/{compact-BglNFZrQ.js → compact-Y_xBqrHN.js} +109 -65
  11. package/dist/{config-giW8X0QA.js → config-CyapYSji.js} +328 -374
  12. package/dist/config-utils-C57JiJrU.js +69 -0
  13. package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
  14. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  15. package/dist/doctor-contract-api.js +144 -14
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BwpvTxaX.js} +1119 -1135
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-CY9Jn4bA.js} +4 -3
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-B9kp6iGC.js} +15 -6
  19. package/dist/event-projector-usage-ZqEVbAEY.js +212 -0
  20. package/dist/{harness-Csz0F-DK.js → harness-Czh2Sdoc.js} +65 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  23. package/dist/index.js +495 -457
  24. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-CRh-51AK.js} +9 -21
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-DcpnJF9x.js} +117 -80
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-DT5vo6qo.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-CcrgSMQR.js} +26 -29
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-B3bvQGcg.js} +63 -122
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-nfbUOtUB.js} +10 -7
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-aQVdyL5g.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-B7gHXJ3d.js} +44 -30
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BwCLTY_U.js} +6 -6
  34. package/dist/provider-capabilities-BpG9Ltc5.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-Fx_l4eNa.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-C8H22rPi.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-BzthFQSM.js} +1616 -2634
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-Bh_gf6-t.js} +22 -15
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-4Ki04sW-.js} +282 -399
  40. package/dist/session-binding-record-CyaXZvTe.js +270 -0
  41. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-B47uwekb.js} +812 -504
  42. package/dist/{session-cli-DMc4oS2w.js → session-cli-BjLhpmS1.js} +1 -1
  43. package/dist/session-history-DWmMqiqW.js +93 -0
  44. package/dist/session-history-read-LHldwvyh.js +36 -0
  45. package/dist/session-history-worker-runtime.js +48 -0
  46. package/dist/session-history.worker-zZVcx0ME.js +267 -0
  47. package/dist/session-history.worker.js +2 -0
  48. package/dist/session-initialization-BCFVnLL4.js +75 -0
  49. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-BaueHmSj.js} +2 -2
  50. package/dist/session-retirement-DNdmYdFc.js +97 -0
  51. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-DReTpGz6.js} +20 -4
  52. package/dist/settled-turn-context-BOM3Y1pl.js +45 -0
  53. package/dist/settled-turn-finalizer-BI-b1sry.js +115 -0
  54. package/dist/{shared-client-DKz23hHY.js → shared-client-CKhX2kFt.js} +3659 -847
  55. package/dist/{side-question-DjA_jsNe.js → side-question-B6DWAw1A.js} +168 -81
  56. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DPK30jad.js} +2412 -2145
  57. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-CbucAmmp.js} +27 -16
  58. package/dist/thread-policy-CTalhWFy.js +105 -0
  59. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-BLgqkUc1.js} +61 -9
  60. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-_njEB84z.js} +114 -110
  61. package/dist/transcript-mirror-attestation-Byl34PC-.js +76 -0
  62. package/dist/transport-stdio-CzTngV0j.js +728 -0
  63. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  64. package/dist/upstream-session-fork-1fwbNKzQ.js +886 -0
  65. package/dist/{usage-ROIaW3wo.js → usage-i9fv5-4m.js} +3 -3
  66. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-DKQn67A7.js} +3 -3
  67. package/openclaw.plugin.json +1 -30
  68. package/package.json +6 -6
  69. package/dist/binding-connection-BMvPX73o.js +0 -45
  70. package/dist/command-rpc-CCeCkf_v.js +0 -90
  71. package/dist/event-projector-usage-Bk1I90Bg.js +0 -328
  72. package/dist/incognito-session-ET_ufDH-.js +0 -2
  73. package/dist/model-catalog-i9rNBthX.js +0 -48
  74. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  75. package/dist/request-DXXGHko0.js +0 -157
  76. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  77. package/dist/session-retirement-CcNC3YSY.js +0 -36
  78. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  79. package/dist/thread-resume-BE9qNEW0.js +0 -30
  80. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  81. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
@@ -1,27 +1,30 @@
1
- import { N as isCodexFastServiceTier, _ as CODEX_PLUGINS_MARKETPLACE_NAME, i as resolveCodexAppServerRuntimeOptions } from "./config-giW8X0QA.js";
2
- import { J as consumeCodexAppServerLiveThread, ct as resolveCodexAppServerAuthAccountCacheKey, dt as resolveCodexAppServerFallbackApiKeyCacheKey, o as getLeasedSharedCodexAppServerClient, p as releaseLeasedSharedCodexAppServerClient, ut as resolveCodexAppServerAuthProfileIdForAgent, vt as isJsonObject } from "./shared-client-DKz23hHY.js";
3
- import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, n as assertCodexBindingMayBeReplaced, r as bindingStoreKey, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-Bcynfd-_.js";
4
- import { i as assertCodexThreadResumeResponse } from "./protocol-validators-VeJxxJ0U.js";
5
- import { t as listAllCodexAppServerModels } from "./models-TS3HBU-q.js";
6
- import { o as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-C1CAdfcP.js";
7
- import { Ct as refreshCodexPluginRuntimeState, Tt as isOpenAiCuratedMarketplaceName } from "./thread-lifecycle-L7oEkTlq.js";
8
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
9
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-BRMh0aIq.js";
10
- import { s as summarizeCodexAccountUsage } from "./rate-limits-C5_D8Gmb.js";
11
- import { a as formatComputerUseStatus, c as formatSkills, i as formatCodexStatus, l as formatThreads, n as formatAccount, o as formatList, r as formatCodexDisplayText, s as formatModels, t as buildHelp } from "./command-formatters-DEsIufkt.js";
12
- import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-kuWTn9C5.js";
13
- import { a as rollbackCodexAppServerBindingSubscription, i as retireCodexConversationThreadBinding, n as releaseCodexAppServerBindingSubscription, o as withCodexConversationThreadActivity, r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-DZeuKH1j.js";
14
- import { _ as hasCodexAdminScope, a as parseCodexPermissionsModeArg, c as setCodexConversationModel, d as stopCodexConversationTurn, g as canMutateCodexHost, h as CODEX_NATIVE_EXECUTION_AUTH_ERROR, i as parseCodexFastModeArg, l as setCodexConversationPermissions, m as CODEX_HOST_INSPECTION_AUTH_ERROR, n as parseCodexPluginMarketplaceId, o as readCodexConversationActiveTurn, p as CODEX_FULL_PERMISSIONS_AUTH_ERROR, r as formatPermissionsMode, s as setCodexConversationFastMode, t as discoverCodexMarketplacePlugins, u as steerCodexConversationTurn } from "./plugin-marketplace-discovery-B9DvaAG-.js";
15
- import { i as readCodexConversationBindingData, n as createCodexCliNodeConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-CW2GuupV.js";
16
- import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-BMvPX73o.js";
17
- import { l as formatCodexCliSessions } from "./session-catalog-DaLKVtpH.js";
18
- import { i as safeCodexControlRequest, n as readCodexStatusProbes, r as requestOptions, t as codexControlRequest } from "./command-rpc-CCeCkf_v.js";
19
- import { i as readCodexComputerUseStatus, r as installCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-CTCT3w5L.js";
1
+ import { m as CODEX_PLUGINS_MARKETPLACE_NAME, n as isCodexFastServiceTier } from "./config-utils-C57JiJrU.js";
2
+ import { l as sessionBindingIdentity, t as bindingStoreKey } from "./session-binding-record-CyaXZvTe.js";
3
+ import { B as isJsonObject, i as resolveCodexAppServerRuntimeOptions } from "./config-CyapYSji.js";
4
+ import { Bt as resolveCodexAppServerAuthAccountCacheKey, Et as hasCodexAppServerLiveThread, Ht as resolveCodexAppServerAuthProfileIdForAgent, Q as assertCodexThreadAcceptsDirectInput, Wt as resolveCodexAppServerFallbackApiKeyCacheKey, c as getLeasedSharedCodexAppServerClient, h as releaseLeasedSharedCodexAppServerClient, tt as assertCodexThreadResumeResponse, wt as consumeCodexAppServerLiveThread } from "./shared-client-CKhX2kFt.js";
5
+ import { l as resolveCodexSessionBinding, n as assertCodexBindingMayBeReplaced, o as normalizeCodexAppServerBindingModelProvider, r as createCodexSessionGenerationSupersededError } from "./session-binding-4Ki04sW-.js";
6
+ import { a as closeCodexStartupClientBestEffort } from "./attempt-client-cleanup-Bp1ZOnk2.js";
7
+ import { t as listAllCodexAppServerModels } from "./models-CcrgSMQR.js";
8
+ import { c as defaultCodexAppInventoryCache, i as buildCodexPluginAppCacheKey } from "./thread-resume-BLgqkUc1.js";
9
+ import { At as refreshCodexPluginRuntimeState, Mt as isOpenAiCuratedMarketplaceName } from "./thread-lifecycle-DPK30jad.js";
10
+ import { a as rollbackCodexAppServerBindingSubscription, c as withExclusiveCodexAppServerThread, i as retireCodexConversationThreadBinding, l as isIncognitoSessionKey, n as releaseCodexAppServerBindingSubscription, r as retainCodexAppServerBindingSubscription, s as withCodexConversationThreadActivity, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-CbucAmmp.js";
11
+ import { r as assertCodexSupervisionThreadLineage, s as resolveCodexBindingAppServerConnection } from "./thread-policy-CTalhWFy.js";
12
+ import { c as resolveCodexNativeSandboxBlock, s as resolveCodexNativeExecutionBlock } from "./request-C8H22rPi.js";
13
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-B1mgvpP8.js";
14
+ import { s as summarizeCodexAccountUsage } from "./rate-limits-Fx_l4eNa.js";
15
+ import { a as formatCodexDisplayText, c as formatComputerUseStatus, d as formatSkills, f as formatThreads, i as formatAccount, l as formatList, n as buildHelp, o as formatCodexStatus, r as escapeCodexChatText, s as formatCodexTextForDisplay, t as CODEX_RESUME_SAFE_THREAD_ID_PATTERN, u as formatModels } from "./command-formatters-DSbGMXA0.js";
16
+ import { _ as hasCodexAdminScope, a as parseCodexPermissionsModeArg, c as setCodexConversationModel, d as stopCodexConversationTurn, g as canMutateCodexHost, h as CODEX_NATIVE_EXECUTION_AUTH_ERROR, i as parseCodexFastModeArg, l as setCodexConversationPermissions, m as CODEX_HOST_INSPECTION_AUTH_ERROR, n as parseCodexPluginMarketplaceId, o as readCodexConversationActiveTurn, p as CODEX_FULL_PERMISSIONS_AUTH_ERROR, r as formatPermissionsMode, s as setCodexConversationFastMode, t as discoverCodexMarketplacePlugins, u as steerCodexConversationTurn } from "./plugin-marketplace-discovery-B7gHXJ3d.js";
17
+ import { i as readCodexConversationBindingData, n as createCodexCliNodeConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BiodpIzq.js";
18
+ import { c as readCodexStatusProbes, l as requestOptions, p as formatCodexCliSessions, s as codexControlRequest, u as safeCodexControlRequest } from "./session-catalog-B47uwekb.js";
19
+ import { i as readCodexComputerUseStatus, r as installCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-BwCLTY_U.js";
20
20
  import { normalizeOptionalString, normalizeUniqueStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
21
- import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
22
21
  import crypto from "node:crypto";
23
- import { ensureAuthProfileStore, findNormalizedProviderValue, resolveAgentDir, resolveAuthProfileEligibility, resolveAuthProfileOrder, resolveProfileUnusableUntilForDisplay, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
22
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
24
23
  import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
24
+ import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
25
+ import { isDeepStrictEqual } from "node:util";
26
+ import { ensureAuthProfileStore, resolveAgentDir as resolveAgentDir$1, resolveAuthProfileEligibility, resolveProfileUnusableUntilForDisplay } from "openclaw/plugin-sdk/agent-runtime";
27
+ import { findNormalizedProviderValue, resolveAuthProfileOrder } from "openclaw/plugin-sdk/provider-auth";
25
28
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
29
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
27
30
  import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
@@ -591,7 +594,8 @@ function parseCodexCliSessionsArgs(args) {
591
594
  continue;
592
595
  }
593
596
  if (arg === "--limit") {
594
- const parsedLimit = parseStrictPositiveInteger(readRequiredOptionValue(args, index));
597
+ const value = readRequiredOptionValue(args, index);
598
+ const parsedLimit = parseStrictPositiveInteger(value);
595
599
  if (parsedLimit === void 0) {
596
600
  parsed.help = true;
597
601
  continue;
@@ -752,11 +756,10 @@ const CODEX_DIAGNOSTICS_COOLDOWN_MS = 6e4;
752
756
  const CODEX_DIAGNOSTICS_ERROR_MAX_CHARS = 500;
753
757
  const CODEX_DIAGNOSTICS_COOLDOWN_MAX_THREADS = 100;
754
758
  const CODEX_DIAGNOSTICS_COOLDOWN_MAX_SCOPES = 100;
755
- const CODEX_DIAGNOSTICS_CONFIRMATION_TTL_MS = 5 * 6e4;
759
+ const CODEX_DIAGNOSTICS_CONFIRMATION_TTL_MS = 3e5;
756
760
  const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_REQUESTS_PER_SCOPE = 100;
757
761
  const CODEX_DIAGNOSTICS_CONFIRMATION_MAX_SCOPES = 100;
758
762
  const CODEX_DIAGNOSTICS_SCOPE_FIELD_MAX_CHARS = 128;
759
- const CODEX_RESUME_SAFE_THREAD_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
760
763
  const { lastUploadByThread: lastCodexDiagnosticsUploadByThread, lastUploadByScope: lastCodexDiagnosticsUploadByScope, pendingConfirmations: pendingCodexDiagnosticsConfirmations, pendingTokensByScope: pendingCodexDiagnosticsConfirmationTokensByScope } = codexDiagnosticsFeedbackState;
761
764
  function normalizeDiagnosticsReason(note) {
762
765
  const normalized = normalizeOptionalString(note);
@@ -909,7 +912,7 @@ function formatCodexDiagnosticsTargetLines(targets) {
909
912
  }
910
913
  function formatCodexDiagnosticsTargetBlock(target, index) {
911
914
  const lines = [`Session ${index + 1}`];
912
- if (target.channel) lines.push(`Channel: ${formatCodexValueForDisplay(target.channel)}`);
915
+ if (target.channel) lines.push(`Channel: ${formatCodexDisplayText(target.channel)}`);
913
916
  if (target.sessionKey) lines.push(`OpenClaw session key: ${formatCodexCopyableValueForDisplay(target.sessionKey)}`);
914
917
  if (target.sessionId) lines.push(`OpenClaw session id: ${formatCodexCopyableValueForDisplay(target.sessionId)}`);
915
918
  lines.push(`Codex thread id: ${formatCodexCopyableValueForDisplay(target.threadId)}`);
@@ -918,30 +921,18 @@ function formatCodexDiagnosticsTargetBlock(target, index) {
918
921
  }
919
922
  function formatCodexDiagnosticsTargetLine(target) {
920
923
  const parts = [];
921
- if (target.channel) parts.push(`channel ${formatCodexValueForDisplay(target.channel)}`);
924
+ if (target.channel) parts.push(`channel ${formatCodexDisplayText(target.channel)}`);
922
925
  const sessionLabel = target.sessionId || target.sessionKey;
923
- if (sessionLabel) parts.push(`OpenClaw session ${formatCodexValueForDisplay(sessionLabel)}`);
924
- parts.push(`Codex thread ${formatCodexThreadIdForDisplay(target.threadId)}`);
926
+ if (sessionLabel) parts.push(`OpenClaw session ${formatCodexDisplayText(sessionLabel)}`);
927
+ parts.push(`Codex thread ${formatCodexDisplayText(target.threadId)}`);
925
928
  return `- ${parts.join(", ")}`;
926
929
  }
927
- function escapeCodexChatText(value) {
928
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("@", "@").replaceAll("`", "`").replaceAll("[", "[").replaceAll("]", "]").replaceAll("(", "(").replaceAll(")", ")").replaceAll("*", "∗").replaceAll("_", "_").replaceAll("~", "~").replaceAll("|", "|");
929
- }
930
- function formatCodexTextForDisplay(value) {
931
- let safe = "";
932
- for (const character of value) {
933
- const codePoint = character.codePointAt(0);
934
- safe += codePoint != null && isUnsafeDisplayCodePoint(codePoint) ? "?" : character;
935
- }
936
- safe = safe.trim();
937
- return safe || "<unknown>";
938
- }
939
930
  function readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, now, options = {}) {
940
931
  for (const target of targets) {
941
932
  const cooldownMs = readCodexDiagnosticsCooldownMs(target.threadId, now);
942
933
  if (cooldownMs > 0) {
943
934
  if (options.includeThreadId === false) return `Codex diagnostics were already sent for one of these Codex threads recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
944
- return `Codex diagnostics were already sent for thread ${formatCodexThreadIdForDisplay(target.threadId)} recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
935
+ return `Codex diagnostics were already sent for thread ${formatCodexDisplayText(target.threadId)} recently. Try again in ${Math.ceil(cooldownMs / 1e3)}s.`;
945
936
  }
946
937
  }
947
938
  const scopeCooldownMs = readCodexDiagnosticsScopeCooldownMs(options.cooldownScope ?? readCodexDiagnosticsCooldownScope(ctx), now);
@@ -973,12 +964,6 @@ function buildDiagnosticsTags(ctx) {
973
964
  function addTag(tags, key, value) {
974
965
  if (typeof value === "string" && value.trim()) tags[key] = value.trim();
975
966
  }
976
- function formatCodexThreadIdForDisplay(threadId) {
977
- return escapeCodexChatText(formatCodexTextForDisplay(threadId));
978
- }
979
- function formatCodexValueForDisplay(value) {
980
- return escapeCodexChatText(formatCodexTextForDisplay(value));
981
- }
982
967
  function formatCodexCopyableValueForDisplay(value) {
983
968
  const safe = formatCodexTextForDisplay(value);
984
969
  if (CODEX_RESUME_SAFE_THREAD_ID_PATTERN.test(safe)) return `\`${safe}\``;
@@ -1014,16 +999,14 @@ function pruneCodexDiagnosticsCooldownMap(map, now) {
1014
999
  for (const [key, lastSentAt] of map) if (now - lastSentAt >= CODEX_DIAGNOSTICS_COOLDOWN_MS) map.delete(key);
1015
1000
  }
1016
1001
  function formatCodexErrorForDisplay(error) {
1017
- return escapeCodexChatText(truncateUtf16Safe(formatCodexTextForDisplay(error), CODEX_DIAGNOSTICS_ERROR_MAX_CHARS)) || "unknown error";
1002
+ const safe = truncateUtf16Safe(formatCodexTextForDisplay(error), CODEX_DIAGNOSTICS_ERROR_MAX_CHARS);
1003
+ return escapeCodexChatText(safe) || "unknown error";
1018
1004
  }
1019
1005
  function formatCodexResumeCommandForDisplay(threadId) {
1020
1006
  const safeThreadId = formatCodexTextForDisplay(threadId);
1021
1007
  if (!CODEX_RESUME_SAFE_THREAD_ID_PATTERN.test(safeThreadId)) return "run codex resume and paste the thread id shown above";
1022
1008
  return `\`codex resume ${safeThreadId}\``;
1023
1009
  }
1024
- function isUnsafeDisplayCodePoint(codePoint) {
1025
- return codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint === 173 || codePoint === 1564 || codePoint === 6158 || codePoint >= 8203 && codePoint <= 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8288 && codePoint <= 8303 || codePoint === 65279 || codePoint >= 65529 && codePoint <= 65531 || codePoint >= 917504 && codePoint <= 917631;
1026
- }
1027
1010
  function normalizeCodexDiagnosticsScopeField(value) {
1028
1011
  const normalized = normalizeOptionalString(value);
1029
1012
  if (!normalized) return;
@@ -1063,7 +1046,8 @@ function resolveCodexCommandDeps(overrides) {
1063
1046
  //#endregion
1064
1047
  //#region extensions/codex/src/command-handler-scope.ts
1065
1048
  async function resolveControlTarget(ctx) {
1066
- const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
1049
+ const binding = await ctx.getCurrentConversationBinding();
1050
+ const data = readCodexConversationBindingData(binding);
1067
1051
  const scope = resolveCodexConversationControlScope(ctx);
1068
1052
  if (data?.kind === "codex-app-server-session") return {
1069
1053
  identity: conversationBindingIdentity(data.bindingId),
@@ -1082,18 +1066,64 @@ async function resolveControlTarget(ctx) {
1082
1066
  agentDir: scope.agentDir
1083
1067
  } : void 0;
1084
1068
  }
1085
- async function resolveCommandAppServerScope(deps, ctx, pluginConfig) {
1069
+ async function resolvePreparedCodexCommandAuthority(deps, ctx) {
1086
1070
  const target = await resolveControlTarget(ctx);
1087
1071
  const fallback = resolveCodexConversationControlScope(ctx);
1072
+ const sessionId = ctx.sessionId;
1073
+ const sessionKey = ctx.sessionKey;
1074
+ const sessionAgentId = ctx.sessionTarget?.agentId ?? fallback.agentId;
1075
+ const storePath = ctx.sessionTarget?.storePath ?? (sessionKey ? resolveStorePath(ctx.config.session?.store, { agentId: sessionAgentId }) : void 0);
1076
+ const sessionIdentity = sessionId ? sessionBindingIdentity({
1077
+ sessionId,
1078
+ sessionKey,
1079
+ agentId: sessionAgentId,
1080
+ config: ctx.config
1081
+ }) : void 0;
1082
+ const currentSession = sessionIdentity ? await resolveCodexSessionBinding({
1083
+ reclaimStale: true,
1084
+ bindingStore: deps.bindingStore,
1085
+ identity: sessionIdentity,
1086
+ config: ctx.config,
1087
+ storePath
1088
+ }) : void 0;
1089
+ const assertHostCurrent = currentSession?.assertCurrent ?? (() => {});
1090
+ const binding = (target && (!sessionIdentity || !isDeepStrictEqual(target.identity, sessionIdentity)) ? await resolveCodexSessionBinding({
1091
+ bindingStore: deps.bindingStore,
1092
+ identity: target.identity,
1093
+ config: ctx.config,
1094
+ storePath,
1095
+ assertCurrent: assertHostCurrent
1096
+ }) : currentSession)?.binding;
1097
+ const assertCurrent = () => {
1098
+ assertHostCurrent();
1099
+ if (target && !isDeepStrictEqual(deps.bindingStore.read(target.identity), binding)) throw new Error("Codex command binding changed before dispatch");
1100
+ assertHostCurrent();
1101
+ };
1102
+ assertCurrent();
1103
+ return {
1104
+ target,
1105
+ binding,
1106
+ currentSessionBinding: currentSession?.binding,
1107
+ sessionId,
1108
+ sessionKey,
1109
+ storePath,
1110
+ assertHostCurrent,
1111
+ assertCurrent
1112
+ };
1113
+ }
1114
+ async function resolveCommandAppServerScope(deps, ctx, pluginConfig) {
1115
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1116
+ const { target, binding } = authority;
1117
+ const fallback = resolveCodexConversationControlScope(ctx);
1088
1118
  const agentDir = target?.agentDir ?? fallback.agentDir;
1089
- const binding = target ? await deps.bindingStore.read(target.identity) : void 0;
1119
+ const authProfileId = binding?.connectionScope === "supervision" ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
1120
+ authProfileId: binding?.authProfileId ?? target?.requestedAuthProfileId,
1121
+ agentDir,
1122
+ config: ctx.config
1123
+ });
1090
1124
  const connection = resolveCodexBindingAppServerConnection({
1091
1125
  binding,
1092
- authProfileId: binding?.connectionScope === "supervision" ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
1093
- authProfileId: binding?.authProfileId ?? target?.requestedAuthProfileId,
1094
- agentDir,
1095
- config: ctx.config
1096
- }),
1126
+ authProfileId,
1097
1127
  pluginConfig
1098
1128
  });
1099
1129
  return {
@@ -1101,8 +1131,10 @@ async function resolveCommandAppServerScope(deps, ctx, pluginConfig) {
1101
1131
  agentDir,
1102
1132
  ...connection.clientAuthProfileId !== void 0 ? { authProfileId: connection.clientAuthProfileId } : {},
1103
1133
  ...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {},
1104
- ...ctx.sessionKey ? { sessionKey: ctx.sessionKey } : {},
1105
- ...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
1134
+ ...authority.sessionKey ? { sessionKey: authority.sessionKey } : {},
1135
+ ...authority.sessionId ? { sessionId: authority.sessionId } : {},
1136
+ ...authority.storePath ? { storePath: authority.storePath } : {},
1137
+ assertCurrent: authority.assertCurrent
1106
1138
  };
1107
1139
  }
1108
1140
  function conversationBindingIdentity(bindingId) {
@@ -1112,14 +1144,14 @@ function conversationBindingIdentity(bindingId) {
1112
1144
  };
1113
1145
  }
1114
1146
  function resolveCodexConversationControlScope(ctx) {
1115
- const { sessionAgentId } = resolveSessionAgentIds({
1147
+ const { sessionAgentId } = resolveSessionAgentIdsStrict({
1116
1148
  sessionKey: ctx.sessionKey,
1117
1149
  agentId: ctx.agentId,
1118
1150
  config: ctx.config
1119
1151
  });
1120
1152
  return {
1121
1153
  agentId: sessionAgentId,
1122
- agentDir: resolveAgentDir(ctx.config, sessionAgentId)
1154
+ agentDir: resolveAgentDir$1(ctx.config, sessionAgentId)
1123
1155
  };
1124
1156
  }
1125
1157
  //#endregion
@@ -1155,7 +1187,7 @@ async function requestCodexDiagnosticsFeedbackApproval(deps, ctx, note, commandP
1155
1187
  });
1156
1188
  const confirmCommand = `${commandPrefix} confirm ${token}`;
1157
1189
  const cancelCommand = `${commandPrefix} cancel ${token}`;
1158
- const displayReason = reason ? escapeCodexChatText(formatCodexTextForDisplay(reason)) : void 0;
1190
+ const displayReason = reason ? formatCodexDisplayText(reason) : void 0;
1159
1191
  return {
1160
1192
  text: [
1161
1193
  targets.length === 1 ? "Codex runtime thread detected." : "Codex runtime threads detected.",
@@ -1197,7 +1229,7 @@ async function previewCodexDiagnosticsFeedbackApproval(deps, ctx, note) {
1197
1229
  const cooldownMessage = readCodexDiagnosticsTargetsCooldownMessage(targets, ctx, Date.now(), { includeThreadId: false });
1198
1230
  if (cooldownMessage) return cooldownMessage;
1199
1231
  const reason = normalizeDiagnosticsReason(note);
1200
- const displayReason = reason ? escapeCodexChatText(formatCodexTextForDisplay(reason)) : void 0;
1232
+ const displayReason = reason ? formatCodexDisplayText(reason) : void 0;
1201
1233
  return [
1202
1234
  targets.length === 1 ? "Codex runtime thread detected." : "Codex runtime threads detected.",
1203
1235
  `Approving diagnostics will also send ${targets.length === 1 ? "this thread's feedback bundle" : "these threads' feedback bundles"} to OpenAI servers.`,
@@ -1216,7 +1248,7 @@ async function confirmCodexDiagnosticsFeedback(deps, ctx, pluginConfig, token) {
1216
1248
  if (scopeMismatch) return scopeMismatch.confirmMessage;
1217
1249
  deletePendingCodexDiagnosticsConfirmation(token);
1218
1250
  if (!pending.privateRouted && !await hasAnyCodexDiagnosticsIdentity(ctx)) return "Cannot send Codex diagnostics because this command did not include a stable session identity.";
1219
- const currentTargets = pending.privateRouted ? await resolvePendingCodexDiagnosticsTargets(deps, pending.targets, ctx.config) : await resolveCodexDiagnosticsTargets(deps, ctx);
1251
+ const currentTargets = pending.privateRouted ? resolvePendingCodexDiagnosticsTargets(deps, pending.targets, ctx.config) : await resolveCodexDiagnosticsTargets(deps, ctx);
1220
1252
  if (!codexDiagnosticsTargetsMatch(pending.targets, currentTargets)) return "The Codex diagnostics sessions changed before confirmation. Run /diagnostics again for the current threads.";
1221
1253
  return await sendCodexDiagnosticsFeedbackForTargets(deps, ctx, pluginConfig, pending.note ?? "", currentTargets, { cooldownScope: pending.scopeKey });
1222
1254
  }
@@ -1323,7 +1355,7 @@ async function resolveCodexDiagnosticsTargets(deps, ctx) {
1323
1355
  });
1324
1356
  candidates.push({
1325
1357
  identity,
1326
- agentDir: resolveAgentDir(ctx.config, identity.agentId),
1358
+ agentDir: resolveAgentDir$1(ctx.config, identity.agentId),
1327
1359
  sessionKey: session.sessionKey,
1328
1360
  sessionId: session.sessionId,
1329
1361
  channel: session.channel,
@@ -1340,17 +1372,17 @@ async function resolveCodexDiagnosticsTargets(deps, ctx) {
1340
1372
  const key = bindingStoreKey(candidate.identity);
1341
1373
  if (seenBindingKeys.has(key)) continue;
1342
1374
  seenBindingKeys.add(key);
1343
- const binding = await deps.bindingStore.read(candidate.identity);
1375
+ const binding = deps.bindingStore.read(candidate.identity);
1344
1376
  if (!binding?.threadId || seenThreadIds.has(binding.threadId)) continue;
1345
1377
  seenThreadIds.add(binding.threadId);
1346
1378
  targets.push(resolveCodexDiagnosticsTarget(candidate, binding, ctx.config));
1347
1379
  }
1348
1380
  return targets;
1349
1381
  }
1350
- async function resolvePendingCodexDiagnosticsTargets(deps, targets, config) {
1382
+ function resolvePendingCodexDiagnosticsTargets(deps, targets, config) {
1351
1383
  const resolved = [];
1352
1384
  for (const target of targets) {
1353
- const binding = await deps.bindingStore.read(target.identity);
1385
+ const binding = deps.bindingStore.read(target.identity);
1354
1386
  if (!binding?.threadId) continue;
1355
1387
  resolved.push(resolveCodexDiagnosticsTarget(target, binding, config));
1356
1388
  }
@@ -1392,8 +1424,9 @@ function isCurrentSessionModelSelectionLocked(ctx) {
1392
1424
  const sessionKey = ctx.sessionKey?.trim();
1393
1425
  if (!sessionKey) return false;
1394
1426
  const { agentId } = resolveCodexConversationControlScope(ctx);
1427
+ const storePath = ctx.sessionTarget?.storePath ?? resolveStorePath(ctx.config.session?.store, { agentId });
1395
1428
  return isModelSelectionLocked(getSessionEntry({
1396
- storePath: resolveStorePath(ctx.config.session?.store, { agentId }),
1429
+ storePath,
1397
1430
  sessionKey,
1398
1431
  hydrateSkillPromptRefs: false,
1399
1432
  readConsistency: "latest"
@@ -1415,7 +1448,7 @@ async function bindConversation(deps, ctx, pluginConfig, args) {
1415
1448
  config: ctx.config
1416
1449
  }) : void 0;
1417
1450
  const currentOwner = currentConversationData?.kind === "codex-app-server-session" ? conversationBindingIdentity(currentConversationData.bindingId) : sessionOwner;
1418
- const existingBinding = currentOwner ? await deps.bindingStore.read(currentOwner) : void 0;
1451
+ const existingBinding = currentOwner ? deps.bindingStore.read(currentOwner) : void 0;
1419
1452
  assertCodexBindingMayBeReplaced(existingBinding, "binding this conversation to another thread");
1420
1453
  const sessionSource = sessionOwner && existingBinding ? {
1421
1454
  agentId: sessionOwner.agentId,
@@ -1450,13 +1483,14 @@ async function bindConversation(deps, ctx, pluginConfig, args) {
1450
1483
  }
1451
1484
  async function detachConversation(deps, ctx) {
1452
1485
  if (isCurrentSessionModelSelectionLocked(ctx)) return MODEL_SELECTION_LOCKED_MESSAGE;
1453
- const data = readCodexConversationBindingData(await ctx.getCurrentConversationBinding());
1486
+ const current = await ctx.getCurrentConversationBinding();
1487
+ const data = readCodexConversationBindingData(current);
1454
1488
  const identity = data?.kind === "codex-app-server-session" ? conversationBindingIdentity(data.bindingId) : void 0;
1455
1489
  const sourceSessionKey = data?.kind === "codex-app-server-session" ? data.source?.sessionKey : void 0;
1456
1490
  let expectedThreadId;
1457
1491
  let expectedStartId;
1458
1492
  if (data?.kind === "codex-app-server-session") {
1459
- const binding = await deps.bindingStore.read(identity);
1493
+ const binding = deps.bindingStore.read(identity);
1460
1494
  assertCodexBindingMayBeReplaced(binding, "detaching its conversation binding");
1461
1495
  if (deps.readCodexConversationActiveTurn(identity)) return "This Codex conversation has an active run; use /codex stop before detaching it.";
1462
1496
  expectedThreadId = binding?.threadId;
@@ -1494,13 +1528,13 @@ async function describeConversationBinding(deps, ctx) {
1494
1528
  "- Active run: not tracked"
1495
1529
  ].join("\n");
1496
1530
  const identity = conversationBindingIdentity(data.bindingId);
1497
- const threadBinding = await deps.bindingStore.read(identity);
1531
+ const threadBinding = deps.bindingStore.read(identity);
1498
1532
  const active = deps.readCodexConversationActiveTurn(identity);
1499
1533
  const sessionKey = ctx.sessionKey?.trim();
1500
1534
  const { agentId } = resolveCodexConversationControlScope(ctx);
1501
1535
  const sessionEntry = sessionKey ? getSessionEntry({
1502
1536
  agentId,
1503
- storePath: resolveStorePath(ctx.config.session?.store, { agentId }),
1537
+ storePath: ctx.sessionTarget?.storePath ?? resolveStorePath(ctx.config.session?.store, { agentId }),
1504
1538
  sessionKey,
1505
1539
  hydrateSkillPromptRefs: false,
1506
1540
  readConsistency: "latest"
@@ -1519,13 +1553,14 @@ async function describeConversationBinding(deps, ctx) {
1519
1553
  }
1520
1554
  async function buildThreads(deps, ctx, pluginConfig, filter) {
1521
1555
  const scope = await resolveCommandAppServerScope(deps, ctx, pluginConfig);
1522
- return formatThreads(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
1556
+ const response = await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
1523
1557
  limit: 10,
1524
1558
  ...filter.trim() ? { searchTerm: filter.trim() } : {}
1525
1559
  }, {
1526
1560
  config: ctx.config,
1527
1561
  ...scope
1528
- }));
1562
+ });
1563
+ return formatThreads(response);
1529
1564
  }
1530
1565
  async function buildCodexCliSessions(deps, args) {
1531
1566
  const parsed = parseCodexCliSessionsArgs(args);
@@ -1551,25 +1586,25 @@ async function resumeThread(deps, ctx, pluginConfig, args) {
1551
1586
  agentId: scope.agentId,
1552
1587
  config: ctx.config
1553
1588
  });
1589
+ const { assertCurrent: assertHostGeneration } = await resolveCodexSessionBinding({
1590
+ reclaimStale: true,
1591
+ bindingStore: deps.bindingStore,
1592
+ identity,
1593
+ config: ctx.config,
1594
+ storePath: ctx.sessionTarget?.storePath
1595
+ });
1554
1596
  return await withExclusiveCodexAppServerThread({
1555
1597
  bindingStore: deps.bindingStore,
1556
1598
  identity,
1557
1599
  threadId: normalizedThreadId,
1558
1600
  run: async () => await deps.bindingStore.withLease(identity, async () => {
1559
- if (!await reclaimCurrentCodexSessionGeneration({
1560
- bindingStore: deps.bindingStore,
1561
- identity,
1562
- config: ctx.config
1563
- })) throw createCodexSessionGenerationSupersededError(identity.sessionId);
1564
- const currentBinding = await deps.bindingStore.read(identity);
1601
+ const generation = await deps.bindingStore.prepareSessionGenerationReclaim(identity);
1602
+ assertHostGeneration();
1603
+ if (generation.kind !== "resolved" || !generation.result) throw createCodexSessionGenerationSupersededError(identity.sessionId);
1604
+ const currentBinding = deps.bindingStore.read(identity);
1565
1605
  assertCodexBindingMayBeReplaced(currentBinding, "attaching a different resumed thread");
1566
- const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
1567
- authProfileId: currentBinding?.authProfileId,
1568
- agentDir: scope.agentDir,
1569
- config: ctx.config
1570
- });
1571
- let committedResponse = false;
1572
- const commitResumedThread = async (value, client) => {
1606
+ let pendingResumeConfiguration = false;
1607
+ const commitResumedThread = async (value, client, { authProfileId, assertCurrent }) => {
1573
1608
  const response = assertCodexThreadResumeResponse(value);
1574
1609
  const effectiveThreadId = response.thread.id;
1575
1610
  if (effectiveThreadId !== normalizedThreadId) throw new Error(`Codex thread/resume returned ${effectiveThreadId} for ${normalizedThreadId}`);
@@ -1581,52 +1616,75 @@ async function resumeThread(deps, ctx, pluginConfig, args) {
1581
1616
  agentDir: scope.agentDir,
1582
1617
  config: ctx.config
1583
1618
  });
1584
- const bindingBeforeCommit = await deps.bindingStore.read(identity);
1585
- assertCodexBindingMayBeReplaced(bindingBeforeCommit, "committing a different resumed thread");
1586
- const clientId = client?.getInstanceId();
1587
- const sameOwner = client ? isSameCodexAppServerThreadOwner(bindingBeforeCommit, {
1588
- threadId: effectiveThreadId,
1589
- clientId
1590
- }) : bindingBeforeCommit?.threadId === effectiveThreadId;
1619
+ const clientId = client.getInstanceId();
1591
1620
  let retained = false;
1621
+ let sameOwner = false;
1622
+ let knownOwnership;
1592
1623
  try {
1593
- if (client) {
1594
- retained = await retainCodexAppServerBindingSubscription(client, effectiveThreadId, sameOwner ? await consumeCodexAppServerLiveThread(client, effectiveThreadId) : void 0);
1595
- if (!retained) throw new Error("Codex resumed thread lost its native subscription owner.");
1596
- }
1597
- if (bindingBeforeCommit && !sameOwner) await releaseCodexAppServerBindingSubscription(bindingBeforeCommit);
1624
+ const bindingBeforeCommit = deps.bindingStore.read(identity);
1625
+ assertCodexBindingMayBeReplaced(bindingBeforeCommit, "committing a different resumed thread");
1626
+ sameOwner = isSameCodexAppServerThreadOwner(bindingBeforeCommit, {
1627
+ threadId: effectiveThreadId,
1628
+ clientId
1629
+ });
1630
+ const sameThreadBinding = bindingBeforeCommit?.threadId === effectiveThreadId ? bindingBeforeCommit : void 0;
1631
+ pendingResumeConfiguration = sameThreadBinding?.preserveNativeModel !== true && (!sameThreadBinding?.dynamicToolsFingerprint || !sameThreadBinding.webSearchThreadConfigFingerprint || sameThreadBinding.pendingResumeConfiguration === true);
1632
+ assertCurrent();
1633
+ assertCodexThreadAcceptsDirectInput(response.thread);
1634
+ knownOwnership = sameOwner ? await consumeCodexAppServerLiveThread(client, effectiveThreadId) : void 0;
1635
+ assertCurrent();
1636
+ retained = await retainCodexAppServerBindingSubscription(client, effectiveThreadId, knownOwnership);
1637
+ assertCurrent();
1638
+ if (!retained) throw new Error("Codex resumed thread lost its native subscription owner.");
1639
+ if (bindingBeforeCommit && !sameOwner) await releaseCodexAppServerBindingSubscription(bindingBeforeCommit, { assertCurrent });
1640
+ assertCurrent();
1598
1641
  if (!await deps.bindingStore.mutate(identity, {
1599
1642
  kind: "set",
1600
1643
  binding: {
1601
- ...bindingBeforeCommit?.threadId === effectiveThreadId ? bindingBeforeCommit : {},
1644
+ ...sameThreadBinding,
1602
1645
  threadId: effectiveThreadId,
1603
- ...clientId ? { clientId } : {},
1646
+ clientId,
1604
1647
  cwd: resumedCwd,
1648
+ rolloutPath: response.thread.path ?? sameThreadBinding?.rolloutPath,
1649
+ pendingResumeConfiguration: pendingResumeConfiguration ? true : void 0,
1605
1650
  authProfileId,
1606
1651
  model: response.model,
1607
1652
  modelProvider,
1608
1653
  historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString()
1609
1654
  }
1610
- })) throw new Error("Codex thread binding changed while attaching the resumed thread.");
1655
+ }, assertCurrent)) throw new Error("Codex thread binding changed while attaching the resumed thread.");
1611
1656
  } catch (error) {
1612
- if (client && !sameOwner) await rollbackCodexAppServerBindingSubscription(client, effectiveThreadId, retained);
1657
+ if (sameOwner && knownOwnership && !retained) {
1658
+ if (!await retainCodexAppServerBindingSubscription(client, effectiveThreadId, knownOwnership)) await closeCodexStartupClientBestEffort(client);
1659
+ } else if (retained && !sameOwner || !hasCodexAppServerLiveThread(client, effectiveThreadId)) await rollbackCodexAppServerBindingSubscription(client, effectiveThreadId, retained);
1613
1660
  throw error;
1614
1661
  }
1615
- committedResponse = true;
1616
1662
  };
1617
- const response = await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
1663
+ await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.resumeThread, {
1618
1664
  threadId: normalizedThreadId,
1619
1665
  excludeTurns: true
1620
1666
  }, {
1621
1667
  config: ctx.config,
1668
+ agentId: scope.agentId,
1622
1669
  agentDir: scope.agentDir,
1623
- authProfileId,
1670
+ authProfileId: currentBinding?.authProfileId,
1624
1671
  sessionKey: ctx.sessionKey,
1625
1672
  sessionId: ctx.sessionId,
1673
+ storePath: ctx.sessionTarget?.storePath,
1674
+ assertCurrent: assertHostGeneration,
1675
+ beforeRequest: async (request) => {
1676
+ const { thread } = await request({
1677
+ method: "thread/read",
1678
+ requestParams: {
1679
+ threadId: normalizedThreadId,
1680
+ includeTurns: false
1681
+ }
1682
+ });
1683
+ assertCodexThreadAcceptsDirectInput(thread);
1684
+ },
1626
1685
  onResponse: commitResumedThread
1627
1686
  });
1628
- if (!committedResponse) await commitResumedThread(response);
1629
- return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(normalizedThreadId)}.`;
1687
+ return `Attached this OpenClaw session to Codex thread ${formatCodexDisplayText(normalizedThreadId)}.${pendingResumeConfiguration ? " The next turn will validate its tools and apply this session's configuration before continuing." : ""}`;
1630
1688
  })
1631
1689
  });
1632
1690
  }
@@ -1635,12 +1693,13 @@ async function bindCodexCliNodeSession(deps, ctx, parsed) {
1635
1693
  if (isCurrentSessionModelSelectionLocked(ctx)) return MODEL_SELECTION_LOCKED_MESSAGE;
1636
1694
  if (ctx.sessionId) {
1637
1695
  const scope = resolveCodexConversationControlScope(ctx);
1638
- assertCodexBindingMayBeReplaced(await deps.bindingStore.read(sessionBindingIdentity({
1696
+ const binding = deps.bindingStore.read(sessionBindingIdentity({
1639
1697
  sessionId: ctx.sessionId,
1640
1698
  sessionKey: ctx.sessionKey,
1641
1699
  agentId: scope.agentId,
1642
1700
  config: ctx.config
1643
- })), "binding a Codex CLI node session");
1701
+ }));
1702
+ assertCodexBindingMayBeReplaced(binding, "binding a Codex CLI node session");
1644
1703
  }
1645
1704
  const resolved = await deps.resolveCodexCliSessionForBindingOnNode({
1646
1705
  requestedNode: parsed.host,
@@ -1755,9 +1814,9 @@ async function handleComputerUseCommand(deps, ctx, pluginConfig, args) {
1755
1814
  async function handleNativeGoal(deps, ctx, pluginConfig, args) {
1756
1815
  const action = (args[0] ?? "status").toLowerCase();
1757
1816
  const objective = args.slice(1).join(" ").trim();
1758
- const target = await resolveControlTarget(ctx);
1817
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1818
+ const { target, binding } = authority;
1759
1819
  if (!target) return "Cannot manage the Codex goal because this command has no stable binding identity.";
1760
- const binding = await deps.bindingStore.read(target.identity);
1761
1820
  if (!binding?.threadId) return "No Codex thread is attached to this OpenClaw session yet.";
1762
1821
  const connection = resolveCodexBindingAppServerConnection({
1763
1822
  binding,
@@ -1768,6 +1827,10 @@ async function handleNativeGoal(deps, ctx, pluginConfig, args) {
1768
1827
  agentDir: target.agentDir,
1769
1828
  authProfileId: connection.clientAuthProfileId,
1770
1829
  config: ctx.config,
1830
+ sessionId: authority.sessionId,
1831
+ sessionKey: authority.sessionKey,
1832
+ storePath: authority.storePath,
1833
+ assertCurrent: authority.assertCurrent,
1771
1834
  ...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {}
1772
1835
  };
1773
1836
  if (action === "status" || action === "get") {
@@ -1783,11 +1846,14 @@ async function handleNativeGoal(deps, ctx, pluginConfig, args) {
1783
1846
  const isObjectiveUpdate = action === "set";
1784
1847
  if (!requestedStatus && !isObjectiveUpdate || isObjectiveUpdate && !objective) return "Usage: /codex goal [status|set <objective>|pause|resume|block|complete|clear]";
1785
1848
  if (requestedStatus && args.length > 1) return `Usage: /codex goal ${action}`;
1786
- return formatNativeGoal(await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.setThreadGoal, {
1849
+ return formatNativeGoal(await deps.bindingStore.withLease(target.identity, () => deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.setThreadGoal, {
1787
1850
  threadId: binding.threadId,
1788
1851
  ...objective ? { objective } : {},
1789
1852
  ...requestedStatus ? { status: requestedStatus } : {}
1790
- }, goalRequestOptions));
1853
+ }, {
1854
+ ...goalRequestOptions,
1855
+ ...(isObjectiveUpdate || requestedStatus === "active") && connection.usesSupervisionConnection ? { beforeRequest: supervisedCommandGuard(deps, target.identity, binding) } : {}
1856
+ })));
1791
1857
  }
1792
1858
  function formatNativeGoal(response) {
1793
1859
  const goal = isJsonObject(response) && isJsonObject(response.goal) ? response.goal : void 0;
@@ -1803,26 +1869,30 @@ function formatNativeGoal(response) {
1803
1869
  ].join("\n");
1804
1870
  }
1805
1871
  async function stopConversationTurn(deps, ctx, pluginConfig) {
1806
- const target = await resolveControlTarget(ctx);
1872
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1873
+ const { target, binding } = authority;
1807
1874
  if (!target) return "Cannot stop Codex because this command did not include a stable binding identity.";
1808
1875
  return (await deps.stopCodexConversationTurn({
1809
1876
  identity: target.identity,
1810
- bindingStore: deps.bindingStore,
1877
+ binding,
1811
1878
  pluginConfig,
1812
1879
  agentDir: target.agentDir,
1813
- config: ctx.config
1880
+ config: ctx.config,
1881
+ assertCurrent: authority.assertCurrent
1814
1882
  })).message;
1815
1883
  }
1816
1884
  async function steerConversationTurn(deps, ctx, pluginConfig, message) {
1817
- const target = await resolveControlTarget(ctx);
1885
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1886
+ const { target, binding } = authority;
1818
1887
  if (!target) return "Cannot steer Codex because this command did not include a stable binding identity.";
1819
1888
  return (await deps.steerCodexConversationTurn({
1820
1889
  identity: target.identity,
1821
- bindingStore: deps.bindingStore,
1890
+ binding,
1822
1891
  message,
1823
1892
  pluginConfig,
1824
1893
  agentDir: target.agentDir,
1825
- config: ctx.config
1894
+ config: ctx.config,
1895
+ assertCurrent: authority.assertCurrent
1826
1896
  })).message;
1827
1897
  }
1828
1898
  async function setConversationModel(deps, ctx, pluginConfig, args) {
@@ -1830,17 +1900,18 @@ async function setConversationModel(deps, ctx, pluginConfig, args) {
1830
1900
  const [model = ""] = args;
1831
1901
  const normalized = model.trim();
1832
1902
  if (normalized && isCurrentSessionModelSelectionLocked(ctx)) return MODEL_SELECTION_LOCKED_MESSAGE;
1833
- const target = await resolveControlTarget(ctx);
1903
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1904
+ const { target, binding } = authority;
1834
1905
  if (!target) return "Cannot set Codex model because this command did not include a stable binding identity.";
1835
1906
  if (!normalized) {
1836
- const currentSession = ctx.sessionId && ctx.sessionKey ? getSessionEntry({
1837
- storePath: resolveStorePath(ctx.config.session?.store, { agentId: target.agentId }),
1838
- sessionKey: ctx.sessionKey,
1907
+ const currentSession = authority.sessionId && authority.sessionKey && authority.storePath ? getSessionEntry({
1908
+ storePath: authority.storePath,
1909
+ sessionKey: authority.sessionKey,
1839
1910
  hydrateSkillPromptRefs: false,
1840
1911
  readConsistency: "latest"
1841
1912
  }) : void 0;
1842
- const selectedModel = currentSession && currentSession.sessionId === ctx.sessionId ? currentSession.modelOverride ?? currentSession.model : void 0;
1843
- const binding = await deps.bindingStore.read(target.identity);
1913
+ const selectedModel = currentSession && currentSession.sessionId === authority.sessionId ? currentSession.modelOverride ?? currentSession.model : void 0;
1914
+ authority.assertCurrent();
1844
1915
  const activeModel = target.identity.kind === "conversation" ? binding?.model : selectedModel ?? binding?.model;
1845
1916
  return activeModel ? `Codex model: ${formatCodexDisplayText(activeModel)}` : "Usage: /codex model <model>";
1846
1917
  }
@@ -1850,12 +1921,16 @@ async function setConversationModel(deps, ctx, pluginConfig, args) {
1850
1921
  pluginConfig,
1851
1922
  model: normalized,
1852
1923
  agentDir: target.agentDir,
1853
- config: ctx.config
1924
+ config: ctx.config,
1925
+ binding,
1926
+ storePath: authority.storePath,
1927
+ assertCurrent: authority.assertCurrent
1854
1928
  });
1855
1929
  }
1856
1930
  async function setConversationFastMode(deps, ctx, args) {
1857
1931
  if (args.length > 1) return "Usage: /codex fast [on|off|status]";
1858
- const target = await resolveControlTarget(ctx);
1932
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1933
+ const { target, binding } = authority;
1859
1934
  if (!target) return "Cannot set Codex fast mode because this command did not include a stable binding identity.";
1860
1935
  const value = args[0];
1861
1936
  const parsed = parseCodexFastModeArg(value);
@@ -1863,12 +1938,15 @@ async function setConversationFastMode(deps, ctx, args) {
1863
1938
  return await deps.setCodexConversationFastMode({
1864
1939
  identity: target.identity,
1865
1940
  bindingStore: deps.bindingStore,
1866
- enabled: parsed
1941
+ binding,
1942
+ enabled: parsed,
1943
+ assertCurrent: authority.assertCurrent
1867
1944
  });
1868
1945
  }
1869
1946
  async function setConversationPermissions(deps, ctx, args) {
1870
1947
  if (args.length > 1) return "Usage: /codex permissions [default|yolo|status]";
1871
- const target = await resolveControlTarget(ctx);
1948
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1949
+ const { target } = authority;
1872
1950
  if (!target || !ctx.sessionId || !ctx.sessionKey) return "Cannot set Codex permissions because this command did not include a complete session identity.";
1873
1951
  const value = args[0];
1874
1952
  const parsed = parseCodexPermissionsModeArg(value);
@@ -1877,6 +1955,8 @@ async function setConversationPermissions(deps, ctx, args) {
1877
1955
  return await deps.setCodexConversationPermissions({
1878
1956
  mode: parsed,
1879
1957
  config: ctx.config,
1958
+ storePath: authority.storePath,
1959
+ assertCurrent: authority.assertHostCurrent,
1880
1960
  session: {
1881
1961
  agentId: target.agentId,
1882
1962
  sessionId: ctx.sessionId,
@@ -1886,30 +1966,26 @@ async function setConversationPermissions(deps, ctx, args) {
1886
1966
  }
1887
1967
  async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
1888
1968
  if (args.length > 0) return `Usage: /codex ${kind}`;
1889
- const target = await resolveControlTarget(ctx);
1969
+ const authority = await resolvePreparedCodexCommandAuthority(deps, ctx);
1970
+ const { target, binding } = authority;
1890
1971
  if (!target) return `Cannot start Codex ${kind === "compact" ? "compaction" : "review"} because this command did not include a stable binding identity.`;
1891
- const binding = await deps.bindingStore.read(target.identity);
1892
1972
  if (!binding?.threadId) return `No Codex thread is attached to this OpenClaw session yet.`;
1893
1973
  if (kind === "compact") {
1894
1974
  const sessionTarget = ctx.sessionTarget;
1895
1975
  if (!ctx.sessionId || !ctx.sessionKey || !sessionTarget || sessionTarget.sessionId !== ctx.sessionId || sessionTarget.sessionKey !== ctx.sessionKey) return "Codex compaction is unavailable because this command is not bound to a complete session identity.";
1896
1976
  const currentSession = getSessionEntry({
1897
- storePath: sessionTarget.storePath,
1977
+ storePath: authority.storePath ?? sessionTarget.storePath,
1898
1978
  sessionKey: ctx.sessionKey,
1899
1979
  hydrateSkillPromptRefs: false,
1900
1980
  readConsistency: "latest"
1901
1981
  });
1902
1982
  if (currentSession?.sessionId !== ctx.sessionId || resolvePersistedSessionRuntimeId(currentSession) !== "codex") return "Codex compaction is unavailable because the current OpenClaw session is not using the Codex runtime.";
1903
1983
  if (target.identity.kind === "conversation") {
1904
- if (!isSameCodexAppServerThreadOwner(binding, ctx.sessionId ? await deps.bindingStore.read(sessionBindingIdentity({
1905
- sessionId: ctx.sessionId,
1906
- sessionKey: ctx.sessionKey,
1907
- agentId: sessionTarget.agentId,
1908
- config: ctx.config
1909
- })) : void 0)) return "Codex compaction is unavailable because the conversation-bound thread differs from the current session binding. Resume that thread into the session first.";
1984
+ if (!isSameCodexAppServerThreadOwner(binding, authority.currentSessionBinding)) return "Codex compaction is unavailable because the conversation-bound thread differs from the current session binding. Resume that thread into the session first.";
1910
1985
  }
1911
1986
  const compactCurrent = ctx.runtimeContext?.compactCurrent;
1912
1987
  if (!compactCurrent) return "Codex compaction is unavailable because this command is not bound to a session.";
1988
+ authority.assertCurrent();
1913
1989
  const result = await compactCurrent();
1914
1990
  return result.compacted ? `Compacted Codex session (${result.tokensAfter ?? "unknown"} tokens after).` : `Codex compaction did not complete: ${formatCodexDisplayText(result.reason ?? "no reason returned")}.`;
1915
1991
  }
@@ -1918,20 +1994,39 @@ async function startThreadAction(deps, ctx, pluginConfig, kind, args) {
1918
1994
  authProfileId: binding.authProfileId,
1919
1995
  pluginConfig
1920
1996
  });
1921
- await deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.review, {
1997
+ await deps.bindingStore.withLease(target.identity, () => deps.codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.review, {
1922
1998
  threadId: binding.threadId,
1923
1999
  target: { type: "uncommittedChanges" }
1924
2000
  }, {
1925
2001
  agentDir: target.agentDir,
1926
2002
  authProfileId: connection.clientAuthProfileId,
1927
2003
  config: ctx.config,
1928
- ...connection.usesSupervisionConnection ? { startOptions: connection.appServer.start } : {}
1929
- });
2004
+ sessionId: authority.sessionId,
2005
+ sessionKey: authority.sessionKey,
2006
+ storePath: authority.storePath,
2007
+ assertCurrent: authority.assertCurrent,
2008
+ ...connection.usesSupervisionConnection ? {
2009
+ startOptions: connection.appServer.start,
2010
+ beforeRequest: supervisedCommandGuard(deps, target.identity, binding)
2011
+ } : {}
2012
+ }));
1930
2013
  return `Started Codex review for thread ${formatCodexDisplayText(binding.threadId)}.`;
1931
2014
  }
2015
+ function supervisedCommandGuard(deps, identity, binding) {
2016
+ return async (_request, client, scope) => {
2017
+ const { thread } = await client.request("thread/read", {
2018
+ threadId: binding.threadId,
2019
+ includeTurns: false
2020
+ });
2021
+ scope.assertCurrent();
2022
+ if (!isDeepStrictEqual(deps.bindingStore.read(identity), binding)) throw new Error("Codex command binding changed before model execution");
2023
+ scope.assertCurrent();
2024
+ assertCodexSupervisionThreadLineage(binding, thread);
2025
+ };
2026
+ }
1932
2027
  //#endregion
1933
2028
  //#region extensions/codex/src/command-plugins-management.ts
1934
- const POLICY_REFRESH_HINT = "New Codex conversations pick this up automatically. Use /new or /reset to refresh the current one.";
2029
+ const POLICY_REFRESH_HINT = "Takes effect on your next message.";
1935
2030
  async function handleCodexPluginsSubcommand(ctx, rest, io, runtime) {
1936
2031
  const [verb = "list", ...args] = rest;
1937
2032
  const normalized = verb.toLowerCase();
@@ -2290,7 +2385,7 @@ function formatPluginList(plugins, options = {}) {
2290
2385
  ...rows.map((r) => ` ${r.state} ${r.displayKey.padEnd(keyW)} ${r.pluginName.padEnd(pluginW)} [${r.marketplace}]`),
2291
2386
  "",
2292
2387
  ...globalEnabled ? [] : ["Global codexPlugins.enabled is off; configured sub-plugins are inactive.", ""],
2293
- "New Codex conversations pick up policy changes automatically; /new or /reset to refresh the current one."
2388
+ POLICY_REFRESH_HINT
2294
2389
  ].join("\n");
2295
2390
  }
2296
2391
  //#endregion