Skip to content

Config changes does not work over ACP #3556

@josevalim

Description

@josevalim

Describe the bug

After starting copilot --acp, initializing ACP, and creating a new session with session/new, calling session/set_config_option fails for permission-related config.

Example request:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "session/set_config_option",
  "params": {
    "sessionId": "<session-id>",
    "configId": "allow_all",
    "value": "on"
  }
}

Response/error:

Failed to set config option 'allow_all' to 'on': Error: Permission service is unavailable for this session.

Affected version

1.0.54

Steps to reproduce the behavior

Script to reproduce it:

#!/usr/bin/env bash
set -euo pipefail

# Minimal Copilot ACP repro:
#   1. start copilot --acp
#   2. initialize
#   3. create a session
#   4. try to set allow_all=on
#
# Expected failure:
#   Failed to set config option 'allow_all' to 'on': Error: Permission service is unavailable for this session.

node <<'NODE'
const { spawn } = require("node:child_process");

const child = spawn(process.env.COPILOT_ACP_COMMAND || "copilot --acp", {
  shell: true,
  stdio: ["pipe", "pipe", "pipe"],
});

let nextId = 1;
let buffer = "";
const pending = new Map();

child.stderr.on("data", (chunk) => process.stderr.write(chunk));
child.stdout.on("data", (chunk) => {
  buffer += chunk.toString();
  const lines = buffer.split(/\r?\n/);
  buffer = lines.pop() || "";
  for (const line of lines) {
    if (line.trim() !== "") handleLine(line);
  }
});

child.on("exit", (code, signal) => {
  for (const { reject } of pending.values()) {
    reject(new Error(`copilot exited: code=${code} signal=${signal}`));
  }
});

function request(method, params) {
  const id = nextId++;
  child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);

  return new Promise((resolve, reject) => {
    pending.set(id, { resolve, reject });
    setTimeout(() => reject(new Error(`timeout waiting for ${method}`)), 60000);
  });
}

function handleLine(line) {
  const message = JSON.parse(line);

  if (message.id !== undefined && message.method) {
    // This repro does not implement client-side ACP methods.
    child.stdin.write(`${JSON.stringify({
      jsonrpc: "2.0",
      id: message.id,
      error: { code: -32601, message: `not implemented: ${message.method}` },
    })}\n`);
    return;
  }

  if (message.id === undefined) return;

  const waiter = pending.get(message.id);
  if (!waiter) return;
  pending.delete(message.id);

  if (message.error) {
    waiter.reject(new Error(message.error.message || JSON.stringify(message.error)));
  } else {
    waiter.resolve(message.result);
  }
}

(async function main() {
  await request("initialize", {
    protocolVersion: 1,
    clientCapabilities: {
      fs: { readTextFile: false, writeTextFile: false },
    },
  });

  const session = await request("session/new", {
    cwd: process.cwd(),
    mcpServers: [],
  });

  console.log(`sessionId=${session.sessionId}`);
  console.log("setting allow_all=on...");

  try {
    await request("session/set_config_option", {
      sessionId: session.sessionId,
      configId: "allow_all",
      value: "on",
    });
    console.log("unexpected success");
    process.exitCode = 1;
  } catch (error) {
    console.error(String(error.message || error));
    process.exitCode = error.message.includes("Permission service is unavailable")
      ? 0
      : 1;
  } finally {
    child.kill("SIGTERM");
  }
})();
NODE

Expected behavior

It should allow mode to be changed.

Additional context

This is a separate but related issue with #2942. #2942 is about the model not being able to leave plan mode. This one applies to all configs in general. Thanks!!!

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:non-interactiveNon-interactive mode (-p), CI/CD, ACP protocol, and headless automationarea:permissionsTool approval, security boundaries, sandbox mode, and directory restrictions

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions