@openclaw/acpx 2026.9.7 → 2026.9.9

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,5 +1,5 @@
1
1
  import { getAcpRuntimeBackend, registerAcpRuntimeBackend, unregisterAcpRuntimeBackend } from "openclaw/plugin-sdk/acp-runtime-backend";
2
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
2
+ import { createLazyRuntimeMethodBinder, createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
3
3
  //#region extensions/acpx/src/runtime-proxy.ts
4
4
  /** Start an ACP turn through a lazy runtime resolver without awaiting resolution up front. */
5
5
  function lazyStartRuntimeTurn(resolveRuntime, input) {
@@ -23,50 +23,27 @@ function lazyStartRuntimeTurn(resolveRuntime, input) {
23
23
  }
24
24
  /** Create an ACP runtime facade backed by an async runtime resolver. */
25
25
  function createLazyAcpRuntimeProxy(resolveRuntime) {
26
+ const bind = createLazyRuntimeMethodBinder(resolveRuntime);
26
27
  return {
27
28
  ownerAwareSessions: 1,
28
- async findSession(input) {
29
- return await (await resolveRuntime()).findSession(input);
30
- },
31
- async shutdown() {
32
- await (await resolveRuntime()).shutdown();
33
- },
34
- async ensureSession(input) {
35
- return await (await resolveRuntime()).ensureSession(input);
36
- },
29
+ findSession: bind((runtime) => runtime.findSession.bind(runtime)),
30
+ shutdown: bind((runtime) => runtime.shutdown.bind(runtime)),
31
+ ensureSession: bind((runtime) => runtime.ensureSession.bind(runtime)),
37
32
  startTurn(input) {
38
33
  return lazyStartRuntimeTurn(resolveRuntime, input);
39
34
  },
40
35
  async *runTurn(input) {
41
36
  yield* (await resolveRuntime()).runTurn(input);
42
37
  },
43
- async getCapabilities(input) {
44
- return await (await resolveRuntime()).getCapabilities(input);
45
- },
46
- async getStatus(input) {
47
- return await (await resolveRuntime()).getStatus(input);
48
- },
49
- async setMode(input) {
50
- await (await resolveRuntime()).setMode(input);
51
- },
52
- async setModel(input) {
53
- await (await resolveRuntime()).setModel(input);
54
- },
55
- async setConfigOption(input) {
56
- return await (await resolveRuntime()).setConfigOption(input);
57
- },
58
- async doctor() {
59
- return await (await resolveRuntime()).doctor();
60
- },
61
- async prepareFreshSession(input) {
62
- await (await resolveRuntime()).prepareFreshSession(input);
63
- },
64
- async cancel(input) {
65
- await (await resolveRuntime()).cancel(input);
66
- },
67
- async close(input) {
68
- await (await resolveRuntime()).close(input);
69
- }
38
+ getCapabilities: bind((runtime) => runtime.getCapabilities.bind(runtime)),
39
+ getStatus: bind((runtime) => runtime.getStatus.bind(runtime)),
40
+ setMode: bind((runtime) => runtime.setMode.bind(runtime)),
41
+ setModel: bind((runtime) => runtime.setModel.bind(runtime)),
42
+ setConfigOption: bind((runtime) => runtime.setConfigOption.bind(runtime)),
43
+ doctor: bind((runtime) => runtime.doctor.bind(runtime)),
44
+ prepareFreshSession: bind((runtime) => runtime.prepareFreshSession.bind(runtime)),
45
+ cancel: bind((runtime) => runtime.cancel.bind(runtime)),
46
+ close: bind((runtime) => runtime.close.bind(runtime))
70
47
  };
71
48
  }
72
49
  //#endregion
@@ -76,7 +53,7 @@ function createLazyAcpRuntimeProxy(resolveRuntime) {
76
53
  * immediately, then imports the heavier service only when a session needs it.
77
54
  */
78
55
  const ACPX_BACKEND_ID = "acpx";
79
- const loadServiceModule = createLazyRuntimeModule(() => import("./service-DMae5pPH.mjs"));
56
+ const loadServiceModule = createLazyRuntimeModule(() => import("./service-D5hGWl5U.mjs"));
80
57
  function unregisterOwnedRuntime(runtime) {
81
58
  if (runtime && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime === runtime) unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
82
59
  }
@@ -1,31 +1,29 @@
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 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-n38HFGdp.mjs";
2
+ import { i as toAcpMcpServers, n as resolveAcpxPluginRoot, r as resolveOpenClawRoot, t as resolveAcpxPluginConfig } from "./config-Blz15uwJ.mjs";
3
+ import { a as resolveAgentCommand, c as CODEX_ACP_PACKAGE, i as renderAgentCommand, l as LEGACY_CODEX_ACP_PACKAGE, n as isCodexAcpCommand, o as splitCommandParts, r as isOpenClawBridgeCommand, s as CODEX_ACP_BIN, t as isClaudeAcpCommand, u as OPENCLAW_CODEX_CONFIG_ARG } from "./command-line-B72femaL.mjs";
5
4
  import { AcpRuntimeError } from "../runtime-api.js";
5
+ import { t as resolveAcpxSessionResource } from "./session-resource-B-4yPr-j.mjs";
6
+ import { adoptAcpxStateDirectory } from "./session-owner-migration-DsGrYq7Y.mjs";
6
7
  import { createRequire } from "node:module";
7
8
  import { AcpxRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
8
9
  import { finiteSecondsToTimerSafeMilliseconds, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
9
10
  import fs from "node:fs";
10
11
  import path, { resolve } from "node:path";
11
- import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
12
+ import { asOptionalObjectRecord, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
12
13
  import os, { availableParallelism } from "node:os";
13
14
  import fs$1 from "node:fs/promises";
14
15
  import { randomUUID } from "node:crypto";
15
16
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
16
17
  import { escapeRegExp, sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
17
- import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
18
+ import { tryReadJson } from "@openclaw/fs-safe/json";
18
19
  import { parse, stringify } from "smol-toml";
20
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
19
21
  import { isPidAlive, runExec } from "openclaw/plugin-sdk/process-runtime";
20
22
  import { AsyncLocalStorage } from "node:async_hooks";
21
23
  import { isDeepStrictEqual } from "node:util";
22
24
  import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
23
25
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
24
26
  //#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
27
  function stripTomlComment(line) {
30
28
  let quote = null;
31
29
  let escaping = false;
@@ -97,10 +95,8 @@ function parseTomlDottedKey(value) {
97
95
  return parts.map((part) => parseTomlString(part) ?? part);
98
96
  }
99
97
  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;
98
+ const parts = parseTableHeader(line);
99
+ return parts?.length === 2 && parts[0] === "projects" ? parts[1] : void 0;
104
100
  }
105
101
  function parseTrustedInlineProjectEntries(value) {
106
102
  const trusted = [];
@@ -113,7 +109,6 @@ function parseTrustedInlineProjectEntries(value) {
113
109
  }
114
110
  return trusted;
115
111
  }
116
- /** Extract trusted project paths from Codex TOML config. */
117
112
  function extractTrustedCodexProjectPaths(configToml) {
118
113
  const trusted = /* @__PURE__ */ new Set();
119
114
  let currentProjectPath;
@@ -211,7 +206,6 @@ function extractInheritedCodexRuntimeConfig(configToml) {
211
206
  while (inheritedLines.length > 0 && inheritedLines[inheritedLines.length - 1] === "") inheritedLines.pop();
212
207
  return inheritedLines.join("\n");
213
208
  }
214
- /** Render a session-local Codex config with inherited runtime settings and trust entries. */
215
209
  function renderIsolatedCodexConfig(params) {
216
210
  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
211
  return [
@@ -258,8 +252,8 @@ function resolvePackageBinPath(packageJsonPath, manifest, binName) {
258
252
  async function resolveInstalledAcpPackageBinPath(packageName, binName) {
259
253
  try {
260
254
  const packageJsonPath = requireFromHere$1.resolve(`${packageName}/package.json`);
261
- const { value: manifest } = await readJsonFileWithFallback(packageJsonPath, {});
262
- if (manifest.name !== packageName) return;
255
+ const manifest = await tryReadJson(packageJsonPath);
256
+ if (manifest?.name !== packageName) return;
263
257
  const binPath = resolvePackageBinPath(packageJsonPath, manifest, binName);
264
258
  if (!binPath) return;
265
259
  await fs$1.access(binPath);
@@ -268,12 +262,6 @@ async function resolveInstalledAcpPackageBinPath(packageName, binName) {
268
262
  return;
269
263
  }
270
264
  }
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
265
  const DIAGNOSTIC_REDACTION_RULES = [
278
266
  {
279
267
  source: String.raw`(authorization\s*[:=]\s*bearer\s+)[^\s'"<>]+`,
@@ -595,7 +583,9 @@ const npmCliPath = resolveNpmCliPath();
595
583
  const installedBinPath = ${params.installedBinPath ? JSON.stringify(params.installedBinPath) : "undefined"};
596
584
  let defaultCommand;
597
585
  let defaultArgs;
598
- if (installedBinPath) {
586
+ // Plugin capture/install directories are disposable: a durable wrapper can
587
+ // outlive the path it captured, so re-check the target before trusting it.
588
+ if (installedBinPath && existsSync(installedBinPath)) {
599
589
  defaultCommand = process.execPath;
600
590
  defaultArgs = [installedBinPath];
601
591
  } else if (npmCliPath) {
@@ -858,13 +848,11 @@ function buildWrapperCommand(wrapperPath, args = []) {
858
848
  ];
859
849
  }
860
850
  function isAcpPackageSpec(value, packageName) {
861
- const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
862
- return new RegExp(`^${escapedPackageName}(?:@.+)?$`, "i").test(value.trim());
851
+ return new RegExp(`^${escapeRegExp(packageName)}(?:@.+)?$`, "i").test(value.trim());
863
852
  }
864
853
  function isAcpBinName(value, binName) {
865
854
  const commandName = basename(value);
866
- const escapedBinName = binName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
867
- return new RegExp(`^${escapedBinName}(?:\\.exe|\\.[cm]?js)?$`, "i").test(commandName);
855
+ return new RegExp(`^${escapeRegExp(binName)}(?:\\.exe|\\.[cm]?js)?$`, "i").test(commandName);
868
856
  }
869
857
  function isPackageRunnerCommand(value) {
870
858
  return /^(?:npx|npm|pnpm|bunx)(?:\.cmd|\.exe)?$/i.test(basename(value));
@@ -974,7 +962,6 @@ function buildClaudeAcpWrapperCommand(wrapperPath, configuredCommand) {
974
962
  }
975
963
  /** Prepare ACPX agent commands and isolated auth homes for Codex/Claude adapters. */
976
964
  async function prepareAcpxCodexAuthConfig(params) {
977
- params.logger;
978
965
  const codexBaseDir = path.join(params.stateDir, "acpx");
979
966
  const configuredCodexCommand = params.pluginConfig.agents.codex;
980
967
  const configuredClaudeCommand = params.pluginConfig.agents.claude;
@@ -986,8 +973,8 @@ async function prepareAcpxCodexAuthConfig(params) {
986
973
  }),
987
974
  migratedConfig: codexLaunch?.migratedConfig
988
975
  });
989
- const installedCodexBinPath = await (params.resolveInstalledCodexAcpBinPath ?? resolveInstalledCodexAcpBinPath)();
990
- const installedClaudeBinPath = await (params.resolveInstalledClaudeAcpBinPath ?? resolveInstalledClaudeAcpBinPath)();
976
+ const installedCodexBinPath = await (params.resolveInstalledCodexAcpBinPath ? params.resolveInstalledCodexAcpBinPath() : resolveInstalledAcpPackageBinPath(CODEX_ACP_PACKAGE, CODEX_ACP_BIN));
977
+ const installedClaudeBinPath = await (params.resolveInstalledClaudeAcpBinPath ? params.resolveInstalledClaudeAcpBinPath() : resolveInstalledAcpPackageBinPath(CLAUDE_ACP_PACKAGE, CLAUDE_ACP_BIN));
991
978
  const wrapperPath = await writeAdapterWrapper(codexBaseDir, "codex-acp-wrapper.mjs", buildCodexAcpWrapperScript(installedCodexBinPath));
992
979
  const claudeWrapperPath = await writeAdapterWrapper(codexBaseDir, "claude-agent-acp-wrapper.mjs", buildClaudeAcpWrapperScript(installedClaudeBinPath));
993
980
  return {
@@ -1114,7 +1101,6 @@ function parseProcessList(stdout) {
1114
1101
  }
1115
1102
  return processes;
1116
1103
  }
1117
- /** List host processes in the compact shape needed by ACPX cleanup. */
1118
1104
  async function listPlatformProcesses() {
1119
1105
  if (process.platform === "win32") return [];
1120
1106
  const { stdout } = await runExec("ps", ["-axo", "pid=,ppid=,command="], {
@@ -1148,9 +1134,7 @@ function uniquePids(processes) {
1148
1134
  }
1149
1135
  async function terminatePids(pids, deps) {
1150
1136
  const killProcess = deps?.killProcess ?? ((pid, signal) => process.kill(pid, signal));
1151
- const sleep = deps?.sleep ?? ((ms) => new Promise((resolve) => {
1152
- setTimeout(resolve, ms);
1153
- }));
1137
+ const sleep = deps?.sleep ?? setTimeout$1;
1154
1138
  const terminated = [];
1155
1139
  for (const pid of pids) {
1156
1140
  deps?.assertCurrent?.();
@@ -1369,7 +1353,7 @@ var AcpxGenerationRegistry = class {
1369
1353
  return generation.delegate;
1370
1354
  }
1371
1355
  currentGeneration(resource) {
1372
- if (this.stopping) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "ACP runtime is shut down.");
1356
+ this.assertRunning();
1373
1357
  let generation = this.generations.get(resource);
1374
1358
  if (!generation) {
1375
1359
  const fresh = this.sessionStore.isFresh(resource);
@@ -1512,40 +1496,28 @@ function captureGenerationRecord(generation, record) {
1512
1496
  const acpxGenerationKey = Symbol("openclaw.acpxGeneration");
1513
1497
  const acpxOperationScope = new AsyncLocalStorage();
1514
1498
  function readSessionRecordName(record) {
1515
- if (typeof record !== "object" || record === null) return "";
1516
- const { name } = record;
1517
- return typeof name === "string" ? name.trim() : "";
1499
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.name) ?? "";
1518
1500
  }
1519
1501
  function readRecordAgentCommand(record) {
1520
1502
  return record?.agentArgv ?? record?.agentCommand;
1521
1503
  }
1522
1504
  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;
1505
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.cwd);
1526
1506
  }
1527
1507
  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;
1508
+ return asOptionalObjectRecord(asOptionalObjectRecord(record)?.acpx)?.reset_on_next_ensure === true;
1532
1509
  }
1533
1510
  function readRecordAgentPid(record) {
1534
- if (typeof record !== "object" || record === null) return;
1535
- const { pid, processId } = record;
1511
+ const { pid, processId } = asOptionalObjectRecord(record) ?? {};
1536
1512
  const rawPid = pid ?? processId;
1537
1513
  const numericPid = typeof rawPid === "number" ? rawPid : typeof rawPid === "string" ? parseStrictPositiveInteger(rawPid) : void 0;
1538
1514
  return numericPid && Number.isInteger(numericPid) && numericPid > 0 ? numericPid : void 0;
1539
1515
  }
1540
1516
  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;
1517
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.openclawLeaseId);
1544
1518
  }
1545
1519
  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;
1520
+ return normalizeOptionalString(asOptionalObjectRecord(record)?.openclawGatewayInstanceId);
1549
1521
  }
1550
1522
  function extractGeneratedWrapperPath(command) {
1551
1523
  return splitCommandParts(command ?? "").find((part) => (part.split(/[\\/]/).pop() ?? "") === "codex-acp-wrapper.mjs" || (part.split(/[\\/]/).pop() ?? "") === "claude-agent-acp-wrapper.mjs") ?? "";
@@ -1569,27 +1541,24 @@ function createResetAwareSessionStore(baseStore, params) {
1569
1541
  const resource = scope?.generation.resource ?? sessionId.trim();
1570
1542
  const pending = pendingWrites.get(sessionId.trim());
1571
1543
  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();
1544
+ if (scope?.generation.retired) return;
1545
+ const normalized = sessionId.trim();
1546
+ if (normalized && freshSessionKeys.has(normalized)) return;
1547
+ const record = await baseStore.load(sessionId);
1548
+ if (scope?.generation.retired || freshSessionKeys.has(scope?.generation.resource ?? normalized)) return;
1549
+ if (scope && record) captureGenerationRecord(scope.generation, record);
1550
+ if (!record || !params?.leaseStore || !params.gatewayInstanceId) return record;
1551
+ const sessionName = readSessionRecordName(record) || normalized;
1552
+ const lease = selectCurrentSessionLease({
1553
+ leases: await params.leaseStore.listOpen(params.gatewayInstanceId),
1554
+ sessionKeys: [sessionName, normalized],
1555
+ rootPid: readRecordAgentPid(record)
1556
+ });
1557
+ if (!lease) return record;
1558
+ if (scope?.generation.retired) return;
1559
+ const leasedRecord = withOpenClawLeaseSessionMetadata(record, lease);
1560
+ if (scope) captureGenerationRecord(scope.generation, leasedRecord);
1561
+ return leasedRecord;
1593
1562
  },
1594
1563
  async save(record) {
1595
1564
  const scope = acpxOperationScope.getStore();
@@ -1791,11 +1760,11 @@ function readAgentFromSessionKey(sessionKey) {
1791
1760
  const normalized = sessionKey?.trim();
1792
1761
  if (!normalized) return;
1793
1762
  const match = /^agent:(?<agent>[^:]+):/i.exec(normalized);
1794
- return normalizeAgentName(match?.groups?.agent);
1763
+ return normalizeOptionalLowercaseString(match?.groups?.agent);
1795
1764
  }
1796
1765
  function readAgentFromHandle(handle) {
1797
1766
  const decoded = decodeAcpxRuntimeHandleState(handle.runtimeSessionName);
1798
- return normalizeAgentName(decoded?.agent) ?? readAgentFromSessionKey(handle.sessionKey);
1767
+ return normalizeOptionalLowercaseString(decoded?.agent) ?? readAgentFromSessionKey(handle.sessionKey);
1799
1768
  }
1800
1769
  function failUnsupportedCodexAcpModel(rawModel) {
1801
1770
  throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", `Codex ACP model "${rawModel}" is not supported. Use openai/<model> or <model>/<reasoning-effort>.`);
@@ -1933,7 +1902,7 @@ var AcpxRuntime$1 = class {
1933
1902
  this.scopedAgentRegistry = {
1934
1903
  resolve: (agentName) => {
1935
1904
  const launch = this.launchCommandScope.getStore();
1936
- return launch && launch.agent === normalizeAgentName(agentName) && launch.command ? launch.command : this.agentRegistry.resolve(agentName);
1905
+ return launch && launch.agent === normalizeOptionalLowercaseString(agentName) && launch.command ? launch.command : this.agentRegistry.resolve(agentName);
1937
1906
  },
1938
1907
  list: () => this.agentRegistry.list()
1939
1908
  };
@@ -1981,7 +1950,7 @@ var AcpxRuntime$1 = class {
1981
1950
  this.delegate = createDelegate();
1982
1951
  this.generationRegistry = new AcpxGenerationRegistry(this.sessionStore, this.delegate, createDelegate);
1983
1952
  this.probe = new AcpxRuntimeProbe({
1984
- getAgent: () => normalizeAgentName(options.getProbeAgent?.() ?? options.probeAgent) ?? "codex",
1953
+ getAgent: () => normalizeOptionalLowercaseString(options.getProbeAgent?.() ?? options.probeAgent) ?? "codex",
1985
1954
  createRuntime: createDelegate,
1986
1955
  assertRunning: () => this.generationRegistry.assertRunning(),
1987
1956
  runWithLease: (agent, run) => this.runWithLaunchLease({
@@ -2076,7 +2045,7 @@ var AcpxRuntime$1 = class {
2076
2045
  command: params.command,
2077
2046
  wrapperRoot: this.wrapperRoot
2078
2047
  })) return await this.launchCommandScope.run({
2079
- agent: normalizeAgentName(params.agent) ?? params.agent,
2048
+ agent: normalizeOptionalLowercaseString(params.agent) ?? params.agent,
2080
2049
  command: params.reusableCommand ?? params.command
2081
2050
  }, params.run);
2082
2051
  const reusableIdentity = readAcpxProcessLeaseIdentity(params.reusableCommand);
@@ -2095,7 +2064,7 @@ var AcpxRuntime$1 = class {
2095
2064
  leasedCommand
2096
2065
  };
2097
2066
  const result = await this.launchLeaseScope.run(launch, () => this.launchCommandScope.run({
2098
- agent: normalizeAgentName(params.agent) ?? params.agent,
2067
+ agent: normalizeOptionalLowercaseString(params.agent) ?? params.agent,
2099
2068
  command: launch.resolvedCommand
2100
2069
  }, params.run));
2101
2070
  if (params.finalizeCompletedProbe) await cleanupOpenClawOwnedAcpxPendingLease({
@@ -2209,7 +2178,7 @@ var AcpxRuntime$1 = class {
2209
2178
  ...logicalInput,
2210
2179
  sessionKey: resolveAcpxSessionResource(logicalInput)
2211
2180
  };
2212
- const isCodexAcp = normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
2181
+ const isCodexAcp = normalizeOptionalLowercaseString(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
2213
2182
  const dropInheritedCodexMax = isCodexAcp && input.thinking === "max" && input.thinkingExplicit === false;
2214
2183
  const effectiveInput = dropInheritedCodexMax ? { ...input } : input;
2215
2184
  if (dropInheritedCodexMax) delete effectiveInput.thinking;
@@ -2476,10 +2445,6 @@ var AcpxRuntime$1 = class {
2476
2445
  };
2477
2446
  //#endregion
2478
2447
  //#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
2448
  const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
2484
2449
  const SKIP_RUNTIME_PROBE_ENV = "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE";
2485
2450
  const MAX_ACPX_TOKIO_WORKER_THREADS = 8;
@@ -2545,14 +2510,11 @@ async function withStartupProbeTimeout(params) {
2545
2510
  if (timeout) clearTimeout(timeout);
2546
2511
  }
2547
2512
  }
2548
- function openGatewayInstanceStateStore(openKeyedStore) {
2549
- return openKeyedStore({
2513
+ async function resolveGatewayInstanceId(openKeyedStore) {
2514
+ const store = openKeyedStore({
2550
2515
  namespace: ACPX_GATEWAY_INSTANCE_NAMESPACE,
2551
2516
  maxEntries: 1
2552
2517
  });
2553
- }
2554
- async function resolveGatewayInstanceId(openKeyedStore) {
2555
- const store = openGatewayInstanceStateStore(openKeyedStore);
2556
2518
  const existing = normalizeAcpxGatewayInstanceRecord(await store.lookup(ACPX_GATEWAY_INSTANCE_KEY));
2557
2519
  if (existing) return existing.instanceId;
2558
2520
  const next = randomUUID();
@@ -2576,29 +2538,18 @@ async function reapOpenAcpxProcessLeases(params) {
2576
2538
  const terminatedPids = [];
2577
2539
  const legacyWrapperRoots = /* @__PURE__ */ new Set();
2578
2540
  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
- }
2541
+ const pending = lease.rootPid <= 0;
2542
+ if (pending) legacyWrapperRoots.add(lease.wrapperRoot);
2598
2543
  assertCurrent();
2599
2544
  await params.leaseStore.markState(lease.leaseId, "closing");
2600
2545
  assertCurrent();
2601
- const result = await cleanupOpenClawOwnedAcpxProcessTree({
2546
+ const result = pending ? await cleanupOpenClawOwnedAcpxPendingLease({
2547
+ leaseId: lease.leaseId,
2548
+ gatewayInstanceId: lease.gatewayInstanceId,
2549
+ wrapperRoot: lease.wrapperRoot,
2550
+ wrapperPath: lease.wrapperPath,
2551
+ deps
2552
+ }) : await cleanupOpenClawOwnedAcpxProcessTree({
2602
2553
  rootPid: lease.rootPid,
2603
2554
  expectedLeaseId: lease.leaseId,
2604
2555
  expectedGatewayInstanceId: lease.gatewayInstanceId,
@@ -2607,8 +2558,9 @@ async function reapOpenAcpxProcessLeases(params) {
2607
2558
  });
2608
2559
  inspectedPids.push(...result.inspectedPids);
2609
2560
  terminatedPids.push(...result.terminatedPids);
2561
+ 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");
2610
2562
  assertCurrent();
2611
- await params.leaseStore.markState(lease.leaseId, result.skippedReason === "process-list-unavailable" || result.skippedReason === "unsupported-platform" ? "open" : result.terminatedPids.length > 0 ? "closed" : "lost");
2563
+ await params.leaseStore.markState(lease.leaseId, retryableEvidenceFailure ? "open" : result.terminatedPids.length > 0 ? "closed" : "lost");
2612
2564
  }
2613
2565
  for (const wrapperRoot of legacyWrapperRoots) {
2614
2566
  assertCurrent();
@@ -2624,7 +2576,6 @@ async function reapOpenAcpxProcessLeases(params) {
2624
2576
  terminatedPids
2625
2577
  };
2626
2578
  }
2627
- /** Create the ACPX plugin service that owns runtime registration and cleanup. */
2628
2579
  function createAcpxRuntimeService(params) {
2629
2580
  let runtime = null;
2630
2581
  let recoverProcesses;
@@ -2659,12 +2610,22 @@ function createAcpxRuntimeService(params) {
2659
2610
  if (!openKeyedStore) throw new Error("ACPX runtime service requires plugin keyed state");
2660
2611
  const basePluginConfig = await measureAcpxStartup(ctx, "config.resolve", () => resolveAcpxPluginConfig({
2661
2612
  rawConfig: params.pluginConfig,
2662
- workspaceDir: ctx.workspaceDir
2613
+ workspaceDir: ctx.workspaceDir,
2614
+ stateDir: ctx.stateDir
2615
+ }));
2616
+ const adoption = await measureAcpxStartup(ctx, "state.adopt", () => adoptAcpxStateDirectory({
2617
+ rawConfig: params.pluginConfig,
2618
+ workspaceDir: ctx.workspaceDir,
2619
+ stateDir: basePluginConfig.stateDir,
2620
+ openKeyedStore,
2621
+ assertCurrent: params.assertCurrent
2663
2622
  }));
2623
+ basePluginConfig.stateDir = adoption.stateDir;
2624
+ for (const change of adoption.changes) ctx.logger.info(change);
2625
+ for (const warning of adoption.warnings) ctx.logger.warn(warning);
2664
2626
  const pluginConfig = await measureAcpxStartup(ctx, "config.prepare-codex-auth", () => prepareAcpxCodexAuthConfig({
2665
2627
  pluginConfig: basePluginConfig,
2666
- stateDir: ctx.stateDir,
2667
- logger: ctx.logger
2628
+ stateDir: ctx.stateDir
2668
2629
  }));
2669
2630
  const wrapperRoot = path.join(ctx.stateDir, "acpx");
2670
2631
  await measureAcpxStartup(ctx, "filesystem.prepare", async () => {