@openclaw/acpx 2026.9.1-beta.1 → 2026.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-v8M2tNu6.js +185 -0
- package/dist/doctor-contract-api.js +15 -2
- package/dist/index.js +6 -3
- package/dist/{pi-session-catalog-runtime-BIu-8uRZ.js → pi-session-catalog-runtime-C0HodUWT.js} +42 -38
- package/dist/{register.runtime-Dj29TKFy.js → register.runtime-Ba7FPMGu.js} +3 -2
- package/dist/register.runtime.js +1 -1
- package/dist/rolldown-runtime-8H4AJuhK.js +14 -0
- package/dist/{runtime-BmOxa15I.js → runtime-memQfUBo.js} +137 -75
- package/dist/{service-PRlUtXMf.js → service-COMUTA5A.js} +347 -6
- package/dist/session-owner-migration-CistlQKM.js +200 -0
- package/dist/session-resource-UWe7qD3m.js +17 -0
- package/openclaw.plugin.json +12 -0
- package/package.json +5 -5
- package/skills/acp-router/SKILL.md +1 -1
- package/dist/process-reaper-DduWm_7N.js +0 -511
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
+
import { n as createLazyAcpRuntimeProxy } from "./register.runtime-Ba7FPMGu.js";
|
|
2
3
|
import "./config-schema-DN_uAi4R.js";
|
|
3
|
-
import { _ as quoteCommandPart, a as createAcpxProcessLeaseStore, f as ACPX_GATEWAY_INSTANCE_KEY, g as normalizeAcpxGatewayInstanceRecord, l as openAcpxProcessLeaseStateStore, n as OPENCLAW_ACPX_LEASE_ID_ARG, p as ACPX_GATEWAY_INSTANCE_NAMESPACE, r as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, v as splitCommandParts } from "./process-lease-Cwvj7WGe.js";
|
|
4
|
-
import {
|
|
4
|
+
import { _ as quoteCommandPart, a as createAcpxProcessLeaseStore, f as ACPX_GATEWAY_INSTANCE_KEY, g as normalizeAcpxGatewayInstanceRecord, l as openAcpxProcessLeaseStateStore, n as OPENCLAW_ACPX_LEASE_ID_ARG, p as ACPX_GATEWAY_INSTANCE_NAMESPACE, r as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, u as readAcpxProcessLeaseIdentity, v as splitCommandParts } from "./process-lease-Cwvj7WGe.js";
|
|
5
|
+
import { n as resolveAcpxPluginRoot, r as toAcpMcpServers, t as resolveAcpxPluginConfig } from "./config-v8M2tNu6.js";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
7
|
import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
7
8
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
@@ -11,10 +12,335 @@ import os from "node:os";
|
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import fs$1 from "node:fs/promises";
|
|
13
14
|
import { randomUUID } from "node:crypto";
|
|
15
|
+
import { isPidAlive, runExec } from "openclaw/plugin-sdk/process-runtime";
|
|
14
16
|
import { inspect } from "node:util";
|
|
15
17
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
16
18
|
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
|
17
19
|
import { parse, stringify } from "smol-toml";
|
|
20
|
+
//#region extensions/acpx/src/codex-adapter.ts
|
|
21
|
+
const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
|
|
22
|
+
const CODEX_ACP_BIN = "codex-acp";
|
|
23
|
+
const LEGACY_CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
|
|
24
|
+
const OPENCLAW_CODEX_CONFIG_ARG = "--openclaw-codex-config";
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region extensions/acpx/src/process-reaper.ts
|
|
27
|
+
/**
|
|
28
|
+
* ACPX process ownership checks and cleanup. The reaper only terminates
|
|
29
|
+
* OpenClaw-owned wrapper trees after validating paths, packages, and lease ids.
|
|
30
|
+
*/
|
|
31
|
+
const requireFromHere$1 = createRequire(import.meta.url);
|
|
32
|
+
const GENERATED_WRAPPER_BASENAMES = /* @__PURE__ */ new Set(["codex-acp-wrapper.mjs", "claude-agent-acp-wrapper.mjs"]);
|
|
33
|
+
const OPENCLAW_PLUGIN_DEPS_MARKER = "/plugin-runtime-deps/";
|
|
34
|
+
const ACPX_PROCESS_LIST_TIMEOUT_MS = 2e3;
|
|
35
|
+
const OWNED_ACP_PACKAGE_NAMES = [
|
|
36
|
+
CODEX_ACP_PACKAGE,
|
|
37
|
+
LEGACY_CODEX_ACP_PACKAGE,
|
|
38
|
+
"@zed-industries/codex-acp-darwin-arm64",
|
|
39
|
+
"@zed-industries/codex-acp-darwin-x64",
|
|
40
|
+
"@zed-industries/codex-acp-linux-arm64",
|
|
41
|
+
"@zed-industries/codex-acp-linux-x64",
|
|
42
|
+
"@zed-industries/codex-acp-win32-arm64",
|
|
43
|
+
"@zed-industries/codex-acp-win32-x64",
|
|
44
|
+
"@agentclientprotocol/claude-agent-acp",
|
|
45
|
+
"acpx"
|
|
46
|
+
];
|
|
47
|
+
const PLUGIN_DEPS_CODEX_PACKAGE_NAMES = [
|
|
48
|
+
"@openai/codex",
|
|
49
|
+
"@openai/codex-darwin-arm64",
|
|
50
|
+
"@openai/codex-darwin-x64",
|
|
51
|
+
"@openai/codex-linux-arm64",
|
|
52
|
+
"@openai/codex-linux-x64",
|
|
53
|
+
"@openai/codex-win32-arm64",
|
|
54
|
+
"@openai/codex-win32-x64"
|
|
55
|
+
];
|
|
56
|
+
const ACP_PACKAGE_MARKERS = [
|
|
57
|
+
...OWNED_ACP_PACKAGE_NAMES.map((packageName) => `/node_modules/${packageName}/`),
|
|
58
|
+
...PLUGIN_DEPS_CODEX_PACKAGE_NAMES.map((packageName) => `/node_modules/${packageName}/`),
|
|
59
|
+
"/acpx/dist/"
|
|
60
|
+
];
|
|
61
|
+
function normalizePathLike(value) {
|
|
62
|
+
return value.replaceAll("\\", "/");
|
|
63
|
+
}
|
|
64
|
+
function resolvePackageRoot(packageName) {
|
|
65
|
+
try {
|
|
66
|
+
return normalizePathLike(path.dirname(requireFromHere$1.resolve(`${packageName}/package.json`)));
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function resolveOpenClawInstallRoot(pluginRoot) {
|
|
72
|
+
if (path.basename(pluginRoot) === "acpx" && path.basename(path.dirname(pluginRoot)) === "extensions") {
|
|
73
|
+
const parent = path.dirname(path.dirname(pluginRoot));
|
|
74
|
+
return path.basename(parent) === "dist" ? path.dirname(parent) : parent;
|
|
75
|
+
}
|
|
76
|
+
return path.resolve(pluginRoot, "..");
|
|
77
|
+
}
|
|
78
|
+
function resolveOwnedAcpPackageRootCandidates(packageName) {
|
|
79
|
+
const pluginRoot = resolveAcpxPluginRoot(import.meta.url);
|
|
80
|
+
const openClawRoot = resolveOpenClawInstallRoot(pluginRoot);
|
|
81
|
+
return [
|
|
82
|
+
resolvePackageRoot(packageName),
|
|
83
|
+
path.join(pluginRoot, "node_modules", packageName),
|
|
84
|
+
path.join(openClawRoot, "node_modules", packageName)
|
|
85
|
+
].flatMap((root) => root ? [normalizePathLike(root)] : []);
|
|
86
|
+
}
|
|
87
|
+
const OWNED_ACP_PACKAGE_ROOTS = Array.from(new Set(OWNED_ACP_PACKAGE_NAMES.flatMap(resolveOwnedAcpPackageRootCandidates)));
|
|
88
|
+
function commandBelongsToResolvedAcpPackage(command) {
|
|
89
|
+
return OWNED_ACP_PACKAGE_ROOTS.some((root) => command.includes(`${root}/`));
|
|
90
|
+
}
|
|
91
|
+
function commandMentionsGeneratedWrapper(command) {
|
|
92
|
+
return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => command.includes(basename));
|
|
93
|
+
}
|
|
94
|
+
function commandWrapperBelongsToRoot(command, wrapperRoot) {
|
|
95
|
+
if (!wrapperRoot) return true;
|
|
96
|
+
const normalizedCommand = normalizePathLike(command);
|
|
97
|
+
const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
|
|
98
|
+
return Array.from(GENERATED_WRAPPER_BASENAMES).some((basename) => normalizedCommand.includes(`${normalizedRoot}/${basename}`));
|
|
99
|
+
}
|
|
100
|
+
function commandContainsExactWrapperPath(command, wrapperPath) {
|
|
101
|
+
const expectedPath = normalizePathLike(wrapperPath);
|
|
102
|
+
return splitCommandParts(command).some((part) => normalizePathLike(part) === expectedPath);
|
|
103
|
+
}
|
|
104
|
+
function wrapperPathBelongsToRoot(wrapperPath, wrapperRoot) {
|
|
105
|
+
const normalizedPath = normalizePathLike(wrapperPath);
|
|
106
|
+
const normalizedRoot = normalizePathLike(wrapperRoot).replace(/\/+$/, "");
|
|
107
|
+
return GENERATED_WRAPPER_BASENAMES.has(path.posix.basename(normalizedPath)) && normalizedPath.startsWith(`${normalizedRoot}/`);
|
|
108
|
+
}
|
|
109
|
+
/** Check whether a command references an OpenClaw-generated ACPX wrapper path. */
|
|
110
|
+
function isOpenClawLeaseAwareAcpxProcessCommand(params) {
|
|
111
|
+
const command = params.command?.trim();
|
|
112
|
+
if (!command) return false;
|
|
113
|
+
const normalized = normalizePathLike(command);
|
|
114
|
+
return commandMentionsGeneratedWrapper(normalized) && commandWrapperBelongsToRoot(normalized, params.wrapperRoot);
|
|
115
|
+
}
|
|
116
|
+
function commandsReferToSameRootCommand(liveCommand, storedCommand) {
|
|
117
|
+
if (!storedCommand?.trim()) return true;
|
|
118
|
+
return normalizePathLike(liveCommand).trim() === normalizePathLike(storedCommand).trim();
|
|
119
|
+
}
|
|
120
|
+
function commandOptionEquals(parts, option, expected) {
|
|
121
|
+
if (!expected) return true;
|
|
122
|
+
const index = parts.indexOf(option);
|
|
123
|
+
return index >= 0 && parts[index + 1] === expected;
|
|
124
|
+
}
|
|
125
|
+
function liveCommandMatchesLeaseIdentity(params) {
|
|
126
|
+
if (!params.expectedLeaseId && !params.expectedGatewayInstanceId) return true;
|
|
127
|
+
const parts = splitCommandParts(params.command ?? "");
|
|
128
|
+
return commandOptionEquals(parts, "--openclaw-acpx-lease-id", params.expectedLeaseId) && commandOptionEquals(parts, "--openclaw-gateway-instance-id", params.expectedGatewayInstanceId);
|
|
129
|
+
}
|
|
130
|
+
/** Check whether a command is owned by OpenClaw ACPX runtime packages or wrappers. */
|
|
131
|
+
function isOpenClawOwnedAcpxProcessCommand(params) {
|
|
132
|
+
const command = params.command?.trim();
|
|
133
|
+
if (!command) return false;
|
|
134
|
+
const normalized = normalizePathLike(command);
|
|
135
|
+
if (isOpenClawLeaseAwareAcpxProcessCommand({
|
|
136
|
+
command: normalized,
|
|
137
|
+
wrapperRoot: params.wrapperRoot
|
|
138
|
+
})) return true;
|
|
139
|
+
if (commandBelongsToResolvedAcpPackage(normalized)) return true;
|
|
140
|
+
if (!normalized.includes(OPENCLAW_PLUGIN_DEPS_MARKER)) return false;
|
|
141
|
+
return ACP_PACKAGE_MARKERS.some((marker) => normalized.includes(marker));
|
|
142
|
+
}
|
|
143
|
+
function parseProcessList(stdout) {
|
|
144
|
+
const processes = [];
|
|
145
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
146
|
+
const match = /^\s*(?<pid>\d+)\s+(?<ppid>\d+)\s+(?<command>.+?)\s*$/.exec(line);
|
|
147
|
+
const pid = match?.groups?.pid;
|
|
148
|
+
const ppid = match?.groups?.ppid;
|
|
149
|
+
const command = match?.groups?.command;
|
|
150
|
+
if (!pid || !ppid || !command) continue;
|
|
151
|
+
processes.push({
|
|
152
|
+
pid: Number.parseInt(pid, 10),
|
|
153
|
+
ppid: Number.parseInt(ppid, 10),
|
|
154
|
+
command
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return processes;
|
|
158
|
+
}
|
|
159
|
+
/** List host processes in the compact shape needed by ACPX cleanup. */
|
|
160
|
+
async function listPlatformProcesses() {
|
|
161
|
+
if (process.platform === "win32") return [];
|
|
162
|
+
const { stdout } = await runExec("ps", ["-axo", "pid=,ppid=,command="], {
|
|
163
|
+
logOutput: false,
|
|
164
|
+
maxBuffer: 8388608,
|
|
165
|
+
timeoutMs: ACPX_PROCESS_LIST_TIMEOUT_MS
|
|
166
|
+
});
|
|
167
|
+
return parseProcessList(stdout);
|
|
168
|
+
}
|
|
169
|
+
function collectProcessTree(processes, rootPid) {
|
|
170
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
171
|
+
for (const processInfo of processes) {
|
|
172
|
+
const children = childrenByParent.get(processInfo.ppid) ?? [];
|
|
173
|
+
children.push(processInfo);
|
|
174
|
+
childrenByParent.set(processInfo.ppid, children);
|
|
175
|
+
}
|
|
176
|
+
const root = new Map(processes.map((processInfo) => [processInfo.pid, processInfo])).get(rootPid);
|
|
177
|
+
const collected = [];
|
|
178
|
+
if (root) collected.push(root);
|
|
179
|
+
const queue = [...childrenByParent.get(rootPid) ?? []];
|
|
180
|
+
while (queue.length > 0) {
|
|
181
|
+
const next = queue.shift();
|
|
182
|
+
if (!next || collected.some((processInfo) => processInfo.pid === next.pid)) continue;
|
|
183
|
+
collected.push(next);
|
|
184
|
+
queue.push(...childrenByParent.get(next.pid) ?? []);
|
|
185
|
+
}
|
|
186
|
+
return collected;
|
|
187
|
+
}
|
|
188
|
+
function uniquePids(processes) {
|
|
189
|
+
return Array.from(new Set(processes.map((processInfo) => processInfo.pid).filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid)));
|
|
190
|
+
}
|
|
191
|
+
async function terminatePids(pids, deps) {
|
|
192
|
+
const killProcess = deps?.killProcess ?? ((pid, signal) => process.kill(pid, signal));
|
|
193
|
+
const sleep = deps?.sleep ?? ((ms) => new Promise((resolve) => {
|
|
194
|
+
setTimeout(resolve, ms);
|
|
195
|
+
}));
|
|
196
|
+
const terminated = [];
|
|
197
|
+
for (const pid of pids) try {
|
|
198
|
+
killProcess(pid, "SIGTERM");
|
|
199
|
+
terminated.push(pid);
|
|
200
|
+
} catch {}
|
|
201
|
+
if (terminated.length === 0) return terminated;
|
|
202
|
+
await sleep(750);
|
|
203
|
+
for (const pid of terminated) if (deps?.killProcess || isPidAlive(pid)) try {
|
|
204
|
+
killProcess(pid, "SIGKILL");
|
|
205
|
+
} catch {}
|
|
206
|
+
return terminated;
|
|
207
|
+
}
|
|
208
|
+
/** Terminate one validated OpenClaw-owned ACPX wrapper process tree. */
|
|
209
|
+
async function cleanupOpenClawOwnedAcpxProcessTree(params) {
|
|
210
|
+
const rootPid = params.rootPid;
|
|
211
|
+
if (!rootPid || rootPid <= 0 || rootPid === process.pid) return {
|
|
212
|
+
inspectedPids: [],
|
|
213
|
+
terminatedPids: [],
|
|
214
|
+
skippedReason: "missing-root"
|
|
215
|
+
};
|
|
216
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
217
|
+
inspectedPids: [],
|
|
218
|
+
terminatedPids: [],
|
|
219
|
+
skippedReason: "unsupported-platform"
|
|
220
|
+
};
|
|
221
|
+
let processes;
|
|
222
|
+
try {
|
|
223
|
+
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
224
|
+
} catch {
|
|
225
|
+
return {
|
|
226
|
+
inspectedPids: [],
|
|
227
|
+
terminatedPids: [],
|
|
228
|
+
skippedReason: "process-list-unavailable"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const listedTree = collectProcessTree(processes, rootPid);
|
|
232
|
+
if (listedTree.length === 0) return {
|
|
233
|
+
inspectedPids: [],
|
|
234
|
+
terminatedPids: [],
|
|
235
|
+
skippedReason: "unverified-root"
|
|
236
|
+
};
|
|
237
|
+
const rootCommand = listedTree[0]?.command ?? params.rootCommand;
|
|
238
|
+
const liveCommandWasGeneratedWrapper = commandMentionsGeneratedWrapper(normalizePathLike(rootCommand ?? ""));
|
|
239
|
+
const storedCommandWasGeneratedWrapper = commandMentionsGeneratedWrapper(normalizePathLike(params.rootCommand ?? ""));
|
|
240
|
+
if (!liveCommandWasGeneratedWrapper && storedCommandWasGeneratedWrapper) return {
|
|
241
|
+
inspectedPids: listedTree.map((processInfo) => processInfo.pid),
|
|
242
|
+
terminatedPids: [],
|
|
243
|
+
skippedReason: "not-openclaw-owned"
|
|
244
|
+
};
|
|
245
|
+
if (!liveCommandWasGeneratedWrapper && !commandsReferToSameRootCommand(rootCommand ?? "", params.rootCommand)) return {
|
|
246
|
+
inspectedPids: listedTree.map((processInfo) => processInfo.pid),
|
|
247
|
+
terminatedPids: [],
|
|
248
|
+
skippedReason: "not-openclaw-owned"
|
|
249
|
+
};
|
|
250
|
+
if (!isOpenClawOwnedAcpxProcessCommand({
|
|
251
|
+
command: rootCommand,
|
|
252
|
+
wrapperRoot: params.wrapperRoot
|
|
253
|
+
})) return {
|
|
254
|
+
inspectedPids: listedTree.map((processInfo) => processInfo.pid),
|
|
255
|
+
terminatedPids: [],
|
|
256
|
+
skippedReason: "not-openclaw-owned"
|
|
257
|
+
};
|
|
258
|
+
if (!liveCommandMatchesLeaseIdentity({
|
|
259
|
+
command: rootCommand,
|
|
260
|
+
expectedLeaseId: params.expectedLeaseId,
|
|
261
|
+
expectedGatewayInstanceId: params.expectedGatewayInstanceId
|
|
262
|
+
})) return {
|
|
263
|
+
inspectedPids: listedTree.map((processInfo) => processInfo.pid),
|
|
264
|
+
terminatedPids: [],
|
|
265
|
+
skippedReason: "not-openclaw-owned"
|
|
266
|
+
};
|
|
267
|
+
const pids = uniquePids(listedTree.toReversed());
|
|
268
|
+
return {
|
|
269
|
+
inspectedPids: uniquePids(listedTree),
|
|
270
|
+
terminatedPids: await terminatePids(pids, params.deps)
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/** Recover a pending lease by matching its exact live wrapper identity. */
|
|
274
|
+
async function cleanupOpenClawOwnedAcpxPendingLease(params) {
|
|
275
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
276
|
+
inspectedPids: [],
|
|
277
|
+
terminatedPids: [],
|
|
278
|
+
skippedReason: "unsupported-platform"
|
|
279
|
+
};
|
|
280
|
+
if (!params.wrapperPath || !wrapperPathBelongsToRoot(params.wrapperPath, params.wrapperRoot)) return {
|
|
281
|
+
inspectedPids: [],
|
|
282
|
+
terminatedPids: [],
|
|
283
|
+
skippedReason: "unverified-root"
|
|
284
|
+
};
|
|
285
|
+
let processes;
|
|
286
|
+
try {
|
|
287
|
+
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
288
|
+
} catch {
|
|
289
|
+
return {
|
|
290
|
+
inspectedPids: [],
|
|
291
|
+
terminatedPids: [],
|
|
292
|
+
skippedReason: "process-list-unavailable"
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const matchingRoots = processes.filter((processInfo) => commandContainsExactWrapperPath(processInfo.command, params.wrapperPath) && liveCommandMatchesLeaseIdentity({
|
|
296
|
+
command: processInfo.command,
|
|
297
|
+
expectedLeaseId: params.leaseId,
|
|
298
|
+
expectedGatewayInstanceId: params.gatewayInstanceId
|
|
299
|
+
}));
|
|
300
|
+
if (matchingRoots.length === 0) return {
|
|
301
|
+
inspectedPids: [],
|
|
302
|
+
terminatedPids: [],
|
|
303
|
+
skippedReason: "missing-root"
|
|
304
|
+
};
|
|
305
|
+
if (matchingRoots.length > 1) return {
|
|
306
|
+
inspectedPids: uniquePids(matchingRoots),
|
|
307
|
+
terminatedPids: [],
|
|
308
|
+
skippedReason: "ambiguous-root"
|
|
309
|
+
};
|
|
310
|
+
const listedTree = collectProcessTree(processes, matchingRoots[0].pid);
|
|
311
|
+
const pids = uniquePids(listedTree.toReversed());
|
|
312
|
+
return {
|
|
313
|
+
inspectedPids: uniquePids(listedTree),
|
|
314
|
+
terminatedPids: await terminatePids(pids, params.deps)
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/** Reap orphaned OpenClaw-owned ACPX wrapper trees during runtime startup. */
|
|
318
|
+
async function reapStaleOpenClawOwnedAcpxOrphans(params) {
|
|
319
|
+
if ((params.deps?.platform ?? process.platform) === "win32") return {
|
|
320
|
+
inspectedPids: [],
|
|
321
|
+
terminatedPids: [],
|
|
322
|
+
skippedReason: "unsupported-platform"
|
|
323
|
+
};
|
|
324
|
+
let processes;
|
|
325
|
+
try {
|
|
326
|
+
processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
|
|
327
|
+
} catch {
|
|
328
|
+
return {
|
|
329
|
+
inspectedPids: [],
|
|
330
|
+
terminatedPids: [],
|
|
331
|
+
skippedReason: "process-list-unavailable"
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const orphanTrees = processes.filter((processInfo) => processInfo.ppid === 1 && !readAcpxProcessLeaseIdentity(processInfo.command) && isOpenClawOwnedAcpxProcessCommand({
|
|
335
|
+
command: processInfo.command,
|
|
336
|
+
wrapperRoot: params.wrapperRoot
|
|
337
|
+
})).map((orphan) => collectProcessTree(processes, orphan.pid));
|
|
338
|
+
return {
|
|
339
|
+
inspectedPids: uniquePids(orphanTrees.flat()),
|
|
340
|
+
terminatedPids: await terminatePids(uniquePids(orphanTrees.flatMap((tree) => tree.toReversed())), params.deps)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
//#endregion
|
|
18
344
|
//#region extensions/acpx/src/codex-trust-config.ts
|
|
19
345
|
/**
|
|
20
346
|
* Builds isolated Codex config for ACPX sessions. It preserves safe inherited
|
|
@@ -1019,9 +1345,13 @@ async function prepareAcpxCodexAuthConfig(params) {
|
|
|
1019
1345
|
* ACPX plugin service lifecycle. It resolves config, prepares isolated adapter
|
|
1020
1346
|
* wrappers, registers the ACP backend, and manages startup/cleanup probes.
|
|
1021
1347
|
*/
|
|
1348
|
+
var service_exports = /* @__PURE__ */ __exportAll({
|
|
1349
|
+
createAcpxRuntimeService: () => createAcpxRuntimeService,
|
|
1350
|
+
resolveAcpxTimerTimeoutMs: () => resolveAcpxTimerTimeoutMs
|
|
1351
|
+
});
|
|
1022
1352
|
const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
|
|
1023
1353
|
const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
|
|
1024
|
-
const loadRuntimeModule = createLazyRuntimeModule(() => import("./runtime-
|
|
1354
|
+
const loadRuntimeModule = createLazyRuntimeModule(() => import("./runtime-memQfUBo.js"));
|
|
1025
1355
|
/** Convert ACPX timeout seconds into timer-safe milliseconds. */
|
|
1026
1356
|
function resolveAcpxTimerTimeoutMs(timeoutSeconds) {
|
|
1027
1357
|
if (timeoutSeconds === void 0) return;
|
|
@@ -1032,9 +1362,20 @@ function createLazyDefaultRuntime(params) {
|
|
|
1032
1362
|
let runtimePromise = null;
|
|
1033
1363
|
async function resolveRuntime() {
|
|
1034
1364
|
if (runtime) return runtime;
|
|
1035
|
-
runtimePromise ??= loadRuntimeModule().then((module) => {
|
|
1365
|
+
runtimePromise ??= loadRuntimeModule().then(async (module) => {
|
|
1366
|
+
const names = await fs$1.readdir(path.join(params.pluginConfig.stateDir, "sessions")).catch((error) => {
|
|
1367
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return [];
|
|
1368
|
+
throw error;
|
|
1369
|
+
});
|
|
1370
|
+
const legacyBareSessionKeys = /* @__PURE__ */ new Set();
|
|
1371
|
+
for (const name of names) {
|
|
1372
|
+
if (!name.endsWith(".json")) continue;
|
|
1373
|
+
const recordId = decodeURIComponent(name.slice(0, -5));
|
|
1374
|
+
if (!recordId.startsWith("agent:") && !recordId.startsWith(".openclaw-owner-") && !recordId.includes(":oneshot:")) legacyBareSessionKeys.add(recordId.toLowerCase());
|
|
1375
|
+
}
|
|
1036
1376
|
runtime = new module.AcpxRuntime({
|
|
1037
1377
|
cwd: params.pluginConfig.cwd,
|
|
1378
|
+
openclawLegacyBareSessionKeys: legacyBareSessionKeys,
|
|
1038
1379
|
openclawGatewayInstanceId: params.gatewayInstanceId,
|
|
1039
1380
|
openclawProcessLeaseStore: params.processLeaseStore,
|
|
1040
1381
|
openclawWrapperRoot: params.wrapperRoot,
|
|
@@ -1279,4 +1620,4 @@ function createAcpxRuntimeService(params) {
|
|
|
1279
1620
|
};
|
|
1280
1621
|
}
|
|
1281
1622
|
//#endregion
|
|
1282
|
-
export { createAcpxRuntimeService, resolveAcpxTimerTimeoutMs };
|
|
1623
|
+
export { CODEX_ACP_PACKAGE as a, createAcpxRuntimeService, isOpenClawLeaseAwareAcpxProcessCommand as i, cleanupOpenClawOwnedAcpxPendingLease as n, OPENCLAW_CODEX_CONFIG_ARG as o, cleanupOpenClawOwnedAcpxProcessTree as r, resolveAcpxTimerTimeoutMs, service_exports as t };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { l as openAcpxProcessLeaseStateStore, o as hashAcpxProcessCommand, s as normalizeAcpxProcessLease, u as readAcpxProcessLeaseIdentity } from "./process-lease-Cwvj7WGe.js";
|
|
2
|
+
import { t as resolveAcpxPluginConfig } from "./config-v8M2tNu6.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import { archiveLegacyStateSource, asObjectRecord } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
import { isDeepStrictEqual } from "node:util";
|
|
8
|
+
//#region extensions/acpx/src/session-owner-migration.ts
|
|
9
|
+
function sessionDirectory(input) {
|
|
10
|
+
if (!input.serviceWorkspaceDir) throw new Error("ACP ownership repair requires the Gateway service workspace; upgrade OpenClaw Doctor.");
|
|
11
|
+
return path.join(resolveAcpxPluginConfig({
|
|
12
|
+
rawConfig: input.config.plugins?.entries?.acpx?.config,
|
|
13
|
+
workspaceDir: input.serviceWorkspaceDir
|
|
14
|
+
}).stateDir, "sessions");
|
|
15
|
+
}
|
|
16
|
+
async function legacyRecords(input) {
|
|
17
|
+
const directory = sessionDirectory(input);
|
|
18
|
+
const ids = (await fs.readdir(directory).catch((error) => {
|
|
19
|
+
if (asObjectRecord(error)?.code === "ENOENT") return [];
|
|
20
|
+
throw error;
|
|
21
|
+
})).filter((name) => name.endsWith(".json")).map((name) => decodeURIComponent(name.slice(0, -5))).filter((id) => !id.startsWith("agent:") && !id.startsWith(".openclaw-owner-"));
|
|
22
|
+
if (ids.length === 0) return {
|
|
23
|
+
directory,
|
|
24
|
+
ids
|
|
25
|
+
};
|
|
26
|
+
const { resolveAcpxSessionResource } = await import("./session-resource-UWe7qD3m.js").then((n) => n.n);
|
|
27
|
+
const evidence = await input.context.inspectAcpSessionClaims?.();
|
|
28
|
+
const { decodeAcpxRuntimeHandleState } = await import("acpx/runtime");
|
|
29
|
+
return {
|
|
30
|
+
directory,
|
|
31
|
+
ids: ids.filter((id) => !evidence?.claims.some((claim) => {
|
|
32
|
+
const locator = decodeAcpxRuntimeHandleState(claim.meta.runtimeSessionName);
|
|
33
|
+
return evidence.incomplete.length === 0 && claim.meta.identity?.state === "resolved" && claim.meta.identity.acpxRecordId === id && locator?.acpxRecordId === id && locator.name === resolveAcpxSessionResource(claim);
|
|
34
|
+
})).filter((id) => !id.includes(":oneshot:") || evidence?.claims.some((claim) => {
|
|
35
|
+
const locator = decodeAcpxRuntimeHandleState(claim.meta.runtimeSessionName);
|
|
36
|
+
return claim.meta.identity?.acpxRecordId === id && locator?.name !== resolveAcpxSessionResource(claim);
|
|
37
|
+
})).toSorted()
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function requireStoppedPid(pid) {
|
|
41
|
+
if (pid === void 0 || pid === null) return;
|
|
42
|
+
if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid <= 0) throw new Error("record process identity is uncertain");
|
|
43
|
+
try {
|
|
44
|
+
process.kill(pid, 0);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (asObjectRecord(error)?.code === "ESRCH") return;
|
|
47
|
+
throw new Error("record process liveness cannot be verified", { cause: error });
|
|
48
|
+
}
|
|
49
|
+
throw new Error("record still has a live process; stop the harness before Doctor repair");
|
|
50
|
+
}
|
|
51
|
+
function recordPath(directory, recordId) {
|
|
52
|
+
return path.join(directory, `${encodeURIComponent(recordId)}.json`);
|
|
53
|
+
}
|
|
54
|
+
function matchesClaimRecord(claim, raw, oldId, resource, decode) {
|
|
55
|
+
const state = decode(claim.meta.runtimeSessionName);
|
|
56
|
+
const identity = claim.meta.identity;
|
|
57
|
+
if (!state || !identity || identity.state !== "resolved" || !identity.acpxSessionId && !identity.agentSessionId || state.mode !== claim.meta.mode) return false;
|
|
58
|
+
const recordId = claim.meta.mode === "oneshot" ? oldId : resource;
|
|
59
|
+
const oldLocator = state.name === raw.name && identity.acpxRecordId === oldId && state.acpxRecordId === oldId;
|
|
60
|
+
const newLocator = state.name === resource && identity.acpxRecordId === recordId && state.acpxRecordId === recordId;
|
|
61
|
+
return (oldLocator || newLocator) && state.agent === claim.meta.agent && (!identity.acpxSessionId || identity.acpxSessionId === raw.acp_session_id) && (!identity.agentSessionId || identity.agentSessionId === raw.agent_session_id) && (!state.backendSessionId || state.backendSessionId === raw.acp_session_id) && (!state.agentSessionId || state.agentSessionId === raw.agent_session_id);
|
|
62
|
+
}
|
|
63
|
+
async function migrateRecord(input, directory, oldId, claims, changes, warnings) {
|
|
64
|
+
const sourcePath = recordPath(directory, oldId);
|
|
65
|
+
const sourceBytes = await fs.readFile(sourcePath, "utf8");
|
|
66
|
+
const raw = asObjectRecord(JSON.parse(sourceBytes));
|
|
67
|
+
if (!raw || raw.acpx_record_id !== oldId || typeof raw.name !== "string" || !raw.name.trim() || raw.name !== oldId && !oldId.startsWith(`${raw.name}:oneshot:`)) throw new Error("record ID/name is not a recognized ACPX locator");
|
|
68
|
+
requireStoppedPid(raw.pid);
|
|
69
|
+
const { resolveAcpxSessionResource } = await import("./session-resource-UWe7qD3m.js").then((n) => n.n);
|
|
70
|
+
const { createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState } = await import("acpx/runtime");
|
|
71
|
+
const candidates = claims.filter((claim) => matchesClaimRecord(claim, raw, oldId, resolveAcpxSessionResource(claim), decodeAcpxRuntimeHandleState));
|
|
72
|
+
if (candidates.length !== 1) throw new Error("exactly one current canonical owner claim is required");
|
|
73
|
+
const claim = candidates[0];
|
|
74
|
+
const resource = resolveAcpxSessionResource(claim);
|
|
75
|
+
const oneshot = claim.meta.mode === "oneshot";
|
|
76
|
+
if (oneshot !== (raw.name !== oldId)) throw new Error("canonical claim and backend record mode disagree");
|
|
77
|
+
if (!oneshot && oldId === resource) return;
|
|
78
|
+
const recordId = oneshot ? oldId : resource;
|
|
79
|
+
const leaseStore = openAcpxProcessLeaseStateStore(input.context.openPluginStateKeyedStore);
|
|
80
|
+
const leases = (await leaseStore.entries()).map((row) => ({
|
|
81
|
+
row,
|
|
82
|
+
lease: normalizeAcpxProcessLease(row.value)
|
|
83
|
+
}));
|
|
84
|
+
if (leases.some(({ lease }) => !lease)) throw new Error("process lease evidence is incomplete");
|
|
85
|
+
const commandIdentity = typeof raw.agent_command === "string" ? readAcpxProcessLeaseIdentity(raw.agent_command) : void 0;
|
|
86
|
+
const matchingLeases = leases.filter(({ lease }) => lease.sessionKey === raw.name || lease.sessionKey === resource);
|
|
87
|
+
for (const { lease } of matchingLeases) {
|
|
88
|
+
if (lease.state === "open" || lease.state === "closing") throw new Error("record has a live or uncertain process lease");
|
|
89
|
+
requireStoppedPid(lease.rootPid || void 0);
|
|
90
|
+
if (!commandIdentity || lease.leaseId !== commandIdentity.leaseId || lease.gatewayInstanceId !== commandIdentity.gatewayInstanceId || lease.commandHash !== hashAcpxProcessCommand(String(raw.agent_command))) throw new Error("record lease association does not match its persisted command");
|
|
91
|
+
}
|
|
92
|
+
const destinationPath = recordPath(directory, recordId);
|
|
93
|
+
const candidate = oneshot ? raw : {
|
|
94
|
+
...raw,
|
|
95
|
+
acpx_record_id: resource,
|
|
96
|
+
name: resource
|
|
97
|
+
};
|
|
98
|
+
const candidateBytes = `${JSON.stringify(candidate, null, 2)}\n`;
|
|
99
|
+
const store = createFileSessionStore({ stateDir: path.dirname(directory) });
|
|
100
|
+
const source = await store.load(oldId);
|
|
101
|
+
if (!source) throw new Error("pinned ACPX reader rejected the source record");
|
|
102
|
+
const temporaryId = `.openclaw-owner-${randomUUID()}`;
|
|
103
|
+
const temporaryPath = recordPath(directory, temporaryId);
|
|
104
|
+
const file = await fs.open(temporaryPath, "wx", 384);
|
|
105
|
+
try {
|
|
106
|
+
try {
|
|
107
|
+
await file.writeFile(candidateBytes);
|
|
108
|
+
await file.sync();
|
|
109
|
+
} finally {
|
|
110
|
+
await file.close();
|
|
111
|
+
}
|
|
112
|
+
const interpreted = await store.load(temporaryId);
|
|
113
|
+
if (!interpreted || !isDeepStrictEqual(interpreted, oneshot ? source : {
|
|
114
|
+
...source,
|
|
115
|
+
acpxRecordId: resource,
|
|
116
|
+
name: resource
|
|
117
|
+
})) throw new Error("rekey would alter interpreted history/event references; source retained");
|
|
118
|
+
const existing = await fs.readFile(destinationPath, "utf8").catch((error) => {
|
|
119
|
+
if (asObjectRecord(error)?.code === "ENOENT") return;
|
|
120
|
+
throw error;
|
|
121
|
+
});
|
|
122
|
+
const originalLocator = decodeAcpxRuntimeHandleState(claim.meta.runtimeSessionName);
|
|
123
|
+
if (!oneshot && originalLocator.name === resource && existing === void 0) throw new Error("migrated metadata has no published destination; source retained");
|
|
124
|
+
if (existing !== void 0 && !isDeepStrictEqual(JSON.parse(existing), candidate)) throw new Error("destination conflicts; no files were overwritten");
|
|
125
|
+
if (existing === void 0) await fs.link(temporaryPath, destinationPath);
|
|
126
|
+
const publicationDirectory = await fs.open(directory, "r");
|
|
127
|
+
try {
|
|
128
|
+
await publicationDirectory.sync();
|
|
129
|
+
} finally {
|
|
130
|
+
await publicationDirectory.close();
|
|
131
|
+
}
|
|
132
|
+
if (await fs.readFile(sourcePath, "utf8") !== sourceBytes) throw new Error("source changed during repair");
|
|
133
|
+
if (!isDeepStrictEqual(JSON.parse(await fs.readFile(destinationPath, "utf8")), candidate)) throw new Error("destination changed during repair");
|
|
134
|
+
for (const { row } of matchingLeases) {
|
|
135
|
+
if (!isDeepStrictEqual(await leaseStore.lookup(row.key), row.value)) throw new Error("lease changed during repair");
|
|
136
|
+
await leaseStore.register(row.key, {
|
|
137
|
+
...row.value,
|
|
138
|
+
sessionKey: resource
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const state = decodeAcpxRuntimeHandleState(claim.meta.runtimeSessionName);
|
|
142
|
+
input.context.updateAcpSessionIdentity({
|
|
143
|
+
claim,
|
|
144
|
+
runtimeSessionName: encodeAcpxRuntimeHandleState({
|
|
145
|
+
...state,
|
|
146
|
+
name: resource,
|
|
147
|
+
acpxRecordId: recordId
|
|
148
|
+
}),
|
|
149
|
+
acpxRecordId: recordId
|
|
150
|
+
});
|
|
151
|
+
const verified = await input.context.inspectAcpSessionClaims();
|
|
152
|
+
if (verified.incomplete.length || !verified.claims.some((item) => item.agentId === claim.agentId && item.sessionKey === claim.sessionKey && item.meta.identity?.acpxRecordId === recordId && isDeepStrictEqual(item.binding, claim.binding))) throw new Error("canonical metadata verification failed; source retained for rerun");
|
|
153
|
+
changes.push(`Migrated ACP backend history for ${claim.agentId}/${claim.sessionKey} to its owner-qualified resource.`);
|
|
154
|
+
if (!oneshot) await archiveLegacyStateSource({
|
|
155
|
+
filePath: sourcePath,
|
|
156
|
+
label: "ACP owner record",
|
|
157
|
+
changes,
|
|
158
|
+
warnings
|
|
159
|
+
});
|
|
160
|
+
} finally {
|
|
161
|
+
await fs.rm(temporaryPath, { force: true });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const acpxSessionOwnerMigration = {
|
|
165
|
+
id: "acpx-session-owner-resources",
|
|
166
|
+
label: "ACP session owners",
|
|
167
|
+
doctorOnly: true,
|
|
168
|
+
phase: "after-session-repair",
|
|
169
|
+
async detectLegacyState(input) {
|
|
170
|
+
const { ids } = await legacyRecords(input);
|
|
171
|
+
return ids.length ? { preview: [`ACP backend has ${ids.length} unqualified record(s). Stop the Gateway and run openclaw doctor --fix; ambiguous histories remain intact.`] } : null;
|
|
172
|
+
},
|
|
173
|
+
async migrateLegacyState(input) {
|
|
174
|
+
const changes = [];
|
|
175
|
+
const warnings = [];
|
|
176
|
+
if (!input.context.inspectAcpSessionClaims || !input.context.updateAcpSessionIdentity) return {
|
|
177
|
+
changes,
|
|
178
|
+
warnings: ["ACP owner repair requires current offline Doctor maintenance authority."]
|
|
179
|
+
};
|
|
180
|
+
const { directory, ids } = await legacyRecords(input);
|
|
181
|
+
const evidence = await input.context.inspectAcpSessionClaims();
|
|
182
|
+
if (evidence.incomplete.length) return {
|
|
183
|
+
changes,
|
|
184
|
+
warnings: [`ACP owner evidence is incomplete; all records retained: ${evidence.incomplete.join("; ")}`]
|
|
185
|
+
};
|
|
186
|
+
for (const oldId of ids) try {
|
|
187
|
+
const current = await input.context.inspectAcpSessionClaims();
|
|
188
|
+
if (current.incomplete.length) throw new Error("canonical ownership evidence became incomplete");
|
|
189
|
+
await migrateRecord(input, directory, oldId, current.claims, changes, warnings);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
warnings.push(`ACP record ${oldId} retained: ${String(error)}`);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
changes,
|
|
195
|
+
warnings
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
//#endregion
|
|
200
|
+
export { acpxSessionOwnerMigration };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
+
import { AcpRuntimeError } from "./runtime-api.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
5
|
+
//#region extensions/acpx/src/session-resource.ts
|
|
6
|
+
var session_resource_exports = /* @__PURE__ */ __exportAll({ resolveAcpxSessionResource: () => resolveAcpxSessionResource });
|
|
7
|
+
/** Logical OpenClaw keys stay intact; only bare backend resource names need a namespace. */
|
|
8
|
+
function resolveAcpxSessionResource(target) {
|
|
9
|
+
const sessionKey = target.sessionKey.trim().toLowerCase();
|
|
10
|
+
const encodedOwner = parseAgentSessionKey(sessionKey)?.agentId;
|
|
11
|
+
const agentId = target.agentId?.trim() ? normalizeAgentId(target.agentId) : encodedOwner;
|
|
12
|
+
if (!sessionKey || encodedOwner && agentId !== encodedOwner || !encodedOwner && !agentId) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP session owner is missing or disagrees with its logical key. Pass the OpenClaw agentId that owns this session.", { detailCode: "SESSION_OWNER_UNSUPPORTED" });
|
|
13
|
+
if (encodedOwner) return sessionKey;
|
|
14
|
+
return `openclaw-owner-v1-${createHash("sha256").update(JSON.stringify([agentId, sessionKey])).digest("hex")}`;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { session_resource_exports as n, resolveAcpxSessionResource as t };
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "acpx",
|
|
3
|
+
"backupResources": [
|
|
4
|
+
{
|
|
5
|
+
"disposition": "regenerable",
|
|
6
|
+
"scope": "state",
|
|
7
|
+
"relativePath": "acpx/codex-home/tmp/arg0"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"disposition": "regenerable",
|
|
11
|
+
"scope": "state",
|
|
12
|
+
"relativePath": "acpx/codex-home/.tmp/plugins"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
3
15
|
"doctorContract": {
|
|
4
16
|
"configRepair": true,
|
|
5
17
|
"stateMigrations": true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/acpx",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.2",
|
|
4
4
|
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@agentclientprotocol/claude-agent-acp": "0.70.0",
|
|
12
|
-
"@agentclientprotocol/codex-acp": "1.6.
|
|
12
|
+
"@agentclientprotocol/codex-acp": "1.6.2",
|
|
13
13
|
"acpx": "0.13.1",
|
|
14
14
|
"smol-toml": "1.8.0",
|
|
15
15
|
"zod": "4.4.3"
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"compat": {
|
|
46
|
-
"pluginApi": ">=2026.9.
|
|
46
|
+
"pluginApi": ">=2026.9.2"
|
|
47
47
|
},
|
|
48
48
|
"build": {
|
|
49
|
-
"openclawVersion": "2026.9.
|
|
49
|
+
"openclawVersion": "2026.9.2",
|
|
50
50
|
"staticAssets": [
|
|
51
51
|
{
|
|
52
52
|
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"skills/**"
|
|
75
75
|
],
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"openclaw": ">=2026.9.
|
|
77
|
+
"openclaw": ">=2026.9.2"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"openclaw": {
|
|
@@ -210,7 +210,7 @@ Defaults are:
|
|
|
210
210
|
|
|
211
211
|
- `openclaw -> openclaw acp`
|
|
212
212
|
- `claude -> bundled @agentclientprotocol/claude-agent-acp@0.70.0`
|
|
213
|
-
- `codex -> bundled @agentclientprotocol/codex-acp@1.6.
|
|
213
|
+
- `codex -> bundled @agentclientprotocol/codex-acp@1.6.2 through OpenClaw's isolated CODEX_HOME wrapper`
|
|
214
214
|
- `copilot -> copilot --acp --stdio`
|
|
215
215
|
- `cursor -> cursor-agent acp`
|
|
216
216
|
- `droid -> droid exec --output-format acp`
|