@openclaw/acpx 2026.8.1 → 2026.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,185 @@
1
+ import { t as AcpxPluginConfigSchema } from "./config-schema-DN_uAi4R.js";
2
+ import { createRequire } from "node:module";
3
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { formatPluginConfigIssue } from "openclaw/plugin-sdk/extension-shared";
8
+ //#region extensions/acpx/src/config.ts
9
+ /**
10
+ * Resolves ACPX plugin config from raw user configuration. It locates the
11
+ * plugin root, injects optional MCP bridge servers, and applies runtime defaults.
12
+ */
13
+ const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
14
+ const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
15
+ const requireFromHere = createRequire(import.meta.url);
16
+ function isAcpxPluginRoot(dir) {
17
+ return fs.existsSync(path.join(dir, "openclaw.plugin.json")) && fs.existsSync(path.join(dir, "package.json"));
18
+ }
19
+ function resolveNearestAcpxPluginRoot(moduleUrl) {
20
+ let cursor = path.dirname(fileURLToPath(moduleUrl));
21
+ for (let i = 0; i < 3; i += 1) {
22
+ if (isAcpxPluginRoot(cursor)) return cursor;
23
+ const parent = path.dirname(cursor);
24
+ if (parent === cursor) break;
25
+ cursor = parent;
26
+ }
27
+ return path.resolve(path.dirname(fileURLToPath(moduleUrl)), "..");
28
+ }
29
+ function resolveWorkspaceAcpxPluginRoot(currentRoot) {
30
+ if (path.basename(currentRoot) !== "acpx" || path.basename(path.dirname(currentRoot)) !== "extensions" || path.basename(path.dirname(path.dirname(currentRoot))) !== "dist") return null;
31
+ const workspaceRoot = path.resolve(currentRoot, "..", "..", "..", "extensions", "acpx");
32
+ return isAcpxPluginRoot(workspaceRoot) ? workspaceRoot : null;
33
+ }
34
+ function resolveRepoAcpxPluginRoot(currentRoot) {
35
+ const workspaceRoot = path.join(currentRoot, "extensions", "acpx");
36
+ return isAcpxPluginRoot(workspaceRoot) ? workspaceRoot : null;
37
+ }
38
+ function resolveAcpxPluginRootFromOpenClawLayout(moduleUrl) {
39
+ let cursor = path.dirname(fileURLToPath(moduleUrl));
40
+ for (let i = 0; i < 5; i += 1) {
41
+ const candidates = [
42
+ path.join(cursor, "extensions", "acpx"),
43
+ path.join(cursor, "dist", "extensions", "acpx"),
44
+ path.join(cursor, "dist-runtime", "extensions", "acpx")
45
+ ];
46
+ for (const candidate of candidates) if (isAcpxPluginRoot(candidate)) return candidate;
47
+ const parent = path.dirname(cursor);
48
+ if (parent === cursor) break;
49
+ cursor = parent;
50
+ }
51
+ return null;
52
+ }
53
+ /** Resolve the ACPX plugin root across source, dist, and dist-runtime layouts. */
54
+ function resolveAcpxPluginRoot(moduleUrl = import.meta.url) {
55
+ const resolvedRoot = resolveNearestAcpxPluginRoot(moduleUrl);
56
+ return resolveWorkspaceAcpxPluginRoot(resolvedRoot) ?? resolveRepoAcpxPluginRoot(resolvedRoot) ?? resolveAcpxPluginRootFromOpenClawLayout(moduleUrl) ?? resolvedRoot;
57
+ }
58
+ const DEFAULT_PERMISSION_MODE = "approve-reads";
59
+ const DEFAULT_NON_INTERACTIVE_POLICY = "fail";
60
+ function parseAcpxPluginConfig(value) {
61
+ if (value === void 0) return {
62
+ ok: true,
63
+ value: void 0
64
+ };
65
+ const parsed = AcpxPluginConfigSchema.safeParse(value);
66
+ if (!parsed.success) return {
67
+ ok: false,
68
+ message: formatPluginConfigIssue(parsed.error.issues[0])
69
+ };
70
+ return {
71
+ ok: true,
72
+ value: parsed.data
73
+ };
74
+ }
75
+ function resolveOpenClawRoot(currentRoot) {
76
+ if (path.basename(currentRoot) === "acpx" && path.basename(path.dirname(currentRoot)) === "extensions") {
77
+ const parent = path.dirname(path.dirname(currentRoot));
78
+ if (path.basename(parent) === "dist") return path.dirname(parent);
79
+ return parent;
80
+ }
81
+ return path.resolve(currentRoot, "..");
82
+ }
83
+ function resolveTsxImportSpecifier() {
84
+ try {
85
+ return requireFromHere.resolve("tsx");
86
+ } catch {
87
+ return "tsx";
88
+ }
89
+ }
90
+ function shellQuoteCommandArg(arg) {
91
+ if (!/[\s'"\\$|&;<>{}()*?[\]~`]/.test(arg)) return arg;
92
+ return `'${arg.replace(/'/g, "'\"'\"'")}'`;
93
+ }
94
+ function resolvePluginToolsMcpServerConfig(moduleUrl = import.meta.url) {
95
+ const openClawRoot = resolveOpenClawRoot(resolveAcpxPluginRoot(moduleUrl));
96
+ const distEntry = path.join(openClawRoot, "dist", "mcp", "plugin-tools-serve.js");
97
+ if (fs.existsSync(distEntry)) return {
98
+ command: process.execPath,
99
+ args: [distEntry]
100
+ };
101
+ const sourceEntry = path.join(openClawRoot, "src", "mcp", "plugin-tools-serve.ts");
102
+ return {
103
+ command: process.execPath,
104
+ args: [
105
+ "--import",
106
+ resolveTsxImportSpecifier(),
107
+ sourceEntry
108
+ ]
109
+ };
110
+ }
111
+ function resolveOpenClawToolsMcpServerConfig(moduleUrl = import.meta.url) {
112
+ const openClawRoot = resolveOpenClawRoot(resolveAcpxPluginRoot(moduleUrl));
113
+ const distEntry = path.join(openClawRoot, "dist", "mcp", "openclaw-tools-serve.js");
114
+ if (fs.existsSync(distEntry)) return {
115
+ command: process.execPath,
116
+ args: [distEntry]
117
+ };
118
+ const sourceEntry = path.join(openClawRoot, "src", "mcp", "openclaw-tools-serve.ts");
119
+ return {
120
+ command: process.execPath,
121
+ args: [
122
+ "--import",
123
+ resolveTsxImportSpecifier(),
124
+ sourceEntry
125
+ ]
126
+ };
127
+ }
128
+ function resolveConfiguredMcpServers(params) {
129
+ const resolved = { ...params.mcpServers };
130
+ if (params.pluginToolsMcpBridge && resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME]) throw new Error(`mcpServers.${ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME} is reserved when pluginToolsMcpBridge=true`);
131
+ if (params.openClawToolsMcpBridge && resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME]) throw new Error(`mcpServers.${ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME} is reserved when openClawToolsMcpBridge=true`);
132
+ if (params.pluginToolsMcpBridge) resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME] = resolvePluginToolsMcpServerConfig(params.moduleUrl);
133
+ if (params.openClawToolsMcpBridge) resolved[ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME] = resolveOpenClawToolsMcpServerConfig(params.moduleUrl);
134
+ return resolved;
135
+ }
136
+ /** Convert OpenClaw MCP server config into ACPX runtime MCP server entries. */
137
+ function toAcpMcpServers(mcpServers) {
138
+ return Object.entries(mcpServers).map(([name, server]) => ({
139
+ name,
140
+ command: server.command,
141
+ args: [...server.args ?? []],
142
+ env: Object.entries(server.env ?? {}).map(([envName, value]) => ({
143
+ name: envName,
144
+ value
145
+ }))
146
+ }));
147
+ }
148
+ /** Validate and normalize raw ACPX plugin config for runtime startup. */
149
+ function resolveAcpxPluginConfig(params) {
150
+ const parsed = parseAcpxPluginConfig(params.rawConfig);
151
+ if (!parsed.ok) throw new Error(parsed.message);
152
+ const normalized = parsed.value ?? {};
153
+ const workspaceDir = params.workspaceDir?.trim() || process.cwd();
154
+ const fallbackCwd = workspaceDir;
155
+ const cwd = path.resolve(normalized.cwd?.trim() || fallbackCwd);
156
+ const stateDir = path.resolve(normalized.stateDir?.trim() || path.join(workspaceDir, "state"));
157
+ const pluginToolsMcpBridge = normalized.pluginToolsMcpBridge === true;
158
+ const openClawToolsMcpBridge = normalized.openClawToolsMcpBridge === true;
159
+ const mcpServers = resolveConfiguredMcpServers({
160
+ mcpServers: normalized.mcpServers,
161
+ pluginToolsMcpBridge,
162
+ openClawToolsMcpBridge,
163
+ moduleUrl: params.moduleUrl
164
+ });
165
+ const agents = Object.fromEntries(Object.entries(normalized.agents ?? {}).map(([name, entry]) => {
166
+ const cmd = entry.command.trim();
167
+ const cmdArgs = entry.args ?? [];
168
+ const fullCommand = cmdArgs.length > 0 ? `${cmd} ${cmdArgs.map(shellQuoteCommandArg).join(" ")}` : cmd;
169
+ return [normalizeLowercaseStringOrEmpty(name), fullCommand];
170
+ }));
171
+ return {
172
+ cwd,
173
+ stateDir,
174
+ probeAgent: normalized.probeAgent,
175
+ permissionMode: normalized.permissionMode ?? DEFAULT_PERMISSION_MODE,
176
+ nonInteractivePermissions: normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
177
+ pluginToolsMcpBridge,
178
+ openClawToolsMcpBridge,
179
+ timeoutSeconds: normalized.timeoutSeconds ?? 120,
180
+ mcpServers,
181
+ agents
182
+ };
183
+ }
184
+ //#endregion
185
+ export { resolveAcpxPluginRoot as n, toAcpMcpServers as r, resolveAcpxPluginConfig as t };
@@ -17,14 +17,16 @@ const legacyConfigRules = RETIRED_ACPX_CONFIG_KEYS.map((key) => ({
17
17
  }));
