@openclaw/codex 2026.7.1-beta.2 → 2026.7.1-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-server-policy-B7hqD3Xj.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-tqyQ_Tdn.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-C9K-KRU-.js → command-handlers-UVniG5Hl.js} +267 -232
- package/dist/command-rpc-BjgEPh19.js +784 -0
- package/dist/compact-CZzbcygx.js +603 -0
- package/dist/{computer-use-CpurU-G9.js → computer-use-DDeySrnb.js} +3 -3
- package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +439 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1117 -19
- package/dist/{media-understanding-provider-BOeoBhEw.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-DpzY1xpp.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-BrZUG9jj.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-CJiWigAJ.js → protocol-validators-dZQ-UTOa.js} +116 -59
- package/dist/{provider-capabilities-wqedK49Z.js → provider-capabilities-gTCwjfmh.js} +2916 -114
- package/dist/{provider-nmtMDcGH.js → provider-cc_62eQE.js} +19 -10
- package/dist/provider.js +2 -2
- package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-DikbXia_.js → run-attempt-8SPLiDlT.js} +907 -3002
- package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
- package/dist/session-binding-Dc03iwRF.js +595 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
- package/dist/{side-question-Ca9wqmd0.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-D6R40plk.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-B6eZEFmd.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +20 -0
- package/package.json +9 -6
- package/dist/client-IcTBKEqA.js +0 -732
- package/dist/client-factory-DfFu9JH_.js +0 -15
- package/dist/compact-CILptrPS.js +0 -278
- package/dist/conversation-binding-DOJtUxOy.js +0 -1570
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-Cm0Gg7Z1.js +0 -339
|
@@ -1,3 +1,396 @@
|
|
|
1
|
+
import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { listAgentIds, resolveAgentDir, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
5
|
+
import { isDeepStrictEqual } from "node:util";
|
|
6
|
+
import { listSessionEntries, resolveSessionFilePath, resolveStorePath, updateSessionStoreEntry } from "openclaw/plugin-sdk/session-store-runtime";
|
|
7
|
+
import { withFileLock } from "openclaw/plugin-sdk/file-lock";
|
|
8
|
+
//#region extensions/codex/src/migration/session-binding-sidecars.ts
|
|
9
|
+
const LEGACY_BINDING_SUFFIX = ".codex-app-server.json";
|
|
10
|
+
const CODEX_AGENT_HARNESS_ID = "codex";
|
|
11
|
+
const MAX_SESSION_DIRECTORY_DEPTH = 16;
|
|
12
|
+
const LEGACY_BINDING_LOCK_OPTIONS = {
|
|
13
|
+
retries: {
|
|
14
|
+
retries: 75,
|
|
15
|
+
factor: 1,
|
|
16
|
+
minTimeout: 1e3,
|
|
17
|
+
maxTimeout: 1e3
|
|
18
|
+
},
|
|
19
|
+
stale: 12e4
|
|
20
|
+
};
|
|
21
|
+
async function collectSessionSurfaces(params) {
|
|
22
|
+
const surfaces = /* @__PURE__ */ new Map();
|
|
23
|
+
const stateRoot = await canonicalizePath(params.stateDir);
|
|
24
|
+
const add = async (root, storePath, agentId, scan) => {
|
|
25
|
+
const canonicalRoot = await canonicalizePath(root);
|
|
26
|
+
const surface = surfaces.get(canonicalRoot) ?? {
|
|
27
|
+
root: canonicalRoot,
|
|
28
|
+
scan: false,
|
|
29
|
+
storePaths: /* @__PURE__ */ new Set(),
|
|
30
|
+
agentIds: /* @__PURE__ */ new Set()
|
|
31
|
+
};
|
|
32
|
+
surface.scan ||= scan;
|
|
33
|
+
surface.storePaths.add(path.resolve(storePath));
|
|
34
|
+
surface.agentIds.add(agentId);
|
|
35
|
+
surfaces.set(canonicalRoot, surface);
|
|
36
|
+
};
|
|
37
|
+
const agentIds = new Set(listAgentIds(params.config));
|
|
38
|
+
const agentsDir = path.join(params.stateDir, "agents");
|
|
39
|
+
for (const entry of await readDirectoryEntries(agentsDir)) {
|
|
40
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
41
|
+
const agentId = resolveSessionAgentIds({
|
|
42
|
+
agentId: entry.name,
|
|
43
|
+
config: params.config
|
|
44
|
+
}).sessionAgentId;
|
|
45
|
+
agentIds.add(agentId);
|
|
46
|
+
const root = path.join(agentsDir, entry.name, "sessions");
|
|
47
|
+
await add(root, path.join(root, "sessions.json"), agentId, true);
|
|
48
|
+
}
|
|
49
|
+
for (const agentId of agentIds) {
|
|
50
|
+
const storePath = resolveStorePath(params.config.session?.store, {
|
|
51
|
+
agentId,
|
|
52
|
+
env: params.env
|
|
53
|
+
});
|
|
54
|
+
const root = path.dirname(storePath);
|
|
55
|
+
await add(root, storePath, agentId, isPathWithin(stateRoot, await canonicalizePath(root)));
|
|
56
|
+
}
|
|
57
|
+
const legacyRoot = path.join(params.stateDir, "sessions");
|
|
58
|
+
const defaultAgentId = resolveSessionAgentIds({ config: params.config }).defaultAgentId;
|
|
59
|
+
await add(legacyRoot, path.join(legacyRoot, "sessions.json"), defaultAgentId, true);
|
|
60
|
+
return [...surfaces.values()].toSorted((a, b) => a.root.localeCompare(b.root));
|
|
61
|
+
}
|
|
62
|
+
async function collectLegacyBindingSources(params, options = {}) {
|
|
63
|
+
const surfaces = await collectSessionSurfaces(params);
|
|
64
|
+
const sources = /* @__PURE__ */ new Map();
|
|
65
|
+
const addSource = async (sidecarPath, surface) => {
|
|
66
|
+
const canonicalSidecar = await canonicalizePath(sidecarPath);
|
|
67
|
+
const source = sources.get(canonicalSidecar) ?? {
|
|
68
|
+
sidecarPath: canonicalSidecar,
|
|
69
|
+
transcriptPath: sidecarPath.slice(0, -22),
|
|
70
|
+
agentIds: /* @__PURE__ */ new Set()
|
|
71
|
+
};
|
|
72
|
+
for (const agentId of surface.agentIds) source.agentIds.add(agentId);
|
|
73
|
+
sources.set(canonicalSidecar, source);
|
|
74
|
+
return source;
|
|
75
|
+
};
|
|
76
|
+
for (const surface of surfaces) {
|
|
77
|
+
const sidecars = surface.scan ? walkSidecars(surface.root) : iterateIndexedSidecars(surface, params);
|
|
78
|
+
for await (const sidecarPath of sidecars) {
|
|
79
|
+
const source = await addSource(sidecarPath, surface);
|
|
80
|
+
if (options.firstOnly) return {
|
|
81
|
+
sources: [source],
|
|
82
|
+
surfaces
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
sources: [...sources.values()].toSorted((a, b) => a.sidecarPath.localeCompare(b.sidecarPath)),
|
|
88
|
+
surfaces
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function* iterateIndexedSidecars(surface, params) {
|
|
92
|
+
for (const storePath of surface.storePaths) {
|
|
93
|
+
let entries;
|
|
94
|
+
try {
|
|
95
|
+
entries = listSessionEntries({
|
|
96
|
+
storePath,
|
|
97
|
+
hydrateSkillPromptRefs: false
|
|
98
|
+
});
|
|
99
|
+
} catch {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
for (const { sessionKey, entry } of entries) {
|
|
103
|
+
const sessionId = entry.sessionId?.trim();
|
|
104
|
+
if (!sessionId) continue;
|
|
105
|
+
const agentId = resolveLegacyBindingOwnerAgentId({
|
|
106
|
+
sessionKey,
|
|
107
|
+
config: params.config,
|
|
108
|
+
storeAgentIds: surface.agentIds
|
|
109
|
+
});
|
|
110
|
+
let transcriptPath;
|
|
111
|
+
try {
|
|
112
|
+
transcriptPath = resolveSessionFilePath(sessionId, entry, {
|
|
113
|
+
sessionsDir: path.dirname(storePath),
|
|
114
|
+
agentId
|
|
115
|
+
});
|
|
116
|
+
} catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const sidecarPath = `${transcriptPath}${LEGACY_BINDING_SUFFIX}`;
|
|
120
|
+
if (await isRegularFile(sidecarPath)) yield sidecarPath;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function* walkSidecars(root) {
|
|
125
|
+
const pending = [{
|
|
126
|
+
directory: root,
|
|
127
|
+
depth: 0
|
|
128
|
+
}];
|
|
129
|
+
while (pending.length > 0) {
|
|
130
|
+
const current = pending.pop();
|
|
131
|
+
for (const entry of (await readDirectoryEntries(current.directory)).toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
132
|
+
if (entry.isSymbolicLink()) continue;
|
|
133
|
+
const entryPath = path.join(current.directory, entry.name);
|
|
134
|
+
if (entry.isFile() && entry.name.endsWith(LEGACY_BINDING_SUFFIX)) yield entryPath;
|
|
135
|
+
else if (entry.isDirectory() && current.depth < MAX_SESSION_DIRECTORY_DEPTH) pending.push({
|
|
136
|
+
directory: entryPath,
|
|
137
|
+
depth: current.depth + 1
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function collectBindingOwners(sources, surfaces, params) {
|
|
143
|
+
const sourcePaths = new Set(await Promise.all(sources.map((source) => canonicalizePath(source.transcriptPath))));
|
|
144
|
+
const owners = /* @__PURE__ */ new Map();
|
|
145
|
+
const storePaths = new Set(surfaces.flatMap((surface) => [...surface.storePaths]));
|
|
146
|
+
const storeAgentIds = /* @__PURE__ */ new Map();
|
|
147
|
+
for (const surface of surfaces) for (const storePath of surface.storePaths) {
|
|
148
|
+
const agents = storeAgentIds.get(storePath) ?? /* @__PURE__ */ new Set();
|
|
149
|
+
for (const agentId of surface.agentIds) agents.add(agentId);
|
|
150
|
+
storeAgentIds.set(storePath, agents);
|
|
151
|
+
}
|
|
152
|
+
for (const storePath of storePaths) {
|
|
153
|
+
let entries;
|
|
154
|
+
try {
|
|
155
|
+
entries = listSessionEntries({
|
|
156
|
+
storePath,
|
|
157
|
+
hydrateSkillPromptRefs: false
|
|
158
|
+
});
|
|
159
|
+
} catch {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const sessionsDir = path.dirname(storePath);
|
|
163
|
+
for (const { sessionKey, entry } of entries) {
|
|
164
|
+
const sessionId = entry.sessionId?.trim();
|
|
165
|
+
if (!sessionId) continue;
|
|
166
|
+
const agentId = resolveLegacyBindingOwnerAgentId({
|
|
167
|
+
sessionKey,
|
|
168
|
+
config: params.config,
|
|
169
|
+
storeAgentIds: storeAgentIds.get(storePath)
|
|
170
|
+
});
|
|
171
|
+
let transcriptPath;
|
|
172
|
+
let legacyTranscriptPath;
|
|
173
|
+
try {
|
|
174
|
+
legacyTranscriptPath = resolveLegacySessionFileLocator(sessionsDir, entry, sessionId);
|
|
175
|
+
transcriptPath = await canonicalizePath(resolveSessionFilePath(sessionId, entry, {
|
|
176
|
+
sessionsDir,
|
|
177
|
+
agentId
|
|
178
|
+
}));
|
|
179
|
+
} catch {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (!sourcePaths.has(transcriptPath)) continue;
|
|
183
|
+
const owner = {
|
|
184
|
+
agentId,
|
|
185
|
+
sessionId,
|
|
186
|
+
sessionKey,
|
|
187
|
+
storePath,
|
|
188
|
+
transcriptPath: legacyTranscriptPath,
|
|
189
|
+
...entry.agentHarnessId?.trim() ? { agentHarnessId: entry.agentHarnessId.trim() } : {}
|
|
190
|
+
};
|
|
191
|
+
const candidates = owners.get(transcriptPath) ?? /* @__PURE__ */ new Map();
|
|
192
|
+
candidates.set(`${agentId}\0${sessionId}\0${sessionKey}\0${storePath}`, owner);
|
|
193
|
+
owners.set(transcriptPath, candidates);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return new Map([...owners].map(([key, values]) => [key, [...values.values()]]));
|
|
197
|
+
}
|
|
198
|
+
function resolveLegacySessionFileLocator(sessionsDir, entry, sessionId) {
|
|
199
|
+
const sessionFile = entry.sessionFile?.trim();
|
|
200
|
+
return path.resolve(sessionsDir, sessionFile || `${sessionId}.jsonl`);
|
|
201
|
+
}
|
|
202
|
+
function resolveLegacyBindingOwnerAgentId(params) {
|
|
203
|
+
if (params.sessionKey.trim().toLowerCase().startsWith("agent:")) return resolveSessionAgentIds({
|
|
204
|
+
sessionKey: params.sessionKey,
|
|
205
|
+
config: params.config
|
|
206
|
+
}).sessionAgentId;
|
|
207
|
+
const storeAgentId = params.storeAgentIds?.size === 1 ? [...params.storeAgentIds][0] : void 0;
|
|
208
|
+
return resolveSessionAgentIds({
|
|
209
|
+
sessionKey: params.sessionKey,
|
|
210
|
+
config: params.config,
|
|
211
|
+
...storeAgentId ? { agentId: storeAgentId } : {}
|
|
212
|
+
}).sessionAgentId;
|
|
213
|
+
}
|
|
214
|
+
function copyBindingForSession(stored, sessionId) {
|
|
215
|
+
return stored.state === "active" ? {
|
|
216
|
+
version: 1,
|
|
217
|
+
state: "active",
|
|
218
|
+
binding: stored.binding,
|
|
219
|
+
sessionId
|
|
220
|
+
} : {
|
|
221
|
+
version: 1,
|
|
222
|
+
state: "cleared",
|
|
223
|
+
sessionId,
|
|
224
|
+
...stored.retired ? { retired: true } : {}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
async function migrateSource(source, owner, params, store) {
|
|
228
|
+
let importedKeys = 0;
|
|
229
|
+
const retain = (reason) => ({
|
|
230
|
+
archived: false,
|
|
231
|
+
importedKeys,
|
|
232
|
+
warning: `Left Codex binding sidecar in place because ${reason}: ${source.sidecarPath}`
|
|
233
|
+
});
|
|
234
|
+
try {
|
|
235
|
+
return await withFileLock(source.sidecarPath, LEGACY_BINDING_LOCK_OPTIONS, async () => {
|
|
236
|
+
const [contents, stat] = await Promise.all([fs.readFile(source.sidecarPath, "utf8"), fs.stat(source.sidecarPath)]);
|
|
237
|
+
const raw = JSON.parse(contents);
|
|
238
|
+
const [{ bindingStoreKey, createStoredCodexAppServerBinding, readCodexAppServerThreadBinding }, { legacyCodexConversationBindingId }] = await Promise.all([import("./session-binding-Dc03iwRF.js").then((n) => n.s), import("./conversation-binding-data-BHRpYbX3.js").then((n) => n.t)]);
|
|
239
|
+
const agentId = owner?.agentId ?? (source.agentIds.size === 1 ? [...source.agentIds][0] : void 0);
|
|
240
|
+
const baseStored = createStoredCodexAppServerBinding(raw, {
|
|
241
|
+
now: stat.mtime.toISOString(),
|
|
242
|
+
lookup: {
|
|
243
|
+
config: params.config,
|
|
244
|
+
...agentId ? { agentDir: resolveAgentDir(params.config, agentId, params.env) } : {}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
if (!baseStored) return retain("its binding is invalid");
|
|
248
|
+
if (owner?.agentHarnessId && owner.agentHarnessId !== CODEX_AGENT_HARNESS_ID) return retain(`its session is owned by agent harness ${owner.agentHarnessId}`);
|
|
249
|
+
const sourceSessionFile = typeof raw.sessionFile === "string" && raw.sessionFile.trim() ? raw.sessionFile : source.transcriptPath;
|
|
250
|
+
const ownerSessionFile = typeof raw.sessionFile === "string" && raw.sessionFile.trim() ? raw.sessionFile : owner?.transcriptPath;
|
|
251
|
+
const conversationKeys = [sourceSessionFile, ...ownerSessionFile && ownerSessionFile !== sourceSessionFile ? [ownerSessionFile] : []].map((sessionFile) => bindingStoreKey({
|
|
252
|
+
kind: "conversation",
|
|
253
|
+
bindingId: legacyCodexConversationBindingId(sessionFile)
|
|
254
|
+
}));
|
|
255
|
+
let currentConversation;
|
|
256
|
+
for (const key of conversationKeys) currentConversation ??= await store.lookup(key);
|
|
257
|
+
const stored = currentConversation ?? baseStored;
|
|
258
|
+
if (stored.state !== "active" && stored.state !== "cleared") return retain(`canonical plugin state changed at ${conversationKeys[0]}`);
|
|
259
|
+
const sessionKey = owner ? bindingStoreKey({
|
|
260
|
+
kind: "session",
|
|
261
|
+
agentId: owner.agentId,
|
|
262
|
+
sessionId: owner.sessionId,
|
|
263
|
+
sessionKey: owner.sessionKey
|
|
264
|
+
}) : void 0;
|
|
265
|
+
const entries = [...conversationKeys.map((key) => ({
|
|
266
|
+
key,
|
|
267
|
+
value: stored
|
|
268
|
+
})), ...owner && sessionKey ? [{
|
|
269
|
+
key: sessionKey,
|
|
270
|
+
value: copyBindingForSession(stored, owner.sessionId)
|
|
271
|
+
}] : []];
|
|
272
|
+
const hasExpected = (value, expected) => expected.state === "cleared" ? value?.state === "cleared" && value.sessionId === expected.sessionId && value.retired === expected.retired : value?.state === "active" && value.sessionId === expected.sessionId && isDeepStrictEqual(readCodexAppServerThreadBinding(value.binding), expected.binding);
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
const current = await store.lookup(entry.key);
|
|
275
|
+
if (current !== void 0 && !hasExpected(current, entry.value)) return retain(`canonical plugin state changed at ${entry.key}`);
|
|
276
|
+
}
|
|
277
|
+
for (const entry of entries) {
|
|
278
|
+
if (await store.registerIfAbsent(entry.key, entry.value)) importedKeys++;
|
|
279
|
+
if (!hasExpected(await store.lookup(entry.key), entry.value)) return retain(`canonical plugin state changed at ${entry.key}`);
|
|
280
|
+
}
|
|
281
|
+
if (!owner) return {
|
|
282
|
+
archived: false,
|
|
283
|
+
importedKeys,
|
|
284
|
+
warning: `Left Codex binding sidecar in place after importing its conversation binding because its session owner could not be resolved: ${source.sidecarPath}`
|
|
285
|
+
};
|
|
286
|
+
const ownershipWarning = await recordSessionOwner(owner);
|
|
287
|
+
if (ownershipWarning) return retain(ownershipWarning);
|
|
288
|
+
for (const entry of entries) if (!hasExpected(await store.lookup(entry.key), entry.value)) return retain(`canonical plugin state changed at ${entry.key}`);
|
|
289
|
+
const archivePath = `${source.sidecarPath}.migrated`;
|
|
290
|
+
if (await pathExists(archivePath)) return retain(`its archive already exists at ${archivePath}`);
|
|
291
|
+
await fs.rename(source.sidecarPath, archivePath);
|
|
292
|
+
return {
|
|
293
|
+
archived: true,
|
|
294
|
+
importedKeys
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
return retain(`migration or archiving failed: ${String(error)}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async function recordSessionOwner(owner) {
|
|
302
|
+
const updated = await updateSessionStoreEntry({
|
|
303
|
+
storePath: owner.storePath,
|
|
304
|
+
sessionKey: owner.sessionKey,
|
|
305
|
+
skipMaintenance: true,
|
|
306
|
+
requireWriteSuccess: true,
|
|
307
|
+
update: (entry) => {
|
|
308
|
+
if (entry.sessionId.trim() !== owner.sessionId) return null;
|
|
309
|
+
return entry.agentHarnessId?.trim() ? null : { agentHarnessId: CODEX_AGENT_HARNESS_ID };
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
if (!updated || updated.sessionId.trim() !== owner.sessionId) return "its session owner changed before Codex ownership could be recorded";
|
|
313
|
+
const harnessId = updated.agentHarnessId?.trim();
|
|
314
|
+
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";
|
|
315
|
+
}
|
|
316
|
+
async function readDirectoryEntries(directory) {
|
|
317
|
+
try {
|
|
318
|
+
return await fs.readdir(directory, { withFileTypes: true });
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if ([
|
|
321
|
+
"EACCES",
|
|
322
|
+
"ENOENT",
|
|
323
|
+
"ENOTDIR",
|
|
324
|
+
"EPERM"
|
|
325
|
+
].includes(error.code ?? "")) return [];
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async function isRegularFile(filePath) {
|
|
330
|
+
try {
|
|
331
|
+
return (await fs.stat(filePath)).isFile();
|
|
332
|
+
} catch {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function isPathWithin(root, candidate) {
|
|
337
|
+
const relative = path.relative(root, candidate);
|
|
338
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
339
|
+
}
|
|
340
|
+
async function canonicalizePath(filePath) {
|
|
341
|
+
try {
|
|
342
|
+
return await fs.realpath(filePath);
|
|
343
|
+
} catch {
|
|
344
|
+
return path.resolve(filePath);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async function pathExists(filePath) {
|
|
348
|
+
try {
|
|
349
|
+
await fs.access(filePath);
|
|
350
|
+
return true;
|
|
351
|
+
} catch {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const stateMigrations = [{
|
|
356
|
+
id: "codex-app-server-sidecars-to-plugin-state",
|
|
357
|
+
label: "Codex app-server thread bindings",
|
|
358
|
+
async detectLegacyState(params) {
|
|
359
|
+
const { sources } = await collectLegacyBindingSources(params, { firstOnly: true });
|
|
360
|
+
return sources.length > 0 ? { preview: [`- Codex app-server bindings: legacy sidecar -> plugin state (${CODEX_APP_SERVER_BINDING_NAMESPACE})`] } : null;
|
|
361
|
+
},
|
|
362
|
+
async migrateLegacyState(params) {
|
|
363
|
+
const changes = [];
|
|
364
|
+
const warnings = [];
|
|
365
|
+
const { sources, surfaces } = await collectLegacyBindingSources(params);
|
|
366
|
+
if (sources.length === 0) return {
|
|
367
|
+
changes,
|
|
368
|
+
warnings
|
|
369
|
+
};
|
|
370
|
+
const owners = await collectBindingOwners(sources, surfaces, params);
|
|
371
|
+
const store = params.context.openPluginStateKeyedStore({
|
|
372
|
+
namespace: CODEX_APP_SERVER_BINDING_NAMESPACE,
|
|
373
|
+
maxEntries: CODEX_APP_SERVER_BINDING_MAX_ENTRIES,
|
|
374
|
+
overflowPolicy: "reject-new"
|
|
375
|
+
});
|
|
376
|
+
let migrated = 0;
|
|
377
|
+
let partialImports = 0;
|
|
378
|
+
for (const source of sources) {
|
|
379
|
+
const candidates = owners.get(await canonicalizePath(source.transcriptPath)) ?? [];
|
|
380
|
+
const result = await migrateSource(source, candidates.length === 1 ? candidates[0] : void 0, params, store);
|
|
381
|
+
if (result.warning) warnings.push(result.warning);
|
|
382
|
+
if (result.archived) migrated++;
|
|
383
|
+
else partialImports += result.importedKeys;
|
|
384
|
+
}
|
|
385
|
+
if (migrated > 0) changes.push(`Migrated ${migrated} Codex app-server binding sidecar(s) to plugin state and archived the legacy sources`);
|
|
386
|
+
if (partialImports > 0) changes.push(`Migrated ${partialImports} safe Codex app-server binding row(s) to plugin state; retained legacy sidecars needing review`);
|
|
387
|
+
return {
|
|
388
|
+
changes,
|
|
389
|
+
warnings
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
}];
|
|
393
|
+
//#endregion
|
|
1
394
|
//#region extensions/codex/doctor-contract-api.ts
|
|
2
395
|
function asRecord(value) {
|
|
3
396
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -12,36 +405,55 @@ function hasLegacyPluginDestructivePolicy(value) {
|
|
|
12
405
|
const plugins = asRecord(codexPlugins.plugins);
|
|
13
406
|
return Object.values(plugins ?? {}).some((plugin) => asRecord(plugin)?.allow_destructive_actions === "on-request");
|
|
14
407
|
}
|
|
408
|
+
function hasRetiredOnFailureApprovalPolicy(value) {
|
|
409
|
+
return asRecord(value)?.approvalPolicy === "on-failure";
|
|
410
|
+
}
|
|
15
411
|
/** Legacy Codex config keys that doctor should report or repair. */
|
|
16
|
-
const legacyConfigRules = [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
412
|
+
const legacyConfigRules = [
|
|
413
|
+
{
|
|
414
|
+
path: [
|
|
415
|
+
"plugins",
|
|
416
|
+
"entries",
|
|
417
|
+
"codex",
|
|
418
|
+
"config"
|
|
419
|
+
],
|
|
420
|
+
message: "plugins.entries.codex.config.codexDynamicToolsProfile is retired; Codex app-server always keeps Codex-native workspace tools native. Run \"openclaw doctor --fix\".",
|
|
421
|
+
match: hasRetiredDynamicToolsProfile
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
path: [
|
|
425
|
+
"plugins",
|
|
426
|
+
"entries",
|
|
427
|
+
"codex",
|
|
428
|
+
"config",
|
|
429
|
+
"codexPlugins"
|
|
430
|
+
],
|
|
431
|
+
message: "plugins.entries.codex.config.codexPlugins.allow_destructive_actions=\"on-request\" was renamed to \"auto\". Run \"openclaw doctor --fix\".",
|
|
432
|
+
match: hasLegacyPluginDestructivePolicy
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
path: [
|
|
436
|
+
"plugins",
|
|
437
|
+
"entries",
|
|
438
|
+
"codex",
|
|
439
|
+
"config",
|
|
440
|
+
"appServer"
|
|
441
|
+
],
|
|
442
|
+
message: "plugins.entries.codex.config.appServer.approvalPolicy=\"on-failure\" was retired by Codex 0.143; use \"on-request\". Run \"openclaw doctor --fix\".",
|
|
443
|
+
match: hasRetiredOnFailureApprovalPolicy
|
|
444
|
+
}
|
|
445
|
+
];
|
|
36
446
|
/**
|
|
37
447
|
* Removes retired Codex plugin config keys while preserving unrelated config.
|
|
38
448
|
*/
|
|
39
449
|
function normalizeCompatibilityConfig({ cfg }) {
|
|
40
450
|
const rawPluginConfig = asRecord(asRecord(cfg.plugins?.entries?.codex)?.config);
|
|
41
451
|
const rawCodexPlugins = asRecord(rawPluginConfig?.codexPlugins);
|
|
452
|
+
const rawAppServer = asRecord(rawPluginConfig?.appServer);
|
|
42
453
|
const shouldRemoveDynamicToolsProfile = rawPluginConfig !== null && hasRetiredDynamicToolsProfile(rawPluginConfig);
|
|
43
454
|
const shouldRewriteDestructivePolicy = hasLegacyPluginDestructivePolicy(rawCodexPlugins);
|
|
44
|
-
|
|
455
|
+
const shouldRewriteApprovalPolicy = hasRetiredOnFailureApprovalPolicy(rawAppServer);
|
|
456
|
+
if (!rawPluginConfig || !shouldRemoveDynamicToolsProfile && !shouldRewriteDestructivePolicy && !shouldRewriteApprovalPolicy) return {
|
|
45
457
|
config: cfg,
|
|
46
458
|
changes: []
|
|
47
459
|
};
|
|
@@ -66,6 +478,11 @@ function normalizeCompatibilityConfig({ cfg }) {
|
|
|
66
478
|
}
|
|
67
479
|
changes.push("Renamed plugins.entries.codex.config.codexPlugins allow_destructive_actions=\"on-request\" values to \"auto\".");
|
|
68
480
|
}
|
|
481
|
+
if (shouldRewriteApprovalPolicy) {
|
|
482
|
+
const nextAppServer = asRecord(nextPluginConfig.appServer);
|
|
483
|
+
if (nextAppServer?.approvalPolicy === "on-failure") nextAppServer.approvalPolicy = "on-request";
|
|
484
|
+
changes.push("Renamed plugins.entries.codex.config.appServer.approvalPolicy=\"on-failure\" to \"on-request\".");
|
|
485
|
+
}
|
|
69
486
|
return {
|
|
70
487
|
config: nextConfig,
|
|
71
488
|
changes
|
|
@@ -89,4 +506,4 @@ const sessionRouteStateOwners = [{
|
|
|
89
506
|
]
|
|
90
507
|
}];
|
|
91
508
|
//#endregion
|
|
92
|
-
export { legacyConfigRules, normalizeCompatibilityConfig, sessionRouteStateOwners };
|
|
509
|
+
export { legacyConfigRules, normalizeCompatibilityConfig, sessionRouteStateOwners, stateMigrations };
|
package/dist/harness.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region extensions/codex/harness.ts
|
|
2
|
-
const DEFAULT_CODEX_HARNESS_PROVIDER_IDS = new Set(["codex", "openai"]);
|
|
2
|
+
const DEFAULT_CODEX_HARNESS_PROVIDER_IDS = /* @__PURE__ */ new Set(["codex", "openai"]);
|
|
3
3
|
const CODEX_APP_SERVER_CONTEXT_ENGINE_HOST_CAPABILITIES = [
|
|
4
4
|
"bootstrap",
|
|
5
5
|
"assemble-before-prompt",
|
|
@@ -32,38 +32,57 @@ function createCodexAppServerAgentHarness(options) {
|
|
|
32
32
|
};
|
|
33
33
|
},
|
|
34
34
|
runAttempt: async (params) => {
|
|
35
|
-
const { runCodexAppServerAttempt } = await import("./run-attempt-
|
|
35
|
+
const { runCodexAppServerAttempt } = await import("./run-attempt-8SPLiDlT.js");
|
|
36
36
|
return runCodexAppServerAttempt(params, {
|
|
37
|
+
bindingStore: options.bindingStore,
|
|
37
38
|
pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
|
|
38
39
|
nativeHookRelay: { enabled: true }
|
|
39
40
|
});
|
|
40
41
|
},
|
|
41
42
|
runSideQuestion: async (params) => {
|
|
42
|
-
const { runCodexAppServerSideQuestion } = await import("./side-question-
|
|
43
|
+
const { runCodexAppServerSideQuestion } = await import("./side-question-DkjXvYv7.js");
|
|
43
44
|
return runCodexAppServerSideQuestion(params, {
|
|
45
|
+
bindingStore: options.bindingStore,
|
|
44
46
|
pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
|
|
45
47
|
nativeHookRelay: { enabled: true }
|
|
46
48
|
});
|
|
47
49
|
},
|
|
48
50
|
compact: async (params) => {
|
|
49
|
-
const { maybeCompactCodexAppServerSession } = await import("./compact-
|
|
50
|
-
return maybeCompactCodexAppServerSession(params, {
|
|
51
|
+
const { maybeCompactCodexAppServerSession } = await import("./compact-CZzbcygx.js");
|
|
52
|
+
return maybeCompactCodexAppServerSession(params, {
|
|
53
|
+
bindingStore: options.bindingStore,
|
|
54
|
+
pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig
|
|
55
|
+
});
|
|
51
56
|
},
|
|
52
57
|
compactAfterContextEngine: async (params) => {
|
|
53
|
-
const { maybeCompactCodexAppServerSession } = await import("./compact-
|
|
58
|
+
const { maybeCompactCodexAppServerSession } = await import("./compact-CZzbcygx.js");
|
|
54
59
|
return maybeCompactCodexAppServerSession(params, {
|
|
60
|
+
bindingStore: options.bindingStore,
|
|
55
61
|
pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
|
|
56
62
|
allowNonManualNativeRequest: true
|
|
57
63
|
});
|
|
58
64
|
},
|
|
59
65
|
reset: async (params) => {
|
|
60
|
-
if (params.
|
|
61
|
-
const {
|
|
62
|
-
|
|
66
|
+
if (params.sessionId) {
|
|
67
|
+
const { reclaimCurrentCodexSessionGeneration, sessionBindingIdentity } = await import("./session-binding-Dc03iwRF.js").then((n) => n.s);
|
|
68
|
+
const identity = sessionBindingIdentity({
|
|
69
|
+
agentId: params.agentId,
|
|
70
|
+
sessionId: params.sessionId,
|
|
71
|
+
sessionKey: params.sessionKey
|
|
72
|
+
});
|
|
73
|
+
let retired = await options.bindingStore.retireSessionGeneration(identity);
|
|
74
|
+
if (retired === "conflict") {
|
|
75
|
+
if (await reclaimCurrentCodexSessionGeneration({
|
|
76
|
+
bindingStore: options.bindingStore,
|
|
77
|
+
identity,
|
|
78
|
+
config: options.resolveConfig?.()
|
|
79
|
+
})) retired = await options.bindingStore.retireSessionGeneration(identity);
|
|
80
|
+
}
|
|
81
|
+
if (retired === "conflict") throw new Error(`Codex binding generation changed before session ${params.sessionId} could reset`);
|
|
63
82
|
}
|
|
64
83
|
},
|
|
65
84
|
dispose: async () => {
|
|
66
|
-
const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-
|
|
85
|
+
const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-4ICy3U6d.js").then((n) => n.c);
|
|
67
86
|
await clearSharedCodexAppServerClientAndWait();
|
|
68
87
|
}
|
|
69
88
|
};
|