merge main (0.6.0) into fix/omp-developer-messages

Align the developer-message fix with the Provider API transport router.
The fix stays scoped to the legacy /alpha/generate converter, and the
OMP advisory compat test now forces the upgrade_required fallback so the
advisory is asserted on the generate request body.
This commit is contained in:
warc0s
2026-08-25 22:52:02 +02:00
48 changed files with 4325 additions and 490 deletions
+131 -16
View File
@@ -16,6 +16,7 @@ import {
mapFinishReason,
messagesToCC,
parseStreamEventLine,
pickCommandCodeApiKey,
projectSlugFromPath,
textContent,
toJsonSchema,
@@ -26,8 +27,18 @@ import { redactCommandCodeErrorText } from "../src/overflow.ts"
import { objectAt } from "./helpers.ts"
describe("getApiKey()", () => {
it("uses COMMANDCODE_API_KEY from provided env", () => {
assert.equal(getApiKey({ env: { COMMANDCODE_API_KEY: "env-key" }, authPaths: [] }), "env-key")
it("uses the official API key env var before the legacy alias", () => {
assert.equal(
getApiKey({
env: { COMMAND_CODE_API_KEY: "official-key", COMMANDCODE_API_KEY: "legacy-key" },
authPaths: [],
}),
"official-key",
)
assert.equal(
getApiKey({ env: { COMMANDCODE_API_KEY: "legacy-key" }, authPaths: [] }),
"legacy-key",
)
})
it("reads apiKey, commandcode, pi OAuth, and official CLI credential fields", () => {
@@ -106,6 +117,39 @@ describe("error redaction", () => {
})
})
describe("pickCommandCodeApiKey()", () => {
it("falls back to the host key for a placeholder registry value", () => {
assert.equal(pickCommandCodeApiKey("$COMMAND_CODE_API_KEY", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey("COMMAND_CODE_API_KEY", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey("$COMMANDCODE_API_KEY", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey("COMMANDCODE_API_KEY", "file-key"), "file-key")
})
it("returns undefined when only a placeholder is provided (no fallback)", () => {
assert.equal(pickCommandCodeApiKey("$COMMAND_CODE_API_KEY", undefined), undefined)
assert.equal(pickCommandCodeApiKey("$COMMANDCODE_API_KEY", undefined), undefined)
})
it("prefers a real registry key over the host fallback", () => {
assert.equal(pickCommandCodeApiKey("real-registry-key", "file-key"), "real-registry-key")
})
it("falls back to the host key when the registry has none", () => {
assert.equal(pickCommandCodeApiKey(undefined, "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey(undefined, undefined), undefined)
})
it("falls back to the host key for empty or whitespace registry values", () => {
assert.equal(pickCommandCodeApiKey("", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey(" ", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey(" ", undefined), undefined)
})
it("trims a real registry key", () => {
assert.equal(pickCommandCodeApiKey(" real-registry-key ", "file-key"), "real-registry-key")
})
})
describe("projectSlugFromPath()", () => {
it("matches the official CLI-style slug from an absolute working directory", () => {
assert.equal(
@@ -117,7 +161,7 @@ describe("projectSlugFromPath()", () => {
})
describe("text-only image handling", () => {
it("rejects image content for models without image support", () => {
it("rejects direct image input for models without image support", () => {
assert.throws(
() =>
assertTextOnlyMessages([
@@ -128,16 +172,17 @@ describe("text-only image handling", () => {
]),
/does not support image content/i,
)
assert.throws(
() =>
assertTextOnlyMessages([
{
role: "toolResult",
toolCallId: "c1",
content: [{ type: "image", data: "base64-data", mimeType: "image/png" }],
},
]),
/does not support image content/i,
})
it("allows historical tool-result images to be omitted for text-only models", () => {
assert.doesNotThrow(() =>
assertTextOnlyMessages([
{
role: "toolResult",
toolCallId: "c1",
content: [{ type: "image", data: "base64-data", mimeType: "image/png" }],
},
]),
)
})
})
@@ -168,6 +213,12 @@ describe("textContent()", () => {
)
})
it("normalizes malformed string and object content", () => {
assert.equal(textContent({ content: "raw result" }), "raw result")
assert.equal(textContent({ content: { ok: true } }), '{"ok":true}')
assert.equal(textContent({ content: null }), "")
})
it("handles empty or missing content", () => {
assert.equal(textContent({ content: [] }), "")
assert.equal(textContent({}), "")
@@ -359,7 +410,7 @@ describe("toJsonSchema()", () => {
if (!outputProperties || typeof outputProperties !== "object") {
throw new Error("expected object properties")
}
assert.ok(Object.prototype.hasOwnProperty.call(outputProperties, "__proto__"))
assert.ok(Object.hasOwn(outputProperties, "__proto__"))
assert.deepEqual(Object.getOwnPropertyDescriptor(outputProperties, "__proto__")?.value, {
type: "string",
})
@@ -500,6 +551,23 @@ describe("messagesToCC()", () => {
assert.equal(objectAt(result, ["2", "content", "0", "output", "value"]), "hello\nworld")
})
it("preserves malformed string tool results instead of sending empty output", () => {
const result = messagesToCC([
{
role: "assistant",
content: [{ type: "toolCall", id: "c1", name: "read", arguments: {} }],
},
{
role: "toolResult",
toolCallId: "c1",
toolName: "read",
content: "raw result",
},
])
assert.equal(objectAt(result, ["1", "content", "0", "output", "value"]), "raw result")
})
it("serializes image inputs in the current Command Code wire format", () => {
assert.deepEqual(
messagesToCC(
@@ -530,6 +598,48 @@ describe("messagesToCC()", () => {
)
})
it("omits tool-result images for text-only models while preserving their text", () => {
const result = messagesToCC([
{ role: "user", content: "read image" },
{
role: "assistant",
content: [{ type: "toolCall", id: "c1", name: "read", arguments: {} }],
},
{
role: "toolResult",
toolCallId: "c1",
toolName: "read",
content: [
{ type: "text", text: "image attached" },
{ type: "image", data: "aGVsbG8=", mimeType: "image/jpeg" },
],
},
])
assert.equal(objectAt(result, ["2", "content", "0", "output", "value"]), "image attached")
assert.equal(objectAt(result, ["3"]), undefined)
})
it("describes an omitted image-only tool result for text-only models", () => {
const result = messagesToCC([
{
role: "assistant",
content: [{ type: "toolCall", id: "c1", name: "read", arguments: {} }],
},
{
role: "toolResult",
toolCallId: "c1",
toolName: "read",
content: [{ type: "image", data: "aGVsbG8=", mimeType: "image/jpeg" }],
},
])
assert.equal(
objectAt(result, ["1", "content", "0", "output", "value"]),
"[Image omitted: model does not support images]",
)
})
it("preserves tool-result images as a following user image message", () => {
const result = messagesToCC(
[
@@ -601,7 +711,7 @@ describe("messagesToCC()", () => {
])
})
it("drops orphaned tool calls that have no matching tool result", () => {
it("synthesizes missing results for orphaned tool calls", () => {
const result = messagesToCC([
{ role: "user", content: "edit a file" },
{
@@ -620,7 +730,12 @@ describe("messagesToCC()", () => {
assert.equal(objectAt(result, ["1", "role"]), "assistant")
assert.equal(objectAt(result, ["1", "content", "0", "type"]), "text")
assert.equal(objectAt(result, ["1", "content", "1"]), undefined)
assert.equal(objectAt(result, ["1", "content", "1", "type"]), "tool-call")
assert.equal(objectAt(result, ["2", "role"]), "tool")
assert.match(
String(objectAt(result, ["2", "content", "0", "output", "value"])),
/did not complete/,
)
})
it("handles empty conversations", () => {