Skip to content

Instantly share code, notes, and snippets.

@avin
Last active August 28, 2026 17:14
Show Gist options
  • Select an option

  • Save avin/17b2ec44004896622d8ae45cc622cd4d to your computer and use it in GitHub Desktop.

Select an option

Save avin/17b2ec44004896622d8ae45cc622cd4d to your computer and use it in GitHub Desktop.
Disable FAST mode for composer 2.5 in ACP
{
"agent_servers": {
"MySimpleAgent": {
"command": "C:\\Program Files\\Volta\\node.exe",
"args": [
"C:\\Users\\avin\\.cursor\\cursor-acp-nonfast.js"
]
}
}
}
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const { spawn } = require('node:child_process');
const readline = require('node:readline');
const home = process.env.USERPROFILE || process.env.HOME;
if (!home) {
throw new Error('USERPROFILE is not set');
}
const cursorDir = path.join(home, '.cursor');
const cliConfigPath = path.join(cursorDir, 'cli-config.json');
const acpConfigPath = path.join(cursorDir, 'acp-config.json');
const agentScriptPath = path.join(home, 'AppData', 'Local', 'cursor-agent', 'agent.ps1');
// === Редактируемый список моделей ACP ===
// Для каждой модели указываются базовый modelId и параметры, которые
// прослойка будет применять автоматически после выбора модели.
const allowedModels = [
{
modelId: 'composer-2.5',
displayName: 'Composer 2.5 — Non-fast',
parameters: [{ id: 'fast', value: 'false' }],
},
{
modelId: 'grok-4.6',
displayName: 'Grok 4.6 — Medium — Non-fast',
parameters: [
{ id: 'effort', value: 'medium' },
{ id: 'fast', value: 'false' },
],
},
];
if (allowedModels.length === 0) {
throw new Error('allowedModels must contain at least one model');
}
const defaultModel = allowedModels[0];
const hiddenModelParameterIds = new Set([
'fast',
'effort',
'reasoning',
'reasoning_effort',
'thinking',
]);
function modelVariantId(model) {
const parameters = model.parameters
.map((parameter) => `${parameter.id}=${parameter.value}`)
.join(',');
return `${model.modelId}[${parameters}]`;
}
function cloneParameters(model = defaultModel) {
return model.parameters.map((parameter) => ({ ...parameter }));
}
function resolveModel(value) {
if (typeof value !== 'string') {
return defaultModel;
}
return (
allowedModels.find(
(model) =>
value === model.modelId ||
value === modelVariantId(model) ||
value.startsWith(`${model.modelId}[`),
) ?? defaultModel
);
}
function modelParameter(model, parameterId) {
return model.parameters.find((parameter) => parameter.id === parameterId);
}
function readJson(filePath) {
if (!fs.existsSync(filePath)) {
return {};
}
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
console.error(`Could not read ${filePath}: ${error.message}`);
return {};
}
}
function writeJson(filePath, value) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
}
function forceLocalModelSelection() {
const config = readJson(cliConfigPath);
config.version = config.version || 1;
config.hasChangedDefaultModel = true;
config.model = {
...(config.model || {}),
modelId: defaultModel.modelId,
displayModelId: defaultModel.modelId,
displayName: defaultModel.displayName,
displayNameShort: defaultModel.displayName,
aliases: Array.isArray(config.model?.aliases) ? config.model.aliases : [],
maxMode: false,
};
config.selectedModel = {
modelId: defaultModel.modelId,
parameters: cloneParameters(defaultModel),
};
config.modelParameters = {
...(config.modelParameters || {}),
};
for (const model of allowedModels) {
config.modelParameters[model.modelId] = cloneParameters(model);
}
writeJson(cliConfigPath, config);
const acpConfig = readJson(acpConfigPath);
acpConfig.selectedModelVariantId = modelVariantId(defaultModel);
writeJson(acpConfigPath, acpConfig);
}
function idKey(id) {
return `${typeof id}:${String(id)}`;
}
function sendJson(stream, message) {
stream.write(`${JSON.stringify(message)}\n`);
}
function isModelSelectionRequest(message) {
return Boolean(
message?.params &&
((message.method === 'session/set_config_option' &&
message.params.configId === 'model') ||
message.method === 'session/set_model' ||
message.method === 'unstable_setSessionModel'),
);
}
function requestedModelValue(message) {
if (message.method === 'session/set_config_option') {
return message.params.value;
}
return message.params.modelId ?? message.params.model;
}
function findSourceModelEntry(entries, model) {
return entries.find((entry) => {
const value = entry?.modelId ?? entry?.value;
return (
value === model.modelId ||
value === modelVariantId(model) ||
(typeof value === 'string' && value.startsWith(`${model.modelId}[`))
);
});
}
function allowedModelOptions(sourceOptions) {
return allowedModels.map((model) => {
const source = findSourceModelEntry(sourceOptions, model);
return {
...(source || {}),
value: model.modelId,
name: model.displayName,
};
});
}
function allowedAvailableModels(sourceModels) {
return allowedModels.map((model) => {
const source = findSourceModelEntry(sourceModels, model);
return {
...(source || {}),
modelId: model.modelId,
name: model.displayName,
};
});
}
function patchClientRequest(message) {
if (!message || typeof message !== 'object' || !message.method) {
return message;
}
if (message.method === 'initialize' && message.params) {
const params = { ...message.params };
const clientCapabilities = { ...(params.clientCapabilities || {}) };
clientCapabilities._meta = {
...(clientCapabilities._meta || {}),
parameterizedModelPicker: true,
};
params.clientCapabilities = clientCapabilities;
return { ...message, params };
}
if (message.method === 'session/set_config_option' && message.params) {
const params = { ...message.params };
if (params.configId === 'model') {
params.value = resolveModel(params.value).modelId;
} else {
const sessionModel = sessionModels.get(params.sessionId) ?? defaultModel;
const configuredParameter = modelParameter(sessionModel, params.configId);
if (configuredParameter) {
params.value = configuredParameter.value;
}
}
return { ...message, params };
}
if (
(message.method === 'session/set_model' ||
message.method === 'unstable_setSessionModel') &&
message.params
) {
const selectedModel = resolveModel(message.params.modelId ?? message.params.model);
const params = { ...message.params, modelId: selectedModel.modelId };
if ('model' in params) {
params.model = selectedModel.modelId;
}
return { ...message, params };
}
return message;
}
function patchSessionResult(message, forcedModel = null) {
if (!message?.result || typeof message.result !== 'object') {
return message;
}
const result = { ...message.result };
const modelOption = Array.isArray(result.configOptions)
? result.configOptions.find((option) => option?.id === 'model')
: null;
const rawCurrentModel =
result.models?.currentModelId ?? modelOption?.currentValue ?? defaultModel.modelId;
const currentModel = forcedModel ?? resolveModel(rawCurrentModel);
if (result.models && typeof result.models === 'object') {
const models = { ...result.models };
if (Array.isArray(models.availableModels)) {
models.availableModels = allowedAvailableModels(models.availableModels);
}
models.currentModelId = currentModel.modelId;
result.models = models;
}
if (Array.isArray(result.configOptions)) {
result.configOptions = result.configOptions.flatMap((option) => {
if (option?.id === 'model') {
const sourceOptions = Array.isArray(option.options) ? option.options : [];
return [
{
...option,
options: allowedModelOptions(sourceOptions),
currentValue: currentModel.modelId,
},
];
}
if (hiddenModelParameterIds.has(option?.id)) {
return [];
}
return [option];
});
}
return { ...message, result };
}
forceLocalModelSelection();
const child = spawn(
'powershell.exe',
[
'-NoProfile',
'-ExecutionPolicy',
'Bypass',
'-File',
agentScriptPath,
'--yolo',
'--trust',
'acp',
],
{
stdio: ['pipe', 'pipe', 'inherit'],
windowsHide: true,
},
);
let internalRequestNumber = 0;
const internalRequests = new Map();
const sessionSetupRequests = new Map();
const sessionModels = new Map();
function nextInternalRequestId() {
return `cursor-model-menu-${++internalRequestNumber}`;
}
function emitModelSelectionResponse(selection, successful) {
if (successful && selection.sessionId) {
sessionModels.set(selection.sessionId, selection.model);
}
if (!selection.heldResponse) {
return;
}
const response = { ...selection.heldResponse };
if (!selection.isSetup) {
if (selection.clientRequestId === undefined || selection.clientRequestId === null) {
return;
}
response.id = selection.clientRequestId;
}
process.stdout.write(
`${JSON.stringify(
patchSessionResult(response, successful || selection.isSetup ? selection.model : null),
)}\n`,
);
}
function emitModelSelectionFailure(selection, message) {
if (selection.isSetup) {
emitModelSelectionResponse(selection, false);
return;
}
if (selection.clientRequestId === undefined || selection.clientRequestId === null) {
return;
}
process.stdout.write(`${JSON.stringify({ ...message, id: selection.clientRequestId })}\n`);
}
function sendNextModelParameter(selection) {
if (selection.parameterIndex >= selection.model.parameters.length) {
emitModelSelectionResponse(selection, true);
return;
}
const parameter = selection.model.parameters[selection.parameterIndex++];
const requestId = nextInternalRequestId();
internalRequests.set(idKey(requestId), {
stage: 'parameter',
selection,
});
sendJson(child.stdin, {
jsonrpc: '2.0',
id: requestId,
method: 'session/set_config_option',
params: {
sessionId: selection.sessionId,
configId: parameter.id,
value: parameter.value,
},
});
}
function beginModelSelection({ sessionId, model, heldResponse = null, clientRequest = null }) {
const isSetup = clientRequest === null;
const selection = {
sessionId,
model,
heldResponse,
isSetup,
clientRequestId: clientRequest?.id,
parameterIndex: 0,
};
const requestId = nextInternalRequestId();
internalRequests.set(idKey(requestId), {
stage: 'model',
selection,
});
if (clientRequest) {
const params = { ...clientRequest.params };
if (clientRequest.method === 'session/set_config_option') {
params.value = model.modelId;
} else {
params.modelId = model.modelId;
if ('model' in params) {
params.model = model.modelId;
}
}
sendJson(child.stdin, { ...clientRequest, id: requestId, params });
return;
}
sendJson(child.stdin, {
jsonrpc: '2.0',
id: requestId,
method: 'session/set_config_option',
params: { sessionId, configId: 'model', value: model.modelId },
});
}
function handleInternalResponse(message) {
if (!message || message.id === undefined || message.id === null) {
return false;
}
const key = idKey(message.id);
const request = internalRequests.get(key);
if (!request) {
return false;
}
internalRequests.delete(key);
if (request.stage === 'model') {
if (message.error) {
emitModelSelectionFailure(request.selection, message);
} else {
if (!request.selection.heldResponse) {
request.selection.heldResponse = message;
}
sendNextModelParameter(request.selection);
}
} else if (message.error) {
emitModelSelectionFailure(request.selection, message);
} else {
sendNextModelParameter(request.selection);
}
return true;
}
const childOutput = readline.createInterface({
input: child.stdout,
crlfDelay: Infinity,
});
childOutput.on('line', (line) => {
let message;
try {
message = JSON.parse(line);
} catch {
process.stdout.write(`${line}\n`);
return;
}
// ACP permission requests are normally forwarded to WebStorm, which then
// shows a confirmation dialog. This bridge is intentionally unattended:
// approve the strongest option offered by Cursor and keep the request away
// from the IDE client.
if (
message?.method === 'session/request_permission' &&
message.id !== undefined &&
message.id !== null
) {
const options = Array.isArray(message.params?.options)
? message.params.options
: [];
const selectedOption =
options.find((option) => option?.optionId === 'allow-always') ??
options.find((option) => option?.optionId === 'allow-once');
sendJson(child.stdin, {
jsonrpc: '2.0',
id: message.id,
result: {
outcome: {
outcome: 'selected',
optionId: selectedOption?.optionId ?? 'allow-once',
},
},
});
return;
}
if (handleInternalResponse(message)) {
return;
}
if (message?.id !== undefined && message?.id !== null) {
const setupKey = idKey(message.id);
const setup = sessionSetupRequests.get(setupKey);
if (setup) {
sessionSetupRequests.delete(setupKey);
if (!message.error && message.result?.sessionId) {
beginModelSelection({
sessionId: message.result.sessionId,
model: defaultModel,
heldResponse: message,
});
return;
}
}
}
process.stdout.write(`${JSON.stringify(patchSessionResult(message))}\n`);
});
const clientInput = readline.createInterface({
input: process.stdin,
crlfDelay: Infinity,
});
clientInput.on('line', (line) => {
let message;
try {
message = JSON.parse(line);
} catch {
child.stdin.write(`${line}\n`);
return;
}
if (
message?.id !== undefined &&
message?.id !== null &&
['session/new', 'session/load', 'session/resume'].includes(message.method)
) {
sessionSetupRequests.set(idKey(message.id), true);
}
if (isModelSelectionRequest(message) && message.params.sessionId) {
beginModelSelection({
sessionId: message.params.sessionId,
model: resolveModel(requestedModelValue(message)),
clientRequest: message,
});
return;
}
sendJson(child.stdin, patchClientRequest(message));
});
clientInput.on('close', () => {
child.stdin.end();
});
child.on('error', (error) => {
console.error(`Cursor ACP process failed: ${error.message}`);
});
child.on('exit', (code, signal) => {
if (signal) {
process.exitCode = 1;
} else {
process.exitCode = code ?? 1;
}
});
function stop() {
if (!child.killed) {
child.kill();
}
}
process.on('SIGINT', stop);
process.on('SIGTERM', stop);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment