Skip to content

Config changes does not work over ACP #3556

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions