@openclaw/acpx 2026.9.7 → 2026.10.1-beta.1

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.
@@ -1,31 +1,28 @@
1
- import { t as resolveAcpxSessionResource } from "./session-resource-BaJ6bs5d.mjs";
2
- import { i as toAcpMcpServers, n as resolveAcpxPluginRoot, r as resolveOpenClawRoot, t as resolveAcpxPluginConfig } from "./config-CdPsIued.mjs";
3
- import { a as renderAgentCommand, c as CODEX_ACP_BIN, d as OPENCLAW_CODEX_CONFIG_ARG, i as normalizeAgentName, l as CODEX_ACP_PACKAGE, n as isCodexAcpCommand, o as resolveAgentCommand, r as isOpenClawBridgeCommand, s as splitCommandParts, t as isClaudeAcpCommand, u as LEGACY_CODEX_ACP_PACKAGE } from "./command-line-CPBLOiZM.mjs";
4
- import { a as hashAcpxProcessCommand, c as openAcpxProcessLeaseStateStore, d as ACPX_GATEWAY_INSTANCE_KEY, f as ACPX_GATEWAY_INSTANCE_NAMESPACE, h as normalizeAcpxGatewayInstanceRecord, i as createAcpxProcessLeaseStore, l as readAcpxProcessLeaseIdentity, n as OPENCLAW_ACPX_LEASE_ID_ARG, r as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, t as ACPX_PROBE_LEASE_SESSION_KEY, u as withAcpxLeaseArgs } from "./process-lease-C3dNPtu8.mjs";
1
+ import { a as isClaudeAcpCommand, c as renderAgentCommand, d as CODEX_ACP_BIN, f as CODEX_ACP_PACKAGE, i as toAcpMcpServers, l as resolveAgentCommand, m as OPENCLAW_CODEX_CONFIG_ARG, n as resolveAcpxPluginRoot, o as isCodexAcpCommand, p as LEGACY_CODEX_ACP_PACKAGE, r as resolveOpenClawRoot, s as isOpenClawBridgeCommand, t as resolveAcpxPluginConfig, u as splitCommandParts } from "./config-76krYh6C.mjs";
5
2
  import { AcpRuntimeError } from "../runtime-api.js";
3
+ import { t as resolveAcpxSessionResource } from "./session-resource-DKJsUJPu.mjs";
4
+ import { a as hashAcpxProcessCommand, adoptAcpxStateDirectory, c as withAcpxLeaseArgs, d as normalizeAcpxGatewayInstanceRecord, i as createAcpxProcessLeaseStore, l as ACPX_GATEWAY_INSTANCE_KEY, n as OPENCLAW_ACPX_LEASE_ID_ARG, o as openAcpxProcessLeaseStateStore, r as OPENCLAW_GATEWAY_INSTANCE_ID_ARG, s as readAcpxProcessLeaseIdentity, t as ACPX_PROBE_LEASE_SESSION_KEY, u as ACPX_GATEWAY_INSTANCE_NAMESPACE } from "./session-owner-migration-C73lfijV.mjs";
6
5
  import { createRequire } from "node:module";
