Ctrl K
ComunidadGuías…

test/kiro-adapter-query.test.js

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

Archivo original ↗Preguntar a KiBot
var test = require("node:test");
var assert = require("node:assert");

var { createKiroAdapter } = require("../lib/yoke/adapters/kiro");
var createKiroQueryHandle = require("../lib/yoke/adapters/kiro").contractTestKit.createQueryHandle;

test("v3 resume suppresses replay and configures supervised model selection", async function() {
  var calls = [];

  function FakeAcpServer() {
    this.started = false;
    this.handlers = [];
    this.proc = null;
  }

  FakeAcpServer.prototype.start = function() {
    this.started = true;
    return Promise.resolve();
  };

  FakeAcpServer.prototype.addRequestHandler = function() {};

  FakeAcpServer.prototype.addHandler = function(fn) {
    var entry = { sessionId: null, fn: fn };
    this.handlers.push(entry);
    return entry;
  };

  FakeAcpServer.prototype.removeHandler = function(entry) {
    var index = this.handlers.indexOf(entry);
    if (index !== -1) this.handlers.splice(index, 1);
  };

  FakeAcpServer.prototype.send = function(method, params) {
    calls.push({ method: method, params: params });
    if (method === "initialize") return Promise.resolve({ protocolVersion: 1 });
    if (method === "session/new") return Promise.resolve({ sessionId: "sess-fresh" });
    if (method === "session/load") {
      this.handlers[0].fn({
        method: "session/update",
        params: {
          sessionId: params.sessionId,
          update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "OLD" } },
        },
      });
      return Promise.resolve({});
    }
    if (method === "session/prompt") {
      this.handlers[0].fn({
        method: "session/update",
        params: {
          sessionId: params.sessionId,
          update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "NEW" } },
        },
      });
      this.handlers[0].fn({
        method: "session/update",
        params: {
          sessionId: params.sessionId,
          update: {
            sessionUpdate: "session_info_update",
            _meta: { kiro: { kind: "context_usage", contextUsage: { usagePercentage: 10 } } },
          },
        },
      });
      return Promise.resolve({ stopReason: "end_turn" });
    }
    return Promise.resolve({});
  };

  FakeAcpServer.prototype.notify = function() {};
  FakeAcpServer.prototype.stop = function() { this.started = false; };

  var adapter = createKiroAdapter({
    cwd: process.cwd(),
    _binaryPath: "/fake/kiro-cli",
    _AcpServerCtor: FakeAcpServer,
    _fetchKasToken: function() { return Promise.resolve({ accessToken: "token" }); },
    _fetchModels: function() {
      return Promise.resolve({
        models: ["auto"],
        defaultModel: "auto",
        contextWindows: { auto: 1000 },
      });
    },
  });

  await adapter.init();
  var bridgeDescriptor = { name: "clay-tools", command: "/usr/bin/node", args: ["bridge.js", "--session", "7", "--query-generation", "2"] };
  var freshHandle = await adapter.createQuery({
    cwd: process.cwd(), model: "auto",
    adapterOptions: { KIRO: { mode: "vibe", mcpServers: [bridgeDescriptor] } },
  });
  freshHandle.pushMessage("fresh");
  for await (var freshEvent of freshHandle) { if (freshEvent.yokeType === "result") break; }
  freshHandle.close();
  var disabledHandle = await adapter.createQuery({
    cwd: process.cwd(),
    model: "auto",
    skillOptions: { disable: true },
    adapterOptions: { KIRO: { mode: "vibe", mcpServers: [bridgeDescriptor] } },
  });
  disabledHandle.pushMessage("disabled");
  for await (var disabledEvent of disabledHandle) { if (disabledEvent.yokeType === "result") break; }
  disabledHandle.close();
  var handle = await adapter.createQuery({
    cwd: process.cwd(),
    model: "auto",
    systemPrompt: "Base instructions",
    appendSystemPrompt: "You are the Driver",
    resumeSessionId: "sess-existing",
    adapterOptions: { KIRO: { mode: "vibe", mcpServers: [bridgeDescriptor] } },
    canUseTool: function() { return Promise.resolve({ behavior: "deny" }); },
  });
  handle.pushMessage("hello");

  var text = "";
  var result = null;
  for await (var event of handle) {
    if (event.yokeType === "text_delta") text += event.text;
    if (event.yokeType === "result") { result = event; break; }
  }
  handle.close();
  await adapter.shutdown();

  assert.strictEqual(text, "NEW");
  assert.strictEqual(result.sessionId, "sess-existing");
  assert.strictEqual(result.usage.input_tokens, 100);
  assert.ok(calls.some(function(call) {
    return call.method === "session/set_config_option"
      && call.params.configId === "autopilot"
      && call.params.value === "off";
  }));
  assert.ok(calls.some(function(call) {
    return call.method === "session/set_config_option"
      && call.params.configId === "model"
      && call.params.value === "auto";
  }));
  assert.ok(!calls.some(function(call) { return call.method === "session/set_model"; }));
  var newCall = calls.find(function(call) { return call.method === "session/new"; });
  var loadCall = calls.find(function(call) { return call.method === "session/load"; });
  assert.deepEqual(newCall.params.mcpServers, [bridgeDescriptor]);
  assert.deepEqual(loadCall.params.mcpServers, [bridgeDescriptor]);
  var promptCall = calls.find(function(call) { return call.method === "session/prompt" && call.params.sessionId === "sess-existing"; });
  assert.match(promptCall.params.prompt[0].text, /Base instructions/);
  assert.match(promptCall.params.prompt[0].text, /You are the Driver/);
  var disabledPromptCall = calls.find(function(call) {
    return call.method === "session/prompt" && call.params.prompt[0].text === "disabled";
  });
  assert.ok(disabledPromptCall);
  assert.doesNotMatch(disabledPromptCall.params.prompt[0].text, /Available shared skills/);
});

function createQueryServer(send) {
  return {
    started: true,
    calls: [],
    responses: [],
    handlers: [],
    send: send,
    addHandler: function(fn) { var entry = { sessionId: null, fn: fn }; this.handlers.push(entry); return entry; },
    removeHandler: function(entry) { var index = this.handlers.indexOf(entry); if (index !== -1) this.handlers.splice(index, 1); },
    notify: function() {},
    respond: function(id, result) { this.responses.push({ id: id, result: result }); },
  };
}

test("Kiro resume and requested configuration failures stop before prompting", async function() {
  var loadServer = createQueryServer(function(method, params) {
    this.calls.push({ method: method, params: params });
    if (method === "session/load") return Promise.reject(new Error("resume unavailable"));
    return Promise.resolve({ sessionId: "must-not-replace" });
  });
  var loadHandle = createKiroQueryHandle(loadServer, {
    cwd: process.cwd(), engine: "v3", model: "auto", resumeSessionId: "existing", mcpServers: [],
  });
  loadHandle.pushMessage("resume");
  var loadErrors = [];
  for await (var loadEvent of loadHandle) if (loadEvent.yokeType === "error") loadErrors.push(loadEvent.text);
  assert.deepStrictEqual(loadErrors, ["resume unavailable"]);
  assert.strictEqual(loadServer.calls.some(function(call) { return call.method === "session/new"; }), false);
  assert.strictEqual(loadServer.calls.some(function(call) { return call.method === "session/prompt"; }), false);

  var configServer = createQueryServer(function(method, params) {
    this.calls.push({ method: method, params: params });
    if (method === "session/new") return Promise.resolve({ sessionId: "fresh" });
    if (method === "session/set_config_option" && params.configId === "model") return Promise.reject(new Error("model unavailable"));
    return Promise.resolve({});
  });
  var configHandle = createKiroQueryHandle(configServer, {
    cwd: process.cwd(), engine: "v3", model: "model-x", mcpServers: [],
  });
  configHandle.pushMessage("configured");
  var configErrors = [];
  for await (var configEvent of configHandle) if (configEvent.yokeType === "error") configErrors.push(configEvent.text);
  assert.deepStrictEqual(configErrors, ["model unavailable"]);
  assert.strictEqual(configServer.calls.some(function(call) { return call.method === "session/prompt"; }), false);
});

test("Kiro cancels permission requests with unrecognized option kinds", async function() {
  var promptResolve;
  var seenTool = null;
  var server = createQueryServer(function(method, params) {
    this.calls.push({ method: method, params: params });
    if (method === "session/new") return Promise.resolve({ sessionId: "fresh" });
    if (method === "session/prompt") return new Promise(function(resolve) { promptResolve = resolve; });
    return Promise.resolve({});
  });
  var handle = createKiroQueryHandle(server, {
    cwd: process.cwd(), engine: "v3", model: "auto", mcpServers: [],
    canUseTool: function(toolName, input) { seenTool = { toolName: toolName, input: input }; return Promise.resolve({ behavior: "allow" }); },
  });
  handle.pushMessage("permission");
  await new Promise(function(resolve) { setImmediate(resolve); });
  server.handlers[0].fn({ method: "session/update", params: { sessionId: "fresh", update: {
    sessionUpdate: "tool_call", toolCallId: "tool-1", name: "future_tool", title: "Unknown", rawInput: { value: 7 },
  } } });
  server.handlers[0].fn({ id: 91, method: "session/request_permission", params: {
    sessionId: "fresh", toolCall: { toolCallId: "tool-1", title: "Unknown" },
    options: [{ optionId: "first", kind: "future_allow" }, { optionId: "last", kind: "future_reject" }],
  } });
  await new Promise(function(resolve) { setImmediate(resolve); });
  assert.deepStrictEqual(seenTool, { toolName: "future_tool", input: { value: 7 } });
  assert.deepStrictEqual(server.responses, [{ id: 91, result: { outcome: { outcome: "cancelled" } } }]);
  promptResolve({ stopReason: "cancelled" });
  handle.endInput();
  for await (var event of handle) {}
});