Compare commits

...

3 Commits

Author SHA1 Message Date
Markus Hartung
97fa38799e board #11072 2026-08-21 22:07:14 -03:00
Markus Hartung
b45aa9eb19 merge: reconcile command-code /provider/v1 migration with tip (resolve #10986 overlap)
The new compact executor already carries the #10986 reasoning-only content fallback,
so the old-executor conflict is resolved by taking the new executor version.
2026-08-21 21:02:12 -03:00
Markus Hartung
690f5739ad fix(command-code): use documented /provider/v1 chat endpoint (#10265) 2026-08-21 20:28:45 -03:00
13 changed files with 527 additions and 2579 deletions

View File

@@ -0,0 +1 @@
- fix(command-code): route chat to the documented /provider/v1/chat/completions endpoint instead of the CLI-only /alpha/generate, which Command Code gates/blocks for external callers (#10265)

View File

@@ -8,7 +8,11 @@ export const command_codeProvider: RegistryEntry = {
format: "openai",
executor: "command-code",
baseUrl: "https://api.commandcode.ai",
chatPath: "/alpha/generate",
// Chat uses the documented /provider/v1/chat/completions (OpenAI-format)
// endpoint — NOT the CLI-only /alpha/generate endpoint, which Command Code
// version-gates and proxy-blocks for external callers (#10265). Discovery
// already targets the sibling /provider/v1/models endpoint.
chatPath: "/provider/v1/chat/completions",
modelsUrl: "https://api.commandcode.ai/provider/v1/models",
// The discovery response is a partial routing catalog; static registry
// entries omitted from it can still be accepted by the gateway.

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
// OpenAI/Gemini-format + Bedrock provider key validators (bedrock, openai-like, command-code, gemini-like, openai-compatible).
// Extracted from validation.ts (god-file decomposition) — top-level functions; behavior is
// byte-identical to the original inline defs.
import { randomUUID } from "node:crypto";
import { getRegistryEntry } from "@omniroute/open-sse/config/providerRegistry.ts";
import {
discoverBedrockNativeModels,
@@ -196,13 +195,12 @@ export async function validateOpenAILikeProvider({
export async function validateCommandCodeProvider({ apiKey, providerSpecificData = {} }: any) {
const entry = getRegistryEntry("command-code");
const baseUrl = normalizeBaseUrl(entry?.baseUrl || "https://api.commandcode.ai");
const chatPath = entry?.chatPath || "/alpha/generate";
const chatPath = entry?.chatPath || "/provider/v1/chat/completions";
const url = `${baseUrl}${chatPath.startsWith("/") ? chatPath : `/${chatPath}`}`;
const validationModelId =
providerSpecificData?.validationModelId ||
entry?.models?.find((model) => model.id === "deepseek/deepseek-v4-flash")?.id ||
"deepseek/deepseek-v4-flash";
const { COMMAND_CODE_VERSION } = await import("@omniroute/open-sse/executors/commandCode.ts");
return validateDirectChatProvider({
url,
@@ -210,37 +208,13 @@ export async function validateCommandCodeProvider({ apiKey, providerSpecificData
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"x-command-code-version": COMMAND_CODE_VERSION,
"x-cli-environment": "external",
"x-project-slug": "pi-cc",
"x-taste-learning": "false",
"x-co-flag": "false",
"x-session-id": randomUUID(),
Accept: "text/event-stream",
},
body: {
config: {
workingDir: "/workspace",
date: new Date().toISOString().slice(0, 10),
environment: "external",
structure: [],
isGitRepo: false,
currentBranch: "",
mainBranch: "",
gitStatus: "",
recentCommits: [],
},
memory: "",
taste: "",
skills: "",
permissionMode: "standard",
params: {
model: validationModelId,
messages: [{ role: "user", content: "test" }],
tools: [],
system: "",
max_tokens: 1,
stream: true,
},
model: validationModelId,
messages: [{ role: "user", content: "test" }],
stream: true,
max_tokens: 1,
},
});
}

View File

@@ -83,7 +83,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
textIcon: "CC",
website: "https://commandcode.ai/",
authHint:
"Use a Command Code API key. Requests are sent to Command Code's /alpha/generate endpoint.",
"Use a Command Code API key. Requests are sent to Command Code's /provider/v1/chat/completions endpoint.",
apiHint: "Create or copy an API key from Command Code, then paste it here as a Bearer token.",
},
openrouter: {

View File

@@ -8,20 +8,13 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-command-c
process.env.DATA_DIR = TEST_DATA_DIR;
const { REGISTRY, getRegistryEntry } = await import("../../open-sse/config/providerRegistry.ts");
const { CommandCodeExecutor, COMMAND_CODE_VERSION } =
await import("../../open-sse/executors/commandCode.ts");
const { CommandCodeExecutor } = await import("../../open-sse/executors/commandCode.ts");
const { getExecutor, hasSpecializedExecutor } = await import("../../open-sse/executors/index.ts");
const { createResponsesApiTransformStream } =
await import("../../open-sse/transformer/responsesTransformer.ts");
const core = await import("../../src/lib/db/core.ts");
const originalFetch = globalThis.fetch;
type JsonRecord = Record<string, unknown>;
type ResponsesEvent = {
event: string;
data: { response: JsonRecord & { usage?: unknown; output?: JsonRecord[] } };
};
type FetchCall = { url: string; init: Record<string, unknown>; body?: Record<string, unknown> };
const PINNED_COMMAND_CODE_MODELS = [
"claude-opus-4-7",
@@ -44,20 +37,7 @@ const PINNED_COMMAND_CODE_MODELS = [
"Qwen/Qwen3.6-Plus",
];
function commandCodeStream(lines: unknown[], { sse = false } = {}) {
const text = lines
.map((line) => {
const json = JSON.stringify(line);
return sse ? `data: ${json}\n\n` : `${json}\n`;
})
.join("");
return new Response(text, { status: 200, headers: { "Content-Type": "application/x-ndjson" } });
}
function toPlainHeaders(headers: Headers | Record<string, string>) {
if (headers instanceof Headers) return Object.fromEntries(headers.entries());
return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key, String(value)]));
}
const CHAT_URL = "https://api.commandcode.ai/provider/v1/chat/completions";
function parseSsePayloads(sse: string) {
return sse
@@ -68,25 +48,21 @@ function parseSsePayloads(sse: string) {
.map((line) => JSON.parse(line));
}
async function responsesFromChatSse(sse: string): Promise<ResponsesEvent[]> {
const input = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(new TextEncoder().encode(sse));
controller.close();
},
});
const transformed = await new Response(
input.pipeThrough(createResponsesApiTransformStream(null, 60_000))
).text();
function openAiSse(obj: unknown): string {
return `data: ${JSON.stringify(obj)}\n\n`;
}
return transformed
.split("\n\n")
.map((part) => {
const event = part.match(/^event:\s*(.+)$/m)?.[1];
const data = part.match(/^data:\s*(.+)$/m)?.[1];
return event && data ? ({ event, data: JSON.parse(data) } as ResponsesEvent) : null;
})
.filter((entry): entry is ResponsesEvent => entry !== null);
function captureFetch(body: Record<string, unknown>) {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({
url: String(url),
init,
body: JSON.parse(String(init.body)),
});
return new Response(JSON.stringify(body), { status: 200 });
};
return calls;
}
test.afterEach(() => {
@@ -105,7 +81,9 @@ test("Command Code provider catalog has pinned models and alias lookup", () => {
assert.equal(entry.alias, "cmd");
assert.equal(entry.executor, "command-code");
assert.equal(entry.baseUrl, "https://api.commandcode.ai");
assert.equal(entry.chatPath, "/alpha/generate");
// Chat targets the documented /provider/v1/chat/completions endpoint, NOT the
// CLI-only /alpha/generate endpoint (#10265).
assert.equal(entry.chatPath, "/provider/v1/chat/completions");
assert.deepEqual(
entry.models.map((model) => model.id),
PINNED_COMMAND_CODE_MODELS
@@ -119,17 +97,10 @@ test("getExecutor returns the specialized Command Code executor", () => {
assert.ok(getExecutor("cmd") instanceof CommandCodeExecutor);
});
type FetchCall = { url: string; init: Record<string, unknown>; body?: unknown };
test("Command Code executor posts wrapped body and required headers to /alpha/generate", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init });
return commandCodeStream([{ type: "text-delta", text: "hello" }, { type: "finish" }]);
};
test("Command Code executor posts a flat OpenAI body + standard headers to /provider/v1/chat/completions (#10265)", async () => {
const calls = captureFetch({});
const executor = getExecutor("command-code");
const { response, url, headers, transformedBody } = await executor.execute({
const { response, url, headers } = await executor.execute({
model: "gpt-5.4-mini",
stream: false,
credentials: { apiKey: "cc_test_key" },
@@ -144,41 +115,34 @@ test("Command Code executor posts wrapped body and required headers to /alpha/ge
},
});
assert.equal(url, "https://api.commandcode.ai/alpha/generate");
assert.equal(url, CHAT_URL);
assert.equal(calls.length, 1);
assert.equal(calls[0].url, "https://api.commandcode.ai/alpha/generate");
assert.equal(calls[0].url, CHAT_URL);
assert.equal(calls[0].init.method, "POST");
assert.equal(headers.Authorization, "Bearer cc_test_key");
assert.equal(headers["x-command-code-version"], COMMAND_CODE_VERSION);
assert.equal(headers["x-cli-environment"], "external");
assert.equal(headers["x-project-slug"], "pi-cc");
assert.equal(headers["x-taste-learning"], "false");
assert.equal(headers["x-co-flag"], "false");
assert.equal(typeof headers["x-session-id"], "string");
// No CLI-impersonation headers.
assert.equal(headers["x-command-code-version"], undefined);
assert.equal(headers["x-cli-environment"], undefined);
assert.equal(headers["x-project-slug"], undefined);
const posted = JSON.parse(String(calls[0].init.body));
assert.deepEqual(posted, transformedBody);
for (const key of ["config", "memory", "taste", "skills", "permissionMode", "params"]) {
assert.ok(key in posted, `missing ${key}`);
}
assert.equal(posted.skills, "");
assert.equal(posted.params.model, "gpt-5.4-mini");
assert.equal(posted.params.stream, true);
assert.equal(posted.params.system, "You are concise.");
assert.equal(posted.params.messages[0].role, "user");
assert.equal(posted.params.tools[0].name, "lookup");
const posted = calls[0].body as Record<string, unknown>;
// No CLI envelope.
assert.equal(posted.config, undefined, "CLI envelope config must not be sent");
assert.equal(posted.params, undefined, "CLI envelope params wrapper must not be sent");
assert.equal(posted.model, "gpt-5.4-mini");
assert.equal(posted.stream, false);
assert.equal((posted.messages as Array<{ role: string }>)[0].role, "system");
const tool = (posted.tools as Array<{ function: { name: string } }>)[0];
assert.equal(tool.function.name, "lookup", "tools in OpenAI shape (function.name)");
assert.equal(posted.max_tokens, 42);
// The upstream OpenAI JSON passes through untouched.
const json = await response.json();
assert.equal(json.choices[0].message.content, "hello");
assert.deepEqual(json, {});
});
test("Command Code executor passes reasoning/thinking fields through to params (#2986 follow-up)", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
test("Command Code executor passes reasoning/thinking fields through at the top level of the OpenAI body", async () => {
const calls = captureFetch({});
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
@@ -189,30 +153,19 @@ test("Command Code executor passes reasoning/thinking fields through to params (
reasoning_effort: "high",
thinking: { type: "enabled" },
effort: "high",
output_config: { effort: "high" },
extra_body: { enable_thinking: true },
},
});
const posted = JSON.parse(String(calls[0].init.body));
assert.equal(posted.params.reasoning_effort, "high");
assert.deepEqual(posted.params.thinking, { type: "enabled" });
assert.equal(posted.params.effort, "high");
assert.deepEqual(posted.params.output_config, { effort: "high" });
assert.deepEqual(posted.params.extra_body, { enable_thinking: true });
const posted = calls[0].body as Record<string, unknown>;
assert.equal(posted.reasoning_effort, "high");
assert.deepEqual(posted.thinking, { type: "enabled" });
assert.equal(posted.effort, "high");
assert.deepEqual(posted.extra_body, { enable_thinking: true });
});
test("Command Code executor honors body.model rewrite from payload rules", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
// Simulate a payload-rule rewrite: combo resolves to "deepseek-v4-pro-max"
// (passed as the execute() model arg), but the payload rule overwrites
// body.model to "deepseek/deepseek-v4-pro" (the vendor-prefixed form
// Command Code's API expects).
const calls = captureFetch({});
await getExecutor("command-code").execute({
model: "deepseek-v4-pro-max",
stream: false,
@@ -225,20 +178,13 @@ test("Command Code executor honors body.model rewrite from payload rules", async
},
});
const posted = JSON.parse(String(calls[0].init.body));
assert.equal(posted.params.model, "deepseek/deepseek-v4-pro");
assert.equal(posted.params.reasoning_effort, "max");
const posted = calls[0].body as Record<string, unknown>;
assert.equal(posted.model, "deepseek/deepseek-v4-pro");
assert.equal(posted.reasoning_effort, "max");
});
test("Command Code executor maps unsupported minimal reasoning_effort to low (upstream 400 regression)", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
// Live upstream rejection: "Validation error: Invalid option: expected one of
// \"low\"|\"medium\"|\"high\"|\"xhigh\"|\"max\" at \"params.reasoning_effort\"" —
const calls = captureFetch({});
// `minimal` (a Muse Spark catalog tier) must be downgraded to `low` before
// the wire body is built, on BOTH the combo and single-model paths.
await getExecutor("command-code").execute({
@@ -252,20 +198,38 @@ test("Command Code executor maps unsupported minimal reasoning_effort to low (up
},
});
const posted = JSON.parse(String(calls[0].init.body));
assert.equal(posted.params.reasoning_effort, "low", "minimal must map to low");
const posted = calls[0].body as Record<string, unknown>;
assert.equal(posted.reasoning_effort, "low", "minimal must map to low");
});
test("Command Code raw NDJSON stream becomes OpenAI chat SSE chunks", async () => {
const calls: FetchCall[] = [];
test("Command Code executor passes the upstream OpenAI SSE stream through untouched", async () => {
const sse =
openAiSse({
id: "c1",
object: "chat.completion.chunk",
model: "gpt-5.4",
choices: [{ index: 0, delta: { role: "assistant" } }],
}) +
openAiSse({
id: "c1",
object: "chat.completion.chunk",
model: "gpt-5.4",
choices: [{ index: 0, delta: { content: "Hello" } }],
}) +
openAiSse({
id: "c1",
object: "chat.completion.chunk",
model: "gpt-5.4",
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
}) +
"data: [DONE]\n\n";
let capturedStreamFlag: unknown = null;
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) });
return commandCodeStream([
{ type: "text-delta", text: "Hello" },
{ type: "reasoning-delta", text: "thinking" },
{ type: "tool-call", toolCallId: "call_1", toolName: "search", input: { q: "docs" } },
{ type: "finish", finishReason: "tool-calls" },
]);
capturedStreamFlag = JSON.parse(String(init.body)).stream;
return new Response(sse, {
status: 200,
headers: { "Content-Type": "text/event-stream" },
});
};
const { response } = await getExecutor("command-code").execute({
@@ -275,39 +239,32 @@ test("Command Code raw NDJSON stream becomes OpenAI chat SSE chunks", async () =
body: { messages: [{ role: "user", content: "Hi" }] },
});
assert.equal(calls[0].body.params.stream, true);
assert.equal(response.headers.get("Content-Type"), "text/event-stream; charset=utf-8");
const sse = await response.text();
assert.match(sse, /data: \[DONE\]/);
const chunks = parseSsePayloads(sse);
assert.equal(chunks[0].object, "chat.completion.chunk");
assert.deepEqual(chunks[0].choices[0].delta, { role: "assistant" });
assert.equal(capturedStreamFlag, true, "stream flag forwarded to upstream");
const text = await response.text();
assert.equal(text, sse, "OpenAI SSE stream passed through byte-for-byte");
assert.ok(text.includes("data: [DONE]"));
const chunks = parseSsePayloads(text);
assert.equal(chunks[0].choices[0].delta.role, "assistant");
assert.equal(chunks[1].choices[0].delta.content, "Hello");
assert.equal(chunks[2].choices[0].delta.reasoning_content, "thinking");
assert.equal(chunks[3].choices[0].delta.tool_calls[0].function.name, "search");
assert.equal(chunks.at(-1).choices[0].finish_reason, "tool_calls");
assert.equal(chunks[2].choices[0].finish_reason, "stop");
});
test("Command Code data: SSE lines aggregate into non-stream ChatCompletion JSON", async () => {
globalThis.fetch = async () =>
commandCodeStream(
[
{ type: "text-delta", text: "Hel" },
{ type: "text-delta", text: "lo" },
{ type: "reasoning-delta", text: "because" },
{ type: "tool-call", id: "call_2", name: "lookup", arguments: { id: 7 } },
{
type: "finish",
finishReason: "max_tokens",
totalUsage: {
inputTokens: 3,
inputTokenDetails: { cacheReadTokens: 2 },
outputTokens: 5,
},
},
],
{ sse: true }
);
test("Command Code executor passes the upstream OpenAI JSON through untouched (non-stream)", async () => {
const upstreamJson = {
id: "chatcmpl-1",
object: "chat.completion",
model: "gpt-5.4-mini",
choices: [{ index: 0, message: { role: "assistant", content: "Hello" }, finish_reason: "stop" }],
usage: { prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 },
};
let capturedStreamFlag: unknown = null;
globalThis.fetch = async (url, init = {}) => {
capturedStreamFlag = JSON.parse(String(init.body)).stream;
return new Response(JSON.stringify(upstreamJson), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
@@ -316,88 +273,12 @@ test("Command Code data: SSE lines aggregate into non-stream ChatCompletion JSON
body: { messages: [{ role: "user", content: "Hi" }] },
});
assert.equal(response.headers.get("Content-Type"), "application/json");
const json = await response.json();
assert.equal(json.object, "chat.completion");
assert.equal(json.choices[0].message.content, "Hello");
assert.equal(json.choices[0].message.reasoning_content, "because");
assert.equal(json.choices[0].message.tool_calls[0].function.arguments, JSON.stringify({ id: 7 }));
assert.equal(json.choices[0].finish_reason, "length");
assert.deepEqual(json.usage, {
prompt_tokens: 3,
prompt_tokens_details: { cached_tokens: 2 },
completion_tokens: 5,
completion_tokens_details: { reasoning_tokens: 0 },
total_tokens: 8,
cache_read_input_tokens: 2,
});
assert.equal(capturedStreamFlag, false, "stream flag forwarded as false for non-stream");
assert.deepEqual(await response.json(), upstreamJson);
});
test("Command Code reasoning-only output falls back to reasoning as content (non-stream)", async () => {
globalThis.fetch = async () =>
commandCodeStream(
[
{ type: "reasoning-delta", text: "The user wants 79874+93658. " },
{ type: "reasoning-delta", text: "That equals 173532." },
{
type: "finish",
finishReason: "stop",
totalUsage: { inputTokens: 20, outputTokens: 64, outputTokenDetails: { reasoningTokens: 61 } },
},
],
{ sse: true }
);
const { response } = await getExecutor("command-code").execute({
model: "meta/muse-spark-1.2-contributor",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Calculate 79874+93658, and reply with the result only." }] },
});
const json = await response.json();
const message = json.choices[0].message;
// Regression #10986: when the model emits only reasoning-delta events (never a
// text-delta), content must fall back to the reasoning text instead of "" (which
// OpenAI-compatible clients treat as null/no answer).
assert.equal(message.content, "The user wants 79874+93658. That equals 173532.");
// reasoning_content must STAY populated for reasoning-aware clients.
assert.equal(message.reasoning_content, "The user wants 79874+93658. That equals 173532.");
});
test("Command Code reasoning-only output emits a content delta chunk when streaming", async () => {
globalThis.fetch = async () =>
commandCodeStream(
[
{ type: "reasoning-delta", text: "The result is 173532." },
{ type: "finish", finishReason: "stop" },
],
{ sse: true }
);
const { response } = await getExecutor("command-code").execute({
model: "meta/muse-spark-1.2-contributor",
stream: true,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Calcular 79874+93658" }] },
});
const sse = await response.text();
assert.match(sse, /data: \[DONE\]/);
const chunks = parseSsePayloads(sse);
assert.equal(chunks[0].choices[0].delta.role, "assistant");
// Regression #10986: the reasoning-only stream must emit a content delta when it
// otherwise ends with no content. reasoning_content stays present too.
const contentDelta = chunks.find((c) => c.choices[0].delta.content !== undefined);
assert.equal(contentDelta.choices[0].delta.content, "The result is 173532.");
const reasoningDelta = chunks.find((c) => c.choices[0].delta.reasoning_content !== undefined);
assert.equal(reasoningDelta.choices[0].delta.reasoning_content, "The result is 173532.");
assert.equal(chunks.at(-1).choices[0].finish_reason, "stop");
});
test("Command Code executor surfaces upstream and streamed errors", async () => {
globalThis.fetch = async () =>
new Response("bad key", { status: 401, statusText: "Unauthorized" });
test("Command Code executor surfaces upstream errors", async () => {
globalThis.fetch = async () => new Response("bad key", { status: 401, statusText: "Unauthorized" });
const upstreamFailure = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: false,
@@ -406,124 +287,68 @@ test("Command Code executor surfaces upstream and streamed errors", async () =>
});
assert.equal(upstreamFailure.response.status, 401);
assert.equal(await upstreamFailure.response.text(), "bad key");
globalThis.fetch = async () => commandCodeStream([{ type: "error", error: { message: "boom" } }]);
await assert.rejects(async () => {
await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
}, /boom/);
});
test("Command Code executor omits max_tokens when the client does not supply one (GLM-5.x)", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
// No client max_tokens: we must NOT fabricate one. Omitting the field lets
// Command Code's upstream apply the model's own native default.
test("Command Code executor omits max_tokens when the client does not supply one", async () => {
const calls = captureFetch({});
await getExecutor("command-code").execute({
model: "zai-org/GLM-5.1",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
assert.ok(!("max_tokens" in calls[0].body.params));
});
test("Command Code executor omits max_tokens for DeepSeek v4 when the client does not supply one", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
// Regression: previously the executor invented max_tokens from the registry
// (384000), which /alpha/generate rejects with a 400
// "Too big: expected number to be <=200000". With no client value we now omit
// the field entirely, so the request succeeds and upstream picks the default.
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
assert.ok(!("max_tokens" in calls[0].body.params));
const posted = calls[0].body as Record<string, unknown>;
assert.ok(!("max_tokens" in posted), "must not fabricate max_tokens");
assert.ok(!("max_completion_tokens" in posted), "must not fabricate max_completion_tokens");
});
test("Command Code executor clamps an oversized client-supplied max_tokens to the endpoint ceiling", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
// A client asking for more than the 200000 endpoint ceiling is clamped down
// (not 400'd), mirroring the provider-driven clamp in antigravity.ts.
const calls = captureFetch({});
// A client asking for more than the 200000 endpoint ceiling is clamped down.
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }], max_tokens: 500000 },
});
assert.equal(calls[0].body.params.max_tokens, 200000);
assert.equal((calls[0].body as Record<string, unknown>).max_tokens, 200000);
});
test("Command Code executor honors a smaller client-provided max_tokens under the per-model cap", async () => {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) });
return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]);
};
test("Command Code executor honors a smaller client-provided max_tokens", async () => {
const calls = captureFetch({});
await getExecutor("command-code").execute({
model: "zai-org/GLM-5.1",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }], max_tokens: 2048 },
});
assert.equal(calls[0].body.params.max_tokens, 2048);
assert.equal((calls[0].body as Record<string, unknown>).max_tokens, 2048);
});
test("Command Code non-stream aggregation throws when the final error event lacks a trailing newline", async () => {
globalThis.fetch = async () =>
new Response(
`${JSON.stringify({ type: "text-delta", text: "Hello" })}\n${JSON.stringify({
type: "error",
error: { message: "boom" },
})}`,
{ status: 200, headers: { "Content-Type": "application/x-ndjson" } }
);
await assert.rejects(async () => {
await getExecutor("command-code").execute({
test("Command Code stream preserves the upstream OpenAI usage chunk (passthrough)", async () => {
const sse =
openAiSse({
id: "c1",
object: "chat.completion.chunk",
model: "gpt-5.4-mini",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
}, /boom/);
});
test("Command Code usage chunk surfaces cache_read and no_cache for the stream pipeline", async () => {
globalThis.fetch = async () =>
commandCodeStream([
{ type: "text-delta", text: "Hi" },
{
type: "finish",
finishReason: "stop",
totalUsage: {
inputTokens: 10,
inputTokenDetails: { noCacheTokens: 6, cacheReadTokens: 4 },
outputTokens: 6,
},
choices: [{ index: 0, delta: { content: "Hi" } }],
}) +
openAiSse({
id: "c1",
object: "chat.completion.chunk",
model: "gpt-5.4-mini",
choices: [],
usage: {
prompt_tokens: 10,
prompt_tokens_details: { cached_tokens: 4 },
completion_tokens: 6,
completion_tokens_details: { reasoning_tokens: 1 },
total_tokens: 16,
},
]);
}) +
"data: [DONE]\n\n";
globalThis.fetch = async () =>
new Response(sse, { status: 200, headers: { "Content-Type": "text/event-stream" } });
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
@@ -532,260 +357,11 @@ test("Command Code usage chunk surfaces cache_read and no_cache for the stream p
body: { messages: [{ role: "user", content: "Hi" }] },
});
const sse = await response.text();
const chunks = parseSsePayloads(sse);
const usageChunk = chunks.find(
(chunk) => Array.isArray(chunk.choices) && chunk.choices.length === 0
);
assert.ok(usageChunk, "expected a usage-only chunk (choices: []) in the stream");
// The usage-only chunk feeds stream.ts's extractUsage, which surfaces
// cache_read_input_tokens / no_cache_tokens into the [USAGE] line.
const { extractUsage } = await import("../../open-sse/utils/usageTracking.ts");
const extracted = extractUsage(usageChunk);
assert.ok(extracted, "extractUsage should recognize the usage-only chunk");
assert.equal(extracted.prompt_tokens, 10);
assert.equal(extracted.completion_tokens, 6);
assert.equal(extracted.cache_read_input_tokens, 4);
assert.equal(extracted.no_cache_tokens, 6);
});
test("Command Code stream emits a usage-only chunk with actual tokens before [DONE]", async () => {
globalThis.fetch = async () =>
commandCodeStream([
{ type: "text-delta", text: "Hi" },
{
type: "finish",
finishReason: "stop",
totalUsage: {
inputTokens: 10,
inputTokenDetails: { cacheReadTokens: 4, cacheCreationTokens: 2 },
outputTokens: 6,
reasoningTokenDetails: { reasoningTokens: 1 },
},
},
]);
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: true,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
const sse = await response.text();
const chunks = parseSsePayloads(sse);
// Find the usage-only chunk: choices must be [] and usage must carry the
// actual upstream numbers. prompt_tokens = inputTokens (10) — cacheRead 4 is
// already included in that 10, so it is reported separately, NOT re-added.
const usageChunk = chunks.find(
(chunk) => Array.isArray(chunk.choices) && chunk.choices.length === 0
);
assert.ok(usageChunk, "expected a usage-only chunk (choices: []) in the stream");
assert.deepEqual(usageChunk.usage, {
prompt_tokens: 10,
prompt_tokens_details: { cached_tokens: 4 },
completion_tokens: 6,
completion_tokens_details: { reasoning_tokens: 1 },
total_tokens: 16,
cache_read_input_tokens: 4,
reasoning_tokens: 1,
});
// The usage chunk must come before the [DONE] marker.
assert.match(sse, /"usage":/);
const doneIndex = sse.indexOf("data: [DONE]");
const usageIndex = sse.indexOf(`"choices":[]`);
assert.ok(usageIndex > -1 && usageIndex < doneIndex, "usage chunk must precede [DONE]");
});
test("Command Code non-stream usage keeps inputTokens as prompt_tokens and reports cache separately", async () => {
globalThis.fetch = async () =>
commandCodeStream(
[
{ type: "text-delta", text: "ok" },
{
type: "finish",
finishReason: "stop",
totalUsage: {
inputTokens: 5,
inputTokenDetails: { noCacheTokens: 2, cacheReadTokens: 3 },
outputTokens: 2,
},
},
],
{ sse: true }
);
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
const json = await response.json();
assert.deepEqual(json.usage, {
prompt_tokens: 5,
prompt_tokens_details: { cached_tokens: 3 },
completion_tokens: 2,
completion_tokens_details: { reasoning_tokens: 0 },
total_tokens: 7,
cache_read_input_tokens: 3,
no_cache_tokens: 2,
});
});
test("Command Code preserves finish-step usage through a finish without totalUsage", async () => {
globalThis.fetch = async () =>
commandCodeStream([
{ type: "text-delta", text: "Hi" },
{
type: "finish-step",
usage: {
inputTokens: 7308,
inputTokenDetails: { noCacheTokens: 27, cacheReadTokens: 7281 },
outputTokens: 177,
outputTokenDetails: { textTokens: 12, reasoningTokens: 165 },
totalTokens: 7485,
},
},
{ type: "finish", finishReason: "stop", totalUsage: null },
]);
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: true,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
const sse = await response.text();
const chunks = parseSsePayloads(sse);
const usageChunk = chunks.find(
(chunk) => Array.isArray(chunk.choices) && chunk.choices.length === 0
);
assert.deepEqual(usageChunk?.usage, {
prompt_tokens: 7308,
prompt_tokens_details: { cached_tokens: 7281 },
completion_tokens: 177,
completion_tokens_details: { reasoning_tokens: 165 },
total_tokens: 7485,
cache_read_input_tokens: 7281,
no_cache_tokens: 27,
reasoning_tokens: 165,
});
const completed = (await responsesFromChatSse(sse)).find(
(event) => event.event === "response.completed"
);
assert.deepEqual(completed?.data.response.usage, {
input_tokens: 7308,
input_tokens_details: { cached_tokens: 7281 },
output_tokens: 177,
output_tokens_details: { reasoning_tokens: 165 },
total_tokens: 7485,
});
});
test("Command Code accepts OpenAI-style usage aliases with absent optional details", async () => {
globalThis.fetch = async () =>
commandCodeStream([
{
type: "finish-step",
usage: {
prompt_tokens: 11,
prompt_tokens_details: { cached_tokens: 4 },
completion_tokens: 5,
completion_tokens_details: { reasoning_tokens: 2 },
total_tokens: 16,
},
},
{ type: "finish", finishReason: "stop" },
]);
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: true,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
const sse = await response.text();
const usageChunk = parseSsePayloads(sse).find(
(chunk) => Array.isArray(chunk.choices) && chunk.choices.length === 0
);
assert.deepEqual(usageChunk?.usage, {
prompt_tokens: 11,
prompt_tokens_details: { cached_tokens: 4 },
completion_tokens: 5,
completion_tokens_details: { reasoning_tokens: 2 },
total_tokens: 16,
cache_read_input_tokens: 4,
reasoning_tokens: 2,
});
globalThis.fetch = async () =>
commandCodeStream([
{ type: "finish-step", usage: { inputTokens: 4, outputTokens: 3, totalTokens: 7 } },
{ type: "finish", finishReason: "stop", totalUsage: null },
]);
const fallback = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: true,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
const fallbackSse = await fallback.response.text();
const completed = (await responsesFromChatSse(fallbackSse)).find(
(event) => event.event === "response.completed"
);
assert.deepEqual(completed?.data.response.usage, {
input_tokens: 4,
input_tokens_details: { cached_tokens: 0 },
output_tokens: 3,
output_tokens_details: { reasoning_tokens: 0 },
total_tokens: 7,
});
});
test("Command Code preserves tool-call streaming while finalizing finish-step usage", async () => {
globalThis.fetch = async () =>
commandCodeStream([
{
type: "tool-call",
toolCallId: "call_1",
toolName: "lookup",
input: { query: "hello" },
},
{ type: "finish-step", usage: { inputTokens: 3, outputTokens: 2, totalTokens: 5 } },
{ type: "finish", finishReason: "tool-calls" },
]);
const { response } = await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: true,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Hi" }] },
});
const sse = await response.text();
assert.ok(
parseSsePayloads(sse).some(
(chunk) => chunk.choices?.[0]?.delta?.tool_calls?.[0]?.id === "call_1"
)
);
const completed = (await responsesFromChatSse(sse)).find(
(event) => event.event === "response.completed"
);
assert.equal(
completed?.data.response.output?.some((item) => item.type === "function_call"),
true
);
assert.deepEqual(completed?.data.response.usage, {
input_tokens: 3,
input_tokens_details: { cached_tokens: 0 },
output_tokens: 2,
output_tokens_details: { reasoning_tokens: 0 },
total_tokens: 5,
});
});
const text = await response.text();
// The upstream OpenAI usage chunk passes through unchanged, including the
// standard OpenAI usage shape the stream pipeline already understands.
assert.ok(text.includes('"prompt_tokens":10'));
assert.ok(text.includes('"cached_tokens":4'));
assert.ok(text.includes('"reasoning_tokens":1'));
assert.ok(text.includes("data: [DONE]"));
});

View File

@@ -34,7 +34,7 @@ test.after(() => {
core.resetDbInstance();
});
async function captureParams(body: Record<string, unknown>): Promise<FetchCall> {
async function captureBody(body: Record<string, unknown>): Promise<FetchCall> {
const calls: FetchCall[] = [];
globalThis.fetch = async (url: any, init: any = {}) => {
calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) });
@@ -50,27 +50,27 @@ async function captureParams(body: Record<string, unknown>): Promise<FetchCall>
}
test("Command Code omits max_tokens when the client sends max_tokens: -1 (#5166)", async () => {
const call = await captureParams({ max_tokens: -1 });
const call = await captureBody({ max_tokens: -1 });
assert.ok(
!("max_tokens" in call.body.params),
`max_tokens:-1 must be omitted, got params.max_tokens=${call.body.params.max_tokens}`
!("max_tokens" in call.body),
`max_tokens:-1 must be omitted, got max_tokens=${call.body.max_tokens}`
);
});
test("Command Code omits max_tokens when the client sends max_completion_tokens: -1 (#5166)", async () => {
const call = await captureParams({ max_completion_tokens: -1 });
const call = await captureBody({ max_completion_tokens: -1 });
assert.ok(
!("max_tokens" in call.body.params),
`max_completion_tokens:-1 must be omitted, got params.max_tokens=${call.body.params.max_tokens}`
!("max_tokens" in call.body),
`max_completion_tokens:-1 must be omitted, got max_tokens=${call.body.max_tokens}`
);
});
test("Command Code omits max_tokens when the client sends 0 (#5166)", async () => {
const call = await captureParams({ max_tokens: 0 });
assert.ok(!("max_tokens" in call.body.params), "max_tokens:0 must be omitted");
const call = await captureBody({ max_tokens: 0 });
assert.ok(!("max_tokens" in call.body), "max_tokens:0 must be omitted");
});
test("Command Code still honors a positive client max_tokens after the #5166 fix", async () => {
const call = await captureParams({ max_tokens: 2048 });
assert.equal(call.body.params.max_tokens, 2048);
});
const call = await captureBody({ max_tokens: 2048 });
assert.equal(call.body.max_tokens, 2048);
});

