@openclaw/codex 2026.9.4 → 2026.9.6

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 (160) hide show
  1. package/assets/activity.svg +30 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/.setup/account-usage-runtime-Eem_WGqY.mjs +75 -0
  4. package/dist/{apply-CvT8tW5i.js → .setup/apply-DDOi8uNx.mjs} +24 -30
  5. package/dist/.setup/async-questions-DdhOCwGi.mjs +27 -0
  6. package/dist/{attempt-client-cleanup-DKHzbwt5.js → .setup/attempt-client-cleanup-CEv1cKwF.mjs} +79 -49
  7. package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +5 -0
  8. package/dist/.setup/attempt-workspace-context-DUVEn_CS.mjs +331 -0
  9. package/dist/{auth-binding-CWlKVccJ.js → .setup/auth-binding-DhLWa_7K.mjs} +1 -1
  10. package/dist/{auth-cache-key-7orQae_j.js → .setup/auth-cache-key-B2mpaI9D.mjs} +9 -81
  11. package/dist/{auth-profile-CwzO4_RG.js → .setup/auth-profile-WqZtZfXN.mjs} +1 -1
  12. package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
  13. package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
  14. package/dist/.setup/binding-connection-ThoatDcb.mjs +50 -0
  15. package/dist/.setup/binding-connection.runtime-yoHrABt-.mjs +48 -0
  16. package/dist/{bounded-turn-C_Hneydj.js → .setup/bounded-turn-B2HScEgT.mjs} +143 -32
  17. package/dist/{build-state-BCmg9-5p.js → .setup/build-state-C7EnDVgr.mjs} +1 -1
  18. package/dist/{capabilities-8vx68WLH.js → .setup/capabilities-CDXOOFdZ.mjs} +1 -1
  19. package/dist/{client-B57KWPQx.js → .setup/client-Cs08OXVQ.mjs} +705 -512
  20. package/dist/.setup/client-catalog-response-BMgt_4q4.mjs +285 -0
  21. package/dist/{command-dispatch-BpduNGVf.js → .setup/command-dispatch-B4JuOsOk.mjs} +8 -4
  22. package/dist/{command-formatters-lxlkNZgU.js → .setup/command-formatters-Bmqvr9tO.mjs} +8 -4
  23. package/dist/{command-handlers-tiL9iWj7.js → .setup/command-handlers-DW7Lx9oH.mjs} +137 -186
  24. package/dist/{command-rpc-CR5EOEZX.js → .setup/command-rpc-DixIj3HW.mjs} +47 -23
  25. package/dist/{compact-B_H3AOd7.js → .setup/compact-Du5UvtxA.mjs} +29 -51
  26. package/dist/{config-oIORaQ5T.js → .setup/config-BoTP_mrL.mjs} +5 -4
  27. package/dist/{config-options-CXMq1T-C.js → .setup/config-options-BvaRs51b.mjs} +50 -446
  28. package/dist/{protocol-5bh1G-H7.js → .setup/config-parsing-CcB9iPoq.mjs} +9 -51
  29. package/dist/{config-runtime-D0zGNIHu.js → .setup/config-runtime-C8Rw1m1m.mjs} +2 -2
  30. package/dist/.setup/config-security-BEReZ6go.mjs +380 -0
  31. package/dist/{conversation-binding-DsB4-O7U.js → .setup/conversation-binding-DP-gMrBP.mjs} +29 -38
  32. package/dist/{conversation-binding-data-BiodpIzq.js → .setup/conversation-binding-data-CrcyZk6O.mjs} +1 -1
  33. package/dist/{conversation-control-CtUu1b3m.js → .setup/conversation-control-uK9Jomlc.mjs} +32 -67
  34. package/dist/.setup/cyber-failover-DYwbmf1o.mjs +123 -0
  35. package/dist/.setup/cyber-failover-notice-e9G3J9-e.mjs +17 -0
  36. package/dist/{dynamic-tools-CmN5RAip.js → .setup/dynamic-tools-BDXl-8Cw.mjs} +936 -620
  37. package/dist/{effective-mcp-catalog-Bd73bqA8.js → .setup/effective-mcp-catalog-CvnvnGIe.mjs} +3 -3
  38. package/dist/{ephemeral-turn-D78_VmzO.js → .setup/ephemeral-turn-poidXgek.mjs} +9 -7
  39. package/dist/{event-projector-assistant-message-BmBM4qBV.js → .setup/event-projector-assistant-message-BsruvNrH.mjs} +8 -7
  40. package/dist/{event-projector-items-5VhsECCO.js → .setup/event-projector-items-C6L_5ZiT.mjs} +42 -8
  41. package/dist/{attempt-notifications-C9XufG6l.js → .setup/event-projector-usage-CnBS8Wa3.mjs} +102 -9
  42. package/dist/{harness-C7rRa7bm.js → .setup/harness-a_MsE4-_.mjs} +81 -21
  43. package/dist/{inference-context-Dr_mzvk4.js → .setup/inference-context-D96l9-ew.mjs} +1 -1
  44. package/dist/.setup/inference-proxy-b-aJMSxm.mjs +601 -0
  45. package/dist/{isolated-completion-DWVb5U2J.js → .setup/isolated-completion-BIZFhTNG.mjs} +18 -11
  46. package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
  47. package/dist/{managed-binary-D22rNEcR.js → .setup/managed-binary-BnshlFag.mjs} +14 -3
  48. package/dist/{managed-thread-store-D1t5jh3G.js → .setup/managed-thread-store-BMqgThVL.mjs} +48 -14
  49. package/dist/{model-catalog-Dq2n8f_p.js → .setup/model-catalog-CfXJf9Ay.mjs} +36 -12
  50. package/dist/{models-PCKOim-h.js → .setup/models-KnqBv0hE.mjs} +3 -3
  51. package/dist/.setup/native-auth-rWbGbMn3.mjs +57 -0
  52. package/dist/{native-subagent-history-DjtPloaU.js → .setup/native-subagent-history-0nYYgo0k.mjs} +43 -35
  53. package/dist/.setup/native-subagent-monitor-D3filLY2.mjs +4859 -0
  54. package/dist/{native-subagent-task-ids-BzTdT0Uw.js → .setup/native-subagent-task-ids-wb8CKZGN.mjs} +98 -44
  55. package/dist/{node-cli-sessions-DKaOmVUh.js → .setup/node-cli-sessions-r3avj02l.mjs} +86 -68
  56. package/dist/.setup/node-exec-server.runtime-BDwWXVpK.mjs +247 -0
  57. package/dist/{plan-7uG-Fp36.js → .setup/plan-CN3VwfNU.mjs} +133 -119
  58. package/dist/{plugin-activation-BYQculil.js → .setup/plugin-activation-DT3MNtqj.mjs} +7 -16
  59. package/dist/{plugin-app-cache-key-d0eKxy7b.js → .setup/plugin-app-cache-key-B2CsSdnV.mjs} +32 -44
  60. package/dist/{plugin-approval-roundtrip-Bz3FT8W5.js → .setup/plugin-approval-roundtrip-C0MZzCuP.mjs} +1 -1
  61. package/dist/{plugin-inventory-oL4Na1ZG.js → .setup/plugin-inventory-BoRei8Z4.mjs} +21 -11
  62. package/dist/{plugin-marketplace-discovery-P-Vndfqq.js → .setup/plugin-marketplace-discovery-CDOi8IFs.mjs} +4 -1
  63. package/dist/{plugin-metadata-cache-ClPJpRio.js → .setup/plugin-metadata-cache-Ctmo83k_.mjs} +226 -141
  64. package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
  65. package/dist/{provider-MQ8PVkuE.js → .setup/provider-BOsxQHOM.mjs} +9 -5
  66. package/dist/{provider-capabilities-BvCUL69A.js → .setup/provider-capabilities-BfE88FZ5.mjs} +31 -3
  67. package/dist/{rate-limits-CBMmWBr9.js → .setup/rate-limits-CMU3DUD0.mjs} +174 -151
  68. package/dist/{request-B7hEkBGq.js → .setup/request-D2L0zMrq.mjs} +130 -42
  69. package/dist/{run-attempt-CvsAAGa0.js → .setup/run-attempt-BHu11r7h.mjs} +1628 -966
  70. package/dist/.setup/run-attempt-lifecycle-B7JbY4-F.mjs +71 -0
  71. package/dist/{runtime-artifact-BJ0rXBZ_.js → .setup/runtime-artifact-CLZ8udh-.mjs} +29 -45
  72. package/dist/{sandbox-guard-BxgyoM8M.js → .setup/sandbox-guard-C2sRMOEY.mjs} +3 -3
  73. package/dist/{session-binding-CI8UuilT.js → .setup/session-binding-Cm0apEbd.mjs} +36 -83
  74. package/dist/{session-binding-record-Bcvslnhf.js → .setup/session-binding-record-BGoz8wOK.mjs} +202 -5
  75. package/dist/.setup/session-binding-sidecars-BlB5EvIS.mjs +546 -0
  76. package/dist/.setup/session-catalog-Pno1xGcl.mjs +2764 -0
  77. package/dist/{session-catalog-adoption-tcZi1IfX.js → .setup/session-catalog-adoption-BI6UTDZT.mjs} +58 -26
  78. package/dist/{session-catalog-archive-CwKg8Xz2.js → .setup/session-catalog-archive-lHBqzhaO.mjs} +6 -6
  79. package/dist/.setup/session-catalog-events-Bj6j94E_.mjs +347 -0
  80. package/dist/.setup/session-catalog-index-B9_VQIUz.mjs +1170 -0
  81. package/dist/.setup/session-catalog-index-order-hcdz07yN.mjs +48 -0
  82. package/dist/.setup/session-catalog-index-state-Dgh8ORJm.mjs +183 -0
  83. package/dist/{thread-history-page-D0JIp48V.js → .setup/session-catalog-native-projection-DowriLid.mjs} +223 -95
  84. package/dist/{session-catalog-node-adoption-B1tgDW-m.js → .setup/session-catalog-node-adoption-ITddWx_X.mjs} +25 -13
  85. package/dist/{session-catalog-node-continue-Ci-DXyJP.js → .setup/session-catalog-node-continue-6g6qi4A9.mjs} +24 -15
  86. package/dist/.setup/session-catalog-projection-zK_PUsiE.mjs +422 -0
  87. package/dist/{session-cli-DNN_jhGc.js → .setup/session-cli-irrD-uo7.mjs} +3 -3
  88. package/dist/{session-history-Cz-tOOYj.js → .setup/session-history-BrqhSCf9.mjs} +19 -19
  89. package/dist/{session-history-import-DwyijFI6.js → .setup/session-history-import-D-3wAgOP.mjs} +4 -4
  90. package/dist/{session-history-read-cxPUxyVV.js → .setup/session-history-read-B3ZfSUbF.mjs} +2 -2
  91. package/dist/{session-history.worker-BeK1YOyT.js → .setup/session-history.worker-CaCh0xF_.mjs} +10 -24
  92. package/dist/{session-initialization-Ct0_Hzmg.js → .setup/session-initialization-rcgbZiid.mjs} +2 -2
  93. package/dist/{session-permission-policy-BBmXA8Eg.js → .setup/session-permission-policy-BI1QK_98.mjs} +2 -2
  94. package/dist/{session-retirement-B2YfbAAT.js → .setup/session-retirement-BH-S6qkJ.mjs} +26 -7
  95. package/dist/{settled-turn-context-Du6gdUDd.js → .setup/settled-turn-context-fmwouT5I.mjs} +2 -2
  96. package/dist/{settled-turn-finalizer-Dn91stmc.js → .setup/settled-turn-finalizer-DiAi6Kx6.mjs} +12 -12
  97. package/dist/{shared-client-CscigXXL.js → .setup/shared-client-DA4VR4Eb.mjs} +332 -221
  98. package/dist/{side-question-BGMJQf4U.js → .setup/side-question-BJ1wSDcB.mjs} +183 -230
  99. package/dist/{source-CElXU0Oy.js → .setup/source-CUzJq5WN.mjs} +11 -10
  100. package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
  101. package/dist/{thread-archive-guard-DfpBr-Sz.js → .setup/thread-archive-guard-C9frICzX.mjs} +10 -3
  102. package/dist/.setup/thread-history-page-DoVpnHim.mjs +83 -0
  103. package/dist/{thread-lifecycle-DXl1-I6b.js → .setup/thread-lifecycle-B3mfz1TG.mjs} +347 -2318
  104. package/dist/{thread-ownership-BsDRTsyp.js → .setup/thread-ownership-DcTtAcXj.mjs} +6 -4
  105. package/dist/{thread-policy-BuEQfE0g.js → .setup/thread-policy-DGuQsuFC.mjs} +19 -3
  106. package/dist/.setup/thread-requests-BLvGkP2R.mjs +2205 -0
  107. package/dist/{thread-resume-DsDOZpFg.js → .setup/thread-resume-DIBXwOpE.mjs} +8 -5
  108. package/dist/.setup/timeout-C910MdAB.mjs +80 -0
  109. package/dist/{transcript-history-projection-v17wXgdK.js → .setup/transcript-history-projection-ZZpkFkJ_.mjs} +96 -61
  110. package/dist/{transcript-mirror-C5CKgNy6.js → .setup/transcript-mirror-BtHDBp1G.mjs} +224 -195
  111. package/dist/{transcript-mirror-attestation-DibE0krH.js → .setup/transcript-mirror-attestation-CzM-94NC.mjs} +4 -1
  112. package/dist/{transport-stdio-BGjHoYLr.js → .setup/transport-stdio-h2VHOskC.mjs} +274 -89
  113. package/dist/{upstream-session-fork-BZerMte_.js → .setup/upstream-session-fork-BUr4OUbC.mjs} +51 -42
  114. package/dist/{usage-tCk-mg_x.js → .setup/usage-BML0r-ua.mjs} +5 -4
  115. package/dist/.setup/usage-limit-error-DUMbefVY.mjs +166 -0
  116. package/dist/{web-search-provider.runtime-B7bGbGWq.js → .setup/web-search-provider.runtime-CDKlN-Rv.mjs} +4 -3
  117. package/dist/api.js +23 -12
  118. package/dist/catalog-page-worker-entrypoint.js +12 -0
  119. package/dist/catalog-page.worker.js +9 -0
  120. package/dist/cli-metadata.js +1 -1
  121. package/dist/doctor-contract-api.js +8 -528
  122. package/dist/harness.js +1 -1
  123. package/dist/index.js +306 -215
  124. package/dist/media-understanding-provider.js +3 -3
  125. package/dist/migration-provider-api.js +1 -1
  126. package/dist/native-tool-policy.js +12 -0
  127. package/dist/provider-discovery.js +18 -0
  128. package/dist/session-history-worker-runtime.js +36 -35
  129. package/dist/session-history.worker.js +2 -2
  130. package/dist/web-search-contract-api.js +1 -1
  131. package/openclaw.plugin.json +354 -69
  132. package/package.json +14 -8
  133. package/dist/attempt-context-D44yeDII.js +0 -636
  134. package/dist/binding-connection-D3udKbIE.js +0 -60
  135. package/dist/event-projector-usage-BfXmpOec.js +0 -102
  136. package/dist/inference-proxy-HGXKUIre.js +0 -311
  137. package/dist/launch-args-RXQwn8zV.js +0 -90
  138. package/dist/native-subagent-history-owner-Pcp7JuVF.js +0 -34
  139. package/dist/native-subagent-monitor-fQ7eIXEf.js +0 -2978
  140. package/dist/node-exec-server.runtime-Cl2vapEi.js +0 -226
  141. package/dist/rpc-error-ttN_QjEm.js +0 -30
  142. package/dist/session-catalog-ct-AH01I.js +0 -1477
  143. package/dist/session-catalog-home-id-CwhLdC03.js +0 -18
  144. package/dist/session-catalog-provenance-D-dIfRAH.js +0 -96
  145. package/dist/timeout-BVw8aaM1.js +0 -33
  146. package/dist/version-omm6IPI3.js +0 -12
  147. /package/dist/{auth-profile-selection-DFu9e167.js → .setup/auth-profile-selection-DFu9e167.mjs} +0 -0
  148. /package/dist/{config-utils-DujwEnhg.js → .setup/config-utils-DujwEnhg.mjs} +0 -0
  149. /package/dist/{context-compaction-activity-CMla5FPJ.js → .setup/context-compaction-activity-CMla5FPJ.mjs} +0 -0
  150. /package/dist/{elicitation-input-DPoK8F7w.js → .setup/elicitation-input-DPoK8F7w.mjs} +0 -0
  151. /package/dist/{history-rejection-B4nOXNXF.js → .setup/history-rejection-B4nOXNXF.mjs} +0 -0
  152. /package/dist/{image-payload-sanitizer-tupAr-2o.js → .setup/image-payload-sanitizer-tupAr-2o.mjs} +0 -0
  153. /package/dist/{incognito-session-uhrBF6wJ.js → .setup/incognito-session-uhrBF6wJ.mjs} +0 -0
  154. /package/dist/{local-runtime-attribution-B_2mnqhb.js → .setup/local-runtime-attribution-B_2mnqhb.mjs} +0 -0
  155. /package/dist/{native-execution-policy-umd0tgh4.js → .setup/native-execution-policy-umd0tgh4.mjs} +0 -0
  156. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  157. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  158. /package/dist/{session-upstream-marker-D15C9NHp.js → .setup/session-upstream-marker-D15C9NHp.mjs} +0 -0
  159. /package/dist/{upstream-prompt-provenance-LphB8slG.js → .setup/upstream-prompt-provenance-LphB8slG.mjs} +0 -0
  160. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
