diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b221876148..058cf6410db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai - Heartbeat/models: show heartbeat model bleed guidance on context-overflow resets when the last runtime model matches configured `heartbeat.model`, so smaller local heartbeat models point users to `isolatedSession` or `lightContext` instead of only compaction-buffer tuning. Fixes #67314. Thanks @Knightmare6890. - Subagents/models: persist `sessions_spawn.model` and configured subagent models as child-session model overrides before the first turn, so spawned subagents actually run on the requested provider/model instead of reverting to the target agent default. Fixes #73180. Thanks @danielzinhu99. - Channels/Telegram: keep webhook-mode local listeners alive and retry Telegram `setWebhook` registration after recoverable startup network failures, so transient Bot API timeouts no longer leave reverse proxies pointing at a closed listener. Fixes #71834. Thanks @jinon86. +- Agents/ACPX: bundle the Codex ACP adapter and launch it from the isolated `CODEX_HOME` wrapper before falling back to npm, so Codex ACP startup no longer depends on live `npx` resolution or the stale `@zed-industries/codex-acp@^0.11.1` range. Fixes #72037; refs #73202. Thanks @jasonftl, @sazora, and @joerod26. - Backup: skip installed plugin `extensions/*/node_modules` dependency trees while keeping plugin manifests and source files in archives, so local backups avoid rebuildable npm payload bloat. Fixes #64144. Thanks @BrilliantWang. - Cron/models: fail isolated cron runs closed when an explicit `payload.model` is not allowed or cannot be resolved, so scheduled jobs do not silently fall back to an unrelated agent default or paid route before configured provider proxies such as LiteLLM can run. Fixes #73146. Thanks @oneandrewwang. - Memory/QMD: back off repeated chat-turn QMD open failures while still letting memory status and CLI probes recheck immediately, so a broken sidecar dependency cannot trigger active-memory or cron retry storms. Fixes #73188 and #73176. Thanks @leonlushgit and @w3i-William. diff --git a/docs/tools/acp-agents.md b/docs/tools/acp-agents.md index e2793298378..cb084718469 100644 --- a/docs/tools/acp-agents.md +++ b/docs/tools/acp-agents.md @@ -53,7 +53,8 @@ an unavailable backend. - If `plugins.allow` is set, it is a restrictive plugin inventory and **must** include `acpx`; otherwise the bundled default is intentionally blocked and `/acp doctor` reports the missing allowlist entry. - - Target harness adapters (Codex, Claude, etc.) may be fetched on demand with `npx` the first time you use them. + - The bundled Codex ACP adapter is staged with the `acpx` plugin and launched locally when possible. + - Other target harness adapters may still be fetched on demand with `npx` the first time you use them. - Vendor auth still has to exist on the host for that harness. - If the host has no npm or network access, first-run adapter fetches fail until caches are pre-warmed or the adapter is installed another way. @@ -792,30 +793,30 @@ permission modes, see ## Troubleshooting -| Symptom | Likely cause | Fix | -| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `ACP runtime backend is not configured` | Backend plugin missing, disabled, or blocked by `plugins.allow`. | Install and enable backend plugin, include `acpx` in `plugins.allow` when that allowlist is set, then run `/acp doctor`. | -| `ACP is disabled by policy (acp.enabled=false)` | ACP globally disabled. | Set `acp.enabled=true`. | -| `ACP dispatch is disabled by policy (acp.dispatch.enabled=false)` | Automatic dispatch from normal thread messages disabled. | Set `acp.dispatch.enabled=true` to resume automatic thread routing; explicit `sessions_spawn({ runtime: "acp" })` calls still work. | -| `ACP agent "" is not allowed by policy` | Agent not in allowlist. | Use allowed `agentId` or update `acp.allowedAgents`. | -| `/acp doctor` reports backend not ready right after startup | Plugin dependency probe or self-repair is still running. | Wait briefly and rerun `/acp doctor`; if it stays unhealthy, inspect the backend install error and plugin allow/deny policy. | -| Harness command not found | Adapter CLI is not installed or first-run `npx` fetch failed. | Install/prewarm the adapter on the Gateway host, or configure the acpx agent command explicitly. | -| Model-not-found from the harness | Model id is valid for another provider/harness but not this ACP target. | Use a model listed by that harness, configure the model in the harness, or omit the override. | -| Vendor auth error from the harness | OpenClaw is healthy, but the target CLI/provider is not logged in. | Log in or provide the required provider key on the Gateway host environment. | -| `Unable to resolve session target: ...` | Bad key/id/label token. | Run `/acp sessions`, copy exact key/label, retry. | -| `--bind here requires running /acp spawn inside an active ... conversation` | `--bind here` used without an active bindable conversation. | Move to the target chat/channel and retry, or use unbound spawn. | -| `Conversation bindings are unavailable for .` | Adapter lacks current-conversation ACP binding capability. | Use `/acp spawn ... --thread ...` where supported, configure top-level `bindings[]`, or move to a supported channel. | -| `--thread here requires running /acp spawn inside an active ... thread` | `--thread here` used outside a thread context. | Move to target thread or use `--thread auto`/`off`. | -| `Only can rebind this channel/conversation/thread.` | Another user owns the active binding target. | Rebind as owner or use a different conversation or thread. | -| `Thread bindings are unavailable for .` | Adapter lacks thread binding capability. | Use `--thread off` or move to supported adapter/channel. | -| `Sandboxed sessions cannot spawn ACP sessions ...` | ACP runtime is host-side; requester session is sandboxed. | Use `runtime="subagent"` from sandboxed sessions, or run ACP spawn from a non-sandboxed session. | -| `sessions_spawn sandbox="require" is unsupported for runtime="acp" ...` | `sandbox="require"` requested for ACP runtime. | Use `runtime="subagent"` for required sandboxing, or use ACP with `sandbox="inherit"` from a non-sandboxed session. | -| `Cannot apply --model ... did not advertise model support` | The target harness does not expose generic ACP model switching. | Use a harness that advertises ACP `models`/`session/set_model`, use Codex ACP model refs, or configure the model directly in the harness if it has its own startup flag. | -| Missing ACP metadata for bound session | Stale/deleted ACP session metadata. | Recreate with `/acp spawn`, then rebind/focus thread. | -| `AcpRuntimeError: Permission prompt unavailable in non-interactive mode` | `permissionMode` blocks writes/exec in non-interactive ACP session. | Set `plugins.entries.acpx.config.permissionMode` to `approve-all` and restart gateway. See [Permission configuration](/tools/acp-agents-setup#permission-configuration). | -| ACP session fails early with little output | Permission prompts are blocked by `permissionMode`/`nonInteractivePermissions`. | Check gateway logs for `AcpRuntimeError`. For full permissions, set `permissionMode=approve-all`; for graceful degradation, set `nonInteractivePermissions=deny`. | -| ACP session stalls indefinitely after completing work | Harness process finished but ACP session did not report completion. | Monitor with `ps aux \| grep acpx`; kill stale processes manually. | -| Harness sees `<<>>` | Internal event envelope leaked across the ACP boundary. | Update OpenClaw and rerun the completion flow; external harnesses should receive plain completion prompts only. | +| Symptom | Likely cause | Fix | +| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ACP runtime backend is not configured` | Backend plugin missing, disabled, or blocked by `plugins.allow`. | Install and enable backend plugin, include `acpx` in `plugins.allow` when that allowlist is set, then run `/acp doctor`. | +| `ACP is disabled by policy (acp.enabled=false)` | ACP globally disabled. | Set `acp.enabled=true`. | +| `ACP dispatch is disabled by policy (acp.dispatch.enabled=false)` | Automatic dispatch from normal thread messages disabled. | Set `acp.dispatch.enabled=true` to resume automatic thread routing; explicit `sessions_spawn({ runtime: "acp" })` calls still work. | +| `ACP agent "" is not allowed by policy` | Agent not in allowlist. | Use allowed `agentId` or update `acp.allowedAgents`. | +| `/acp doctor` reports backend not ready right after startup | Plugin dependency probe or self-repair is still running. | Wait briefly and rerun `/acp doctor`; if it stays unhealthy, inspect the backend install error and plugin allow/deny policy. | +| Harness command not found | Adapter CLI is not installed, staged plugin deps are missing, or first-run `npx` fetch failed for a non-Codex adapter. | Run `/acp doctor`, repair plugin dependencies, install/prewarm the adapter on the Gateway host, or configure the acpx agent command explicitly. | +| Model-not-found from the harness | Model id is valid for another provider/harness but not this ACP target. | Use a model listed by that harness, configure the model in the harness, or omit the override. | +| Vendor auth error from the harness | OpenClaw is healthy, but the target CLI/provider is not logged in. | Log in or provide the required provider key on the Gateway host environment. | +| `Unable to resolve session target: ...` | Bad key/id/label token. | Run `/acp sessions`, copy exact key/label, retry. | +| `--bind here requires running /acp spawn inside an active ... conversation` | `--bind here` used without an active bindable conversation. | Move to the target chat/channel and retry, or use unbound spawn. | +| `Conversation bindings are unavailable for .` | Adapter lacks current-conversation ACP binding capability. | Use `/acp spawn ... --thread ...` where supported, configure top-level `bindings[]`, or move to a supported channel. | +| `--thread here requires running /acp spawn inside an active ... thread` | `--thread here` used outside a thread context. | Move to target thread or use `--thread auto`/`off`. | +| `Only can rebind this channel/conversation/thread.` | Another user owns the active binding target. | Rebind as owner or use a different conversation or thread. | +| `Thread bindings are unavailable for .` | Adapter lacks thread binding capability. | Use `--thread off` or move to supported adapter/channel. | +| `Sandboxed sessions cannot spawn ACP sessions ...` | ACP runtime is host-side; requester session is sandboxed. | Use `runtime="subagent"` from sandboxed sessions, or run ACP spawn from a non-sandboxed session. | +| `sessions_spawn sandbox="require" is unsupported for runtime="acp" ...` | `sandbox="require"` requested for ACP runtime. | Use `runtime="subagent"` for required sandboxing, or use ACP with `sandbox="inherit"` from a non-sandboxed session. | +| `Cannot apply --model ... did not advertise model support` | The target harness does not expose generic ACP model switching. | Use a harness that advertises ACP `models`/`session/set_model`, use Codex ACP model refs, or configure the model directly in the harness if it has its own startup flag. | +| Missing ACP metadata for bound session | Stale/deleted ACP session metadata. | Recreate with `/acp spawn`, then rebind/focus thread. | +| `AcpRuntimeError: Permission prompt unavailable in non-interactive mode` | `permissionMode` blocks writes/exec in non-interactive ACP session. | Set `plugins.entries.acpx.config.permissionMode` to `approve-all` and restart gateway. See [Permission configuration](/tools/acp-agents-setup#permission-configuration). | +| ACP session fails early with little output | Permission prompts are blocked by `permissionMode`/`nonInteractivePermissions`. | Check gateway logs for `AcpRuntimeError`. For full permissions, set `permissionMode=approve-all`; for graceful degradation, set `nonInteractivePermissions=deny`. | +| ACP session stalls indefinitely after completing work | Harness process finished but ACP session did not report completion. | Monitor with `ps aux \| grep acpx`; kill stale processes manually. | +| Harness sees `<<>>` | Internal event envelope leaked across the ACP boundary. | Update OpenClaw and rerun the completion flow; external harnesses should receive plain completion prompts only. | ## Related diff --git a/extensions/acpx/package.json b/extensions/acpx/package.json index 5f2e183ea36..abcee2cf85d 100644 --- a/extensions/acpx/package.json +++ b/extensions/acpx/package.json @@ -4,6 +4,7 @@ "description": "OpenClaw ACP runtime backend", "type": "module", "dependencies": { + "@zed-industries/codex-acp": "0.12.0", "acpx": "0.6.1" }, "devDependencies": { diff --git a/extensions/acpx/skills/acp-router/SKILL.md b/extensions/acpx/skills/acp-router/SKILL.md index 6673e017d57..6d828208876 100644 --- a/extensions/acpx/skills/acp-router/SKILL.md +++ b/extensions/acpx/skills/acp-router/SKILL.md @@ -211,8 +211,8 @@ ${ACPX_CMD} codex sessions close oc-codex- Defaults are: - `openclaw -> openclaw acp` -- `claude -> npx -y @zed-industries/claude-agent-acp@0.21.0` -- `codex -> npx @zed-industries/codex-acp@^0.9.5` +- `claude -> npx -y @agentclientprotocol/claude-agent-acp@^0.31.0` +- `codex -> bundled @zed-industries/codex-acp@0.12.0 through OpenClaw's isolated CODEX_HOME wrapper` - `copilot -> copilot --acp --stdio` - `cursor -> cursor-agent acp` - `droid -> droid exec --output-format acp` diff --git a/extensions/acpx/src/codex-auth-bridge.test.ts b/extensions/acpx/src/codex-auth-bridge.test.ts index 4742d68dc19..44065e9f752 100644 --- a/extensions/acpx/src/codex-auth-bridge.test.ts +++ b/extensions/acpx/src/codex-auth-bridge.test.ts @@ -1,10 +1,13 @@ +import { execFile } from "node:child_process"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { promisify } from "node:util"; import { afterEach, describe, expect, it } from "vitest"; import { prepareAcpxCodexAuthConfig } from "./codex-auth-bridge.js"; import { resolveAcpxPluginConfig } from "./config.js"; +const execFileAsync = promisify(execFile); const tempDirs: string[] = []; const previousEnv = { CODEX_HOME: process.env.CODEX_HOME, @@ -59,6 +62,14 @@ describe("prepareAcpxCodexAuthConfig", () => { const agentDir = path.join(root, "agent"); const stateDir = path.join(root, "state"); const generated = generatedCodexPaths(stateDir); + const installedBinPath = path.join( + root, + "node_modules", + "@zed-industries", + "codex-acp", + "bin", + "codex-acp.js", + ); process.env.OPENCLAW_AGENT_DIR = agentDir; delete process.env.PI_CODING_AGENT_DIR; @@ -69,17 +80,90 @@ describe("prepareAcpxCodexAuthConfig", () => { const resolved = await prepareAcpxCodexAuthConfig({ pluginConfig, stateDir, + resolveInstalledCodexAcpBinPath: async () => installedBinPath, }); expectCodexWrapperCommand(resolved.agents.codex, generated.wrapperPath); await expect(fs.access(generated.wrapperPath)).resolves.toBeUndefined(); const wrapper = await fs.readFile(generated.wrapperPath, "utf8"); - expect(wrapper).toContain('"--", "codex-acp"'); + expect(wrapper).toContain(JSON.stringify(installedBinPath)); + expect(wrapper).toContain("defaultArgs = [installedBinPath]"); await expect( fs.access(path.join(agentDir, "acp-auth", "codex", "auth.json")), ).rejects.toMatchObject({ code: "ENOENT" }); }); + it("falls back to the current Codex ACP package range when the local adapter is unavailable", async () => { + const root = await makeTempDir(); + const stateDir = path.join(root, "state"); + const generated = generatedCodexPaths(stateDir); + const pluginConfig = resolveAcpxPluginConfig({ + rawConfig: {}, + workspaceDir: root, + }); + + await prepareAcpxCodexAuthConfig({ + pluginConfig, + stateDir, + resolveInstalledCodexAcpBinPath: async () => undefined, + }); + + const wrapper = await fs.readFile(generated.wrapperPath, "utf8"); + expect(wrapper).toContain('"@zed-industries/codex-acp@^0.12.0"'); + expect(wrapper).toContain('"--", "codex-acp"'); + expect(wrapper).not.toContain("@zed-industries/codex-acp@^0.11.1"); + }); + + it("uses the bundled Codex ACP dependency by default when it is installed", async () => { + const root = await makeTempDir(); + const stateDir = path.join(root, "state"); + const generated = generatedCodexPaths(stateDir); + const pluginConfig = resolveAcpxPluginConfig({ + rawConfig: {}, + workspaceDir: root, + }); + + await prepareAcpxCodexAuthConfig({ + pluginConfig, + stateDir, + }); + + const wrapper = await fs.readFile(generated.wrapperPath, "utf8"); + expect(wrapper).toContain("@zed-industries/codex-acp"); + expect(wrapper).toContain("bin/codex-acp.js"); + expect(wrapper).toContain("defaultArgs = [installedBinPath]"); + }); + + it("launches the locally installed Codex ACP bin with isolated CODEX_HOME", async () => { + const root = await makeTempDir(); + const stateDir = path.join(root, "state"); + const generated = generatedCodexPaths(stateDir); + const installedBinPath = path.join(root, "codex-acp-bin.js"); + await fs.writeFile( + installedBinPath, + "console.log(JSON.stringify({ argv: process.argv.slice(2), codexHome: process.env.CODEX_HOME }));\n", + "utf8", + ); + const pluginConfig = resolveAcpxPluginConfig({ + rawConfig: {}, + workspaceDir: root, + }); + + await prepareAcpxCodexAuthConfig({ + pluginConfig, + stateDir, + resolveInstalledCodexAcpBinPath: async () => installedBinPath, + }); + + const { stdout } = await execFileAsync(process.execPath, [generated.wrapperPath], { + cwd: root, + }); + const launched = JSON.parse(stdout.trim()) as { argv?: unknown; codexHome?: unknown }; + expect(launched.argv).toEqual([]); + const expectedCodexHome = await fs.realpath(path.join(stateDir, "acpx", "codex-home")); + expect(path.resolve(String(launched.codexHome))).toBe(expectedCodexHome); + }); + it("does not copy source Codex auth", async () => { const root = await makeTempDir(); const sourceCodexHome = path.join(root, "source-codex"); @@ -106,6 +190,7 @@ describe("prepareAcpxCodexAuthConfig", () => { const resolved = await prepareAcpxCodexAuthConfig({ pluginConfig, stateDir, + resolveInstalledCodexAcpBinPath: async () => undefined, }); expectCodexWrapperCommand(resolved.agents.codex, generated.wrapperPath); @@ -138,7 +223,7 @@ describe("prepareAcpxCodexAuthConfig", () => { rawConfig: { agents: { codex: { - command: "npx @zed-industries/codex-acp@^0.11.1 -c 'model=\"gpt-5.4\"'", + command: "npx @zed-industries/codex-acp@0.12.0 -c 'model=\"gpt-5.4\"'", }, }, }, @@ -148,10 +233,11 @@ describe("prepareAcpxCodexAuthConfig", () => { const resolved = await prepareAcpxCodexAuthConfig({ pluginConfig, stateDir, + resolveInstalledCodexAcpBinPath: async () => path.join(root, "codex-acp.js"), }); expectCodexWrapperCommand(resolved.agents.codex, generated.wrapperPath); - expect(resolved.agents.codex).toContain("npx @zed-industries/codex-acp@^0.11.1"); + expect(resolved.agents.codex).toContain("npx @zed-industries/codex-acp@0.12.0"); expect(resolved.agents.codex).toContain("-c 'model=\"gpt-5.4\"'"); const isolatedConfig = await fs.readFile(generated.configPath, "utf8"); expect(isolatedConfig).not.toContain("notify"); diff --git a/extensions/acpx/src/codex-auth-bridge.ts b/extensions/acpx/src/codex-auth-bridge.ts index e63cb2609e8..e304421659d 100644 --- a/extensions/acpx/src/codex-auth-bridge.ts +++ b/extensions/acpx/src/codex-auth-bridge.ts @@ -1,16 +1,60 @@ import fs from "node:fs/promises"; +import { createRequire } from "node:module"; import path from "node:path"; import type { ResolvedAcpxPluginConfig } from "./config.js"; const CODEX_ACP_PACKAGE = "@zed-industries/codex-acp"; -const CODEX_ACP_PACKAGE_RANGE = "^0.11.1"; +const CODEX_ACP_PACKAGE_RANGE = "^0.12.0"; const CODEX_ACP_BIN = "codex-acp"; +const requireFromHere = createRequire(import.meta.url); + +type PackageManifest = { + name?: unknown; + bin?: unknown; +}; function quoteCommandPart(value: string): string { return JSON.stringify(value); } -function buildCodexAcpWrapperScript(): string { +function resolvePackageBinPath( + packageJsonPath: string, + manifest: PackageManifest, +): string | undefined { + const { bin } = manifest; + const relativeBinPath = + typeof bin === "string" + ? bin + : bin && typeof bin === "object" + ? (bin as Record)[CODEX_ACP_BIN] + : undefined; + if (typeof relativeBinPath !== "string" || relativeBinPath.trim() === "") { + return undefined; + } + return path.resolve(path.dirname(packageJsonPath), relativeBinPath); +} + +async function resolveInstalledCodexAcpBinPath(): Promise { + try { + // Keep OpenClaw's isolated CODEX_HOME wrapper, but launch the plugin-local + // Codex ACP adapter when runtime-deps staging made it available. + const packageJsonPath = requireFromHere.resolve(`${CODEX_ACP_PACKAGE}/package.json`); + const manifest = JSON.parse(await fs.readFile(packageJsonPath, "utf8")) as PackageManifest; + if (manifest.name !== CODEX_ACP_PACKAGE) { + return undefined; + } + const binPath = resolvePackageBinPath(packageJsonPath, manifest); + if (!binPath) { + return undefined; + } + await fs.access(binPath); + return binPath; + } catch { + return undefined; + } +} + +function buildCodexAcpWrapperScript(installedBinPath?: string): string { return `#!/usr/bin/env node import { existsSync } from "node:fs"; import path from "node:path"; @@ -38,10 +82,19 @@ function resolveNpmCliPath() { } const npmCliPath = resolveNpmCliPath(); -const defaultCommand = npmCliPath ? process.execPath : process.platform === "win32" ? "npx.cmd" : "npx"; -const defaultArgs = npmCliPath - ? [npmCliPath, "exec", "--yes", "--package", "${CODEX_ACP_PACKAGE}@${CODEX_ACP_PACKAGE_RANGE}", "--", "${CODEX_ACP_BIN}"] - : ["--yes", "--package", "${CODEX_ACP_PACKAGE}@${CODEX_ACP_PACKAGE_RANGE}", "--", "${CODEX_ACP_BIN}"]; +const installedBinPath = ${installedBinPath ? quoteCommandPart(installedBinPath) : "undefined"}; +let defaultCommand; +let defaultArgs; +if (installedBinPath) { + defaultCommand = process.execPath; + defaultArgs = [installedBinPath]; +} else if (npmCliPath) { + defaultCommand = process.execPath; + defaultArgs = [npmCliPath, "exec", "--yes", "--package", "${CODEX_ACP_PACKAGE}@${CODEX_ACP_PACKAGE_RANGE}", "--", "${CODEX_ACP_BIN}"]; +} else { + defaultCommand = process.platform === "win32" ? "npx.cmd" : "npx"; + defaultArgs = ["--yes", "--package", "${CODEX_ACP_PACKAGE}@${CODEX_ACP_PACKAGE_RANGE}", "--", "${CODEX_ACP_BIN}"]; +} const command = configuredArgs[0] ?? defaultCommand; const args = configuredArgs.length > 0 ? configuredArgs.slice(1) : defaultArgs; @@ -82,10 +135,12 @@ async function prepareIsolatedCodexHome(baseDir: string): Promise { return codexHome; } -async function writeCodexAcpWrapper(baseDir: string): Promise { +async function writeCodexAcpWrapper(baseDir: string, installedBinPath?: string): Promise { await fs.mkdir(baseDir, { recursive: true }); const wrapperPath = path.join(baseDir, "codex-acp-wrapper.mjs"); - await fs.writeFile(wrapperPath, buildCodexAcpWrapperScript(), { encoding: "utf8" }); + await fs.writeFile(wrapperPath, buildCodexAcpWrapperScript(installedBinPath), { + encoding: "utf8", + }); await fs.chmod(wrapperPath, 0o755); return wrapperPath; } @@ -101,11 +156,15 @@ export async function prepareAcpxCodexAuthConfig(params: { pluginConfig: ResolvedAcpxPluginConfig; stateDir: string; logger?: unknown; + resolveInstalledCodexAcpBinPath?: () => Promise; }): Promise { void params.logger; const codexBaseDir = path.join(params.stateDir, "acpx"); await prepareIsolatedCodexHome(codexBaseDir); - const wrapperPath = await writeCodexAcpWrapper(codexBaseDir); + const installedBinPath = await ( + params.resolveInstalledCodexAcpBinPath ?? resolveInstalledCodexAcpBinPath + )(); + const wrapperPath = await writeCodexAcpWrapper(codexBaseDir, installedBinPath); const configuredCodexCommand = params.pluginConfig.agents.codex; return { diff --git a/extensions/acpx/src/manifest.test.ts b/extensions/acpx/src/manifest.test.ts index 17fa486be29..43dd9fb551c 100644 --- a/extensions/acpx/src/manifest.test.ts +++ b/extensions/acpx/src/manifest.test.ts @@ -17,6 +17,7 @@ describe("acpx package manifest", () => { ) as AcpxPackageManifest; expect(packageJson.dependencies?.acpx).toBeDefined(); + expect(packageJson.dependencies?.["@zed-industries/codex-acp"]).toBe("0.12.0"); expect(packageJson.openclaw?.bundle?.stageRuntimeDependencies).toBe(true); }); }); diff --git a/extensions/acpx/src/runtime.test.ts b/extensions/acpx/src/runtime.test.ts index efc540f232c..97e2ab4a0b9 100644 --- a/extensions/acpx/src/runtime.test.ts +++ b/extensions/acpx/src/runtime.test.ts @@ -9,7 +9,7 @@ type TestSessionStore = { const DOCUMENTED_OPENCLAW_BRIDGE_COMMAND = "env OPENCLAW_HIDE_BANNER=1 OPENCLAW_SUPPRESS_NOTES=1 openclaw acp --url ws://127.0.0.1:18789 --token-file ~/.openclaw/gateway.token --session agent:main:main"; -const CODEX_ACP_COMMAND = "npx @zed-industries/codex-acp@^0.11.1"; +const CODEX_ACP_COMMAND = "npx @zed-industries/codex-acp@^0.12.0"; const CODEX_ACP_WRAPPER_COMMAND = `node "/tmp/openclaw/acpx/codex-acp-wrapper.mjs"`; function makeRuntime( @@ -189,7 +189,7 @@ describe("AcpxRuntime fresh reset wrapper", () => { reasoningEffort: "medium", }), ).toBe( - "npx @zed-industries/codex-acp@^0.11.1 -c model=gpt-5.4 -c model_reasoning_effort=medium", + "npx @zed-industries/codex-acp@^0.12.0 -c model=gpt-5.4 -c model_reasoning_effort=medium", ); expect(__testing.isCodexAcpCommand("openclaw acp")).toBe(false); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b0199bf3fa..a4bf4171265 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,6 +217,9 @@ importers: extensions/acpx: dependencies: + '@zed-industries/codex-acp': + specifier: 0.12.0 + version: 0.12.0 acpx: specifier: 0.6.1 version: 0.6.1 @@ -4365,6 +4368,46 @@ packages: resolution: {tarball: https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67} version: 2.0.1 + '@zed-industries/codex-acp-darwin-arm64@0.12.0': + resolution: {integrity: sha512-RvTXH21sLpswEo8xLeQXcA/uWZauyNP1y+WI6b355+/o7sQ5wrvBkxt+NyhaJXJIQvbfdpl04LND4cmM+DTcNg==} + cpu: [arm64] + os: [darwin] + hasBin: true + + '@zed-industries/codex-acp-darwin-x64@0.12.0': + resolution: {integrity: sha512-N7EhrUTioix3L21qnm6kZzAESc+B7Mac+/uW3khn/UQe7fJJ7u1ojbgMPDdGo/8Xm6HBBXgak2NOj7mJ+NNXSw==} + cpu: [x64] + os: [darwin] + hasBin: true + + '@zed-industries/codex-acp-linux-arm64@0.12.0': + resolution: {integrity: sha512-Kq35FclgZiSMBKyf80PnCvvJ3xfMjZIkPJXpci35U/VqXVQelhHCwYWwA3waTxvW07tNHxsehv1eQICz7wZdVQ==} + cpu: [arm64] + os: [linux] + hasBin: true + + '@zed-industries/codex-acp-linux-x64@0.12.0': + resolution: {integrity: sha512-twmX9noSqfgWgVkGG1dd9u20Pxp8vNRXggvJ61RQSrNYITGuqHil2F3ViYICZoXyr9w1gok28bWG5DU2d9adPg==} + cpu: [x64] + os: [linux] + hasBin: true + + '@zed-industries/codex-acp-win32-arm64@0.12.0': + resolution: {integrity: sha512-VoFsTIrQopO917x2EpxYXm3jTIoSknCbzP76FwX9uOThlRms+M+fHWJ4kJttOPpeofz1ulAS3vPVMQ3WNlvnhw==} + cpu: [arm64] + os: [win32] + hasBin: true + + '@zed-industries/codex-acp-win32-x64@0.12.0': + resolution: {integrity: sha512-HImgXGIYgW6Wxr3rylrHS7Dzs35zvcQQB7eqAEWZ2Lj+3AxP/7TViW9KkjS+PTPnVWqpTkz0hYDQhk63Ruw3JA==} + cpu: [x64] + os: [win32] + hasBin: true + + '@zed-industries/codex-acp@0.12.0': + resolution: {integrity: sha512-0d7gRzOiYTgDmIyh783mCcq50h3mdOg/TtKdLfBIghOLushpQRwhuLjKK8Q9hxZfNlPL0Ua56DoPjnsW8amf8g==} + hasBin: true + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -11115,6 +11158,33 @@ snapshots: curve25519-js: 0.0.4 protobufjs: 7.5.5 + '@zed-industries/codex-acp-darwin-arm64@0.12.0': + optional: true + + '@zed-industries/codex-acp-darwin-x64@0.12.0': + optional: true + + '@zed-industries/codex-acp-linux-arm64@0.12.0': + optional: true + + '@zed-industries/codex-acp-linux-x64@0.12.0': + optional: true + + '@zed-industries/codex-acp-win32-arm64@0.12.0': + optional: true + + '@zed-industries/codex-acp-win32-x64@0.12.0': + optional: true + + '@zed-industries/codex-acp@0.12.0': + optionalDependencies: + '@zed-industries/codex-acp-darwin-arm64': 0.12.0 + '@zed-industries/codex-acp-darwin-x64': 0.12.0 + '@zed-industries/codex-acp-linux-arm64': 0.12.0 + '@zed-industries/codex-acp-linux-x64': 0.12.0 + '@zed-industries/codex-acp-win32-arm64': 0.12.0 + '@zed-industries/codex-acp-win32-x64': 0.12.0 + abbrev@1.1.1: optional: true