View File

@@ -1,14 +1,13 @@
/**
* Regression test for #5166 (user-content-array 400 on Command Code / deepseek-v4-pro).
* #5166 (user-content-array 400 on Command Code / deepseek-v4-pro) context.
*
* When a client sends a user message whose `content` is an array of content parts
* (e.g. [{type:"text",text:"Hello"},{type:"text",text:"World"}]), the raw array
* must NOT reach the Command Code upstream — it requires user content to be a plain
* string. The executor must normalise the array to a string before posting.
*
* NOTE: this file covers ONLY the user-content-array/400 symptom of #5166.
* The 0-output-token symptom on mimo-v2.5-pro (reasoning-only models) is tracked
* separately and is NOT addressed here.
* The original regression was that a user message whose `content` was an array of
* content parts reached the CLI-only /alpha/generate endpoint, which required
* user content to be a plain string. Since #10265 the executor posts to the
* documented /provider/v1/chat/completions endpoint, which natively speaks the
* OpenAI chat.completions format — array content (text + image_url parts) is
* valid there and passes through unchanged. These tests pin that OpenAI-shaped
* passthrough.
*/
import test from "node:test";
import assert from "node:assert/strict";
@@ -26,9 +25,8 @@ const core = await import("../../src/lib/db/core.ts");
const originalFetch = globalThis.fetch;
function commandCodeStream(lines: unknown[]) {
const text = lines.map((l) => JSON.stringify(l)).join("\n") + "\n";
return new Response(text, { status: 200, headers: { "Content-Type": "application/x-ndjson" } });
function okResponse() {
return new Response("{}", { status: 200, headers: { "Content-Type": "application/json" } });
}
test.after(() => {
@@ -41,155 +39,100 @@ test.afterEach(() => {
globalThis.fetch = originalFetch;
});
// ── helpers ────────────────────────────────────────────────────────────────────
// ── helpers ────────────────────────────────────────────────────────────
type FetchCall = { url: string; init: Record<string, unknown>; body: Record<string, unknown> };
function captureFetch(response: Response) {
const calls: FetchCall[] = [];
globalThis.fetch = async (url, init: RequestInit = {}) => {
calls.push({ url: String(url), init: init as Record<string, unknown>, body: JSON.parse(String(init.body)) });
calls.push({
url: String(url),
init: init as Record<string, unknown>,
body: JSON.parse(String(init.body)),
});
return response;
};
return calls;
}
// ── failing tests (before fix, user content is the raw array) ──────────────
test("#5166 user message with multi-part array content passes through as an OpenAI array", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Hello" },
{ type: "text", text: "World" },
],
},
],
},
});
test(
"#5166 user message with multi-part array content is flattened to a string (#5166)",
async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
const userMsg = (calls[0].body.messages as Record<string, unknown>[])[0];
// OpenAI array content is valid on /provider/v1 — forwarded as-is.
assert.ok(Array.isArray(userMsg.content), "array content forwarded (no CLI flattening)");
const parts = userMsg.content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[0].text, "Hello");
assert.equal(parts[1].text, "World");
});
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Hello" },
{ type: "text", text: "World" },
],
},
],
},
});
test("#5166 user message with single text-part array passes through", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [{ role: "user", content: [{ type: "text", text: "Hi there" }] }],
},
});
const userMsg = (calls[0].body.messages as Record<string, unknown>[])[0];
const parts = userMsg.content as Record<string, unknown>[];
assert.equal(parts.length, 1);
assert.equal(parts[0].text, "Hi there");
});
const posted = calls[0].body;
const userMsg = (posted.params as Record<string, unknown[]>).messages[0] as Record<
string,
unknown
>;
test("#5166 user message with plain string content passes through unchanged", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Plain string message" }] },
});
const userMsg = (calls[0].body.messages as Record<string, unknown>[])[0];
assert.equal(userMsg.content, "Plain string message");
});
// Must be a string — never an array — otherwise Command Code's upstream returns 400.
assert.equal(
typeof userMsg.content,
"string",
`user message content must be a string, got ${typeof userMsg.content}`
);
// Joined text parts with "\n"
assert.equal(userMsg.content, "Hello\nWorld");
}
);
test(
"#5166 user message with single text-part array is flattened to a plain string",
async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [{ type: "text", text: "Hi there" }],
},
],
},
});
const posted = calls[0].body;
const userMsg = (posted.params as Record<string, unknown[]>).messages[0] as Record<
string,
unknown
>;
assert.equal(typeof userMsg.content, "string");
assert.equal(userMsg.content, "Hi there");
}
);
test(
"#5166 user message with plain string content passes through unchanged (no regression)",
async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: "Plain string message",
},
],
},
});
const posted = calls[0].body;
const userMsg = (posted.params as Record<string, unknown[]>).messages[0] as Record<
string,
unknown
>;
assert.equal(typeof userMsg.content, "string");
assert.equal(userMsg.content, "Plain string message");
}
);
test(
"#5166 user message with mixed parts (text + image_url) keeps only text parts",
async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this:" },
{ type: "image_url", image_url: { url: "https://example.com/img.png" } },
],
},
],
},
});
const posted = calls[0].body;
const userMsg = (posted.params as Record<string, unknown[]>).messages[0] as Record<
string,
unknown
>;
assert.equal(typeof userMsg.content, "string");
// Only text parts extracted; image_url part is dropped (not a "text" type)
assert.equal(userMsg.content, "Describe this:");
}
);
test("#5166 user message with mixed parts (text + image_url) keeps all parts", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this:" },
{ type: "image_url", image_url: { url: "https://example.com/img.png" } },
],
},
],
},
});
const userMsg = (calls[0].body.messages as Record<string, unknown>[])[0];
const parts = userMsg.content as Record<string, unknown>[];
assert.equal(parts.length, 2, "text + image both preserved");
assert.equal(parts[0].text, "Describe this:");
assert.equal(parts[1].type, "image_url");
});

