@openclaw/codex 2026.7.2-beta.7 → 2026.7.34

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 (64) hide show
  1. package/README.md +3 -27
  2. package/dist/{command-formatters-C336eRVS.js → .setup/app-server-policy-C_qTMEMy.mjs} +51 -9
  3. package/dist/{attempt-notifications-AP-a-Q5k.js → .setup/attempt-notifications-F8krwWL4.mjs} +6 -3
  4. package/dist/{command-handlers-DAHUSry9.js → .setup/command-handlers-DSRFfor4.mjs} +1156 -1319
  5. package/dist/.setup/command-rpc-BvYsfQ7G.mjs +786 -0
  6. package/dist/{compact-D_uvHurS.js → .setup/compact-BNgh7I4t.mjs} +10 -40
  7. package/dist/.setup/computer-use-C3QkmSO3.mjs +377 -0
  8. package/dist/.setup/config-CYEDnLJ2.mjs +1138 -0
  9. package/dist/{conversation-binding-data-BHRpYbX3.js → .setup/conversation-binding-data-CrRxtDBu.mjs} +1 -1
  10. package/dist/{bounded-turn-D2ZAgZVz.js → .setup/media-understanding-provider-EbJCyDqe.mjs} +203 -109
  11. package/dist/{models-BBMKU4jA.js → .setup/models-C_aZk03A.mjs} +11 -6
  12. package/dist/.setup/notification-correlation-iYs4WieS.mjs +35 -0
  13. package/dist/.setup/plugin-app-cache-key-DR8jV9oS.mjs +99 -0
  14. package/dist/.setup/protocol-2POPqAY4.mjs +13 -0
  15. package/dist/{transcript-mirror-DzsdNCf5.js → .setup/protocol-validators-dZQ-UTOa.mjs} +112 -922
  16. package/dist/{rate-limits-CU-gVCCS.js → .setup/provider-DGxDs6g8.mjs} +269 -13
  17. package/dist/{dynamic-tools-D2gYkSBO.js → .setup/provider-capabilities-DS2N_zc1.mjs} +5632 -3953
  18. package/dist/.setup/request-DFR0JyoV.mjs +36 -0
  19. package/dist/.setup/run-attempt-C7xEKFVB.mjs +8180 -0
  20. package/dist/{sandbox-guard-NuhpXmPB.js → .setup/sandbox-guard-DA2TQfZW.mjs} +1 -14
  21. package/dist/.setup/session-binding-DuihlMJc.mjs +628 -0
  22. package/dist/.setup/shared-client-B2KmLPRw.mjs +1854 -0
  23. package/dist/{side-question-_ulBk1AL.js → .setup/side-question-Cv6JXWCS.mjs} +84 -159
  24. package/dist/.setup/thread-lifecycle-u6CZdOOH.mjs +2593 -0
  25. package/dist/{web-search-provider.runtime-CCFhg2z5.js → .setup/web-search-provider.runtime-Ce2zOHxW.mjs} +3 -3
  26. package/dist/doctor-contract-api.js +122 -88
  27. package/dist/harness.js +20 -89
  28. package/dist/index.js +436 -1615
  29. package/dist/media-understanding-provider.js +1 -173
  30. package/dist/prompt-overlay.js +15 -0
  31. package/dist/provider-catalog.js +79 -0
  32. package/dist/provider-discovery.js +34 -0
  33. package/dist/provider.js +2 -0
  34. package/dist/web-search-contract-api.js +1 -1
  35. package/openclaw.plugin.json +178 -221
  36. package/package.json +8 -9
  37. package/dist/app-server-policy-BSaO2JIT.js +0 -49
  38. package/dist/auth-binding-B4FfbcCg.js +0 -62
  39. package/dist/capabilities-BFDTVYr4.js +0 -36
  40. package/dist/cli-metadata.js +0 -20
  41. package/dist/computer-use-CYEc8U3w.js +0 -693
  42. package/dist/conversation-control-xsIeXbo4.js +0 -287
  43. package/dist/effective-mcp-catalog-Bbd98Inw.js +0 -114
  44. package/dist/event-projector-assistant-message-DTgnPPVF.js +0 -101
  45. package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
  46. package/dist/incognito-session-ET_ufDH-.js +0 -2
  47. package/dist/notification-correlation-CU6_AnOb.js +0 -67
  48. package/dist/plugin-app-cache-key-DztPafOA.js +0 -408
  49. package/dist/request-qKh331yu.js +0 -157
  50. package/dist/run-attempt-CY9WlDLc.js +0 -12413
  51. package/dist/runtime-artifact-CmtG_t1L.js +0 -554
  52. package/dist/session-binding-BXpo9ymA.js +0 -2214
  53. package/dist/session-catalog-DyOgR4WH.js +0 -2668
  54. package/dist/session-cli-2PZaFLRh.js +0 -175
  55. package/dist/session-upstream-marker-B6U7tLw5.js +0 -82
  56. package/dist/settled-turn-finalizer-CXY2Z9dT.js +0 -316
  57. package/dist/shared-client-DTbvH-XV.js +0 -3069
  58. package/dist/thread-lifecycle-DF53ogiA.js +0 -3987
  59. package/dist/transport-stdio-DXgGZ7Ib.js +0 -88
  60. package/dist/upstream-session-fork-Dy4Xvuoj.js +0 -320
  61. package/dist/usage-CQ6GhD5-.js +0 -25
  62. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  63. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  64. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
