Skip to content

feat(desktop): browser pane の Connect 導線と session バインディング UI (Phase 1)#345

Merged
MocA-Love merged 25 commits intomainfrom
feature/browser-pane-connect
Apr 20, 2026
Merged

feat(desktop): browser pane の Connect 導線と session バインディング UI (Phase 1)#345
MocA-Love merged 25 commits intomainfrom
feature/browser-pane-connect

Conversation

@MocA-Love
Copy link
Copy Markdown
Owner

@MocA-Love MocA-Love commented Apr 19, 2026

Summary

browser pane から任意の LLM セッションを選んで接続できる UX を v1 workspace の BrowserPane に組み込みました。session 列挙と MCP readiness 判定は本番データで動作します。実 automation bridge (binding を使って外部 MCP から webview を駆動する経路) は本 PR の対象外です。

実装済み

  • renderer/stores/browser-automation.ts: UI-only state (modal / listView) を保持する zustand store
  • renderer/hooks/useBrowserAutomationData.ts: todoAgent.listAll から live session (running / preparing / verifying / waiting) だけを抽出、browserAutomation.getMcpStatus を突き合わせて mcpStatus を解決
  • lib/trpc/routers/browser-automation/: paneId → sessionId binding を in-memory で保持 (listBindings / setBinding / removeBinding / onBindingsChanged subscription)、~/.claude/settings.json~/.codex/config.toml を走査して `superset-browser` MCP entry の有無を判定する getMcpStatus
  • ConnectButton: toolbar に配置、未接続は `Connect`、接続済みは `Session N · Provider`
  • SessionConnectModal: 2カラムで session 一覧 + 詳細 / 未設定ガイド、再割当はトーストで通知、stale selection / 空状態もハンドル
  • BrowserAutomationList: ContentView 直下にマウント、tabs.workspaceId で現在の workspace に属する browser pane のみ表示、Focus は setActiveTab + setFocusedPane で別 tab からもナビゲート

本 PR の対象外 (別 PR で対応)

  • 実 automation bridge: binding は保存されるが、外部 MCP サーバ (例: packages/desktop-mcp) がその binding を参照して electronTrpc.browser.navigate / evaluateJS / screenshot を呼ぶ経路は未配線。MCP 側に `resolvePaneForSession` 相当のツールを足す必要がある
  • v2 workspace 対応: apps/desktop/src/renderer/routes/**/v2-workspace/** には手を入れていない (v1 の `WorkspaceView` 配下のみ)
  • binding の永続化: 再起動で消える in-memory 保持。必要ならば local-db に移す
  • 非 Claude session のサポート: 現状 todoAgent session (Claude Code) を Claude として列挙するのみ。Codex runner / chat pane も session として扱いたい場合は hook の変換側で追加

Review 対応履歴

  • Codex: P1 terminal/非 live status フィルタ、P2 workspace フィルタ・Focus での setActiveTab・stale binding での disconnect 許可・live status のみ connect 可、P3 onBindingsChanged で listBindings invalidate
  • CodeRabbit: EventEmitter の `setMaxListeners(0)`、selectedSessionId が live set から外れた際の自動フォールバック

すべて resolve 済み。

Test plan

  • v1 workspace で browser pane を開き、toolbar の `Connect` からモーダルが開く
  • `~/.claude/settings.json` に `superset-browser` を含めない状態で live な todoAgent session が "Needs MCP" になる / 含めた後に ready になる
  • 実際の todoAgent session を選んで Connect すると toolbar ラベルが `<title> · Claude` に更新される
  • 別 pane から同じ session を選ぶと reassign toast が出て、前の pane の binding が剥がれる
  • session が terminal 状態になったらリストから消え、選択中だった場合は自動フォールバックが走る
  • BrowserAutomationList が 現在の workspace の browser pane のみを出す。Focus で別 tab に跨いだ pane もアクティブ化される
  • 2 つ目の window を開いて setBinding を叩いた時、もう一方の window も即座に反映する (onBindingsChanged invalidation)

Summary by CodeRabbit

  • New Features

    • ブラウザペインと実行中LLMセッションを結びつけるUIを追加:ツールバーの接続ボタン、接続モーダル、一覧ビュー、接続状態バッジ、メトリクス表示、ペインへのフォーカス移動と接続/切断操作。
  • Chores

    • ペイン⇄セッションの永続化とバックエンドAPI/同期を導入(再起動後も割当が保持され、ライブ状態を監視)。
  • Documentation

    • 接続ワークフローと要件をまとめた設計/TODOドキュメントを追加。

…1 UI)

browser pane toolbar に Connect ボタン・session 選択モーダル・一覧ビューを追加。
paneId -> sessionId のバインディングは renderer 側 zustand store で管理し、
ダミーデータで UX を一通り動作させる。Phase 2 以降で実セッション/MCP 連携に
置き換える想定。

- browser-automation store: sessions/bindings/connectModal/listView の状態管理
- ConnectButton: toolbar に配置、接続済みは "Session X · Provider" を表示
- SessionConnectModal: 2カラム(セッション一覧 + 詳細/MCP セットアップ案内)
- BrowserAutomationList: workspace の全 browser pane とバインディング状況
- 再割当トースト / 切断導線 / MCP missing の inline ガイドを実装
- mock.html / todo.md を参考資料として追加 (biome では除外)
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

ブラウザペインと実行中LLMセッションを結び付けるUIとバックエンドを追加。Zustandストア、TRPCルーターと永続BindingStore、データフック、Connectボタン、セッション接続モーダル、一覧ダイアログ、DBマイグレーション、モックと仕様書が含まれます。

Changes

Cohort / File(s) Summary
ストア管理
apps/desktop/src/renderer/stores/browser-automation.ts
ブラウザ自動化用Zustandストア追加(connectModal, selectedSessionId, listViewOpen 等)、型定義、getSnippetForSessionformatRelativeTime を公開。
TRPC ルーター & BindingStore
apps/desktop/src/lib/trpc/routers/browser-automation/index.ts, apps/desktop/src/lib/trpc/routers/index.ts
BindingStore(ローカルDB永続化 + EventEmitter)とTRPCルーター追加:bindings CRUD、liveness、MCP検出、セッション列挙、onBindingsChanged サブスクリプション。アプリルーターへ登録。
DB スキーマ / マイグレーション
packages/local-db/drizzle/0067_add_browser_automation_bindings.sql, packages/local-db/src/schema/browser-automation-bindings.ts, packages/local-db/.../meta/*, packages/local-db/src/schema/index.ts, packages/local-db/src/schema/schema.ts
browser_automation_bindings テーブル追加マイグレーション、スキーマ定義、スナップショット、再エクスポートを追加。
フロントデータフック / 同期
apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts, apps/desktop/src/renderer/hooks/useBrowserBindingsSync.ts
TRPCからセッション/MCP/バインディングを取得して正規化するフックと、onBindingsChanged を購読してキャッシュ無効化する同期フックを追加。
UI — Connect ボタン
apps/desktop/src/renderer/.../ConnectButton/ConnectButton.tsx, .../ConnectButton/index.ts
paneId ベースでバインディングの liveness を表示するアイコンボタン追加。クリックで connect モーダルを開く。状態に応じたツールチップ/ラベル。
UI — セッション接続モーダル
apps/desktop/src/renderer/.../SessionConnectModal/SessionConnectModal.tsx, .../SessionConnectModal/index.ts
セッション一覧・選択、MCP Ready/Setup 分岐、Connect/Disconnect ミューテーション、スニペットコピー、再割当検出、トースト通知を備えた大型モーダルを追加(選択同期・フォールバック選択ロジック含む)。
UI — 自動化リスト表示
apps/desktop/src/renderer/.../BrowserAutomationList/BrowserAutomationList.tsx, .../BrowserAutomationList/index.ts
ワークスペース内 webview ペーン一覧ダイアログを追加。メトリクス、状態バッジ、Focus/Change(Connect)アクション、空状態処理を提供。
既存統合ポイント
apps/desktop/src/renderer/.../BrowserPane/BrowserPane.tsx, apps/desktop/src/renderer/.../ContentView/index.tsx
BrowserPane に ConnectButtonSessionConnectModal を統合。ContentView で useBrowserBindingsSync() を起動し BrowserAutomationList を制御レンダー。
モック・ドキュメント・設定
mock.html, todo.md, biome.jsonc
対話型HTMLモック、TODO仕様ドキュメント追加。biome.jsoncmock.html を除外。
その他(軽微な整形/フォーマット変更)
apps/desktop/src/main/.../todo-agent/*, apps/desktop/src/renderer/.../TodoModal/TodoModal.tsx, apps/desktop/src/renderer/lib/*
ログ/デバッグ呼び出しやインポート順の整形・フォーマット調整など軽微変更。

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant UI as Renderer UI
    participant Store as BrowserAutomation Store
    participant Hook as useBrowserAutomationData
    participant TRPC as Main TRPC Router
    participant DB as BindingStore / Local DB

    UI->>Store: openConnectModal(paneId, preselectSession?)
    Store-->>UI: connectModal state (isOpen, paneId, selectedSessionId)
    UI->>Hook: fetch sessions & bindings (enabled)
    Hook->>TRPC: query todo/terminal sessions, mcp status, listBindings
    TRPC->>DB: read/write bindings, detect MCP, list sessions
    DB-->>TRPC: bindings / sessions / mcpStatus
    TRPC-->>Hook: return sessions, bindingsByPane, mcpStatus
    UI->>TRPC: setBinding(paneId, sessionId) / removeBinding(paneId)
    TRPC->>DB: set/remove binding (emit onBindingsChanged)
    DB-->>TRPC: emit change
    TRPC->>Hook: subscription onBindingsChanged -> invalidate caches
    Hook-->>UI: refreshed data
    UI->>Store: close modal / setSelectedSession / setListViewOpen
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50分

Possibly related PRs

🐰 小さなラビットの祝辞

ペインに紐を結び ぴょんと飛び込む
モーダルで選べば セッションと踊る
接続の光が庭を照らすよ
にんじん抱えて 今日は跳ねる!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed タイトルは、PR の主要な実装内容である browser pane に Connect UI と session バインディング機能を追加したことを明確に反映している。
Description check ✅ Passed PR の説明は実装済み項目・除外事項・テスト計画が詳細に記載されており、テンプレートの主要セクション(Summary、実装内容)を網羅しているが、Type of Change と Screenshots セクションが未記入。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/browser-pane-connect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a22a516763

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
biome.jsonc (1)

15-15: !mock.html はリポジトリ直下のファイルにのみマッチします。

現状 mock.html はリポジトリ直下にあるので意図通り動きますが、将来的にサブディレクトリにもモックHTMLを置く可能性があるなら !**/mock.html の方が安全です。現時点ではこのままでも問題ありません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@biome.jsonc` at line 15, 現在のパターン "!mock.html" はリポジトリ直下の mock.html
にのみマッチするため、将来サブディレクトリにも mock.html を置く可能性がある場合はパターンを "!**/mock.html"
に変更してください;具体的には biome.jsonc のエントリ "!mock.html" を見つけて、リポジトリ内の任意のサブディレクトリにある
mock.html も除外できるように "!**/mock.html" に置き換えます。
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsx (1)

80-85: SessionConnectModal の配置を ContentView 側に引き上げることを検討してください (recommended)。

SessionConnectModal は概念的にシングルトン (connectModal.paneId が単数) ですが、現状 BrowserPane 内にレンダリングしているため、ワークスペースに開いている browser pane の数だけ Radix Dialog ツリーがマウントされます。加えて Line 80-85 で全 BrowserPane インスタンスが connectModal を購読するため、モーダル開閉のたびに全 pane が再レンダリングされます。

同じ PR で BrowserAutomationListContentView 直下に単一マウントしているのと整合させ、SessionConnectModalContentView でマウントして connectModal.paneId から pane context を解決する方が、マウント数・再レンダー範囲の両面で健全です。Phase 1 のモック段階ではブロッカーではないので、Phase 2 以降の実データ配線と合わせて整理する形で問題ありません。

Also applies to: 437-442

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsx`
around lines 80 - 85, Move the SessionConnectModal out of BrowserPane and mount
it once under ContentView: stop rendering SessionConnectModal inside BrowserPane
and instead add a single SessionConnectModal in ContentView (aligned with
BrowserAutomationList being single-mounted). Update BrowserPane to stop
subscribing to connectModal via useBrowserAutomationStore for local render
gating (remove the isConnectOpenForThisPane logic that uses connectModal and
paneId), and resolve the active pane context inside ContentView from
connectModal.paneId when opening the modal so the modal can derive the target
pane. Ensure references to SessionConnectModal, ContentView, BrowserPane,
connectModal, useBrowserAutomationStore, isConnectOpenForThisPane and paneId are
updated accordingly so only one Radix Dialog tree is mounted and panes no longer
re-render on modal state changes.
apps/desktop/src/renderer/stores/browser-automation.ts (1)

