Ctrl K
ComunidadGuías…

crates/engine/tests/fixtures/acp-options-agent.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// A deterministic Agent Client Protocol agent that advertises its own models,
// a mode, a config option and one control Codewhale cannot write, and reports
// token usage. Behavior is steered by `options-fixture.json` in the session
// cwd; everything it receives is appended to `options-received.jsonl` there.
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';

const plan = () => {
  const path = join(process.cwd(), 'options-fixture.json');
  return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : {};
};
const record = (entry) =>
  appendFileSync(join(process.cwd(), 'options-received.jsonl'), `${JSON.stringify({ pid: process.pid, ...entry })}\n`);
writeFileSync(join(process.cwd(), 'options-env.json'), JSON.stringify(process.env));

const MODELS = [
  { value: 'model-a', name: 'Model A', description: 'Fast' },
  { value: 'model-b', name: 'Model B', description: 'Careful' },
  { value: 'model-c', name: 'Model C' },
];
const EFFORTS = [
  { value: 'low', name: 'Low' },
  { value: 'high', name: 'High' },
];
const MODES = [
  { id: 'default', name: 'Default' },
  { id: 'review', name: 'Review only', description: 'Reads, never edits' },
];

let buffer = '';
let sessionId = null;
const state = { model: 'model-a', effort: 'low', mode: 'default' };

const send = (message) => process.stdout.write(`${JSON.stringify(message)}\n`);
const result = (id, value) => send({ jsonrpc: '2.0', id, result: value });
const failure = (id, code, message) => send({ jsonrpc: '2.0', id, error: { code, message } });
const notify = (method, params) => send({ jsonrpc: '2.0', method, params });

function offer() {
  const withdrawn = new Set(plan().withdraw ?? []);
  const models = MODELS.filter((model) => !withdrawn.has(model.value));
  const configOptions = [
    { id: 'model', name: 'Model', category: 'model', type: 'select', currentValue: state.model, options: models },
    { id: 'effort', name: 'Reasoning effort', category: 'thought_level', type: 'select', currentValue: state.effort, options: EFFORTS },
    { id: 'verbose', name: 'Verbose logging', type: 'boolean', currentValue: false },
  ];
  const reply = {
    configOptions,
    modes: { currentModeId: state.mode, availableModes: MODES },
  };
  if (plan().malformed) {
    reply.configOptions.push({ id: '', name: 'No id', type: 'select', currentValue: 'x', options: [] });
    reply.configOptions.push({ id: 'empty', name: 'Empty', type: 'select', currentValue: 'x', options: [] });
  }
  if (plan().huge) {
    reply.configOptions.push({
      id: 'many',
      name: 'Many',
      type: 'select',
      currentValue: 'v0',
      options: Array.from({ length: 600 }, (_, index) => ({ value: `v${index}`, name: `Value ${index}` })),
    });
  }
  return reply;
}

function offered(config, value) {
  const found = offer().configOptions.find((option) => option.id === config);
  return Boolean(found && found.type === 'select' && found.options.some((option) => option.value === value));
}

function prompt(request) {
  const text = (request.params?.prompt ?? []).map((block) => block.text ?? '').join('');
  record({ method: 'session/prompt', text, state: { ...state } });
  notify('session/update', {
    sessionId,
    update: {
      sessionUpdate: 'agent_message_chunk',
      content: { type: 'text', text: `model=${state.model} effort=${state.effort} mode=${state.mode}` },
    },
  });
  if (!plan().no_usage) {
    notify('session/update', { sessionId, update: { sessionUpdate: 'usage_update', used: 1200, size: 200000 } });
  }
  result(request.id, {
    stopReason: 'end_turn',
    ...(plan().no_usage ? {} : { usage: { inputTokens: 100, outputTokens: 50, totalTokens: 150, thoughtTokens: 7 } }),
  });
}

function handle(message) {
  if (message.id !== undefined && message.method === undefined) return;
  const params = message.params ?? {};
  switch (message.method) {
    case 'initialize':
      result(message.id, {
        protocolVersion: 1,
        agentInfo: { name: 'options-fixture', version: '1.0.0' },
        authMethods: [],
        agentCapabilities: {
          loadSession: true,
          promptCapabilities: { image: true, embeddedContext: true },
          sessionCapabilities: { fork: {} },
        },
      });
      break;
    case 'session/new':
      if (plan().auth_required) {
        failure(message.id, -32000, 'Authentication required');
        break;
      }
      sessionId = `options-session-${process.pid}`;
      record({ method: 'session/new', cwd: params.cwd });
      if (plan().hang) break;
      result(message.id, { sessionId, ...offer() });
      break;
    case 'session/load':
    case 'session/resume':
      sessionId = params.sessionId ?? null;
      record({ method: message.method, sessionId });
      result(message.id, offer());
      break;
    case 'session/set_config_option':
      record({ method: message.method, configId: params.configId, value: params.value });
      if (!offered(params.configId, params.value)) {
        failure(message.id, -32602, 'value not offered');
        break;
      }
      state[params.configId] = params.value;
      result(message.id, { configOptions: offer().configOptions });
      break;
    case 'session/set_mode':
      record({ method: message.method, modeId: params.modeId });
      if (!MODES.some((mode) => mode.id === params.modeId)) {
        failure(message.id, -32602, 'mode not offered');
        break;
      }
      state.mode = params.modeId;
      result(message.id, {});
      break;
    case 'session/prompt':
      prompt(message);
      break;
    case 'session/cancel':
      break;
    default:
      if (message.id !== undefined) failure(message.id, -32601, `no method ${message.method}`);
  }
}

process.stdin.on('data', (chunk) => {
  buffer += chunk;
  let index;
  while ((index = buffer.indexOf('\n')) >= 0) {
    const line = buffer.slice(0, index);
    buffer = buffer.slice(index + 1);
    if (!line.trim()) continue;
    try {
      handle(JSON.parse(line));
    } catch (error) {
      process.stderr.write(`options fixture: bad line: ${String(error)}\n`);
    }
  }
});
process.stdin.on('end', () => process.exit(0));