Repository navigation
Description
Summary
When an assistant message contains a markdown link [label](https://example.com/path), OpenCode’s TUI correctly attaches an OSC 8 hyperlink, but still renders the URL in the clear as:
label (https://example.com/path)
Expected (terminals with OSC 8, e.g. Warp): visible text is only label, clickable via OSC 8. The href should not appear next to the label.
Environment
- OpenCode 1.18.23 (
opencode-ai) - Terminal: Warp (OSC 8 works — span is underlined/clickable)
- OS: Linux
Steps to reproduce
- Have the agent (or paste as assistant content) output:
See [codecrafters-io/build-your-own-x](https://github.com/codecrafters-io/build-your-own-x).
- View the message in the TUI.
Actual
Visible: codecrafters-io/build-your-own-x (https://github.com/codecrafters-io/build-your-own-x)
OSC 8 covers the whole span (label + paren URL).
Expected
Visible: codecrafters-io/build-your-own-x only
OSC 8 target: https://github.com/codecrafters-io/build-your-own-x
Why it matters
Agents are instructed to use short labels (owner/repo, etc.) so tables and lists stay readable. Appending the full URL defeats that and breaks narrow layouts (tables especially).
Agents must not emit raw OSC 8 in chat (control chars are stripped/mangled). The only workable path is markdown links → TUI paint. So the TUI needs to conceal the href when OSC 8 is available.
Root cause (1.18.23)
Inline markdown renderer, case "link" (minified opencode.exe):
case "link": {
let $ = { url: _.href };
if (this._conceal) {
for (let Z of _.tokens)
this.renderInlineTokenWithStyle(Z, J, "markup.link.label", $);
J.push(this.createChunk(" (", "markup.link", $));
J.push(this.createChunk(_.href, "markup.link.url", $));
J.push(this.createChunk(")", "markup.link", $));
} else {
// raw [label](href)
...
}
}createChunk(..., { url: href })correctly sets the OSC 8 link field.- With
_concealdefault true (_contentDefaultOptionshasconceal: !0), it still appends" (" + href + ")".
Related symbols: OSC8LinkProvider, _linkifyMarkdownChunks, conceal: !0.
Suggested fix
When conceal is on and hyperlinks are supported (OSC 8 / setHyperlinksCapability), render only the label tokens with { url: href }. Skip the " (", href, ")" chunks.
Optional fallback when OSC 8 is unavailable: keep today’s label (url) (or bare URL) so non-OSC terminals still show a usable target.
Out of scope / related
Not the same as wrap/click bugs:
- URLs in tree-sitter markdown rendering have no OSC 8 hyperlink support (links break at line wrap) #14966 (closed) — OSC 8 missing on tree-sitter plain URLs
- Links wrapped across lines not clickable in Kitty terminal #35649 — wrapped links not clickable (Kitty)
- tui: Ctrl+click does not open Markdown links when mouse capture is active #44442 — Ctrl+click with mouse capture
This issue is only about visible href duplication next to an already-linked label.
Activity
Alphaxiaoteng commentedon Aug 25, 2026
Upstream Root Cause & Architectural Investigation
Investigated the link rendering behavior in
packages/tui/src/routes/session/index.tsx(<markdown conceal={ctx.conceal()} ... />).1. Source Ownership & Boundary
The Markdown inline token rendering logic (
case "link") lives entirely inside upstream dependency@opentui/core(specifically in its internal markdown token renderer), rather than inside OpenCode repository source:conceal: trueis active,@opentui/coreunconditionally formats markdown links by appending" (" + href + ")"chunks:@opentui/solid/@opentui/core's built-in<markdown>component directly and passes formatting options (such asconceal,syntaxStyle,tableOptions), but@opentui/coredoes not expose a public token-render hook or markdown AST visitor to customize inline link chunk emission from consumer code.2. Conclusion & Recommended Upstream Resolution
UPSTREAM_DEPENDENCY/ No local OpenCode code patch.@opentui/core): Make link formatting in@opentui/corecapability-aware (e.g. check terminal OSC 8 hyperlink support /setHyperlinksCapabilitywhenconceal: true). In terminals supporting OSC 8, emit only the label tokens styled with{ url: href }and omit the trailing" (" + href + ")"chunks; keep the expanded fallback format for terminals lacking OSC 8 support.tkossak commentedon Aug 25, 2026
Fix lives in
@opentui/core(MarkdownRenderablecase "link"), not in OpenCode. Filed:anomalyco/opentui#1435
Not a duplicate of anomalyco/opentui#1050 — that one only wants to stop bare
url (url)duplication, and treats namedlabel (url)as intended. This issue is: when OSC 8 is available, named links should be label-only; keeplabel (url)only as a no-OSC-8 fallback.paakwasi317 commentedon Sep 16, 2026
The upstream fix is now released. Could OpenCode V2 update its OpenTUI dependencies to include it?
@opentui/core,@opentui/solid, and@opentui/keymapto 0.5.10 in its dependency catalog: https://github.com/anomalyco/opencode/blob/v2.0.5/package.jsonI still see the label and destination displayed together after upgrading to OpenCode 2.0.5 and relaunching, using Ghostty 1.3.1 on Linux. Explicitly setting
session.markdowntorenderedand relaunching did not resolve it; that override has since been removed.The upstream PR implements compact named links when OSC 8 is supported, with visible URL fallbacks otherwise. Is an OpenTUI dependency update planned for an upcoming OpenCode V2 release?