Repository navigation
Teach a table-safe image size syntax, and only to agents that can use it - #323
Merged
Merged
Conversation
Two problems with how the screenshot-embed behaviour was prompted. The documented size syntax broke inside tables. A pipe in a GFM table row is a column delimiter, so `` is split into `` before any inline parsing happens — no image node is produced at all and the cells render as broken literal text. The pipe form was the one form we taught, so this fired exactly when an agent did what it was told. Every prompt surface now teaches the `#w=` fragment instead: ``, `#w=50%`, `#w=400x300`. It's part of the URL, so it survives table cells, lists and blockquotes untouched. The pipe and `=title` forms still work for agents that guess them; the pipe is now documented only as the thing to avoid. Fragments also needed percent-decoding — markdown encodes the URL on the way through, so an agent's `#w=50%` arrives as `#w=50%25`. The tool description also over-promised. The MCP bridge is shared between Chat and Terminal tabs, but inline rendering only exists in Chat; a terminal-tab agent was told "Ness renders the screenshot inline in the chat" and would emit markdown into an xterm, where it's literal text. CallerScope gains rendersMarkdown (true for 'json-claude' tabs), /scope carries it, and the bridge strips the embed sentence from the tool description and the hint from the result line when it's false — mirroring how stripForkAffordance already works. The path itself is still reported either way. Defaults permissive when /scope can't say. src/renderer/chat-image-markdown.test.ts pins the actual failure: it renders each syntax through ReactMarkdown + remark-gfm inside a table cell and asserts the pipe form yields no image while the fragment does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Follow-up to #320. Two problems with how the screenshot-embed behaviour was prompted.
The documented size syntax broke inside tables
A pipe in a GFM table row is a column delimiter, so
gets split intobefore any inline parsing happens — no image node is produced at all, and the cells render as broken literal text:The pipe form was the one form we taught, so this fired precisely when an agent did what it was told.
Every prompt surface now teaches the
#w=fragment instead —,#w=50%,#w=400x300. It's part of the URL, so it survives table cells, lists and blockquotes untouched. The pipe and=titleforms still work for agents that guess them; the pipe is now mentioned only as the thing to avoid.Fragments also needed percent-decoding: markdown encodes the URL on the way through, so an agent's
#w=50%arrives as#w=50%25.The tool description over-promised
The MCP bridge is shared between Chat and Terminal tabs, but inline rendering only exists in Chat (json-mode). A terminal-tab agent was told "Ness renders the screenshot inline in the chat" and would dutifully emit markdown into an xterm, where it's literal text.
CallerScopegainsrendersMarkdown(true forjson-claudetabs),/scopecarries it, and the bridge strips the embed sentence from the tool description and the hint from the per-result line when it's false — mirroring howstripForkAffordancealready works forcreate_worktree. The path itself is still reported either way, since that's useful regardless. Defaults permissive when/scopecan't answer, matching the existing browser gates.Verification
src/renderer/chat-image-markdown.test.ts(new) pins the actual bug: renders each syntax through ReactMarkdown + remark-gfm inside a table cell and asserts the pipe form yields no image while the fragment,=title, and escaped-pipe forms all resolve to the right size. Follows the existingremark-insight.test.tspattern — no new deps./scopetest assertsrendersMarkdownreaches the caller, and that a missing terminal id still 400s rather than guessing.npm run typecheck,npx electron-vite build,node -c resources/mcp-bridge.js, and all 1756 tests pass.One note for whoever merges: the system-prompt change only reaches users with no persisted
harnessSystemPrompt. Anyone who has edited their prompt in Settings keeps their copy, pipe syntax and all.🤖 Generated with Claude Code