@@ -1,6 +1,6 @@
1
- import { W as isJsonObject } from "./shared-client-DTbvH-XV.js";
2
- import { h as buildCodexNativeWebSearchThreadConfig } from "./thread-lifecycle-DF53ogiA.js";
3
- import { t as runBoundedCodexAppServerTurn } from "./bounded-turn-D2ZAgZVz.js";
1
+ import { r as isJsonObject } from "./protocol-2POPqAY4.mjs";
2
+ import { _ as buildCodexNativeWebSearchThreadConfig } from "./thread-lifecycle-u6CZdOOH.mjs";
3
+ import { n as runBoundedCodexAppServerTurn } from "./media-understanding-provider-EbJCyDqe.mjs";
4
4
  import { readStringParam, resolveSearchTimeoutSeconds, wrapWebContent } from "openclaw/plugin-sdk/provider-web-search";
5
5
  //#region extensions/codex/src/web-search-provider.runtime.ts
6
6
  async function executeCodexWebSearchProviderTool(ctx, args, executionContext, options) {
@@ -1,13 +1,10 @@
1
- import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
1
+ import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./.setup/session-binding-meta-B7aEMU7g.mjs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { listAgentIds, resolveAgentDir, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
5
5
  import { isDeepStrictEqual } from "node:util";
6
- import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
7
- import { patchSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
8
- import { canonicalPathFromExistingAncestor, isPathInside, pathExists } from "openclaw/plugin-sdk/security-runtime";
6
+ import { listSessionEntries, resolveSessionFilePath, resolveStorePath, updateSessionStoreEntry } from "openclaw/plugin-sdk/session-store-runtime";
9
7
  import { withFileLock } from "openclaw/plugin-sdk/file-lock";
10
- import { archiveLegacyStateSource, legacyStateFileExists } from "openclaw/plugin-sdk/runtime-doctor";
11
8
  //#region extensions/codex/src/migration/session-binding-sidecars.ts
12
9
  const LEGACY_BINDING_SUFFIX = ".codex-app-server.json";
13
10
  const CODEX_AGENT_HARNESS_ID = "codex";
@@ -23,9 +20,9 @@ const LEGACY_BINDING_LOCK_OPTIONS = {
23
20
  };
24
21
  async function collectSessionSurfaces(params) {
25
22
  const surfaces = /* @__PURE__ */ new Map();
26
- const stateRoot = await canonicalPathFromExistingAncestor(params.stateDir);
23
+ const stateRoot = await canonicalizePath(params.stateDir);
27
24
  const add = async (root, storePath, agentId, scan) => {
28
- const canonicalRoot = await canonicalPathFromExistingAncestor(root);
25
+ const canonicalRoot = await canonicalizePath(root);
29
26
  const surface = surfaces.get(canonicalRoot) ?? {
30
27
  root: canonicalRoot,
31
28
  scan: false,
@@ -55,7 +52,7 @@ async function collectSessionSurfaces(params) {
55
52
  env: params.env
56
53
  });
57
54
  const root = path.dirname(storePath);
58
- await add(root, storePath, agentId, isPathInside(stateRoot, await canonicalPathFromExistingAncestor(root)));
55
+ await add(root, storePath, agentId, isPathWithin(stateRoot, await canonicalizePath(root)));
59
56
  }
60
57
  const legacyRoot = path.join(params.stateDir, "sessions");
61
58
  const defaultAgentId = resolveSessionAgentIds({ config: params.config }).defaultAgentId;
@@ -66,7 +63,7 @@ async function collectLegacyBindingSources(params, options = {}) {
66
63
  const surfaces = await collectSessionSurfaces(params);
67
64
  const sources = /* @__PURE__ */ new Map();
68
65
  const addSource = async (sidecarPath, surface) => {
69
- const canonicalSidecar = await canonicalPathFromExistingAncestor(sidecarPath);
66
+ const canonicalSidecar = await canonicalizePath(sidecarPath);
70
67
  const source = sources.get(canonicalSidecar) ?? {
71
68
  sidecarPath: canonicalSidecar,
72
69
  transcriptPath: sidecarPath.slice(0, -22),
@@ -77,7 +74,7 @@ async function collectLegacyBindingSources(params, options = {}) {
77
74
  return source;
78
75
  };
79
76
  for (const surface of surfaces) {
80
- const sidecars = surface.scan ? walkSidecars(surface.root) : iterateIndexedSidecars(surface);
77
+ const sidecars = surface.scan ? walkSidecars(surface.root) : iterateIndexedSidecars(surface, params);
81
78
  for await (const sidecarPath of sidecars) {
82
79
  const source = await addSource(sidecarPath, surface);
83
80
  if (options.firstOnly) return {
@@ -106,42 +103,59 @@ async function readLegacySessionIndex(storePath) {
106
103
  return { failure: `session index ${storePath} could not be read (invalid JSON)` };
107
104
  }
108
105
  if (!isRecord(raw)) return { failure: `session index ${storePath} has invalid entries` };
106
+ let normalizedEntries;
107
+ try {
108
+ normalizedEntries = listSessionEntries({
109
+ storePath,
110
+ hydrateSkillPromptRefs: false
111
+ });
112
+ } catch {
113
+ return { failure: `session index ${storePath} could not be normalized` };
114
+ }
115
+ const normalizedByKey = new Map(normalizedEntries.map(({ sessionKey, entry }) => [sessionKey, entry]));
109
116
  const entries = [];
110
117
  for (const [sessionKey, value] of Object.entries(raw)) {
111
118
  if (!isRecord(value)) return { failure: `session index ${storePath} has invalid entries` };
112
119
  if (value.sessionId === void 0) continue;
113
- const sessionId = typeof value.sessionId === "string" ? value.sessionId.trim() : "";
120
+ const rawSessionId = typeof value.sessionId === "string" ? value.sessionId.trim() : "";
121
+ const sessionId = normalizedByKey.get(sessionKey)?.sessionId?.trim() ?? "";
114
122
  const sessionFile = value.sessionFile;
115
123
  const lifecycleRevision = value.lifecycleRevision;
116
124
  const agentHarnessId = value.agentHarnessId;
117
- if (!isSafeLegacySessionId(value.sessionId) || sessionFile !== void 0 && typeof sessionFile !== "string" || lifecycleRevision !== void 0 && typeof lifecycleRevision !== "string" || agentHarnessId !== void 0 && typeof agentHarnessId !== "string") return { failure: `session index ${storePath} has invalid entries` };
125
+ if (!sessionId || sessionId !== rawSessionId || sessionFile !== void 0 && typeof sessionFile !== "string" || lifecycleRevision !== void 0 && typeof lifecycleRevision !== "string" || agentHarnessId !== void 0 && typeof agentHarnessId !== "string") return { failure: `session index ${storePath} has invalid entries` };
118
126
  entries.push({
119
127
  sessionKey,
120
128
  entry: {
121
129
  sessionId,
122
130
  ...typeof sessionFile === "string" ? { sessionFile } : {},
123
131
  ...typeof lifecycleRevision === "string" ? { lifecycleRevision } : {},
124
- ...typeof agentHarnessId === "string" ? { agentHarnessId } : {},
125
- ...typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && value.updatedAt >= 0 ? { updatedAt: value.updatedAt } : {}
132
+ ...typeof agentHarnessId === "string" ? { agentHarnessId } : {}
126
133
  }
127
134
  });
128
135
  }
129
136
  return { entries };
130
137
  }
131
- async function* iterateIndexedSidecars(surface) {
138
+ async function* iterateIndexedSidecars(surface, params) {
132
139
  for (const storePath of surface.storePaths) {
133
140
  const index = await readLegacySessionIndex(storePath);
134
141
  if ("failure" in index) continue;
135
- for (const { entry } of index.entries) {
136
- if ((entry.sessionFile?.trim() ?? "").startsWith("sqlite:")) continue;
142
+ for (const { sessionKey, entry } of index.entries) {
143
+ const agentId = resolveLegacyBindingOwnerAgentId({
144
+ sessionKey,
145
+ config: params.config,
146
+ storeAgentIds: surface.agentIds
147
+ });
137
148
  let transcriptPath;
138
149
  try {
139
- transcriptPath = await resolveLegacySessionFileLocator(path.dirname(storePath), entry, entry.sessionId);
150
+ transcriptPath = resolveSessionFilePath(entry.sessionId, entry, {
151
+ sessionsDir: path.dirname(storePath),
152
+ agentId
153
+ });
140
154
  } catch {
141
155
  continue;
142
156
  }
143
157
  const sidecarPath = `${transcriptPath}${LEGACY_BINDING_SUFFIX}`;
144
- if (await legacyStateFileExists(sidecarPath)) yield sidecarPath;
158
+ if (await isRegularFile(sidecarPath)) yield sidecarPath;
145
159
  }
146
160
  }
147
161
  }
@@ -164,7 +178,7 @@ async function* walkSidecars(root) {
164
178
  }
165
179
  }
166
180
  async function collectBindingOwners(sources, surfaces, params) {
167
- const sourcePaths = new Set(await Promise.all(sources.map((source) => canonicalPathFromExistingAncestor(source.transcriptPath))));
181
+ const sourcePaths = new Set(await Promise.all(sources.map((source) => canonicalizePath(source.transcriptPath))));
168
182
  const owners = /* @__PURE__ */ new Map();
169
183
  const storePaths = new Set(surfaces.flatMap((surface) => [...surface.storePaths]));
170
184
  const storeAgentIds = /* @__PURE__ */ new Map();
@@ -175,7 +189,7 @@ async function collectBindingOwners(sources, surfaces, params) {
175
189
  }
176
190
  const failures = [];
177
191
  for (const storePath of storePaths) {
178
- const canonicalStorePath = await canonicalPathFromExistingAncestor(storePath);
192
+ const canonicalStorePath = await canonicalizePath(storePath);
179
193
  const index = await readLegacySessionIndex(storePath);
180
194
  if ("failure" in index) {
181
195
  failures.push(index.failure);
@@ -189,34 +203,35 @@ async function collectBindingOwners(sources, surfaces, params) {
189
203
  config: params.config,
190
204
  storeAgentIds: storeAgentIds.get(storePath)
191
205
  });
192
- let legacyTranscriptPath;
193
- let canonicalLegacyTranscriptPath;
206
+ let effectiveTranscriptPath;
194
207
  try {
195
- legacyTranscriptPath = await resolveLegacySessionFileLocator(sessionsDir, entry, sessionId);
196
- canonicalLegacyTranscriptPath = await canonicalPathFromExistingAncestor(legacyTranscriptPath);
208
+ effectiveTranscriptPath = resolveSessionFilePath(sessionId, entry, {
209
+ sessionsDir,
210
+ agentId
211
+ });
197
212
  } catch {
198
213
  failures.push(`session index ${storePath} has an invalid locator for ${sessionKey}`);
199
214
  continue;
200
215
  }
201
- if (!sourcePaths.has(canonicalLegacyTranscriptPath)) continue;
216
+ const transcriptPath = await canonicalizePath(effectiveTranscriptPath);
217
+ if (!sourcePaths.has(transcriptPath)) continue;
202
218
  const owner = {
203
219
  agentId,
204
220
  sessionId,
205
221
  sessionKey,
206
222
  storePath,
207
- transcriptPath: legacyTranscriptPath,
223
+ transcriptPath: effectiveTranscriptPath,
208
224
  ...entry.lifecycleRevision ? { lifecycleRevision: entry.lifecycleRevision } : {},
209
- ...entry.agentHarnessId?.trim() ? { agentHarnessId: entry.agentHarnessId.trim() } : {},
210
- ...entry.updatedAt !== void 0 ? { updatedAt: entry.updatedAt } : {}
225
+ ...entry.agentHarnessId?.trim() ? { agentHarnessId: entry.agentHarnessId.trim() } : {}
211
226
  };
212
- const candidates = owners.get(canonicalLegacyTranscriptPath) ?? /* @__PURE__ */ new Map();
227
+ const candidates = owners.get(transcriptPath) ?? /* @__PURE__ */ new Map();
213
228
  const ownerKey = `${agentId}\0${sessionId}\0${sessionKey}\0${canonicalStorePath}`;
214
229
  const configuredStorePath = resolveStorePath(params.config.session?.store, {
215
230
  agentId,
216
231
  env: params.env
217
232
  });
218
233
  if (!candidates.has(ownerKey) || storePath === configuredStorePath) candidates.set(ownerKey, owner);
219
- owners.set(canonicalLegacyTranscriptPath, candidates);
234
+ owners.set(transcriptPath, candidates);
220
235
  }
221
236
  }
222
237
  return {
@@ -224,16 +239,6 @@ async function collectBindingOwners(sources, surfaces, params) {
224
239
  failures
225
240
  };
226
241
  }
227
- async function resolveLegacySessionFileLocator(sessionsDir, entry, sessionId) {
228
- const base = path.resolve(sessionsDir);
229
- const fallback = path.join(base, `${sessionId}.jsonl`);
230
- const sessionFile = entry.sessionFile?.trim();
231
- if (!sessionFile) return fallback;
232
- const candidate = path.resolve(base, sessionFile);
233
- const [canonicalBase, canonicalCandidate] = await Promise.all([canonicalPathFromExistingAncestor(base), canonicalPathFromExistingAncestor(candidate)]);
234
- if (!isPathInside(canonicalBase, canonicalCandidate)) throw new Error("legacy session file locator escapes its session directory");
235
- return candidate;
236
- }
237
242
  function resolveLegacyBindingOwnerAgentId(params) {
238
243
  if (params.sessionKey.trim().toLowerCase().startsWith("agent:")) return resolveSessionAgentIds({
239
244
  sessionKey: params.sessionKey,
@@ -276,7 +281,7 @@ async function migrateSource(source, candidates, params, store) {
276
281
  return await withFileLock(source.sidecarPath, LEGACY_BINDING_LOCK_OPTIONS, async () => {
277
282
  const [contents, stat] = await Promise.all([fs.readFile(source.sidecarPath, "utf8"), fs.stat(source.sidecarPath)]);
278
283
  const raw = JSON.parse(contents);
279
- const [{ bindingStoreKey, createStoredCodexAppServerBinding, normalizeStoredCodexAppServerBindingFingerprints, readStoredCodexAppServerBinding }, { legacyCodexConversationBindingId }] = await Promise.all([import("./session-binding-BXpo9ymA.js").then((n) => n.f), import("./conversation-binding-data-BHRpYbX3.js").then((n) => n.t)]);
284
+ const [{ bindingStoreKey, createStoredCodexAppServerBinding, normalizeStoredCodexAppServerBindingFingerprints, readStoredCodexAppServerBinding }, { legacyCodexConversationBindingId }] = await Promise.all([import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c), import("./.setup/conversation-binding-data-CrRxtDBu.mjs").then((n) => n.t)]);
280
285
  const agentId = owner?.agentId ?? (source.agentIds.size === 1 ? [...source.agentIds][0] : void 0);
281
286
  const baseStored = createStoredCodexAppServerBinding(raw, {
282
287
  now: stat.mtime.toISOString(),
@@ -354,7 +359,7 @@ async function migrateSource(source, candidates, params, store) {
354
359
  if (!hasExpected(await store.lookup(entry.key), entry.value)) return retain(`canonical plugin state changed at ${entry.key}`);
355
360
  }
356
361
  if (owner) {
357
- const ownershipWarning = await recordSessionOwner(owner, params.env);
362
+ const ownershipWarning = await recordSessionOwner(owner);
358
363
  if (ownershipWarning) {
359
364
  if (sessionEntry?.value.state === "active") {
360
365
  const update = store.update;
@@ -372,18 +377,11 @@ async function migrateSource(source, candidates, params, store) {
372
377
  });
373
378
  if (hasExpected(await store.lookup(sessionEntry.key), sessionEntry.value)) return retain(`${ownershipWarning}; its stale session binding could not be retired`);
374
379
  }
375
- return retainNotice(ownershipWarning);
380
+ return retain(ownershipWarning);
376
381
  }
377
382
  for (const entry of entries) if (!hasExpected(await store.lookup(entry.key), entry.value)) return retain(`canonical plugin state changed at ${entry.key}`);
378
383
  }
379
- const archiveWarnings = [];
380
- await archiveLegacyStateSource({
381
- filePath: source.sidecarPath,
382
- label: "Codex app-server binding",
383
- changes: [],
384
- warnings: archiveWarnings
385
- });
386
- if (archiveWarnings.length > 0 && await pathExists(source.sidecarPath)) return retain(archiveWarnings.join("; "));
384
+ await archiveBindingSidecar(source.sidecarPath);
387
385
  return {
388
386
  archived: true,
389
387
  importedKeys
@@ -397,39 +395,18 @@ async function migrateSource(source, candidates, params, store) {
397
395
  return retain(`migration or archiving failed: ${String(error)}`);
398
396
  }
399
397
  }
400
- async function recordSessionOwner(owner, env) {
401
- const currentIndex = await readLegacySessionIndex(owner.storePath);
402
- if ("failure" in currentIndex) return "its legacy session owner could not be revalidated";
403
- const currentOwner = currentIndex.entries.find(({ sessionKey }) => sessionKey === owner.sessionKey);
404
- if (!currentOwner || currentOwner.entry.sessionId !== owner.sessionId) return "its session owner changed before Codex ownership could be recorded";
405
- let currentTranscriptPath;
406
- try {
407
- currentTranscriptPath = await resolveLegacySessionFileLocator(path.dirname(owner.storePath), currentOwner.entry, currentOwner.entry.sessionId);
408
- } catch {
409
- return "its session owner changed before Codex ownership could be recorded";
410
- }
411
- if (await canonicalPathFromExistingAncestor(currentTranscriptPath) !== await canonicalPathFromExistingAncestor(owner.transcriptPath) || currentOwner.entry.lifecycleRevision !== owner.lifecycleRevision) return "its session owner changed before Codex ownership could be recorded";
412
- const legacyHarnessId = currentOwner.entry.agentHarnessId?.trim();
413
- if (currentOwner.entry.agentHarnessId !== void 0 && !legacyHarnessId) return "its session owner changed before Codex ownership could be recorded";
414
- if (legacyHarnessId && legacyHarnessId !== CODEX_AGENT_HARNESS_ID) return `its session is owned by agent harness ${legacyHarnessId}`;
398
+ async function recordSessionOwner(owner) {
415
399
  let observedForeignHarness;
416
- const updated = await patchSessionEntry({
417
- agentId: owner.agentId,
418
- env,
419
- fallbackEntry: {
420
- sessionId: owner.sessionId,
421
- updatedAt: currentOwner.entry.updatedAt ?? owner.updatedAt ?? 0,
422
- ...owner.lifecycleRevision ? { lifecycleRevision: owner.lifecycleRevision } : {}
423
- },
424
- preserveActivity: true,
425
- requireWriteSuccess: true,
426
- skipMaintenance: true,
400
+ const updated = await updateSessionStoreEntry({
427
401
  storePath: owner.storePath,
428
402
  sessionKey: owner.sessionKey,
403
+ skipMaintenance: true,
404
+ requireWriteSuccess: true,
429
405
  update: (entry) => {
430
- if (entry.sessionId.trim() !== owner.sessionId || entry.lifecycleRevision !== owner.lifecycleRevision) return null;
406
+ const transcriptPath = resolveOwnerTranscriptPath(owner, entry);
407
+ if (entry.sessionId.trim() !== owner.sessionId || transcriptPath !== owner.transcriptPath || entry.lifecycleRevision !== owner.lifecycleRevision) return null;
431
408
  const harnessId = typeof entry.agentHarnessId === "string" ? entry.agentHarnessId.trim() : void 0;
432
- if (entry.agentHarnessId !== void 0 && !harnessId) return null;
409
+ if (entry.agentHarnessId !== void 0 && harnessId === void 0) return null;
433
410
  if (harnessId && harnessId !== CODEX_AGENT_HARNESS_ID) {
434
411
  observedForeignHarness = harnessId;
435
412
  return null;
@@ -438,10 +415,21 @@ async function recordSessionOwner(owner, env) {
438
415
  }
439
416
  });
440
417
  if (!updated) return observedForeignHarness ? `its session is owned by agent harness ${observedForeignHarness}` : "its session owner changed before Codex ownership could be recorded";
441
- if (updated.sessionId.trim() !== owner.sessionId || updated.lifecycleRevision !== owner.lifecycleRevision) return "its session owner changed before Codex ownership could be recorded";
418
+ const transcriptPath = resolveOwnerTranscriptPath(owner, updated);
419
+ if (updated.sessionId.trim() !== owner.sessionId || transcriptPath !== owner.transcriptPath || updated.lifecycleRevision !== owner.lifecycleRevision) return "its session owner changed before Codex ownership could be recorded";
442
420
  const harnessId = updated.agentHarnessId?.trim();
443
421
  return harnessId === CODEX_AGENT_HARNESS_ID ? void 0 : harnessId ? `its session is owned by agent harness ${harnessId}` : "Codex harness ownership could not be recorded on its session";
444
422
  }
423
+ function resolveOwnerTranscriptPath(owner, entry) {
424
+ try {
425
+ return resolveSessionFilePath(entry.sessionId, entry, {
426
+ sessionsDir: path.dirname(owner.storePath),
427
+ agentId: owner.agentId
428
+ });
429
+ } catch {
430
+ return;
431
+ }
432
+ }
445
433
  async function readDirectoryEntries(directory) {
446
434
  try {
447
435
  return await fs.readdir(directory, { withFileTypes: true });
@@ -455,10 +443,53 @@ async function readDirectoryEntries(directory) {
455
443
  throw error;
456
444
  }
457
445
  }
458
- function isSafeLegacySessionId(value) {
459
- if (typeof value !== "string") return false;
460
- const trimmed = value.trim();
461
- return trimmed.length > 0 && trimmed.length <= 255 && /^[A-Za-z0-9][A-Za-z0-9._:@-]*$/.test(trimmed);
446
+ async function isRegularFile(filePath) {
447
+ try {
448
+ return (await fs.stat(filePath)).isFile();
449
+ } catch {
450
+ return false;
451
+ }
452
+ }
453
+ function isRecord(value) {
454
+ return value !== null && typeof value === "object" && !Array.isArray(value);
455
+ }
456
+ function isPathWithin(root, candidate) {
457
+ const relative = path.relative(root, candidate);
458
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
459
+ }
460
+ async function canonicalizePath(filePath) {
461
+ try {
462
+ return await fs.realpath(filePath);
463
+ } catch {
464
+ return path.resolve(filePath);
465
+ }
466
+ }
467
+ async function pathExists(filePath) {
468
+ try {
469
+ await fs.access(filePath);
470
+ return true;
471
+ } catch {
472
+ return false;
473
+ }
474
+ }
475
+ async function firstFreeArchivePath(sourcePath) {
476
+ for (let index = 2;; index++) {
477
+ const candidate = `${sourcePath}.migrated.${index}`;
478
+ if (!await pathExists(candidate)) return candidate;
479
+ }
480
+ }
481
+ async function archiveBindingSidecar(sourcePath) {
482
+ const archivePath = `${sourcePath}.migrated`;
483
+ if (await pathExists(archivePath)) {
484
+ const [sourceBytes, archiveBytes] = await Promise.all([fs.readFile(sourcePath), fs.readFile(archivePath)]);
485
+ if (sourceBytes.equals(archiveBytes)) {
486
+ await fs.rm(sourcePath, { force: true });
487
+ return;
488
+ }
489
+ await fs.rename(sourcePath, await firstFreeArchivePath(sourcePath));
490
+ return;
491
+ }
492
+ await fs.rename(sourcePath, archivePath);
462
493
  }
463
494
  const stateMigrations = [{
464
495
  id: "codex-app-server-sidecars-to-plugin-state",
@@ -492,7 +523,7 @@ const stateMigrations = [{
492
523
  let migrated = 0;
493
524
  let partialImports = 0;
494
525
  for (const source of sources) {
495
- const result = await migrateSource(source, ownerCollection.owners.get(await canonicalPathFromExistingAncestor(source.transcriptPath)) ?? [], params, store);
526
+ const result = await migrateSource(source, ownerCollection.owners.get(await canonicalizePath(source.transcriptPath)) ?? [], params, store);
496
527
  if (result.warning) warnings.push(result.warning);
497
528
  if (result.notice) notices.push(result.notice);
498
529
  if (result.archived) migrated++;
@@ -500,10 +531,13 @@ const stateMigrations = [{
500
531
  }
501
532
  if (migrated > 0) changes.push(`Migrated ${migrated} Codex app-server binding sidecar(s) to plugin state and archived the legacy sources`);
502
533
  if (partialImports > 0) changes.push(`Migrated ${partialImports} safe Codex app-server binding row(s) to plugin state; retained legacy sidecars needing review`);
503
- return {
534
+ return notices.length > 0 ? {
504
535
  changes,
505
536
  warnings,
506
- ...notices.length > 0 ? { notices } : {}
537
+ notices
538
+ } : {
539
+ changes,
540
+ warnings
507
541
  };
508
542
  }
509
543
  }];
package/dist/harness.js CHANGED
@@ -1,6 +1,5 @@
1
1
  //#region extensions/codex/harness.ts
2
2
  const DEFAULT_CODEX_HARNESS_PROVIDER_IDS = /* @__PURE__ */ new Set(["codex", "openai"]);
3
- const SHARED_CODEX_APP_SERVER_CLIENT_DISPOSER = Symbol.for("openclaw.codexAppServerClientDisposer");
4
3
  const CODEX_APP_SERVER_CONTEXT_ENGINE_HOST_CAPABILITIES = [
5
4
  "bootstrap",
6
5
  "assemble-before-prompt",
@@ -10,108 +9,38 @@ const CODEX_APP_SERVER_CONTEXT_ENGINE_HOST_CAPABILITIES = [
10
9
  "runtime-llm-complete",
11
10
  "thread-bootstrap-projection"
12
11
  ];
13
- async function disposeSharedCodexAppServerClients() {
14
- const dispose = globalThis[SHARED_CODEX_APP_SERVER_CLIENT_DISPOSER];
15
- await dispose?.();
16
- }
17
12
  /**
18
13
  * Creates the Codex app-server harness used for attempts, side questions,
19
14
  * compaction, reset, and disposal.
20
15
  */
21
16
  function createCodexAppServerAgentHarness(options) {
22
- const harnessRuntimeId = options?.id ?? "codex";
23
- const normalizedHarnessRuntimeId = harnessRuntimeId.trim().toLowerCase();
24
17
  const providerIds = new Set([...options?.providerIds ?? DEFAULT_CODEX_HARNESS_PROVIDER_IDS].map((id) => id.trim().toLowerCase()));
25
- const sessionCatalogControl = options.sessionCatalogControl;
26
- const sessionRuntime = options.runtime;
27
18
  return {
28
- id: harnessRuntimeId,
19
+ id: options?.id ?? "codex",
29
20
  label: options?.label ?? "Codex agent harness",
30
- autoSelection: { providerIds: [...providerIds] },
31
- delegatedExecutionPluginIds: ["voice-call"],
32
21
  contextEngineHostCapabilities: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST_CAPABILITIES,
33
- deliveryDefaults: { visibleReplies: "message_tool" },
34
- authBootstrap: "harness",
35
- ...sessionCatalogControl && sessionRuntime ? { sessionFork: {
36
- upstreamKinds: ["codex-app-server"],
37
- fork: async (params) => {
38
- const { forkCodexUpstreamSession } = await import("./upstream-session-fork-Dy4Xvuoj.js");
39
- return await forkCodexUpstreamSession(params, {
40
- bindingStore: options.bindingStore,
41
- control: sessionCatalogControl,
42
- harnessRuntimeId,
43
- resolveConfig: options.resolveConfig,
44
- runtime: sessionRuntime
45
- });
46
- }
47
- } } : {},
48
- authBinding: { fingerprint: async (params) => {
49
- const { fingerprintCodexAppServerAuthBinding } = await import("./auth-binding-B4FfbcCg.js").then((n) => n.t);
50
- return fingerprintCodexAppServerAuthBinding(params);
51
- } },
52
- runtimeArtifact: { validate: async (binding) => {
53
- const { validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-CmtG_t1L.js");
54
- return validateCodexAppServerRuntimeArtifact(binding);
55
- } },
56
- fetchUsageSnapshot: async (ctx) => {
57
- const { fetchCodexAppServerUsageSnapshot } = await import("./usage-CQ6GhD5-.js");
58
- return await fetchCodexAppServerUsageSnapshot(ctx, { pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig });
59
- },
60
- loadMcpToolCatalog: async (params) => {
61
- const { loadCodexEffectiveMcpCatalog } = await import("./effective-mcp-catalog-Bbd98Inw.js");
62
- return await loadCodexEffectiveMcpCatalog(params, { bindingStore: options.bindingStore });
63
- },
22
+ deliveryDefaults: { sourceVisibleReplies: "message_tool" },
64
23
  supports: (ctx) => {
65
24
  const provider = ctx.provider.trim().toLowerCase();
66
- if (!providerIds.has(provider)) return {
67
- supported: false,
68
- reason: `provider is not one of: ${[...providerIds].toSorted().join(", ")}`
69
- };
70
- if (ctx.modelProvider?.requestTransportOverrides === "present") return {
71
- supported: false,
72
- reason: "Codex cannot reproduce authored request transport overrides"
73
- };
74
- const preparedAuth = ctx.modelProvider?.preparedAuth;
75
- const runtimePolicy = ctx.modelProvider?.runtimePolicy;
76
- if (runtimePolicy) {
77
- if (!runtimePolicy.compatibleIds.some((id) => id.trim().toLowerCase() === normalizedHarnessRuntimeId)) return {
78
- supported: false,
79
- reason: "Codex cannot reproduce the prepared provider route"
80
- };
81
- } else if (ctx.modelProvider && provider !== "codex") return {
82
- supported: false,
83
- reason: "provider route compatibility with Codex is not declared"
84
- };
85
- if (preparedAuth?.requirement === "subscription") {
86
- if (!(preparedAuth.source === "profile" && (preparedAuth.mode === "oauth" || preparedAuth.mode === "token"))) return {
87
- supported: false,
88
- reason: "Codex subscription auth requires a prepared OAuth or token profile"
89
- };
90
- } else if (preparedAuth?.requirement === "api-key") {
91
- if (!(preparedAuth.source !== "none" && preparedAuth.source !== "harness" && (preparedAuth.mode === "api-key" || preparedAuth.mode === "api_key"))) return {
92
- supported: false,
93
- reason: "Codex Platform auth requires a prepared API key"
94
- };
95
- }
96
- return {
25
+ if (providerIds.has(provider)) return {
97
26
  supported: true,
98
27
  priority: 100
99
28
  };
29
+ return {
30
+ supported: false,
31
+ reason: `provider is not one of: ${[...providerIds].toSorted().join(", ")}`
32
+ };
100
33
  },
101
34
  runAttempt: async (params) => {
102
- const { runCodexAppServerAttempt } = await import("./run-attempt-CY9WlDLc.js");
35
+ const { runCodexAppServerAttempt } = await import("./.setup/run-attempt-C7xEKFVB.mjs");
103
36
  return runCodexAppServerAttempt(params, {
104
37
  bindingStore: options.bindingStore,
105
38
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
106
39
  nativeHookRelay: { enabled: true }
107
40
  });
108
41
  },
109
- finalizeSettledTurn: async (params) => {
110
- const { runCodexSettledTurnFinalization } = await import("./settled-turn-finalizer-CXY2Z9dT.js");
111
- return runCodexSettledTurnFinalization(params, { pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig });
112
- },
113
42
  runSideQuestion: async (params) => {
114
- const { runCodexAppServerSideQuestion } = await import("./side-question-_ulBk1AL.js");
43
+ const { runCodexAppServerSideQuestion } = await import("./.setup/side-question-Cv6JXWCS.mjs");
115
44
  return runCodexAppServerSideQuestion(params, {
116
45
  bindingStore: options.bindingStore,
117
46
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
@@ -119,14 +48,14 @@ function createCodexAppServerAgentHarness(options) {
119
48
  });
120
49
  },
121
50
  compact: async (params) => {
122
- const { maybeCompactCodexAppServerSession } = await import("./compact-D_uvHurS.js");
51
+ const { maybeCompactCodexAppServerSession } = await import("./.setup/compact-BNgh7I4t.mjs");
123
52
  return maybeCompactCodexAppServerSession(params, {
124
53
  bindingStore: options.bindingStore,
125
54
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig
126
55
  });
127
56
  },
128
57
  compactAfterContextEngine: async (params) => {
129
- const { maybeCompactCodexAppServerSession } = await import("./compact-D_uvHurS.js");
58
+ const { maybeCompactCodexAppServerSession } = await import("./.setup/compact-BNgh7I4t.mjs");
130
59
  return maybeCompactCodexAppServerSession(params, {
131
60
  bindingStore: options.bindingStore,
132
61
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
@@ -135,25 +64,27 @@ function createCodexAppServerAgentHarness(options) {
135
64
  },
136
65
  reset: async (params) => {
137
66
  if (params.sessionId) {
138
- const { reclaimCurrentCodexSessionGeneration, sessionBindingIdentity } = await import("./session-binding-BXpo9ymA.js").then((n) => n.f);
67
+ const { reclaimCurrentCodexSessionGeneration, sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
139
68
  const identity = sessionBindingIdentity({
140
69
  agentId: params.agentId,
141
70
  sessionId: params.sessionId,
142
71
  sessionKey: params.sessionKey
143
72
  });
144
- const resetGeneration = params.reason === "deleted" ? options.bindingStore.retireSessionGeneration.bind(options.bindingStore) : options.bindingStore.resetSessionGeneration.bind(options.bindingStore);
145
- let reset = await resetGeneration(identity);
146
- if (reset === "conflict") {
73
+ let retired = await options.bindingStore.retireSessionGeneration(identity);
74
+ if (retired === "conflict") {
147
75
  if (await reclaimCurrentCodexSessionGeneration({
148
76
  bindingStore: options.bindingStore,
149
77
  identity,
150
78
  config: options.resolveConfig?.()
151
- })) reset = await resetGeneration(identity);
79
+ })) retired = await options.bindingStore.retireSessionGeneration(identity);
152
80
  }
153
- if (reset === "conflict") throw new Error(`Codex binding generation changed before session ${params.sessionId} could reset`);
81
+ if (retired === "conflict") throw new Error(`Codex binding generation changed before session ${params.sessionId} could reset`);
154
82
  }
155
83
  },
156
- dispose: disposeSharedCodexAppServerClients
84
+ dispose: async () => {
85
+ const { clearSharedCodexAppServerClientAndWait } = await import("./.setup/shared-client-B2KmLPRw.mjs").then((n) => n.c);
86
+ await clearSharedCodexAppServerClientAndWait();
87
+ }
157
88
  };
158
89
  }
159
90
  //#endregion