authorgravatar for otavio@ossystems.com.brOtavio Salvador <otavio@ossystems.com.br> 2026-09-29 21:02:52-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-29 21:02:52-03:00
log88fbc2cac77ff2bf0d406c44754ecd8014976f5f
treefab4eb2417eb238c5b075f6ef714c62a850f7da4
parent60cb7d180d9ebdd1010460ccd94cdfad1ec83042
signature Signed by PGP key B5690EEEBB952194

fix(client-runtime): keep model ids in inline code from becoming file chips (#13909)


2 files changed, 13 insertions(+), 0 deletions(-)

packages/client-runtime/src/markdownLinks.test.ts+9
...@@ -22,6 +22,15 @@ describe("inlineCodeFilePathCandidate", () => {...@@ -22,6 +22,15 @@ describe("inlineCodeFilePathCandidate", () => {
22 ["127.0.0.1:3000", null],22 ["127.0.0.1:3000", null],
23 ["example.com/index.html", null],23 ["example.com/index.html", null],
24 ["example.pl/index.html", null],24 ["example.pl/index.html", null],
25 ["z-ai/glm-5.3", null],
26 ["z-ai/glm-5.3:12", null],
27 ["python/3.12", null],
28 ["Qwen/Qwen2.5-Coder", null],
29 ["meta-llama/Llama-3.1-8B", null],
30 ["share/man/ls.1", "share/man/ls.1"],
31 ["usr/lib/libfoo.so.1", "usr/lib/libfoo.so.1"],
32 ["vendor/jquery-3.6.0.min.js", "vendor/jquery-3.6.0.min.js"],
33 ["./models/glm-5.3", "./models/glm-5.3"],
25 ])("distinguishes file paths from code and hostnames in %s", (source, candidate) => {34 ])("distinguishes file paths from code and hostnames in %s", (source, candidate) => {
26 expect(inlineCodeFilePathCandidate(source)).toBe(candidate);35 expect(inlineCodeFilePathCandidate(source)).toBe(candidate);
27 });36 });
packages/client-runtime/src/markdownLinks.ts+4
...@@ -14,6 +14,9 @@ const POSITION_ONLY_PATTERN = /^\d+(?::\d+)?$/;...@@ -14,6 +14,9 @@ const POSITION_ONLY_PATTERN = /^\d+(?::\d+)?$/;
14const INLINE_CODE_DISQUALIFIER_PATTERN = /[\s`]/;14const INLINE_CODE_DISQUALIFIER_PATTERN = /[\s`]/;
15const PATH_SEPARATOR_PATTERN = /[\\/]/;15const PATH_SEPARATOR_PATTERN = /[\\/]/;
16const FILE_EXTENSION_PATTERN = /\.[A-Za-z0-9_-]+$/;16const FILE_EXTENSION_PATTERN = /\.[A-Za-z0-9_-]+$/;
17// A final dot between digits marks a version or model id (`glm-5.3`,
18// `Qwen2.5-Coder`), not an extension. `ls.1` and `libfoo.so.1` stay files.
19const VERSION_SUFFIX_PATTERN = /\d\.\d[^.]*$/;
17const NUMERIC_DOTTED_PATTERN = /^\d+(?:\.\d+)+$/;20const NUMERIC_DOTTED_PATTERN = /^\d+(?:\.\d+)+$/;
18// Standard OS and dev-container roots; deliberately excludes app-route-ish21// Standard OS and dev-container roots; deliberately excludes app-route-ish
19// prefixes like /app/ or /chat/ so SPA routes never read as files.22// prefixes like /app/ or /chat/ so SPA routes never read as files.
...@@ -176,6 +179,7 @@ export function inlineCodeFilePathCandidate(codeText: string): string | null {...@@ -176,6 +179,7 @@ export function inlineCodeFilePathCandidate(codeText: string): string | null {
176 .replace(/[/\\]+$/, "")179 .replace(/[/\\]+$/, "")
177 .split(/[\\/]/)180 .split(/[\\/]/)
178 .at(-1) ?? "";181 .at(-1) ?? "";
182 if (VERSION_SUFFIX_PATTERN.test(basename)) return null;
179 if (!hasPosition && !FILE_EXTENSION_PATTERN.test(basename)) return null;183 if (!hasPosition && !FILE_EXTENSION_PATTERN.test(basename)) return null;
180 }184 }
181 return candidate;185 return candidate;