Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Repo: https://github.com/openclaw/acpx

### Changes

- Agents/built-ins: refresh the default Claude and Pi adapter ranges so fresh built-in launches pick up the current adapter releases, and keep sessions saved under the superseded launch commands on the current built-in launch path when their record carries no usable stored argv.

### Breaking

### Fixes
Expand Down
1 change: 1 addition & 0 deletions agents/Claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Default command: `npx -y @agentclientprotocol/claude-agent-acp`
- Upstream: https://github.com/agentclientprotocol/claude-agent-acp
- ACPX pins the built-in package range so fresh installs pick up Claude model and ACP adapter fixes without depending on a global adapter binary.
- When that range advances, a session persisted under the superseded built-in command is mapped back to the current built-in launch argv on reuse, but only when the record carries no usable stored `agent_argv`. A record with usable stored argv intentionally keeps that launcher.
- Model switches can change the available effort controls. ACPX updates existing saved effort selections from the accepted response and removes selections whose controls disappear.
- Saved model and config selections are restored after reconnect, without replacing the conversation.

Expand Down
25 changes: 25 additions & 0 deletions agents/Pi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Pi

- Built-in name: `pi`
- Default command: `npx pi-acp`
- Upstream: [mariozechner/pi](https://github.com/mariozechner/pi)
- Adapter: [svkozak/pi-acp](https://github.com/svkozak/pi-acp)
- ACPX owns the built-in package range so fresh launches use the repository-selected adapter line without depending on a globally installed adapter binary.
- When that range advances, a session persisted under the superseded built-in command is mapped back to the current built-in launch argv on reuse, but only when the record carries no usable stored `agent_argv`. A record with usable stored argv intentionally keeps that launcher.

`acpx pi` starts the Pi coding agent through the `pi-acp` ACP adapter. The adapter
drives an installed Pi CLI, so install and authenticate Pi first; the adapter README
states the minimum supported Pi version.

If the adapter needs a different resolution path or extra startup arguments, override
the built-in argv in `~/.acpx/config.json`:

```json
{
"agents": {
"pi": {
"argv": ["npx", "pi-acp"]
}
}
}
```
1 change: 1 addition & 0 deletions agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Built-in agents:

Harness-specific docs in this directory:

- [Pi](Pi.md): built-in `pi -> npx pi-acp` via an ACPX-owned npm range
- [Codex](Codex.md): built-in `codex -> npx -y @agentclientprotocol/codex-acp`
- [Claude](Claude.md): built-in `claude -> npx -y @agentclientprotocol/claude-agent-acp`
- [Gemini](Gemini.md): built-in `gemini -> gemini --acp`
Expand Down
5 changes: 4 additions & 1 deletion skills/acpx/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ If prompt text is omitted and stdin is piped, `acpx` reads prompt text from stdi

Friendly agent names resolve to commands:

- `pi` -> `npx pi-acp`
- `pi` -> `npx pi-acp` (ACPX-owned package range)
- `openclaw` -> `openclaw acp`
- `codex` -> `npx -y @agentclientprotocol/codex-acp` (ACPX-owned package range)
- `claude` -> `npx -y @agentclientprotocol/claude-agent-acp` (ACPX-owned package range)
Expand Down Expand Up @@ -110,6 +110,9 @@ Rules:
- Unknown positional agent tokens are treated as raw agent commands.
- `--agent <command>` explicitly sets a raw ACP adapter command.
- Do not combine a positional agent and `--agent` in the same command.
- When ACPX advances an ACPX-owned package range, a session persisted under the superseded
built-in command is mapped back to the current built-in launch argv on reuse only when the
record carries no usable stored `agent_argv`. A usable stored argv always wins.

## Commands

Expand Down
2 changes: 1 addition & 1 deletion src/acp/builtin-command-migration.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AGENT_ARGV_REGISTRY, AGENT_REGISTRY, BUILT_IN_AGENT_PACKAGES } from "../agent-registry.js";

const LEGACY_AGENT_COMMANDS: Record<string, string[]> = {
pi: ["npx pi-acp", "npx pi-acp@^0.0.22", "npx pi-acp@^0.0.26"],
pi: ["npx pi-acp", "npx pi-acp@^0.0.22", "npx pi-acp@^0.0.26", "npx pi-acp@^0.0.31"],
codex: [
"npx @zed-industries/codex-acp",
"npx @zed-industries/codex-acp@^0.9.5",
Expand Down
6 changes: 4 additions & 2 deletions src/agent-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import path from "node:path";
import { fileURLToPath } from "node:url";

const ACP_ADAPTER_PACKAGE_RANGES = {
pi: "^0.0.31",
pi: "^0.0.33",
codex: "^1.1.5",
claude: "^0.60.0",
claude: "^0.70.0",
mux: "^0.28.0",
} as const;

Expand Down Expand Up @@ -103,6 +103,8 @@ export const BUILT_IN_AGENT_PACKAGES = {
fallbackCommand: AGENT_REGISTRY.claude,
legacyFallbackCommands: [
`npm exec @agentclientprotocol/claude-agent-acp@${ACP_ADAPTER_PACKAGE_RANGES.claude}`,
"npx -y @agentclientprotocol/claude-agent-acp@^0.60.0",
"npm exec @agentclientprotocol/claude-agent-acp@^0.60.0",
],
},
} as const satisfies Record<string, BuiltInAgentPackageSpec>;
Expand Down
24 changes: 21 additions & 3 deletions test/agent-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ test("default agent is codex", () => {
});

test("claude built-in uses the current ACP adapter package range", () => {
assert.equal(BUILT_IN_AGENT_PACKAGES.claude.packageRange, "^0.60.0");
assert.equal(AGENT_REGISTRY.claude, "npx -y @agentclientprotocol/claude-agent-acp@^0.60.0");
assert.equal(BUILT_IN_AGENT_PACKAGES.claude.packageRange, "^0.70.0");
assert.equal(AGENT_REGISTRY.claude, "npx -y @agentclientprotocol/claude-agent-acp@^0.70.0");
});

test("npm-backed built-ins use current adapter package ranges", () => {
assert.equal(BUILT_IN_AGENT_PACKAGES.codex.packageRange, "^1.1.5");
assert.equal(AGENT_REGISTRY.codex, "npx -y @agentclientprotocol/codex-acp@^1.1.5");
assert.equal(AGENT_REGISTRY.pi, "npx pi-acp@^0.0.31");
assert.equal(AGENT_REGISTRY.pi, "npx pi-acp@^0.0.33");
});

test("resolveInstalledBuiltInAgentLaunch uses a locally installed adapter when available", (t) => {
Expand Down Expand Up @@ -177,6 +177,24 @@ test("resolveInstalledBuiltInAgentLaunch ignores non-built-in commands", () => {
assert.equal(resolveInstalledBuiltInAgentLaunch("custom-acp-server --stdio"), undefined);
});

test("package-exec resolution recognizes the retired Claude built-in commands", () => {
const npmCliPath = path.join(os.tmpdir(), "acpx-test-npm-cli.js");
for (const retiredCommand of [
"npx -y @agentclientprotocol/claude-agent-acp@^0.60.0",
"npm exec @agentclientprotocol/claude-agent-acp@^0.60.0",
]) {
const launch = resolvePackageExecBuiltInAgentLaunch(retiredCommand, {
execPath: "/tmp/node",
existsSync: (candidate) => candidate === npmCliPath,
resolveNpmCliPath: () => npmCliPath,
});

assert.equal(launch?.source, "package-exec");
assert.equal(launch?.packageName, BUILT_IN_AGENT_PACKAGES.claude.packageName);
assert.equal(launch?.packageRange, BUILT_IN_AGENT_PACKAGES.claude.packageRange);
}
});

test("resolvePackageExecBuiltInAgentLaunch bridges built-ins through the current Node npm CLI", () => {
const npmCliPath = path.join(os.tmpdir(), "acpx-test-npm-cli.js");
const launch = resolvePackageExecBuiltInAgentLaunch(AGENT_REGISTRY.codex, {
Expand Down
22 changes: 22 additions & 0 deletions test/session-persistence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ test("parseSessionRecord backfills argv for historical built-in commands", () =>
for (const [agentCommand, expectedArgv] of [
["npx @zed-industries/codex-acp@^0.12.0", AGENT_ARGV_REGISTRY.codex],
["npm exec @agentclientprotocol/claude-agent-acp@^0.37.0", AGENT_ARGV_REGISTRY.claude],
["npx -y @agentclientprotocol/claude-agent-acp@^0.60.0", AGENT_ARGV_REGISTRY.claude],
["npm exec @agentclientprotocol/claude-agent-acp@^0.60.0", AGENT_ARGV_REGISTRY.claude],
["npx pi-acp@^0.0.31", AGENT_ARGV_REGISTRY.pi],
["npx -y mux@^0.27.0 acp", AGENT_ARGV_REGISTRY.mux],
["gemini --experimental-acp", AGENT_ARGV_REGISTRY.gemini],
["kiro-cli acp", AGENT_ARGV_REGISTRY.kiro],
Expand All @@ -94,6 +97,25 @@ test("parseSessionRecord backfills argv for historical built-in commands", () =>
}
});

test("parseSessionRecord keeps usable stored argv over historical command migration", () => {
const storedArgv = ["npx", "-y", "@agentclientprotocol/claude-agent-acp@^0.60.0"];
const serialized = serializeSessionRecordForDisk(
makeSessionRecord({
acpxRecordId: "stored-argv-wins",
acpSessionId: "stored-argv-wins",
agentCommand: "npx -y @agentclientprotocol/claude-agent-acp@^0.60.0",
cwd: "/tmp/stored-argv-wins",
}),
);
serialized.agent_argv = storedArgv;

const parsed = parseSessionRecord(serialized);

assert.ok(parsed);
assert.deepEqual(parsed.agentArgv, storedArgv);
assert.notDeepEqual(parsed.agentArgv, AGENT_ARGV_REGISTRY.claude);
});

test("parseSessionRecord preserves persisted session env", () => {
const serialized = serializeSessionRecordForDisk(
makeSessionRecord({
Expand Down