5-13: "Literal" | stringstring に畳まれてしまい、リテラル補完が効きません

provider: "Claude" | "Codex" | string および kind: "Terminal" | "Chat" | string は、| string によって union が string に吸収されるため、せっかく列挙したリテラル型が IDE 補完・型ガードのどちらにも寄与しません。Phase 2 以降で provider を文字列比較(例: SessionConnectModal 内の session.provider === "Codex")している箇所の網羅性チェックにも支障が出ます。

既知値を型で担保したい場合は string を落として union のみにし、拡張したい場合は string & {} などの「literal union を保つ」イディオムを使うのが安全です。

♻️ 提案
-	provider: "Claude" | "Codex" | string;
-	kind: "Terminal" | "Chat" | string;
+	provider: "Claude" | "Codex" | (string & {});
+	kind: "Terminal" | "Chat" | (string & {});

将来 provider を厳格に絞る方針なら string & {} も外して純粋な union にしてください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/stores/browser-automation.ts` around lines 5 - 13,
The AutomationSession interface's provider and kind fields use `"Claude" |
"Codex" | string` and `"Terminal" | "Chat" | string`, which collapses the union
to plain string and disables literal completion and exhaustiveness checks;
update the fields in AutomationSession (provider and kind) to preserve literal
unions either by removing the plain string if you want strict enums or by
replacing `string` with the "preserve literals" idiom `string & {}` so code that
does `session.provider === "Codex"` (e.g., SessionConnectModal) gets proper
completion and TS exhaustiveness support.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx (1)

135-153: assignedElsewherePaneName の算出が 1 カードあたり 3 回 Object.entries(bindings).find(...) を走らせていて、可読性・計算量ともに悪いです

セッション件数 × pane 件数 × 3 回の線形走査が毎レンダリング発生します。さらに上部 (Lines 52-59) で assignedPaneIdForSelected として同等の検索を済ませているのに、ここでは再利用せず 3 重に書いており DRY 違反です。sessionId → paneId の逆引きマップを useMemo で一度だけ作れば、ロジックも読みやすく、同じ値を footer / ReadyPanel の分岐でも使い回せます。

♻️ 提案
 	const sessionList = useMemo(() => Object.values(sessions), [sessions]);
 	const session = selectedSessionId ? sessions[selectedSessionId] : null;
 	const currentBinding = paneId ? bindings[paneId] : null;
 	const currentSession = currentBinding ? sessions[currentBinding] : null;
 
+	// sessionId -> paneId (他の pane に割当済み) の逆引き
+	const sessionToOtherPaneId = useMemo(() => {
+		const map: Record<string, string> = {};
+		for (const [pid, sid] of Object.entries(bindings)) {
+			if (pid !== paneId) map[sid] = pid;
+		}
+		return map;
+	}, [bindings, paneId]);
+
 	const assignedPaneIdForSelected = useMemo(() => {
-		if (!selectedSessionId) return null;
-		return (
-			Object.entries(bindings).find(
-				([pid, sid]) => sid === selectedSessionId && pid !== paneId,
-			)?.[0] ?? null
-		);
-	}, [bindings, paneId, selectedSessionId]);
+		if (!selectedSessionId) return null;
+		return sessionToOtherPaneId[selectedSessionId] ?? null;
+	}, [sessionToOtherPaneId, selectedSessionId]);
@@
-							{sessionList.map((s) => (
-								<SessionCard
-									key={s.id}
-									session={s}
-									isSelected={s.id === selectedSessionId}
-									assignedElsewherePaneName={
-										Object.entries(bindings).find(
-											([pid, sid]) => sid === s.id && pid !== paneId,
-										)
-											? (panes[
-													Object.entries(bindings).find(
-														([pid, sid]) => sid === s.id && pid !== paneId,
-													)?.[0] ?? ""
-												]?.name ?? null)
-											: null
-									}
-									onSelect={() => setSelectedSession(s.id)}
-								/>
-							))}
+							{sessionList.map((s) => {
+								const otherPaneId = sessionToOtherPaneId[s.id];
+								return (
+									<SessionCard
+										key={s.id}
+										session={s}
+										isSelected={s.id === selectedSessionId}
+										assignedElsewherePaneName={
+											otherPaneId ? (panes[otherPaneId]?.name ?? null) : null
+										}
+										onSelect={() => setSelectedSession(s.id)}
+									/>
+								);
+							})}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`
around lines 135 - 153, The assignedElsewherePaneName calculation inside the
SessionCard map is inefficient and duplicated: it calls
Object.entries(bindings).find(...) three times per session and redoes work
already performed for assignedPaneIdForSelected; create a memoized reverse map
(e.g., sessionIdToPaneId) using useMemo from bindings once, then for each
SessionCard compute assignedElsewherePaneName by looking up
sessionIdToPaneId[s.id] and, if found and not equal to paneId, retrieve
panes[thatPaneId]?.name or null; update references in the SessionCard props and
reuse this memo for footer / ReadyPanel branching to remove the repeated find
calls and DRY up the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/renderer/stores/browser-automation.ts`:
- Around line 115-121: The markSessionReady updater currently spreads
s.sessions[sessionId] even when it is undefined, creating a broken session
object; update markSessionReady to check whether s.sessions[sessionId] exists
and if not return the state unchanged (early return) so you only call set to
update mcpStatus when the session is present; reference the markSessionReady
function and the s.sessions map to locate and implement this guard.

In `@mock.html`:
- Around line 1798-1909: The Guide click handler sets selectedSessionId but
openModal() immediately overwrites it; update openModal to accept an optional
sessionId parameter (openModal(sessionId?)) and, inside openModal, only set
selectedSessionId from pane.connectedSessionId || pane.suggestedSessionId ||
"session-14" when sessionId is undefined, otherwise use the passed sessionId;
then change the caller in renderAutomationCards (the Guide button click) to call
openModal(button.dataset.sessionId) so the clicked session remains selected.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsx`:
- Around line 80-85: Move the SessionConnectModal out of BrowserPane and mount
it once under ContentView: stop rendering SessionConnectModal inside BrowserPane
and instead add a single SessionConnectModal in ContentView (aligned with
BrowserAutomationList being single-mounted). Update BrowserPane to stop
subscribing to connectModal via useBrowserAutomationStore for local render
gating (remove the isConnectOpenForThisPane logic that uses connectModal and
paneId), and resolve the active pane context inside ContentView from
connectModal.paneId when opening the modal so the modal can derive the target
pane. Ensure references to SessionConnectModal, ContentView, BrowserPane,
connectModal, useBrowserAutomationStore, isConnectOpenForThisPane and paneId are
updated accordingly so only one Radix Dialog tree is mounted and panes no longer
re-render on modal state changes.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`:
- Around line 135-153: The assignedElsewherePaneName calculation inside the
SessionCard map is inefficient and duplicated: it calls
Object.entries(bindings).find(...) three times per session and redoes work
already performed for assignedPaneIdForSelected; create a memoized reverse map
(e.g., sessionIdToPaneId) using useMemo from bindings once, then for each
SessionCard compute assignedElsewherePaneName by looking up
sessionIdToPaneId[s.id] and, if found and not equal to paneId, retrieve
panes[thatPaneId]?.name or null; update references in the SessionCard props and
reuse this memo for footer / ReadyPanel branching to remove the repeated find
calls and DRY up the logic.

In `@apps/desktop/src/renderer/stores/browser-automation.ts`:
- Around line 5-13: The AutomationSession interface's provider and kind fields
use `"Claude" | "Codex" | string` and `"Terminal" | "Chat" | string`, which
collapses the union to plain string and disables literal completion and
exhaustiveness checks; update the fields in AutomationSession (provider and
kind) to preserve literal unions either by removing the plain string if you want
strict enums or by replacing `string` with the "preserve literals" idiom `string
& {}` so code that does `session.provider === "Codex"` (e.g.,
SessionConnectModal) gets proper completion and TS exhaustiveness support.

In `@biome.jsonc`:
- Line 15: 現在のパターン "!mock.html" はリポジトリ直下の mock.html にのみマッチするため、将来サブディレクトリにも
mock.html を置く可能性がある場合はパターンを "!**/mock.html" に変更してください;具体的には biome.jsonc のエントリ
"!mock.html" を見つけて、リポジトリ内の任意のサブディレクトリにある mock.html も除外できるように "!**/mock.html"
に置き換えます。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 459140a5-ef70-4524-ac45-6ed3e7b5e082

📥 Commits

Reviewing files that changed from the base of the PR and between 5fb5a02 and a22a516.

📒 Files selected for processing (12)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/ConnectButton/ConnectButton.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/ConnectButton/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
  • apps/desktop/src/renderer/stores/browser-automation.ts
  • biome.jsonc
  • mock.html
  • todo.md

Comment thread apps/desktop/src/renderer/stores/browser-automation.ts Outdated
Comment thread mock.html Outdated
ダミーデータを削除し、実データを参照するよう配線:

- main-process に browserAutomation tRPC router を追加
  - listBindings / setBinding / removeBinding (paneId -> sessionId 管理を in-memory で保持)
  - getMcpStatus (~/.claude/settings.json と ~/.codex/config.toml を走査し
    superset-browser MCP entry の有無を検出)
  - onBindingsChanged subscription で renderer を即時同期
- renderer の store は UI-only state (connectModal, listView) のみを保持し、
  session / binding / MCP status は useBrowserAutomationData フックから取得
- session 一覧は todoAgent.listAll (動いている Claude Code の TODO-Agent セッション)
  を変換して表示
- SessionConnectModal の Connect / Disconnect / 再割当は trpc mutation 経由で永続化
- 空状態 (セッション無し) と MCP 未設定 (実 configPath を案内) の文言を追加
- lint:fix による TodoModal.tsx 等の format 追従も含む
- workspace フィルタ: tabs.workspaceId で現在の workspace に属する browser pane だけを表示
- Focus ボタン: setFocusedPane の前に setActiveTab で pane が属する tab をアクティブ化し、
  別 tab 上の pane を選んだ時もユーザーが遷移を体感できるようにする

Refs:
- apps/desktop/src/renderer/.../BrowserAutomationList.tsx:31 (codex P2)
- apps/desktop/src/renderer/.../BrowserAutomationList.tsx:108 (codex P2)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

実セッション連携と codex レビュー指摘を反映しました:

  • browserAutomation tRPC router を追加(bindings / MCP detection)
  • ダミーデータを削除し、todoAgent.listAll + ~/.claude/settings.json / ~/.codex/config.toml を参照
  • BrowserAutomationList: workspace フィルタ + Focus で setActiveTab
    もう一度レビューお願いします。

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73b636498a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts Outdated
- useBrowserAutomationData: exclude aborted/escalated sessions too (P1)
- SessionConnectModal: allow disconnect when pane has a binding even if the
  bound session is no longer in the running list (P2)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

前回指摘を反映:

  • P1: useBrowserAutomationData で aborted / escalated 状態も terminal として除外
  • P2: currentBinding がある限り Disconnect できるように修正(session が実行中リストから消えた場合でも stale binding を外せる)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b75711bae2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts Outdated
Comment thread apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts Outdated
- useBrowserAutomationData: only include sessions with live status
  (running / preparing / verifying / waiting). queued / paused / terminal
  are now filtered out so users cannot bind a pane to a dormant worker. (P2)
- onBindingsChanged: invalidate listBindings so other windows see updates
  without waiting for the next poll. (P3)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
apps/desktop/src/renderer/stores/browser-automation.ts (2)

82-95: formatRelativeTime で未来時刻(clock skew など)に負の値が出ます。

ホスト/todo-daemon 側の時計がずれたり、直前に書き込まれたタイムスタンプが Date.now() を僅かに上回ると "-1s ago" のような表記になります。Math.max(0, ...) でクランプするか、< 0"just now" に丸めるのが無難です。

♻️ 提案
-	const diffSec = Math.round((Date.now() - ts) / 1000);
-	if (diffSec < 5) return "just now";
+	const diffSec = Math.max(0, Math.round((Date.now() - ts) / 1000));
+	if (diffSec < 5) return "just now";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/stores/browser-automation.ts` around lines 82 - 95,
The function formatRelativeTime can return negative relative strings when ts is
slightly in the future; update formatRelativeTime to clamp negative differences
(e.g., compute diffSec = Math.max(0, Math.round((Date.now() - ts) / 1000))) or
treat any diffSec < 0 as 0/“just now” so you never produce "-1s ago"; modify the
logic in formatRelativeTime accordingly and keep the existing branch ordering
(just now, seconds, minutes, hours, days).

