Skip to content

fix(ai): open Cloud resource links in Assistant answers even when the model drops the leading slash - #1262

Merged
ValentinKolb merged 1 commit into
mainfrom
fix/assistant-root-resource-links
Oct 16, 2026
Merged

ValentinKolb merged 1 commit into
mainfrom
fix/assistant-root-resource-links

Conversation

@ValentinKolb

Copy link
Copy Markdown
Collaborator

Outcome

In Assistant chat answers, a Cloud resource link the model wrote without its leading slash, such as [Meeting notes](app/notebooks/abc/notes/def) or [Draft](open/mail.draft/~x), now opens the resource. Before, the browser resolved it against /app/assistant?conversation=... and opened the broken /app/app/notebooks/....

Root cause

Tools return correct root-relative hrefs and the system prompt asks the model to copy them, but models sometimes drop the leading /. The shared links extension renders any scheme-less href as an internal reference pill and keeps it unchanged, and AssistantMarkdownBlock only rewrote chat-file links (on the client).

Fix

AssistantMarkdownBlock (packages/cloud/src/ai/chat/primitives.tsx) renders every Assistant answer and work text. It now passes the sanitized HTML through rootResourceLinks (answer-links.ts) before rendering:

  • Only an <a> href that starts exactly with app/ or open/ becomes /app/... or /open/.... The result is always root-relative under /app/ or /open/, so it stays same-origin.
  • Not touched: external links, /app/..., #heading, ?x, ./app/..., report.md, docs/app/x, App/x, note://, attach://, other attributes, text, <img src>.
  • The rewrite is a pure function in render, so the server render and the first client render produce the same href. The pill keeps the same type icon and accessible name as a correct /app/... link. The test checks that the anchor is byte-identical.
  • Chat-file resolution (resolveFileLink) runs as before. It now sees the rooted href.

Design note: I did not use the refs that tool results returned. That would need new plumbing from the timeline into the renderer, so this uses the narrow prefix rule. The rule builds no new URLs. It only adds the missing slash to the two Cloud route roots that refs use. The system prompt is unchanged.

Docs

docs-site/docs/en/ai/chat-interface.md: one sentence after the paragraph on answer resource links. No Help change, because Help does not describe answer links. No cloud-dev skill change, because no stable workflow or cross-cutting platform invariant changed.

How do we prevent this next time?

The render test reproduces the bug: it fails on main with /app/app/notebooks/... and /app/open/.... With the unit and behavior tests, it pins the rule and the unchanged cases. No existing lessons-learned error class fits (model output that drifts from a copied href), and no repository check could catch it. The tests are the guard.

Verification

  • New SSR render test fails on origin/main code (2 hrefs resolved to /app/app/... / /app/open/...) and passes with the fix.
  • bun test answer-links.test.ts primitives.render.test.tsx: 7 pass.
  • bun run --cwd packages/cloud typecheck: pass.
  • bun run test --filter packages/assistant: all pass.
  • bun run test --filter packages/cloud: all pass except scripts/cli-pack.test.ts > "packs the shared bundle contract..." The run hit the 5 s default timeout under machine load. The test is unrelated to this change and passed on its own (6/6, 4.15 s for the file).
  • git diff --check, biome on the changed files: clean.
  • Codex peer review (gpt-6.1-sol, high): no findings.
  • Not run: a manual check in a real browser against a running stack.

… model drops the leading slash

A model sometimes writes a Cloud resource link as app/notebooks/... or
open/... without its leading slash. The browser resolved that against
/app/assistant and opened /app/app/notebooks/..., a broken address.

AssistantMarkdownBlock renders every Assistant answer and work text, so it
now roots scheme-less app/ and open/ hrefs in the sanitized HTML before the
first render. Server and client output stay identical, the pill keeps its
type and accessible name, and the result stays same-origin under /app/ or
/open/. Every other link stays as written.
@ValentinKolb
ValentinKolb added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 5de9eb6 Oct 16, 2026
23 checks passed
@ValentinKolb
ValentinKolb deleted the fix/assistant-root-resource-links branch October 16, 2026 17:19
@github-actions github-actions Bot mentioned this pull request Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant