fix(auggie): update model registry to match v0.32.0 CLI model IDs (#7032)

* fix(auggie): update model registry to match v0.32.0 CLI model IDs

All previous model IDs (claude-sonnet-4.6, claude-opus-4.6, gpt-5.5-high,
etc.) were synthetic — the actual  IDs use a different
naming scheme (sonnet4.6, opus4.6, gpt5.5, etc.).

Replaced the static best-guess registry with the 31 real model IDs
from  on v0.32.0, including:
- All Claude variants (fable-5, haiku4.5, sonnet4.x/5, opus4.x/5)
- Gemini 3.1 Pro Preview
- Full GPT-5.x family (gpt5 ~ gpt5.6-terra)
- GLM 5.2, Kimi K2.6/K2.7
- Prism composite routers (prism-a, prism-b)

Removed unused entries that don't exist in v0.32.0 (gemini-3.0-flash,
thinking variants, high/medium split IDs).

Updated unit tests to reference valid model IDs (haiku4.5, sonnet4.6, opus4.6).

* feat(auggie): auto-fetch model IDs on first execute()

* fix(auggie): move sonnet4.6 first in model list, remove duplicate

* fix(tests): update old claude-sonnet-4.6 model ID to sonnet4.6 in auggie test

The registry was updated to use sonnet4.6 but the test at line 352
still referenced the old model ID claude-sonnet-4.6, causing
resolveAuggieModel to reject it.

* test(autoCombo): account for auggie's new glm-5.2 model in auto/glm family test

The v0.32.0 auggie registry update in this PR adds a literal "glm-5.2"
model id. auggie is a no-auth candidate (always in the auto/<family>
pool per open-sse/services/autoCombo/virtualFactory.ts), and the family
filter matches by model-id pattern (open-sse/services/autoCombo/modelFamily.ts),
so it now legitimately joins auto/glm alongside the glm/zai connections —
same documented behavior the "degrades gracefully" test below already
covers for opencode/minimax. Updates the strict-equality assertion to
include it instead of narrowing the pool in production code.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(auggie): add backward-compat alias map for v0.32.0 model IDs

Saved combos may reference old model IDs (claude-sonnet-4.6 → sonnet4.6,
gemini-3.1-pro → gemini-3.1-pro-preview, gpt-5.5-high → gpt5.5, etc).
The alias map in resolveAuggieModel() resolves these before the allowlist
check so existing combos continue working after the registry rename.

Refs: #7032

* fix(auggie): use Map.get() for the pre-v0.32.0 alias lookup + changelog

resolveAuggieModel() indexed AUGGIE_MODEL_ALIASES (a Map) with bracket
notation (AUGGIE_MODEL_ALIASES[requested]), which always returns undefined
for a Map instance — the alias branch never actually fired, so every
pre-v0.32.0 saved model id still hit "Unknown Auggie model" after the
v0.32.0 registry rename. Switch to .get(requested), the Map accessor.

Adds a red-first regression test (fails on the old bracket access, passes
with .get()) covering every old->new id pair in the alias map, and a
changelog.d fragment documenting the breaking model-id rename + the
alias fallback that keeps existing combos working.

Refs: #7032
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
Paijo
2026-07-18 21:33:57 +07:00
committed by GitHub
parent afd696e6b2
commit 6d9caa8943
5 changed files with 331 additions and 48 deletions

View File

@@ -0,0 +1 @@
- **fix(providers):** Auggie (Augment CLI) model registry updated to the real v0.32.0 CLI model IDs, with a live `auggie model list` auto-discovery fallback for future renames; the old pre-v0.32.0 IDs (`claude-sonnet-4.6`, `claude-opus-4.6`, `claude-haiku-4.5`, `gemini-3.1-pro`, `gemini-3.0-flash`, the `gpt-5.4`/`gpt-5.5` high/medium variants) are a **breaking rename**, but a backward-compat alias map in `resolveAuggieModel()` transparently remaps them to their v0.32.0 equivalents so existing saved combos keep working without any manual update (#7032 — thanks @oyi77).

View File

@@ -3,6 +3,8 @@ import type { RegistryEntry } from "../../shared.ts";
// Augment / Auggie CLI — local no-auth provider. The executor spawns the
// user's local `auggie` binary (auth handled entirely by `auggie login`);
// OmniRoute never stores credentials for this connection.
//
// Model IDs sourced from `auggie model list` on auggie v0.32.0.
export const auggieProvider: RegistryEntry = {
id: "auggie",
alias: "aug",
@@ -13,26 +15,38 @@ export const auggieProvider: RegistryEntry = {
authHeader: "none",
defaultContextLength: 200000,
models: [
// Claude
{ id: "claude-sonnet-4.6", name: "Claude Sonnet 4.6", contextLength: 200000 },
{
id: "claude-sonnet-4.6-thinking",
name: "Claude Sonnet 4.6 Thinking",
contextLength: 200000,
},
{ id: "claude-opus-4.6", name: "Claude Opus 4.6", contextLength: 200000 },
{ id: "claude-haiku-4.5", name: "Claude Haiku 4.5", contextLength: 200000 },
// Gemini
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro", contextLength: 1000000 },
{ id: "gemini-3.0-flash", name: "Gemini 3 Flash", contextLength: 1000000 },
// GPT-5.x
{ id: "gpt-5.5-high", name: "GPT-5.5 High", contextLength: 200000 },
{ id: "gpt-5.5-medium", name: "GPT-5.5 Medium", contextLength: 200000 },
{ id: "gpt-5.4-high", name: "GPT-5.4 High", contextLength: 200000 },
{ id: "gpt-5.4-medium", name: "GPT-5.4 Medium", contextLength: 200000 },
// Kimi
// ── Anthropic Claude ────────────────────────────────────────────────
{ id: "sonnet4.6", name: "Sonnet 4.6", contextLength: 200000 },
{ id: "fable-5", name: "Claude Fable 5", contextLength: 200000 },
{ id: "haiku4.5", name: "Haiku 4.5", contextLength: 200000 },
{ id: "sonnet4.5", name: "Sonnet 4.5", contextLength: 200000 },
{ id: "sonnet4.6-500k", name: "Sonnet 4.6 (500K)", contextLength: 500000 },
{ id: "sonnet5-high", name: "Claude Sonnet 5", contextLength: 200000 },
{ id: "sonnet5-500k", name: "Claude Sonnet 5 (500K)", contextLength: 500000 },
{ id: "opus4.5", name: "Opus 4.5", contextLength: 200000 },
{ id: "opus4.6", name: "Opus 4.6", contextLength: 200000 },
{ id: "opus4.6-500k", name: "Opus 4.6 (500K)", contextLength: 500000 },
{ id: "opus4.7", name: "Opus 4.7", contextLength: 200000 },
{ id: "opus4.7-500k", name: "Opus 4.7 (500K)", contextLength: 500000 },
{ id: "opus4.8", name: "Opus 4.8", contextLength: 200000 },
// ── Gemini ──────────────────────────────────────────────────────────
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro", contextLength: 1000000 },
// ── OpenAI GPT ──────────────────────────────────────────────────────
{ id: "gpt5", name: "GPT-5", contextLength: 200000 },
{ id: "gpt5.1", name: "GPT-5.1", contextLength: 200000 },
{ id: "gpt5.2", name: "GPT-5.2", contextLength: 200000 },
{ id: "gpt5.4", name: "GPT-5.4", contextLength: 200000 },
{ id: "gpt5.4-mini", name: "GPT-5.4 Mini", contextLength: 200000 },
{ id: "gpt5.5", name: "GPT-5.5", contextLength: 200000 },
{ id: "gpt5.6-luna", name: "GPT-5.6 Luna", contextLength: 200000 },
{ id: "gpt5.6-sol", name: "GPT-5.6 Sol", contextLength: 200000 },
{ id: "gpt5.6-terra", name: "GPT-5.6 Terra", contextLength: 200000 },
// ── Others ──────────────────────────────────────────────────────────
{ id: "glm-5.2", name: "GLM 5.2", contextLength: 200000 },
{ id: "kimi-k2.6", name: "Kimi K2.6", contextLength: 131000 },
// Prism (Augment's in-house model)
{ id: "prism", name: "Augment Prism", contextLength: 200000 },
{ id: "kimi-k2.7", name: "Kimi K2.7 Code", contextLength: 131000 },
// ── Augment Prism (composite routers) ───────────────────────────────
{ id: "prism-a", name: "Prism (Claude + Gemini)", contextLength: 200000 },
{ id: "prism-b", name: "Prism (GPT + Kimi)", contextLength: 200000 },
],
};

View File

@@ -38,8 +38,114 @@ const AUGGIE_URL = "auggie://cli/stdio";
// untrusted-input sink. We only ever pass a model that is declared in the
// registry entry — this closes flag-smuggling (a `model` starting with "-" would
// otherwise be parsed by auggie as an option) and unknown-model passthrough.
//
// The static registry (shipped with the code) is checked first. On first use
// the executor also spawns `auggie model list` at runtime and merges any IDs it
// finds — this lets the allowlist stay current when auggie adds or renames
// models without a code update.
const AUGGIE_MODEL_ALLOWLIST: ReadonlySet<string> = new Set(auggieProvider.models.map((m) => m.id));
const DEFAULT_AUGGIE_MODEL = auggieProvider.models[0]?.id ?? "claude-sonnet-4.6";
// ─── Model alias map (backward compat for saved combos) ─────────────────────
// Old model IDs from before the v0.32.0 registry update; each maps to the
// equivalent v0.32.0 ID so existing combos continue to work after the rename.
const AUGGIE_MODEL_ALIASES: ReadonlyMap<string, string> = new Map([
// Claude
["claude-sonnet-4.6", "sonnet4.6"],
["claude-sonnet-4.6-thinking", "sonnet4.6"],
["claude-opus-4.6", "opus4.6"],
["claude-haiku-4.5", "haiku4.5"],
// Gemini
["gemini-3.1-pro", "gemini-3.1-pro-preview"],
["gemini-3.0-flash", "gemini-3.1-pro-preview"],
// GPT-5.x (high/medium split was synthetic — v0.32.0 has a single ID per version)
["gpt-5.5-high", "gpt5.5"],
["gpt-5.5-medium", "gpt5.5"],
["gpt-5.4-high", "gpt5.4"],
["gpt-5.4-medium", "gpt5.4"],
]);
/**
* Live model cache populated by `initAuggieModels()`.
* - `null` = not yet attempted
* - `Set` = successfully fetched IDs (possibly empty)
*/
let liveModelSet: Set<string> | null = null;
/**
* Spawn `auggie model list`, parse `[model-id]` entries, and merge them into
* the live allowlist so the executor accepts models auggie recognises even
* when the static registry has not been updated yet.
*
* Safe to call repeatedly: only the first call spawns the process; subsequent
* calls are a no-op (including after a failed fetch — `liveModelSet` is set to
* an empty set so we don't retry every request).
*/
export async function initAuggieModels(
signal?: AbortSignal | null,
timeoutMs = 8000
): Promise<void> {
if (liveModelSet !== null) return;
let bin: string;
try {
bin = resolveAuggieBin();
} catch {
liveModelSet = new Set();
return;
}
const child = spawn(bin, ["model", "list"], {
env: process.env,
stdio: ["ignore", "pipe", "pipe"],
shell: false,
windowsHide: true,
});
const fragments: string[] = [];
child.stdout.on("data", (d: Buffer) => fragments.push(d.toString("utf8")));
let settled = false;
const settle = (result: Set<string>) => {
if (settled) return;
settled = true;
liveModelSet = result;
};
const timer = setTimeout(() => {
if (!child.killed) child.kill("SIGKILL");
settle(new Set());
}, timeoutMs);
const onAbort = () => {
if (!child.killed) child.kill("SIGKILL");
clearTimeout(timer);
settle(new Set());
};
if (signal) {
if (signal.aborted) {
clearTimeout(timer);
settle(new Set());
return;
}
signal.addEventListener("abort", onAbort, { once: true });
}
try {
const code = await new Promise<number | null>((resolve, reject) => {
child.on("close", resolve);
child.on("error", (e: Error) => reject(e));
});
clearTimeout(timer);
signal?.removeEventListener("abort", onAbort);
if (code !== 0) {
settle(new Set());
return;
}
const ids = new Set<string>();
for (const line of fragments.join("").split("\n")) {
const m = line.match(/\[([^\]]+)\]/);
if (m) ids.add(m[1]);
}
settle(ids.size > 0 ? ids : new Set());
} catch {
clearTimeout(timer);
settle(new Set());
signal?.removeEventListener("abort", onAbort);
}
}
type AuggieModelResolution = { ok: true; model: string } | { ok: false; error: string };
@@ -47,6 +153,9 @@ type AuggieModelResolution = { ok: true; model: string } | { ok: false; error: s
* Validate + resolve the requested model against the registry allowlist.
* Rejects flag-smuggling (leading "-") and any id not declared in the registry.
* An empty/absent model resolves to the registry's first (default) model.
*
* Note: `initAuggieModels()` must be called at least once before this function
* sees live-discovered models (the executor's `execute()` does this).
*/
export function resolveAuggieModel(model: unknown): AuggieModelResolution {
const requested = typeof model === "string" ? model.trim() : "";
@@ -57,15 +166,20 @@ export function resolveAuggieModel(model: unknown): AuggieModelResolution {
error: `Invalid Auggie model "${requested}": model must not start with "-".`,
};
}
if (!AUGGIE_MODEL_ALLOWLIST.has(requested)) {
return {
ok: false,
error: `Unknown Auggie model "${requested}". Supported models: ${[
...AUGGIE_MODEL_ALLOWLIST,
].join(", ")}.`,
};
}
return { ok: true, model: requested };
// Backward-compat alias: resolve old model IDs → v0.32.0 equivalents.
// This lets saved combos referencing the old names keep working.
const requestedAlias = AUGGIE_MODEL_ALIASES.get(requested);
if (requestedAlias) return { ok: true, model: requestedAlias };
// Static registry — always authoritative for the shipped set.
if (AUGGIE_MODEL_ALLOWLIST.has(requested)) return { ok: true, model: requested };
// Live-discovered models (if loaded) extend the static list.
if (liveModelSet?.has(requested)) return { ok: true, model: requested };
const known = [...AUGGIE_MODEL_ALLOWLIST];
if (liveModelSet) known.push(...liveModelSet);
return {
ok: false,
error: `Unknown Auggie model "${requested}". Supported models: ${known.join(", ")}.`,
};
}
/**
@@ -151,6 +265,7 @@ export function buildAuggiePrompt(messages: OpenAIMsg[]): string {
}
}
if (!text.trim()) continue;
if (role === "system") {
lines.push(`[System]\n${text}`);
} else if (role === "assistant") {
@@ -252,7 +367,6 @@ export class AuggieExecutor extends BaseExecutor {
): Promise<Partial<ProviderCredentials> | null> {
return null;
}
async execute({ model, body, stream, signal, log }: ExecuteInput): Promise<{
response: Response;
url: string;
@@ -265,6 +379,9 @@ export class AuggieExecutor extends BaseExecutor {
const auggieBin = resolveAuggieBin();
const wantsStream = stream !== false;
// On first execution, try to discover model IDs the local auggie recognises.
// Best-effort: missing/inactive CLI falls through to the static list.
await initAuggieModels(signal);
// Argument-injection defense: never forward an unvalidated model into the argv.
const modelResolution = resolveAuggieModel(model);
if (!modelResolution.ok) {
@@ -601,3 +718,13 @@ function buildAuggieSseError(message: string): Response {
},
});
}
// ─── Test helpers ──────────────────────────────────────────────────────────
/**
* Reset the live model cache for testing.
* Not exported from the package index.
*/
export function __resetAuggieModels(): void {
liveModelSet = null;
}

View File

@@ -14,9 +14,8 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const { AuggieExecutor, buildAuggiePrompt, resolveAuggieBin, resolveAuggieModel } = await import(
"@omniroute/open-sse/executors/auggie"
);
const { AuggieExecutor, buildAuggiePrompt, resolveAuggieBin, resolveAuggieModel } =
await import("@omniroute/open-sse/executors/auggie");
const TMP_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-auggie-test-"));
@@ -27,7 +26,6 @@ function writeFakeBin(name: string, script: string): string {
return p;
}
async function readSseEvents(response: Response): Promise<Record<string, unknown>[]> {
const text = await response.text();
const events: Record<string, unknown>[] = [];
@@ -61,7 +59,13 @@ test("buildAuggiePrompt flattens system/user/assistant turns with role tags", ()
test("buildAuggiePrompt flattens array-shaped content blocks and skips empty turns", () => {
const prompt = buildAuggiePrompt([
{ role: "user", content: [{ type: "text", text: "part1 " }, { type: "text", text: "part2" }] },
{
role: "user",
content: [
{ type: "text", text: "part1 " },
{ type: "text", text: "part2" },
],
},
{ role: "user", content: "" },
{ role: "user", content: [] },
]);
@@ -93,7 +97,7 @@ test("execute() surfaces a sanitized 'CLI not found' error on ENOENT (streaming)
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: true,
credentials: {} as never,
@@ -120,7 +124,7 @@ test("execute() surfaces a sanitized 'CLI not found' error on ENOENT (non-stream
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {} as never,
@@ -138,13 +142,16 @@ test("execute() surfaces a sanitized 'CLI not found' error on ENOENT (non-stream
// ─── execute(): non-zero exit → sanitized error ────────────────────────────
test("execute() surfaces a sanitized error when the CLI exits non-zero (streaming)", async () => {
const bin = writeFakeBin("fake-auggie-fail.sh", 'echo "boom at /home/attacker/secret.ts:42" 1>&2\nexit 3');
const bin = writeFakeBin(
"fake-auggie-fail.sh",
'echo "boom at /home/attacker/secret.ts:42" 1>&2\nexit 3'
);
const prevBin = process.env.AUGGIE_BIN;
process.env.AUGGIE_BIN = bin;
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: true,
credentials: {} as never,
@@ -168,7 +175,7 @@ test("execute() surfaces a sanitized error when the CLI exits non-zero (non-stre
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {} as never,
@@ -191,7 +198,7 @@ test("execute() with stream=true returns SSE deltas + [DONE]", async () => {
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "say hi" }] },
stream: true,
credentials: {} as never,
@@ -221,7 +228,7 @@ test("execute() with stream=false returns a single chat.completion JSON body", a
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "say hi" }] },
stream: false,
credentials: {} as never,
@@ -249,8 +256,41 @@ test("resolveAuggieModel defaults to a real allowlisted model when unset", () =>
});
test("resolveAuggieModel accepts a known registry model id verbatim", () => {
const result = resolveAuggieModel("claude-haiku-4.5");
assert.deepEqual(result, { ok: true, model: "claude-haiku-4.5" });
const result = resolveAuggieModel("haiku4.5");
assert.deepEqual(result, { ok: true, model: "haiku4.5" });
});
// ─── resolveAuggieModel: pre-v0.32.0 alias back-compat (#7032) ────────────
// The v0.32.0 registry rename dropped ~10 old model IDs outright. Saved combos
// created before the rename still reference those old IDs, so resolveAuggieModel
// must transparently map each one to its v0.32.0 replacement instead of erroring
// with "Unknown Auggie model".
test("resolveAuggieModel resolves a pre-v0.32.0 saved model id via the alias map (#7032)", () => {
const result = resolveAuggieModel("claude-sonnet-4.6");
assert.deepEqual(result, { ok: true, model: "sonnet4.6" });
});
test("resolveAuggieModel resolves every pre-v0.32.0 alias to an allowlisted v0.32.0 id (#7032)", () => {
const OLD_TO_NEW: Record<string, string> = {
"claude-sonnet-4.6": "sonnet4.6",
"claude-sonnet-4.6-thinking": "sonnet4.6",
"claude-opus-4.6": "opus4.6",
"claude-haiku-4.5": "haiku4.5",
"gemini-3.1-pro": "gemini-3.1-pro-preview",
"gemini-3.0-flash": "gemini-3.1-pro-preview",
"gpt-5.5-high": "gpt5.5",
"gpt-5.5-medium": "gpt5.5",
"gpt-5.4-high": "gpt5.4",
"gpt-5.4-medium": "gpt5.4",
};
for (const [oldId, newId] of Object.entries(OLD_TO_NEW)) {
assert.deepEqual(
resolveAuggieModel(oldId),
{ ok: true, model: newId },
`${oldId} should resolve to ${newId}`
);
}
});
// ─── execute(): model allowlist (argument-injection defense) ──────────────
@@ -314,7 +354,7 @@ test("execute() spawns with a valid allowlisted model, no shell, and a '--' argv
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "claude-opus-4.6",
model: "opus4.6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {} as never,
@@ -323,7 +363,7 @@ test("execute() spawns with a valid allowlisted model, no shell, and a '--' argv
const body = await response.json();
assert.equal(body.choices[0].message.content, "ok");
const argv = fs.readFileSync(argvFile, "utf8").trim().split("\n");
assert.deepEqual(argv, ["--print", "--quiet", "--model", "claude-opus-4.6", "--"]);
assert.deepEqual(argv, ["--print", "--quiet", "--model", "opus4.6", "--"]);
} finally {
if (prevBin === undefined) delete process.env.AUGGIE_BIN;
else process.env.AUGGIE_BIN = prevBin;
@@ -342,7 +382,7 @@ test("execute() aborts a long-running CLI process instead of hanging (streaming)
const executor = new AuggieExecutor();
const controller = new AbortController();
const { response } = await executor.execute({
model: "claude-sonnet-4.6",
model: "sonnet4.6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: true,
credentials: {} as never,
@@ -378,3 +418,99 @@ test("buildUrl/buildHeaders/transformRequest match the CLI-passthrough shape", (
assert.deepEqual(executor.buildHeaders(), {});
assert.equal(executor.transformRequest(), null);
});
// ─── initAuggieModels / resolveAuggieModel auto-fetch ────────────────────
test("initAuggieModels discovers models from `auggie model list` output", async () => {
const { initAuggieModels, resolveAuggieModel, __resetAuggieModels } =
await import("@omniroute/open-sse/executors/auggie");
__resetAuggieModels(); // wipe stale cache from previous tests
const bin = writeFakeBin(
"fake-auggie-list.sh",
`printf '[new-model-alpha]\\nignored\\n[new-model-beta]'`
);
const prevBin = process.env.AUGGIE_BIN;
process.env.AUGGIE_BIN = bin;
try {
await initAuggieModels();
const r = resolveAuggieModel("new-model-alpha");
assert.deepEqual(r, { ok: true, model: "new-model-alpha" });
} finally {
if (prevBin === undefined) delete process.env.AUGGIE_BIN;
else process.env.AUGGIE_BIN = prevBin;
__resetAuggieModels(); // restore for subsequent tests
}
});
test("execute() spawns a model discovered via auto-fetch", async () => {
const { __resetAuggieModels } = await import("@omniroute/open-sse/executors/auggie");
__resetAuggieModels();
const bin = writeFakeBin(
"fake-auggie-autofetch.sh",
[
`case "$*" in`,
` *model\\ list*)`,
` printf '[auto-discovered-model]'`,
` exit 0`,
` ;;`,
` *)`,
` printf 'auto-ok'`,
` ;;`,
`esac`,
].join("\n")
);
const prevBin = process.env.AUGGIE_BIN;
process.env.AUGGIE_BIN = bin;
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "auto-discovered-model",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {} as never,
});
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.choices[0].message.content, "auto-ok");
} finally {
if (prevBin === undefined) delete process.env.AUGGIE_BIN;
else process.env.AUGGIE_BIN = prevBin;
__resetAuggieModels();
}
});
test("initAuggieModels failure does not block execute() for a known model", async () => {
const { __resetAuggieModels } = await import("@omniroute/open-sse/executors/auggie");
__resetAuggieModels();
const bin = writeFakeBin(
"fake-auggie-crash.sh",
[
`case "$*" in`,
` *model\\ list*)`,
` exit 1 # crash on model list so init fails`,
` ;;`,
` *)`,
` printf 'known-model-ok'`,
` ;;`,
`esac`,
].join("\n")
);
const prevBin = process.env.AUGGIE_BIN;
process.env.AUGGIE_BIN = bin;
try {
const executor = new AuggieExecutor();
const { response } = await executor.execute({
model: "haiku4.5", // static-registry model, should work regardless
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {} as never,
});
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.choices[0].message.content, "known-model-ok");
} finally {
if (prevBin === undefined) delete process.env.AUGGIE_BIN;
else process.env.AUGGIE_BIN = prevBin;
__resetAuggieModels();
}
});

View File

@@ -123,7 +123,12 @@ describe("auto/<family> materialization (#6453)", () => {
assert.equal(combo.id, "auto/glm");
assert.equal(combo.strategy, "auto");
const providerIds = combo.models.map((m) => m.providerId).sort();
assert.deepEqual(providerIds, ["glm", "zai"]);
// Includes the always-on `auggie` no-auth candidate: its registry (v0.32.0 CLI
// model ids) advertises a literal "glm-5.2" model, and — same as the
// "degrades gracefully" test below documents for opencode/minimax — a
// no-auth backend that genuinely serves a family model IS a legitimate
// member of the family pool, not just credentialed provider_connections rows.
assert.deepEqual(providerIds, ["auggie", "glm", "zai"]);
assert.ok(combo.models.every((m) => m.model.endsWith("glm-5.2")));
});