@openclaw/acpx 2026.6.5-beta.2 → 2026.6.5-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.
@@ -0,0 +1,121 @@
1
+ import { c as normalizeAcpxProcessLeaseFile, d as ACPX_GATEWAY_INSTANCE_KEY, f as ACPX_GATEWAY_INSTANCE_NAMESPACE, h as normalizeAcpxGatewayInstanceRecord, l as openAcpxProcessLeaseStateStore, m as ACPX_LEGACY_PROCESS_LEASE_FILE, p as ACPX_LEGACY_GATEWAY_INSTANCE_FILE, s as normalizeAcpxProcessLease } from "./process-lease-DiKkFj6F.js";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ //#region extensions/acpx/doctor-contract-api.ts
5
+ function resolveLegacyGatewayInstancePath(stateDir) {
6
+ return path.join(stateDir, ACPX_LEGACY_GATEWAY_INSTANCE_FILE);
7
+ }
8
+ function resolveLegacyProcessLeasePath(stateDir) {
9
+ return path.join(stateDir, "acpx", ACPX_LEGACY_PROCESS_LEASE_FILE);
10
+ }
11
+ async function fileExists(filePath) {
12
+ try {
13
+ return (await fs.stat(filePath)).isFile();
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ async function readLegacyGatewayInstanceId(filePath) {
19
+ try {
20
+ return (await fs.readFile(filePath, "utf8")).trim() || null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ async function readLegacyOpenProcessLeases(filePath) {
26
+ try {
27
+ return normalizeAcpxProcessLeaseFile(JSON.parse(await fs.readFile(filePath, "utf8"))).leases.filter((lease) => lease.state === "open" || lease.state === "closing");
28
+ } catch {
29
+ return [];
30
+ }
31
+ }
32
+ async function archiveLegacySource(params) {
33
+ const archivedPath = `${params.filePath}.migrated`;
34
+ if (await fileExists(archivedPath)) {
35
+ params.warnings.push(`Left migrated ACPX ${params.label} source in place because ${archivedPath} already exists`);
36
+ return;
37
+ }
38
+ try {
39
+ await fs.rename(params.filePath, archivedPath);
40
+ params.changes.push(`Archived ACPX ${params.label} legacy source -> ${archivedPath}`);
41
+ } catch (err) {
42
+ params.warnings.push(`Failed archiving ACPX ${params.label} legacy source: ${String(err)}`);
43
+ }
44
+ }
45
+ const stateMigrations = [{
46
+ id: "acpx-runtime-state-to-plugin-state",
47
+ label: "ACPX runtime state",
48
+ async detectLegacyState(params) {
49
+ const gatewayInstanceId = await readLegacyGatewayInstanceId(resolveLegacyGatewayInstancePath(params.stateDir));
50
+ const openLeases = await readLegacyOpenProcessLeases(resolveLegacyProcessLeasePath(params.stateDir));
51
+ if (!gatewayInstanceId && openLeases.length === 0) return null;
52
+ const preview = [];
53
+ if (gatewayInstanceId) preview.push(`- ACPX gateway instance id: ${resolveLegacyGatewayInstancePath(params.stateDir)} -> plugin state (${ACPX_GATEWAY_INSTANCE_NAMESPACE})`);
54
+ if (openLeases.length > 0) preview.push(`- ACPX process leases: ${resolveLegacyProcessLeasePath(params.stateDir)} -> plugin state (${openLeases.length} open lease(s))`);
55
+ return { preview };
56
+ },
57
+ async migrateLegacyState(params) {
58
+ const changes = [];
59
+ const warnings = [];
60
+ const gatewayInstancePath = resolveLegacyGatewayInstancePath(params.stateDir);
61
+ const gatewayInstanceId = await readLegacyGatewayInstanceId(gatewayInstancePath);
62
+ const processLeasePath = resolveLegacyProcessLeasePath(params.stateDir);
63
+ const openLeases = await readLegacyOpenProcessLeases(processLeasePath);
64
+ const processLeaseStore = openAcpxProcessLeaseStateStore(params.context.openPluginStateKeyedStore);
65
+ const gatewayStore = params.context.openPluginStateKeyedStore({
66
+ namespace: ACPX_GATEWAY_INSTANCE_NAMESPACE,
67
+ maxEntries: 1
68
+ });
69
+ const existingGateway = normalizeAcpxGatewayInstanceRecord(await gatewayStore.lookup(ACPX_GATEWAY_INSTANCE_KEY));
70
+ const existingLiveLeases = (await processLeaseStore.entries()).map((entry) => normalizeAcpxProcessLease(entry.value)).filter((lease) => lease != null && (lease.state === "open" || lease.state === "closing"));
71
+ const leaseGatewayIds = new Set(openLeases.map((lease) => lease.gatewayInstanceId));
72
+ const onlyLeaseGatewayId = leaseGatewayIds.size === 1 ? [...leaseGatewayIds][0] : null;
73
+ const canAdoptLegacyGateway = existingGateway && gatewayInstanceId && existingGateway.instanceId !== gatewayInstanceId && onlyLeaseGatewayId === gatewayInstanceId && existingLiveLeases.length === 0;
74
+ const canonicalGatewayInstanceId = canAdoptLegacyGateway || !existingGateway ? gatewayInstanceId ?? onlyLeaseGatewayId : existingGateway.instanceId;
75
+ if (openLeases.length > 0 && (!canonicalGatewayInstanceId || [...leaseGatewayIds].some((leaseGatewayId) => leaseGatewayId !== canonicalGatewayInstanceId))) {
76
+ warnings.push("Skipped ACPX process lease migration because legacy leases do not match the canonical gateway instance id; left legacy sources in place for manual cleanup");
77
+ return {
78
+ changes,
79
+ warnings
80
+ };
81
+ }
82
+ if (canAdoptLegacyGateway && canonicalGatewayInstanceId) {
83
+ await gatewayStore.register(ACPX_GATEWAY_INSTANCE_KEY, {
84
+ instanceId: canonicalGatewayInstanceId,
85
+ createdAt: Date.now()
86
+ });
87
+ changes.push("Migrated ACPX gateway instance id -> plugin state");
88
+ } else if (canonicalGatewayInstanceId && !existingGateway) {
89
+ await gatewayStore.register(ACPX_GATEWAY_INSTANCE_KEY, {
90
+ instanceId: canonicalGatewayInstanceId,
91
+ createdAt: Date.now()
92
+ });
93
+ changes.push("Migrated ACPX gateway instance id -> plugin state");
94
+ } else if (gatewayInstanceId && existingGateway?.instanceId !== gatewayInstanceId) warnings.push("Skipped ACPX gateway instance id import because plugin state already differs");
95
+ if (gatewayInstanceId) await archiveLegacySource({
96
+ filePath: gatewayInstancePath,
97
+ label: "gateway-instance-id",
98
+ changes,
99
+ warnings
100
+ });
101
+ if (openLeases.length > 0) {
102
+ let imported = 0;
103
+ let alreadyPresent = 0;
104
+ for (const lease of openLeases) if (await processLeaseStore.registerIfAbsent(lease.leaseId, lease)) imported++;
105
+ else alreadyPresent++;
106
+ changes.push(`Migrated ACPX process leases -> plugin state (${imported} imported, ${alreadyPresent} already present)`);
107
+ await archiveLegacySource({
108
+ filePath: processLeasePath,
109
+ label: "process-leases",
110
+ changes,
111
+ warnings
112
+ });
113
+ }
114
+ return {
115
+ changes,
116
+ warnings
117
+ };
118
+ }
119
+ }];
120
+ //#endregion
121
+ export { stateMigrations };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createAcpxRuntimeService } from "./register.runtime-DVYJo9yv.js";
1
+ import { t as createAcpxRuntimeService } from "./register.runtime-FxCa4GGt.js";
2
2
  import { tryDispatchAcpReplyHook } from "openclaw/plugin-sdk/acp-runtime-backend";
3
3
  //#region extensions/acpx/index.ts
4
4
  /**
@@ -10,7 +10,10 @@ const plugin = {
10
10
  name: "ACPX Runtime",
11
11
  description: "Embedded ACP runtime backend with plugin-owned session and transport management.",
12
12
  register(api) {
13
- api.registerService(createAcpxRuntimeService({ pluginConfig: api.pluginConfig }));
13
+ api.registerService(createAcpxRuntimeService({
14
+ pluginConfig: api.pluginConfig,
15
+ openKeyedStore: (options) => api.runtime.state.openKeyedStore(options)
16
+ }));
14
17
  api.on("reply_dispatch", tryDispatchAcpReplyHook);
15
18
  }
16
19
  };
@@ -0,0 +1,141 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ //#region extensions/acpx/src/state.ts
3
+ const ACPX_PROCESS_LEASE_NAMESPACE = "process-leases";
4
+ const ACPX_PROCESS_LEASE_MAX_ENTRIES = 4096;
5
+ const ACPX_LEGACY_PROCESS_LEASE_FILE = "process-leases.json";
6
+ const ACPX_GATEWAY_INSTANCE_NAMESPACE = "gateway-instance";
7
+ const ACPX_GATEWAY_INSTANCE_KEY = "current";
8
+ const ACPX_LEGACY_GATEWAY_INSTANCE_FILE = "gateway-instance-id";
9
+ function normalizeAcpxGatewayInstanceRecord(value) {
10
+ if (typeof value !== "object" || value === null) return;
11
+ const record = value;
12
+ if (typeof record.instanceId !== "string" || !record.instanceId.trim()) return;
13
+ const createdAt = typeof record.createdAt === "number" && Number.isFinite(record.createdAt) ? Math.trunc(record.createdAt) : 0;
14
+ return {
15
+ instanceId: record.instanceId.trim(),
16
+ createdAt
17
+ };
18
+ }
19
+ //#endregion
20
+ //#region extensions/acpx/src/process-lease.ts
21
+ /**
22
+ * Persistent lease store for ACPX wrapper processes. Leases let OpenClaw attach
23
+ * gateway/session identity to spawned ACP processes and clean them up later.
24
+ */
25
+ /** Environment variable carrying the ACPX process lease id. */
26
+ const OPENCLAW_ACPX_LEASE_ID_ENV = "OPENCLAW_ACPX_LEASE_ID";
27
+ /** Environment variable carrying the owning gateway instance id. */
28
+ const OPENCLAW_GATEWAY_INSTANCE_ID_ENV = "OPENCLAW_GATEWAY_INSTANCE_ID";
29
+ /** CLI argument carrying the ACPX process lease id for platforms without env wrapping. */
30
+ const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id";
31
+ /** CLI argument carrying the owning gateway instance id. */
32
+ const OPENCLAW_GATEWAY_INSTANCE_ID_ARG = "--openclaw-gateway-instance-id";
33
+ function normalizeAcpxProcessLease(value) {
34
+ if (typeof value !== "object" || value === null) return;
35
+ const record = value;
36
+ if (typeof record.leaseId !== "string" || typeof record.gatewayInstanceId !== "string" || typeof record.sessionKey !== "string" || typeof record.wrapperRoot !== "string" || typeof record.wrapperPath !== "string" || typeof record.rootPid !== "number" || typeof record.commandHash !== "string" || typeof record.startedAt !== "number" || ![
37
+ "open",
38
+ "closing",
39
+ "closed",
40
+ "lost"
41
+ ].includes(String(record.state))) return;
42
+ return {
43
+ leaseId: record.leaseId,
44
+ gatewayInstanceId: record.gatewayInstanceId,
45
+ sessionKey: record.sessionKey,
46
+ wrapperRoot: record.wrapperRoot,
47
+ wrapperPath: record.wrapperPath,
48
+ rootPid: record.rootPid,
49
+ ...typeof record.processGroupId === "number" ? { processGroupId: record.processGroupId } : {},
50
+ commandHash: record.commandHash,
51
+ startedAt: record.startedAt,
52
+ state: record.state
53
+ };
54
+ }
55
+ function normalizeAcpxProcessLeaseFile(value) {
56
+ const root = typeof value === "object" && value !== null ? value : {};
57
+ return {
58
+ version: 1,
59
+ leases: Array.isArray(root.leases) ? root.leases.map(normalizeAcpxProcessLease).filter((lease) => Boolean(lease)) : []
60
+ };
61
+ }
62
+ function openAcpxProcessLeaseStateStore(openKeyedStore) {
63
+ return openKeyedStore({
64
+ namespace: ACPX_PROCESS_LEASE_NAMESPACE,
65
+ maxEntries: ACPX_PROCESS_LEASE_MAX_ENTRIES
66
+ });
67
+ }
68
+ /** Create a serialized SQLite-backed ACPX process lease store. */
69
+ function createAcpxProcessLeaseStore(params) {
70
+ let updateQueue = Promise.resolve();
71
+ async function update(mutator) {
72
+ const run = updateQueue.then(async () => {
73
+ await mutator();
74
+ });
75
+ updateQueue = run.catch(() => {});
76
+ await run;
77
+ }
78
+ async function readCurrent() {
79
+ await updateQueue;
80
+ return (await params.store.entries()).map((entry) => normalizeAcpxProcessLease(entry.value)).filter((lease) => Boolean(lease));
81
+ }
82
+ return {
83
+ async load(leaseId) {
84
+ await updateQueue;
85
+ return normalizeAcpxProcessLease(await params.store.lookup(leaseId));
86
+ },
87
+ async listOpen(gatewayInstanceId) {
88
+ return (await readCurrent()).filter((lease) => (lease.state === "open" || lease.state === "closing") && (!gatewayInstanceId || lease.gatewayInstanceId === gatewayInstanceId));
89
+ },
90
+ async save(lease) {
91
+ await update(async () => {
92
+ await params.store.register(lease.leaseId, lease);
93
+ });
94
+ },
95
+ async markState(leaseId, state) {
96
+ await update(async () => {
97
+ if (state === "closed" || state === "lost") {
98
+ await params.store.delete(leaseId);
99
+ return;
100
+ }
101
+ const lease = normalizeAcpxProcessLease(await params.store.lookup(leaseId));
102
+ if (lease) await params.store.register(leaseId, {
103
+ ...lease,
104
+ state
105
+ });
106
+ });
107
+ }
108
+ };
109
+ }
110
+ /** Create a unique lease id for one ACPX wrapper process. */
111
+ function createAcpxProcessLeaseId() {
112
+ return randomUUID();
113
+ }
114
+ /** Hash a wrapper command so process leases can detect command drift. */
115
+ function hashAcpxProcessCommand(command) {
116
+ return createHash("sha256").update(command).digest("hex");
117
+ }
118
+ function quoteEnvValue(value) {
119
+ return /^[A-Za-z0-9_./:=@+-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
120
+ }
121
+ function appendAcpxLeaseArgs(params) {
122
+ return [
123
+ params.command,
124
+ OPENCLAW_ACPX_LEASE_ID_ARG,
125
+ quoteEnvValue(params.leaseId),
126
+ OPENCLAW_GATEWAY_INSTANCE_ID_ARG,
127
+ quoteEnvValue(params.gatewayInstanceId)
128
+ ].join(" ");
129
+ }
130
+ /** Add ACPX lease identity to a command through env vars and portable args. */
131
+ function withAcpxLeaseEnvironment(params) {
132
+ if ((params.platform ?? process.platform) === "win32") return appendAcpxLeaseArgs(params);
133
+ return [
134
+ "env",
135
+ `${OPENCLAW_ACPX_LEASE_ID_ENV}=${quoteEnvValue(params.leaseId)}`,
136
+ `${OPENCLAW_GATEWAY_INSTANCE_ID_ENV}=${quoteEnvValue(params.gatewayInstanceId)}`,
137
+ appendAcpxLeaseArgs(params)
138
+ ].join(" ");
139
+ }
140
+ //#endregion
141
+ export { createAcpxProcessLeaseStore as a, normalizeAcpxProcessLeaseFile as c, ACPX_GATEWAY_INSTANCE_KEY as d, ACPX_GATEWAY_INSTANCE_NAMESPACE as f, normalizeAcpxGatewayInstanceRecord as h, createAcpxProcessLeaseId as i, openAcpxProcessLeaseStateStore as l, ACPX_LEGACY_PROCESS_LEASE_FILE as m, OPENCLAW_ACPX_LEASE_ID_ENV as n, hashAcpxProcessCommand as o, ACPX_LEGACY_GATEWAY_INSTANCE_FILE as p, OPENCLAW_GATEWAY_INSTANCE_ID_ARG as r, normalizeAcpxProcessLease as s, OPENCLAW_ACPX_LEASE_ID_ARG as t, withAcpxLeaseEnvironment as u };
@@ -1,9 +1,7 @@
1
+ import "./process-lease-DiKkFj6F.js";
1
2
  import { createRequire } from "node:module";
2
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
3
- import fs from "node:fs/promises";
4
3
  import path from "node:path";
5
- import { createHash, randomUUID } from "node:crypto";
6
- import { readJsonFileWithFallback, writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
4
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
7
5
  import { execFile } from "node:child_process";
8
6
  import { promisify } from "node:util";
9
7
  import fsSync from "node:fs";
@@ -58,123 +56,6 @@ function splitCommandParts(value) {
58
56
  return parts;
59
57
  }
60
58
  //#endregion
61
- //#region extensions/acpx/src/process-lease.ts
62
- /**
63
- * Persistent lease store for ACPX wrapper processes. Leases let OpenClaw attach
64
- * gateway/session identity to spawned ACP processes and clean them up later.
65
- */
66
- /** Environment variable carrying the ACPX process lease id. */
67
- const OPENCLAW_ACPX_LEASE_ID_ENV = "OPENCLAW_ACPX_LEASE_ID";
68
- /** Environment variable carrying the owning gateway instance id. */
69
- const OPENCLAW_GATEWAY_INSTANCE_ID_ENV = "OPENCLAW_GATEWAY_INSTANCE_ID";
70
- /** CLI argument carrying the ACPX process lease id for platforms without env wrapping. */
71
- const OPENCLAW_ACPX_LEASE_ID_ARG = "--openclaw-acpx-lease-id";
72
- /** CLI argument carrying the owning gateway instance id. */
73
- const OPENCLAW_GATEWAY_INSTANCE_ID_ARG = "--openclaw-gateway-instance-id";
74
- const LEASE_FILE = "process-leases.json";
75
- function normalizeLease(value) {
76
- if (typeof value !== "object" || value === null) return;
77
- const record = value;
78
- if (typeof record.leaseId !== "string" || typeof record.gatewayInstanceId !== "string" || typeof record.sessionKey !== "string" || typeof record.wrapperRoot !== "string" || typeof record.wrapperPath !== "string" || typeof record.rootPid !== "number" || typeof record.commandHash !== "string" || typeof record.startedAt !== "number" || ![
79
- "open",
80
- "closing",
81
- "closed",
82
- "lost"
83
- ].includes(String(record.state))) return;
84
- return {
85
- leaseId: record.leaseId,
86
- gatewayInstanceId: record.gatewayInstanceId,
87
- sessionKey: record.sessionKey,
88
- wrapperRoot: record.wrapperRoot,
89
- wrapperPath: record.wrapperPath,
90
- rootPid: record.rootPid,
91
- ...typeof record.processGroupId === "number" ? { processGroupId: record.processGroupId } : {},
92
- commandHash: record.commandHash,
93
- startedAt: record.startedAt,
94
- state: record.state
95
- };
96
- }
97
- async function readLeaseFile(filePath) {
98
- const { value } = await readJsonFileWithFallback(filePath, {
99
- version: 1,
100
- leases: []
101
- });
102
- return {
103
- version: 1,
104
- leases: Array.isArray(value.leases) ? value.leases.map(normalizeLease).filter((lease) => Boolean(lease)) : []
105
- };
106
- }
107
- function writeLeaseFile(filePath, value) {
108
- return writeJsonFileAtomically(filePath, value);
109
- }
110
- /** Create a serialized JSON-backed ACPX process lease store. */
111
- function createAcpxProcessLeaseStore(params) {
112
- const filePath = path.join(params.stateDir, LEASE_FILE);
113
- let updateQueue = Promise.resolve();
114
- async function update(mutator) {
115
- const run = updateQueue.then(async () => {
116
- await fs.mkdir(params.stateDir, { recursive: true });
117
- await writeLeaseFile(filePath, {
118
- version: 1,
119
- leases: mutator((await readLeaseFile(filePath)).leases)
120
- });
121
- });
122
- updateQueue = run.catch(() => {});
123
- await run;
124
- }
125
- async function readCurrent() {
126
- await updateQueue;
127
- return await readLeaseFile(filePath);
128
- }
129
- return {
130
- async load(leaseId) {
131
- return (await readCurrent()).leases.find((lease) => lease.leaseId === leaseId);
132
- },
133
- async listOpen(gatewayInstanceId) {
134
- return (await readCurrent()).leases.filter((lease) => (lease.state === "open" || lease.state === "closing") && (!gatewayInstanceId || lease.gatewayInstanceId === gatewayInstanceId));
135
- },
136
- async save(lease) {
137
- await update((leases) => [...leases.filter((entry) => entry.leaseId !== lease.leaseId), lease]);
138
- },
139
- async markState(leaseId, state) {
140
- await update((leases) => leases.map((lease) => lease.leaseId === leaseId ? {
141
- ...lease,
142
- state
143
- } : lease));
144
- }
145
- };
146
- }
147
- /** Create a unique lease id for one ACPX wrapper process. */
148
- function createAcpxProcessLeaseId() {
149
- return randomUUID();
150
- }
151
- /** Hash a wrapper command so process leases can detect command drift. */
152
- function hashAcpxProcessCommand(command) {
153
- return createHash("sha256").update(command).digest("hex");
154
- }
155
- function quoteEnvValue(value) {
156
- return /^[A-Za-z0-9_./:=@+-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
157
- }
158
- function appendAcpxLeaseArgs(params) {
159
- return [
160
- params.command,
161
- OPENCLAW_ACPX_LEASE_ID_ARG,
162
- quoteEnvValue(params.leaseId),
163
- OPENCLAW_GATEWAY_INSTANCE_ID_ARG,
164
- quoteEnvValue(params.gatewayInstanceId)
165
- ].join(" ");
166
- }
167
- /** Add ACPX lease identity to a command through env vars and portable args. */
168
- function withAcpxLeaseEnvironment(params) {
169
- if ((params.platform ?? process.platform) === "win32") return appendAcpxLeaseArgs(params);
170
- return [
171
- "env",
172
- `${OPENCLAW_ACPX_LEASE_ID_ENV}=${quoteEnvValue(params.leaseId)}`,
173
- `${OPENCLAW_GATEWAY_INSTANCE_ID_ENV}=${quoteEnvValue(params.gatewayInstanceId)}`,
174
- appendAcpxLeaseArgs(params)
175
- ].join(" ");
176
- }
177
- //#endregion
178
59
  //#region extensions/acpx/src/config-schema.ts
179
60
  /**
180
61
  * ACPX plugin configuration schema and public config types. Runtime setup uses
@@ -639,4 +520,4 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
639
520
  };
640
521
  }
641
522
  //#endregion
642
- export { resolveAcpxPluginRoot as a, OPENCLAW_ACPX_LEASE_ID_ENV as c, createAcpxProcessLeaseStore as d, hashAcpxProcessCommand as f, splitCommandParts as h, resolveAcpxPluginConfig as i, OPENCLAW_GATEWAY_INSTANCE_ID_ARG as l, quoteCommandPart as m, isOpenClawLeaseAwareAcpxProcessCommand as n, toAcpMcpServers as o, withAcpxLeaseEnvironment as p, reapStaleOpenClawOwnedAcpxOrphans as r, OPENCLAW_ACPX_LEASE_ID_ARG as s, cleanupOpenClawOwnedAcpxProcessTree as t, createAcpxProcessLeaseId as u };
523
+ export { resolveAcpxPluginRoot as a, splitCommandParts as c, resolveAcpxPluginConfig as i, isOpenClawLeaseAwareAcpxProcessCommand as n, toAcpMcpServers as o, reapStaleOpenClawOwnedAcpxOrphans as r, quoteCommandPart as s, cleanupOpenClawOwnedAcpxProcessTree as t };
@@ -205,7 +205,7 @@ function createLazyAcpRuntimeProxy(resolveRuntime) {
205
205
  const ACPX_BACKEND_ID = "acpx";
206
206
  let serviceModulePromise = null;
207
207
  function loadServiceModule() {
208
- serviceModulePromise ??= import("./service-8gUnMlij.js");
208
+ serviceModulePromise ??= import("./service-DepTzi7g.js");
209
209
  return serviceModulePromise;
210
210
  }
211
211
  async function startRealService(state) {
@@ -1,2 +1,2 @@
1
- import { t as createAcpxRuntimeService } from "./register.runtime-DVYJo9yv.js";
1
+ import { t as createAcpxRuntimeService } from "./register.runtime-FxCa4GGt.js";
2
2
  export { createAcpxRuntimeService };
@@ -1,9 +1,10 @@
1
+ import { i as createAcpxProcessLeaseId, o as hashAcpxProcessCommand, u as withAcpxLeaseEnvironment } from "./process-lease-DiKkFj6F.js";
1
2
  import { AcpRuntimeError } from "./runtime-api.js";
2
- import { f as hashAcpxProcessCommand, h as splitCommandParts, n as isOpenClawLeaseAwareAcpxProcessCommand, p as withAcpxLeaseEnvironment, t as cleanupOpenClawOwnedAcpxProcessTree, u as createAcpxProcessLeaseId } from "./process-reaper-CEUqe0IY.js";
3
- import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
4
- import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { c as splitCommandParts, n as isOpenClawLeaseAwareAcpxProcessCommand, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-Dv_iMcOo.js";
5
4
  import fs from "node:fs/promises";
6
5
  import path, { resolve } from "node:path";
6
+ import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import { AsyncLocalStorage } from "node:async_hooks";
7
8
  import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState } from "acpx/runtime";
8
9
  import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
9
10
  import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
@@ -83,6 +84,11 @@ function readOpenClawLeaseIdFromRecord(record) {
83
84
  const { openclawLeaseId } = record;
84
85
  return typeof openclawLeaseId === "string" ? openclawLeaseId.trim() || void 0 : void 0;
85
86
  }
87
+ function readOpenClawGatewayInstanceIdFromRecord(record) {
88
+ if (typeof record !== "object" || record === null) return;
89
+ const { openclawGatewayInstanceId } = record;
90
+ return typeof openclawGatewayInstanceId === "string" ? openclawGatewayInstanceId.trim() || void 0 : void 0;
91
+ }
86
92
  function extractGeneratedWrapperPath(command) {
87
93
  return splitCommandParts(command ?? "").find((part) => basename(part) === "codex-acp-wrapper.mjs" || basename(part) === "claude-agent-acp-wrapper.mjs") ?? "";
88
94
  }
@@ -502,9 +508,12 @@ var AcpxRuntime = class {
502
508
  agentRegistry: this.agentRegistry
503
509
  });
504
510
  if (!rootPid || !rootCommand) return;
511
+ const expectedGatewayInstanceId = readOpenClawGatewayInstanceIdFromRecord(record);
505
512
  await cleanupOpenClawOwnedAcpxProcessTree({
506
513
  rootPid,
507
514
  rootCommand,
515
+ ...leaseId ? { expectedLeaseId: leaseId } : {},
516
+ ...expectedGatewayInstanceId ? { expectedGatewayInstanceId } : {},
508
517
  wrapperRoot: this.wrapperRoot,
509
518
  deps: this.processCleanupDeps
510
519
  });
@@ -1,16 +1,17 @@
1
- import { n as createLazyAcpRuntimeProxy } from "./register.runtime-DVYJo9yv.js";
1
+ import { n as createLazyAcpRuntimeProxy } from "./register.runtime-FxCa4GGt.js";
2
+ import { a as createAcpxProcessLeaseStore, d as ACPX_GATEWAY_INSTANCE_KEY, f as ACPX_GATEWAY_INSTANCE_NAMESPACE, h as normalizeAcpxGatewayInstanceRecord, l as openAcpxProcessLeaseStateStore, n as OPENCLAW_ACPX_LEASE_ID_ENV, r as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, t as OPENCLAW_ACPX_LEASE_ID_ARG } from "./process-lease-DiKkFj6F.js";
2
3
  import { registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "./runtime-api.js";
3
- import { a as resolveAcpxPluginRoot, c as OPENCLAW_ACPX_LEASE_ID_ENV, d as createAcpxProcessLeaseStore, h as splitCommandParts, i as resolveAcpxPluginConfig, l as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, m as quoteCommandPart, o as toAcpMcpServers, r as reapStaleOpenClawOwnedAcpxOrphans, s as OPENCLAW_ACPX_LEASE_ID_ARG, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-CEUqe0IY.js";
4
+ import { a as resolveAcpxPluginRoot, c as splitCommandParts, i as resolveAcpxPluginConfig, o as toAcpMcpServers, r as reapStaleOpenClawOwnedAcpxOrphans, s as quoteCommandPart, t as cleanupOpenClawOwnedAcpxProcessTree } from "./process-reaper-Dv_iMcOo.js";
4
5
  import { createRequire } from "node:module";
5
6
  import fs from "node:fs/promises";
6
7
  import path from "node:path";
7
- import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
8
8
  import { randomUUID } from "node:crypto";
9
- import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
9
+ import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
10
10
  import { inspect } from "node:util";
11
11
  import fsSync from "node:fs";
12
12
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
13
13
  import os from "node:os";
14
+ import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
14
15
  //#region extensions/acpx/src/codex-trust-config.ts
15
16
  /**
16
17
  * Builds isolated Codex config for ACPX sessions. It preserves safe inherited
@@ -846,7 +847,7 @@ const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
846
847
  const ACPX_BACKEND_ID = "acpx";
847
848
  let runtimeModulePromise = null;
848
849
  function loadRuntimeModule() {
849
- runtimeModulePromise ??= import("./runtime-COFrLvHN.js");
850
+ runtimeModulePromise ??= import("./runtime-C3wjx6fQ.js");
850
851
  return runtimeModulePromise;
851
852
  }
852
853
  /** Convert ACPX timeout seconds into timer-safe milliseconds. */
@@ -955,17 +956,21 @@ async function withStartupProbeTimeout(params) {
955
956
  if (timeout) clearTimeout(timeout);
956
957
  }
957
958
  }
958
- async function resolveGatewayInstanceId(stateDir) {
959
- const filePath = path.join(stateDir, "gateway-instance-id");
960
- try {
961
- const existing = (await fs.readFile(filePath, "utf8")).trim();
962
- if (existing) return existing;
963
- } catch (error) {
964
- if (error.code !== "ENOENT") throw error;
965
- }
959
+ function openGatewayInstanceStateStore(openKeyedStore) {
960
+ return openKeyedStore({
961
+ namespace: ACPX_GATEWAY_INSTANCE_NAMESPACE,
962
+ maxEntries: 1
963
+ });
964
+ }
965
+ async function resolveGatewayInstanceId(openKeyedStore) {
966
+ const store = openGatewayInstanceStateStore(openKeyedStore);
967
+ const existing = normalizeAcpxGatewayInstanceRecord(await store.lookup(ACPX_GATEWAY_INSTANCE_KEY));
968
+ if (existing) return existing.instanceId;
966
969
  const next = randomUUID();
967
- await fs.mkdir(stateDir, { recursive: true });
968
- await fs.writeFile(filePath, `${next}\n`, { mode: 384 });
970
+ await store.register(ACPX_GATEWAY_INSTANCE_KEY, {
971
+ instanceId: next,
972
+ createdAt: Date.now()
973
+ });
969
974
  return next;
970
975
  }
971
976
  async function reapOpenAcpxProcessLeases(params) {
@@ -1017,6 +1022,8 @@ function createAcpxRuntimeService(params = {}) {
1017
1022
  ctx.logger.info("skipping embedded acpx runtime backend (OPENCLAW_SKIP_ACPX_RUNTIME=1)");
1018
1023
  return;
1019
1024
  }
1025
+ const openKeyedStore = params.openKeyedStore;
1026
+ if (!openKeyedStore) throw new Error("ACPX runtime service requires plugin keyed state");
1020
1027
  const basePluginConfig = await measureAcpxStartup(ctx, "config.resolve", () => resolveAcpxPluginConfig({
1021
1028
  rawConfig: params.pluginConfig,
1022
1029
  workspaceDir: ctx.workspaceDir
@@ -1035,8 +1042,8 @@ function createAcpxRuntimeService(params = {}) {
1035
1042
  await fs.mkdir(pluginConfig.stateDir, { recursive: true });
1036
1043
  await fs.mkdir(wrapperRoot, { recursive: true });
1037
1044
  });
1038
- const gatewayInstanceId = await measureAcpxStartup(ctx, "gateway-instance-id", () => resolveGatewayInstanceId(ctx.stateDir));
1039
- const processLeaseStore = createAcpxProcessLeaseStore({ stateDir: wrapperRoot });
1045
+ const gatewayInstanceId = await measureAcpxStartup(ctx, "gateway-instance-id", () => resolveGatewayInstanceId(openKeyedStore));
1046
+ const processLeaseStore = createAcpxProcessLeaseStore({ store: openAcpxProcessLeaseStateStore(openKeyedStore) });
1040
1047
  const startupReap = await measureAcpxStartup(ctx, "process-leases.reap", () => reapOpenAcpxProcessLeases({
1041
1048
  gatewayInstanceId,
1042
1049
  leaseStore: processLeaseStore,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/acpx",
3
- "version": "2026.6.5-beta.2",
3
+ "version": "2026.6.5-beta.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/acpx",
9
- "version": "2026.6.5-beta.2",
9
+ "version": "2026.6.5-beta.5",
10
10
  "dependencies": {
11
11
  "@agentclientprotocol/claude-agent-acp": "0.39.0",
12
12
  "@zed-industries/codex-acp": "0.15.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/acpx",
3
- "version": "2026.6.5-beta.2",
3
+ "version": "2026.6.5-beta.5",
4
4
  "description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,10 +26,10 @@
26
26
  "minHostVersion": ">=2026.4.25"
27
27
  },
28
28
  "compat": {
29
- "pluginApi": ">=2026.6.5-beta.2"
29
+ "pluginApi": ">=2026.6.5-beta.5"
30
30
  },
31
31
  "build": {
32
- "openclawVersion": "2026.6.5-beta.2",
32
+ "openclawVersion": "2026.6.5-beta.5",
33
33
  "staticAssets": [
34
34
  {
35
35
  "source": "./src/runtime-internals/mcp-proxy.mjs",
@@ -58,7 +58,7 @@
58
58
  "skills/**"
59
59
  ],
60
60
  "peerDependencies": {
61
- "openclaw": ">=2026.6.5-beta.2"
61
+ "openclaw": ">=2026.6.5-beta.5"
62
62
  },
63
63
  "peerDependenciesMeta": {
64
64
  "openclaw": {