Skip to content

fix(tabs): exclude sidecar URL when picking the main Perplexity tab#16

Merged
RapierCraft merged 1 commit into
RapierCraft:mainfrom
sergei-aronsen:fix/sidecar-tab-filter
May 16, 2026
Merged

fix(tabs): exclude sidecar URL when picking the main Perplexity tab#16
RapierCraft merged 1 commit into
RapierCraft:mainfrom
sergei-aronsen:fix/sidecar-tab-filter

Conversation

@sergei-aronsen
Copy link
Copy Markdown

Summary

Comet renders its right-panel chat helper at a URL that also matches the `perplexity.ai` substring (e.g. `perplexity.ai/.../sidecar`). Five sites in the codebase pick "any tab whose URL contains `perplexity.ai`" without excluding the sidecar:

  • `reconnect()` fresh-start fallback — after a reconnect, sendPrompt / stopAgent route to the wrong tab.
  • `ensureOnPerplexityTab()` initial current-URL check — returns `true` when on the sidecar.
  • `ensureOnPerplexityTab()` last-resort "any Perplexity tab" fallback — picks the sidecar if `listTabsCategorized().main` is null.
  • `isOnPerplexityTab()` predicate — callers checking this before dispatching prompts silently send to the sidecar.
  • `comet_connect` initial target picker + `comet_ask` recovery path — first-time connection lands on the sidecar.

Fix

`listTabsCategorized()` already encodes the right filter (`!t.url.includes('sidecar')`); we just weren't applying it consistently. Each affected site now does the same exclusion, with a secondary fallback to "any perplexity.ai tab" if literally no main tab exists.

Compatibility

No public API change. Default user with one main tab + sidecar gets the main tab from now on, instead of whichever appeared first in the CDP target list (often the sidecar after a recent agent run).

Test plan

  • `npm run test:unit` passes
  • Manual: open Comet sidecar then run `comet_connect` — connects to main tab, not sidecar
  • Manual: `comet_ask` after a forced reconnect targets the main tab
  • Manual: `comet_poll` returns status from main tab, not sidecar

🤖 Generated with Claude Code

Comet renders its right-panel chat helper at a URL that also matches
the `perplexity.ai` substring (e.g. `perplexity.ai/.../sidecar`). Five
sites in the codebase pick "any tab whose URL contains `perplexity.ai`"
without excluding the sidecar:

  - `reconnect()` "find best target" fresh-start fallback
    -> after a reconnect, sendPrompt / stopAgent route to the wrong tab.
  - `ensureOnPerplexityTab()` initial current-URL check
    -> the function happily returns `true` when we're on the sidecar.
  - `ensureOnPerplexityTab()` last-resort "any Perplexity tab" fallback
    -> picks the sidecar if `listTabsCategorized().main` is null.
  - `isOnPerplexityTab()` predicate
    -> callers that check `isOnPerplexityTab` before dispatching prompts
       silently send to the sidecar.
  - `comet_connect` initial target picker + `comet_ask` recovery path
    -> first-time connection lands on the sidecar.

The `listTabsCategorized()` helper already encodes the right filter
(`!t.url.includes('sidecar')`); we just weren't applying it
consistently. Each affected site now does the same exclusion, with a
secondary fallback to "any perplexity.ai tab" if literally no main tab
exists.

No public API change. Default user with one main tab + sidecar gets
the main tab from now on, instead of whichever appeared first in the
CDP target list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

@RapierCraft RapierCraft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #16 — fix(tabs): exclude sidecar URL when picking the main Perplexity tab

Reviewed commit: 978c63c

Overall Assessment

The fix is well-motivated and the approach is sound. listTabsCategorized() already had the correct sidecar exclusion logic — this PR correctly propagates that pattern to the 5 call sites that were doing raw url.includes('perplexity.ai') matching. The tiered fallback (main tab → any perplexity tab → any page) is a good defensive pattern.


Findings

1. Missed site: src/http-bridge.ts:102 (Medium severity)

The HTTP bridge has an identical handleConnect() function that still uses the unfiltered pattern:

// src/http-bridge.ts:102
const perplexityTab = targets.find(t => t.type === 'page' && t.url.includes('perplexity.ai'));

This suffers from the exact same sidecar-first-in-CDP-list bug. Users connecting via the HTTP bridge (Linux/n8n) will still land on the sidecar. Should get the same 3-tier fallback treatment as src/index.ts:187.

2. Missed site: src/index.ts:291 — newChat recovery path (Low severity)

const mainTab = targets.find(t => t.type === 'page' && t.url.includes('perplexity'));

This uses the substring 'perplexity' (not even .ai), and does not exclude sidecar. Since this is in a catch block (navigation failure recovery), hitting it means the user is already in a degraded state — connecting to the sidecar here would compound the problem. Lower priority since it immediately navigates to perplexity.ai after connecting, but the sidecar tab could still receive the navigation.

3. Missed site: src/index.ts:312 — non-newChat isOnPerplexity check (Low severity)

const isOnPerplexity = currentUrl?.includes('perplexity.ai');

After listTabsCategorized().main is connected (line 305-308), this check evaluates the URL. If tabs.main was null and we fell through, we'd evaluate whatever tab was already connected. However, if already connected to the sidecar, this returns true and skips navigation — so the prompt goes to the sidecar. Mitigated by the fact that listTabsCategorized().main uses the correct filter upstream, so this only triggers if tabs.main was found and connected successfully. Risk: if connect(tabs.main.id) fails silently, the old (possibly sidecar) connection remains active.

4. String-matching robustness (Informational — not blocking)

The filter !t.url.includes('sidecar') is a substring match. If Perplexity ever introduces a URL path containing "sidecar" for a non-sidecar purpose, this would create a false negative (legitimate tab excluded). Current risk is negligible — "sidecar" is a highly specific term unlikely to appear in normal Perplexity URLs. Consider whether matching a more specific path pattern (e.g., /sidecar) would be more precise.

5. isOnPerplexityTab() return value equivalence (Informational)

The original url?.includes('perplexity.ai') || false becomes !!url && url.includes('perplexity.ai') && !url.includes('sidecar'). Both handle null/undefined correctly. The new version also returns false for empty string "" — which is correct. No issue, just noting equivalence is maintained.


Summary

# Location Severity Type
1 src/http-bridge.ts:102 Medium Missed site (same bug)
2 src/index.ts:291 Low Missed site (recovery path)
3 src/index.ts:312 Low Missed site (conditional)
4 Filter specificity Info Future-proofing
5 Return value equivalence Info No action needed

Verdict

Approve with suggestions. The core fix is correct and well-reasoned. Finding #1 (http-bridge.ts) should ideally be addressed in this PR since it is the same class of bug and the HTTP bridge is a first-class entry point. Findings #2-3 are lower risk but worth fixing for consistency.

The PR does not introduce regressions — the fallback chains ensure that if no non-sidecar tab exists, the code gracefully degrades to picking any available perplexity tab (or any page). This is the right defensive behavior.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants