From b086743ba340538947b2eea53fca489e962e54f0 Mon Sep 17 00:00:00 2001 From: luvs01 Date: Sat, 8 Aug 2026 15:37:29 +0900 Subject: [PATCH] fix(grok): match managed markers only on fence lines --- src/grok/inject.ts | 14 +++++++++----- tests/grok-config-inject.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/grok/inject.ts b/src/grok/inject.ts index 9304d923ca..33f445a044 100644 --- a/src/grok/inject.ts +++ b/src/grok/inject.ts @@ -58,11 +58,15 @@ export function isDirectory(path: string): boolean { /** INTERNAL API — see `ManagedRegion` above. Not a public fence-parsing surface. */ export function findManagedRegion(content: string): ManagedRegion | null { - const start = content.indexOf(BEGIN_MARKER); - if (start === -1) return null; - const endMarkerStart = content.indexOf(END_MARKER, start + BEGIN_MARKER.length); - if (endMarkerStart === -1) return { start, end: content.length, orphaned: true }; - return { start, end: endMarkerStart + END_MARKER.length, orphaned: false }; + const markerLine = (marker: string): RegExp => + new RegExp(`^[ \\t]*${marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[ \\t]*$`, "gm"); + const begin = markerLine(BEGIN_MARKER).exec(content); + if (!begin) return null; + const end = markerLine(END_MARKER); + end.lastIndex = begin.index + begin[0].length; + const endMatch = end.exec(content); + if (!endMatch) return { start: begin.index, end: content.length, orphaned: true }; + return { start: begin.index, end: endMatch.index + endMatch[0].length, orphaned: false }; } /** diff --git a/tests/grok-config-inject.test.ts b/tests/grok-config-inject.test.ts index 6e89f44c1d..db7bdf6cee 100644 --- a/tests/grok-config-inject.test.ts +++ b/tests/grok-config-inject.test.ts @@ -63,6 +63,28 @@ describe("Grok config injection", () => { expect(content).toContain("[model.ocx-newer-model]"); }); + test("treats managed markers in model metadata as TOML data", () => { + const configPath = join(grokHome, "config.toml"); + const userContent = 'theme = "dark"\n'; + writeFileSync(configPath, userContent, "utf8"); + const hostileId = `provider/${BEGIN_MARKER} ${END_MARKER} stale-tail`; + + injectGrokConfig(10100, [{ id: hostileId }], { grokHome }); + const injected = readFileSync(configPath, "utf8"); + expect(() => Bun.TOML.parse(injected)).not.toThrow(); + + const stripped = stripGrokConfig({ grokHome }); + expect(stripped).toMatchObject({ ok: true, changed: true }); + expect(readFileSync(configPath, "utf8")).toBe(userContent); + + injectGrokConfig(10100, [{ id: hostileId }], { grokHome }); + injectGrokConfig(10100, [{ id: "replacement" }], { grokHome }); + const replaced = readFileSync(configPath, "utf8"); + expect(replaced).not.toContain("stale-tail"); + expect(replaced).toContain('model = "replacement"'); + expect(() => Bun.TOML.parse(replaced)).not.toThrow(); + }); + test("emits per-model direct fields (grok 0.2.101 ignores model_providers inheritance)", () => { const block = buildGrokManagedBlock(10190, [{ id: "cursor/grok-4.5", contextWindow: 500_000 }]); expect(block).not.toContain("[model_providers");