mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 05:12:11 +03:00
Root cause: SmartCrusher's system-message guard only excluded role === "system", but Codex CLI (open-sse/executors/codex.ts) sends its instructions/tool-schema turn with role "developer" (the Responses-API equivalent of system used by newer models). Every other system-exclusion guard in this codebase also covers developer (roleNormalizer.ts, contextManager.ts, claudeUpstreamMessages.ts, etc.) except this one, so Headroom happily tabular-compacted JSON arrays embedded in the developer turn (e.g. an update_plan tool schema example), corrupting the instructions the model needs to call the plan tool and breaking Codex CLI plan mode. Fix: extend the guard in crushMessages()/collectCompactableArrays() (smartcrusher.ts) to skip role === "developer" alongside role === "system". Reported-by: SingCJ (https://github.com/decolua/9router/issues/2132)
92 lines
4.0 KiB
TypeScript
92 lines
4.0 KiB
TypeScript
/**
|
|
* Regression test for upstream 9router#2132 (ported): "Token saver Headroom ruins plan mode
|
|
* in Codex CLI".
|
|
*
|
|
* Root cause: SmartCrusher's system-message guard only checked `role === "system"`. Codex CLI
|
|
* (open-sse/executors/codex.ts) sends its instructions/tool-schema turn with role "developer"
|
|
* (the Responses-API equivalent of "system" used by newer models). Every other guard in this
|
|
* codebase that excludes "system" also excludes "developer" (see roleNormalizer.ts,
|
|
* contextManager.ts, claudeUpstreamMessages.ts, etc.) — SmartCrusher was the exception, so it
|
|
* happily tabular-compacted JSON arrays (e.g. the update_plan tool schema/examples) embedded in
|
|
* the developer-role turn, corrupting the instructions the model needs to call the plan tool.
|
|
*/
|
|
|
|
import { describe, it, before } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
let crushMessages: typeof import("../../../open-sse/services/compression/engines/headroom/smartcrusher.ts").crushMessages;
|
|
let collectCompactableArrays: typeof import("../../../open-sse/services/compression/engines/headroom/smartcrusher.ts").collectCompactableArrays;
|
|
let headroomEngine: import("../../../open-sse/services/compression/engines/headroom/index.ts").headroomEngine;
|
|
|
|
before(async () => {
|
|
const mod = await import("../../../open-sse/services/compression/engines/headroom/smartcrusher.ts");
|
|
crushMessages = mod.crushMessages;
|
|
collectCompactableArrays = mod.collectCompactableArrays;
|
|
|
|
const engineMod = await import("../../../open-sse/services/compression/engines/headroom/index.ts");
|
|
headroomEngine = engineMod.headroomEngine;
|
|
});
|
|
|
|
/** A homogeneous array big enough (>= default minRows=8) to trigger compaction. */
|
|
function makePlanSchemaExample(): Record<string, unknown>[] {
|
|
return Array.from({ length: 10 }, (_, i) => ({
|
|
step: `step-${i + 1}`,
|
|
status: i === 0 ? "in_progress" : "pending",
|
|
}));
|
|
}
|
|
|
|
describe("headroom SmartCrusher — developer-role guard (9router#2132)", () => {
|
|
it("does NOT compact JSON arrays embedded in a developer-role message (crushMessages)", () => {
|
|
const json = JSON.stringify(makePlanSchemaExample());
|
|
const messages = [
|
|
{
|
|
role: "developer",
|
|
content: `Use the update_plan tool. Example plan:\n\`\`\`json\n${json}\n\`\`\``,
|
|
},
|
|
{ role: "user", content: "Refactor the auth module." },
|
|
];
|
|
|
|
const { messages: result, changed } = crushMessages(messages, 8);
|
|
|
|
assert.equal(changed, false, "developer-role content must not be touched");
|
|
assert.equal(result[0].content, messages[0].content);
|
|
});
|
|
|
|
it("still compacts the same payload when placed under role: system (control case)", () => {
|
|
// Sanity check: this proves the array itself WOULD be compactable — the guard, not the
|
|
// shape of the payload, is what must change.
|
|
const json = JSON.stringify(makePlanSchemaExample());
|
|
const messages = [{ role: "user", content: `\`\`\`json\n${json}\n\`\`\`` }];
|
|
|
|
const { changed } = crushMessages(messages, 8);
|
|
assert.equal(changed, true, "control case: user-role content of the same shape IS compacted");
|
|
});
|
|
|
|
it("collectCompactableArrays does not surface arrays from developer-role messages", () => {
|
|
const json = JSON.stringify(makePlanSchemaExample());
|
|
const messages = [
|
|
{ role: "developer", content: `\`\`\`json\n${json}\n\`\`\`` },
|
|
];
|
|
const found = collectCompactableArrays(messages, 8);
|
|
assert.equal(found.length, 0);
|
|
});
|
|
|
|
it("headroomEngine.apply leaves a Codex-CLI-shaped developer turn untouched end-to-end", () => {
|
|
const json = JSON.stringify(makePlanSchemaExample());
|
|
const body: Record<string, unknown> = {
|
|
model: "gpt-5-codex",
|
|
messages: [
|
|
{
|
|
role: "developer",
|
|
content: `Instructions with an embedded schema example:\n\`\`\`json\n${json}\n\`\`\``,
|
|
},
|
|
{ role: "user", content: "Implement the feature." },
|
|
],
|
|
};
|
|
|
|
const result = headroomEngine.apply(body);
|
|
assert.equal(result.compressed, false);
|
|
assert.deepEqual(result.body, body);
|
|
});
|
|
});
|