Skip to content

fix(client): links in agent questions open when clicked - #19214

Closed
SunkenInTime wants to merge 2 commits into
pingdotgg:mainfrom
SunkenInTime:t3/question-view-clickable-links
Closed

SunkenInTime wants to merge 2 commits into
pingdotgg:mainfrom
SunkenInTime:t3/question-view-clickable-links

Conversation

@SunkenInTime

Copy link
Copy Markdown
Contributor
Before After
Web / desktop web before web after
iOS ios before ios after
Android android before android after

Problem

When an agent asks a question (Claude's AskUserQuestion, Codex's request_user_input), the question card prints the text as plain text. Agents often put a link in the question, like "mocks are at https://... which one should I build?", and there was no way to open it short of copying the URL by hand. This happened on web, desktop and mobile.

Change

A small helper in @t3tools/shared/plainTextLinks splits text into prose and bare http(s) URLs. It drops trailing sentence punctuation and markdown emphasis the way GitHub's autolinker does, keeps brackets the URL opened itself (Wikipedia paths), and stops at quotes and at the ]( of a markdown link whose label is the URL.

Each client renders the URL pieces as links and opens them the same way it already opens links next to the chat:

  • Web and desktop (ComposerPendingUserInputPanel) use useOpenPrLink. A pull request link opens in the PR panel, other links follow the "Open links in" setting, and a modifier-click still goes to the system browser.
  • Mobile (PendingUserInputCard) uses useOpenPullRequest, the same fallback chat markdown links take for http(s) URLs. Links are nested Text with accessibilityRole="link", so VoiceOver reads them as links.

Only the expanded question paragraph gets links. The collapsed web header stays plain because the whole header is the expand button, and option labels and descriptions stay plain because they sit inside the answer buttons. Linking those, and the answered-question history in the timeline, would need its own interaction design.

Scope and approval

Small fix for an obvious bug: text that looks like a link but can't be opened. No new setting or behavior beyond making the URL open the way other links in the app already do. Filed by a maintainer.

Verification

All on one dev server with a live Claude thread asking a question that contains a GitHub PR link and https://t3.codes. Each "before" is the same build with only the card component swapped back to main.

  • Web: clicking https://t3.codes opened a new tab. Clicking the PR link opened PR fix(desktop): stop unsolicited Messages code autofill #19118 in the side panel. screenshot
  • iOS simulator (iPhone 17 Pro): tapping https://t3.codes opened Safari on t3.codes. screenshot
  • Android emulator (Pixel 9 Pro, fresh dev build): tapping https://t3.codes opened Chrome on t3.codes. screenshot
  • vp test run packages/shared/src/plainTextLinks.test.ts (6 cases) and the existing ComposerPendingUserInputPanel tests pass; typecheck is clean for web, mobile and shared.

Desktop wasn't run separately. It renders the same web component, and its _blank handling is the path chat links already use.

Made with Claude Opus 5.5 in Claude Code, running inside T3 Code.

🤖 Generated with Claude Code

SunkenInTime and others added 2 commits October 13, 2026 16:16
Question text from an agent rendered as plain text on web, desktop and
mobile, so a URL in it could not be opened. Bare http(s) URLs in the
question now render as links that open the way links in chat messages do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A URL wrapped in curly quotes kept the closing quote, and a markdown link
whose label is the URL swallowed the whole `](...)` tail, so both opened
the wrong address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Oct 14, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a focused fix that makes bare URLs in active agent questions clickable across web and mobile using existing link-routing behavior. Human review is required because the production UI changes add line-level lint suppressions for array-index keys.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 716239e5-05f1-4106-a568-1ba3d2935828
📥 Commits

Reviewing files that changed from the base of the PR and between 85272ee and 52edbbd.

📒 Files selected for processing: 9
  • apps/mobile/src/features/threads/PendingUserInputCard.tsx
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/chat/ComposerPendingUserInputPanel.navigation.test.tsx
  • apps/web/src/components/chat/ComposerPendingUserInputPanel.permissions.test.tsx
  • apps/web/src/components/chat/ComposerPendingUserInputPanel.test.tsx
  • apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx
  • packages/shared/package.json
  • packages/shared/src/plainTextLinks.test.ts
  • packages/shared/src/plainTextLinks.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Pending-user-input question text is now split into plain-text and HTTP(S) URL segments. Web and mobile views render URL segments as tappable links and pass link activation to their respective open callbacks.

Changes: Pending user input links

Layer / File(s) Summary
Plain-text URL parsing
packages/shared/src/plainTextLinks.ts, packages/shared/package.json, packages/shared/src/plainTextLinks.test.ts
Adds and exports splitPlainTextLinks and its segment type. The parser separates HTTP(S) URLs from surrounding text and trims specified trailing punctuation and unmatched closing brackets. Tests cover URL boundaries, punctuation, brackets, and text without links.
Pending-input link rendering
apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx, apps/web/src/components/chat/ChatComposer.tsx, apps/web/src/components/chat/ComposerPendingUserInputPanel*.test.tsx, apps/mobile/src/features/threads/PendingUserInputCard.tsx
Web and mobile question text now renders parsed URLs as links. The web panel forwards its open callback through ChatComposer; the mobile card uses useOpenPullRequest. Updated web test fixtures provide the callback.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant ComposerPendingUserInputPanel
  participant splitPlainTextLinks
  participant onOpenLink
  participant openPrLink
  ComposerPendingUserInputPanel->>splitPlainTextLinks: Parse question text
  splitPlainTextLinks-->>ComposerPendingUserInputPanel: Return text and URL segments
  User->>ComposerPendingUserInputPanel: Activate URL link
  ComposerPendingUserInputPanel->>onOpenLink: Pass click event and URL
  onOpenLink->>openPrLink: Open URL
Loading

Suggested reviewers: t3dotgg

Merge Risk: ⚪ Minimal · up to 52edb

No merge-blocking issue is established for the pending-input link change; it is ready to merge after normal checks.

Security architecture risk: 🔵 Low · up to 52edb

The links require a user click or tap and are limited to HTTP(S), while existing link-opening behavior is reused. No new security vulnerability was established, but protection of authenticated pull-request reads and recovery after mobile navigation could not be fully confirmed.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A question author can influence the destination activated by a viewing user. Reachable outcomes include external websites, an environment-scoped browser preview when permitted, and PR reads through existing project credentials. The inspected callers derive environment/project selection from application context rather than from the question URL; downstream mobile host enforcement remains unverified.

Security Findings and Attack Paths

  • inferred — The newly reachable path is agent question text to HTTP(S) segmentation, then explicit user activation and an existing opener. This expands interactive reachability, but does not directly render executable markup or submit a pending answer. An authorization bypass or credential disclosure was not established by the inspected path.

Trust Boundaries and Controls

  • observed — Web PR activation filters projects to the thread environment, resolves repository or permitted host matches, and checks PR capabilities before opening the panel. Ordinary in-app opening additionally requires preview-operation scope and preview availability. New web anchors carry noopener and noreferrer.
  • observed — Mobile internal routing requires both a recognized HTTP(S) PR URL and environment/project context. Otherwise it falls back to native external opening. The external helper catches opening errors, but is not itself a destination allowlist; this caller supplies the HTTP(S)-restricted parser output.

Resilience and Maintainability Implications

  • observed — The inherited web flow stops on preview interruption, rejects settings-read failure without choosing a fallback, and otherwise falls back to external opening after preview failure. Successful preview state is applied only through the successful command result. Mobile external failure returns false without dispatching internal navigation.
Pre-merge checks: Passed 4
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: making links in agent questions open when clicked.
Description check Passed The description includes all required sections: Problem, Change, Scope and approval, and Verification. It provides reproduction context, implementation details, before-and-after screenshots, focused t…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches: 💡 1
⚔️ Resolve merge conflicts: 💡
  • Resolve merge conflict in branch t3/question-view-clickable-links
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant