@openclaw/codex 2026.7.2-beta.5 → 2026.7.2-beta.7

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 (42) hide show
  1. package/dist/{app-server-policy-BmDgseo_.js → app-server-policy-BSaO2JIT.js} +1 -1
  2. package/dist/{attempt-notifications-CMR2_fmR.js → attempt-notifications-AP-a-Q5k.js} +1 -1
  3. package/dist/{bounded-turn-FEbUXoYT.js → bounded-turn-D2ZAgZVz.js} +29 -22
  4. package/dist/{capabilities-BHRSI6Cn.js → capabilities-BFDTVYr4.js} +1 -1
  5. package/dist/cli-metadata.js +1 -1
  6. package/dist/{command-formatters-ico4ZFnk.js → command-formatters-C336eRVS.js} +2 -2
  7. package/dist/{command-handlers-CRmnwI2N.js → command-handlers-DAHUSry9.js} +12 -12
  8. package/dist/{compact-dEN49UH6.js → compact-D_uvHurS.js} +5 -5
  9. package/dist/{computer-use-BjGylO-E.js → computer-use-CYEc8U3w.js} +111 -113
  10. package/dist/{conversation-control-DE7d9DOj.js → conversation-control-xsIeXbo4.js} +6 -6
  11. package/dist/doctor-contract-api.js +1 -1
  12. package/dist/{dynamic-tools-LIt9ojep.js → dynamic-tools-D2gYkSBO.js} +438 -51
  13. package/dist/effective-mcp-catalog-Bbd98Inw.js +114 -0
  14. package/dist/{event-projector-usage-tvgb1NCX.js → event-projector-usage-Xp4AlR2k.js} +20 -3
  15. package/dist/harness.js +13 -9
  16. package/dist/index.js +131 -108
  17. package/dist/media-understanding-provider.js +1 -1
  18. package/dist/{models-BA01NEez.js → models-BBMKU4jA.js} +2 -2
  19. package/dist/{notification-correlation-Bj_Ak_jI.js → notification-correlation-CU6_AnOb.js} +3 -3
  20. package/dist/{plugin-app-cache-key-DSXX-rXO.js → plugin-app-cache-key-DztPafOA.js} +132 -39
  21. package/dist/{rate-limits-S_PXs0ul.js → rate-limits-CU-gVCCS.js} +1 -1
  22. package/dist/{request-CXpPennK.js → request-qKh331yu.js} +3 -3
  23. package/dist/{run-attempt-C5GTPJOE.js → run-attempt-CY9WlDLc.js} +677 -199
  24. package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-CmtG_t1L.js} +90 -24
  25. package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-NuhpXmPB.js} +4 -0
  26. package/dist/{session-binding-DlVQ-Pxs.js → session-binding-BXpo9ymA.js} +60 -7
  27. package/dist/{session-catalog-CP2oCcEb.js → session-catalog-DyOgR4WH.js} +268 -44
  28. package/dist/{session-cli-CqZ0Fp2a.js → session-cli-2PZaFLRh.js} +1 -1
  29. package/dist/{session-upstream-marker-QE-m5QFG.js → session-upstream-marker-B6U7tLw5.js} +1 -1
  30. package/dist/{settled-turn-finalizer-CA_2QXIE.js → settled-turn-finalizer-CXY2Z9dT.js} +19 -6
  31. package/dist/{shared-client-BVKE40c-.js → shared-client-DTbvH-XV.js} +166 -49
  32. package/dist/{side-question-CO12sHxo.js → side-question-_ulBk1AL.js} +10 -10
  33. package/dist/{thread-lifecycle-DRg_FdzC.js → thread-lifecycle-DF53ogiA.js} +968 -214
  34. package/dist/{protocol-validators-Dm4eXZmt.js → transcript-mirror-DzsdNCf5.js} +589 -2
  35. package/dist/{upstream-session-fork-_Vrz4WJr.js → upstream-session-fork-Dy4Xvuoj.js} +3 -3
  36. package/dist/{usage-UpVbdX5G.js → usage-CQ6GhD5-.js} +3 -3
  37. package/dist/{web-search-provider.runtime-CaQMYDbp.js → web-search-provider.runtime-CCFhg2z5.js} +3 -3
  38. package/package.json +6 -7
  39. package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
  40. package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
  41. package/dist/realtime-voice-api.js +0 -2
  42. package/dist/transcript-mirror-uKA-A4tB.js +0 -550
