diff --git a/server/drivers/acp/core.ts b/server/drivers/acp/core.ts index 7deb7279e..253adb919 100644 --- a/server/drivers/acp/core.ts +++ b/server/drivers/acp/core.ts @@ -13,7 +13,10 @@ // is never a security contract). session/load REPLAYS history as ordinary // session/update notifications, so updates are double-gated: nothing emits // before the prompt is sent, and `_meta.isReplay` updates are dropped. +import { existsSync } from "node:fs"; import { homedir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; import { execCli, killCliTree, spawnCli } from "../../procs.ts"; @@ -28,6 +31,13 @@ import type { } from "../../contracts.ts"; import { newEventId, newId } from "../../contracts.ts"; import { augmentedPath } from "../../env-path.ts"; + +// the computer proxy entry: .ts in dev (node type stripping), .js in the +// compiled dist-server the packaged app ships +const COMPUTER_PROXY_PATH = (() => { + const ts = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "computer-proxy.ts"); + return existsSync(ts) ? ts : ts.replace(/\.ts$/, ".js"); +})(); import { appendNative } from "../native.ts"; export interface AcpConfig { @@ -131,13 +141,34 @@ export function createAcpDriver(support: AcpSupport): ProviderDriver // fine here. env is the ACP {name,value}[] shape. const acpMcpServers = (turn: SendTurnInput) => { const servers: Array<{ name: string; command: string; args: string[]; env: Array<{ name: string; value: string }> }> = []; + const acpEnv = (env: Record) => + Object.entries(env).map(([name, value]) => ({ name, value: String(value) })); const agents = turn.integrations?.agents; if (agents) { + servers.push({ name: "agents", command: agents.command, args: agents.args, env: acpEnv(agents.env) }); + } + // the bot's computer, mounted exactly like the claude driver does: + // an ACP agent gets the same screenshot/click/batch tools instead of + // being told it has a machine it cannot touch + const computer = turn.integrations?.computer; + if (computer) { + servers.push({ + name: "computer", + command: process.execPath, + args: [COMPUTER_PROXY_PATH], + env: acpEnv({ + ELECTRON_RUN_AS_NODE: "1", + OGB_BOX_ID: computer.boxId, + OGB_BOX_TOKEN: computer.token, + }), + }); + } else if (turn.integrations?.localComputer) { + const local = turn.integrations.localComputer; servers.push({ - name: "agents", - command: agents.command, - args: agents.args, - env: Object.entries(agents.env).map(([name, value]) => ({ name, value: String(value) })), + name: "computer", + command: local.command, + args: local.args, + env: acpEnv(local.env ?? {}), }); } return servers; @@ -478,7 +509,7 @@ export function createAcpDriver(support: AcpSupport): ProviderDriver snapshot, adapter: { provider: DRIVER_KIND, - capabilities: { sessionModelSwitch: "unsupported", agentsMcp: true }, + capabilities: { sessionModelSwitch: "unsupported", agentsMcp: true, computerMcp: true }, sendTurn, interruptTurn: async (threadId) => active.get(threadId)?.interrupt(), respondToRequest: async (threadId, requestId, decision) => {