View File

@@ -1,9 +1,13 @@
/**
* Vision / multimodal support tests for the Command Code executor.
*
* Verifies that vision-capable models (MiniMax M3, MiMo V2.5, Kimi K2, Qwen 3.x, GPT-5, Claude 3/4, Fable 5, Gemini 3.x, Stepfun, Fugu, etc.)
* receive image parts in Command Code CLI format, while text-only
* models strip images as before (no regression).
* Since #10265 the executor posts to the documented /provider/v1/chat/completions
* endpoint, which speaks the standard OpenAI chat.completions format. User image
* content (OpenAI `image_url` parts and Anthropic Messages-style source blocks)
* passes through unchanged — the endpoint natively understands both shapes, so
* there is no CLI-specific conversion (and no CLI-wire image stripping) left to
* verify. These tests pin that passthrough plus the #10809 wire-model
* normalization, which still applies to /provider/v1.
*/
import test from "node:test";
import assert from "node:assert/strict";
@@ -19,9 +23,8 @@ const core = await import("../../src/lib/db/core.ts");
const originalFetch = globalThis.fetch;
function commandCodeStream(lines: unknown[]) {
const text = lines.map((l) => JSON.stringify(l)).join("\n") + "\n";
return new Response(text, { status: 200, headers: { "Content-Type": "application/x-ndjson" } });
function okResponse() {
return new Response("{}", { status: 200, headers: { "Content-Type": "application/json" } });
}
test.after(() => {
@@ -52,44 +55,28 @@ function captureFetch(response: Response) {
}
function userContent(calls: FetchCall[]): unknown {
return (
(calls[0].body.params as Record<string, unknown[]>).messages as Record<string, unknown>[]
)[0].content;
return (calls[0].body.messages as Record<string, unknown>[])[0].content;
}
function wireModel(calls: FetchCall[]): string {
return calls[0].body.model as string;
}
// ── wire model normalization (#10809) ────────────────────────────────
//
// Command Code's /alpha/generate endpoint serves most models under a
// vendor-prefixed wire id and defaults an unprefixed id to the `anthropic:`
// provider (403 "Model/provider not recognized: anthropic:<id>"). A bare id
// reaches the executor when an operator sets a custom vision model in the
// Vision Bridge picker (e.g. `command-code/mimo-v2.5`). The executor must
// normalize to the documented vendor-prefixed wire form.
function wireModel(calls: FetchCall[]): string {
return (calls[0].body.params as Record<string, unknown>).model as string;
}
test("#10809: command-code/mimo-v2.5 wire model is normalized to xiaomi/mimo-v2.5", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "command-code/mimo-v2.5",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
model: "command-code/mimo-v2.5",
messages: [{ role: "user", content: "hi" }],
},
body: { model: "command-code/mimo-v2.5", messages: [{ role: "user", content: "hi" }] },
});
assert.equal(wireModel(calls), "xiaomi/mimo-v2.5");
});
test("#10809: cmd/mimo-v2.5 (alias prefix) is also normalized", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "cmd/mimo-v2.5",
stream: false,
@@ -100,9 +87,7 @@ test("#10809: cmd/mimo-v2.5 (alias prefix) is also normalized", async () => {
});
test("#10809: already vendor-prefixed wire ids pass through unchanged", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "command-code/deepseek/deepseek-v4-pro",
stream: false,
@@ -115,13 +100,10 @@ test("#10809: already vendor-prefixed wire ids pass through unchanged", async ()
assert.equal(wireModel(calls), "deepseek/deepseek-v4-pro");
});
// ── vision models: image parts preserved in CC CLI format ─────────────
test("vision model minimax-m3 preserves image_url part as CC CLI {type:image}", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
// ── image content passthrough (OpenAI /provider/v1 surface) ──────────
test("image_url parts pass through unchanged (text + image preserved)", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "MiniMaxAI/MiniMax-M3",
stream: false,
@@ -132,451 +114,25 @@ test("vision model minimax-m3 preserves image_url part as CC CLI {type:image}",
role: "user",
content: [
{ type: "text", text: "What's in this?" },
{
type: "image_url",
image_url: { url: "data:image/png;base64,iVBORw0KGgo=" },
},
{ type: "image_url", image_url: { url: "data:image/png;base64,iVBORw0KGgo=" } },
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content), "vision model user content must be an array");
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
// Text part preserved
assert.equal(parts[0].type, "text");
assert.equal(parts[0].text, "What's in this?");
// Image part converted to CC CLI format
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "data:image/png;base64,iVBORw0KGgo=");
const content = userContent(calls) as Record<string, unknown>[];
assert.equal(content.length, 2);
assert.equal(content[0].type, "text");
assert.equal(content[1].type, "image_url", "image_url part preserved as-is");
assert.equal(
(content[1].image_url as { url: string }).url,
"data:image/png;base64,iVBORw0KGgo="
);
});
test("vision model minimax-m3 preserves image_url with HTTP URL", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe" },
{
type: "image_url",
image_url: { url: "https://example.com/photo.jpg" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "https://example.com/photo.jpg");
});
test("vision model mimo-v2.5 preserves image parts", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "mimo-v2.5",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Analyze" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
});
test("vision model mimo-v2.5-pro is text-only (no image parts)", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "mimo-v2.5-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Hi" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
],
},
],
},
});
const content = userContent(calls);
// mimo-v2.5-pro is text-only — content must be flattened to a plain string
assert.equal(typeof content, "string");
assert.equal(content, "Hi");
});
test("vision model mimo-v2-omni preserves image parts", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "mimo-v2-omni",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Check" },
{
type: "image_url",
image_url: { url: "data:image/jpeg;base64,/9j/4AAQ=" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "data:image/jpeg;base64,/9j/4AAQ=");
});
// ── non-vision models: images still stripped (no regression) ──────────
test("text-only model deepseek-v4-pro strips image_url parts", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Hello" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
],
},
],
},
});
const content = userContent(calls);
// Non-vision model: content is a plain string, images stripped
assert.equal(typeof content, "string");
assert.equal(content, "Hello");
});
test("text-only model deepseek-v4-flash strips image_url parts (no regression)", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-flash",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Text only" },
{
type: "image_url",
image_url: { url: "data:image/png;base64,AAA=" },
},
],
},
],
},
});
const content = userContent(calls);
assert.equal(typeof content, "string");
assert.equal(content, "Text only");
});
// ── edge cases ────────────────────────────────────────────────────────
test("vision model with only image content emits empty text fallback", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{
type: "image_url",
image_url: { url: "data:image/png;base64,iVBOR=" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
// Single image part preserved — no empty text injected because
// the image itself keeps content non-empty.
assert.equal(parts.length, 1);
assert.equal(parts[0].type, "image");
assert.equal(parts[0].image, "data:image/png;base64,iVBOR=");
});
test("vision model passes plain string content through unchanged", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [{ role: "user", content: "Plain string message" }],
},
});
const content = userContent(calls);
assert.equal(typeof content, "string");
assert.equal(content, "Plain string message");
});
test("vision model honors body.model rewrite for vision detection", async () => {
// #5166 scenario: body.model overwrites the execute model arg.
// Vision detection must use the rewritten model id.
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
// execute() gets a non-vision combo model, body.model rewrites to a vision model
await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
model: "MiniMaxAI/MiniMax-M3",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
],
},
],
},
});
const content = userContent(calls);
// body.model = MiniMax-M3 (vision) → images preserved
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
});
test("vision model with multiple image parts preserves all of them", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Compare" },
{
type: "image_url",
image_url: { url: "https://example.com/a.jpg" },
},
{
type: "image_url",
image_url: { url: "https://example.com/b.jpg" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 3);
assert.equal(parts[0].type, "text");
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "https://example.com/a.jpg");
assert.equal(parts[2].type, "image");
assert.equal(parts[2].image, "https://example.com/b.jpg");
});
test("vision model with image_url as plain string (no object wrapper) still works", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Look" },
{
type: "image_url",
image_url: "https://example.com/img.png",
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "https://example.com/img.png");
});
// ── CC vision models (Command Code docs registry) ──────────────────
const VISION_CASES = [
["Kimi K2.6", "moonshotai/Kimi-K2.6"],
["Kimi K2.7 Code", "moonshotai/Kimi-K2.7-Code"],
["Kimi K2.5", "moonshotai/Kimi-K2.5"],
["Qwen 3.6 Plus", "Qwen/Qwen3.6-Plus"],
["Qwen 3.7 Plus", "Qwen/Qwen3.7-Plus"],
["Step 3.7 Flash", "stepfun/Step-3.7-Flash"],
["GPT-5.5", "gpt-5.5"],
["GPT-5.4", "gpt-5.4"],
["GPT-5.3 Codex", "gpt-5.3-codex"],
["GPT-5.4 Mini", "gpt-5.4-mini"],
["Claude Fable 5", "claude-fable-5"],
["Sakana Fugu Ultra", "sakana/fugu-ultra"],
["Claude Opus 4.7 (isVisionModelId)", "claude-opus-4-7"],
["Claude Sonnet 4.6 (isVisionModelId)", "claude-sonnet-4-6"],
["Gemini 3.5 Flash (isVisionModelId)", "google/gemini-3.5-flash"],
];
for (const [name, model] of VISION_CASES) {
test(`vision model ${name} preserves image parts`, async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model,
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Check" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content), `${name} user content must be an array`);
const parts = content;
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
});
}
// ── Anthropic-shaped image blocks (Zoo Code / Claude-Code-compatible clients) ──
test("vision model mimo-v2.5 preserves Anthropic source.base64 image block", async () => {
// Zoo Code sends Messages-API-shaped content blocks to the OpenAI
// /v1/chat/completions surface: { type:"image", source:{ base64 } }.
// The vision-bridge guardrail skips vision-capable models (cmd/xiaomi/mimo-v2.5
// resolves supportsVision=true via the mimo-v2.5 leaf spec), so the raw block
// must survive to the executor and be converted to CC CLI { type:"image" }.
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
test("Anthropic Messages-style source image blocks pass through unchanged", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "xiaomi/mimo-v2.5",
stream: false,
@@ -601,24 +157,15 @@ test("vision model mimo-v2.5 preserves Anthropic source.base64 image block", asy
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content), "user content must be an array");
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2, "text + image parts preserved");
assert.equal(parts[0].type, "text");
assert.equal(parts[1].type, "image");
assert.equal(
parts[1].image,
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
"base64 payload is rebuilt into a CC CLI data URL"
);
const content = userContent(calls) as Record<string, unknown>[];
assert.equal(content.length, 2, "text + image parts preserved");
assert.equal(content[1].type, "image");
assert.equal((content[1].source as { type: string }).type, "base64");
assert.equal((content[1].source as { data: string }).data, "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==");
});
test("vision model preserves Anthropic source.url image block", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
test("Anthropic source.url image block passes through unchanged", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "xiaomi/mimo-v2.5",
stream: false,
@@ -629,31 +176,23 @@ test("vision model preserves Anthropic source.url image block", async () => {
role: "user",
content: [
{ type: "text", text: "Look" },
{
type: "image",
source: { type: "url", url: "https://example.com/img.png" },
},
{ type: "image", source: { type: "url", url: "https://example.com/img.png" } },
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content));
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "https://example.com/img.png");
const content = userContent(calls) as Record<string, unknown>[];
assert.equal(content.length, 2);
assert.equal(content[1].type, "image");
assert.deepEqual(content[1].source, { type: "url", url: "https://example.com/img.png" });
});
test("text-only model deepseek-v4-flash strips Anthropic source.base64 image block", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
test("multiple image parts are all preserved", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "deepseek/deepseek-v4-flash",
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
@@ -661,44 +200,41 @@ test("text-only model deepseek-v4-flash strips Anthropic source.base64 image blo
{
role: "user",
content: [
{ type: "text", text: "Text only" },
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
},
},
{ type: "text", text: "Compare" },
{ type: "image_url", image_url: { url: "https://example.com/a.jpg" } },
{ type: "image_url", image_url: { url: "https://example.com/b.jpg" } },
],
},
],
},
});
const content = userContent(calls) as Record<string, unknown>[];
assert.equal(content.length, 3);
assert.equal(content[1].type, "image_url");
assert.equal(content[2].type, "image_url");
});
test("plain string content passes through unchanged", async () => {
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "minimax-m3",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: { messages: [{ role: "user", content: "Plain string message" }] },
});
const content = userContent(calls);
// Text-only model: content flattened to plain string, image stripped.
assert.equal(typeof content, "string");
assert.equal(content, "Text only");
assert.equal(content, "Plain string message");
});
// ── conservative vision family lock (gpt-5.4-mini / gpt-5.3-codex) ─────
//
// These two ids stay INSIDE the `/gpt-5/` vision family: both accept image
// input on the OpenAI API, and there is no verified Command Code backend data
// marking them text-only. These tests pin that conservative executor behavior
// so a future "narrow the regex" change cannot silently strip images from models
// that can see them (the #4071 regression class). The #10703 Vision Bridge
// candidate-list fix lives in the shared capability resolution
// (KNOWN_TEXT_ONLY_DESPITE_SYNC), NOT in the executor's wire transform.
test("gpt-5.4-mini keeps image parts (conservative vision family lock)", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
test("text-only model still forwards image parts (passthrough, no CLI stripping)", async () => {
// The /provider/v1 OpenAI surface accepts image content for any model id; the
// executor forwards content untouched, so there is no text-only stripping.
const calls = captureFetch(okResponse());
await getExecutor("command-code").execute({
model: "gpt-5.4-mini",
model: "deepseek/deepseek-v4-pro",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
@@ -706,54 +242,15 @@ test("gpt-5.4-mini keeps image parts (conservative vision family lock)", async (
{
role: "user",
content: [
{ type: "text", text: "What's in this?" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
{ type: "text", text: "Hello" },
{ type: "image_url", image_url: { url: "https://example.com/img.png" } },
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content), "gpt-5.4-mini must be treated as vision-capable");
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "https://example.com/img.png");
});
test("gpt-5.3-codex keeps image parts (conservative vision family lock)", async () => {
const calls = captureFetch(
commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }])
);
await getExecutor("command-code").execute({
model: "gpt-5.3-codex",
stream: false,
credentials: { apiKey: "cc_test_key" },
body: {
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe" },
{
type: "image_url",
image_url: { url: "https://example.com/img.png" },
},
],
},
],
},
});
const content = userContent(calls);
assert.ok(Array.isArray(content), "gpt-5.3-codex must be treated as vision-capable");
const parts = content as Record<string, unknown>[];
assert.equal(parts.length, 2);
assert.equal(parts[1].type, "image");
assert.equal(parts[1].image, "https://example.com/img.png");
});
const content = userContent(calls) as Record<string, unknown>[];
assert.equal(content.length, 2, "content array forwarded unchanged");
assert.equal(content[1].type, "image_url");
});

