diff --git a/src/grok/inject.ts b/src/grok/inject.ts index 2e7681024b3..69a107fb140 100644 --- a/src/grok/inject.ts +++ b/src/grok/inject.ts @@ -309,6 +309,9 @@ function canonicalDottedKey(raw: string): string[] { * TOML admits equivalent header spellings for BOTH segments (`["model"."ocx-mine"]`, * `['model'.ocx-mine]`, `[ model . ocx-mine ]`); all of them redefine the same table, so each * form is canonicalized before it is reserved. + * Only exactly two-segment headers collide: `[[model.x]]` makes `model.x` an array that a + * generated `[model.x]` table cannot redefine, while a sub-table like `[model.x.extra]` does + * not collide at all — TOML permits its implicitly created parent to be defined explicitly later. */ function userModelAliases(content: string, region: ManagedRegion | null): Set { const outsideManagedRegion = region @@ -316,7 +319,7 @@ function userModelAliases(content: string, region: ManagedRegion | null): Set(); for (const header of analyzeTomlStructure(outsideManagedRegion).headers) { - if (header.segments[0] !== "model" || header.segments.length < 2) continue; + if (header.segments[0] !== "model" || header.segments.length !== 2) continue; aliases.add(header.segments[1]!); } return aliases; diff --git a/tests/providers/xai/grok-config-inject.test.ts b/tests/providers/xai/grok-config-inject.test.ts index 9c6b0d02de4..62049b36d7d 100644 --- a/tests/providers/xai/grok-config-inject.test.ts +++ b/tests/providers/xai/grok-config-inject.test.ts @@ -194,7 +194,6 @@ describe("Grok config injection", () => { ["mixed quoting with whitespace", `[ "model" . 'ocx-mine' ]`], ["bare (baseline)", "[model.ocx-mine]"], ["array of tables", "[[model.ocx-mine]]"], - ["sub-table", "[model.ocx-mine.extra]"], ["trailing comment", '[model."ocx-mine"] # mine'], ]; @@ -211,6 +210,21 @@ describe("Grok config injection", () => { }); } + test("does not reserve an alias from a user sub-table", () => { + // [model.ocx-mine.extra] implicitly creates model.ocx-mine, and TOML permits that parent + // to be defined explicitly later — reserving "ocx-mine" here would needlessly suffix the + // generated alias. + writeFileSync(configPath(), "[model.ocx-mine.extra]\nx = 1\n", "utf8"); + + injectGrokConfig(10100, [{ id: "mine" }], { grokHome }); + + const written = readFileSync(configPath(), "utf8"); + const generated = written.slice(written.indexOf(BEGIN_MARKER)); + expect(written).toContain("[model.ocx-mine.extra]"); + expect(generated).toContain("[model.ocx-mine]\n"); + expect(generated).not.toContain("[model.ocx-mine-2]"); + }); + test("does not reserve aliases from unrelated tables", () => { // [models.*] and [model_providers.*] are different tables entirely — reserving from them // would needlessly suffix our aliases.