@@ -1,8 +1,8 @@
1
- import { c as CatalogParamsError } from "./thread-history-page-D0JIp48V.js";
1
+ import { v as CatalogParamsError } from "./session-catalog-native-projection-DowriLid.mjs";
2
2
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
4
- import { resolveStorePath } from "openclaw/plugin-sdk/session-store-paths";
5
4
  import { listSessionCatalogEntries, sessionCatalogAdoptedSessionKey, sessionCatalogAdoptedSourceKey } from "openclaw/plugin-sdk/session-catalog";
5
+ import { resolveStorePath } from "openclaw/plugin-sdk/session-store-paths";
6
6
  //#region extensions/codex/src/session-catalog-node-adoption.ts
7
7
  const CODEX_NODE_SESSION_KEY_PREFIX = "harness:codex:node-session:";
8
8
  const continueOperations = /* @__PURE__ */ new Map();
@@ -21,17 +21,25 @@ function adoptionSessionKeyRest(sessionKey) {
21
21
  const trimmed = sessionKey.trim();
22
22
  return parseAgentSessionKey(trimmed)?.rest ?? trimmed;
23
23
  }
24
- function nodeAdoptionSessionKey(hostId, threadId) {
25
- const source = JSON.stringify([hostId, threadId]);
24
+ function nodeAdoptedSourceKey(hostId, threadId, sourceHomeId) {
25
+ return sessionCatalogAdoptedSourceKey(JSON.stringify([hostId, sourceHomeId]), threadId);
26
+ }
27
+ function nodeAdoptionSessionKey(hostId, threadId, sourceHomeId) {
28
+ const source = JSON.stringify([
29
+ hostId,
30
+ sourceHomeId,
31
+ threadId
32
+ ]);
26
33
  return sessionCatalogAdoptedSessionKey(CODEX_NODE_SESSION_KEY_PREFIX, source);
27
34
  }
28
35
  function readNodeSessionMarker(entry) {
29
36
  const codex = isRecord(entry.pluginExtensions?.codex) ? entry.pluginExtensions.codex : void 0;
30
37
  const marker = codex && isRecord(codex.sessionCatalog) ? codex.sessionCatalog : void 0;
31
- if (!marker || typeof marker.sourceHostId !== "string" || !marker.sourceHostId.startsWith("node:") || typeof marker.sourceThreadId !== "string" || !marker.sourceThreadId.trim() || typeof marker.nodeId !== "string" || !marker.nodeId.trim()) return;
38
+ if (!marker || typeof marker.sourceHostId !== "string" || !marker.sourceHostId.startsWith("node:") || typeof marker.sourceThreadId !== "string" || !marker.sourceThreadId.trim() || typeof marker.nodeId !== "string" || !marker.nodeId.trim() || marker.sourceHomeId !== void 0 && (typeof marker.sourceHomeId !== "string" || !marker.sourceHomeId.trim() || marker.sourceHomeId.length > 256)) return;
32
39
  return {
33
40
  sourceHostId: marker.sourceHostId,
34
41
  sourceThreadId: marker.sourceThreadId,
42
+ ...typeof marker.sourceHomeId === "string" ? { sourceHomeId: marker.sourceHomeId } : {},
35
43
  nodeId: marker.nodeId,
36
44
  ...marker.initializing === true ? { initializing: true } : {}
37
45
  };
@@ -46,8 +54,8 @@ function listNodeAdoptedSessionEntries(params) {
46
54
  })) {
47
55
  const marker = readNodeSessionMarker(entry);
48
56
  const sessionId = entry.sessionId?.trim();
49
- if (!marker || marker.initializing === true && params.includeInitializing !== true || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !sessionId || adoptionSessionKeyRest(sessionKey) !== nodeAdoptionSessionKey(marker.sourceHostId, marker.sourceThreadId) || marker.sourceHostId !== `node:${marker.nodeId}`) continue;
50
- const sourceKey = sessionCatalogAdoptedSourceKey(marker.sourceHostId, marker.sourceThreadId);
57
+ if (!marker || !marker.sourceHomeId || marker.initializing === true && params.includeInitializing !== true || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !sessionId || adoptionSessionKeyRest(sessionKey) !== nodeAdoptionSessionKey(marker.sourceHostId, marker.sourceThreadId, marker.sourceHomeId) || marker.sourceHostId !== `node:${marker.nodeId}`) continue;
58
+ const sourceKey = nodeAdoptedSourceKey(marker.sourceHostId, marker.sourceThreadId, marker.sourceHomeId);
51
59
  if (adopted.has(sourceKey)) throw new Error(`multiple OpenClaw sessions adopt Codex thread ${marker.sourceThreadId} on ${marker.sourceHostId}`);
52
60
  adopted.set(sourceKey, {
53
61
  key: sessionKey,
@@ -59,12 +67,13 @@ function listNodeAdoptedSessionEntries(params) {
59
67
  return adopted;
60
68
  }
61
69
  function findNodeAdoptedSessionEntry(params) {
62
- return listNodeAdoptedSessionEntries(params).get(sessionCatalogAdoptedSourceKey(params.hostId, params.threadId));
70
+ return listNodeAdoptedSessionEntries(params).get(nodeAdoptedSourceKey(params.hostId, params.threadId, params.sourceHomeId));
63
71
  }
64
72
  function nodeSessionMarker(params) {
65
73
  return {
66
74
  sourceHostId: params.hostId,
67
75
  sourceThreadId: params.threadId,
76
+ sourceHomeId: params.sourceHomeId,
68
77
  nodeId: params.nodeId,
69
78
  ...params.initializing === true ? { initializing: true } : {}
70
79
  };
@@ -79,7 +88,7 @@ async function finalizeNodeAdoptedSession(params) {
79
88
  preserveActivity: true,
80
89
  update: (entry) => {
81
90
  const current = readNodeSessionMarker(entry);
82
- if (entry.sessionId?.trim() !== params.adopted.sessionId || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !current || current.sourceHostId !== params.marker.sourceHostId || current.sourceThreadId !== params.marker.sourceThreadId || current.nodeId !== params.marker.nodeId) throw changedError();
91
+ if (entry.sessionId?.trim() !== params.adopted.sessionId || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !current || current.sourceHostId !== params.marker.sourceHostId || current.sourceThreadId !== params.marker.sourceThreadId || current.sourceHomeId !== params.marker.sourceHomeId || current.nodeId !== params.marker.nodeId) throw changedError();
83
92
  if (current.initializing !== true) return {
84
93
  archivedAt: void 0,
85
94
  archivedBy: void 0,
@@ -106,7 +115,7 @@ async function finalizeNodeAdoptedSession(params) {
106
115
  readConsistency: "latest"
107
116
  });
108
117
  const current = currentEntry ? readNodeSessionMarker(currentEntry) : void 0;
109
- if (currentEntry?.sessionId?.trim() === params.adopted.sessionId && current?.initializing !== true && current?.sourceHostId === params.marker.sourceHostId && current.sourceThreadId === params.marker.sourceThreadId && current.nodeId === params.marker.nodeId) return;
118
+ if (currentEntry?.sessionId?.trim() === params.adopted.sessionId && current?.initializing !== true && current?.sourceHostId === params.marker.sourceHostId && current.sourceThreadId === params.marker.sourceThreadId && current.sourceHomeId === params.marker.sourceHomeId && current.nodeId === params.marker.nodeId) return;
110
119
  throw error;
111
120
  }
112
121
  if (!finalized) throw changedError();
@@ -118,6 +127,7 @@ async function createOrReuseNodeAdoptedSession(params) {
118
127
  runtime: params.api.runtime,
119
128
  hostId: params.hostId,
120
129
  threadId: params.record.threadId,
130
+ sourceHomeId: params.sourceHomeId,
121
131
  includeInitializing: true
122
132
  });
123
133
  if (existing) return existing;
@@ -125,6 +135,7 @@ async function createOrReuseNodeAdoptedSession(params) {
125
135
  ...nodeSessionMarker({
126
136
  hostId: params.hostId,
127
137
  threadId: params.record.threadId,
138
+ sourceHomeId: params.sourceHomeId,
128
139
  nodeId: params.nodeId
129
140
  }),
130
141
  initializing: true
@@ -132,7 +143,7 @@ async function createOrReuseNodeAdoptedSession(params) {
132
143
  try {
133
144
  const created = await params.api.runtime.agent.session.createSessionEntry({
134
145
  cfg: params.config,
135
- key: nodeAdoptionSessionKey(params.hostId, params.record.threadId),
146
+ key: nodeAdoptionSessionKey(params.hostId, params.record.threadId, params.sourceHomeId),
136
147
  agentId: params.agentId,
137
148
  recoverMatchingInitialEntry: true,
138
149
  displayName: params.record.name ?? void 0,
@@ -144,7 +155,7 @@ async function createOrReuseNodeAdoptedSession(params) {
144
155
  },
145
156
  afterCreate: async (entry) => {
146
157
  const storePath = resolveStorePath(params.config.session?.store, { agentId: entry.agentId });
147
- const { importCodexThreadHistoryToTranscript } = await import("./transcript-mirror-C5CKgNy6.js").then((n) => n.a);
158
+ const { importCodexThreadHistoryToTranscript } = await import("./transcript-mirror-BtHDBp1G.mjs").then((n) => n.a);
148
159
  await importCodexThreadHistoryToTranscript({
149
160
  thread: params.history.thread,
150
161
  throughTurnId: params.history.throughTurnId,
@@ -172,6 +183,7 @@ async function createOrReuseNodeAdoptedSession(params) {
172
183
  runtime: params.api.runtime,
173
184
  hostId: params.hostId,
174
185
  threadId: params.record.threadId,
186
+ sourceHomeId: params.sourceHomeId,
175
187
  includeInitializing: true
176
188
  });
177
189
  if (raced) return raced;
@@ -179,4 +191,4 @@ async function createOrReuseNodeAdoptedSession(params) {
179
191
  }
180
192
  }
181
193
  //#endregion
182
- export { adoptionSessionKeyRest, continueOperations, createOrReuseNodeAdoptedSession, finalizeNodeAdoptedSession, findNodeAdoptedSessionEntry, listNodeAdoptedSessionEntries, nodeSessionMarker, runSessionActionExclusive };
194
+ export { CODEX_NODE_SESSION_KEY_PREFIX, adoptionSessionKeyRest, continueOperations, createOrReuseNodeAdoptedSession, finalizeNodeAdoptedSession, findNodeAdoptedSessionEntry, listNodeAdoptedSessionEntries, nodeAdoptedSourceKey, nodeSessionMarker, readNodeSessionMarker, runSessionActionExclusive };
@@ -1,10 +1,10 @@
1
- import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-BiodpIzq.js";
2
- import { t as CODEX_CLI_SESSION_RESUME_COMMAND } from "./node-cli-sessions-DKaOmVUh.js";
3
- import { a as lookupNodeCodexCatalogRecord } from "./session-catalog-ct-AH01I.js";
4
- import { D as unwrapNodeInvokePayload, _ as parseCatalogPage, a as CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, c as CatalogParamsError, d as NODE_INVOKE_TIMEOUT_MS, f as boundedCatalogString, h as isInteractiveThreadSource, i as CODEX_APP_SERVER_THREADS_LIST_COMMAND, m as filterCatalogPageByTitle, p as catalogError, y as parseTranscriptPage } from "./thread-history-page-D0JIp48V.js";
5
- import { t as codexLastTerminalTurnId } from "./session-upstream-marker-D15C9NHp.js";
6
- import { n as withTimeout } from "./timeout-BVw8aaM1.js";
7
- import { createOrReuseNodeAdoptedSession, finalizeNodeAdoptedSession, findNodeAdoptedSessionEntry, nodeSessionMarker, runSessionActionExclusive } from "./session-catalog-node-adoption-B1tgDW-m.js";
1
+ import { A as parseCatalogPage, C as boundedCatalogString, D as filterCatalogPageByTitle, H as unwrapNodeInvokePayload, M as parseTranscriptPage, O as isInteractiveThreadSource, S as NODE_INVOKE_TIMEOUT_MS, h as CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, m as CODEX_APP_SERVER_THREADS_LIST_COMMAND, v as CatalogParamsError, w as catalogError } from "./session-catalog-native-projection-DowriLid.mjs";
2
+ import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-CrcyZk6O.mjs";
3
+ import { i as withTimeout } from "./timeout-C910MdAB.mjs";
4
+ import { r as CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE, t as CODEX_CLI_SESSION_RESUME_COMMAND } from "./node-cli-sessions-r3avj02l.mjs";
5
+ import { o as lookupNodeCodexCatalogRecord } from "./session-catalog-Pno1xGcl.mjs";
6
+ import { t as codexLastTerminalTurnId } from "./session-upstream-marker-D15C9NHp.mjs";
7
+ import { createOrReuseNodeAdoptedSession, finalizeNodeAdoptedSession, findNodeAdoptedSessionEntry, nodeAdoptedSourceKey, nodeSessionMarker, runSessionActionExclusive } from "./session-catalog-node-adoption-ITddWx_X.mjs";
8
8
  import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
9
9
  import { createSessionCatalogAdoptionCoordinator, publishSessionCatalogHost, sessionCatalogAdoptedSourceKey } from "openclaw/plugin-sdk/session-catalog";
10
10
  //#region extensions/codex/src/session-catalog-node-continue.ts
@@ -26,7 +26,7 @@ function compareNodeLabels(left, right) {
26
26
  return 0;
27
27
  }
28
28
  function canContinueCodexOnNode(node) {
29
- return node.connected === true && CODEX_NODE_CONTINUE_COMMANDS.every((command) => node.commands?.includes(command) === true && node.invocableCommands?.includes(command) === true);
29
+ return node.connected === true && node.caps?.includes("codex-cli-session-source") === true && CODEX_NODE_CONTINUE_COMMANDS.every((command) => node.commands?.includes(command) === true && node.invocableCommands?.includes(command) === true);
30
30
  }
31
31
  async function listPairedNode(params) {
32
32
  const hostId = `node:${params.node.nodeId}`;
@@ -79,10 +79,8 @@ async function listPairedNode(params) {
79
79
  ...common,
80
80
  connected: true,
81
81
  ...page,
82
- sessions: page.sessions.map((session) => {
83
- const adopted = params.adoptedSessions.get(sessionCatalogAdoptedSourceKey(hostId, session.threadId));
84
- return adopted ? Object.assign({}, session, { sessionKey: adopted.key }) : session;
85
- })
82
+ canContinueCodex: common.canContinueCodex && page.canContinueCodex === true && Boolean(page.sourceHomeId),
83
+ sessions: page.sessions.map((session) => Object.assign({}, session, page.sourceHomeId ? { sourceHomeId: page.sourceHomeId } : {}))
86
84
  };
87
85
  }).catch((error) => ({
88
86
  ...common,
@@ -106,7 +104,10 @@ async function requireNodeForCodexContinue(params) {
106
104
  const nodeId = params.hostId.slice(5).trim();
107
105
  if (!nodeId || params.hostId !== `node:${nodeId}`) throw new CatalogParamsError("Codex session catalog hostId is invalid");
108
106
  const node = (await params.runtime.nodes.list()).nodes.find((candidate) => candidate.nodeId === nodeId);
109
- if (!node || !canContinueCodexOnNode(node)) throw new CatalogParamsError("paired node does not permit Codex session continuation");
107
+ if (!node || !canContinueCodexOnNode(node)) {
108
+ if (node?.connected && !node.caps?.includes("codex-cli-session-source")) throw new CatalogParamsError(CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE);
109
+ throw new CatalogParamsError("paired node does not permit Codex session continuation");
110
+ }
110
111
  return {
111
112
  node,
112
113
  nodeId
@@ -125,6 +126,7 @@ async function readNodeCodexHistory(params) {
125
126
  command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND,
126
127
  params: {
127
128
  agentId: params.agentId,
129
+ sourceHomeId: params.sourceHomeId,
128
130
  threadId: params.record.threadId,
129
131
  limit: 50
130
132
  },
@@ -153,9 +155,12 @@ async function continueNodeCodexSessionInner(params) {
153
155
  agentId: params.agentId,
154
156
  runtime: params.api.runtime,
155
157
  nodeId,
156
- threadId: params.threadId
158
+ threadId: params.threadId,
159
+ sourceHomeId: params.sourceHomeId
157
160
  });
158
161
  if (lookup.kind !== "found") throw new CatalogParamsError(lookup.kind === "cursor-cycle" ? "Codex session eligibility could not be verified" : "Codex session is unavailable on the paired node");
162
+ if (lookup.canContinueCodex !== true || !lookup.sourceHomeId) throw new CatalogParamsError("Codex session source does not support Chat continuation; use a local stdio source or update the node.");
163
+ const sourceHomeId = lookup.sourceHomeId;
159
164
  const record = lookup.record;
160
165
  requireContinuableNodeRecord(record);
161
166
  const existing = findNodeAdoptedSessionEntry({
@@ -164,6 +169,7 @@ async function continueNodeCodexSessionInner(params) {
164
169
  runtime: params.api.runtime,
165
170
  hostId: params.hostId,
166
171
  threadId: params.threadId,
172
+ sourceHomeId,
167
173
  includeInitializing: true
168
174
  });
169
175
  let adopted;
@@ -176,6 +182,7 @@ async function continueNodeCodexSessionInner(params) {
176
182
  agentId: params.agentId,
177
183
  runtime: params.api.runtime,
178
184
  nodeId,
185
+ sourceHomeId,
179
186
  record
180
187
  });
181
188
  adopted = await createOrReuseNodeAdoptedSession({
@@ -184,6 +191,7 @@ async function continueNodeCodexSessionInner(params) {
184
191
  config: params.config,
185
192
  hostId: params.hostId,
186
193
  nodeId,
194
+ sourceHomeId,
187
195
  record,
188
196
  history
189
197
  });
@@ -192,6 +200,7 @@ async function continueNodeCodexSessionInner(params) {
192
200
  const marker = nodeSessionMarker({
193
201
  hostId: params.hostId,
194
202
  threadId: params.threadId,
203
+ sourceHomeId,
195
204
  nodeId
196
205
  });
197
206
  return {
@@ -223,7 +232,7 @@ async function continueNodeCodexSession(params) {
223
232
  agentId: params.agentId
224
233
  }).sessionAgentId;
225
234
  const sourceKey = sessionCatalogAdoptedSourceKey(`node:${nodeId}`, params.threadId);
226
- const operationKey = sessionCatalogAdoptedSourceKey(agentId, sourceKey);
235
+ const operationKey = sessionCatalogAdoptedSourceKey(agentId, params.sourceHomeId ? nodeAdoptedSourceKey(`node:${nodeId}`, params.threadId, params.sourceHomeId) : sourceKey);
227
236
  return await continueNodeAdoption({
228
237
  sourceKey: operationKey,
229
238
  findExisting: () => void 0,
@@ -0,0 +1,422 @@
1
+ import { B as toCatalogSession, C as boundedCatalogString, P as readControlCursor, V as truncateCodexCatalogPreview, W as detachCodexCatalogString, c as copyCodexCatalogSource, f as setCodexCatalogSource, l as getCodexCatalogSource, r as projectCodexCatalogNativeThread, z as selectCodexCatalogPreviewInput } from "./session-catalog-native-projection-DowriLid.mjs";
2
+ import { a as isOpenClawManagedCodexThread } from "./session-catalog-events-Bj6j94E_.mjs";
3
+ import { n as codexCatalogRowRecency } from "./session-catalog-index-order-hcdz07yN.mjs";
4
+ import { asFiniteNumber, isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import path from "node:path";
6
+ import fs from "node:fs/promises";
7
+ import { root } from "openclaw/plugin-sdk/file-access-runtime";
8
+ import { extractErrorCode, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
9
+ import { constants, createZstdDecompress } from "node:zlib";
10
+ import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
11
+ //#region extensions/codex/src/session-catalog-rollouts.ts
12
+ const WINDOW_BYTES = 131072;
13
+ const DIRECTORY_PARTS = [
14
+ /^\d{4}$/,
15
+ /^(?:0[1-9]|1[0-2])$/,
16
+ /^(?:0[1-9]|[12]\d|3[01])$/
17
+ ];
18
+ const ROLLOUT_FILE_NAME = /\.jsonl(?:\.zst)?$/;
19
+ const ROLLOUT_SOURCE_DENIAL_CODES = /* @__PURE__ */ new Set([
20
+ "symlink",
21
+ "hardlink",
22
+ "outside-workspace",
23
+ "invalid-path",
24
+ "device-path",
25
+ "not-file",
26
+ "path-alias"
27
+ ]);
28
+ function compareRolloutCandidates(left, right) {
29
+ return left[1].mtimeMs - right[1].mtimeMs || (left[0] < right[0] ? -1 : left[0] > right[0] ? 1 : 0);
30
+ }
31
+ /** Retain only the newest fingerprints while streaming an arbitrarily large home. */
32
+ var NewestRolloutCandidates = class {
33
+ constructor() {
34
+ this.heap = [];
35
+ }
36
+ offer(candidate) {
37
+ if (this.heap.length < 2e4) {
38
+ let childIndex = this.heap.length;
39
+ this.heap.push(candidate);
40
+ while (childIndex > 0) {
41
+ const parentIndex = Math.floor((childIndex - 1) / 2);
42
+ const parent = this.heap[parentIndex];
43
+ if (!parent || compareRolloutCandidates(parent, candidate) <= 0) break;
44
+ this.heap[childIndex] = parent;
45
+ childIndex = parentIndex;
46
+ }
47
+ this.heap[childIndex] = candidate;
48
+ return;
49
+ }
50
+ const oldest = this.heap[0];
51
+ if (!oldest || compareRolloutCandidates(candidate, oldest) <= 0) return;
52
+ let parentIndex = 0;
53
+ while (true) {
54
+ let childIndex = parentIndex * 2 + 1;
55
+ let child = this.heap[childIndex];
56
+ if (!child) break;
57
+ const right = this.heap[childIndex + 1];
58
+ if (right && compareRolloutCandidates(right, child) < 0) {
59
+ child = right;
60
+ childIndex++;
61
+ }
62
+ if (compareRolloutCandidates(candidate, child) <= 0) break;
63
+ this.heap[parentIndex] = child;
64
+ parentIndex = childIndex;
65
+ }
66
+ this.heap[parentIndex] = candidate;
67
+ }
68
+ finish() {
69
+ this.heap.sort((left, right) => compareRolloutCandidates(right, left));
70
+ return new Map(this.heap);
71
+ }
72
+ };
73
+ /** Codex returns the plain logical path for either rollout representation. */
74
+ function codexCatalogRolloutLogicalPath(rolloutPath) {
75
+ return rolloutPath.replace(/\.zst$/u, "");
76
+ }
77
+ function resolveCodexCatalogRolloutFingerprint(rolloutPath, previous, files) {
78
+ const logicalPath = rolloutPath && codexCatalogRolloutLogicalPath(rolloutPath);
79
+ if (!logicalPath) return;
80
+ return files?.get(logicalPath) ?? files?.get(`${logicalPath}.zst`) ?? (previous?.rolloutPath && codexCatalogRolloutLogicalPath(previous.rolloutPath) === logicalPath ? previous.fingerprint : void 0);
81
+ }
82
+ function indexCodexCatalogRowsByRollout(rows) {
83
+ return new Map([...rows].flatMap((row) => row.rolloutPath ? [[codexCatalogRolloutLogicalPath(row.rolloutPath), row]] : []));
84
+ }
85
+ /** Absence is meaningful only inside the layout visited by the currency scan. */
86
+ function isCodexCatalogRolloutPathCovered(sessionsRoot, rolloutPath) {
87
+ const directories = path.relative(sessionsRoot, rolloutPath).split(path.sep);
88
+ const fileName = directories.pop();
89
+ return fileName !== void 0 && ROLLOUT_FILE_NAME.test(fileName) && directories.length === DIRECTORY_PARTS.length && directories.every((part, index) => DIRECTORY_PARTS[index]?.test(part) === true);
90
+ }
91
+ async function unlessMissing(operation) {
92
+ try {
93
+ return await operation;
94
+ } catch (error) {
95
+ if (isRecord(error) && error.code === "ENOENT") return;
96
+ throw error;
97
+ }
98
+ }
99
+ /** Stat-only currency scan. Content is read separately, only for changed fingerprints. */
100
+ async function scanCodexCatalogRollouts(sessionsRoot, trackedPaths) {
101
+ const present = /* @__PURE__ */ new Set();
102
+ const rootStat = await unlessMissing(fs.lstat(sessionsRoot));
103
+ if (!rootStat?.isDirectory() || rootStat.isSymbolicLink()) return {
104
+ files: /* @__PURE__ */ new Map(),
105
+ present
106
+ };
107
+ const rootReal = await unlessMissing(fs.realpath(sessionsRoot));
108
+ if (!rootReal) return {
109
+ files: /* @__PURE__ */ new Map(),
110
+ present
111
+ };
112
+ const candidates = new NewestRolloutCandidates();
113
+ const scan = async (directory, depth) => {
114
+ if (await unlessMissing(fs.realpath(directory)) !== directory) return;
115
+ const entries = await unlessMissing(fs.opendir(directory));
116
+ if (!entries) return;
117
+ const directoryPattern = DIRECTORY_PARTS[depth];
118
+ for await (const entry of entries) {
119
+ const file = path.join(directory, entry.name);
120
+ if (directoryPattern) {
121
+ if (entry.isDirectory() && directoryPattern.test(entry.name)) await scan(file, depth + 1);
122
+ } else if (entry.isFile() && ROLLOUT_FILE_NAME.test(entry.name)) {
123
+ const stat = await unlessMissing(fs.lstat(file));
124
+ if (stat?.isFile() && stat.nlink === 1) {
125
+ const physicalPath = path.join(sessionsRoot, path.relative(rootReal, file));
126
+ if (physicalPath.length > 4096) continue;
127
+ const logicalPath = codexCatalogRolloutLogicalPath(physicalPath);
128
+ if (trackedPaths.has(logicalPath)) present.add(logicalPath);
129
+ if (physicalPath !== logicalPath) {
130
+ const plain = await unlessMissing(fs.lstat(codexCatalogRolloutLogicalPath(file)));
131
+ if (plain?.isFile() && plain.nlink === 1) continue;
132
+ }
133
+ candidates.offer([detachCodexCatalogString(physicalPath), {
134
+ mtimeMs: stat.mtimeMs,
135
+ size: stat.size
136
+ }]);
137
+ }
138
+ }
139
+ }
140
+ };
141
+ await scan(rootReal, 0);
142
+ return {
143
+ files: candidates.finish(),
144
+ present
145
+ };
146
+ }
147
+ function records(bytes, skipFirst) {
148
+ const text = bytes.toString("utf8");
149
+ const lines = text.slice(0, text.lastIndexOf("\n") + 1).split("\n");
150
+ if (skipFirst) lines.shift();
151
+ return lines.flatMap((line) => {
152
+ try {
153
+ const value = JSON.parse(line);
154
+ return isRecord(value) ? [value] : [];
155
+ } catch {
156
+ return [];
157
+ }
158
+ });
159
+ }
160
+ function sourceFromMetadata(value) {
161
+ if (value === void 0) return "vscode";
162
+ if (value === "cli" || value === "vscode" || value === "exec") return value;
163
+ if (value === "mcp") return "appServer";
164
+ if (isRecord(value)) {
165
+ const custom = boundedCatalogString(value.custom, 500);
166
+ if (custom) return { custom };
167
+ }
168
+ return "unknown";
169
+ }
170
+ function previewFromEvent(payload, sanitize) {
171
+ let message;
172
+ let image = false;
173
+ let audio = false;
174
+ if (payload.type === "user_message") {
175
+ message = typeof payload.message === "string" ? payload.message : void 0;
176
+ image = [payload.images, payload.local_images].some((items) => Array.isArray(items) && items.length > 0);
177
+ audio = [payload.audio, payload.local_audio].some((items) => Array.isArray(items) && items.length > 0);
178
+ } else if (payload.type === "item_completed" && isRecord(payload.item) && payload.item.type === "UserMessage" && Array.isArray(payload.item.content)) {
179
+ const content = payload.item.content.filter(isRecord);
180
+ message = content.flatMap((item) => item.type === "text" && typeof item.text === "string" ? [item.text] : []).join("");
181
+ image = content.some((item) => item.type === "image" || item.type === "local_image");
182
+ audio = content.some((item) => item.type === "audio" || item.type === "local_audio");
183
+ }
184
+ const prefix = "## My request for Codex:";
185
+ if (message?.includes(prefix)) message = message.slice(message.indexOf(prefix) + 24);
186
+ return truncateCodexCatalogPreview(selectCodexCatalogPreviewInput(message ?? ""), sanitize) || (image ? "[Image]" : audio ? "[Audio]" : void 0);
187
+ }
188
+ async function compressedHead(handle, size) {
189
+ const input = Buffer.alloc(Math.min(size, WINDOW_BYTES));
190
+ const { bytesRead } = await handle.read(input, 0, input.length, 0);
191
+ const decoder = createZstdDecompress({
192
+ chunkSize: 16384,
193
+ params: { [constants.ZSTD_d_windowLogMax]: 25 }
194
+ });
195
+ decoder.end(input.subarray(0, bytesRead));
196
+ const timer = setTimeout(() => decoder.destroy(/* @__PURE__ */ new Error("Rollout read timed out")), 5e3);
197
+ const chunks = [];
198
+ let sizeRead = 0;
199
+ try {
200
+ for await (const chunk of decoder) {
201
+ const bounded = (Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)).subarray(0, WINDOW_BYTES - sizeRead);
202
+ chunks.push(bounded);
203
+ sizeRead += bounded.length;
204
+ if (sizeRead === WINDOW_BYTES) break;
205
+ }
206
+ return Buffer.concat(chunks);
207
+ } finally {
208
+ clearTimeout(timer);
209
+ decoder.destroy();
210
+ }
211
+ }
212
+ /** Background-only display projection; never reads an entire large rollout. */
213
+ async function readCodexCatalogRollout(sessionsRoot, rolloutPath) {
214
+ let opened;
215
+ try {
216
+ const rootStat = await fs.lstat(sessionsRoot);
217
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) return;
218
+ opened = await (await root(sessionsRoot, {
219
+ hardlinks: "reject",
220
+ symlinks: "reject",
221
+ maxBytes: Number.MAX_SAFE_INTEGER
222
+ })).open(path.relative(sessionsRoot, rolloutPath));
223
+ const { handle, stat } = opened;
224
+ if (!stat.size) return;
225
+ const compressed = rolloutPath.endsWith(".zst");
226
+ const head = compressed ? await compressedHead(handle, stat.size) : Buffer.alloc(Math.min(stat.size, WINDOW_BYTES));
227
+ if (!compressed) await handle.read(head, 0, head.length, 0);
228
+ const first = records(head, false);
229
+ const envelope = records(head.subarray(0, head.indexOf(10) + 1), false)[0];
230
+ if (envelope?.type !== "session_meta" || !isRecord(envelope.payload)) return;
231
+ const metadata = envelope.payload;
232
+ const id = boundedCatalogString(metadata.id, 256);
233
+ const createdAt = typeof metadata.timestamp === "string" ? Date.parse(metadata.timestamp) : NaN;
234
+ if (!id || !Number.isFinite(createdAt)) return;
235
+ let tail = first;
236
+ if (!compressed && stat.size > head.length) {
237
+ const offset = Math.max(head.length, stat.size - WINDOW_BYTES);
238
+ const bytes = Buffer.alloc(stat.size - offset);
239
+ await handle.read(bytes, 0, bytes.length, offset);
240
+ tail = records(bytes, true);
241
+ }
242
+ const current = await handle.stat();
243
+ if (stat.size !== current.size || stat.mtimeMs !== current.mtimeMs) return;
244
+ const thread = {
245
+ id,
246
+ projectId: null,
247
+ path: detachCodexCatalogString(rolloutPath),
248
+ source: sourceFromMetadata(metadata.source),
249
+ createdAt: createdAt / 1e3,
250
+ updatedAt: stat.mtimeMs / 1e3,
251
+ cwd: boundedCatalogString(metadata.cwd, 4096),
252
+ originator: typeof metadata.originator === "string" && metadata.originator.length <= 500 ? detachCodexCatalogString(metadata.originator) : void 0,
253
+ sessionId: boundedCatalogString(metadata.session_id, 256)
254
+ };
255
+ const { sanitizeTerminalText } = await import("openclaw/plugin-sdk/text-chunking");
256
+ for (const record of first) if (record.type === "event_msg" && isRecord(record.payload)) {
257
+ const preview = previewFromEvent(record.payload, sanitizeTerminalText);
258
+ if (preview) {
259
+ thread.preview = preview;
260
+ break;
261
+ }
262
+ }
263
+ for (const record of tail === first ? first : [...first, ...tail]) {
264
+ if (record.type !== "event_msg" || !isRecord(record.payload)) continue;
265
+ const payload = record.payload;
266
+ if (payload.type === "task_started" || payload.type === "turn_started") {
267
+ const startedAt = typeof payload.started_at === "number" ? payload.started_at : typeof record.timestamp === "string" ? Date.parse(record.timestamp) / 1e3 : NaN;
268
+ if (Number.isFinite(startedAt)) thread.recencyAt = Math.max(thread.recencyAt ?? Number.NEGATIVE_INFINITY, Math.floor(startedAt));
269
+ }
270
+ if (payload.type === "thread_settings_applied" && isRecord(payload.thread_settings)) thread.cwd = boundedCatalogString(payload.thread_settings.cwd, 4096) ?? thread.cwd;
271
+ }
272
+ return thread;
273
+ } catch (error) {
274
+ const code = extractErrorCode(error);
275
+ if (code && ROLLOUT_SOURCE_DENIAL_CODES.has(code)) return;
276
+ throw error;
277
+ } finally {
278
+ await opened?.handle.close().catch(() => void 0);
279
+ }
280
+ }
281
+ //#endregion
282
+ //#region extensions/codex/src/session-catalog-projection.ts
283
+ /** Single-thread responses remain owned by their native consumers. */
284
+ function projectCodexCatalogThread(thread, localSessionsRoot) {
285
+ try {
286
+ const prepared = projectCodexCatalogNativeThread(thread, sanitizeTerminalText);
287
+ return projectCodexCatalogPage({ data: [prepared] }, {
288
+ localSessionsRoot,
289
+ sanitize: sanitizeTerminalText,
290
+ source: getCodexCatalogSource(prepared)
291
+ });
292
+ } catch (error) {
293
+ return Promise.reject(toErrorObject(error, "Codex catalog projection failed"));
294
+ }
295
+ }
296
+ var CodexCatalogProjectionCapacityError = class extends Error {
297
+ constructor() {
298
+ super("Codex catalog projection queue reached its resident row limit");
299
+ }
300
+ };
301
+ /** Direct mutations and events share one bound across asynchronous projection work. */
302
+ var CodexCatalogProjections = class {
303
+ constructor() {
304
+ this.active = 0;
305
+ }
306
+ run(work) {
307
+ if (this.active >= 2e4) return Promise.reject(new CodexCatalogProjectionCapacityError());
308
+ this.active++;
309
+ try {
310
+ return work().finally(() => {
311
+ this.active--;
312
+ });
313
+ } catch (error) {
314
+ this.active--;
315
+ return Promise.reject(toErrorObject(error, "Codex catalog projection failed"));
316
+ }
317
+ }
318
+ };
319
+ async function projectCodexCatalogPage(response, params) {
320
+ const { diagnostics, sanitize } = params;
321
+ const responseStarted = performance.now();
322
+ const rows = [];
323
+ const excludedThreadIds = [];
324
+ try {
325
+ readControlCursor(response.backwardsCursor, "backwards response");
326
+ for (const thread of response.data) if (typeof thread.preview === "string" && thread.preview) thread.preview = truncateCodexCatalogPreview(selectCodexCatalogPreviewInput(thread.preview), sanitize);
327
+ for (const thread of response.data) {
328
+ if (thread.ephemeral === true) {
329
+ excludedThreadIds.push(thread.id);
330
+ continue;
331
+ }
332
+ const page = { sessions: [] };
333
+ if (await isOpenClawManagedCodexThread(thread, params.localSessionsRoot, diagnostics ?? void 0)) {
334
+ const rolloutPath = typeof thread.path === "string" ? thread.path.trim() : "";
335
+ page.managedThreads = [{
336
+ threadId: thread.id,
337
+ ...rolloutPath ? { rolloutPath } : {}
338
+ }];
339
+ } else {
340
+ const session = toCatalogSession(thread, false, sanitize, { value: typeof thread.preview === "string" ? thread.preview : void 0 });
341
+ if (session) page.sessions.push(session);
342
+ }
343
+ rows.push(setCodexCatalogSource({
344
+ threadId: thread.id,
345
+ archived: false,
346
+ nativeMetadata: true,
347
+ ...typeof thread.preview === "string" ? { preview: thread.preview } : {},
348
+ ...thread.path ? { rolloutPath: thread.path } : {},
349
+ updatedAt: asFiniteNumber(thread.updatedAt) ?? null,
350
+ recencyAt: asFiniteNumber(thread.recencyAt) ?? null,
351
+ page
352
+ }, params.source ?? getCodexCatalogSource(thread)));
353
+ }
354
+ return {
355
+ rows,
356
+ ...excludedThreadIds.length ? { excludedThreadIds } : {},
357
+ nextCursor: readControlCursor(response.nextCursor, "next response"),
358
+ backwardsCursor: readControlCursor(response.backwardsCursor, "backwards response")
359
+ };
360
+ } finally {
361
+ if (diagnostics) diagnostics.fields.postResponseMs = (diagnostics.fields.postResponseMs ?? 0) + performance.now() - responseStarted;
362
+ }
363
+ }
364
+ /** Native activity/path changes invalidate the retained first-user preview. */
365
+ function canReuseCodexCatalogPreview(row, thread) {
366
+ return Boolean(row && !row.archived && row.updatedAt === (asFiniteNumber(thread.updatedAt) ?? null) && row.recencyAt === (asFiniteNumber(thread.recencyAt) ?? null) && (row.rolloutPath ? codexCatalogRolloutLogicalPath(row.rolloutPath) : void 0) === (thread.path ? codexCatalogRolloutLogicalPath(thread.path) : void 0));
367
+ }
368
+ async function projectCodexCatalogDeltaPage(response, params) {
369
+ const reusable = response.data.map((thread) => {
370
+ const row = params.getRow(thread.id);
371
+ if (thread.ephemeral === true || !row || !canReuseCodexCatalogPreview(row, thread)) return;
372
+ if (row.page.sessions.length === 0) return copyCodexCatalogSource(thread, {
373
+ ...row,
374
+ nativeMetadata: true
375
+ });
376
+ if (typeof thread.preview === "string" && Boolean(thread.preview) !== Boolean(row.preview)) return;
377
+ const session = toCatalogSession(thread, false, params.sanitize, { value: row.preview });
378
+ return copyCodexCatalogSource(thread, {
379
+ ...row,
380
+ nativeMetadata: true,
381
+ page: { sessions: session ? [session] : [] }
382
+ });
383
+ });
384
+ const changed = await projectCodexCatalogPage({
385
+ ...response,
386
+ data: response.data.filter((_thread, index) => !reusable[index])
387
+ }, params);
388
+ let changedIndex = 0;
389
+ return {
390
+ ...changed,
391
+ rows: reusable.flatMap((row, index) => response.data[index]?.ephemeral === true ? [] : [row ?? changed.rows[changedIndex++]])
392
+ };
393
+ }
394
+ /** Sampled rollout content cannot supersede authoritative native settings. */
395
+ function mergeCodexCatalogRolloutRow(row, existing, fingerprint) {
396
+ const recencyAt = row.recencyAt === null ? existing?.recencyAt ?? null : Math.max(row.recencyAt, existing?.recencyAt ?? row.recencyAt);
397
+ const metadata = existing?.nativeMetadata ? existing : row;
398
+ const preview = row.preview ?? metadata.preview;
399
+ const sessions = metadata.page.sessions.map((session) => {
400
+ const updated = Object.assign({}, session);
401
+ delete updated.fallbackName;
402
+ if (!session.name && preview) updated.fallbackName = preview;
403
+ if (recencyAt !== null) updated.recencyAt = recencyAt;
404
+ return updated;
405
+ });
406
+ const merged = {
407
+ ...metadata,
408
+ nativeMetadata: existing?.nativeMetadata ?? false,
409
+ archived: existing?.archived ?? false,
410
+ recencyAt,
411
+ fingerprint,
412
+ ...preview !== void 0 ? { preview } : {},
413
+ page: {
414
+ ...metadata.page,
415
+ sessions
416
+ }
417
+ };
418
+ if (existing && codexCatalogRowRecency(merged) > codexCatalogRowRecency(existing)) delete merged.sourceOrder;
419
+ return merged;
420
+ }
421
+ //#endregion
422
+ export { CodexCatalogProjectionCapacityError, CodexCatalogProjections, resolveCodexCatalogRolloutFingerprint as a, canReuseCodexCatalogPreview, readCodexCatalogRollout as i, mergeCodexCatalogRolloutRow, indexCodexCatalogRowsByRollout as n, scanCodexCatalogRollouts as o, projectCodexCatalogDeltaPage, projectCodexCatalogPage, projectCodexCatalogThread, isCodexCatalogRolloutPathCovered as r, codexCatalogRolloutLogicalPath as t };