5-13: provider/kindstring との union にするとリテラル型の恩恵が消えます。

"Claude" | "Codex" | string は TypeScript 上では実質 string に縮退し、switch/絞り込みで網羅チェックが効きません。Phase 1 段階でも確定しているリテラル("Claude" | "Codex", "Terminal" | "Chat")に留め、未知値が必要になったら provider: Provider | { custom: string } のように明示的に分岐させるほうが将来安全です。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/stores/browser-automation.ts` around lines 5 - 13,
The AutomationSession interface currently defines provider and kind as `"Claude"
| "Codex" | string` and `"Terminal" | "Chat" | string`, which collapses to plain
string and loses literal type benefits; update AutomationSession so provider is
a strict union of known literals (e.g., "Claude" | "Codex") and kind is a strict
union of known literals (e.g., "Terminal" | "Chat"), and if you need to
represent unknown/third‑party values add an explicit discriminated variant (for
example Provider = "Claude" | "Codex" | { custom: string } and likewise for
kind) so switch/case and type narrowing on provider and kind remain exhaustive
and type-safe while still supporting extensibility.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx (1)

42-47: Needs setup メトリクスのスコープについてご確認ください。

connectedCount は当該 workspace の browser pane に絞って数えていますが、needsSetupCount は全 sessions(他 workspace で稼働中のものも含む)の MCP 未整備数を数えています。本モーダルは「この workspace の browser panes と結合状況」を提示する UI なので、メトリクスも workspace スコープに揃えたほうが一貫性があります(例: この workspace 内の pane に現在バインドされている/バインド候補のセッションに限定)。意図的にグローバル集計しているならコメントを一行添えておくと後続レビューで迷いません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx`
around lines 42 - 47, The needsSetupCount currently counts MCP-missing across
all sessions but should be scoped to this workspace like connectedCount; update
the calculation for needsSetupCount to only consider sessions related to this
workspace's browserPanes (e.g., sessions whose pane or candidate matches an id
in browserPanes) and still check s.mcpStatus === "missing" using the existing
symbols (browserPanes, sessions, bindingsByPane, needsSetupCount,
connectedCount); if the global aggregation was intended instead, add a one-line
comment above the needsSetupCount declaration explaining that it's intentionally
global.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx (2)