View File

@@ -14,11 +14,15 @@ describe("CommandCodeExecutor", () => {
assert.ok(executor);
});
it("buildUrl returns a string", () => {
it("buildUrl targets the documented /provider/v1/chat/completions endpoint (#10265)", () => {
const executor = new mod.CommandCodeExecutor();
const url = executor.buildUrl();
assert.ok(typeof url === "string");
assert.ok(url.includes("generate") && url.includes("commandcode"));
assert.ok(
url.includes("/provider/v1/chat/completions"),
`expected the documented provider API endpoint, got: ${url}`
);
assert.ok(url.includes("commandcode"));
});
it("execute throws when no API key", async () => {
@@ -57,7 +61,7 @@ describe("CommandCodeExecutor", () => {
}
});
it("assistant tool-call conversion always emits a valid required arguments field (#regression input[N] missing required field arguments)", async () => {
it("posts a flat OpenAI chat.completions body (no CLI envelope) to /provider/v1/chat/completions (#10265)", async () => {
const calls: Array<{ url: string; init: RequestInit; body: unknown }> = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = (async (url: string | URL | Request, init?: RequestInit) => {
@@ -70,182 +74,8 @@ describe("CommandCodeExecutor", () => {
}) as typeof fetch;
const executor = new mod.CommandCodeExecutor();
const pairedId = "call_paired";
const body = {
messages: [
{ role: "user", content: "hi" },
{
role: "assistant",
content: "",
tool_calls: [
// Missing arguments entirely -> must still get a valid arguments field
{ id: "call_missing", type: "function", function: { name: "lookup" } },
// Empty string arguments -> "{}"
{
id: "call_empty",
type: "function",
function: { name: "lookup", arguments: "" },
},
// Valid object arguments -> round-trips as JSON string
{
id: pairedId,
type: "function",
function: { name: "lookup", arguments: { q: "docs" } },
},
// Valid string arguments -> preserved as-is
{
id: "call_string",
type: "function",
function: { name: "lookup", arguments: '{"q":"string"}' },
},
// Invalid JSON string arguments -> defaults to "{}"
{
id: "call_invalid",
type: "function",
function: { name: "lookup", arguments: "{invalid-json" },
},
// Tool call without name -> defaults tool-result toolName to "unknown"
{
id: "call_unnamed",
type: "function",
function: { arguments: { q: "unnamed" } },
},
],
},
{ role: "tool", tool_call_id: "call_missing", content: "r1" },
{ role: "tool", tool_call_id: "call_empty", content: "r2" },
{ role: "tool", tool_call_id: pairedId, content: "r3" },
{ role: "tool", tool_call_id: "call_string", content: "r4" },
{ role: "tool", tool_call_id: "call_invalid", content: "r5" },
{ role: "tool", tool_call_id: "call_unnamed", content: "r6" },
],
};
try {
await executor.execute({
model: "test",
body,
stream: false,
credentials: { apiKey: "fake-key" },
signal: null,
});
} finally {
globalThis.fetch = originalFetch;
}
assert.equal(calls.length, 1, "exactly one upstream call");
const sentBody = calls[0].body as {
params: { messages: Array<{ role: string; content: unknown }> };
};
const assistant = sentBody.params.messages.find((m) => m.role === "assistant");
assert.ok(assistant, "assistant turn present");
const parts = assistant.content as Array<Record<string, unknown>>;
const toolCalls = parts.filter((p) => p.type === "tool-call");
assert.equal(toolCalls.length, 6, "all six paired tool calls converted");
for (const call of toolCalls) {
assert.equal(
typeof call.arguments,
"string",
`tool-call ${String(call.toolCallId)} must carry a string arguments field`
);
const parsed = JSON.parse(call.arguments as string);
assert.equal(typeof parsed, "object");
assert.ok(!Array.isArray(parsed), "arguments must parse to a JSON object");
}
const byId = new Map(toolCalls.map((c) => [String(c.toolCallId), c]));
assert.equal(byId.get("call_missing").arguments, "{}", "missing arguments -> empty object");
assert.equal(byId.get("call_empty").arguments, "{}", "empty string arguments -> empty object");
assert.equal(
byId.get(pairedId).arguments,
'{"q":"docs"}',
"object arguments round-trip as JSON string"
);
assert.equal(
byId.get("call_string").arguments,
'{"q":"string"}',
"valid string arguments preserved as-is"
);
assert.equal(
byId.get("call_invalid").arguments,
"{}",
"invalid JSON string arguments -> empty object"
);
const toolMsgs = sentBody.params.messages.filter((m) => m.role === "tool");
assert.equal(toolMsgs.length, 6, "all 6 tool result messages present");
const resultByName = new Map(
toolMsgs.map((m) => {
const p = (m.content as Array<Record<string, unknown>>)[0];
return [String(p.toolCallId), String(p.toolName)];
})
);
assert.equal(resultByName.get("call_missing"), "lookup");
assert.equal(
resultByName.get("call_unnamed"),
"unknown",
"unnamed call falls back to 'unknown'"
);
// /alpha/generate also requires `arguments` on tool-result parts; a
// missing one is rejected with `input[N] missing required field 'arguments'`
// (the index landing on the tool message). Echo the paired call's
// normalized arguments.
const resultById = new Map(
toolMsgs.map((m) => {
const p = (m.content as Array<Record<string, unknown>>)[0];
return [String(p.toolCallId), p];
})
);
assert.equal(resultById.size, 6, "each tool result maps to its call id");
for (const p of resultById.values()) {
assert.equal(
typeof p.arguments,
"string",
`tool-result ${String(p.toolCallId)} must carry a string arguments field`
);
const parsed = JSON.parse(p.arguments as string);
assert.equal(typeof parsed, "object");
assert.ok(!Array.isArray(parsed), "tool-result arguments must parse to a JSON object");
}
assert.equal(
resultById.get("call_missing").arguments,
"{}",
"tool-result echoes paired call's missing arguments as empty object"
);
assert.equal(
resultById.get(pairedId).arguments,
'{"q":"docs"}',
"tool-result echoes paired call's object arguments as JSON string"
);
assert.equal(
resultById.get("call_string").arguments,
'{"q":"string"}',
"tool-result echoes paired call's valid string arguments as-is"
);
assert.equal(
resultById.get("call_empty").arguments,
"{}",
"tool-result echoes paired call's empty arguments as empty object"
);
assert.equal(
resultById.get("call_invalid").arguments,
"{}",
"tool-result echoes paired call's invalid JSON arguments as empty object"
);
});
it("COMMAND_CODE_VERSION default constant is 1.15.1", () => {
assert.equal(mod.COMMAND_CODE_VERSION, "1.15.1");
});
it("renames tool names colliding with upstream built-ins on the wire and un-renames on the response (#regression input[N] missing required field arguments from a tool_search result)", async () => {
// Upstream /alpha/generate normalizes tool-call/result parts against its
// OWN built-in registry for matching names; `tool_search` collides and its
// result is rejected with `input[N] missing required field 'arguments'`.
// Verified live: renaming the pair to a non-colliding name passes.
const body = {
model: "gpt-5.4",
messages: [
{ role: "user", content: "hi" },
{
@@ -253,29 +83,19 @@ describe("CommandCodeExecutor", () => {
content: "",
tool_calls: [
{
id: "call_00_AAAAAAAAAAAAAAAAA",
id: "call_1",
type: "function",
function: { name: "tool_search", arguments: '{"query":"x"}' },
},
{
id: "call_01_BBBBBBBBBBBBBBBBB",
type: "function",
function: { name: "lookup", arguments: '{"q":"1"}' },
function: { name: "lookup", arguments: '{"q":"docs"}' },
},
// Missing arguments entirely stays missing — passthrough, no CLI
// envelope injection of a synthetic `arguments` field.
{ id: "call_2", type: "function", function: { name: "search" } },
],
},
{ role: "tool", tool_call_id: "call_00_AAAAAAAAAAAAAAAAA", content: "r1" },
{ role: "tool", tool_call_id: "call_01_BBBBBBBBBBBBBBBBB", content: "r2" },
{ role: "tool", tool_call_id: "call_1", content: "r1" },
{ role: "tool", tool_call_id: "call_2", content: "r2" },
],
tools: [
{
type: "function",
function: {
name: "tool_search",
description: "search tools",
parameters: { type: "object", properties: { query: { type: "string" } } },
},
},
{
type: "function",
function: {
@@ -287,31 +107,9 @@ describe("CommandCodeExecutor", () => {
],
};
const calls: Array<{ url: string; init: RequestInit; body: unknown }> = [];
const originalFetch = globalThis.fetch;
// execute() makes a single upstream fetch; capture the wire request and
// return a stream with a tool-call event using the renamed wire name.
globalThis.fetch = (async (url: string | URL | Request, init?: RequestInit) => {
calls.push({
url: String(url),
init: init || {},
body: JSON.parse(String((init as RequestInit | undefined)?.body)),
});
const streamBody =
'data: {"type":"tool-call","toolCallId":"c1","toolName":"omniroute_tool_search","input":{"query":"x"}}\n\n' +
'data: {"type":"finish","finishReason":"tool_use"}\n\n' +
"data: [DONE]\n\n";
return new Response(streamBody, {
status: 200,
headers: { "Content-Type": "text/event-stream" },
});
}) as typeof fetch;
const executor = new mod.CommandCodeExecutor();
let result: { response: Response } | null = null;
try {
result = await executor.execute({
model: "test",
await executor.execute({
model: "gpt-5.4",
body,
stream: false,
credentials: { apiKey: "fake-key" },
@@ -321,46 +119,85 @@ describe("CommandCodeExecutor", () => {
globalThis.fetch = originalFetch;
}
const sentBody = calls[0].body as {
params: {
messages: Array<{ role: string; content: unknown }>;
tools: Array<{ name: string }>;
};
};
const toolDefNames = sentBody.params.tools.map((t) => t.name);
assert.equal(calls.length, 1, "exactly one upstream call");
assert.ok(
toolDefNames.includes("omniroute_tool_search"),
"colliding tool def renamed on the wire"
calls[0].url.includes("/provider/v1/chat/completions"),
`expected documented provider endpoint, got: ${calls[0].url}`
);
assert.ok(toolDefNames.includes("lookup"), "non-colliding tool def untouched");
const assistant = sentBody.params.messages.find((m) => m.role === "assistant");
const toolCallParts = (assistant?.content as Array<Record<string, unknown>>).filter(
(p) => p.type === "tool-call"
const sent = calls[0].body as Record<string, unknown>;
// No CLI envelope.
assert.equal(sent.config, undefined, "CLI envelope `config` must not be sent");
assert.equal(sent.params, undefined, "CLI envelope `params` wrapper must not be sent");
assert.equal(sent.model, "gpt-5.4", "flat OpenAI model at top level");
assert.equal((sent.messages as Array<{ role: string }>)[0].role, "user");
// Assistant tool_calls pass through unchanged (no CLI tool-call/tool-result parts).
const assistant = (sent.messages as Array<Record<string, unknown>>).find(
(m) => m.role === "assistant"
);
const toolSearchCall = toolCallParts.find((p) => p.toolName === "omniroute_tool_search");
assert.ok(toolSearchCall, "assistant tool-call part renamed on the wire");
const lookupCall = toolCallParts.find((p) => p.toolName === "lookup");
assert.ok(lookupCall, "non-colliding tool-call part untouched");
const toolMsgs = sentBody.params.messages.filter((m) => m.role === "tool");
const toolSearchResult = toolMsgs.find(
(m) => (m.content as Array<Record<string, unknown>>)[0]?.toolName === "omniroute_tool_search"
assert.ok(assistant, "assistant turn present");
const toolCalls = assistant?.tool_calls as Array<{
id: string;
function: { name: string; arguments?: string };
}>;
assert.equal(toolCalls.length, 2, "both tool calls pass through untouched");
assert.equal(toolCalls[0].function.name, "lookup");
assert.equal(toolCalls[0].function.arguments, '{"q":"docs"}');
assert.equal(toolCalls[1].function.arguments, undefined, "missing arguments stays missing (no injection)");
// Tool role message (OpenAI flat) preserved.
const toolMsg = (sent.messages as Array<Record<string, unknown>>).find(
(m) => m.role === "tool"
);
assert.ok(toolSearchResult, "tool-result part renamed on the wire");
// Response path: upstream emits the renamed wire name; the client must get
// its original name back.
assert.ok(result, "execute returned a response");
const json = (await result.response.json()) as {
choices: Array<{ message: { tool_calls?: Array<{ function: { name: string } }> } }>;
};
const toolCalls = json.choices[0].message.tool_calls ?? [];
assert.equal(toolCalls.length, 1, "one tool call translated");
assert.equal(toolMsg?.tool_call_id, "call_1");
assert.equal(
toolCalls[0].function.name,
"tool_search",
"renamed wire name un-renamed for the client"
(sent.tools as Array<{ function: { name: string } }>)[0].function.name,
"lookup",
"tool definitions pass through in OpenAI shape (no rename)"
);
});
});
it("passes through the upstream OpenAI response and drops CLI-impersonation headers (#10265)", async () => {
const calls: Array<{ url: string; init: RequestInit }> = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = (async (url: string | URL | Request, init?: RequestInit) => {
calls.push({ url: String(url), init: init || {} });
const chunk =
'data: {"id":"c1","object":"chat.completion.chunk","model":"gpt-5.4",' +
'"choices":[{"index":0,"delta":{"content":"hi"}}]}\n\n' +
'data: {"id":"c1","object":"chat.completion.chunk","model":"gpt-5.4",' +
'"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":' +
'{"prompt_tokens":2,"completion_tokens":1,"total_tokens":3}}\n\n' +
"data: [DONE]\n\n";
return new Response(chunk, {
status: 200,
headers: { "Content-Type": "text/event-stream" },
});
}) as typeof fetch;
const executor = new mod.CommandCodeExecutor();
let result: { response: Response; headers: Record<string, string> } | null = null;
try {
result = await executor.execute({
model: "gpt-5.4",
body: { messages: [{ role: "user", content: "hi" }] },
stream: true,
credentials: { apiKey: "fake-key" },
signal: null,
});
} finally {
globalThis.fetch = originalFetch;
}
assert.ok(result, "execute returned a result");
const headers = result.headers;
assert.equal(headers["x-command-code-version"], undefined, "CLI-impersonation header dropped");
assert.equal(headers["x-cli-environment"], undefined, "CLI-impersonation header dropped");
assert.equal(headers.Authorization, "Bearer fake-key");
// The upstream OpenAI SSE passes through untouched (no CLI re-parsing).
const text = await result.response.text();
assert.ok(text.includes("chat.completion.chunk"), "OpenAI-format SSE passed through");
assert.ok(text.includes('"content":"hi"'), "delta content preserved");
assert.ok(text.includes("[DONE]"), "stream terminator preserved");
assert.ok(text.includes('"prompt_tokens":2'), "OpenAI usage block passed through");
});
});

View File

@@ -8,12 +8,12 @@ import { resolveChatCoreTargetFormat } from "../../open-sse/handlers/chatCore/ta
// ghe-copilot catalog. getModelTargetFormat falls back to getGlobalModel() when
// the provider's own catalog lacks the model id, importing the DECLARING
// provider's endpoint semantics into every other provider serving the same id.
// command-code's chat-shaped /alpha/generate executor then received a
// command-code's chat-shaped executor then received a
// Responses-format body (input, not messages) and shipped `messages: []`
// upstream — upstream rejected with "Invalid prompt: messages must not be empty"
// (502). Model-level targetFormat is provider-scoped: it must not leak.
test("model-level targetFormat does not leak across provider catalogs", () => {
// command-code serves gpt-5.6-luna over its chat-shaped /alpha/generate endpoint
// command-code serves gpt-5.6-luna over its chat-shaped provider endpoint
assert.equal(getModelTargetFormat("cmd", "gpt-5.6-luna"), null);
// raw provider id form behaves identically (alias resolution)
assert.equal(getModelTargetFormat("command-code", "gpt-5.6-luna"), null);

View File

@@ -16,7 +16,6 @@ const { __setTlsFetchOverrideForTesting: __setPplxTlsFetchOverride } =
const { __setTlsFetchOverrideForTesting: __setGrokTlsFetchOverride } =
await import("../../open-sse/services/grokTlsClient.ts");
const { COMMAND_CODE_VERSION } = await import("../../open-sse/executors/commandCode.ts");
const originalFetch = globalThis.fetch;
@@ -216,11 +215,11 @@ test("specialty provider validators cover Deepgram, AssemblyAI, ElevenLabs and I
test("validateCommandCodeProvider ignores caller baseUrl and chatPath overrides", async () => {
globalThis.fetch = async (url, init = {}) => {
assert.equal(String(url), "https://api.commandcode.ai/alpha/generate");
assert.equal(String(url), "https://api.commandcode.ai/provider/v1/chat/completions");
const headers = init.headers as Record<string, string>;
assert.equal(headers.Authorization, "Bearer cc-key");
const body = JSON.parse(String(init.body));
assert.equal(body.params.model, "command-code-validation-model");
assert.equal(body.model, "command-code-validation-model");
return new Response(JSON.stringify({ ok: true }), { status: 200 });
};
@@ -239,7 +238,7 @@ test("validateCommandCodeProvider ignores caller baseUrl and chatPath overrides"
test("validateCommandCodeProvider defaults probe model to DeepSeek flash", async () => {
globalThis.fetch = async (_url, init = {}) => {
const body = JSON.parse(String(init.body));
assert.equal(body.params.model, "deepseek/deepseek-v4-flash");
assert.equal(body.model, "deepseek/deepseek-v4-flash");
return new Response("", { status: 400 });
};
@@ -2285,7 +2284,7 @@ test("specialty validator rejects invalid Runway credentials", async () => {
assert.equal(runway.error, "Invalid API key");
});
test("validateCommandCodeProvider sends Command Code probe URL, headers, and wrapper body", async () => {
test("validateCommandCodeProvider sends Command Code probe URL, headers, and flat OpenAI body", async () => {
const calls: Array<{
url: string;
method?: string;
@@ -2309,22 +2308,21 @@ test("validateCommandCodeProvider sends Command Code probe URL, headers, and wra
assert.deepEqual(result, { valid: true, error: null });
assert.equal(calls.length, 1);
assert.equal(calls[0].url, "https://api.commandcode.ai/alpha/generate");
// Probe targets the documented /provider/v1/chat/completions endpoint, not
// the CLI-only /alpha/generate (#10265).
assert.equal(calls[0].url, "https://api.commandcode.ai/provider/v1/chat/completions");
assert.equal(calls[0].method, "POST");
assert.equal(calls[0].headers.Authorization, "Bearer cc_test_key");
assert.equal(calls[0].headers["Content-Type"], "application/json");
assert.equal(calls[0].headers["x-command-code-version"], COMMAND_CODE_VERSION);
assert.equal(calls[0].headers["x-cli-environment"], "external");
assert.equal(calls[0].headers["x-project-slug"], "pi-cc");
assert.equal(calls[0].headers["x-taste-learning"], "false");
assert.equal(calls[0].headers["x-co-flag"], "false");
assert.equal(typeof calls[0].headers["x-session-id"], "string");
assert.equal(calls[0].body.config.environment, "external");
assert.equal(calls[0].body.permissionMode, "standard");
assert.equal(calls[0].body.skills, "");
assert.equal(calls[0].body.params.model, "gpt-5.4-mini");
assert.equal(calls[0].body.params.stream, true);
assert.equal(calls[0].body.params.max_tokens, 1);
// No CLI-impersonation headers.
assert.equal(calls[0].headers["x-command-code-version"], undefined);
assert.equal(calls[0].headers["x-cli-environment"], undefined);
assert.equal(calls[0].headers["x-project-slug"], undefined);
// Flat OpenAI chat.completions body (no CLI wrapper).
assert.equal(calls[0].body.params, undefined, "CLI envelope params wrapper must not be sent");
assert.equal(calls[0].body.model, "gpt-5.4-mini");
assert.equal(calls[0].body.stream, true);
assert.equal(calls[0].body.max_tokens, 1);
});
for (const status of [400, 422, 429]) {

View File

@@ -10,7 +10,6 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const { handleResponsesCore } = await import("../../open-sse/handlers/responsesHandler.ts");
const { COMMAND_CODE_VERSION } = await import("../../open-sse/executors/commandCode.ts");
const originalFetch = globalThis.fetch;
@@ -354,26 +353,31 @@ test("handleResponsesCore transforms Command Code executor SSE through Responses
input: "hello command code",
},
responseFactory() {
// /provider/v1/chat/completions returns standard OpenAI SSE (#10265).
const chunk = (delta: Record<string, unknown>) =>
`data: ${JSON.stringify({
id: "c1",
object: "chat.completion.chunk",
model: "gpt-5.4-mini",
choices: [{ index: 0, delta }],
})}\n\n`;
return new Response(
[
`data: ${JSON.stringify({ type: "text-delta", text: "command" })}`,
"",
`data: ${JSON.stringify({ type: "reasoning-delta", text: "thinking" })}`,
"",
`data: ${JSON.stringify({ type: "finish", finishReason: "stop" })}`,
"",
].join("\n"),
{ status: 200, headers: { "Content-Type": "application/x-ndjson" } }
chunk({ role: "assistant" }),
chunk({ content: "command" }),
chunk({}),
].join("") + "data: [DONE]\n\n",
{ status: 200, headers: { "Content-Type": "text/event-stream" } }
);
},
});
assert.equal(result.success, true);
assert.equal(call.url, "https://api.commandcode.ai/alpha/generate");
assert.equal(call.url, "https://api.commandcode.ai/provider/v1/chat/completions");
assert.equal(call.headers.Authorization, "Bearer cc_test_key");
assert.equal(call.headers["x-command-code-version"], COMMAND_CODE_VERSION);
assert.equal(call.body.params.model, "gpt-5.4-mini");
assert.equal(call.body.params.stream, true);
assert.equal(call.headers["x-command-code-version"], undefined);
assert.equal(call.body.model, "gpt-5.4-mini");
assert.equal(call.body.stream, true);
const sse = await result.response.text();
assert.match(sse, /event: response\.created/);