Ctrl K
ComunidadGuías…

test/codex-reasoning-config.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 fs = require("node:fs");
var path = require("node:path");

var { createCodexAdapter } = require("../lib/yoke/adapters/codex");

function createFakeServer(options) {
  return {
    options: options,
    started: false,
    proc: null,
    start: function () { this.started = true; return Promise.resolve(); },
    send: function (method) {
      if (method === "skills/list") return Promise.resolve({ data: [] });
      return Promise.resolve({});
    },
    notify: function () {},
    stop: function () { this.started = false; },
  };
}

// Initializes an adapter against a fake app-server and returns the config the
// adapter would have handed to the real spawn.
async function spawnedConfig(initOpts) {
  var spawned = [];
  var adapter = createCodexAdapter({
    cwd: process.cwd(),
    createAppServer: function (options) {
      var server = createFakeServer(options);
      spawned.push(server);
      return server;
    },
  });
  await adapter.init(initOpts || {});
  assert.strictEqual(spawned.length, 1, "exactly one app-server is spawned");
  return spawned[0].options.config || {};
}

test("the spawned app-server asks for detailed reasoning summaries", async function () {
  var config = await spawnedConfig({});
  assert.strictEqual(config.model_reasoning_summary, "detailed");
  assert.strictEqual(config.model_supports_reasoning_summaries, undefined);
});

test("without a runtime default flag, Codex falls back to its bundled default GPT-6.1 Sol", async function () {
  var adapter = createCodexAdapter({
    cwd: process.cwd(),
    createAppServer: function (options) { return createFakeServer(options); },
  });
  var models = await adapter.supportedModels();
  var ready = await adapter.init({});
  assert.strictEqual(models[0], "gpt-6.1-sol");
  assert.strictEqual(ready.defaultModel, "gpt-6.1-sol");
});

test("raw agent reasoning is left to the user and never forced on", async function () {
  var config = await spawnedConfig({});
  assert.ok(
    !Object.prototype.hasOwnProperty.call(config, "show_raw_agent_reasoning"),
    "show_raw_agent_reasoning must stay a user opt-in: it is encrypted on ChatGPT-auth models"
  );
});

test("user-supplied Codex config overrides the reasoning defaults", async function () {
  var config = await spawnedConfig({
    adapterOptions: { CODEX: { config: { model_reasoning_summary: "none" } } },
  });
  assert.strictEqual(config.model_reasoning_summary, "none", "the user's choice wins");
  assert.strictEqual(
    config.model_supports_reasoning_summaries, undefined,
    "obsolete settings are not injected"
  );
});

test("a user can turn summary forcing off entirely", async function () {
  var config = await spawnedConfig({
    adapterOptions: {
      CODEX: { config: { model_reasoning_summary: "concise", model_supports_reasoning_summaries: false } },
    },
  });
  assert.strictEqual(config.model_reasoning_summary, "concise");
  assert.strictEqual(config.model_supports_reasoning_summaries, false);
});

test("user config can enable raw reasoning explicitly", async function () {
  var config = await spawnedConfig({
    adapterOptions: { CODEX: { config: { show_raw_agent_reasoning: true } } },
  });
  assert.strictEqual(config.show_raw_agent_reasoning, true);
  assert.strictEqual(config.model_reasoning_summary, "detailed", "defaults still apply alongside");
});

test("unrelated user config keys are carried through with the defaults", async function () {
  var config = await spawnedConfig({
    adapterOptions: { CODEX: { config: { model_verbosity: "low" } } },
  });
  assert.strictEqual(config.model_verbosity, "low");
  assert.strictEqual(config.model_reasoning_summary, "detailed");
  assert.strictEqual(config.model_supports_reasoning_summaries, undefined);
});

test("reasoning defaults survive the MCP server config merge", async function () {
  // MCP servers are merged into the same config object after the defaults are
  // applied, so a regression there would silently drop the reasoning keys.
  var config = await spawnedConfig({});
  assert.strictEqual(config.model_reasoning_summary, "detailed");
  if (config.mcp_servers) {
    assert.strictEqual(typeof config.mcp_servers, "object", "mcp_servers coexists with reasoning keys");
  }
});

test("the reasoning defaults serialize to valid TOML --config flags", function () {
  // serializeConfig is module-private; evaluate it from source so the test
  // asserts the exact flags the app-server receives on argv.
  var source = fs.readFileSync(
    path.join(__dirname, "..", "lib", "yoke", "codex-app-server.js"),
    "utf8"
  );
  var start = source.indexOf("function serializeConfig");
  var end = source.indexOf("// --- CodexAppServer ---");
  assert.ok(start !== -1 && end > start, "serializeConfig and toTomlValue must be present");
  var helpers = new Function(
    source.slice(start, end) + "\nreturn { serializeConfig: serializeConfig };"
  )();

  var args = helpers.serializeConfig({
    model_reasoning_summary: "detailed",
  }, "");
  assert.deepStrictEqual(args, [
    'model_reasoning_summary="detailed"',
  ]);
});

test("the adapter still maps every reasoning event to thinking", function () {
  // The config change is only useful because these mappings already exist;
  // pin them so a rename on either side cannot silently blank out thinking.
  var kit = require("../lib/yoke/adapters/codex").contractTestKit;
  var state = kit.createEventState("gpt-5.6-terra");
  state.threadId = "t1";
  // The first summary delta opens a thinking block and streams its text.
  var summaryDelta = kit.normalizeEvent({
    method: "item/reasoning/summaryTextDelta",
    params: { threadId: "t1", itemId: "i1", delta: "Weighing options" },
  }, state);
  assert.deepStrictEqual(summaryDelta, [
    { yokeType: "thinking_start", blockId: "blk_1" },
    { yokeType: "thinking_delta", blockId: "blk_1", text: "Weighing options" },
  ]);

  // Later deltas append to the block that is already open.
  var more = kit.normalizeEvent({
    method: "item/reasoning/summaryTextDelta",
    params: { threadId: "t1", itemId: "i1", delta: " carefully" },
  }, state);
  assert.deepStrictEqual(more, [
    { yokeType: "thinking_delta", blockId: "blk_1", text: " carefully" },
  ]);

  // summaryPartAdded separates sections within the same block.
  var partAdded = kit.normalizeEvent({
    method: "item/reasoning/summaryPartAdded",
    params: { threadId: "t1", itemId: "i1" },
  }, state);
  assert.deepStrictEqual(partAdded, [
    { yokeType: "thinking_delta", blockId: "blk_1", text: "\n\n" },
  ]);

  // Raw reasoning text (only present when the user opts into
  // show_raw_agent_reasoning) streams into the same thinking channel.
  var textDelta = kit.normalizeEvent({
    method: "item/reasoning/textDelta",
    params: { threadId: "t1", itemId: "i2", delta: "raw" },
  }, state);
  assert.deepStrictEqual(textDelta, [
    { yokeType: "thinking_start", blockId: "blk_2" },
    { yokeType: "thinking_delta", blockId: "blk_2", text: "raw" },
  ]);
});