@openclaw/acpx 2026.10.10 → 2026.11.5-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.
- package/dist/.setup/{config-CE-ej7LM.mjs → config-Cplh34lQ.mjs} +15 -23
- package/dist/.setup/{harness-attempt-Bgcm-zBR.mjs → harness-attempt-Co9oLpPg.mjs} +5 -4
- package/dist/.setup/{pi-session-catalog-runtime-Cdf46b05.mjs → pi-session-catalog-runtime-DgzkbadO.mjs} +35 -57
- package/dist/.setup/{pi-session-paths-Co5515li.mjs → pi-session-paths-pUr6hnVA.mjs} +2 -2
- package/dist/.setup/{service-Dr8U5n0S.mjs → service-DmgP1gPb.mjs} +135 -373
- package/dist/.setup/{session-owner-migration-WmiXoSmh.mjs → session-owner-migration-CQA7L68K.mjs} +13 -10
- package/dist/.setup/{session-resource-Cv-RAA6y.mjs → session-resource-D_tXKvcv.mjs} +2 -2
- package/dist/.source-module-urls/9369b1b274d2ba70ea21d859abb3415ad8d63d0a1022a301763b953a5798469e.json +1 -0
- package/dist/doctor-contract-api.js +17 -11
- package/dist/index.js +206 -21
- package/openclaw.plugin.json +28 -1
- package/package.json +9 -9
- package/dist/.setup/register.runtime-CSJY10FS.mjs +0 -195
- package/dist/.source-module-urls/78a9d561eb54d4bf1ca0db6e6d12bb5688647241f2c0b289c611a0effb44d715.json +0 -1
- package/dist/register.runtime.js +0 -1
package/dist/.setup/{session-owner-migration-WmiXoSmh.mjs → session-owner-migration-CQA7L68K.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as runtime_doctor_migrations_exports } from "./runtime-doctor-migrations-CSvRANPN.mjs";
|
|
2
|
-
import { c as renderAgentCommand, t as resolveAcpxPluginConfig, u as splitCommandParts } from "./config-
|
|
2
|
+
import { c as renderAgentCommand, t as resolveAcpxPluginConfig, u as splitCommandParts } from "./config-Cplh34lQ.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { asFiniteNumber, asOptionalObjectRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -228,7 +228,7 @@ async function legacyRecords(input, directory = sessionDirectory(input)) {
|
|
|
228
228
|
directory,
|
|
229
229
|
ids
|
|
230
230
|
};
|
|
231
|
-
const { resolveAcpxSessionResource } = await import("./session-resource-
|
|
231
|
+
const { resolveAcpxSessionResource } = await import("./session-resource-D_tXKvcv.mjs").then((n) => n.n);
|
|
232
232
|
const evidence = await input.context.inspectAcpSessionClaims?.();
|
|
233
233
|
const { decodeAcpxRuntimeHandleState } = await import("acpx/runtime");
|
|
234
234
|
return {
|
|
@@ -267,11 +267,11 @@ function matchesClaimRecord(claim, raw, oldId, resource, decode) {
|
|
|
267
267
|
}
|
|
268
268
|
async function migrateRecord(input, directory, oldId, claims, changes, warnings) {
|
|
269
269
|
const sourcePath = recordPath(directory, oldId);
|
|
270
|
-
const sourceBytes = await fs.readFile(sourcePath
|
|
271
|
-
const raw = (0, runtime_doctor_migrations_exports.asObjectRecord)(JSON.parse(sourceBytes));
|
|
270
|
+
const sourceBytes = await fs.readFile(sourcePath);
|
|
271
|
+
const raw = (0, runtime_doctor_migrations_exports.asObjectRecord)(JSON.parse((0, runtime_doctor_migrations_exports.decodeUtf8Input)(sourceBytes, `ACP record ${sourcePath}`)));
|
|
272
272
|
if (!raw || raw.acpx_record_id !== oldId || typeof raw.name !== "string" || !raw.name.trim() || raw.name !== oldId && !oldId.startsWith(`${raw.name}:oneshot:`)) throw new Error("record ID/name is not a recognized ACPX locator");
|
|
273
273
|
requireStoppedPid(raw.pid);
|
|
274
|
-
const { resolveAcpxSessionResource } = await import("./session-resource-
|
|
274
|
+
const { resolveAcpxSessionResource } = await import("./session-resource-D_tXKvcv.mjs").then((n) => n.n);
|
|
275
275
|
const { createFileSessionStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState } = await import("acpx/runtime");
|
|
276
276
|
const candidates = claims.filter((claim) => matchesClaimRecord(claim, raw, oldId, resolveAcpxSessionResource(claim), decodeAcpxRuntimeHandleState));
|
|
277
277
|
if (candidates.length !== 1) throw new Error("exactly one current owner claim is required");
|
|
@@ -321,13 +321,13 @@ async function migrateRecord(input, directory, oldId, claims, changes, warnings)
|
|
|
321
321
|
acpxRecordId: resource,
|
|
322
322
|
name: resource
|
|
323
323
|
})) throw new Error("rekey would alter interpreted history/event references; source retained");
|
|
324
|
-
const existing = await fs.readFile(destinationPath
|
|
324
|
+
const existing = await fs.readFile(destinationPath).catch((error) => {
|
|
325
325
|
if ((0, runtime_doctor_migrations_exports.asObjectRecord)(error)?.code === "ENOENT") return;
|
|
326
326
|
throw error;
|
|
327
327
|
});
|
|
328
328
|
const originalLocator = decodeAcpxRuntimeHandleState(claim.meta.runtimeSessionName);
|
|
329
329
|
if (!oneshot && originalLocator.name === resource && existing === void 0) throw new Error("migrated metadata has no published destination; source retained");
|
|
330
|
-
if (existing !== void 0 && !isDeepStrictEqual(JSON.parse(existing), candidate)) throw new Error("destination conflicts; no files were overwritten");
|
|
330
|
+
if (existing !== void 0 && !isDeepStrictEqual(JSON.parse((0, runtime_doctor_migrations_exports.decodeUtf8Input)(existing, `ACP record ${destinationPath}`)), candidate)) throw new Error("destination conflicts; no files were overwritten");
|
|
331
331
|
if (existing === void 0) await fs.link(temporaryPath, destinationPath);
|
|
332
332
|
const publicationDirectory = await fs.open(directory, "r");
|
|
333
333
|
try {
|
|
@@ -335,8 +335,8 @@ async function migrateRecord(input, directory, oldId, claims, changes, warnings)
|
|
|
335
335
|
} finally {
|
|
336
336
|
await publicationDirectory.close();
|
|
337
337
|
}
|
|
338
|
-
if (await fs.readFile(sourcePath
|
|
339
|
-
if (!isDeepStrictEqual(JSON.parse(await fs.readFile(destinationPath,
|
|
338
|
+
if (!(await fs.readFile(sourcePath)).equals(sourceBytes)) throw new Error("source changed during repair");
|
|
339
|
+
if (!isDeepStrictEqual(JSON.parse((0, runtime_doctor_migrations_exports.decodeUtf8Input)(await fs.readFile(destinationPath), `ACP record ${destinationPath}`)), candidate)) throw new Error("destination changed during repair");
|
|
340
340
|
for (const { row } of matchingLeases) {
|
|
341
341
|
if (!isDeepStrictEqual(await leaseStore.lookup(row.key), row.value)) throw new Error("lease changed during repair");
|
|
342
342
|
await leaseStore.register(row.key, {
|
|
@@ -412,16 +412,19 @@ const acpxSessionOwnerMigration = {
|
|
|
412
412
|
changes,
|
|
413
413
|
warnings: [`ACP owner evidence is incomplete; all records retained: ${evidence.incomplete.join("; ")}`]
|
|
414
414
|
};
|
|
415
|
+
let recoverableWarnings = 0;
|
|
415
416
|
for (const oldId of ids) try {
|
|
416
417
|
const current = await input.context.inspectAcpSessionClaims();
|
|
417
418
|
if (current.incomplete.length) throw new Error("current ownership evidence became incomplete");
|
|
418
419
|
await migrateRecord(input, directory, oldId, current.claims, changes, warnings);
|
|
419
420
|
} catch (error) {
|
|
421
|
+
if (error instanceof runtime_doctor_migrations_exports.Utf8InputError) recoverableWarnings++;
|
|
420
422
|
warnings.push(`ACP record ${oldId} retained: ${String(error)}`);
|
|
421
423
|
}
|
|
422
424
|
return {
|
|
423
425
|
changes,
|
|
424
|
-
warnings
|
|
426
|
+
warnings,
|
|
427
|
+
...warnings.length > 0 && warnings.length === recoverableWarnings ? { warningDisposition: "recoverable" } : {}
|
|
425
428
|
};
|
|
426
429
|
}
|
|
427
430
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __exportAll } from "./runtime-doctor-migrations-CSvRANPN.mjs";
|
|
2
2
|
import { AcpRuntimeError } from "../runtime-api.js";
|
|
3
|
-
import {
|
|
3
|
+
import { sha256Hex } from "openclaw/plugin-sdk/node-crypto-runtime";
|
|
4
4
|
import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
5
5
|
// extensions/acpx/src/session-resource.ts
|
|
6
6
|
var session_resource_exports = /* @__PURE__ */ __exportAll({ resolveAcpxSessionResource: () => resolveAcpxSessionResource });
|
|
@@ -11,6 +11,6 @@ function resolveAcpxSessionResource(target) {
|
|
|
11
11
|
const agentId = target.agentId?.trim() ? normalizeAgentId(target.agentId) : encodedOwner;
|
|
12
12
|
if (!sessionKey || encodedOwner && agentId !== encodedOwner || !encodedOwner && !agentId) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP session owner is missing or disagrees with its logical key. Pass the OpenClaw agentId that owns this session.", { detailCode: "SESSION_OWNER_UNSUPPORTED" });
|
|
13
13
|
if (encodedOwner) return sessionKey;
|
|
14
|
-
return `openclaw-owner-v1-${
|
|
14
|
+
return `openclaw-owner-v1-${sha256Hex(JSON.stringify([agentId, sessionKey]))}`;
|
|
15
15
|
}
|
|
16
16
|
export { session_resource_exports as n, resolveAcpxSessionResource as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"entries":[{"output":"doctor-contract-api.js","source":"extensions/acpx/doctor-contract-api.ts"},{"output":"index.js","source":"extensions/acpx/index.ts"},{"output":"runtime-api.js","source":"extensions/acpx/runtime-api.ts"},{"output":"setup-api.js","source":"extensions/acpx/setup-api.ts"}],"chunks":[{"output":".setup/config-Cplh34lQ.mjs","sha256":"d26588a4061ab1327c1c8f3b226d58b18f32167dae0c6cc88f39008eabab7a5d","facadeSource":null,"sources":["extensions/acpx/src/codex-adapter.ts","extensions/acpx/src/command-line.ts","extensions/acpx/src/config-schema.ts","extensions/acpx/src/config.ts"]},{"output":".setup/harness-attempt-Co9oLpPg.mjs","sha256":"1f4ff7f279f78641c8b0160aad3be6062ae981c184bc2a15088678e2db4d6add","facadeSource":"extensions/acpx/src/harness-attempt.ts","sources":["extensions/acpx/src/harness-attempt.ts"]},{"output":".setup/pi-session-catalog-runtime-DgzkbadO.mjs","sha256":"52b9233beb364ff104c8e7a6044cc746aaa883c49fa915ce39bbd32d0b9dd90d","facadeSource":"extensions/acpx/src/pi-session-catalog-runtime.ts","sources":["extensions/acpx/src/pi-session-catalog-runtime.ts","extensions/acpx/src/pi-session-catalog.ts","extensions/acpx/src/pi-session-store.ts","extensions/acpx/src/pi-session-upstream-activity.ts"]},{"output":".setup/pi-session-paths-pUr6hnVA.mjs","sha256":"da9568f3f583d68ff895d109855b4e61cb680e6550019dc3b7c643d08696dab0","facadeSource":null,"sources":["extensions/acpx/src/pi-session-catalog-shared.ts","extensions/acpx/src/pi-session-paths.ts"]},{"output":".setup/runtime-doctor-migrations-CSvRANPN.mjs","sha256":"0f9941caf2101d12539e47fd11f7da27fd330dd82bf8e80cdc11d8fd81ae2c6f","facadeSource":null,"sources":["packages/normalization-core/src/record-coerce.ts","src/config/channel-config-normalization.ts","src/plugin-sdk/legacy-webhook-listener-migration.ts"]},{"output":".setup/service-DmgP1gPb.mjs","sha256":"36916a546c59602225008bd4dbb4e88482a78f3b4dde9a9239d23f335ad00832","facadeSource":"extensions/acpx/src/service.ts","sources":["extensions/acpx/src/adapter-wrapper-script.ts","extensions/acpx/src/codex-auth-bridge.ts","extensions/acpx/src/codex-trust-config.ts","extensions/acpx/src/model-ref.ts","extensions/acpx/src/process-reaper.ts","extensions/acpx/src/runtime-generations.ts","extensions/acpx/src/runtime-probe.ts","extensions/acpx/src/runtime-process-cleanup.ts","extensions/acpx/src/runtime-session-store.ts","extensions/acpx/src/runtime.ts","extensions/acpx/src/service.ts","extensions/acpx/src/session-owner.ts"]},{"output":".setup/session-owner-migration-CQA7L68K.mjs","sha256":"26ed540907ae4bed933ec1c0c7211e8a3d5225ed17385bf047cf597a2c83c836","facadeSource":"extensions/acpx/src/session-owner-migration.ts","sources":["extensions/acpx/src/process-lease.ts","extensions/acpx/src/session-owner-migration.ts","extensions/acpx/src/state.ts"]},{"output":".setup/session-resource-D_tXKvcv.mjs","sha256":"dc4ea50fd410b0dadaf6c5fe5924a7e95d4c109d3128825d5bf999b668ac52f2","facadeSource":null,"sources":["extensions/acpx/src/session-resource.ts"]},{"output":"doctor-contract-api.js","sha256":"b93f9058efbf3d385e9a5168c563e68cdcb6caecf6e7e9fc4d15f7da0bb75070","facadeSource":"extensions/acpx/doctor-contract-api.ts","sources":["extensions/acpx/doctor-contract-api.ts"]},{"output":"index.js","sha256":"6a9615e8a9892102569672a50c1e63b6594b8edebe0f6ba9f286053af1b45399","facadeSource":"extensions/acpx/index.ts","sources":["extensions/acpx/index.ts","extensions/acpx/src/harness.ts","extensions/acpx/src/native-agents.ts","extensions/acpx/src/pi-session-catalog-plugin.ts","extensions/acpx/src/runtime-proxy.ts"]},{"output":"runtime-api.js","sha256":"fc9734d9dfabafe7281fca6ede1415215a44ac03bd0aa798fce919d11ae68605","facadeSource":"extensions/acpx/runtime-api.ts","sources":[]},{"output":"setup-api.js","sha256":"8251d1074687501cbbb6aee52f51d74113118f5c93570733c3a2544f9beeac31","facadeSource":"extensions/acpx/setup-api.ts","sources":["extensions/acpx/setup-api.ts"]}],"unrenderedSources":["extensions/acpx/runtime-api.ts"]}
|
|
@@ -21,16 +21,22 @@ function normalizeCompatibilityConfig({ cfg }) {
|
|
|
21
21
|
config: cfg,
|
|
22
22
|
changes: []
|
|
23
23
|
};
|
|
24
|
-
const
|
|
25
|
-
const nextEntry = (0, runtime_doctor_migrations_exports.asObjectRecord)(nextConfig.plugins?.entries?.acpx);
|
|
26
|
-
const nextPluginConfig = (0, runtime_doctor_migrations_exports.asObjectRecord)(nextEntry?.config);
|
|
27
|
-
if (!nextPluginConfig) return {
|
|
28
|
-
config: cfg,
|
|
29
|
-
changes: []
|
|
30
|
-
};
|
|
24
|
+
const nextPluginConfig = { ...pluginConfig };
|
|
31
25
|
for (const key of retiredKeys) delete nextPluginConfig[key];
|
|
32
26
|
return {
|
|
33
|
-
config:
|
|
27
|
+
config: {
|
|
28
|
+
...cfg,
|
|
29
|
+
plugins: {
|
|
30
|
+
...cfg.plugins,
|
|
31
|
+
entries: {
|
|
32
|
+
...cfg.plugins?.entries,
|
|
33
|
+
acpx: {
|
|
34
|
+
...entry,
|
|
35
|
+
config: nextPluginConfig
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
34
40
|
changes: [`Removed retired ACPX plugin config: ${retiredKeys.map((key) => [...ACPX_CONFIG_PATH, key].join(".")).join(", ")}.`]
|
|
35
41
|
};
|
|
36
42
|
}
|
|
@@ -40,13 +46,13 @@ const stateMigrations = [{
|
|
|
40
46
|
doctorOnly: true,
|
|
41
47
|
phase: "after-session-repair",
|
|
42
48
|
async collectBackupResources(input) {
|
|
43
|
-
return (await import("./.setup/session-owner-migration-
|
|
49
|
+
return (await import("./.setup/session-owner-migration-CQA7L68K.mjs")).acpxSessionOwnerMigration.collectBackupResources(input);
|
|
44
50
|
},
|
|
45
51
|
async detectLegacyState(input) {
|
|
46
|
-
return (await import("./.setup/session-owner-migration-
|
|
52
|
+
return (await import("./.setup/session-owner-migration-CQA7L68K.mjs")).acpxSessionOwnerMigration.detectLegacyState(input);
|
|
47
53
|
},
|
|
48
54
|
async migrateLegacyState(input) {
|
|
49
|
-
return (await import("./.setup/session-owner-migration-
|
|
55
|
+
return (await import("./.setup/session-owner-migration-CQA7L68K.mjs")).acpxSessionOwnerMigration.migrateLegacyState(input);
|
|
50
56
|
}
|
|
51
57
|
}];
|
|
52
58
|
export { legacyConfigRules, normalizeCompatibilityConfig, stateMigrations };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
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 "./.setup/pi-session-paths-Co5515li.mjs";
|
|
1
|
+
import { g as AcpxNativeAgentsSchema, h as ACPX_NATIVE_AGENT_IDS, t as resolveAcpxPluginConfig } from "./.setup/config-Cplh34lQ.mjs";
|
|
2
|
+
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 "./.setup/pi-session-paths-pUr6hnVA.mjs";
|
|
4
3
|
import { createAgentRegistry } from "acpx/agent-registry";
|
|
5
|
-
import { tryDispatchAcpReplyHook } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
6
|
-
import { createLazyRuntimeModule, createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
|
|
4
|
+
import { getAcpRuntimeBackend, registerAcpRuntimeBackend, requireAcpRuntimeBackend, tryDispatchAcpReplyHook, unregisterAcpRuntimeBackend } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
5
|
+
import { createLazyRuntimeMethodBinder, createLazyRuntimeModule, createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
|
|
7
6
|
import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
8
7
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
8
|
import { resolveNodeHostExecutable } from "openclaw/plugin-sdk/node-host";
|
|
@@ -168,6 +167,8 @@ function createAcpAgentHarness(params) {
|
|
|
168
167
|
agentCommand: inspection.launch.argv,
|
|
169
168
|
cwd: input.workspaceDir ?? config.cwd,
|
|
170
169
|
signal: generation.signal,
|
|
170
|
+
agentProcessEnvAllow: config.agentProcessEnvAllow,
|
|
171
|
+
agentProcessEnvBlock: config.agentProcessEnvBlock,
|
|
171
172
|
timeoutMs: config.timeoutSeconds === void 0 ? void 0 : finiteSecondsToTimerSafeMilliseconds(config.timeoutSeconds) ?? 1
|
|
172
173
|
});
|
|
173
174
|
generation.signal.throwIfAborted();
|
|
@@ -203,7 +204,7 @@ function createAcpAgentHarness(params) {
|
|
|
203
204
|
if (inspection?.launch.kind !== "installed") throw new Error(`${params.label} is not installed; refresh the model catalog`);
|
|
204
205
|
const runtime = await runtimeFor(input.workspaceDir);
|
|
205
206
|
generation.signal.throwIfAborted();
|
|
206
|
-
const { runAcpHarnessAttempt } = await import("./.setup/harness-attempt-
|
|
207
|
+
const { runAcpHarnessAttempt } = await import("./.setup/harness-attempt-Co9oLpPg.mjs");
|
|
207
208
|
return await runAcpHarnessAttempt({
|
|
208
209
|
input,
|
|
209
210
|
runtime,
|
|
@@ -244,7 +245,7 @@ function createAcpAgentHarness(params) {
|
|
|
244
245
|
};
|
|
245
246
|
}
|
|
246
247
|
// extensions/acpx/src/pi-session-catalog-plugin.ts
|
|
247
|
-
const loadPiSessionCatalogModule = createLazyRuntimeModule(() => import("./.setup/pi-session-catalog-runtime-
|
|
248
|
+
const loadPiSessionCatalogModule = createLazyRuntimeModule(() => import("./.setup/pi-session-catalog-runtime-DgzkbadO.mjs"));
|
|
248
249
|
function fullConfigCatalogEnabled(config) {
|
|
249
250
|
if (!isRecord(config) || !isRecord(config.plugins) || !isRecord(config.plugins.entries)) return true;
|
|
250
251
|
const entry = config.plugins.entries.acpx;
|
|
@@ -306,22 +307,210 @@ function registerPiSessionCatalog(api) {
|
|
|
306
307
|
for (const command of nodeHost.commands) api.registerNodeHostCommand(command);
|
|
307
308
|
for (const policy of nodeHost.policies) api.registerNodeInvokePolicy(policy);
|
|
308
309
|
}
|
|
310
|
+
// extensions/acpx/src/runtime-proxy.ts
|
|
311
|
+
/** Start an ACP turn through a lazy runtime resolver without awaiting resolution up front. */
|
|
312
|
+
function lazyStartRuntimeTurn(resolveRuntime, input) {
|
|
313
|
+
const turnPromise = resolveRuntime().then((runtime) => runtime.startTurn(input));
|
|
314
|
+
return {
|
|
315
|
+
requestId: input.requestId,
|
|
316
|
+
get promptStarted() {
|
|
317
|
+
return turnPromise.then((turn) => turn.promptStarted);
|
|
318
|
+
},
|
|
319
|
+
events: { async *[Symbol.asyncIterator]() {
|
|
320
|
+
yield* (await turnPromise).events;
|
|
321
|
+
} },
|
|
322
|
+
result: turnPromise.then((turn) => turn.result),
|
|
323
|
+
cancel(inputArgs) {
|
|
324
|
+
return turnPromise.then((turn) => turn.cancel(inputArgs));
|
|
325
|
+
},
|
|
326
|
+
closeStream(inputArgs) {
|
|
327
|
+
return turnPromise.then((turn) => turn.closeStream(inputArgs));
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/** Create an ACP runtime facade backed by an async runtime resolver. */
|
|
332
|
+
function createLazyAcpRuntimeProxy(resolveRuntime) {
|
|
333
|
+
const bind = createLazyRuntimeMethodBinder(resolveRuntime);
|
|
334
|
+
return {
|
|
335
|
+
ownerAwareSessions: 1,
|
|
336
|
+
findSession: bind((runtime) => runtime.findSession.bind(runtime)),
|
|
337
|
+
shutdown: bind((runtime) => runtime.shutdown.bind(runtime)),
|
|
338
|
+
ensureSession: bind((runtime) => runtime.ensureSession.bind(runtime)),
|
|
339
|
+
startTurn(input) {
|
|
340
|
+
return lazyStartRuntimeTurn(resolveRuntime, input);
|
|
341
|
+
},
|
|
342
|
+
async *runTurn(input) {
|
|
343
|
+
yield* (await resolveRuntime()).runTurn(input);
|
|
344
|
+
},
|
|
345
|
+
getCapabilities: bind((runtime) => runtime.getCapabilities.bind(runtime)),
|
|
346
|
+
getStatus: bind((runtime) => runtime.getStatus.bind(runtime)),
|
|
347
|
+
setMode: bind((runtime) => runtime.setMode.bind(runtime)),
|
|
348
|
+
setModel: bind((runtime) => runtime.setModel.bind(runtime)),
|
|
349
|
+
setConfigOption: bind((runtime) => runtime.setConfigOption.bind(runtime)),
|
|
350
|
+
doctor: bind((runtime) => runtime.doctor.bind(runtime)),
|
|
351
|
+
prepareFreshSession: bind((runtime) => runtime.prepareFreshSession.bind(runtime)),
|
|
352
|
+
cancel: bind((runtime) => runtime.cancel.bind(runtime)),
|
|
353
|
+
close: bind((runtime) => runtime.close.bind(runtime))
|
|
354
|
+
};
|
|
355
|
+
}
|
|
309
356
|
// extensions/acpx/index.ts
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
357
|
+
const ACPX_BACKEND_ID = "acpx";
|
|
358
|
+
const loadServiceModule = createLazyRuntimeModule(() => import("./.setup/service-DmgP1gPb.mjs"));
|
|
359
|
+
function unregisterOwnedRuntime(runtime) {
|
|
360
|
+
if (runtime && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime === runtime) unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
361
|
+
}
|
|
314
362
|
const plugin = {
|
|
315
363
|
id: "acpx",
|
|
316
364
|
name: "ACPX Runtime",
|
|
317
365
|
description: "Embedded ACP runtime backend with plugin-owned session and transport management.",
|
|
318
366
|
register(api) {
|
|
319
367
|
registerPiSessionCatalog(api);
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
368
|
+
const pluginConfig = api.pluginConfig;
|
|
369
|
+
let ctx = null;
|
|
370
|
+
let published = false;
|
|
371
|
+
let lifecycleRevision = 0;
|
|
372
|
+
let ownedRuntime = null;
|
|
373
|
+
let realService = null;
|
|
374
|
+
let startPromise = null;
|
|
375
|
+
let stopPromise = null;
|
|
376
|
+
async function startRealService(expectedRevision, deferredRuntime, purpose = "gateway", probeAtStartup = purpose === "gateway") {
|
|
377
|
+
if (lifecycleRevision !== expectedRevision || !ctx) throw new Error("ACPX runtime service is not started");
|
|
378
|
+
if (startPromise) return await startPromise;
|
|
379
|
+
const activeContext = ctx;
|
|
380
|
+
let starting = true;
|
|
381
|
+
startPromise = (async () => {
|
|
382
|
+
let publishedRuntime = null;
|
|
383
|
+
const { createAcpxRuntimeService: createAcpxRuntimeServiceLocal } = await loadServiceModule();
|
|
384
|
+
const activeService = createAcpxRuntimeServiceLocal({
|
|
385
|
+
pluginConfig,
|
|
386
|
+
getAllowedAgents: () => api.runtime.config.current().acp?.allowedAgents,
|
|
387
|
+
openKeyedStore: (options) => api.runtime.state.openKeyedStoreV2(options),
|
|
388
|
+
probeAtStartup,
|
|
389
|
+
startupPurpose: purpose,
|
|
390
|
+
assertCurrent: () => {
|
|
391
|
+
if (lifecycleRevision !== expectedRevision || ctx !== activeContext || published && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== ownedRuntime) throw new Error("ACPX runtime service lost recovery ownership");
|
|
392
|
+
},
|
|
393
|
+
backendLifecycle: {
|
|
394
|
+
publish(backend) {
|
|
395
|
+
if (lifecycleRevision !== expectedRevision || ctx !== activeContext) throw new Error("ACPX runtime service stopped during activation");
|
|
396
|
+
if (published && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== deferredRuntime) throw new Error("ACPX runtime service lost registry ownership during activation");
|
|
397
|
+
if (published) {
|
|
398
|
+
const { healthy } = backend;
|
|
399
|
+
registerAcpRuntimeBackend({
|
|
400
|
+
id: ACPX_BACKEND_ID,
|
|
401
|
+
...backend,
|
|
402
|
+
runtime: deferredRuntime,
|
|
403
|
+
...healthy ? { healthy: () => starting || healthy() } : {}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
publishedRuntime = backend.runtime;
|
|
407
|
+
},
|
|
408
|
+
retract() {}
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
realService = activeService;
|
|
412
|
+
await activeService.start(activeContext);
|
|
413
|
+
if (lifecycleRevision !== expectedRevision || ctx !== activeContext) throw new Error("ACPX runtime service stopped during activation");
|
|
414
|
+
if (!publishedRuntime) throw new Error("ACPX runtime service did not register an ACP backend");
|
|
415
|
+
if (published && getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime !== deferredRuntime) throw new Error("ACPX runtime service lost registry ownership during activation");
|
|
416
|
+
return publishedRuntime;
|
|
417
|
+
})().finally(() => {
|
|
418
|
+
starting = false;
|
|
419
|
+
});
|
|
420
|
+
try {
|
|
421
|
+
return await startPromise;
|
|
422
|
+
} catch (error) {
|
|
423
|
+
if (lifecycleRevision === expectedRevision) {
|
|
424
|
+
startPromise = null;
|
|
425
|
+
realService = null;
|
|
426
|
+
}
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function createDeferredRuntime(expectedRevision) {
|
|
431
|
+
const deferredRuntime = {
|
|
432
|
+
...createLazyAcpRuntimeProxy(() => startRealService(expectedRevision, deferredRuntime)),
|
|
433
|
+
async ensureSession(input) {
|
|
434
|
+
const runtime = await startRealService(expectedRevision, deferredRuntime);
|
|
435
|
+
requireAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
436
|
+
return await runtime.ensureSession(input);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
return deferredRuntime;
|
|
440
|
+
}
|
|
441
|
+
const service = {
|
|
442
|
+
id: "acpx-runtime",
|
|
443
|
+
async getRuntime(serviceContext) {
|
|
444
|
+
if (stopPromise) await stopPromise;
|
|
445
|
+
if (!ctx) {
|
|
446
|
+
ctx = serviceContext;
|
|
447
|
+
lifecycleRevision += 1;
|
|
448
|
+
ownedRuntime = createDeferredRuntime(lifecycleRevision);
|
|
449
|
+
}
|
|
450
|
+
if (!ownedRuntime) throw new Error("ACPX runtime service lost its runtime owner");
|
|
451
|
+
return await startRealService(lifecycleRevision, ownedRuntime, published ? "gateway" : "inspection", false);
|
|
452
|
+
},
|
|
453
|
+
async start(serviceContext) {
|
|
454
|
+
if (process.env.OPENCLAW_SKIP_ACPX_RUNTIME === "1") {
|
|
455
|
+
serviceContext.logger.info("skipping embedded acpx runtime backend (OPENCLAW_SKIP_ACPX_RUNTIME=1)");
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (stopPromise) await stopPromise;
|
|
459
|
+
if (ctx && ownedRuntime) {
|
|
460
|
+
const revision = lifecycleRevision;
|
|
461
|
+
const previous = getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime;
|
|
462
|
+
const assertCurrent = () => {
|
|
463
|
+
const current = getAcpRuntimeBackend(ACPX_BACKEND_ID)?.runtime;
|
|
464
|
+
if (lifecycleRevision !== revision || !ctx || current !== previous && current !== ownedRuntime) throw new Error("ACPX runtime service lost promotion ownership");
|
|
465
|
+
};
|
|
466
|
+
await startPromise;
|
|
467
|
+
assertCurrent();
|
|
468
|
+
await realService?.promote(serviceContext, assertCurrent);
|
|
469
|
+
assertCurrent();
|
|
470
|
+
published = true;
|
|
471
|
+
registerAcpRuntimeBackend({
|
|
472
|
+
id: ACPX_BACKEND_ID,
|
|
473
|
+
runtime: ownedRuntime
|
|
474
|
+
});
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
published = true;
|
|
478
|
+
lifecycleRevision += 1;
|
|
479
|
+
ctx = serviceContext;
|
|
480
|
+
const deferredRuntime = createDeferredRuntime(lifecycleRevision);
|
|
481
|
+
ownedRuntime = deferredRuntime;
|
|
482
|
+
registerAcpRuntimeBackend({
|
|
483
|
+
id: ACPX_BACKEND_ID,
|
|
484
|
+
runtime: deferredRuntime
|
|
485
|
+
});
|
|
486
|
+
serviceContext.logger.info("embedded acpx runtime backend registered lazily");
|
|
487
|
+
},
|
|
488
|
+
async stop(serviceContext) {
|
|
489
|
+
if (stopPromise) return await stopPromise;
|
|
490
|
+
lifecycleRevision += 1;
|
|
491
|
+
ctx = null;
|
|
492
|
+
published = false;
|
|
493
|
+
const stoppingRuntime = ownedRuntime;
|
|
494
|
+
unregisterOwnedRuntime(stoppingRuntime);
|
|
495
|
+
const startingPromise = startPromise;
|
|
496
|
+
stopPromise = (async () => {
|
|
497
|
+
await startingPromise?.catch(() => void 0);
|
|
498
|
+
try {
|
|
499
|
+
await realService?.stop?.(serviceContext);
|
|
500
|
+
} finally {
|
|
501
|
+
unregisterOwnedRuntime(stoppingRuntime);
|
|
502
|
+
ownedRuntime = null;
|
|
503
|
+
realService = null;
|
|
504
|
+
startPromise = null;
|
|
505
|
+
}
|
|
506
|
+
})();
|
|
507
|
+
try {
|
|
508
|
+
await stopPromise;
|
|
509
|
+
} finally {
|
|
510
|
+
stopPromise = null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
};
|
|
325
514
|
api.registerService(service);
|
|
326
515
|
const currentConfig = () => api.runtime.config.current().plugins?.entries?.acpx?.config;
|
|
327
516
|
const registry = createAgentRegistry();
|
|
@@ -334,11 +523,7 @@ const plugin = {
|
|
|
334
523
|
isEnabled: () => isAcpxNativeAgentEnabled(currentConfig()?.nativeAgents, agentId),
|
|
335
524
|
api,
|
|
336
525
|
getRuntime: service.getRuntime,
|
|
337
|
-
shutdown: () => service.stop?.(
|
|
338
|
-
config: api.config,
|
|
339
|
-
stateDir: api.runtime.state.resolveStateDir(),
|
|
340
|
-
logger: api.logger
|
|
341
|
-
})
|
|
526
|
+
shutdown: () => stopPromise ?? (ctx ? service.stop?.(ctx) : void 0)
|
|
342
527
|
}));
|
|
343
528
|
return {
|
|
344
529
|
id: agentId,
|
package/openclaw.plugin.json
CHANGED
|
@@ -36,7 +36,16 @@
|
|
|
36
36
|
"id": "agent-runtime",
|
|
37
37
|
"title": "Agent runtime",
|
|
38
38
|
"order": 10,
|
|
39
|
-
"properties": [
|
|
39
|
+
"properties": [
|
|
40
|
+
"cwd",
|
|
41
|
+
"stateDir",
|
|
42
|
+
"probeAgent",
|
|
43
|
+
"agents",
|
|
44
|
+
"timeoutSeconds",
|
|
45
|
+
"nativeAgents",
|
|
46
|
+
"agentProcessEnvAllow",
|
|
47
|
+
"agentProcessEnvBlock"
|
|
48
|
+
]
|
|
40
49
|
},
|
|
41
50
|
{
|
|
42
51
|
"id": "permissions",
|
|
@@ -92,6 +101,14 @@
|
|
|
92
101
|
"minimum": 0.001,
|
|
93
102
|
"default": 120
|
|
94
103
|
},
|
|
104
|
+
"agentProcessEnvAllow": {
|
|
105
|
+
"type": "array",
|
|
106
|
+
"items": { "type": "string", "pattern": "^[^=\\s]+$" }
|
|
107
|
+
},
|
|
108
|
+
"agentProcessEnvBlock": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": { "type": "string", "pattern": "^[^=\\s]+$" }
|
|
111
|
+
},
|
|
95
112
|
"piSessionCatalog": {
|
|
96
113
|
"type": "object",
|
|
97
114
|
"additionalProperties": false,
|
|
@@ -189,6 +206,16 @@
|
|
|
189
206
|
"help": "Timeout for embedded ACP runtime startup and control operations. ACP turns use OpenClaw agent/run timeouts.",
|
|
190
207
|
"advanced": true
|
|
191
208
|
},
|
|
209
|
+
"agentProcessEnvAllow": {
|
|
210
|
+
"label": "Agent Environment Allowlist",
|
|
211
|
+
"help": "Default unset (agents inherit the Gateway environment). When set, ACP agents, their command probes, and terminals inherit only process essentials (PATH, HOME, locale, temp, XDG_*), these names, and ACPX_AUTH_* grants.",
|
|
212
|
+
"advanced": true
|
|
213
|
+
},
|
|
214
|
+
"agentProcessEnvBlock": {
|
|
215
|
+
"label": "Agent Environment Blocklist",
|
|
216
|
+
"help": "Default unset. Gateway environment variable names that ACP agents, their command probes, and terminals do not inherit, for example OPENCLAW_GATEWAY_TOKEN. Applied after the allowlist; explicit grants such as ACPX_AUTH_* still apply.",
|
|
217
|
+
"advanced": true
|
|
218
|
+
},
|
|
192
219
|
"piSessionCatalog.enabled": {
|
|
193
220
|
"label": "Pi Session Catalog",
|
|
194
221
|
"help": "Auto-detect Pi sessions on the Gateway and paired nodes, then show them in the sessions sidebar."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/acpx",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.11.5-beta.1",
|
|
4
4
|
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@agentclientprotocol/claude-agent-acp": "0.95.0",
|
|
12
|
-
"@agentclientprotocol/codex-acp": "2.
|
|
13
|
-
"@openclaw/fs-safe": "0.26.
|
|
14
|
-
"acpx": "0.
|
|
15
|
-
"smol-toml": "1.9.
|
|
16
|
-
"zod": "4.6.
|
|
12
|
+
"@agentclientprotocol/codex-acp": "2.2.2",
|
|
13
|
+
"@openclaw/fs-safe": "0.26.13",
|
|
14
|
+
"acpx": "0.21.0",
|
|
15
|
+
"smol-toml": "1.9.1",
|
|
16
|
+
"zod": "4.6.7"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@openclaw/plugin-sdk": "workspace:*"
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"compat": {
|
|
48
|
-
"pluginApi": ">=2026.
|
|
48
|
+
"pluginApi": ">=2026.11.5-beta.1"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.
|
|
51
|
+
"openclawVersion": "2026.11.5-beta.1"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"skills/**"
|
|
69
69
|
],
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"openclaw": ">=2026.
|
|
71
|
+
"openclaw": ">=2026.11.5-beta.1"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
74
74
|
"openclaw": {
|