111-123: Disconnect 後にモーダルを閉じない挙動は意図通りかご確認ください。

handleConnect は成功時に onOpenChange(false) でモーダルを閉じますが、handleDisconnect は閉じません。Disconnect 後にそのまま別セッションを選び直せるよう意図的に開いたままにしているなら問題ありませんが、単発で切断だけしたいユーザーは余計にキャンセル操作が必要になります。意図を明確にする意味でも、例えば currentBinding が切れたら footer 文言を "Disconnected — pick another session or close" に変える/あるいは閉じる、どちらかのハンドリングを検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`
around lines 111 - 123, handleDisconnect currently disconnects but does not
close the modal, while handleConnect calls onOpenChange(false); decide and
implement consistent behavior: either close the modal on successful disconnect
by calling onOpenChange(false) inside handleDisconnect after
removeBinding.mutateAsync succeeds, or update the modal footer when
currentBinding becomes null (e.g., set a local footer state or derive footer
text from currentBinding and render "Disconnected — pick another session or
close"); update the Footer rendering logic to read that state/derived text so UI
reflects the new disconnected state. Ensure to reference and use onOpenChange,
handleDisconnect, handleConnect, and currentBinding when making the change.

284-480: 同一ファイル内に多数のサブコンポーネント(SessionCard/Tag/ReadyPanel/DetailItem/SetupPanel)が定義されており、肥大化しています。

本ファイルは 480 行超で 5 つのローカルコンポーネントを抱えています。コーディングガイドラインの「1 コンポーネント 1 フォルダ」原則は外部公開コンポーネント向けの規約ですが、再利用性・可読性の観点でもこの規模だと SessionConnectModal/ フォルダ配下に SessionCard.tsx / SetupPanel.tsx / ReadyPanel.tsx を分割しておくのが将来のメンテ上有利です(Phase 2 で Codex 固有の SetupPanel 分岐や、ReadyPanel のリアルタイム情報追加が予定されているなら特に)。

As per coding guidelines: Use one folder per component with structure ComponentName/ComponentName.tsx plus index.ts for barrel export.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`
around lines 284 - 480, Split the large SessionConnectModal file by extracting
the local subcomponents SessionCard, Tag, ReadyPanel, DetailItem, and SetupPanel
into their own component folders (e.g. SessionCard/SessionCard.tsx,
SetupPanel/SetupPanel.tsx, ReadyPanel/ReadyPanel.tsx) with a barrel index.ts
export for each, move any related helper usage (e.g. getSnippetForSession) or
prop types they need into shared exports or pass them as props, update the main
SessionConnectModal to import these components instead of defining them inline,
and ensure all props and callbacks (like onSelect, onCopy, mcpConfigPath,
session, paneName, reassigning, previousPaneName) are preserved and typed the
same so behavior remains identical.
apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts (2)

48-75: provider を常に "Claude" に固定している点は Phase 1 の割り切りとして問題ありませんが、mcpStatus のマッピングにご注意を。

todoSessions は現状すべて Claude Code ワーカーというコメント前提で provider = "Claude" / mcpStatus.claudeReady のみを見ていますが、SessionConnectModal では session.provider === "Codex" 分岐で codexConfigPath を参照する UI があり(スニペット/セットアップガイド)、Codex ワーカーが本フックから生えてくるまで到達不能コードになっています。Phase 2 以降で Codex 対応を入れる際は、ここで provider を判定し codexReady を参照するように合わせて更新する必要があります。現段階では // TODO(phase2): codex worker -> provider="Codex" & codexReady といったマーカーを残しておくと後で迷子になりにくいです。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts` around lines 48
- 75, The mapping for todoSessions assumes every session is Claude by hardcoding
provider = "Claude" and only reading mcpStatus.claudeReady; add a TODO marker
and update instructions so future Phase 2 Codex support is obvious: leave the
provider assignment but add a comment like "// TODO(phase2): detect Codex
workers -> provider = 'Codex' and use mcpStatus.codexReady" near the
provider/mcpStatus logic (referencing provider, mcpStatus, todoSessions), and
ensure SessionConnectModal/codexConfigPath consumers will be able to rely on
provider === "Codex" and mcpStatus.codexReady once implemented.

27-36: onBindingsChanged サブスクリプションと refetchInterval: 1000 のポーリングが冗長です。

サブスクリプション経由で listBindings を invalidate しているため、追加で 1 秒間隔のポーリングを掛ける必要はありません。ポーリングを外すか、サブスクリプション未接続時のフォールバックとしてもう少し長い間隔(例: 30 秒)に緩めるのが望ましいです。

さらに useBrowserAutomationDataConnectButton(pane ごと)、BrowserAutomationListSessionConnectModal で個別に呼ばれるため、pane 数が増えると同じクエリに対して複数本のサブスクリプションがサーバ側に張られます。bindingStore の EventEmitter は setMaxListeners() の設定がないため、既定の 10 リスナー警告を超える可能性があります。(React Query はクエリを重複排除しますが、subscriptions は共有されません)

