[Bug]: OSC 8 terminal hyperlinks go permanently dead after a worktree/tab switch (linkifier hover-cache not reset on reveal)
- Dominant language
- TypeScript
- Stars
- 71.3k
- Forks
- 4.7k
- Avg merge
- 14h 54m
- Merged PRs (30d)
- 520
Description
### Operating system
macOS
### Orca version
1.4.162 (terminal reports `TERM_PROGRAM_VERSION=1.4.162`)
### Details
#### Summary
OSC 8 hyperlinks emitted by a full‑screen TUI / agent in an Orca terminal (e.g. the `pi` coding agent's markdown links, or any program that emits `ESC]8;;ESC\ESC]8;;ESC\`) become **permanently non‑interactive ("dead") after switching to another worktree/tab and returning**. The label keeps its underline styling (the app's own SGR), so it still *looks* like a link, but hovering shows no tooltip and Cmd/Ctrl‑click does nothing. Links rendered **after** the switch work fine in the same terminal.
This is the in‑app worktree/tab‑switch analogue of the (closed) window‑focus bug #9116, and the existing linkifier hover‑reset does not cover it.
#### Steps to reproduce
1. In an Orca terminal, run a TUI/agent that emits OSC 8 hyperlinks with labels (e.g. `pi` rendering a markdown link `[Example](https://example.com)`; `TERM_PROGRAM=Orca` + the app advertising hyperlink support).
2. Confirm the link is live: hover shows the tooltip, Cmd/Ctrl‑click opens it.
3. Switch to a **different worktree/tab and back** — most reliably with a **keyboard** switch (a mouse‑click switch often self‑heals because the pointer leaving the terminal fires `mouseleave`, which already resets the cache).
4. Hover / Cmd‑click the **same** link again.
#### Actual behavior
The previously‑live link is dead: no hover tooltip, no Cmd/Ctrl‑click activation. The underline styling remains (it's the app's own SGR, not xterm's link decoration). The dead state is **permanent** for those cells — it is **not** revived by moving the mouse across cells, scrolling, or resizing the window. Only re‑rendering the link (new output over those cells) or a full linkifier reset restores it.
#### Expected behavior
OSC 8 links that were live before a worktree/tab switch remain hoverable/clickable after returning.
#### Frequency
Consistently reproducible via a keyboard worktree switch.
#### Scope
- Affects **all** OSC 8 links in TUI/agent output, not any specific provider (GitLab/GitHub/etc.).
- The affected pane was on xterm's **DOM renderer** (`.xterm.xterm-dom-renderer-owner-*`).
#### This is not the emitting app's fault (ruled out)
Verified via DevTools on the running app that the TUI emits correct OSC 8 and that Orca keeps the data on the reveal:
- The interactive TUI writes a correct sequence on the wire: `␛]8;;https://example.com␛\ … Example … ␛]8;;␛\`.
- On a **dead** link, the cells still carry `cell.extended.urlId`, `terminal._core._oscLinkService.getLinkData(urlId)` still resolves to the URL, and `terminal.options.linkHandler` is still set. (Measured across ~1950 OSC‑link cells: `oscResolvable === oscCellsWithUrlId`, `linkHandlerSet: true`.)
So the link **data** and the **handler** survive the switch — the break is purely in the live linkifier interaction on reveal.
#### Root cause analysis
Instrumenting `terminal._core.linkifier` (xterm `@xterm/xterm` 6.1.0‑beta.287, `src/browser/Linkifier.ts`) while hovering a dead link:
- `_handleMouseMove` reaches the element and `_handleHover(position)` fires with **correct** coordinates for the link cell.
- But `_askForLink` / the OSC `provideLinks` provider / `linkHandler.hover` are **never** called.
Per xterm's `_handleHover`, `_askForLink` is only skipped when the linkifier already believes a link is active at that cell:
```js
private _handleHover(position) {
if (this._activeLine !== position.y || this._wasResized) { this._clearCurrentLink(); this._askForLink(position, false); this._wasResized = false; return; }
const isCurrentLinkInPosition = this._currentLink && this._linkAtPosition(this._currentLink.link, position);
if (!isCurrentLinkInPosition) { this._clearCurrentLink(); this._askForLink(position, true); }
}
```
i.e. a **stale `_currentLink` / `_activeProviderReplies` hover cache survives the reveal**, so the linkifier short‑circuits and never re‑queries providers for those lines. (Freshly‑rendered links work because rendering over new cells re‑seeds the cache.)
#### Why the existing fix doesn't cover this
Orca already has `resetTerminalLinkifierHoverState()` in `src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset.ts`, whose own doc‑comment describes exactly this "link stays dead after reveal" scenario. But:
1. It is wired only to **`mouseleave`** and **on‑write** (`src/renderer/src/lib/pane-manager/pane-lifecycle.ts`), **not** to the pane **reveal** path. A keyboard worktree/tab switch never fires `mouseleave` and (when the agent is idle) never writes, so the reset never runs.
2. It only clears `_lastBufferCell` and `_activeLine` — **not** `_currentLink` or `_activeProviderReplies`, which (per the trace above) are the fields that short‑circuit `_askForLink`. This is consistent with the observation that the dead state survives mouse movement, scroll, and resize.
The pane‑reveal transition already exists at the `resumedFromHidden` branch in `src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts` (it already does resize/PTY‑tracking repair there).
#### Suggested fix
1. Call `resetTerminalLinkifierHoverState(pane.terminal)` for each visible pane on the `resumedFromHidden` reveal path in `use-terminal-pane-lifecycle.ts` (the same helper already used for mouseleave/write).
2. Extend `resetTerminalLinkifierHoverState` to also clear `_currentLink` and `_activeProviderReplies` (guarded, like the existing `_lastBufferCell`/`_activeLine` clears) so a reveal fully re‑linkifies on the next mouse move.
A quick console repro of the fix on a dead link (clearing the caches + a mouse move revives it):
```js
const lf = terminal._core.linkifier;
lf._currentLink = undefined; lf._activeLine = -1; lf._lastBufferCell = undefined; lf._activeProviderReplies = undefined;
// then move the mouse over the previously‑dead link → it becomes live again
```
#### Environment
- macOS, Apple Silicon
- Orca 1.4.162 (`TERM_PROGRAM_VERSION=1.4.162`)
- `@xterm/xterm` / `@xterm/headless` 6.1.0‑beta.287, `@xterm/addon-web-links` 0.13.0‑beta.287
- Repro TUI: `pi` coding agent (emits standard OSC 8; not Orca‑specific)
#### Related
- #9116 (closed) — "GitLab links become non-clickable after switching away from Orca" — same failure mode via **window** blur/focus; this issue is the **worktree/tab‑switch** path that the current reset does not cover.
- #10212 — terminal link clicks breaking for local worktrees under a focused remote runtime (possibly related reveal/focus interaction).
Contributor guide
Assessment
This issue has not been assessed yet.