diff --git a/frontend/app/view/term/term-links.test.ts b/frontend/app/view/term/term-links.test.ts
new file mode 100644
index 0000000000..42a895200a
--- /dev/null
+++ b/frontend/app/view/term/term-links.test.ts
@@ -0,0 +1,44 @@
+// Copyright 2026, Command Line Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+import { describe, expect, it, vi } from "vitest";
+import { makeTermLinkHandlers } from "./term-links";
+
+function linkEvent(metaKey = false, ctrlKey = false): MouseEvent {
+ return { metaKey, ctrlKey, preventDefault: vi.fn(), clientX: 12, clientY: 34 } as unknown as MouseEvent;
+}
+
+describe("terminal link handlers", () => {
+ it.each([
+ { isMacOS: true, modifier: "metaKey" },
+ { isMacOS: false, modifier: "ctrlKey" },
+ ])("only opens $modifier-clicked links on the corresponding platform", ({ isMacOS }) => {
+ const openUri = vi.fn();
+ const handlers = makeTermLinkHandlers(isMacOS, openUri, vi.fn());
+ const plainClick = linkEvent();
+ handlers.activate(plainClick, "https://example.com/first");
+ expect(plainClick.preventDefault).toHaveBeenCalledOnce();
+ expect(openUri).not.toHaveBeenCalled();
+
+ const wrongModifier = linkEvent(!isMacOS, isMacOS);
+ handlers.activate(wrongModifier, "https://example.com/second");
+ expect(openUri).not.toHaveBeenCalled();
+
+ const rightModifier = linkEvent(isMacOS, !isMacOS);
+ handlers.activate(rightModifier, "https://example.com/third");
+ expect(openUri).toHaveBeenCalledExactlyOnceWith("https://example.com/third");
+ });
+
+ it("shows the destination for OSC 8 links but keeps plain URL hover unchanged", () => {
+ const onHover = vi.fn();
+ const handlers = makeTermLinkHandlers(false, vi.fn(), onHover);
+ handlers.hover(linkEvent(), "https://example.com/visible");
+ handlers.osc8Hover(linkEvent(), "https://example.com/hidden");
+ handlers.leave();
+ expect(onHover.mock.calls).toEqual([
+ ["https://example.com/visible", 12, 34, false],
+ ["https://example.com/hidden", 12, 34, true],
+ [null, 0, 0, false],
+ ]);
+ });
+});
diff --git a/frontend/app/view/term/term-links.ts b/frontend/app/view/term/term-links.ts
new file mode 100644
index 0000000000..ceaf15c7ea
--- /dev/null
+++ b/frontend/app/view/term/term-links.ts
@@ -0,0 +1,21 @@
+// Copyright 2026, Command Line Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+export function makeTermLinkHandlers(
+ isMacOS: boolean,
+ openUri: (uri: string) => void,
+ onHover: (uri: string | null, x: number, y: number, showUrl: boolean) => void
+) {
+ return {
+ activate: (event: MouseEvent, uri: string) => {
+ event.preventDefault();
+ if (!(isMacOS ? event.metaKey : event.ctrlKey)) {
+ return;
+ }
+ openUri(uri);
+ },
+ hover: (event: MouseEvent, uri: string) => onHover(uri, event.clientX, event.clientY, false),
+ osc8Hover: (event: MouseEvent, uri: string) => onHover(uri, event.clientX, event.clientY, true),
+ leave: () => onHover(null, 0, 0, false),
+ };
+}
diff --git a/frontend/app/view/term/term-tooltip.test.tsx b/frontend/app/view/term/term-tooltip.test.tsx
new file mode 100644
index 0000000000..15213c37e1
--- /dev/null
+++ b/frontend/app/view/term/term-tooltip.test.tsx
@@ -0,0 +1,22 @@
+// Copyright 2026, Command Line Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+import { renderToStaticMarkup } from "react-dom/server";
+import { describe, expect, it } from "vitest";
+import { TermLinkTooltipContent } from "./term-tooltip";
+
+describe("TermLinkTooltipContent", () => {
+ it("keeps the existing modifier-click hint for plain URLs", () => {
+ const markup = renderToStaticMarkup();
+ expect(markup).toContain("-click to open link");
+ expect(markup).not.toContain("break-all");
+ });
+
+ it("shows and safely escapes the destination for OSC 8 links", () => {
+ const markup = renderToStaticMarkup();
+ expect(markup).toContain("-click to open link");
+ expect(markup).toContain("https://example.com/?next=<script>&x=1");
+ expect(markup).toContain("break-all");
+ expect(markup).not.toContain("