@@ -1,14 +1,13 @@
1
- import { E as resolveCodexAppServerClientInstanceId, L as CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES, R as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, V as isJsonObject, _ as CodexAppServerRpcError, a as getLeasedSharedCodexAppServerClient, h as withTimeout, l as releaseLeasedSharedCodexAppServerClient } from "./shared-client-BVKE40c-.js";
2
- import { c as reclaimCurrentCodexSessionGeneration, g as resolveCodexSupervisionAppServerRuntimeOptions, l as sessionBindingIdentity, p as resolveCodexAppServerRuntimeOptions } from "./session-binding-DlVQ-Pxs.js";
3
- import { t as assertCodexThreadForkParams } from "./protocol-validators-Dm4eXZmt.js";
4
- import { n as listCodexAppServerModels } from "./models-BA01NEez.js";
5
- import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-DSXX-rXO.js";
6
- import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-uKA-A4tB.js";
7
- import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-BHRSI6Cn.js";
8
- import { r as formatCodexDisplayText } from "./command-formatters-ico4ZFnk.js";
1
+ import { B as CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES, D as resolveCodexAppServerClientInstanceId, V as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, W as isJsonObject, a as getLeasedSharedCodexAppServerClient, g as withTimeout, l as releaseLeasedSharedCodexAppServerClient, v as CodexAppServerRpcError } from "./shared-client-DTbvH-XV.js";
2
+ import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, g as resolveCodexAppServerRuntimeOptions, y as resolveCodexSupervisionAppServerRuntimeOptions } from "./session-binding-BXpo9ymA.js";
3
+ import { m as assertCodexThreadForkParams, r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-DzsdNCf5.js";
4
+ import { n as listCodexAppServerModels } from "./models-BBMKU4jA.js";
5
+ import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-DztPafOA.js";
6
+ import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-BFDTVYr4.js";
7
+ import { r as formatCodexDisplayText } from "./command-formatters-C336eRVS.js";
9
8
  import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-BHRpYbX3.js";
10
- import { n as requestCodexAppServerClientJson, r as requestCodexAppServerJson } from "./request-CXpPennK.js";
11
- import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-QE-m5QFG.js";
9
+ import { n as requestCodexAppServerClientJson, r as requestCodexAppServerJson } from "./request-qKh331yu.js";
10
+ import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-B6U7tLw5.js";
12
11
  import fs from "node:fs/promises";
13
12
  import path from "node:path";
14
13
  import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
@@ -27,6 +26,103 @@ import process$1 from "node:process";
27
26
  import { listSessionCatalogEntries } from "openclaw/plugin-sdk/session-catalog";
28
27
  import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
29
28
  import { decodeNodePtyResumeParams, resolveNodeHostExecutable, runNodePtyCommand } from "openclaw/plugin-sdk/node-host";
29
+ //#region extensions/codex/src/jsonl-lines.ts
30
+ const JSONL_STREAM_THRESHOLD_BYTES = 4 * 1024 * 1024;
31
+ const JSONL_READ_CHUNK_BYTES = 1024 * 1024;
32
+ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTES) {
33
+ let size;
34
+ try {
35
+ size = (await fs.stat(file)).size;
36
+ } catch {
37
+ return {
38
+ ok: false,
39
+ lineCount: 0
40
+ };
41
+ }
42
+ if (size <= JSONL_STREAM_THRESHOLD_BYTES) {
43
+ let content;
44
+ try {
45
+ content = await fs.readFile(file, "utf8");
46
+ } catch {
47
+ return {
48
+ ok: false,
49
+ lineCount: 0
50
+ };
51
+ }
52
+ if (content.length === 0) return {
53
+ ok: true,
54
+ lineCount: 0
55
+ };
56
+ let lineCount = 0;
57
+ for (const line of content.split(/\r?\n/u)) {
58
+ lineCount += 1;
59
+ if (visitor(line) === false) break;
60
+ }
61
+ return {
62
+ ok: true,
63
+ lineCount
64
+ };
65
+ }
66
+ let handle;
67
+ try {
68
+ handle = await fs.open(file, "r");
69
+ } catch {
70
+ return {
71
+ ok: false,
72
+ lineCount: 0
73
+ };
74
+ }
75
+ const buffer = Buffer.allocUnsafe(chunkBytes);
76
+ const decoder = new TextDecoder();
77
+ let pendingFragments = [];
78
+ let lineCount = 0;
79
+ try {
80
+ while (true) {
81
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
82
+ if (bytesRead === 0) break;
83
+ const content = decoder.decode(buffer.subarray(0, bytesRead), { stream: true });
84
+ let lineStart = 0;
85
+ while (true) {
86
+ const newline = content.indexOf("\n", lineStart);
87
+ if (newline === -1) break;
88
+ let rawLine = content.slice(lineStart, newline);
89
+ if (pendingFragments.length > 0) {
90
+ pendingFragments.push(rawLine);
91
+ rawLine = pendingFragments.join("");
92
+ pendingFragments = [];
93
+ }
94
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
95
+ lineCount += 1;
96
+ if (visitor(line) === false) return {
97
+ ok: true,
98
+ lineCount
99
+ };
100
+ lineStart = newline + 1;
101
+ }
102
+ if (lineStart < content.length) pendingFragments.push(content.slice(lineStart));
103
+ }
104
+ const decoderTail = decoder.decode();
105
+ if (decoderTail.length > 0) pendingFragments.push(decoderTail);
106
+ if (pendingFragments.length > 0) {
107
+ const rawLine = pendingFragments.join("");
108
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
109
+ lineCount += 1;
110
+ visitor(line);
111
+ }
112
+ return {
113
+ ok: true,
114
+ lineCount
115
+ };
116
+ } catch {
117
+ return {
118
+ ok: false,
119
+ lineCount: 0
120
+ };
121
+ } finally {
122
+ await handle.close().catch(() => void 0);
123
+ }
124
+ }
125
+ //#endregion
30
126
  //#region extensions/codex/src/node-cli-sessions.ts
31
127
  const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
32
128
  const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
@@ -249,20 +345,18 @@ function resolveCodexCliResumeSpawnInvocation(args, runtime = DEFAULT_RESUME_SPA
249
345
  }
250
346
  async function readHistorySessions(codexHome) {
251
347
  const summaries = /* @__PURE__ */ new Map();
252
- const content = await readFileIfExists(path.join(codexHome, "history.jsonl"));
253
- if (!content) return summaries;
254
- for (const line of content.split(/\r?\n/u)) {
348
+ if (!(await visitJsonlLines(path.join(codexHome, "history.jsonl"), (line) => {
255
349
  const trimmed = line.trim();
256
- if (!trimmed) continue;
350
+ if (!trimmed) return;
257
351
  let parsed;
258
352
  try {
259
353
  parsed = JSON.parse(trimmed);
260
354
  } catch {
261
- continue;
355
+ return;
262
356
  }
263
- if (!isRecord(parsed) || typeof parsed.session_id !== "string") continue;
357
+ if (!isRecord(parsed) || typeof parsed.session_id !== "string") return;
264
358
  const sessionId = parsed.session_id.trim();
265
- if (!sessionId) continue;
359
+ if (!sessionId) return;
266
360
  const entry = summaries.get(sessionId) ?? {
267
361
  sessionId,
268
362
  messageCount: 0
@@ -271,7 +365,7 @@ async function readHistorySessions(codexHome) {
271
365
  if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
272
366
  if (typeof parsed.ts === "number") entry.updatedAt = timestampMsToIsoString(parsed.ts * 1e3) ?? entry.updatedAt;
273
367
  summaries.set(sessionId, entry);
274
- }
368
+ })).ok) return /* @__PURE__ */ new Map();
275
369
  return summaries;
276
370
  }
277
371
  async function hydrateSessionFiles(codexHome, summaries) {
@@ -309,35 +403,35 @@ async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
309
403
  }
310
404
  }
311
405
  async function readSessionFileSummary(file) {
312
- const content = await readFileIfExists(file);
313
- if (!content) return null;
314
406
  let sessionId = "";
315
407
  let cwd;
316
408
  let updatedAt;
317
409
  let lastMessage;
318
410
  let messageCount = 0;
319
- for (const line of content.split(/\r?\n/u)) {
411
+ const result = await visitJsonlLines(file, (line) => {
320
412
  const trimmed = line.trim();
321
- if (!trimmed) continue;
413
+ if (!trimmed) return;
322
414
  let parsed;
323
415
  try {
324
416
  parsed = JSON.parse(trimmed);
325
417
  } catch {
326
- continue;
418
+ return;
327
419
  }
328
- if (!isRecord(parsed)) continue;
420
+ if (!isRecord(parsed)) return;
329
421
  if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
330
422
  if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
331
423
  if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
332
424
  if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
333
- continue;
425
+ return;
334
426
  }
335
427
  const messageText = readResponseItemMessageText(parsed);
336
428
  if (messageText) {
337
429
  messageCount += 1;
338
430
  lastMessage = truncateText(messageText, 140);
339
431
  }
340
- }
432
+ });
433
+ if (!result.ok) return null;
434
+ if (result.lineCount === 0) return null;
341
435
  if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
342
436
  if (!sessionId) return null;
343
437
  return {
@@ -445,15 +539,12 @@ function readRecordParam(paramsJSON) {
445
539
  function resolveCodexHome() {
446
540
  return process$1.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
447
541
  }
448
- async function readFileIfExists(file) {
449
- try {
450
- return await fs.readFile(file, "utf8");
451
- } catch {
452
- return;
453
- }
454
- }
455
542
  async function readFirstLine(file) {
456
- return (await readFileIfExists(file))?.split(/\r?\n/u)[0];
543
+ let firstLine;
544
+ return (await visitJsonlLines(file, (line) => {
545
+ firstLine = line;
546
+ return false;
547
+ }, 65536)).ok ? firstLine : void 0;
457
548
  }
458
549
  async function readFileMtimeIso(file) {
459
550
  try {
@@ -1339,12 +1430,20 @@ function codexNodeTerminalCapability(node) {
1339
1430
  return node.connected === true && commands?.includes("codex.terminal.resume.v1") === true ? { canOpenTerminalCodex: true } : {};
1340
1431
  }
1341
1432
  async function requireCatalogEligibleThread(control, threadId) {
1433
+ const cached = await findCatalogEligibleThread(control, threadId, false);
1434
+ if (cached) return cached;
1435
+ const refreshed = await findCatalogEligibleThread(control, threadId, true);
1436
+ if (refreshed) return refreshed;
1437
+ throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1438
+ }
1439
+ async function findCatalogEligibleThread(control, threadId, forceRefresh) {
1342
1440
  let cursor;
1343
1441
  const seenCursors = /* @__PURE__ */ new Set();
1344
1442
  for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
1345
1443
  const page = await control.listPage({
1346
1444
  limit: 100,
1347
- ...cursor ? { cursor } : {}
1445
+ ...cursor ? { cursor } : {},
1446
+ ...forceRefresh ? { forceRefresh: true } : {}
1348
1447
  });
1349
1448
  const candidate = page.sessions.find((session) => session.threadId === threadId);
1350
1449
  if (candidate) {
@@ -1352,7 +1451,7 @@ async function requireCatalogEligibleThread(control, threadId) {
1352
1451
  throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1353
1452
  }
1354
1453
  const nextCursor = page.nextCursor?.trim();
1355
- if (!nextCursor) throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1454
+ if (!nextCursor) return;
1356
1455
  if (seenCursors.has(nextCursor)) throw new CatalogParamsError("Codex session eligibility could not be verified");
1357
1456
  seenCursors.add(nextCursor);
1358
1457
  cursor = nextCursor;
@@ -1618,6 +1717,16 @@ function createChecker(params) {
1618
1717
  //#region extensions/codex/src/session-catalog.ts
1619
1718
  const boundCatalogSessionId = (value) => boundedCatalogString(value, 256);
1620
1719
  const CODEX_SUPERVISION_SESSION_KEY_PREFIX = "harness:codex:supervision:";
1720
+ const CODEX_SESSION_CATALOG_LIST_TTL_MS = 32e3;
1721
+ const CODEX_SESSION_CATALOG_LIST_CACHE_MAX_ENTRIES = 32;
1722
+ function codexCatalogPageCacheKey(params) {
1723
+ return JSON.stringify([
1724
+ params.cursor ?? null,
1725
+ params.limit ?? null,
1726
+ params.searchTerm?.trim().toLocaleLowerCase() || null,
1727
+ params.cwd?.trim() || null
1728
+ ]);
1729
+ }
1621
1730
  function createCodexSessionCatalogControlFromRequests(params) {
1622
1731
  return {
1623
1732
  ...params.clientId ? { clientId: params.clientId } : {},
@@ -1687,13 +1796,27 @@ function createCodexSessionCatalogControlFromRequests(params) {
1687
1796
  function createCodexSessionCatalogControl(params) {
1688
1797
  const now = params.now ?? Date.now;
1689
1798
  const getPluginConfig = () => params.getPluginConfig();
1799
+ const requestOptionsByConfig = /* @__PURE__ */ new WeakMap();
1800
+ const catalogPagesByConfig = /* @__PURE__ */ new WeakMap();
1801
+ const resolveRequestOptions = (startOptions) => {
1802
+ const runtimeConfig = params.getRuntimeConfig();
1803
+ if (!runtimeConfig) return {
1804
+ config: void 0,
1805
+ startOptions: structuredClone(startOptions)
1806
+ };
1807
+ const cached = requestOptionsByConfig.get(runtimeConfig);
1808
+ if (cached) return cached;
1809
+ const resolved = {
1810
+ config: structuredClone(runtimeConfig),
1811
+ startOptions: structuredClone(startOptions)
1812
+ };
1813
+ requestOptionsByConfig.set(runtimeConfig, resolved);
1814
+ return resolved;
1815
+ };
1690
1816
  const createRequestSnapshot = () => {
1691
1817
  const pluginConfig = getPluginConfig();
1692
1818
  const runtime = resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig });
1693
- const requestOptions = {
1694
- config: structuredClone(params.getRuntimeConfig()),
1695
- startOptions: structuredClone(runtime.start)
1696
- };
1819
+ const requestOptions = resolveRequestOptions(runtime.start);
1697
1820
  return {
1698
1821
  requestTimeoutMs: runtime.requestTimeoutMs,
1699
1822
  listThreads: async (listParams, timeoutMs) => await codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, listParams, {
@@ -1713,10 +1836,10 @@ function createCodexSessionCatalogControl(params) {
1713
1836
  };
1714
1837
  const withPinnedConnection = async (run) => {
1715
1838
  const runtime = resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig: getPluginConfig() });
1716
- const runtimeConfig = structuredClone(params.getRuntimeConfig());
1839
+ const { config: runtimeConfig, startOptions } = resolveRequestOptions(runtime.start);
1717
1840
  const client = await getLeasedSharedCodexAppServerClient({
1718
1841
  config: runtimeConfig,
1719
- startOptions: structuredClone(runtime.start),
1842
+ startOptions,
1720
1843
  timeoutMs: runtime.requestTimeoutMs
1721
1844
  });
1722
1845
  try {
@@ -1775,11 +1898,112 @@ function createCodexSessionCatalogControl(params) {
1775
1898
  releaseLeasedSharedCodexAppServerClient(client);
1776
1899
  }
1777
1900
  };
1778
- return createCodexSessionCatalogControlFromRequests({
1901
+ const control = createCodexSessionCatalogControlFromRequests({
1779
1902
  createRequestSnapshot,
1780
1903
  now,
1781
1904
  withPinnedConnection
1782
1905
  });
1906
+ return {
1907
+ ...control,
1908
+ async listPage(pageParams) {
1909
+ const runtimeConfig = params.getRuntimeConfig();
1910
+ if (!runtimeConfig) return await control.listPage(pageParams);
1911
+ let cache = catalogPagesByConfig.get(runtimeConfig);
1912
+ if (!cache) {
1913
+ cache = /* @__PURE__ */ new Map();
1914
+ catalogPagesByConfig.set(runtimeConfig, cache);
1915
+ }
1916
+ const key = codexCatalogPageCacheKey(pageParams);
1917
+ const cached = cache.get(key);
1918
+ if (pageParams.forceRefresh !== true && cached) {
1919
+ cache.delete(key);
1920
+ cache.set(key, cached);
1921
+ if (cached.stalePage) return await cached.stalePage;
1922
+ if (cached.expiresAt > now()) return await cached.page;
1923
+ const stalePage = cached.settledPage;
1924
+ if (!stalePage) return await cached.page;
1925
+ const page = control.listPage(pageParams);
1926
+ const entry = {
1927
+ expiresAt: Number.POSITIVE_INFINITY,
1928
+ page,
1929
+ settledPage: stalePage,
1930
+ stalePage
1931
+ };
1932
+ cache.set(key, entry);
1933
+ page.then(() => {
1934
+ if (cache.get(key) === entry) {
1935
+ delete entry.stalePage;
1936
+ entry.settledPage = page;
1937
+ entry.expiresAt = now() + CODEX_SESSION_CATALOG_LIST_TTL_MS;
1938
+ }
1939
+ }, async () => {
1940
+ let stale;
1941
+ try {
1942
+ stale = await stalePage;
1943
+ } catch {}
1944
+ if (cache.get(key) !== entry) return;
1945
+ if (stale) {
1946
+ cache.delete(key);
1947
+ const settledPage = Promise.resolve(stale);
1948
+ cache.set(key, {
1949
+ expiresAt: now(),
1950
+ page: settledPage,
1951
+ settledPage
1952
+ });
1953
+ } else cache.delete(key);
1954
+ });
1955
+ return await stalePage;
1956
+ }
1957
+ if (cached) cache.delete(key);
1958
+ const serveStaleOnError = pageParams.forceRefresh !== true;
1959
+ const page = control.listPage(pageParams);
1960
+ const stalePage = cached?.settledPage;
1961
+ const entry = {
1962
+ expiresAt: Number.POSITIVE_INFINITY,
1963
+ page,
1964
+ ...stalePage ? {
1965
+ stalePage,
1966
+ settledPage: stalePage
1967
+ } : {}
1968
+ };
1969
+ cache.set(key, entry);
1970
+ while (cache.size > CODEX_SESSION_CATALOG_LIST_CACHE_MAX_ENTRIES) {
1971
+ const oldest = cache.keys().next();
1972
+ if (oldest.done) break;
1973
+ cache.delete(oldest.value);
1974
+ }
1975
+ try {
1976
+ const result = await page;
1977
+ if (cache.get(key) === entry) {
1978
+ delete entry.stalePage;
1979
+ entry.settledPage = page;
1980
+ entry.expiresAt = now() + CODEX_SESSION_CATALOG_LIST_TTL_MS;
1981
+ }
1982
+ return result;
1983
+ } catch (error) {
1984
+ if (stalePage) {
1985
+ let stale;
1986
+ try {
1987
+ stale = await stalePage;
1988
+ } catch {}
1989
+ if (stale) {
1990
+ if (cache.get(key) === entry) {
1991
+ cache.delete(key);
1992
+ const settledPage = Promise.resolve(stale);
1993
+ cache.set(key, {
1994
+ expiresAt: now(),
1995
+ page: settledPage,
1996
+ settledPage
1997
+ });
1998
+ }
1999
+ if (serveStaleOnError) return stale;
2000
+ }
2001
+ }
2002
+ if (cache.get(key) === entry) cache.delete(key);
2003
+ throw error;
2004
+ }
2005
+ }
2006
+ };
1783
2007
  }
1784
2008
  async function listGatewayHost(params) {
1785
2009
  try {
@@ -1832,7 +2056,7 @@ async function listCodexSessionCatalog(params) {
1832
2056
  if (!(!requestedHostIds || query.hostIds?.some((hostId) => hostId.startsWith("node:")))) return { hosts: await Promise.all(localHosts) };
1833
2057
  let nodes;
1834
2058
  try {
1835
- nodes = (await (params.listNodes?.() ?? params.runtime.nodes.list())).nodes.filter((node) => node.commands?.includes("codex.appServer.threads.list.v1") && (!requestedHostIds || requestedHostIds.has(`node:${node.nodeId}`))).slice(0, 100 - localHosts.length);
2059
+ nodes = (await (params.listNodes?.() ?? params.runtime.nodes.list())).nodes.filter((node) => node.gatewayLocal !== true && node.commands?.includes("codex.appServer.threads.list.v1") && (!requestedHostIds || requestedHostIds.has(`node:${node.nodeId}`))).slice(0, 100 - localHosts.length);
1836
2060
  } catch (error) {
1837
2061
  const registryHost = {
1838
2062
  hostId: "node:registry",
@@ -1,4 +1,4 @@
1
- import { a as CODEX_LOCAL_SESSION_HOST_ID } from "./session-catalog-CP2oCcEb.js";
1
+ import { a as CODEX_LOCAL_SESSION_HOST_ID } from "./session-catalog-DyOgR4WH.js";
2
2
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
4
4
  import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
@@ -1,4 +1,4 @@
1
- import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-uKA-A4tB.js";
1
+ import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-DzsdNCf5.js";
2
2
  import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
3
3
  //#region extensions/codex/src/app-server/session-history-import.ts
4
4
  /** Creates a session whose transcript is derived from one verified Codex thread snapshot. */
@@ -1,6 +1,8 @@
1
- import { c as fingerprintCodexMirrorSourceMessage, d as attachCodexMirrorIdentity, f as readMirrorIdentity, l as readCodexMirrorSourceFingerprint, p as readUpstreamUserText, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-uKA-A4tB.js";
2
- import { t as runBoundedCodexAppServerTurn } from "./bounded-turn-FEbUXoYT.js";
1
+ import { W as isJsonObject } from "./shared-client-DTbvH-XV.js";
2
+ import { c as fingerprintCodexMirrorSourceMessage, d as attachCodexMirrorIdentity, f as readMirrorIdentity, l as readCodexMirrorSourceFingerprint, p as readUpstreamUserText, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-DzsdNCf5.js";
3
+ import { t as runBoundedCodexAppServerTurn } from "./bounded-turn-D2ZAgZVz.js";
3
4
  import { n as createAssistantMessage } from "./event-projector-assistant-message-DTgnPPVF.js";
5
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
4
6
  import { Buffer } from "node:buffer";
5
7
  import { isSilentReplyText } from "openclaw/plugin-sdk/reply-runtime";
6
8
  //#region extensions/codex/src/app-server/settled-turn-projection.ts
@@ -9,9 +11,6 @@ const MAX_PROJECTION_BYTES = 512 * 1024;
9
11
  const MAX_TEXT_BYTES = 64 * 1024;
10
12
  const TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/u;
11
13
  const TOOL_ERROR_STATUS_PREFIX = "[Tool result status: error]\n";
12
- function isRecord(value) {
13
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14
- }
15
14
  function readNonEmptyString(value) {
16
15
  return typeof value === "string" ? value.trim() || void 0 : void 0;
17
16
  }
@@ -265,7 +264,21 @@ async function runCodexSettledTurnFinalization(operation, options) {
265
264
  historyItems,
266
265
  requireNoExternalCapabilities: true
267
266
  });
268
- const unexpectedItem = bounded.items.find((item) => !FINALIZER_PASSIVE_ITEM_TYPES.has(item.type));
267
+ let promptEchoSeen = false;
268
+ let unexpectedItem;
269
+ for (const item of bounded.items) {
270
+ if (FINALIZER_PASSIVE_ITEM_TYPES.has(item.type)) continue;
271
+ if (item.type === "userMessage" && !promptEchoSeen) {
272
+ const content = Array.isArray(item.content) ? item.content : [];
273
+ const input = content[0];
274
+ if (content.length === 1 && isJsonObject(input) && input.type === "text" && input.text === attempt.prompt) {
275
+ promptEchoSeen = true;
276
+ continue;
277
+ }
278
+ }
279
+ unexpectedItem = item;
280
+ break;
281
+ }
269
282
  if (unexpectedItem) throw new Error(`Codex settled-turn finalization returned unexpected native item: ${unexpectedItem.type}`);
270
283
  const text = bounded.text.trim();
271
284
  if (!text || isSilentReplyText(text)) throw new Error("Codex settled-turn finalization completed without a visible answer");