7
6
  import { AcpxRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
8
7
  import { finiteSecondsToTimerSafeMilliseconds, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
9
8
  import fs from "node:fs";
10
9
  import path, { resolve } from "node:path";
11
- import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
10
+ import { asOptionalObjectRecord, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
12
11
  import os, { availableParallelism } from "node:os";
13
- import fs$1 from "node:fs/promises";
14
12
  import { randomUUID } from "node:crypto";
15
13
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
16
14
  import { escapeRegExp, sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
17
- import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
15
+ import fs$1 from "node:fs/promises";
16
+ import { raceWithTimeout } from "openclaw/plugin-sdk/time-runtime";
17
+ import { tryReadJson } from "@openclaw/fs-safe/json";
18
18
  import { parse, stringify } from "smol-toml";
19
+ import { setTimeout } from "node:timers/promises";
19
20
  import { isPidAlive, runExec } from "openclaw/plugin-sdk/process-runtime";
20
21
  import { AsyncLocalStorage } from "node:async_hooks";
21
22
  import { isDeepStrictEqual } from "node:util";
22
23
  import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
23
24
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
24
25
  //#region extensions/acpx/src/codex-trust-config.ts
25
- /**
26
- * Builds isolated Codex config for ACPX sessions. It preserves safe inherited
27
- * runtime options while rendering only trusted project entries for the session.
28
- */
29
26
  function stripTomlComment(line) {
30
27
  let quote = null;
31
28
  let escaping = false;
@@ -97,10 +94,8 @@ function parseTomlDottedKey(value) {
97
94
  return parts.map((part) => parseTomlString(part) ?? part);
98
95
  }
99
96
  function parseProjectHeader(line) {
100
- const trimmed = line.trim();
101
- if (!trimmed.startsWith("[") || !trimmed.endsWith("]") || trimmed.startsWith("[[")) return;
102
- const parts = parseTomlDottedKey(trimmed.slice(1, -1));
103
- return parts.length === 2 && parts[0] === "projects" ? parts[1] : void 0;
97
+ const parts = parseTableHeader(line);
98
+ return parts?.length === 2 && parts[0] === "projects" ? parts[1] : void 0;
104
99
  }
105
100
  function parseTrustedInlineProjectEntries(value) {
106
101
  const trusted = [];
@@ -113,7 +108,6 @@ function parseTrustedInlineProjectEntries(value) {
113
108
  }
114
109
  return trusted;
115
110
  }
116
- /** Extract trusted project paths from Codex TOML config. */
117
111
  function extractTrustedCodexProjectPaths(configToml) {
118
112
  const trusted = /* @__PURE__ */ new Set();
119
113
  let currentProjectPath;
@@ -208,10 +202,8 @@ function extractInheritedCodexRuntimeConfig(configToml) {
208
202
  if (!INHERITED_TOP_LEVEL_CODEX_CONFIG_KEYS.has(key)) continue;
209
203
  inheritedLines.push(rawLine.trimEnd());
210
204
  }
211
- while (inheritedLines.length > 0 && inheritedLines[inheritedLines.length - 1] === "") inheritedLines.pop();
212
205
  return inheritedLines.join("\n");
213
206
  }
214
- /** Render a session-local Codex config with inherited runtime settings and trust entries. */
215
207
  function renderIsolatedCodexConfig(params) {
216
208
  const normalized = Array.from(new Set(params.projectPaths.map((projectPath) => projectPath.trim()).filter(Boolean).map((projectPath) => path.resolve(projectPath)))).toSorted((left, right) => left.localeCompare(right));
217
209
  return [
@@ -258,8 +250,8 @@ function resolvePackageBinPath(packageJsonPath, manifest, binName) {
258
250
  async function resolveInstalledAcpPackageBinPath(packageName, binName) {
259
251
  try {
260
252
  const packageJsonPath = requireFromHere$1.resolve(`${packageName}/package.json`);
261
- const { value: manifest } = await readJsonFileWithFallback(packageJsonPath, {});
262
- if (manifest.name !== packageName) return;
253
+ const manifest = await tryReadJson(packageJsonPath);
254
+ if (manifest?.name !== packageName) return;
263
255
  const binPath = resolvePackageBinPath(packageJsonPath, manifest, binName);
264
256
  if (!binPath) return;
265
257
  await fs$1.access(binPath);
@@ -268,12 +260,6 @@ async function resolveInstalledAcpPackageBinPath(packageName, binName) {
268
260
  return;
269
261
  }
270
262
  }
271
- async function resolveInstalledCodexAcpBinPath() {
272
- return await resolveInstalledAcpPackageBinPath(CODEX_ACP_PACKAGE, CODEX_ACP_BIN);
273
- }
274
- async function resolveInstalledClaudeAcpBinPath() {
275
- return await resolveInstalledAcpPackageBinPath(CLAUDE_ACP_PACKAGE, CLAUDE_ACP_BIN);
276
- }
277
263
  const DIAGNOSTIC_REDACTION_RULES = [
278
264
  {
279
265
  source: String.raw`(authorization\s*[:=]\s*bearer\s+)[^\s'"<>]+`,
@@ -595,7 +581,9 @@ const npmCliPath = resolveNpmCliPath();
595
581
  const installedBinPath = ${params.installedBinPath ? JSON.stringify(params.installedBinPath) : "undefined"};
596
582
  let defaultCommand;
597
583
  let defaultArgs;
598
- if (installedBinPath) {
584
+ // Plugin capture/install directories are disposable: a durable wrapper can
585
+ // outlive the path it captured, so re-check the target before trusting it.
586
+ if (installedBinPath && existsSync(installedBinPath)) {
599
587
  defaultCommand = process.execPath;
600
588
  defaultArgs = [installedBinPath];
601
589
  } else if (npmCliPath) {
@@ -858,13 +846,11 @@ function buildWrapperCommand(wrapperPath, args = []) {
858
846
  ];
859
847
  }
860
848
  function isAcpPackageSpec(value, packageName) {
861
- const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
862
- return new RegExp(`^${escapedPackageName}(?:@.+)?$`, "i").test(value.trim());
849
+ return new RegExp(`^${escapeRegExp(packageName)}(?:@.+)?$`, "i").test(value.trim());
863
850
  }
864
851
  function isAcpBinName(value, binName) {
865
852
  const commandName = basename(value);
866
- const escapedBinName = binName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
867
- return new RegExp(`^${escapedBinName}(?:\\.exe|\\.[cm]?js)?$`, "i").test(commandName);
853
+ return new RegExp(`^${escapeRegExp(binName)}(?:\\.exe|\\.[cm]?js)?$`, "i").test(commandName);
868
854
  }
869
855
  function isPackageRunnerCommand(value) {
870
856
  return /^(?:npx|npm|pnpm|bunx)(?:\.cmd|\.exe)?$/i.test(basename(value));
@@ -974,7 +960,6 @@ function buildClaudeAcpWrapperCommand(wrapperPath, configuredCommand) {
974
960
  }
975
961
  /** Prepare ACPX agent commands and isolated auth homes for Codex/Claude adapters. */
976
962
  async function prepareAcpxCodexAuthConfig(params) {
977
- params.logger;
978
963
  const codexBaseDir = path.join(params.stateDir, "acpx");
979
964
  const configuredCodexCommand = params.pluginConfig.agents.codex;
980
965
  const configuredClaudeCommand = params.pluginConfig.agents.claude;
@@ -986,8 +971,8 @@ async function prepareAcpxCodexAuthConfig(params) {
986
971
  }),
987
972
  migratedConfig: codexLaunch?.migratedConfig
988
973
  });
989
- const installedCodexBinPath = await (params.resolveInstalledCodexAcpBinPath ?? resolveInstalledCodexAcpBinPath)();
990
- const installedClaudeBinPath = await (params.resolveInstalledClaudeAcpBinPath ?? resolveInstalledClaudeAcpBinPath)();
974
+ const installedCodexBinPath = await (params.resolveInstalledCodexAcpBinPath ? params.resolveInstalledCodexAcpBinPath() : resolveInstalledAcpPackageBinPath(CODEX_ACP_PACKAGE, CODEX_ACP_BIN));
975
+ const installedClaudeBinPath = await (params.resolveInstalledClaudeAcpBinPath ? params.resolveInstalledClaudeAcpBinPath() : resolveInstalledAcpPackageBinPath(CLAUDE_ACP_PACKAGE, CLAUDE_ACP_BIN));
991
976
  const wrapperPath = await writeAdapterWrapper(codexBaseDir, "codex-acp-wrapper.mjs", buildCodexAcpWrapperScript(installedCodexBinPath));
992
977
  const claudeWrapperPath = await writeAdapterWrapper(codexBaseDir, "claude-agent-acp-wrapper.mjs", buildClaudeAcpWrapperScript(installedClaudeBinPath));
993
978
  return {
@@ -1114,7 +1099,6 @@ function parseProcessList(stdout) {
1114
1099
  }
1115
1100
  return processes;
1116
1101
  }
1117
- /** List host processes in the compact shape needed by ACPX cleanup. */
1118
1102
  async function listPlatformProcesses() {
1119
1103
  if (process.platform === "win32") return [];
1120
1104
  const { stdout } = await runExec("ps", ["-axo", "pid=,ppid=,command="], {
@@ -1146,25 +1130,21 @@ function collectProcessTree(processes, rootPid) {
1146
1130
  function uniquePids(processes) {
1147
1131
  return Array.from(new Set(processes.map((processInfo) => processInfo.pid).filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid)));
1148
1132
  }
1149
- async function terminatePids(pids, deps) {
1150
- const killProcess = deps?.killProcess ?? ((pid, signal) => process.kill(pid, signal));
1151
- const sleep = deps?.sleep ?? ((ms) => new Promise((resolve) => {
1152
- setTimeout(resolve, ms);
1153
- }));
1133
+ async function terminatePids(pids, assertCurrent) {
1154
1134
  const terminated = [];
1155
1135
  for (const pid of pids) {
1156
- deps?.assertCurrent?.();
1136
+ assertCurrent?.();
1157
1137
  try {
1158
- killProcess(pid, "SIGTERM");
1138
+ process.kill(pid, "SIGTERM");
1159
1139
  terminated.push(pid);
1160
1140
  } catch {}
1161
1141
  }
1162
1142
  if (terminated.length === 0) return terminated;
1163
- await sleep(750);
1143
+ await setTimeout(750);
1164
1144
  for (const pid of terminated) {
1165
- deps?.assertCurrent?.();
1166
- if (deps?.killProcess || isPidAlive(pid)) try {
1167
- killProcess(pid, "SIGKILL");
1145
+ assertCurrent?.();
1146
+ if (isPidAlive(pid)) try {
1147
+ process.kill(pid, "SIGKILL");
1168
1148
  } catch {}
1169
1149
  }
1170
1150
  return terminated;
@@ -1177,14 +1157,14 @@ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
1177
1157
  terminatedPids: [],
1178
1158
  skippedReason: "missing-root"
1179
1159
  };
1180
- if ((params.deps?.platform ?? process.platform) === "win32") return {
1160
+ if (process.platform === "win32") return {
1181
1161
  inspectedPids: [],
1182
1162
  terminatedPids: [],
1183
1163
  skippedReason: "unsupported-platform"
1184
1164
  };
1185
1165
  let processes;
1186
1166
  try {
1187
- processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
1167
+ processes = await listPlatformProcesses();
1188
1168
  } catch {
1189
1169
  return {
1190
1170
  inspectedPids: [],
@@ -1216,12 +1196,12 @@ async function cleanupOpenClawOwnedAcpxProcessTree(params) {
1216
1196
  const pids = uniquePids(listedTree.toReversed());
1217
1197
  return {
1218
1198
  inspectedPids: uniquePids(listedTree),
1219
- terminatedPids: await terminatePids(pids, params.deps)
1199
+ terminatedPids: await terminatePids(pids, params.assertCurrent)
1220
1200
  };
1221
1201
  }
1222
1202
  /** Recover a pending lease by matching its exact live wrapper identity. */
1223
1203
  async function cleanupOpenClawOwnedAcpxPendingLease(params) {
1224
- if ((params.deps?.platform ?? process.platform) === "win32") return {
1204
+ if (process.platform === "win32") return {
1225
1205
  inspectedPids: [],
1226
1206
  terminatedPids: [],
1227
1207
  skippedReason: "unsupported-platform"
@@ -1233,7 +1213,7 @@ async function cleanupOpenClawOwnedAcpxPendingLease(params) {
1233
1213
  };
1234
1214
  let processes;
1235
1215
  try {
1236
- processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
1216
+ processes = await listPlatformProcesses();
1237
1217
  } catch {
1238
1218
  return {
1239
1219
  inspectedPids: [],
@@ -1260,19 +1240,19 @@ async function cleanupOpenClawOwnedAcpxPendingLease(params) {
1260
1240
  const pids = uniquePids(listedTree.toReversed());
1261
1241
  return {
1262
1242
  inspectedPids: uniquePids(listedTree),
1263
- terminatedPids: await terminatePids(pids, params.deps)
1243
+ terminatedPids: await terminatePids(pids, params.assertCurrent)
1264
1244
  };
1265
1245
  }
1266
1246
  /** Reap orphaned OpenClaw-owned ACPX wrapper trees during runtime startup. */
1267
1247
  async function reapStaleOpenClawOwnedAcpxOrphans(params) {
1268
- if ((params.deps?.platform ?? process.platform) === "win32") return {
1248
+ if (process.platform === "win32") return {
1269
1249
  inspectedPids: [],
1270
1250
  terminatedPids: [],
1271
1251
  skippedReason: "unsupported-platform"
1272
1252
  };
1273
1253
  let processes;
1274
1254
  try {
1275
- processes = await (params.deps?.listProcesses ?? listPlatformProcesses)();
1255
+ processes = await listPlatformProcesses();
1276
1256
  } catch {
1277
1257
  return {
1278
1258
  inspectedPids: [],
@@ -1286,7 +1266,7 @@ async function reapStaleOpenClawOwnedAcpxOrphans(params) {
1286
1266
  })).map((orphan) => collectProcessTree(processes, orphan.pid));
1287
1267
  return {
1288
1268
  inspectedPids: uniquePids(orphanTrees.flat()),
1289
- terminatedPids: await terminatePids(uniquePids(orphanTrees.flatMap((tree) => tree.toReversed())), params.deps)
1269
+ terminatedPids: await terminatePids(uniquePids(orphanTrees.flatMap((tree) => tree.toReversed())), params.assertCurrent)
1290
1270
  };
1291
1271
  }
1292
1272
  //#endregion
@@ -1369,7 +1349,7 @@ var AcpxGenerationRegistry = class {
1369
1349
  return generation.delegate;
1370
1350
  }
1371
1351
  currentGeneration(resource) {
1372
- if (this.stopping) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
1352
+ this.assertRunning();
1373
1353
  let generation = this.generations.get(resource);
1374
1354
  if (!generation) {
1375
1355
  const fresh = this.sessionStore.isFresh(resource);
@@ -1512,40 +1492,28 @@ function captureGenerationRecord(generation, record) {
1512
1492
  const acpxGenerationKey = Symbol("openclaw.acpxGeneration");
1513
1493
  const acpxOperationScope = new AsyncLocalStorage();
1514
1494
  function readSessionRecordName(record) {
1515
- if (typeof record !== "object" || record === null) return "";
1516
- const { name } = record;
1517
- return typeof name === "string" ? name.trim() : "";
1495
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.name) ?? "";
1518
1496
  }
1519
1497
  function readRecordAgentCommand(record) {
1520
1498
  return record?.agentArgv ?? record?.agentCommand;
1521
1499
  }
1522
1500
  function readRecordCwd(record) {
1523
- if (typeof record !== "object" || record === null) return;
1524
- const { cwd } = record;
1525
- return typeof cwd === "string" ? cwd.trim() || void 0 : void 0;
1501
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.cwd);
1526
1502
  }
1527
1503
  function readRecordResetOnNextEnsure(record) {
1528
- if (typeof record !== "object" || record === null) return false;
1529
- const { acpx } = record;
1530
- if (typeof acpx !== "object" || acpx === null) return false;
1531
- return acpx.reset_on_next_ensure === true;
1504
+ return asOptionalObjectRecord(asOptionalObjectRecord(record)?.acpx)?.reset_on_next_ensure === true;
1532
1505
  }
1533
1506
  function readRecordAgentPid(record) {
1534
- if (typeof record !== "object" || record === null) return;
1535
- const { pid, processId } = record;
1507
+ const { pid, processId } = asOptionalObjectRecord(record) ?? {};
1536
1508
  const rawPid = pid ?? processId;
1537
1509
  const numericPid = typeof rawPid === "number" ? rawPid : typeof rawPid === "string" ? parseStrictPositiveInteger(rawPid) : void 0;
1538
1510
  return numericPid && Number.isInteger(numericPid) && numericPid > 0 ? numericPid : void 0;
1539
1511
  }
1540
1512
  function readOpenClawLeaseIdFromRecord(record) {
1541
- if (typeof record !== "object" || record === null) return;
1542
- const { openclawLeaseId } = record;
1543
- return typeof openclawLeaseId === "string" ? openclawLeaseId.trim() || void 0 : void 0;
1513
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.openclawLeaseId);
1544
1514
  }
1545
1515
  function readOpenClawGatewayInstanceIdFromRecord(record) {
1546
- if (typeof record !== "object" || record === null) return;
1547
- const { openclawGatewayInstanceId } = record;
1548
- return typeof openclawGatewayInstanceId === "string" ? openclawGatewayInstanceId.trim() || void 0 : void 0;
1516
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.openclawGatewayInstanceId);
1549
1517
  }
1550
1518
  function extractGeneratedWrapperPath(command) {
1551
1519
  return splitCommandParts(command ?? "").find((part) => (part.split(/[\\/]/).pop() ?? "") === "codex-acp-wrapper.mjs" || (part.split(/[\\/]/).pop() ?? "") === "claude-agent-acp-wrapper.mjs") ?? "";
@@ -1569,27 +1537,24 @@ function createResetAwareSessionStore(baseStore, params) {
1569
1537
  const resource = scope?.generation.resource ?? sessionId.trim();
1570
1538
  const pending = pendingWrites.get(sessionId.trim());
1571
1539
  if (pending && (scope?.generation.awaitPriorWrites || freshSessionKeys.has(resource))) await Promise.allSettled(pending);
1572
- const load = async () => {
1573
- if (scope?.generation.retired) return;
1574
- const normalized = sessionId.trim();
1575
- if (normalized && freshSessionKeys.has(normalized)) return;
1576
- const record = await baseStore.load(sessionId);
1577
- if (scope?.generation.retired || freshSessionKeys.has(scope?.generation.resource ?? normalized)) return;
1578
- if (scope && record) captureGenerationRecord(scope.generation, record);
1579
- if (!record || !params?.leaseStore || !params.gatewayInstanceId) return record;
1580
- const sessionName = readSessionRecordName(record) || normalized;
1581
- const lease = selectCurrentSessionLease({
1582
- leases: await params.leaseStore.listOpen(params.gatewayInstanceId),
1583
- sessionKeys: [sessionName, normalized],
1584
- rootPid: readRecordAgentPid(record)
1585
- });
1586
- if (!lease) return record;
1587
- if (scope?.generation.retired) return;
1588
- const leasedRecord = withOpenClawLeaseSessionMetadata(record, lease);
1589
- if (scope) captureGenerationRecord(scope.generation, leasedRecord);
1590
- return leasedRecord;
1591
- };
1592
- return await load();
1540
+ if (scope?.generation.retired) return;
1541
+ const normalized = sessionId.trim();
1542
+ if (normalized && freshSessionKeys.has(normalized)) return;
1543
+ const record = await baseStore.load(sessionId);
1544
+ if (scope?.generation.retired || freshSessionKeys.has(scope?.generation.resource ?? normalized)) return;
1545
+ if (scope && record) captureGenerationRecord(scope.generation, record);
1546
+ if (!record || !params?.leaseStore || !params.gatewayInstanceId) return record;
1547
+ const sessionName = readSessionRecordName(record) || normalized;
1548
+ const lease = selectCurrentSessionLease({
1549
+ leases: await params.leaseStore.listOpen(params.gatewayInstanceId),
1550
+ sessionKeys: [sessionName, normalized],
1551
+ rootPid: readRecordAgentPid(record)
1552
+ });
1553
+ if (!lease) return record;
1554
+ if (scope?.generation.retired) return;
1555
+ const leasedRecord = withOpenClawLeaseSessionMetadata(record, lease);
1556
+ if (scope) captureGenerationRecord(scope.generation, leasedRecord);
1557
+ return leasedRecord;
1593
1558
  },
1594
1559
  async save(record) {
1595
1560
  const scope = acpxOperationScope.getStore();
@@ -1660,7 +1625,7 @@ function createResetAwareSessionStore(baseStore, params) {
1660
1625
  //#region extensions/acpx/src/runtime-process-cleanup.ts
1661
1626
  /** Capture OpenClaw wrapper cleanup ownership before a backend close can yield. */
1662
1627
  async function prepareAcpxProcessCleanup(params) {
1663
- const { leaseStore, gatewayInstanceId, wrapperRoot, deps } = params;
1628
+ const { leaseStore, gatewayInstanceId, wrapperRoot } = params;
1664
1629
  const rootPid = readRecordAgentPid(params.record);
1665
1630
  const rootCommand = params.command ? renderAgentCommand(params.command) : void 0;
1666
1631
  const identity = readAcpxProcessLeaseIdentity(params.command);
@@ -1684,14 +1649,12 @@ async function prepareAcpxProcessCleanup(params) {
1684
1649
  rootCommand,
1685
1650
  expectedLeaseId: lease.leaseId,
1686
1651
  expectedGatewayInstanceId: lease.gatewayInstanceId,
1687
- wrapperRoot: lease.wrapperRoot,
1688
- deps
1652
+ wrapperRoot: lease.wrapperRoot
1689
1653
  }) : await cleanupOpenClawOwnedAcpxPendingLease({
1690
1654
  leaseId: lease.leaseId,
1691
1655
  gatewayInstanceId: lease.gatewayInstanceId,
1692
1656
  wrapperRoot: lease.wrapperRoot,
1693
- wrapperPath: lease.wrapperPath,
1694
- deps
1657
+ wrapperPath: lease.wrapperPath
1695
1658
  });
1696
1659
  await leaseStore?.markState(lease.leaseId, result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" || lease.rootPid <= 0 && (result.skippedReason === "ambiguous-root" || result.skippedReason === "unverified-root") ? "open" : result.terminatedPids.length > 0 || result.skippedReason === "missing-root" ? "closed" : "lost");
1697
1660
  return;
@@ -1702,8 +1665,7 @@ async function prepareAcpxProcessCleanup(params) {
1702
1665
  rootCommand,
1703
1666
  ...leaseId ? { expectedLeaseId: leaseId } : {},
1704
1667
  ...expectedGatewayInstanceId ? { expectedGatewayInstanceId } : {},
1705
- wrapperRoot,
1706
- deps
1668
+ wrapperRoot
1707
1669
  });
1708
1670
  };
1709
1671
  }
@@ -1791,11 +1753,11 @@ function readAgentFromSessionKey(sessionKey) {
1791
1753
  const normalized = sessionKey?.trim();
1792
1754
  if (!normalized) return;
1793
1755
  const match = /^agent:(?<agent>[^:]+):/i.exec(normalized);
1794
- return normalizeAgentName(match?.groups?.agent);
1756
+ return normalizeOptionalLowercaseString(match?.groups?.agent);
1795
1757
  }
1796
1758
  function readAgentFromHandle(handle) {
1797
1759
  const decoded = decodeAcpxRuntimeHandleState(handle.runtimeSessionName);
1798
- return normalizeAgentName(decoded?.agent) ?? readAgentFromSessionKey(handle.sessionKey);
1760
+ return normalizeOptionalLowercaseString(decoded?.agent) ?? readAgentFromSessionKey(handle.sessionKey);
1799
1761
  }
1800
1762
  function failUnsupportedCodexAcpModel(rawModel) {
1801
1763
  throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Codex ACP model "${rawModel}" is not supported. Use openai/<model> or <model>/<reasoning-effort>.`);
@@ -1914,8 +1876,6 @@ var AcpxRuntime$1 = class {
1914
1876
  this.sessionScope = new AsyncLocalStorage();
1915
1877
  this.launchLeaseScope = new AsyncLocalStorage();
1916
1878
  this.legacyBareSessionKeys = new Set(options.openclawLegacyBareSessionKeys);
1917
- const { openclawProcessCleanup, ...delegateTestOptions } = testOptions ?? {};
1918
- this.processCleanupDeps = openclawProcessCleanup;
1919
1879
  this.wrapperRoot = options.openclawWrapperRoot;
1920
1880
  this.gatewayInstanceId = options.openclawGatewayInstanceId;
1921
1881
  this.processLeaseStore = options.openclawProcessLeaseStore;
@@ -1933,7 +1893,7 @@ var AcpxRuntime$1 = class {
1933
1893
  this.scopedAgentRegistry = {
1934
1894
  resolve: (agentName) => {
1935
1895
  const launch = this.launchCommandScope.getStore();
1936
- return launch && launch.agent === normalizeAgentName(agentName) && launch.command ? launch.command : this.agentRegistry.resolve(agentName);
1896
+ return launch && launch.agent === normalizeOptionalLowercaseString(agentName) && launch.command ? launch.command : this.agentRegistry.resolve(agentName);
1937
1897
  },
1938
1898
  list: () => this.agentRegistry.list()
1939
1899
  };
@@ -1977,11 +1937,11 @@ var AcpxRuntime$1 = class {
1977
1937
  onSpawnFailed: options.processLifecycle?.onSpawnFailed,
1978
1938
  onExit: options.processLifecycle?.onExit
1979
1939
  }
1980
- }, delegateTestOptions);
1940
+ }, testOptions);
1981
1941
  this.delegate = createDelegate();
1982
1942
  this.generationRegistry = new AcpxGenerationRegistry(this.sessionStore, this.delegate, createDelegate);
1983
1943
  this.probe = new AcpxRuntimeProbe({
1984
- getAgent: () => normalizeAgentName(options.getProbeAgent?.() ?? options.probeAgent) ?? "codex",
1944
+ getAgent: () => normalizeOptionalLowercaseString(options.getProbeAgent?.() ?? options.probeAgent) ?? "codex",
1985
1945
  createRuntime: createDelegate,
1986
1946
  assertRunning: () => this.generationRegistry.assertRunning(),
1987
1947
  runWithLease: (agent, run) => this.runWithLaunchLease({
@@ -2076,7 +2036,7 @@ var AcpxRuntime$1 = class {
2076
2036
  command: params.command,
2077
2037
  wrapperRoot: this.wrapperRoot
2078
2038
  })) return await this.launchCommandScope.run({
2079
- agent: normalizeAgentName(params.agent) ?? params.agent,
2039
+ agent: normalizeOptionalLowercaseString(params.agent) ?? params.agent,
2080
2040
  command: params.reusableCommand ?? params.command
2081
2041
  }, params.run);
2082
2042
  const reusableIdentity = readAcpxProcessLeaseIdentity(params.reusableCommand);
@@ -2095,15 +2055,14 @@ var AcpxRuntime$1 = class {
2095
2055
  leasedCommand
2096
2056
  };
2097
2057
  const result = await this.launchLeaseScope.run(launch, () => this.launchCommandScope.run({
2098
- agent: normalizeAgentName(params.agent) ?? params.agent,
2058
+ agent: normalizeOptionalLowercaseString(params.agent) ?? params.agent,
2099
2059
  command: launch.resolvedCommand
2100
2060
  }, params.run));
2101
2061
  if (params.finalizeCompletedProbe) await cleanupOpenClawOwnedAcpxPendingLease({
2102
2062
  leaseId,
2103
2063
  gatewayInstanceId: launch.gatewayInstanceId,
2104
2064
  wrapperRoot: launch.wrapperRoot,
2105
- wrapperPath: extractGeneratedWrapperPath(leasedCommand),
2106
- deps: this.processCleanupDeps
2065
+ wrapperPath: extractGeneratedWrapperPath(leasedCommand)
2107
2066
  });
2108
2067
  return result;
2109
2068
  }
@@ -2209,7 +2168,7 @@ var AcpxRuntime$1 = class {
2209
2168
  ...logicalInput,
2210
2169
  sessionKey: resolveAcpxSessionResource(logicalInput)
2211
2170
  };
2212
- const isCodexAcp = normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
2171
+ const isCodexAcp = normalizeOptionalLowercaseString(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
2213
2172
  const dropInheritedCodexMax = isCodexAcp && input.thinking === "max" && input.thinkingExplicit === false;
2214
2173
  const effectiveInput = dropInheritedCodexMax ? { ...input } : input;
2215
2174
  if (dropInheritedCodexMax) delete effectiveInput.thinking;
@@ -2452,8 +2411,7 @@ var AcpxRuntime$1 = class {
2452
2411
  sessionKey: resolveAcpxSessionResource(input.handle),
2453
2412
  gatewayInstanceId: this.gatewayInstanceId,
2454
2413
  wrapperRoot: this.wrapperRoot,
2455
- leaseStore: this.processLeaseStore,
2456
- deps: this.processCleanupDeps
2414
+ leaseStore: this.processLeaseStore
2457
2415
  }).catch((error) => async () => {
2458
2416
  throw error;
2459
2417
  });
@@ -2476,10 +2434,6 @@ var AcpxRuntime$1 = class {
2476
2434
  };
2477
2435
  //#endregion
2478
2436
  //#region extensions/acpx/src/service.ts
2479
- /**
2480
- * ACPX plugin service lifecycle. It resolves config, prepares isolated adapter
2481
- * wrappers, registers the ACP backend, and manages startup/cleanup probes.
2482
- */
2483
2437
  const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
2484
2438
  const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
2485
2439
  const MAX_ACPX_TOKIO_WORKER_THREADS = 8;
@@ -2531,28 +2485,11 @@ async function measureAcpxStartup(ctx, name, run) {
2531
2485
  function shouldProbeRuntimeAtStartup(env = process.env) {
2532
2486
  return env[ENABLE_STARTUP_PROBE_ENV] !== "0" && env[SKIP_RUNTIME_PROBE_ENV] !== "1";
2533
2487
  }
2534
- async function withStartupProbeTimeout(params) {
2535
- let timeout;
2536
- const timeoutMs = resolveAcpxTimerTimeoutMs(params.timeoutSeconds) ?? 1;
2537
- try {
2538
- return await Promise.race([params.promise, new Promise((_, reject) => {
2539
- timeout = setTimeout(() => {
2540
- reject(/* @__PURE__ */ new Error(`embedded acpx runtime backend startup probe timed out after ${params.timeoutSeconds}s`));
2541
- }, timeoutMs);
2542
- timeout.unref?.();
2543
- })]);
2544
- } finally {
2545
- if (timeout) clearTimeout(timeout);
2546
- }
2547
- }
2548
- function openGatewayInstanceStateStore(openKeyedStore) {
2549
- return openKeyedStore({
2488
+ async function resolveGatewayInstanceId(openKeyedStore) {
2489
+ const store = openKeyedStore({
2550
2490
  namespace: ACPX_GATEWAY_INSTANCE_NAMESPACE,
2551
2491
  maxEntries: 1
2552
2492
  });
2553
- }
2554
- async function resolveGatewayInstanceId(openKeyedStore) {
2555
- const store = openGatewayInstanceStateStore(openKeyedStore);
2556
2493
  const existing = normalizeAcpxGatewayInstanceRecord(await store.lookup(ACPX_GATEWAY_INSTANCE_KEY));
2557
2494
  if (existing) return existing.instanceId;
2558
2495
  const next = randomUUID();
@@ -2563,58 +2500,41 @@ async function resolveGatewayInstanceId(openKeyedStore) {
2563
2500
  return next;
2564
2501
  }
2565
2502
  async function reapOpenAcpxProcessLeases(params) {
2566
- const assertCurrent = () => {
2567
- params.assertCurrent?.();
2568
- params.deps?.assertCurrent?.();
2569
- };
2570
- const deps = {
2571
- ...params.deps,
2572
- assertCurrent
2573
- };
2503
+ const { assertCurrent } = params;
2574
2504
  const leases = await params.leaseStore.listOpen(params.gatewayInstanceId);
2575
2505
  const inspectedPids = [];
2576
2506
  const terminatedPids = [];
2577
2507
  const legacyWrapperRoots = /* @__PURE__ */ new Set();
2578
2508
  for (const lease of leases) {
2579
- if (lease.rootPid <= 0) {
2580
- legacyWrapperRoots.add(lease.wrapperRoot);
2581
- assertCurrent();
2582
- await params.leaseStore.markState(lease.leaseId, "closing");
2583
- assertCurrent();
2584
- const result = await cleanupOpenClawOwnedAcpxPendingLease({
2585
- leaseId: lease.leaseId,
2586
- gatewayInstanceId: lease.gatewayInstanceId,
2587
- wrapperRoot: lease.wrapperRoot,
2588
- wrapperPath: lease.wrapperPath,
2589
- deps
2590
- });
2591
- inspectedPids.push(...result.inspectedPids);
2592
- terminatedPids.push(...result.terminatedPids);
2593
- const retryableEvidenceFailure = result.skippedReason === "ambiguous-root" || result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" || result.skippedReason === "unverified-root" || lease.sessionKey === "openclaw:acpx:probe" && result.skippedReason === "missing-root";
2594
- assertCurrent();
2595
- await params.leaseStore.markState(lease.leaseId, retryableEvidenceFailure ? "open" : result.terminatedPids.length > 0 ? "closed" : "lost");
2596
- continue;
2597
- }
2598
- assertCurrent();
2509
+ const pending = lease.rootPid <= 0;
2510
+ if (pending) legacyWrapperRoots.add(lease.wrapperRoot);
2511
+ assertCurrent?.();
2599
2512
  await params.leaseStore.markState(lease.leaseId, "closing");
2600
- assertCurrent();
2601
- const result = await cleanupOpenClawOwnedAcpxProcessTree({
2513
+ assertCurrent?.();
2514
+ const result = pending ? await cleanupOpenClawOwnedAcpxPendingLease({
2515
+ leaseId: lease.leaseId,
2516
+ gatewayInstanceId: lease.gatewayInstanceId,
2517
+ wrapperRoot: lease.wrapperRoot,
2518
+ wrapperPath: lease.wrapperPath,
2519
+ assertCurrent
2520
+ }) : await cleanupOpenClawOwnedAcpxProcessTree({
2602
2521
  rootPid: lease.rootPid,
2603
2522
  expectedLeaseId: lease.leaseId,
2604
2523
  expectedGatewayInstanceId: lease.gatewayInstanceId,
2605
2524
  wrapperRoot: lease.wrapperRoot,
2606
- deps
2525
+ assertCurrent
2607
2526
  });
2608
2527
  inspectedPids.push(...result.inspectedPids);
2609
2528
  terminatedPids.push(...result.terminatedPids);
2610
- assertCurrent();
2611
- await params.leaseStore.markState(lease.leaseId, result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" ? "open" : result.terminatedPids.length > 0 ? "closed" : "lost");
2529
+ const retryableEvidenceFailure = result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" || pending && (result.skippedReason === "ambiguous-root" || result.skippedReason === "unverified-root" || lease.sessionKey === "openclaw:acpx:probe" && result.skippedReason === "missing-root");
2530
+ assertCurrent?.();
2531
+ await params.leaseStore.markState(lease.leaseId, retryableEvidenceFailure ? "open" : result.terminatedPids.length > 0 ? "closed" : "lost");
2612
2532
  }
2613
2533
  for (const wrapperRoot of legacyWrapperRoots) {
2614
- assertCurrent();
2534
+ assertCurrent?.();
2615
2535
  const legacyResult = await reapStaleOpenClawOwnedAcpxOrphans({
2616
2536
  wrapperRoot,
2617
- deps
2537
+ assertCurrent
2618
2538
  });
2619
2539
  inspectedPids.push(...legacyResult.inspectedPids);
2620
2540
  terminatedPids.push(...legacyResult.terminatedPids);
@@ -2624,7 +2544,6 @@ async function reapOpenAcpxProcessLeases(params) {
2624
2544
  terminatedPids
2625
2545
  };
2626
2546
  }
2627
- /** Create the ACPX plugin service that owns runtime registration and cleanup. */
2628
2547
  function createAcpxRuntimeService(params) {
2629
2548
  let runtime = null;
2630
2549
  let recoverProcesses;
@@ -2659,12 +2578,22 @@ function createAcpxRuntimeService(params) {
2659
2578
  if (!openKeyedStore) throw new Error("ACPX runtime service requires plugin keyed state");
2660
2579
  const basePluginConfig = await measureAcpxStartup(ctx, "config.resolve", () => resolveAcpxPluginConfig({
2661
2580
  rawConfig: params.pluginConfig,
2662
- workspaceDir: ctx.workspaceDir
2581
+ workspaceDir: ctx.workspaceDir,
2582
+ stateDir: ctx.stateDir
2663
2583
  }));
2584
+ const adoption = await measureAcpxStartup(ctx, "state.adopt", () => adoptAcpxStateDirectory({
2585
+ rawConfig: params.pluginConfig,
2586
+ workspaceDir: ctx.workspaceDir,
2587
+ stateDir: basePluginConfig.stateDir,
2588
+ openKeyedStore,
2589
+ assertCurrent: params.assertCurrent
2590
+ }));
2591
+ basePluginConfig.stateDir = adoption.stateDir;
2592
+ for (const change of adoption.changes) ctx.logger.info(change);
2593
+ for (const warning of adoption.warnings) ctx.logger.warn(warning);
2664
2594
  const pluginConfig = await measureAcpxStartup(ctx, "config.prepare-codex-auth", () => prepareAcpxCodexAuthConfig({
2665
2595
  pluginConfig: basePluginConfig,
2666
- stateDir: ctx.stateDir,
2667
- logger: ctx.logger
2596
+ stateDir: ctx.stateDir
2668
2597
  }));
2669
2598
  const wrapperRoot = path.join(ctx.stateDir, "acpx");
2670
2599
  await measureAcpxStartup(ctx, "filesystem.prepare", async () => {
@@ -2676,7 +2605,6 @@ function createAcpxRuntimeService(params) {
2676
2605
  recoverProcesses = (assertCurrent) => reapOpenAcpxProcessLeases({
2677
2606
  gatewayInstanceId,
2678
2607
  leaseStore: processLeaseStore,
2679
- deps: params.processCleanupDeps,
2680
2608
  assertCurrent
2681
2609
  });
2682
2610
  if (params.startupPurpose !== "inspection") await promote(ctx);
@@ -2705,10 +2633,10 @@ function createAcpxRuntimeService(params) {
2705
2633
  lifecycleRevision += 1;
2706
2634
  const currentRevision = lifecycleRevision;
2707
2635
  try {
2708
- const doctorReport = await measureAcpxStartup(ctx, "probe.availability", () => withStartupProbeTimeout({
2709
- promise: startedRuntime.doctor(),
2710
- timeoutSeconds: pluginConfig.timeoutSeconds ?? 120
2711
- }));
2636
+ const timeoutSeconds = pluginConfig.timeoutSeconds ?? 120;
2637
+ const doctorReport = await measureAcpxStartup(ctx, "probe.availability", () => raceWithTimeout(startedRuntime.doctor(), resolveAcpxTimerTimeoutMs(timeoutSeconds) ?? 1, () => {
2638
+ throw new Error(`embedded acpx runtime backend startup probe timed out after ${timeoutSeconds}s`);
2639
+ }, { ref: false }));
2712
2640
  if (currentRevision !== lifecycleRevision) return;
2713
2641
  if (doctorReport.ok) {
2714
2642
  ctx.startupTrace?.detail?.("probe.result", [["healthyCount", 1]]);