@openclaw/acpx 2026.5.2-beta.1 → 2026.5.3-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/index.js +14 -0
- package/dist/register.runtime.js +108 -0
- package/dist/runtime-B3LWev_t.js +357 -0
- package/dist/runtime-api.js +4 -0
- package/dist/service-YhcC786_.js +665 -0
- package/dist/setup-api.js +16 -0
- package/package.json +33 -3
- package/AGENTS.md +0 -54
- package/index.test.ts +0 -119
- package/index.ts +0 -19
- package/register.runtime.ts +0 -154
- package/runtime-api.ts +0 -46
- package/setup-api.ts +0 -18
- package/src/acpx-runtime-compat.d.ts +0 -62
- package/src/claude-agent-acp-completion.test.ts +0 -129
- package/src/codex-auth-bridge.test.ts +0 -448
- package/src/codex-auth-bridge.ts +0 -385
- package/src/config-schema.ts +0 -117
- package/src/config.test.ts +0 -144
- package/src/config.ts +0 -273
- package/src/manifest.test.ts +0 -20
- package/src/runtime-internals/mcp-command-line.test.ts +0 -59
- package/src/runtime-internals/mcp-proxy.test.ts +0 -114
- package/src/runtime.test.ts +0 -816
- package/src/runtime.ts +0 -613
- package/src/service.test.ts +0 -401
- package/src/service.ts +0 -278
- package/tsconfig.json +0 -16
- /package/{src/runtime-internals → dist}/error-format.mjs +0 -0
- /package/{src/runtime-internals → dist}/mcp-command-line.mjs +0 -0
- /package/{src/runtime-internals → dist}/mcp-proxy.mjs +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/acpx",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.3-beta.1",
|
|
4
4
|
"description": "OpenClaw ACP runtime backend",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,14 +25,44 @@
|
|
|
25
25
|
"minHostVersion": ">=2026.4.25"
|
|
26
26
|
},
|
|
27
27
|
"compat": {
|
|
28
|
-
"pluginApi": ">=2026.5.
|
|
28
|
+
"pluginApi": ">=2026.5.3-beta.1"
|
|
29
29
|
},
|
|
30
30
|
"build": {
|
|
31
|
-
"openclawVersion": "2026.5.
|
|
31
|
+
"openclawVersion": "2026.5.3-beta.1",
|
|
32
|
+
"staticAssets": [
|
|
33
|
+
{
|
|
34
|
+
"source": "./src/runtime-internals/mcp-proxy.mjs",
|
|
35
|
+
"output": "mcp-proxy.mjs"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"source": "./src/runtime-internals/error-format.mjs",
|
|
39
|
+
"output": "error-format.mjs"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"source": "./src/runtime-internals/mcp-command-line.mjs",
|
|
43
|
+
"output": "mcp-command-line.mjs"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
32
46
|
},
|
|
33
47
|
"release": {
|
|
34
48
|
"publishToClawHub": true,
|
|
35
49
|
"publishToNpm": true
|
|
50
|
+
},
|
|
51
|
+
"runtimeExtensions": [
|
|
52
|
+
"./dist/index.js"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"dist/**",
|
|
57
|
+
"openclaw.plugin.json",
|
|
58
|
+
"skills/**"
|
|
59
|
+
],
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"openclaw": ">=2026.5.3-beta.1"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"openclaw": {
|
|
65
|
+
"optional": true
|
|
36
66
|
}
|
|
37
67
|
}
|
|
38
68
|
}
|
package/AGENTS.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# ACPX Extension Notes
|
|
2
|
-
|
|
3
|
-
This file applies to work under `extensions/acpx/`.
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
The ACPX extension is a thin OpenClaw wrapper around the published `acpx` package. Keep reusable ACP runtime logic in `openclaw/acpx`, not in this extension.
|
|
8
|
-
|
|
9
|
-
## Default Version Policy
|
|
10
|
-
|
|
11
|
-
- `extensions/acpx/package.json` should point at a published npm release by default.
|
|
12
|
-
- Do not leave the extension pinned to a temporary GitHub commit or local checkout once the ACPX release exists.
|
|
13
|
-
- Do not leave temporary pnpm build-script allowlist exceptions behind after switching back to a published ACPX package.
|
|
14
|
-
|
|
15
|
-
## Unreleased ACPX Development Flow
|
|
16
|
-
|
|
17
|
-
Use this flow when OpenClaw needs unreleased ACPX changes before the ACPX version is published.
|
|
18
|
-
|
|
19
|
-
1. Make the ACPX code change in the `openclaw/acpx` repo first.
|
|
20
|
-
2. In OpenClaw, temporarily point `extensions/acpx/package.json` at the ACPX GitHub commit you need.
|
|
21
|
-
3. If pnpm blocks ACPX lifecycle/build scripts for that temporary GitHub-sourced package, temporarily add `acpx` to `onlyBuiltDependencies` in both `package.json` and `pnpm-workspace.yaml`.
|
|
22
|
-
4. Refresh the root workspace lock:
|
|
23
|
-
- `pnpm install --lockfile-only --filter ./extensions/acpx`
|
|
24
|
-
5. Refresh the extension-local npm lock for install metadata:
|
|
25
|
-
- `cd extensions/acpx && npm install --package-lock-only --ignore-scripts`
|
|
26
|
-
6. Rebuild OpenClaw and restart the gateway before doing live ACP validation.
|
|
27
|
-
7. Once ACPX is released, switch `extensions/acpx/package.json` back to the published npm version and refresh the same lockfiles again.
|
|
28
|
-
8. Remove any temporary `acpx` build-script allowlist entries that were only needed for the GitHub-sourced development pin.
|
|
29
|
-
|
|
30
|
-
## Lockfile Notes
|
|
31
|
-
|
|
32
|
-
- `pnpm-lock.yaml` is the tracked workspace lockfile and must match the ACPX version referenced by `extensions/acpx/package.json`.
|
|
33
|
-
- `extensions/acpx/package-lock.json` is useful local install metadata for the plugin package.
|
|
34
|
-
- If `extensions/acpx/package-lock.json` is gitignored in this repo state, regenerating it is still useful for local verification, but it will not appear in `git status`.
|
|
35
|
-
|
|
36
|
-
## Local Runtime Validation
|
|
37
|
-
|
|
38
|
-
When ACPX integration changes here, prefer this sequence:
|
|
39
|
-
|
|
40
|
-
1. `pnpm install --filter ./extensions/acpx`
|
|
41
|
-
2. `pnpm test:extension acpx`
|
|
42
|
-
3. `pnpm build`
|
|
43
|
-
4. Restart the local gateway if ACP runtime behavior or bundled plugin wiring changed.
|
|
44
|
-
5. If the change affects direct ACP behavior in chat, run a real ACP smoke after restart.
|
|
45
|
-
|
|
46
|
-
## Direct ACPX Binary Policy
|
|
47
|
-
|
|
48
|
-
- Prefer the plugin-local ACPX binary under `extensions/acpx/node_modules/.bin/acpx`.
|
|
49
|
-
- Do not rely on a globally installed `acpx` binary for OpenClaw ACP validation.
|
|
50
|
-
- If the plugin-local ACPX binary is missing or on the wrong version, reinstall it from the version pinned in `extensions/acpx/package.json`.
|
|
51
|
-
|
|
52
|
-
## Boundary Rule
|
|
53
|
-
|
|
54
|
-
If a change feels like shared ACP runtime behavior instead of OpenClaw-specific glue, move it to `openclaw/acpx` and consume it from here instead of re-implementing it inside `extensions/acpx`.
|
package/index.test.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
-
import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
|
|
3
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
import setupPlugin from "./setup-api.js";
|
|
5
|
-
|
|
6
|
-
const { createAcpxRuntimeServiceMock, tryDispatchAcpReplyHookMock } = vi.hoisted(() => ({
|
|
7
|
-
createAcpxRuntimeServiceMock: vi.fn(),
|
|
8
|
-
tryDispatchAcpReplyHookMock: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
vi.mock("./register.runtime.js", () => ({
|
|
12
|
-
createAcpxRuntimeService: createAcpxRuntimeServiceMock,
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
vi.mock("openclaw/plugin-sdk/acp-runtime-backend", () => ({
|
|
16
|
-
tryDispatchAcpReplyHook: tryDispatchAcpReplyHookMock,
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
import plugin from "./index.js";
|
|
20
|
-
|
|
21
|
-
type AcpxAutoEnableProbe = Parameters<OpenClawPluginApi["registerAutoEnableProbe"]>[0];
|
|
22
|
-
|
|
23
|
-
function registerAcpxAutoEnableProbe(): AcpxAutoEnableProbe {
|
|
24
|
-
const probes: AcpxAutoEnableProbe[] = [];
|
|
25
|
-
setupPlugin.register(
|
|
26
|
-
createTestPluginApi({
|
|
27
|
-
registerAutoEnableProbe(probe) {
|
|
28
|
-
probes.push(probe);
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
32
|
-
const probe = probes[0];
|
|
33
|
-
if (!probe) {
|
|
34
|
-
throw new Error("expected ACPX setup plugin to register an auto-enable probe");
|
|
35
|
-
}
|
|
36
|
-
return probe;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe("acpx plugin", () => {
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("registers the runtime service and reply_dispatch hook", () => {
|
|
45
|
-
const service = { id: "acpx-service", start: vi.fn() };
|
|
46
|
-
createAcpxRuntimeServiceMock.mockReturnValue(service);
|
|
47
|
-
|
|
48
|
-
const api = {
|
|
49
|
-
pluginConfig: { stateDir: "/tmp/acpx" },
|
|
50
|
-
registerService: vi.fn(),
|
|
51
|
-
on: vi.fn(),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
plugin.register(api as never);
|
|
55
|
-
|
|
56
|
-
expect(createAcpxRuntimeServiceMock).toHaveBeenCalledWith({
|
|
57
|
-
pluginConfig: api.pluginConfig,
|
|
58
|
-
});
|
|
59
|
-
expect(api.registerService).toHaveBeenCalledWith(service);
|
|
60
|
-
expect(api.on).toHaveBeenCalledWith("reply_dispatch", tryDispatchAcpReplyHookMock);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("preserves the ACP reply_dispatch runtime path through the registered hook", async () => {
|
|
64
|
-
const service = { id: "acpx-service", start: vi.fn() };
|
|
65
|
-
createAcpxRuntimeServiceMock.mockReturnValue(service);
|
|
66
|
-
tryDispatchAcpReplyHookMock.mockResolvedValue({
|
|
67
|
-
handled: true,
|
|
68
|
-
queuedFinal: true,
|
|
69
|
-
counts: { tool: 1, block: 0, final: 1 },
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const on = vi.fn();
|
|
73
|
-
const api = createTestPluginApi({
|
|
74
|
-
pluginConfig: { stateDir: "/tmp/acpx" },
|
|
75
|
-
registerService: vi.fn(),
|
|
76
|
-
on,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
plugin.register(api);
|
|
80
|
-
|
|
81
|
-
const hook = on.mock.calls.find(([hookName]) => hookName === "reply_dispatch")?.[1];
|
|
82
|
-
if (!hook) {
|
|
83
|
-
throw new Error("expected reply_dispatch hook to be registered");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const event = {
|
|
87
|
-
ctx: { raw: "reply ctx" },
|
|
88
|
-
runId: "run-1",
|
|
89
|
-
sessionKey: "agent:test:session",
|
|
90
|
-
inboundAudio: false,
|
|
91
|
-
shouldRouteToOriginating: false,
|
|
92
|
-
shouldSendToolSummaries: true,
|
|
93
|
-
sendPolicy: "allow",
|
|
94
|
-
};
|
|
95
|
-
const ctx = {
|
|
96
|
-
cfg: {},
|
|
97
|
-
dispatcher: { dispatch: vi.fn(), getQueuedCounts: vi.fn(), getFailedCounts: vi.fn() },
|
|
98
|
-
recordProcessed: vi.fn(),
|
|
99
|
-
markIdle: vi.fn(),
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
await expect(hook(event, ctx)).resolves.toEqual({
|
|
103
|
-
handled: true,
|
|
104
|
-
queuedFinal: true,
|
|
105
|
-
counts: { tool: 1, block: 0, final: 1 },
|
|
106
|
-
});
|
|
107
|
-
expect(tryDispatchAcpReplyHookMock).toHaveBeenCalledWith(event, ctx);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("declares setup auto-enable reasons for ACPX-owned ACP config", () => {
|
|
111
|
-
const probe = registerAcpxAutoEnableProbe();
|
|
112
|
-
|
|
113
|
-
expect(probe({ config: { acp: { enabled: true } }, env: {} })).toBe("ACP runtime configured");
|
|
114
|
-
expect(probe({ config: { acp: { backend: "acpx" } }, env: {} })).toBe("ACP runtime configured");
|
|
115
|
-
expect(probe({ config: { acp: { enabled: true, backend: "custom-runtime" } }, env: {} })).toBe(
|
|
116
|
-
null,
|
|
117
|
-
);
|
|
118
|
-
});
|
|
119
|
-
});
|
package/index.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { tryDispatchAcpReplyHook } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
2
|
-
import { createAcpxRuntimeService } from "./register.runtime.js";
|
|
3
|
-
import type { OpenClawPluginApi } from "./runtime-api.js";
|
|
4
|
-
|
|
5
|
-
const plugin = {
|
|
6
|
-
id: "acpx",
|
|
7
|
-
name: "ACPX Runtime",
|
|
8
|
-
description: "Embedded ACP runtime backend with plugin-owned session and transport management.",
|
|
9
|
-
register(api: OpenClawPluginApi) {
|
|
10
|
-
api.registerService(
|
|
11
|
-
createAcpxRuntimeService({
|
|
12
|
-
pluginConfig: api.pluginConfig,
|
|
13
|
-
}),
|
|
14
|
-
);
|
|
15
|
-
api.on("reply_dispatch", tryDispatchAcpReplyHook);
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export default plugin;
|
package/register.runtime.ts
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAcpRuntimeBackend,
|
|
3
|
-
registerAcpRuntimeBackend,
|
|
4
|
-
unregisterAcpRuntimeBackend,
|
|
5
|
-
type AcpRuntime,
|
|
6
|
-
type AcpRuntimeCapabilities,
|
|
7
|
-
type AcpRuntimeDoctorReport,
|
|
8
|
-
type AcpRuntimeStatus,
|
|
9
|
-
} from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
10
|
-
import type { OpenClawPluginService, OpenClawPluginServiceContext } from "openclaw/plugin-sdk/core";
|
|
11
|
-
|
|
12
|
-
const ACPX_BACKEND_ID = "acpx";
|
|
13
|
-
const ENABLE_STARTUP_PROBE_ENV = "OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE";
|
|
14
|
-
|
|
15
|
-
type RealAcpxServiceModule = typeof import("./src/service.js");
|
|
16
|
-
type CreateAcpxRuntimeServiceParams = NonNullable<
|
|
17
|
-
Parameters<RealAcpxServiceModule["createAcpxRuntimeService"]>[0]
|
|
18
|
-
>;
|
|
19
|
-
|
|
20
|
-
type AcpxRuntimeLike = AcpRuntime & {
|
|
21
|
-
probeAvailability(): Promise<void>;
|
|
22
|
-
doctor?(): Promise<AcpRuntimeDoctorReport>;
|
|
23
|
-
isHealthy(): boolean;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type DeferredServiceState = {
|
|
27
|
-
ctx: OpenClawPluginServiceContext | null;
|
|
28
|
-
params: CreateAcpxRuntimeServiceParams;
|
|
29
|
-
realRuntime: AcpxRuntimeLike | null;
|
|
30
|
-
realService: OpenClawPluginService | null;
|
|
31
|
-
startPromise: Promise<AcpxRuntimeLike> | null;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
let serviceModulePromise: Promise<RealAcpxServiceModule> | null = null;
|
|
35
|
-
|
|
36
|
-
function loadServiceModule(): Promise<RealAcpxServiceModule> {
|
|
37
|
-
serviceModulePromise ??= import("./src/service.js");
|
|
38
|
-
return serviceModulePromise;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function shouldRunStartupProbe(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
42
|
-
return env[ENABLE_STARTUP_PROBE_ENV] === "1";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function startRealService(state: DeferredServiceState): Promise<AcpxRuntimeLike> {
|
|
46
|
-
if (state.realRuntime) {
|
|
47
|
-
return state.realRuntime;
|
|
48
|
-
}
|
|
49
|
-
if (!state.ctx) {
|
|
50
|
-
throw new Error("ACPX runtime service is not started");
|
|
51
|
-
}
|
|
52
|
-
state.startPromise ??= (async () => {
|
|
53
|
-
const { createAcpxRuntimeService } = await loadServiceModule();
|
|
54
|
-
const service = createAcpxRuntimeService(state.params);
|
|
55
|
-
state.realService = service;
|
|
56
|
-
await service.start(state.ctx as OpenClawPluginServiceContext);
|
|
57
|
-
const backend = getAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
58
|
-
if (!backend?.runtime) {
|
|
59
|
-
throw new Error("ACPX runtime service did not register an ACP backend");
|
|
60
|
-
}
|
|
61
|
-
state.realRuntime = backend.runtime as AcpxRuntimeLike;
|
|
62
|
-
return state.realRuntime;
|
|
63
|
-
})();
|
|
64
|
-
return await state.startPromise;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function createDeferredRuntime(state: DeferredServiceState): AcpxRuntimeLike {
|
|
68
|
-
return {
|
|
69
|
-
async ensureSession(input) {
|
|
70
|
-
return await (await startRealService(state)).ensureSession(input);
|
|
71
|
-
},
|
|
72
|
-
async *runTurn(input) {
|
|
73
|
-
yield* (await startRealService(state)).runTurn(input);
|
|
74
|
-
},
|
|
75
|
-
async getCapabilities(input): Promise<AcpRuntimeCapabilities> {
|
|
76
|
-
const runtime = await startRealService(state);
|
|
77
|
-
return (await runtime.getCapabilities?.(input)) ?? { controls: [] };
|
|
78
|
-
},
|
|
79
|
-
async getStatus(input): Promise<AcpRuntimeStatus> {
|
|
80
|
-
const runtime = await startRealService(state);
|
|
81
|
-
return (await runtime.getStatus?.(input)) ?? {};
|
|
82
|
-
},
|
|
83
|
-
async setMode(input) {
|
|
84
|
-
await (await startRealService(state)).setMode?.(input);
|
|
85
|
-
},
|
|
86
|
-
async setConfigOption(input) {
|
|
87
|
-
await (await startRealService(state)).setConfigOption?.(input);
|
|
88
|
-
},
|
|
89
|
-
async doctor(): Promise<AcpRuntimeDoctorReport> {
|
|
90
|
-
const runtime = await startRealService(state);
|
|
91
|
-
return (await runtime.doctor?.()) ?? { ok: true, message: "ok" };
|
|
92
|
-
},
|
|
93
|
-
async prepareFreshSession(input) {
|
|
94
|
-
await (await startRealService(state)).prepareFreshSession?.(input);
|
|
95
|
-
},
|
|
96
|
-
async cancel(input) {
|
|
97
|
-
await (await startRealService(state)).cancel(input);
|
|
98
|
-
},
|
|
99
|
-
async close(input) {
|
|
100
|
-
await (await startRealService(state)).close(input);
|
|
101
|
-
},
|
|
102
|
-
async probeAvailability() {
|
|
103
|
-
await (await startRealService(state)).probeAvailability();
|
|
104
|
-
},
|
|
105
|
-
isHealthy() {
|
|
106
|
-
return state.realRuntime?.isHealthy() ?? false;
|
|
107
|
-
},
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function createAcpxRuntimeService(
|
|
112
|
-
params: CreateAcpxRuntimeServiceParams = {},
|
|
113
|
-
): OpenClawPluginService {
|
|
114
|
-
const state: DeferredServiceState = {
|
|
115
|
-
ctx: null,
|
|
116
|
-
params,
|
|
117
|
-
realRuntime: null,
|
|
118
|
-
realService: null,
|
|
119
|
-
startPromise: null,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
id: "acpx-runtime",
|
|
124
|
-
async start(ctx) {
|
|
125
|
-
if (process.env.OPENCLAW_SKIP_ACPX_RUNTIME === "1") {
|
|
126
|
-
ctx.logger.info("skipping embedded acpx runtime backend (OPENCLAW_SKIP_ACPX_RUNTIME=1)");
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
state.ctx = ctx;
|
|
131
|
-
if (shouldRunStartupProbe()) {
|
|
132
|
-
await startRealService(state);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
registerAcpRuntimeBackend({
|
|
137
|
-
id: ACPX_BACKEND_ID,
|
|
138
|
-
runtime: createDeferredRuntime(state),
|
|
139
|
-
});
|
|
140
|
-
ctx.logger.info("embedded acpx runtime backend registered lazily");
|
|
141
|
-
},
|
|
142
|
-
async stop(ctx) {
|
|
143
|
-
if (state.realService) {
|
|
144
|
-
await state.realService.stop?.(ctx);
|
|
145
|
-
} else {
|
|
146
|
-
unregisterAcpRuntimeBackend(ACPX_BACKEND_ID);
|
|
147
|
-
}
|
|
148
|
-
state.ctx = null;
|
|
149
|
-
state.realRuntime = null;
|
|
150
|
-
state.realService = null;
|
|
151
|
-
state.startPromise = null;
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
}
|
package/runtime-api.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export type { AcpRuntimeErrorCode } from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
2
|
-
export {
|
|
3
|
-
AcpRuntimeError,
|
|
4
|
-
getAcpRuntimeBackend,
|
|
5
|
-
tryDispatchAcpReplyHook,
|
|
6
|
-
registerAcpRuntimeBackend,
|
|
7
|
-
unregisterAcpRuntimeBackend,
|
|
8
|
-
} from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
9
|
-
export type {
|
|
10
|
-
AcpRuntime,
|
|
11
|
-
AcpRuntimeCapabilities,
|
|
12
|
-
AcpRuntimeDoctorReport,
|
|
13
|
-
AcpRuntimeEnsureInput,
|
|
14
|
-
AcpRuntimeEvent,
|
|
15
|
-
AcpRuntimeHandle,
|
|
16
|
-
AcpRuntimeStatus,
|
|
17
|
-
AcpRuntimeTurnAttachment,
|
|
18
|
-
AcpRuntimeTurnInput,
|
|
19
|
-
AcpSessionUpdateTag,
|
|
20
|
-
} from "openclaw/plugin-sdk/acp-runtime-backend";
|
|
21
|
-
export type {
|
|
22
|
-
OpenClawPluginApi,
|
|
23
|
-
OpenClawPluginConfigSchema,
|
|
24
|
-
OpenClawPluginService,
|
|
25
|
-
OpenClawPluginServiceContext,
|
|
26
|
-
PluginLogger,
|
|
27
|
-
} from "openclaw/plugin-sdk/core";
|
|
28
|
-
export type {
|
|
29
|
-
PluginHookReplyDispatchContext,
|
|
30
|
-
PluginHookReplyDispatchEvent,
|
|
31
|
-
PluginHookReplyDispatchResult,
|
|
32
|
-
} from "openclaw/plugin-sdk/core";
|
|
33
|
-
export type {
|
|
34
|
-
WindowsSpawnProgram,
|
|
35
|
-
WindowsSpawnProgramCandidate,
|
|
36
|
-
WindowsSpawnResolution,
|
|
37
|
-
} from "openclaw/plugin-sdk/windows-spawn";
|
|
38
|
-
export {
|
|
39
|
-
applyWindowsSpawnProgramPolicy,
|
|
40
|
-
materializeWindowsSpawnProgram,
|
|
41
|
-
resolveWindowsSpawnProgramCandidate,
|
|
42
|
-
} from "openclaw/plugin-sdk/windows-spawn";
|
|
43
|
-
export {
|
|
44
|
-
listKnownProviderAuthEnvVarNames,
|
|
45
|
-
omitEnvKeysCaseInsensitive,
|
|
46
|
-
} from "openclaw/plugin-sdk/provider-env-vars";
|
package/setup-api.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
3
|
-
|
|
4
|
-
export default definePluginEntry({
|
|
5
|
-
id: "acpx",
|
|
6
|
-
name: "ACPX Setup",
|
|
7
|
-
description: "Lightweight ACPX setup hooks",
|
|
8
|
-
register(api) {
|
|
9
|
-
api.registerAutoEnableProbe(({ config }) => {
|
|
10
|
-
const backendRaw = normalizeLowercaseStringOrEmpty(config.acp?.backend);
|
|
11
|
-
const configured =
|
|
12
|
-
config.acp?.enabled === true ||
|
|
13
|
-
config.acp?.dispatch?.enabled === true ||
|
|
14
|
-
backendRaw === "acpx";
|
|
15
|
-
return configured && (!backendRaw || backendRaw === "acpx") ? "ACP runtime configured" : null;
|
|
16
|
-
});
|
|
17
|
-
},
|
|
18
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
declare module "acpx/runtime" {
|
|
2
|
-
export const ACPX_BACKEND_ID: string;
|
|
3
|
-
|
|
4
|
-
export type AcpRuntimeDoctorReport = import("../runtime-api.js").AcpRuntimeDoctorReport;
|
|
5
|
-
export type AcpRuntimeEnsureInput = import("../runtime-api.js").AcpRuntimeEnsureInput;
|
|
6
|
-
export type AcpRuntimeEvent = import("../runtime-api.js").AcpRuntimeEvent;
|
|
7
|
-
export type AcpRuntimeHandle = import("../runtime-api.js").AcpRuntimeHandle;
|
|
8
|
-
export type AcpRuntimeCapabilities = import("../runtime-api.js").AcpRuntimeCapabilities;
|
|
9
|
-
export type AcpRuntimeStatus = import("../runtime-api.js").AcpRuntimeStatus;
|
|
10
|
-
export type AcpRuntimeTurnInput = import("../runtime-api.js").AcpRuntimeTurnInput;
|
|
11
|
-
|
|
12
|
-
export type AcpAgentRegistry = {
|
|
13
|
-
resolve(agent: string): string | undefined;
|
|
14
|
-
list(): string[];
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type AcpSessionRecord = Record<string, unknown>;
|
|
18
|
-
|
|
19
|
-
export type AcpSessionStore = {
|
|
20
|
-
load(sessionId: string): Promise<AcpSessionRecord | undefined>;
|
|
21
|
-
save(record: AcpSessionRecord): Promise<void>;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type AcpRuntimeOptions = {
|
|
25
|
-
cwd: string;
|
|
26
|
-
sessionStore: AcpSessionStore;
|
|
27
|
-
agentRegistry: AcpAgentRegistry;
|
|
28
|
-
probeAgent?: string;
|
|
29
|
-
mcpServers?: unknown;
|
|
30
|
-
permissionMode?: unknown;
|
|
31
|
-
nonInteractivePermissions?: unknown;
|
|
32
|
-
timeoutMs?: number;
|
|
33
|
-
probeAgent?: string;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export class AcpxRuntime {
|
|
37
|
-
constructor(options: AcpRuntimeOptions, testOptions?: unknown);
|
|
38
|
-
isHealthy(): boolean;
|
|
39
|
-
probeAvailability(): Promise<void>;
|
|
40
|
-
doctor(): Promise<AcpRuntimeDoctorReport>;
|
|
41
|
-
ensureSession(input: AcpRuntimeEnsureInput): Promise<AcpRuntimeHandle>;
|
|
42
|
-
runTurn(input: AcpRuntimeTurnInput): AsyncIterable<AcpRuntimeEvent>;
|
|
43
|
-
getCapabilities(input?: {
|
|
44
|
-
handle?: AcpRuntimeHandle;
|
|
45
|
-
}): AcpRuntimeCapabilities | Promise<AcpRuntimeCapabilities>;
|
|
46
|
-
getStatus(input: { handle: AcpRuntimeHandle; signal?: AbortSignal }): Promise<AcpRuntimeStatus>;
|
|
47
|
-
setMode(input: { handle: AcpRuntimeHandle; mode: string }): Promise<void>;
|
|
48
|
-
setConfigOption(input: { handle: AcpRuntimeHandle; key: string; value: string }): Promise<void>;
|
|
49
|
-
cancel(input: { handle: AcpRuntimeHandle; reason?: string }): Promise<void>;
|
|
50
|
-
close(input: {
|
|
51
|
-
handle: AcpRuntimeHandle;
|
|
52
|
-
reason?: string;
|
|
53
|
-
discardPersistentState?: boolean;
|
|
54
|
-
}): Promise<void>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function createAcpRuntime(...args: unknown[]): AcpxRuntime;
|
|
58
|
-
export function createAgentRegistry(params: { overrides?: unknown }): AcpAgentRegistry;
|
|
59
|
-
export function createFileSessionStore(params: { stateDir: string }): AcpSessionStore;
|
|
60
|
-
export function decodeAcpxRuntimeHandleState(...args: unknown[]): unknown;
|
|
61
|
-
export function encodeAcpxRuntimeHandleState(...args: unknown[]): unknown;
|
|
62
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { ClaudeAcpAgent } from "@agentclientprotocol/claude-agent-acp";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
type IteratorResultResolver = (value: IteratorResult<unknown>) => void;
|
|
5
|
-
|
|
6
|
-
class ManualAsyncIterator implements AsyncIterator<unknown> {
|
|
7
|
-
private readonly pending: IteratorResultResolver[] = [];
|
|
8
|
-
private readonly queued: IteratorResult<unknown>[] = [];
|
|
9
|
-
|
|
10
|
-
next(): Promise<IteratorResult<unknown>> {
|
|
11
|
-
const next = this.queued.shift();
|
|
12
|
-
if (next) {
|
|
13
|
-
return Promise.resolve(next);
|
|
14
|
-
}
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
this.pending.push(resolve);
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
push(value: unknown): void {
|
|
21
|
-
this.resolve({ value, done: false });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
end(): void {
|
|
25
|
-
this.resolve({ value: undefined, done: true });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private resolve(value: IteratorResult<unknown>): void {
|
|
29
|
-
const pending = this.pending.shift();
|
|
30
|
-
if (pending) {
|
|
31
|
-
pending(value);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
this.queued.push(value);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function makeResultMessage() {
|
|
39
|
-
return {
|
|
40
|
-
type: "result",
|
|
41
|
-
subtype: "success",
|
|
42
|
-
is_error: false,
|
|
43
|
-
result: "finished",
|
|
44
|
-
stop_reason: null,
|
|
45
|
-
total_cost_usd: 0,
|
|
46
|
-
usage: {
|
|
47
|
-
input_tokens: 1,
|
|
48
|
-
output_tokens: 1,
|
|
49
|
-
cache_read_input_tokens: 0,
|
|
50
|
-
cache_creation_input_tokens: 0,
|
|
51
|
-
},
|
|
52
|
-
modelUsage: [],
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function makeIdleMessage() {
|
|
57
|
-
return {
|
|
58
|
-
type: "system",
|
|
59
|
-
subtype: "session_state_changed",
|
|
60
|
-
state: "idle",
|
|
61
|
-
session_id: "session-1",
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function flushMicrotasks(): Promise<void> {
|
|
66
|
-
await Promise.resolve();
|
|
67
|
-
await Promise.resolve();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
describe("patched claude-agent-acp completion", () => {
|
|
71
|
-
it("does not resolve a prompt on idle before the result message", async () => {
|
|
72
|
-
const query = new ManualAsyncIterator();
|
|
73
|
-
const agent = new ClaudeAcpAgent({
|
|
74
|
-
sessionUpdate: vi.fn(),
|
|
75
|
-
extNotification: vi.fn(),
|
|
76
|
-
} as unknown as ConstructorParameters<typeof ClaudeAcpAgent>[0]);
|
|
77
|
-
agent.sessions["session-1"] = {
|
|
78
|
-
cancelled: false,
|
|
79
|
-
accumulatedUsage: {
|
|
80
|
-
inputTokens: 0,
|
|
81
|
-
outputTokens: 0,
|
|
82
|
-
cachedReadTokens: 0,
|
|
83
|
-
cachedWriteTokens: 0,
|
|
84
|
-
},
|
|
85
|
-
contextWindowSize: 200_000,
|
|
86
|
-
cwd: "/tmp",
|
|
87
|
-
emitRawSDKMessages: false,
|
|
88
|
-
input: {
|
|
89
|
-
push: vi.fn(),
|
|
90
|
-
end: vi.fn(),
|
|
91
|
-
},
|
|
92
|
-
nextPendingOrder: 0,
|
|
93
|
-
pendingMessages: new Map(),
|
|
94
|
-
promptRunning: false,
|
|
95
|
-
query,
|
|
96
|
-
settingsManager: {
|
|
97
|
-
dispose: vi.fn(),
|
|
98
|
-
},
|
|
99
|
-
} as unknown as (typeof agent.sessions)[string];
|
|
100
|
-
|
|
101
|
-
let resolved = false;
|
|
102
|
-
const promptPromise = agent
|
|
103
|
-
.prompt({
|
|
104
|
-
sessionId: "session-1",
|
|
105
|
-
prompt: [{ type: "text", text: "do work" }],
|
|
106
|
-
})
|
|
107
|
-
.then((value) => {
|
|
108
|
-
resolved = true;
|
|
109
|
-
return value;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
query.push(makeIdleMessage());
|
|
113
|
-
await flushMicrotasks();
|
|
114
|
-
expect(resolved).toBe(false);
|
|
115
|
-
|
|
116
|
-
query.push(makeResultMessage());
|
|
117
|
-
await flushMicrotasks();
|
|
118
|
-
expect(resolved).toBe(false);
|
|
119
|
-
|
|
120
|
-
query.push(makeIdleMessage());
|
|
121
|
-
await expect(promptPromise).resolves.toMatchObject({
|
|
122
|
-
stopReason: "end_turn",
|
|
123
|
-
usage: {
|
|
124
|
-
inputTokens: 1,
|
|
125
|
-
outputTokens: 1,
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
});
|