提案:ポーリング間隔の緩和
 	const { data: bindings = [] } =
 		electronTrpc.browserAutomation.listBindings.useQuery(undefined, {
-			refetchInterval: 1000,
+			// Subscription invalidates on change; keep a long fallback only for
+			// resilience when the subscription is (re)connecting.
+			refetchInterval: 30_000,
 		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts` around lines 27
- 36, The current useBrowserAutomationData hook both polls via
electronTrpc.browserAutomation.listBindings.useQuery (refetchInterval: 1000) and
opens a subscription with
electronTrpc.browserAutomation.onBindingsChanged.useSubscription, which is
redundant and causes multiple server subscriptions when used from ConnectButton,
BrowserAutomationList and SessionConnectModal; fix by removing the aggressive 1s
polling (delete refetchInterval) or relax it to a long fallback (e.g. 30000)
and, more importantly, centralize the onBindingsChanged subscription so only one
subscription is created app-wide (move the
electronTrpc.browserAutomation.onBindingsChanged.useSubscription call out of
useBrowserAutomationData into a single top-level component/provider) to avoid
creating many server subscriptions and hitting bindingStore EventEmitter
listener limits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/lib/trpc/routers/browser-automation/index.ts`:
- Around line 27-79: The EventEmitter in BindingStore can hit the 10-listener
default limit; inside the BindingStore class (where emitter is declared) call
this.emitter.setMaxListeners(0) (or setMaxListeners(n) with a sensible higher
limit) right after creating the emitter to avoid MaxListenersExceededWarning;
update both places where an EventEmitter is created for browser automation (the
emitter field in class BindingStore and the other emitter instance referenced
around the other occurrence) so each uses setMaxListeners(...) and keep
onChange/remove/emitChange logic unchanged.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`:
- Around line 51-72: The selectedSessionId can remain stale when its session is
removed from sessions; update the useEffect that runs on modal open to also
detect when selectedSessionId is truthy but no longer present (i.e., session ===
null) and in that case call setSelectedSession with the computed fallback
(currentBinding ?? sessions[0]?.id ?? null); reference selectedSessionId,
sessions, session, currentBinding, paneId, useEffect and setSelectedSession so
the change is done inside the existing effect branch that guards for open and
paneId and uses fallback selection.

---

Nitpick comments:
In `@apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts`:
- Around line 48-75: The mapping for todoSessions assumes every session is
Claude by hardcoding provider = "Claude" and only reading mcpStatus.claudeReady;
add a TODO marker and update instructions so future Phase 2 Codex support is
obvious: leave the provider assignment but add a comment like "// TODO(phase2):
detect Codex workers -> provider = 'Codex' and use mcpStatus.codexReady" near
the provider/mcpStatus logic (referencing provider, mcpStatus, todoSessions),
and ensure SessionConnectModal/codexConfigPath consumers will be able to rely on
provider === "Codex" and mcpStatus.codexReady once implemented.
- Around line 27-36: The current useBrowserAutomationData hook both polls via
electronTrpc.browserAutomation.listBindings.useQuery (refetchInterval: 1000) and
opens a subscription with
electronTrpc.browserAutomation.onBindingsChanged.useSubscription, which is
redundant and causes multiple server subscriptions when used from ConnectButton,
BrowserAutomationList and SessionConnectModal; fix by removing the aggressive 1s
polling (delete refetchInterval) or relax it to a long fallback (e.g. 30000)
and, more importantly, centralize the onBindingsChanged subscription so only one
subscription is created app-wide (move the
electronTrpc.browserAutomation.onBindingsChanged.useSubscription call out of
useBrowserAutomationData into a single top-level component/provider) to avoid
creating many server subscriptions and hitting bindingStore EventEmitter
listener limits.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx`:
- Around line 42-47: The needsSetupCount currently counts MCP-missing across all
sessions but should be scoped to this workspace like connectedCount; update the
calculation for needsSetupCount to only consider sessions related to this
workspace's browserPanes (e.g., sessions whose pane or candidate matches an id
in browserPanes) and still check s.mcpStatus === "missing" using the existing
symbols (browserPanes, sessions, bindingsByPane, needsSetupCount,
connectedCount); if the global aggregation was intended instead, add a one-line
comment above the needsSetupCount declaration explaining that it's intentionally
global.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`:
- Around line 111-123: handleDisconnect currently disconnects but does not close
the modal, while handleConnect calls onOpenChange(false); decide and implement
consistent behavior: either close the modal on successful disconnect by calling
onOpenChange(false) inside handleDisconnect after removeBinding.mutateAsync
succeeds, or update the modal footer when currentBinding becomes null (e.g., set
a local footer state or derive footer text from currentBinding and render
"Disconnected — pick another session or close"); update the Footer rendering
logic to read that state/derived text so UI reflects the new disconnected state.
Ensure to reference and use onOpenChange, handleDisconnect, handleConnect, and
currentBinding when making the change.
- Around line 284-480: Split the large SessionConnectModal file by extracting
the local subcomponents SessionCard, Tag, ReadyPanel, DetailItem, and SetupPanel
into their own component folders (e.g. SessionCard/SessionCard.tsx,
SetupPanel/SetupPanel.tsx, ReadyPanel/ReadyPanel.tsx) with a barrel index.ts
export for each, move any related helper usage (e.g. getSnippetForSession) or
prop types they need into shared exports or pass them as props, update the main
SessionConnectModal to import these components instead of defining them inline,
and ensure all props and callbacks (like onSelect, onCopy, mcpConfigPath,
session, paneName, reassigning, previousPaneName) are preserved and typed the
same so behavior remains identical.

In `@apps/desktop/src/renderer/stores/browser-automation.ts`:
- Around line 82-95: The function formatRelativeTime can return negative
relative strings when ts is slightly in the future; update formatRelativeTime to
clamp negative differences (e.g., compute diffSec = Math.max(0,
Math.round((Date.now() - ts) / 1000))) or treat any diffSec < 0 as 0/“just now”
so you never produce "-1s ago"; modify the logic in formatRelativeTime
accordingly and keep the existing branch ordering (just now, seconds, minutes,
hours, days).
- Around line 5-13: The AutomationSession interface currently defines provider
and kind as `"Claude" | "Codex" | string` and `"Terminal" | "Chat" | string`,
which collapses to plain string and loses literal type benefits; update
AutomationSession so provider is a strict union of known literals (e.g.,
"Claude" | "Codex") and kind is a strict union of known literals (e.g.,
"Terminal" | "Chat"), and if you need to represent unknown/third‑party values
add an explicit discriminated variant (for example Provider = "Claude" | "Codex"
| { custom: string } and likewise for kind) so switch/case and type narrowing on
provider and kind remain exhaustive and type-safe while still supporting
extensibility.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c223f114-91cb-4ea6-89e7-0039a9d8ff2e

📥 Commits

Reviewing files that changed from the base of the PR and between a22a516 and 7376ba5.

📒 Files selected for processing (14)
  • apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
  • apps/desktop/src/lib/trpc/routers/index.ts
  • apps/desktop/src/main/lib/todo-daemon/client.ts
  • apps/desktop/src/main/todo-agent/daemon-bridge.ts
  • apps/desktop/src/main/todo-agent/debug.ts
  • apps/desktop/src/main/todo-agent/runtime-config.ts
  • apps/desktop/src/main/todo-agent/trpc-router.ts
  • apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx
  • apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/ConnectButton/ConnectButton.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
  • apps/desktop/src/renderer/stores/browser-automation.ts
✅ Files skipped from review due to trivial changes (6)
  • apps/desktop/src/main/todo-agent/runtime-config.ts
  • apps/desktop/src/main/todo-agent/trpc-router.ts
  • apps/desktop/src/main/lib/todo-daemon/client.ts
  • apps/desktop/src/main/todo-agent/daemon-bridge.ts
  • apps/desktop/src/main/todo-agent/debug.ts
  • apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

  • P2: live status (running/preparing/verifying/waiting) のみを connect 対象に
  • P3: onBindingsChanged で listBindings を invalidate

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

- BindingStore: call setMaxListeners(0) so many browser panes don't trip
  Node's default 10-listener warning. (coderabbit Minor)
- SessionConnectModal: clear stale selectedSessionId when the session drops
  out of the live list so the right-column detail view is never empty with
  a truthy selection. (coderabbit Minor)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

  • setMaxListeners(0) を BindingStore に追加
  • selectedSessionId が live session から外れた場合に自動でフォールバック

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a2605a118

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts Outdated
- useBrowserAutomationData takes an `enabled` flag. Sessions and MCP
  status only poll while the Connect dialog / list view is open; the
  binding query relies on the push subscription. Poll intervals relaxed
  to 15s / 30s from 5s / 10s. (codex P2)
- SessionConnectModal: fallback selection only keeps currentBinding when
  it still points at a live session, otherwise picks sessions[0]. Stops
  the modal from opening with a truthy-but-dead selection. (codex P3)
- ConnectButton now only consumes the binding data and renders a static
  "Connected" label instead of the full session name + provider.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/lib/trpc/routers/browser-automation/index.ts`:
- Around line 95-99: detectSupersetBrowserMcp currently uses a simple substring
check (contents.includes("superset-browser")) which yields false positives from
comments or inactive settings; update detectSupersetBrowserMcp(filePath: string)
to parse the file and verify an active configuration entry instead: if the file
is JSON, JSON.parse the contents and return Boolean(parsed.mcpServers &&
parsed.mcpServers["superset-browser"]); if the file is TOML (or non-JSON),
detect an uncommented section/header or key for superset-browser (e.g., a TOML
table like [mcpServers."superset-browser"] or an uncommented key) using a regex
that ignores lines starting with # or // and only matches active lines; keep
readFileSync(filePath, "utf8") to load contents, but replace the naive includes
check with these structured validations inside detectSupersetBrowserMcp.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`:
- Around line 184-188: The empty-state message in SessionConnectModal is
misleading because useBrowserAutomationData converts only todoAgent.listAll
(Claude terminal) sessions into sessions; update the displayed copy in the
sessions.length === 0 branch to instruct users to start a TODO-Agent (Claude)
session only (remove the "open a chat pane" suggestion) so the message reflects
the actual connectable session sources (change the string used in the <div>
inside SessionConnectModal where sessions is checked).
- Around line 329-338: The button in SessionConnectModal (in the
SessionConnectModal component where the <button> uses onSelect and the
isSelected variable to style selection) only signals selection visually; add an
accessibility state by adding aria-pressed set to the boolean isSelected on that
button (i.e., aria-pressed={isSelected}) so screen readers can announce which
session is selected while keeping the existing onSelect handler and className
logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9dbf6785-08c7-41b5-8ba9-f72c39441601

📥 Commits

Reviewing files that changed from the base of the PR and between 7376ba5 and 88547a9.

📒 Files selected for processing (5)
  • apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
  • apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/ConnectButton/ConnectButton.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx
  • apps/desktop/src/renderer/stores/browser-automation.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/ConnectButton/ConnectButton.tsx

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts Outdated
…e/codex

- packages/local-db: add browser_automation_bindings table + migration
- browserAutomation router: use localDb for bindings so they survive restart
  (terminal daemon re-attaches panes, binding must survive too)
- Add listTerminalAgentSessions that walks each live PTY's process tree and
  returns the ones running claude or codex. Hook merges them into the session
  list keyed by `terminal:<paneId>` so bindings self-heal across shell
  re-spawns in the same pane.
- setBinding takes sessionKind ("todo-agent" | "terminal") for future
  dispatch logic on the consumer side.

Addresses:
- Persist across restarts (user report)
- Bind to claude/codex started in an ordinary terminal tab (user report)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b85c59e173

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

SessionConnectModal and BrowserAutomationList are mounted at parent level
so they were polling sessions / MCP status / terminal process trees even
while closed. Pass { enabled: open } so background polling only runs while
the dialog is actually visible. (codex P1 / P2)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0765ed9887

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts Outdated
…sync

- Add browserAutomation.listBindingLiveness: single main-process query that
  joins bindings with live status (todo-agent whitelist + terminal process
  tree). ConnectButton uses it instead of the heavy session list, and the
  badge now shows "Session ended" with an amber state when the bound
  worker has exited. (codex P2)
- Move the onBindingsChanged subscription into a dedicated
  `useBrowserBindingsSync` hook mounted once in ContentView, removing
  the per-pane fan-out. (codex P2)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
apps/desktop/src/lib/trpc/routers/browser-automation/index.ts (1)

143-147: ⚠️ Potential issue | 🟡 Minor

MCP 設定は文字列検索ではなく有効な entry として判定してください。

コメントアウト済み TOML や JSON 内のメモ文字列だけでも ready 扱いになります。Claude は JSON の mcpServers["superset-browser"]、Codex は未コメントの TOML section/key を見る形に寄せてください。

🛡️ 修正案
-function detectSupersetBrowserMcp(filePath: string): boolean {
+function readConfig(filePath: string): string | null {
 	try {
-		const contents = readFileSync(filePath, "utf8");
-		return contents.includes("superset-browser");
+		return readFileSync(filePath, "utf8");
 	} catch {
-		return false;
+		return null;
 	}
 }
+
+function detectClaudeSupersetBrowserMcp(filePath: string): boolean {
+	const contents = readConfig(filePath);
+	if (!contents) return false;
+	try {
+		const parsed = JSON.parse(contents) as {
+			mcpServers?: Record<string, unknown>;
+		};
+		return Boolean(parsed.mcpServers?.["superset-browser"]);
+	} catch {
+		return false;
+	}
+}
+
+function detectCodexSupersetBrowserMcp(filePath: string): boolean {
+	const contents = readConfig(filePath);
+	if (!contents) return false;
+	return /^\s*\[(?:mcpServers|mcp_servers)\.(?:"superset-browser"|superset-browser)\]\s*$/m.test(
+		contents,
+	);
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/lib/trpc/routers/browser-automation/index.ts` around lines
143 - 147, detectSupersetBrowserMcp currently uses a raw substring search which
falsely matches commented text; change it to parse the file and detect a real
MCP entry. In detectSupersetBrowserMcp, read the file, detect whether it’s JSON
or TOML, then: for JSON parse and check for a real
mcpServers["superset-browser"] key; for TOML parse and check for an un-commented
section/key corresponding to superset-browser (e.g. mcpServers.superset-browser
or equivalent), ensuring commented lines are ignored; return true only when the
parsed structure contains a valid entry. Use the function name
detectSupersetBrowserMcp to locate the code and add robust error handling for
parse failures.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx (2)

334-342: ⚠️ Potential issue | 🟡 Minor

選択状態を aria-pressed でも伝えてください。

選択状態が現在は色だけなので、支援技術ではどの session が選択中か分かりにくいです。過去コメントと同じ指摘です。

修正案
 		<button
 			type="button"
+			aria-pressed={isSelected}
 			onClick={onSelect}
 			className={cn(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`
around lines 334 - 342, The button that renders the session selector currently
conveys selection only via color; update the element in SessionConnectModal (the
button with onClick and className using isSelected) to include an accessible
state by adding aria-pressed={isSelected} (or
aria-pressed={Boolean(isSelected)}) so assistive tech can detect which session
is selected; ensure the prop uses the same isSelected variable used for
className so the visual and ARIA states remain synchronized.

64-77: ⚠️ Potential issue | 🟠 Major

setSelectedSession の無操作更新を防いでください。

Line 77 では fallback が現在の selectedSessionId と同じ値であっても Zustand state を更新しています。sessions 依存が再評価されると、モーダル表示中に不要な再レンダリングループが発生します。

修正案
 		const fallback =
 			(bindingIsLive ? currentBinding : null) ?? sessions[0]?.id ?? null;
-		setSelectedSession(fallback);
+		if (fallback !== selectedSessionId) {
+			setSelectedSession(fallback);
+		}

加えて、SessionCard コンポーネント(line 334)の <button>aria-pressed={isSelected} 属性を追加し、アクセシビリティを改善してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`
around lines 64 - 77, The effect updates Zustand state even when fallback equals
the current selectedSessionId causing unnecessary re-renders; in the useEffect
that computes fallback (references: selectedSessionId, sessions, currentBinding,
setSelectedSession) add an identity check and only call
setSelectedSession(fallback) if fallback !== selectedSessionId (taking care of
null/undefined equality), so no-op updates are avoided. Also update the
SessionCard button (reference: SessionCard component) to include
aria-pressed={isSelected} to improve accessibility.
🧹 Nitpick comments (6)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx (1)

143-152: Metric props は interface に切り出してください。

object shape の型定義は project 方針に合わせて interface に寄せると一貫します。

♻️ 修正案
-function Metric({ label, value }: { label: string; value: number }) {
+interface MetricProps {
+	label: string;
+	value: number;
+}
+
+function Metric({ label, value }: MetricProps) {

As per coding guidelines, Prefer interface for defining object shapes in TypeScript.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx`
around lines 143 - 152, The Metric component currently types its props inline;
extract the props into a named interface (e.g., interface MetricProps { label:
string; value: number }) and update the Metric signature to use MetricProps
(function Metric({ label, value }: MetricProps) { ... }) to comply with the
project guideline of preferring interface for object shapes; keep the interface
local to the file (or export it if used elsewhere) and ensure any references or
tests use the new MetricProps type.
apps/desktop/src/renderer/hooks/useBrowserBindingsSync.ts (1)

11-15: invalidate() の Promise を await してください。

キャッシュ無効化の失敗が未処理になります。onData 内でまとめて await しておくと、Promise handling の方針にも沿います。

♻️ 修正案
 	electronTrpc.browserAutomation.onBindingsChanged.useSubscription(undefined, {
-		onData: () => {
-			utils.browserAutomation.listBindings.invalidate();
-			utils.browserAutomation.listBindingLiveness.invalidate();
+		onData: async () => {
+			await Promise.all([
+				utils.browserAutomation.listBindings.invalidate(),
+				utils.browserAutomation.listBindingLiveness.invalidate(),
+			]);
 		},
 	});

As per coding guidelines, Use async/await for all promise handling in JavaScript/TypeScript code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useBrowserBindingsSync.ts` around lines 11 -
15, The onData handler passed to
electronTrpc.browserAutomation.onBindingsChanged.useSubscription should await
the invalidate() calls so their revalidation failures are handled; make the
onData callback async and await
Promise.all([utils.browserAutomation.listBindings.invalidate(),
utils.browserAutomation.listBindingLiveness.invalidate()]) (or await each)
inside that async function to ensure both invalidations are awaited and errors
surface; update the onData declaration in useBrowserBindingsSync.ts accordingly.
packages/local-db/src/schema/browser-automation-bindings.ts (1)

20-23: sessionKind も TypeScript 側で union に寄せてください。

現状は select 結果上 string になり、router 側の "terminal" / "todo-agent" 分岐と型が噛み合いません。

♻️ 修正案
+export type BrowserAutomationSessionKind = "todo-agent" | "terminal";
+
 export const browserAutomationBindings = sqliteTable(
 	"browser_automation_bindings",
 	{
 		paneId: text("pane_id").primaryKey(),
 		sessionId: text("session_id").notNull(),
-		sessionKind: text("session_kind").notNull().default("todo-agent"),
+		sessionKind: text("session_kind")
+			.notNull()
+			.$type<BrowserAutomationSessionKind>()
+			.default("todo-agent"),
 		connectedAt: integer("connected_at").notNull(),
 	},
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/local-db/src/schema/browser-automation-bindings.ts` around lines 20
- 23, The sessionKind column is currently declared as text(...) so select
results are typed as string; create a proper enum/union so TypeScript infers
"terminal" | "todo-agent". Define a TS union type (e.g. type SessionKind =
"terminal" | "todo-agent") and replace the column declaration sessionKind:
text("session_kind").notNull().default("todo-agent") with the schema enum helper
your ORM provides (e.g. sqliteEnum/textEnum/sqlEnum(...) or enums(...)) listing
["terminal","todo-agent"] so the resulting column and selects are typed as
SessionKind and match the router branches.
apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts (2)

25-29: Hook options は named interface にしてください。

この options object は公開 hook の引数なので、inline type より UseBrowserAutomationDataOptions のような interface に切り出す方が規約に沿います。

修正案
+interface UseBrowserAutomationDataOptions {
+	enabled?: boolean;
+}
+
 export function useBrowserAutomationData({
 	enabled = true,
-}: {
-	enabled?: boolean;
-} = {}) {
+}: UseBrowserAutomationDataOptions = {}) {

As per coding guidelines, **/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts` around lines 25
- 29, The hook useBrowserAutomationData should accept a named interface rather
than an inline type for its options; create an interface named
UseBrowserAutomationDataOptions describing { enabled?: boolean } and replace the
inline parameter type in the useBrowserAutomationData signature with that
interface (and update the default param destructuring to use the interface as
the function parameter type). Ensure any exports or JSDoc reflect the new
UseBrowserAutomationDataOptions name so callers and tests import/consume the
typed options consistently.

32-60: 空配列のデフォルト値を安定化してください。

data: todoSessions = [] のような destructuring default は、未取得時に render ごとに新しい配列を作ります。useMemo の依存が毎回変わり、sessions / bindingsByPane を見る downstream effect が不要に再実行されます。

修正案
 import { useTabsStore } from "renderer/stores/tabs/store";
 
+const EMPTY_TODO_SESSIONS = [];
+const EMPTY_TERMINAL_AGENTS = [];
+const EMPTY_BINDINGS = [];
+
 /**
  * Aggregates browser-automation session and binding data from the
@@
-	const { data: todoSessions = [], refetch: refetchSessions } =
+	const { data: todoSessions = EMPTY_TODO_SESSIONS, refetch: refetchSessions } =
 		electronTrpc.todoAgent.listAll.useQuery(undefined, {
@@
-	const { data: terminalAgents = [] } =
+	const { data: terminalAgents = EMPTY_TERMINAL_AGENTS } =
 		electronTrpc.browserAutomation.listTerminalAgentSessions.useQuery(
@@
-	const { data: bindings = [] } =
+	const { data: bindings = EMPTY_BINDINGS } =
 		electronTrpc.browserAutomation.listBindings.useQuery(undefined, {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts` around lines 32
- 60, Destructuring defaults like "data: todoSessions = []", "data:
terminalAgents = []", and "data: bindings = []" create new arrays on each render
causing downstream useMemo/useEffect deps to change; instead remove those inline
defaults and coalesce the query results against stable frozen/memoized empty
arrays (e.g., create a single frozen EMPTY_ARRAY or use useMemo(() => [], [])
and use that for fallback) when assigning todoSessions, terminalAgents, bindings
(and similarly handle mcpStatus if needed), so the variables used by sessions /
bindingsByPane remain referentially stable across renders.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx (1)

300-310: Props の object shape は interface に切り出してください。

SessionCard / Tag / ReadyPanel / DetailItem / SetupPanel の props が inline object type になっています。再利用・差分追跡しやすいように named interface に寄せるのがこのリポジトリの規約に合います。

修正例
+interface SessionCardProps {
+	session: AutomationSession;
+	isSelected: boolean;
+	assignedElsewherePaneName: string | null;
+	onSelect: () => void;
+}
+
 function SessionCard({
 	session,
 	isSelected,
 	assignedElsewherePaneName,
 	onSelect,
-}: {
-	session: AutomationSession;
-	isSelected: boolean;
-	assignedElsewherePaneName: string | null;
-	onSelect: () => void;
-}) {
+}: SessionCardProps) {

As per coding guidelines, **/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript.

Also applies to: 374-447

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`
around lines 300 - 310, Several components (SessionCard, Tag, ReadyPanel,
DetailItem, SetupPanel) use inline object-type props; extract each inline type
into a named interface (e.g., interface SessionCardProps { ... }) and replace
the inline annotations with the interface names in the function/component
signatures; ensure exported or locally-scoped interfaces match existing property
names and types (AutomationSession, boolean, string | null, callbacks) and
update all occurrences within the file (including the other ranges mentioned) to
use the new interfaces to follow the repository convention of using interfaces
for object shapes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/lib/trpc/routers/browser-automation/index.ts`:
- Around line 174-198: The Promise.all over sessions can reject if
getProcessTree or getProcessName throws for a stale PID; wrap each session's
work in its own try/catch (inside the sessions.map async callback) or use
Promise.allSettled so one failing session doesn't reject the whole batch; catch
errors around getProcessTree/getProcessName and skip that session while
optionally logging the error; specifically modify the async callback that calls
getProcessTree(s.pid) and the inner Promise.all of getProcessName calls (and the
names.find logic) to handle/recover from per-session failures so
listTerminalAgentSessions returns other successful sessions.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx`:
- Around line 44-49: connectedCount currently counts any pane with an existing
binding (bindingsByPane[p.id]) which includes stale bindings; change the logic
to only count a pane as connected when it has both a binding and a corresponding
active/live session (use sessions.some(...) to find a session linked to the pane
id and that is not in a terminal/missing state). Update the connectedCount
computation (and the analogous logic between lines 73-110) to require
bindingsByPane[p.id] && sessions.some(s => s.paneId === p.id && /* check not
terminal: e.g. s.mcpStatus !== 'missing' or s.status !== 'ended' */) so
stale/ended sessions are excluded from the connected metric.

In `@packages/local-db/drizzle/0067_add_browser_automation_bindings.sql`:
- Around line 1-6: The session_kind column in browser_automation_bindings allows
arbitrary values; add a DB-level constraint to enforce the allowed domain (e.g.,
restrict session_kind to the expected enum values such as 'todo-agent' and
'terminal') by altering the CREATE TABLE to include a CHECK(session_kind IN
(...)) (or an equivalent ENUM type) and update the migration and regenerate the
snapshot so the constraint is persisted; target the browser_automation_bindings
table and the session_kind column when making these changes.

---

Duplicate comments:
In `@apps/desktop/src/lib/trpc/routers/browser-automation/index.ts`:
- Around line 143-147: detectSupersetBrowserMcp currently uses a raw substring
search which falsely matches commented text; change it to parse the file and
detect a real MCP entry. In detectSupersetBrowserMcp, read the file, detect
whether it’s JSON or TOML, then: for JSON parse and check for a real
mcpServers["superset-browser"] key; for TOML parse and check for an un-commented
section/key corresponding to superset-browser (e.g. mcpServers.superset-browser
or equivalent), ensuring commented lines are ignored; return true only when the
parsed structure contains a valid entry. Use the function name
detectSupersetBrowserMcp to locate the code and add robust error handling for
parse failures.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`:
- Around line 334-342: The button that renders the session selector currently
conveys selection only via color; update the element in SessionConnectModal (the
button with onClick and className using isSelected) to include an accessible
state by adding aria-pressed={isSelected} (or
aria-pressed={Boolean(isSelected)}) so assistive tech can detect which session
is selected; ensure the prop uses the same isSelected variable used for
className so the visual and ARIA states remain synchronized.
- Around line 64-77: The effect updates Zustand state even when fallback equals
the current selectedSessionId causing unnecessary re-renders; in the useEffect
that computes fallback (references: selectedSessionId, sessions, currentBinding,
setSelectedSession) add an identity check and only call
setSelectedSession(fallback) if fallback !== selectedSessionId (taking care of
null/undefined equality), so no-op updates are avoided. Also update the
SessionCard button (reference: SessionCard component) to include
aria-pressed={isSelected} to improve accessibility.

---

Nitpick comments:
In `@apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts`:
- Around line 25-29: The hook useBrowserAutomationData should accept a named
interface rather than an inline type for its options; create an interface named
UseBrowserAutomationDataOptions describing { enabled?: boolean } and replace the
inline parameter type in the useBrowserAutomationData signature with that
interface (and update the default param destructuring to use the interface as
the function parameter type). Ensure any exports or JSDoc reflect the new
UseBrowserAutomationDataOptions name so callers and tests import/consume the
typed options consistently.
- Around line 32-60: Destructuring defaults like "data: todoSessions = []",
"data: terminalAgents = []", and "data: bindings = []" create new arrays on each
render causing downstream useMemo/useEffect deps to change; instead remove those
inline defaults and coalesce the query results against stable frozen/memoized
empty arrays (e.g., create a single frozen EMPTY_ARRAY or use useMemo(() => [],
[]) and use that for fallback) when assigning todoSessions, terminalAgents,
bindings (and similarly handle mcpStatus if needed), so the variables used by
sessions / bindingsByPane remain referentially stable across renders.

In `@apps/desktop/src/renderer/hooks/useBrowserBindingsSync.ts`:
- Around line 11-15: The onData handler passed to
electronTrpc.browserAutomation.onBindingsChanged.useSubscription should await
the invalidate() calls so their revalidation failures are handled; make the
onData callback async and await
Promise.all([utils.browserAutomation.listBindings.invalidate(),
utils.browserAutomation.listBindingLiveness.invalidate()]) (or await each)
inside that async function to ensure both invalidations are awaited and errors
surface; update the onData declaration in useBrowserBindingsSync.ts accordingly.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx`:
- Around line 143-152: The Metric component currently types its props inline;
extract the props into a named interface (e.g., interface MetricProps { label:
string; value: number }) and update the Metric signature to use MetricProps
(function Metric({ label, value }: MetricProps) { ... }) to comply with the
project guideline of preferring interface for object shapes; keep the interface
local to the file (or export it if used elsewhere) and ensure any references or
tests use the new MetricProps type.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx`:
- Around line 300-310: Several components (SessionCard, Tag, ReadyPanel,
DetailItem, SetupPanel) use inline object-type props; extract each inline type
into a named interface (e.g., interface SessionCardProps { ... }) and replace
the inline annotations with the interface names in the function/component
signatures; ensure exported or locally-scoped interfaces match existing property
names and types (AutomationSession, boolean, string | null, callbacks) and
update all occurrences within the file (including the other ranges mentioned) to
use the new interfaces to follow the repository convention of using interfaces
for object shapes.

In `@packages/local-db/src/schema/browser-automation-bindings.ts`:
- Around line 20-23: The sessionKind column is currently declared as text(...)
so select results are typed as string; create a proper enum/union so TypeScript
infers "terminal" | "todo-agent". Define a TS union type (e.g. type SessionKind
= "terminal" | "todo-agent") and replace the column declaration sessionKind:
text("session_kind").notNull().default("todo-agent") with the schema enum helper
your ORM provides (e.g. sqliteEnum/textEnum/sqlEnum(...) or enums(...)) listing
["terminal","todo-agent"] so the resulting column and selects are typed as
SessionKind and match the router branches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a31a6497-cb67-4a5c-95c2-0d83178fc0f7

📥 Commits

Reviewing files that changed from the base of the PR and between 88547a9 and bfcd8a3.

📒 Files selected for processing (13)
  • apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
  • apps/desktop/src/renderer/hooks/useBrowserAutomationData.ts
  • apps/desktop/src/renderer/hooks/useBrowserBindingsSync.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserAutomationList/BrowserAutomationList.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/ConnectButton/ConnectButton.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/SessionConnectModal/SessionConnectModal.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
  • packages/local-db/drizzle/0067_add_browser_automation_bindings.sql
  • packages/local-db/drizzle/meta/0067_snapshot.json
  • packages/local-db/drizzle/meta/_journal.json
  • packages/local-db/src/schema/browser-automation-bindings.ts
  • packages/local-db/src/schema/index.ts
  • packages/local-db/src/schema/schema.ts
✅ Files skipped from review due to trivial changes (3)
  • packages/local-db/src/schema/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
  • packages/local-db/drizzle/meta/_journal.json

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
Comment thread packages/local-db/drizzle/0067_add_browser_automation_bindings.sql
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be0f062b9b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts Outdated
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

…lures

- detectClaudeMcp: JSON.parse + check mcpServers["superset-browser"] is an
  object with a command/url/args and not disabled. (codex P2)
- detectCodexMcp: isolate [mcp_servers.superset-browser] section, skip if
  disabled=true, require command/url/args. Avoid adding a TOML parser dep
  just for this single lookup.
- detectTerminalAgentSessions: catch terminal-host IPC errors and degrade
  to an empty result so listBindingLiveness can still serve non-terminal
  bindings when the daemon is unavailable. (codex P1)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c967c80ec1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts Outdated
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

detectClaudeMcp previously only inspected ~/.claude/settings.json, but
`claude mcp add` writes to ~/.claude.json. Check both paths (plus
project-scope later) so a normally-configured Claude session is correctly
reported as MCP-ready. Expose the primary user path in claudeConfigPath
so the setup guide points users at the right file. (codex P1)
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7c41b1cd2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts Outdated
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 941983c0c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts
- collectProjectMcpJsonPaths: enumerate every workspace's worktree /
  mainRepoPath and include <path>/.mcp.json in the Claude MCP detection
  paths so `claude mcp add -s project` configurations are recognized.
  (codex P1)
- detectCodexMcp: accept quoted TOML header forms
  `[mcp_servers."superset-browser"]` and `[mcp_servers.'superset-browser']`
  alongside the bare form. (codex P2)
…connect

# Conflicts:
#	apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/index.tsx
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

1 similar comment
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6db35639c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts Outdated
getMcpStatus previously OR'd every workspace's .mcp.json into a single
claudeReady boolean, so one configured project made all Claude sessions
look ready across projects. The router now returns:
  - claudeHomeReady: home-scope only
  - claudeReadyByWorkspaceId: per-workspace .mcp.json probe
useBrowserAutomationData resolves each session against its own workspace
(home || project) so Connect stays blocked for a session whose workspace
has no MCP entry even if a sibling project does. (codex P1)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0257cf0b94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/lib/trpc/routers/browser-automation/index.ts Outdated
`claude mcp add` (default local scope) stores MCP server definitions at
projects[<cwd>].mcpServers inside ~/.claude.json, not at the top level.
detectClaudeMcpInFile now inspects both locations, keyed by each
workspace's resolved path. getMcpStatus passes per-workspace paths so a
workspace that only has local-scope MCP still reports ready. (codex P1)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ef6bd025c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/stores/browser-automation.ts
- Snippet / command name: the real bin shipped by packages/desktop-mcp
  is `desktop-mcp`, not `superset-browser-mcp`. Update both the TOML
  and the Claude CLI snippet so copy-pasting actually works. (codex P1)
- Claude setup: recommend `claude mcp add superset-browser -s local --
  desktop-mcp` and explain /mcp-reload, rather than telling users to
  append a complete JSON document into ~/.claude.json (which would
  produce invalid JSON). Codex still gets the TOML section-append path
  since that is mergeable as-is. (codex P2)
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

@MocA-Love MocA-Love merged commit f5a5088 into main Apr 20, 2026
14 checks passed
@MocA-Love
Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

1 participant