fix(chatbook): resolve @ mentions followed by punctuation or naming paths with spaces - #505
Merged
mbektas merged 2 commits intoSep 29, 2026
Merged
Conversation
…aths with spaces A mention token ran to the next whitespace, so `@file:data/README.md,` looked up `data/README.md,` and a path with a space could not be named at all. Both failed quietly: the model received the mention as `[unavailable]` and generated code anyway. Following Claude Code and Codex, as agreed on plmbr#503: - A quoted form, `@file:"data/my notes.md"`, names a path containing whitespace or `@`. It is taken literally, cannot hold a quote or a line break, and a closing quote glued to a word character keeps the old single token. - Sentence punctuation typed after an unquoted file or dir mention is not part of the path. Resolution tries the name as written, then with up to five of those characters removed one at a time, and stops at the first name that exists even if it cannot be read. A real name ending in punctuation still resolves, and a shorter, unrelated name is never reached past it. Trimming is an explicit character set rather than `\W`, so `+`, `-`, combining marks, and invisible or bidi characters are never stripped, and every candidate still goes through the path-safety checks. - `ext:` values accept the quoted form but are never trimmed: provider values are opaque, and a provider may answer "" for a value it does not know, so trimming could resolve a different item. - Each resolved path, or provider value, is sent once, and duplicates are skipped before any file is read or provider is called. The test that pinned `@dir:docs,` as unavailable ("NUI-compatible tokens") now expects it to resolve; NUI needs the matching change, tracked on plmbr#503.
…d cut short The mention menu inserted `@file:<path>` verbatim, so picking `my notes.md` or `logo@2x.png` produced a token the server read as `my` or `logo`. The menu now inserts the quoted form, `@file:"my notes.md"`, when the value contains whitespace, `@`, or the separator controls Python treats as whitespace, and leaves values it cannot quote (a double quote or a line break) as they are. Selection shares `chatbookMentionInsertText` with `applyChatbookMention`, and the caret is placed from the inserted text's length; the previous `value.length` arithmetic would land two characters short of a quoted token. `CHATBOOK_MENTION_TOKEN_RE` stays as it was, since it only detects whether a prompt has a mention and every quoted token already matches it; its comment now says so and notes the ASCII-only `\w` difference from the server.
This was referenced Sep 28, 2026
mbektas
approved these changes
Sep 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Chatbook
@mentions dropped the referenced file in two common cases. Punctuation typed right after a mention, as inUsing @file:data/README.md, convert..., became part of the path. And a path containing a space, which the mention menu itself offers, could not be expressed at all. In both cases the model received the mention as[unavailable]and generated code anyway, with nothing shown to the user. This follows the direction agreed on #503: match how Claude Code and Codex treat@paths.Solution
Server (
notebook_intelligence/chatbook_mentions.py)Quoted form.
@file:"data/my notes.md",@dir:"My Folder", and@ext:"provider:value"name a value containing whitespace or@, following Claude Code's@"path"and Codex's habit of quoting a picked path that contains whitespace. A quoted value is taken literally. It cannot hold a double quote or a line break, and a closing quote glued to a word character (@file:"a"b.csv) keeps the old single-token reading.Trailing punctuation. Sentence punctuation typed after an unquoted file or dir mention is not part of the path. That means
.,;:!?…, closing brackets and quotes, and their full-width forms. Resolution tries the name as written, then with up to five of those characters removed one at a time, and stops at the first name that exists, even if it cannot be read. So:notes.is found beforenotes);data.does not fall through to a text filedata);+,-, combining marks, and invisible or bidi characters are never trimmed.foo.c++staysfoo.c++, and a trailing U+202E is refused rather than stripped;_safe_relative_path, so trimming cannot reach a dotfile, a skipped directory, or anything outside the root.This is deliberately narrower than Claude Code's
\b, which backtracks off any non-word character.ext:values are quoted-capable but never trimmed. The extension docs describe provider values as opaque, and the example provider returns""for an unknown value, so there is no reliable miss signal. Trimming could quietly resolve a different item.docs/chatbook-extensions.mdnow says this.Duplicates are sent once. Mentions are deduplicated by resolved path, or by provider value for
ext:, before any file is read or provider is called. So@file:big.csv, … @file:"big.csv"reads the file once.Frontend (
src/chatbook-mentions.ts)@, or U+001C to U+001F (which Python's\salso treats as separators). It leaves alone values it cannot quote.select()andapplyChatbookMentionnow sharechatbookMentionInsertText, and the caret is placed from the inserted text's length. The previousitem.value.length + 1 + …arithmetic would land two characters short inside a quoted token.CHATBOOK_MENTION_TOKEN_REis unchanged. It only answers whether a prompt has any mention, and every quoted token already matches it. Its comment now says so.The existing test that pinned
@dir:docs,as unavailable ("NUI-compatible tokens") now expects it to resolve, per the #503 decision.Testing
…,.”, and。;(@dir:data(2))findsdata(2));foo.c++, NFDé,, binarydata.);.., bidi and zero-width suffixes);@;ext:quoted and unquoted call the provider once;@, whitespace, the U+001C to U+001F separators, and the quote and line-break exclusions), plus a menu test that picks a spaced path through the realEditorViewand asserts both the inserted text and the caret position.\W+$trim, trimming before the exact name, skipping the safety check on a trimmed candidate, the old caret arithmetic, keying a refused mention on its trimmed name, and dropping theext:dedupe. The linear-time test hits pytest-timeout against a regex trim.pytest tests/ -q(2181 passed, plus 77 intest_claude_client.py),jlpm tsc --noEmit,jlpm lint:check,jlpm jest(595 passed).MENTION_CONTEXTit received. Through the real menu I pickeddata/my notes.mdandlogo@2x.txt, which were inserted as@file:"data/my notes.md"and@file:"logo@2x.txt", with text typed afterwards landing after the closing quote. I then wrote@file:data/README.md. Done. All three reached the model with the right path and the file's contents.\W+$, which was quadratic on long punctuation runs, stripped combining marks and bidi characters, and could fall through to a different file. The punctuation set, one-at-a-time trimming, the "first existing name wins" rule, and the pre-read dedupe all came out of that review.Risks / follow-ups
@, because Claude Code, which receives nui's rewritten@pathmentions, requires the same.@file:data.csv,used to send[unavailable]and now sends the file, so regenerated code can differ. That is the intended fix.docs/chatbook.mdsays so.@dir:data/...is not trimmed back todata/. That is conservative, and rare.No active debugger sessionfrom JupyterLab's debugger. I reproduced this on released 6.0.0 with a plain prompt, no mentions, and filed it as Running a Chatbook cell throws an uncaught "No active debugger session" error #507.CHANGELOG.mdis untouched, since entries are curated at release and its top section still reads[6.0.0] - unreleased.Screenshots
The confirm bar from the live run, showing which mentions the stand-in model received, is available to add here.
Closes #503