18
18
  /** Removes retired plugin-owned config without keeping runtime compatibility keys. */
19
19
  function normalizeCompatibilityConfig({ cfg }) {
20
- const pluginConfig = asObjectRecord(asObjectRecord(cfg.plugins?.entries?.acpx)?.config);
20
+ const entry = asObjectRecord(cfg.plugins?.entries?.acpx);
21
+ const pluginConfig = asObjectRecord(entry?.config);
21
22
  const retiredKeys = RETIRED_ACPX_CONFIG_KEYS.filter((key) => Object.hasOwn(pluginConfig ?? {}, key));
22
23
  if (!pluginConfig || retiredKeys.length === 0) return {
23
24
  config: cfg,
24
25
  changes: []
25
26
  };
26
27
  const nextConfig = structuredClone(cfg);
27
- const nextPluginConfig = asObjectRecord(asObjectRecord(nextConfig.plugins?.entries?.acpx)?.config);
28
+ const nextEntry = asObjectRecord(nextConfig.plugins?.entries?.acpx);
29
+ const nextPluginConfig = asObjectRecord(nextEntry?.config);
28
30
  if (!nextPluginConfig) return {
29
31
  config: cfg,
30
32
  changes: []
@@ -129,6 +131,17 @@ const stateMigrations = [{
129
131
  warnings
130
132
  };
131
133
  }
134
+ }, {
135
+ id: "acpx-session-owner-resources",
136
+ label: "ACP session owners",
137
+ doctorOnly: true,
138
+ phase: "after-session-repair",
139
+ async detectLegacyState(input) {
140
+ return (await import("./session-owner-migration-Dt5yf0xJ.js")).acpxSessionOwnerMigration.detectLegacyState(input);
141
+ },
142
+ async migrateLegacyState(input) {
143
+ return (await import("./session-owner-migration-Dt5yf0xJ.js")).acpxSessionOwnerMigration.migrateLegacyState(input);
144
+ }
132
145
  }];
133
146
  //#endregion
134
147
  export { legacyConfigRules, normalizeCompatibilityConfig, stateMigrations };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createAcpxRuntimeService } from "./register.runtime-DFRkXzZ_.js";
1
+ import { t as createAcpxRuntimeService } from "./register.runtime-BHFSFWWK.js";
2
2
  import "./config-schema-DN_uAi4R.js";
3
3
  import { a as PI_SESSIONS_CAPABILITY, c as PI_SESSION_READ_COMMAND, l as PI_TERMINAL_RESUME_COMMAND, o as PI_SESSIONS_LIST_COMMAND, r as piSessionStoreAvailable, s as PI_SESSION_ID_PATTERN } from "./pi-session-paths-EMbd4Hkz.js";
4
4
  import { tryDispatchAcpReplyHook } from "openclaw/plugin-sdk/acp-runtime-backend";
@@ -8,7 +8,7 @@ import { resolveNodeHostExecutable } from "openclaw/plugin-sdk/node-host";
8
8
  import { createSessionCatalogNodeHostBindings } from "openclaw/plugin-sdk/session-catalog";
9
9
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
10
10
  //#region extensions/acpx/src/pi-session-catalog-plugin.ts
11
- const loadPiSessionCatalogModule = createLazyRuntimeModule(() => import("./pi-session-catalog-runtime-g6A6ADEf.js"));
11
+ const loadPiSessionCatalogModule = createLazyRuntimeModule(() => import("./pi-session-catalog-runtime-C0HodUWT.js"));
12
12
  function fullConfigCatalogEnabled(config) {
13
13
  if (!isRecord(config) || !isRecord(config.plugins) || !isRecord(config.plugins.entries)) return true;
14
14
  const entry = config.plugins.entries.acpx;
@@ -22,9 +22,9 @@ function parsePiSessionTimestampMs(value) {
22
22
  const MAX_DISCOVERY_FILES = 1e4;
23
23
  const SUMMARY_SCAN_BATCH_SIZE = 100;
24
24
  const MAX_SUMMARY_CACHE_ENTRIES = 256;
25
- const MAX_SESSION_BYTES = 32 * 1024 * 1024;
26
- const MAX_SUMMARY_LINE_BYTES = 1024 * 1024;
27
- const APPEND_PROOF_EDGE_BYTES = 64 * 1024;
25
+ const MAX_SESSION_BYTES = 33554432;
26
+ const MAX_SUMMARY_LINE_BYTES = 1048576;
27
+ const APPEND_PROOF_EDGE_BYTES = 65536;
28
28
  const IO_CONCURRENCY = 8;
29
29
  const PI_FILE_CANDIDATE_CACHE_TTL_MS = 32e3;
30
30
  const PI_FILE_CANDIDATE_CACHE_MAX_ENTRIES = 8;
@@ -316,8 +316,9 @@ async function listPiSummaryPage(env, params) {
316
316
  const matches = [];
317
317
  const needle = params.searchTerm?.toLocaleLowerCase();
318
318
  for (let index = 0; index < candidates.length && matches.length < target; index += SUMMARY_SCAN_BATCH_SIZE) {
319
+ const batch = candidates.slice(index, index + SUMMARY_SCAN_BATCH_SIZE);
319
320
  const { results: summaries } = await runTasksWithConcurrency({
320
- tasks: candidates.slice(index, index + SUMMARY_SCAN_BATCH_SIZE).map((candidate) => () => readPiSessionSummary(candidate)),
321
+ tasks: batch.map((candidate) => () => readPiSessionSummary(candidate)),
321
322
  limit: IO_CONCURRENCY,
322
323
  throwOnError: true
323
324
  });
@@ -584,7 +585,7 @@ async function readLocalPiTranscriptPage(value) {
584
585
  }
585
586
  //#endregion
586
587
  //#region extensions/acpx/src/pi-session-upstream-activity.ts
587
- const MAX_PI_UPSTREAM_SCAN_BYTES = 1024 * 1024;
588
+ const MAX_PI_UPSTREAM_SCAN_BYTES = 1048576;
588
589
  async function readFileRange(handle, position, length) {
589
590
  const buffer = Buffer.alloc(length);
590
591
  let offset = 0;
@@ -671,7 +672,8 @@ async function checkPiSessionUpstreamActivity(probe) {
671
672
  let occurredAt;
672
673
  for (const entry of entries) {
673
674
  if (entry.type !== "message" || !isRecord(entry.message) || entry.message.role !== "user") continue;
674
- if (!isExternalUserText(probe, textFromContent(entry.message.content))) continue;
675
+ const text = textFromContent(entry.message.content);
676
+ if (!isExternalUserText(probe, text)) continue;
675
677
  humanTurns += 1;
676
678
  occurredAt = Math.max(occurredAt ?? 0, parsePiSessionTimestampMs(entry.message.timestamp) ?? parsePiSessionTimestampMs(entry.timestamp) ?? stat.mtimeMs);
677
679
  }
@@ -24,6 +24,7 @@ function lazyStartRuntimeTurn(resolveRuntime, input) {
24
24
  /** Create an ACP runtime facade backed by an async runtime resolver. */
25
25
  function createLazyAcpRuntimeProxy(resolveRuntime) {
26
26
  return {
27
+ ownerAwareSessions: 1,
27
28
  async ensureSession(input) {
28
29
  return await (await resolveRuntime()).ensureSession(input);
29
30
  },
@@ -66,7 +67,7 @@ function createLazyAcpRuntimeProxy(resolveRuntime) {
66
67
  * immediately, then imports the heavier service only when a session needs it.
67
68
  */
68
69
  const ACPX_BACKEND_ID = "acpx";
69
- const loadServiceModule = createLazyRuntimeModule(() => import("./service-BFWEiHbQ.js"));
70
+ const loadServiceModule = createLazyRuntimeModule(() => import("./service-DLjjP2kv.js").then((n) => n.t));
70
71
  function unregisterOwnedRuntime(runtime) {
71
72
  if (runtime && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime === runtime) unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
72
73
  }
@@ -1,2 +1,2 @@
1
- import { t as createAcpxRuntimeService } from "./register.runtime-DFRkXzZ_.js";
1
+ import { t as createAcpxRuntimeService } from "./register.runtime-BHFSFWWK.js";
2
2
  export { createAcpxRuntimeService };
@@ -1,14 +1,45 @@
1
1
  import { d as withAcpxLeaseEnvironment, i as createAcpxProcessLeaseId, o as hashAcpxProcessCommand, t as ACPX_PROBE_LEASE_SESSION_KEY, u as readAcpxProcessLeaseIdentity, v as splitCommandParts } from "./process-lease-Cwvj7WGe.js";
2
2
  import { AcpRuntimeError } from "./runtime-api.js";
3
- import { d as OPENCLAW_CODEX_CONFIG_ARG, l as CODEX_ACP_PACKAGE, n as cleanupOpenClawOwnedAcpxProcessTree, r as isOpenClawLeaseAwareAcpxProcessCommand, t as cleanupOpenClawOwnedAcpxPendingLease } from "./process-reaper-DduWm_7N.js";
3
+ import { t as resolveAcpxSessionResource } from "./session-resource-CRWvFl2-.js";
4
+ import { a as CODEX_ACP_PACKAGE, i as isOpenClawLeaseAwareAcpxProcessCommand, n as cleanupOpenClawOwnedAcpxPendingLease, o as OPENCLAW_CODEX_CONFIG_ARG, r as cleanupOpenClawOwnedAcpxProcessTree } from "./service-DLjjP2kv.js";
4
5
  import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
5
6
  import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
6
7
  import path, { resolve } from "node:path";
7
8
  import fs from "node:fs/promises";
8
9
  import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
9
10
  import { AsyncLocalStorage } from "node:async_hooks";
10
- import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
11
+ import { ACPX_BACKEND_ID, AcpxRuntime as AcpxRuntime$1, createAcpRuntime, createAgentRegistry, createFileSessionStore, decodeAcpxRuntimeHandleState, decodeAcpxRuntimeHandleState as decodeAcpxRuntimeHandleState$1, encodeAcpxRuntimeHandleState, isRequestedModelUnsupportedError } from "acpx/runtime";
11
12
  import { redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
13
+ //#region extensions/acpx/src/session-owner.ts
14
+ function requireAcpxOwnerMigration(sessionKey) {
15
+ throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", `ACP session "${sessionKey}" has an unqualified or unverifiable backend locator. Stop the Gateway and run "openclaw doctor --fix" to migrate ownership without losing history, then restart.`, { detailCode: "SESSION_OWNER_MIGRATION_REQUIRED" });
16
+ }
17
+ function assertAcpxSessionOwnerLocator(target, legacyBareSessionKeys) {
18
+ const resource = resolveAcpxSessionResource(target);
19
+ const qualified = resource === target.sessionKey.trim().toLowerCase();
20
+ const persisted = target.persistedHandle;
21
+ if (!qualified && (legacyBareSessionKeys?.has(target.sessionKey.trim().toLowerCase()) || legacyBareSessionKeys?.has(resource) && !persisted)) requireAcpxOwnerMigration(target.sessionKey);
22
+ if (persisted) {
23
+ const decoded = decodeAcpxRuntimeHandleState$1(persisted.runtimeSessionName);
24
+ if (!qualified && !decoded || decoded && (decoded.name !== resource || persisted.acpxRecordId && decoded.acpxRecordId !== persisted.acpxRecordId)) requireAcpxOwnerMigration(target.sessionKey);
25
+ }
26
+ return resource;
27
+ }
28
+ /** Preserve physical oneshot record IDs and the upstream-encoded runtime handle. */
29
+ function toAcpxResourceInput(input) {
30
+ const sessionKey = assertAcpxSessionOwnerLocator({
31
+ ...input.handle,
32
+ persistedHandle: input.handle
33
+ });
34
+ return {
35
+ ...input,
36
+ handle: {
37
+ ...input.handle,
38
+ sessionKey
39
+ }
40
+ };
41
+ }
42
+ //#endregion
12
43
  //#region extensions/acpx/src/runtime.ts
13
44
  /**
14
45
  * OpenClaw ACPX runtime adapter. It wraps the upstream acpx runtime with
@@ -50,7 +81,8 @@ function isGenericInternalAcpError(error) {
50
81
  async function readCodexWrapperStderrTail(params) {
51
82
  if (!params.wrapperRoot || !params.leaseId) return "";
52
83
  try {
53
- return compactDiagnosticText(redactSensitiveText(sliceUtf16Safe(await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8"), -6e3)));
84
+ const text = await fs.readFile(path.join(params.wrapperRoot, codexWrapperStderrLogFileName(params.leaseId)), "utf8");
85
+ return compactDiagnosticText(redactSensitiveText(sliceUtf16Safe(text, -6e3)));
54
86
  } catch {
55
87
  return "";
56
88
  }
@@ -427,7 +459,12 @@ function withManagedToolsMcpSessionEnv(params) {
427
459
  }];
428
460
  return {
429
461
  ...server,
430
- env
462
+ env,
463
+ args: params.agentId ? [
464
+ ...server.args,
465
+ "--openclaw-agent-id",
466
+ params.agentId
467
+ ] : server.args
431
468
  };
432
469
  });
433
470
  return changed ? nextServers : params.mcpServers;
@@ -435,6 +472,7 @@ function withManagedToolsMcpSessionEnv(params) {
435
472
  /** OpenClaw-managed ACP runtime implementation backed by the upstream acpx runtime. */
436
473
  var AcpxRuntime = class {
437
474
  constructor(options, testOptions) {
475
+ this.ownerAwareSessions = 1;
438
476
  this.codexAcpModelOverrideScope = new AsyncLocalStorage();
439
477
  this.managedToolsSessionDelegates = /* @__PURE__ */ new Map();
440
478
  this.launchLeaseScope = new AsyncLocalStorage();
@@ -442,6 +480,7 @@ var AcpxRuntime = class {
442
480
  this.processLeaseTransitionTails = /* @__PURE__ */ new Map();
443
481
  this.processLeaseOperationCounts = /* @__PURE__ */ new Map();
444
482
  this.uncertainProcessLeaseIds = /* @__PURE__ */ new Set();
483
+ this.legacyBareSessionKeys = new Set(options.openclawLegacyBareSessionKeys);
445
484
  const { openclawProcessCleanup, ...delegateTestOptions } = testOptions ?? {};
446
485
  this.processCleanupDeps = openclawProcessCleanup;
447
486
  this.wrapperRoot = options.openclawWrapperRoot;
@@ -485,12 +524,11 @@ var AcpxRuntime = class {
485
524
  }
486
525
  resolveDelegateForSession(params) {
487
526
  if (shouldUseBridgeSafeDelegateForCommand(params.command)) return this.bridgeSafeDelegate;
488
- return this.resolveManagedToolsDelegateForSession(params.sessionKey);
527
+ return this.resolveManagedToolsDelegateForSession(params);
489
528
  }
490
- resolveManagedToolsDelegateForSession(sessionKey) {
529
+ resolveManagedToolsDelegateForSession(target) {
491
530
  if (!this.managedToolsMcpBridgeEnabled) return this.delegate;
492
- const normalizedSessionKey = sessionKey.trim();
493
- if (!normalizedSessionKey) return this.delegate;
531
+ const normalizedSessionKey = resolveAcpxSessionResource(target);
494
532
  const cached = this.managedToolsSessionDelegates.get(normalizedSessionKey);
495
533
  if (cached) return cached;
496
534
  const delegate = new AcpxRuntime$1({
@@ -499,7 +537,8 @@ var AcpxRuntime = class {
499
537
  pluginToolsEnabled: this.pluginToolsMcpBridgeEnabled,
500
538
  openclawToolsEnabled: this.openclawToolsMcpBridgeEnabled,
501
539
  mcpServers: this.delegateOptions.mcpServers,
502
- sessionKey: normalizedSessionKey
540
+ sessionKey: target.sessionKey,
541
+ agentId: target.agentId
503
542
  })
504
543
  }, this.delegateTestOptions);
505
544
  this.managedToolsSessionDelegates.set(normalizedSessionKey, delegate);
@@ -512,7 +551,11 @@ var AcpxRuntime = class {
512
551
  this.managedToolsSessionDelegates.delete(normalizedSessionKey);
513
552
  }
514
553
  async loadOperationSnapshotForHandle(handle) {
515
- const record = await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey);
554
+ assertAcpxSessionOwnerLocator({
555
+ ...handle,
556
+ persistedHandle: handle
557
+ }, this.legacyBareSessionKeys);
558
+ const record = await this.sessionStore.load(handle.acpxRecordId ?? resolveAcpxSessionResource(handle));
516
559
  return {
517
560
  record,
518
561
  command: readAgentCommandFromRecord(record) ?? resolveAgentCommand({
@@ -524,7 +567,8 @@ var AcpxRuntime = class {
524
567
  resolveDelegateForOperationSnapshot(handle, snapshot) {
525
568
  return this.resolveDelegateForSession({
526
569
  command: snapshot.command,
527
- sessionKey: handle.sessionKey
570
+ sessionKey: handle.sessionKey,
571
+ agentId: handle.agentId
528
572
  });
529
573
  }
530
574
  commandWithLaunchLease(command) {
@@ -623,7 +667,7 @@ var AcpxRuntime = class {
623
667
  await processLeaseStore.save({
624
668
  leaseId: identity.leaseId,
625
669
  gatewayInstanceId: identity.gatewayInstanceId,
626
- sessionKey: handle.sessionKey,
670
+ sessionKey: resolveAcpxSessionResource(handle),
627
671
  wrapperRoot,
628
672
  wrapperPath: extractGeneratedWrapperPath(command),
629
673
  rootPid: recordPid ?? 0,
@@ -633,7 +677,7 @@ var AcpxRuntime = class {
633
677
  });
634
678
  return;
635
679
  }
636
- if (existing.gatewayInstanceId !== identity.gatewayInstanceId || existing.sessionKey !== handle.sessionKey || existing.wrapperRoot !== wrapperRoot) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another session`);
680
+ if (existing.gatewayInstanceId !== identity.gatewayInstanceId || existing.sessionKey !== resolveAcpxSessionResource(handle) || existing.wrapperRoot !== wrapperRoot) throw new AcpRuntimeError("ACP_TURN_FAILED", `ACPX process lease ${identity.leaseId} belongs to another session`);
637
681
  });
638
682
  return identity;
639
683
  }
@@ -673,7 +717,7 @@ var AcpxRuntime = class {
673
717
  });
674
718
  }
675
719
  async finalizeProcessLeaseForOperation(handle, identity) {
676
- await this.finalizeProcessLeaseForSession(handle.acpxRecordId ?? handle.sessionKey, identity);
720
+ await this.finalizeProcessLeaseForSession(handle.acpxRecordId ?? resolveAcpxSessionResource(handle), identity);
677
721
  }
678
722
  async finalizeProcessLeaseForSession(sessionId, identity) {
679
723
  if (!identity || !this.processLeaseStore) return;
@@ -688,7 +732,8 @@ var AcpxRuntime = class {
688
732
  }
689
733
  this.uncertainProcessLeaseIds.delete(identity.leaseId);
690
734
  try {
691
- const recordIdentity = readAcpxProcessLeaseIdentity(readAgentCommandFromRecord(await this.sessionStore.load(sessionId)));
735
+ const record = await this.sessionStore.load(sessionId);
736
+ const recordIdentity = readAcpxProcessLeaseIdentity(readAgentCommandFromRecord(record));
692
737
  if (recordIdentity?.leaseId !== identity.leaseId || recordIdentity.gatewayInstanceId !== identity.gatewayInstanceId) await processLeaseStore.markState(identity.leaseId, "lost");
693
738
  } catch {}
694
739
  });
@@ -764,7 +809,7 @@ var AcpxRuntime = class {
764
809
  }
765
810
  }
766
811
  async readCodexTurnFailureStderr(params) {
767
- const record = await this.sessionStore.load(params.handle.acpxRecordId ?? params.handle.sessionKey);
812
+ const record = await this.sessionStore.load(params.handle.acpxRecordId ?? resolveAcpxSessionResource(params.handle));
768
813
  return readCodexWrapperStderrTail({
769
814
  wrapperRoot: this.wrapperRoot,
770
815
  leaseId: readOpenClawLeaseIdFromRecord(record)
@@ -773,7 +818,7 @@ var AcpxRuntime = class {
773
818
  async cleanupProcessTreeForRecord(handle, record) {
774
819
  const leaseId = readOpenClawLeaseIdFromRecord(record);
775
820
  const rootPid = readAgentPidFromRecord(record);
776
- const sessionKeys = [handle.sessionKey, readSessionRecordName(record)];
821
+ const sessionKeys = [resolveAcpxSessionResource(handle), readSessionRecordName(record)];
777
822
  const selectedLease = selectCurrentSessionLease({
778
823
  leases: this.gatewayInstanceId && this.processLeaseStore ? await this.processLeaseStore.listOpen(this.gatewayInstanceId) : [],
779
824
  sessionKeys,
@@ -829,18 +874,28 @@ var AcpxRuntime = class {
829
874
  });
830
875
  }
831
876
  async ensureSession(input) {
832
- return await this.runSerializedSessionEnsure(input.sessionKey, () => this.ensureSessionUnlocked(input));
877
+ const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
878
+ return await this.runSerializedSessionEnsure(resource, () => this.ensureSessionUnlocked(input));
833
879
  }
834
- async ensureSessionUnlocked(input) {
835
- assertSupportedRuntimeSessionMode(input.mode);
880
+ async ensureSessionUnlocked(logicalInput) {
881
+ assertSupportedRuntimeSessionMode(logicalInput.mode);
836
882
  const command = resolveAgentCommand({
837
- agentName: input.agent,
883
+ agentName: logicalInput.agent,
838
884
  agentRegistry: this.agentRegistry
839
885
  });
840
886
  const delegate = this.resolveDelegateForSession({
841
887
  command,
842
- sessionKey: input.sessionKey
888
+ sessionKey: logicalInput.sessionKey,
889
+ agentId: logicalInput.agentId
843
890
  });
891
+ const logicalTarget = {
892
+ sessionKey: logicalInput.sessionKey,
893
+ agentId: logicalInput.agentId
894
+ };
895
+ const input = {
896
+ ...logicalInput,
897
+ sessionKey: resolveAcpxSessionResource(logicalInput)
898
+ };
844
899
  const isCodexAcp = normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command);
845
900
  const claudeModelOverride = isClaudeAcpCommand(command) ? normalizeClaudeAcpModelOverride(input.model) : void 0;
846
901
  const codexClassification = isCodexAcp ? classifyCodexAcpModelRequest(input.model, input.thinking) : void 0;
@@ -864,38 +919,42 @@ var AcpxRuntime = class {
864
919
  command: stableLaunchCommand,
865
920
  resumeSessionId: input.resumeSessionId
866
921
  });
867
- const handle = !codexModelOverride ? await this.runWithLaunchLease({
868
- sessionKey: ensureInput.sessionKey,
869
- command: stableLaunchCommand,
870
- reusableCommand,
871
- run: () => this.withCodexWrapperDiagnostics({
922
+ return {
923
+ ...!codexModelOverride ? await this.runWithLaunchLease({
924
+ sessionKey: ensureInput.sessionKey,
872
925
  command: stableLaunchCommand,
873
- fallbackCode: "ACP_SESSION_INIT_FAILED",
874
- run: () => ensureDelegateSessionWithModelFallback(delegate, ensureInput)
875
- })
876
- }) : await this.runWithLaunchLease({
877
- sessionKey: input.sessionKey,
878
- command: stableLaunchCommand,
879
- reusableCommand,
880
- run: () => this.codexAcpModelOverrideScope.run(codexModelOverride, () => this.withCodexWrapperDiagnostics({
926
+ reusableCommand,
927
+ run: () => this.withCodexWrapperDiagnostics({
928
+ command: stableLaunchCommand,
929
+ fallbackCode: "ACP_SESSION_INIT_FAILED",
930
+ run: () => ensureDelegateSessionWithModelFallback(delegate, ensureInput)
931
+ })
932
+ }) : await this.runWithLaunchLease({
933
+ sessionKey: input.sessionKey,
881
934
  command: stableLaunchCommand,
882
- fallbackCode: "ACP_SESSION_INIT_FAILED",
883
- run: () => delegate.ensureSession(withAcpxSessionOptions(ensureInput))
884
- }))
885
- });
886
- return appliedModel ? {
887
- ...handle,
888
- appliedModel
889
- } : handle;
935
+ reusableCommand,
936
+ run: () => this.codexAcpModelOverrideScope.run(codexModelOverride, () => this.withCodexWrapperDiagnostics({
937
+ command: stableLaunchCommand,
938
+ fallbackCode: "ACP_SESSION_INIT_FAILED",
939
+ run: () => delegate.ensureSession(withAcpxSessionOptions(ensureInput))
940
+ }))
941
+ }),
942
+ ...logicalTarget,
943
+ ...appliedModel ? { appliedModel } : {}
944
+ };
890
945
  }
891
946
  async *runTurn(input) {
892
- const record = await this.sessionStore.load(input.handle.acpxRecordId ?? input.handle.sessionKey);
947
+ assertAcpxSessionOwnerLocator({
948
+ ...input.handle,
949
+ persistedHandle: input.handle
950
+ }, this.legacyBareSessionKeys);
951
+ const record = await this.sessionStore.load(input.handle.acpxRecordId ?? resolveAcpxSessionResource(input.handle));
893
952
  const turnLease = await this.prepareProcessLeaseForOperation(input.handle, record);
894
953
  let command;
895
954
  try {
896
955
  command = (await this.loadOperationSnapshotForHandle(input.handle)).command;
897
956
  const delegate = this.resolveDelegateForOperationSnapshot(input.handle, await this.loadOperationSnapshotForHandle(input.handle));
898
- for await (const event of delegate.runTurn(withOpenClawManagedTurnTimeout(input))) {
957
+ for await (const event of delegate.runTurn(withOpenClawManagedTurnTimeout(toAcpxResourceInput(input)))) {
899
958
  if (event.type !== "error" || !isCodexAcpCommand(command) || !isGenericInternalAcpErrorMessage(event.message)) {
900
959
  yield event;
901
960
  continue;
@@ -930,7 +989,7 @@ var AcpxRuntime = class {
930
989
  try {
931
990
  return {
932
991
  command,
933
- turn: delegate.startTurn(withOpenClawManagedTurnTimeout(input))
992
+ turn: delegate.startTurn(withOpenClawManagedTurnTimeout(toAcpxResourceInput(input)))
934
993
  };
935
994
  } catch (error) {
936
995
  if (!isCodexAcpCommand(command) || !isGenericInternalAcpError(error)) throw error;
@@ -1000,23 +1059,24 @@ var AcpxRuntime = class {
1000
1059
  };
1001
1060
  }
1002
1061
  getCapabilities(input) {
1003
- return this.delegate.getCapabilities(input);
1062
+ return this.delegate.getCapabilities(input?.handle ? toAcpxResourceInput({ handle: input.handle }) : input);
1004
1063
  }
1005
1064
  async getStatus(input) {
1006
1065
  const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
1007
- return this.resolveDelegateForOperationSnapshot(input.handle, snapshot).getStatus(input);
1066
+ return this.resolveDelegateForOperationSnapshot(input.handle, snapshot).getStatus(toAcpxResourceInput(input));
1008
1067
  }
1009
1068
  async setMode(input) {
1010
1069
  const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
1011
- await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).setMode(input));
1070
+ await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.resolveDelegateForOperationSnapshot(input.handle, snapshot).setMode(toAcpxResourceInput(input)));
1012
1071
  }
1013
1072
  async setConfigOption(input) {
1014
1073
  const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
1015
1074
  return await this.runWithProcessLeaseForHandle(input.handle, snapshot.record, () => this.setConfigOptionUnlocked(input, snapshot));
1016
1075
  }
1017
- async setConfigOptionUnlocked(input, snapshot) {
1076
+ async setConfigOptionUnlocked(logicalInput, snapshot) {
1018
1077
  const { command } = snapshot;
1019
- const delegate = this.resolveDelegateForOperationSnapshot(input.handle, snapshot);
1078
+ const delegate = this.resolveDelegateForOperationSnapshot(logicalInput.handle, snapshot);
1079
+ const input = toAcpxResourceInput(logicalInput);
1020
1080
  const key = input.key.trim().toLowerCase();
1021
1081
  const isCodexAcp = isCodexAcpCommand(command);
1022
1082
  if (WIRE_TIMEOUT_CONFIG_KEYS.has(key) && (isCodexAcp || isClaudeAcpCommand(command))) return;
@@ -1056,10 +1116,12 @@ var AcpxRuntime = class {
1056
1116
  }
1057
1117
  async cancel(input) {
1058
1118
  const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
1059
- await this.resolveDelegateForOperationSnapshot(input.handle, snapshot).cancel(input);
1119
+ await this.resolveDelegateForOperationSnapshot(input.handle, snapshot).cancel(toAcpxResourceInput(input));
1060
1120
  }
1061
1121
  async prepareFreshSession(input) {
1062
- this.sessionStore.markFresh(input.sessionKey);
1122
+ const resource = assertAcpxSessionOwnerLocator(input, this.legacyBareSessionKeys);
1123
+ this.sessionStore.markFresh(resource);
1124
+ this.legacyBareSessionKeys.delete(resource);
1063
1125
  }
1064
1126
  async close(input) {
1065
1127
  const snapshot = await this.loadOperationSnapshotForHandle(input.handle);
@@ -1070,7 +1132,7 @@ var AcpxRuntime = class {
1070
1132
  let closeSucceeded;
1071
1133
  try {
1072
1134
  await delegate.close({
1073
- handle: input.handle,
1135
+ handle: toAcpxResourceInput(input).handle,
1074
1136
  reason: input.reason,
1075
1137
  discardPersistentState: input.discardPersistentState
1076
1138
  });
@@ -1079,8 +1141,11 @@ var AcpxRuntime = class {
1079
1141
  await this.cleanupProcessTreeForRecord(input.handle, snapshot.record);
1080
1142
  cleanupSucceeded = true;
1081
1143
  }
1082
- if (closeSucceeded) this.releaseManagedToolsDelegateForSession(input.handle.sessionKey);
1083
- if (closeSucceeded && input.discardPersistentState) this.sessionStore.markFresh(input.handle.sessionKey);
1144
+ if (closeSucceeded) this.releaseManagedToolsDelegateForSession(resolveAcpxSessionResource(input.handle));
1145
+ if (closeSucceeded && input.discardPersistentState) await this.prepareFreshSession({
1146
+ ...input.handle,
1147
+ persistedHandle: input.handle
1148
+ });
1084
1149
  } finally {
1085
1150
  if (cleanupSucceeded) await this.finalizeProcessLeaseForOperation(input.handle, closeLease);
1086
1151
  else await this.releaseProcessLeaseAfterUncertainFailure(closeLease);