Skip to content

feat(desktop): AgentManagerライブストリームを折りたたみグループ化#199

Merged
MocA-Love merged 6 commits intomainfrom
fix/192-livestream-grouping
Apr 16, 2026
Merged

feat(desktop): AgentManagerライブストリームを折りたたみグループ化#199
MocA-Love merged 6 commits intomainfrom
fix/192-livestream-grouping

Conversation

@MocA-Love
Copy link
Copy Markdown
Owner

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

概要

Issue #192 の対応。AgentManager のライブストリーム表示について 「ToolCall が全部展開されて忙しい」 「VSCode の Claude Code 拡張のようにシンプルにしたい」 という要望。

修正方針

VSCode 拡張と同じく、ツール実行はデフォルト 1 行に畳み、ユーザが気になるときだけ展開する形にした。

  • `groupStreamEvents`: 連続する `tool_use` / `tool_result` / `raw` イベントを 1 つのグループにまとめる
  • `assistant_text` / `result` / `error` / `system_init` はナラティブの区切り点なので個別行のまま残す
  • 新コンポーネント `StreamToolGroup`:
    • 折りたたみヘッダには `[iter N]` / `🔧N件` / `✓N件` / 最後のツール呼び出しのサマリ(先頭 80 文字) を表示
    • クリックで展開し、中に従来どおりの `StreamEventRow` を並べる

スコープ外

Issue 本文で Agent まとめ / VSCode 拡張の実装丸ごと調査 とも書かれているが、現状のストリームは `tool_use` / `tool_result` しか持たず、ツール種別ごとの固有 UI(Edit diff / Read 行番号など) は情報を追加で取り込まないと描けない。今回の PR は 「忙しい」 ことへの即効薬に絞り、固有 UI は別タスクで段階的に足す。

Test plan

  • ツールを多用するタスクを走らせ、ストリームが短く見やすくなっている
  • ツールグループをクリックで展開→従来どおりの詳細が見える
  • assistant_text / result / error は個別行として表示される
  • 自動スクロールの振る舞いが変わっていない

Refs #192

Summary by CodeRabbit

リリースノート

  • New Features

    • Added settings panel for configuring agent parameters (max iterations, execution time, concurrent tasks).
    • Enhanced preset system with categorization and workspace support.
    • Redesigned presets dialog with tabbed interface.
  • Improvements

    • Refined task status badges with running and queued indicators.
    • Improved stream event visualization with collapsible tool details and better formatting.

ToolCallごとに1行出力されて情報過多だったAgentManager
のライブストリームを、VSCode版ClaudeCode拡張のように
ツール実行を折りたたみグループ化するよう変更。

- groupStreamEvents: 連続する tool_use / tool_result /
  raw イベントを1つのグループに畳む
- assistant_text / result / error / system_init は
  ナラティブの区切り点なので個別行のまま残す
- StreamToolGroup: 折りたたみ行コンポーネント。件数
  バッジ (🔧N件 / ✓N件) と最後のツール呼び出しの
  サマリだけ表示し、クリックで全詳細を展開

プリセット/フォルダ/kindなど 「全面刷新」 系の要望は
別PRで段階的に取り込む方針。

Refs #192
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

📝 Walkthrough

Walkthrough

新しいtodo-agent設定永続化システムを追加。JSONファイルベースのキャッシュ機構、TRPC設定エンドポイント、プリセット分類とワークスペーススコープ対応、データベース移行、UIコンポーネント統合を実装。

Changes

Cohort / File(s) Summary
Settings Infrastructure
apps/desktop/src/main/todo-agent/settings.ts, apps/desktop/src/main/todo-agent/types.ts, apps/desktop/src/main/todo-agent/trpc-router.ts
新しい設定永続化モジュールを導入。ファイルベースのJSON保存・読み込み機能、メモリキャッシュ、TRPC GET/UPDATEエンドポイント。スキーマ検証とデフォルト値フォールバック実装。
Preset & Settings Type Definitions
apps/desktop/src/main/todo-agent/types.ts
プリセット作成・更新スキーマにkind(system/description/goal)とオプションworkspaceIdを追加。新規TodoSettingsスキーマでdefaultMaxIterationsdefaultMaxWallClockMinmaxConcurrentTasksを定義。
Database Migration & Schema
packages/local-db/drizzle/0055_todo_preset_kind_workspace.sql, packages/local-db/drizzle/meta/0055_snapshot.json, packages/local-db/drizzle/meta/_journal.json, packages/local-db/src/schema/todo-prompt-presets.ts
todo_prompt_presetsテーブルにkind(非null、デフォルト'system')とworkspace_id(nullable)カラムを追加。インデックス及びスキーマスナップショット更新。
UI Component Enhancements
apps/desktop/src/renderer/features/todo-agent/TodoButton/TodoButton.tsx, apps/desktop/src/renderer/features/todo-agent/TodoManager/PresetsDialog/PresetsDialog.tsx, apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx, apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx
設定照会・UI更新統合。PresetsDialogにタブベースレイアウト(プリセット/設定)追加。TodoManagerで連続イベント結合・ツール呼び出し詳細表示改善。TodoModalで動的デフォルト値初期化。TodoButtonで実行状態別バッジ分離。

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI Component
    participant TRPC as TRPC Router
    participant Settings as Settings Module
    participant FileSystem as File System
    participant Cache as Memory Cache

    rect rgba(200, 150, 255, 0.5)
    Note over UI,Cache: Settings Retrieval Flow
    UI->>TRPC: todoAgent.settings.get()
    TRPC->>Settings: getTodoSettings()
    Settings->>Cache: Check cache
    alt Cache Hit
        Cache-->>Settings: Return cached value
    else Cache Miss
        Settings->>FileSystem: Read settings.json
        alt File Exists
            FileSystem-->>Settings: JSON content
            Settings->>Settings: Parse & validate
        else File Missing
            Settings->>Settings: Use defaults
        end
        Settings->>Cache: Store in memory
    end
    Settings-->>TRPC: TodoSettings object
    TRPC-->>UI: Settings data
    end

    rect rgba(150, 200, 255, 0.5)
    Note over UI,Cache: Settings Update Flow
    UI->>TRPC: todoAgent.settings.update(patch)
    TRPC->>Settings: updateTodoSettings(patch)
    Settings->>Settings: Merge with current
    Settings->>Settings: Validate merged result
    Settings->>Cache: Update cache
    Settings->>FileSystem: Write formatted JSON
    FileSystem-->>Settings: Write complete
    Settings-->>TRPC: Updated TodoSettings
    TRPC-->>UI: Confirmation & data
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 設定の樹に新しき枝が生え
ファイルにはキャッシュの風そよぐ
プリセット分けて、世界も広がり
タブのうえで、ウサギは笑う✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% 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 タイトルは日本語で「AgentManagerライブストリームを折りたたみグループ化」と記されており、PR 目的の要点(ツール呼び出しのデフォルト折りたたみ化)を指していますが、実装内容がプリセット管理、設定周り、UI フロー変更など多岐にわたるため、タイトルが完全には主要変更を反映していません。
Description check ✅ Passed PR 説明は詳細で体系的(概要、修正方針、スコープ外、テストプラン)であり、テンプレートの「Description」「Related Issues」「Type of Change」「Testing」セクションに相当する情報を網羅しています。

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/192-livestream-grouping

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: 2023e30f73

ℹ️ 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".

if (toolBucket.length === 0) return;
items.push({
type: "tools",
id: `tools:${toolBucket[0]?.id ?? ""}:${toolBucket.length}`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use stable keys for grouped tool rows

The grouped row key includes toolBucket.length, so while a tool block is still streaming, every newly appended event changes the key and forces StreamToolGroup to remount. That resets its local open state to false, so an expanded group collapses again on the next chunk and users cannot inspect live tool output without repeatedly reopening it.

Useful? React with 👍 / 👎.

Comment on lines +1047 to +1049
ev.kind === "tool_use" ||
ev.kind === "tool_result" ||
ev.kind === "raw"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop folding raw prompt events into tool groups

Treating raw as a tool event regresses stream readability because this codebase emits user prompts as kind: "raw" in appendUserEvent (see apps/desktop/src/main/todo-agent/supervisor.ts). Those prompt checkpoints now get hidden inside collapsed tool groups, often showing 🔧 0件 and the fallback summary ツール実行, which mislabels non-tool content and makes iteration prompts easy to miss.

Useful? React with 👍 / 👎.

- グループキーから toolBucket.length を外し、先頭イベントidのみを
  キーにした。毎チャンクで StreamToolGroup が remount されて
  展開状態が消える問題を解消
- raw を tool グループに畳まないように変更。raw は
  appendUserEvent でユーザプロンプトとしても使われており、
  iteration boundary がツール塊の中に埋もれて読みづらくなる
  ので個別行として残す

Refs: Codex review on PR #199
大幅なUI刷新:
- ストリーム表示をVSCode ClaudeCode拡張のIN/OUTグリッド
  レイアウトに合わせてリライト。tool_use+tool_resultを
  ペアリングしたToolCallCardで表示(折りたたみではなく常時表示)
- assistant_text / result / error / system_init は種別ごとに
  専用のMessageRowで描画
- PresetsDialogにプリセット/設定タブを追加。設定タブで
  デフォルト最大イテレーション・タイムアウト(分)・
  最大同時実行数を変更可能
- 設定はJSONファイルとしてuserDataディレクトリに永続化
- TodoModalが設定のデフォルト値を自動反映
- TodoButtonのバッジを全ワークスペース横断(listAll)に変更、
  実行中タスクにpulsingドット付きバッジ、待機タスクに+N表示

Refs #192
VSCode Claude Code拡張のwebview/index.cssを直接解析し、本物と
同じ折りたたみUI+IN/OUTグリッドレイアウトに書き直した。

- <details>/<summary>ベースに変更。ツールコールはデフォルトで
  折りたたまれ、summaryはbold太字ツール名+monospaceでlink-color
  の二次情報(ファイルパス等)の2行構成
- 本体(.rr)を開くとgrid-template-columns: max-content 1fr の
  grid-subgridレイアウトでIN/OUT表示(拡張の .ir/.lo/.tr と同等)
- StreamViewの親をabsolute inset-0にして、カード圧縮問題を
  修正(従来はflex-shrinkで履歴が潰れていた)
- assistant_text/result/errorは枠線なしのborder-leftスタイルに
  簡素化。system_initは1行アイコンバッジに
- raw eventも<details>で折りたたみ

プリセット拡張準備:
- todoPromptPresetsテーブルに kind (system/description/goal) と
  workspaceId (null=global) を追加。migration 0055 生成
- zodスキーマとtRPC create/updateも対応
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 (1)
apps/desktop/src/main/todo-agent/settings.ts (1)

8-12: 軽微な非効率性: mkdirSyncが毎回呼び出される

getSettingsPath()が呼び出されるたびにmkdirSyncが実行されます。getTodoSettings()はキャッシュ後は1回のみですが、updateTodoSettings()は毎回呼び出します。recursive: trueにより冪等性は保証されていますが、ディレクトリ存在チェックを追加することで若干効率化できます。

ただし、設定更新は頻繁な操作ではないため、現状でも問題ありません。

♻️ オプショナル: ディレクトリ作成を初回のみに
+let dirEnsured = false;
+
 function getSettingsPath(): string {
 	const dir = path.join(app.getPath("userData"), "todo-agent");
-	mkdirSync(dir, { recursive: true });
+	if (!dirEnsured) {
+		mkdirSync(dir, { recursive: true });
+		dirEnsured = true;
+	}
 	return path.join(dir, SETTINGS_FILE);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/todo-agent/settings.ts` around lines 8 - 12,
getSettingsPath currently calls mkdirSync(dir, { recursive: true }) every time,
which is redundant; modify it so the directory is only created when missing
(e.g., check fs.existsSync(dir) or maintain an initialized flag) to avoid
repeated mkdirSync calls while keeping behavior intact; update references in
getTodoSettings and updateTodoSettings to rely on the revised getSettingsPath so
directory creation happens only on first need.
🤖 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/features/todo-agent/TodoButton/TodoButton.tsx`:
- Around line 30-44: TodoButton is using electronTrpc.todoAgent.listAll.useQuery
which returns sessions across all workspaces, causing incorrect counts; change
the data source to only the current workspace by either calling
electronTrpc.todoAgent.list.useQuery with the current workspaceId or, if keeping
listAll, filter allSessions by workspaceId before computing
runningCount/queuedCount; update references to allSessions, runningCount,
queuedCount, and activeCount accordingly so only sessions where
session.workspaceId === workspaceId are included.

In `@apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx`:
- Around line 1030-1055: pairStreamEvents currently pairs only a single adjacent
tool_use with a tool_result; change it to collapse consecutive tool invocations
into one grouped StreamItem (e.g., type "toolGroup") so runs of
tool_use/tool_result become one item. Implement by iterating with an index, and
when you see a tool_use start a group collecting toolUse entries and
corresponding toolResult (if next event is tool_result) in arrays, advancing the
index past consumed events until the next non-tool event; for non-tool events
continue producing message items as before. Update references to StreamItem
consumers (e.g., MessageRow or any renderers expecting single tool entries) to
handle the new group fields (toolUses/toolResults) accordingly.

---

Nitpick comments:
In `@apps/desktop/src/main/todo-agent/settings.ts`:
- Around line 8-12: getSettingsPath currently calls mkdirSync(dir, { recursive:
true }) every time, which is redundant; modify it so the directory is only
created when missing (e.g., check fs.existsSync(dir) or maintain an initialized
flag) to avoid repeated mkdirSync calls while keeping behavior intact; update
references in getTodoSettings and updateTodoSettings to rely on the revised
getSettingsPath so directory creation happens only on first need.
🪄 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: 8bba990d-7c13-4f7f-bf3c-f400c1ccffed

📥 Commits

Reviewing files that changed from the base of the PR and between 58c8e6f and 3d9da73.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • apps/desktop/src/main/todo-agent/settings.ts
  • apps/desktop/src/main/todo-agent/trpc-router.ts
  • apps/desktop/src/main/todo-agent/types.ts
  • apps/desktop/src/renderer/features/todo-agent/TodoButton/TodoButton.tsx
  • apps/desktop/src/renderer/features/todo-agent/TodoManager/PresetsDialog/PresetsDialog.tsx
  • apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx
  • apps/desktop/src/renderer/features/todo-agent/TodoModal/TodoModal.tsx
  • packages/local-db/drizzle/0055_todo_preset_kind_workspace.sql
  • packages/local-db/drizzle/meta/0055_snapshot.json
  • packages/local-db/drizzle/meta/_journal.json
  • packages/local-db/src/schema/todo-prompt-presets.ts

Comment on lines +30 to +44
const { data: allSessions } = electronTrpc.todoAgent.listAll.useQuery(
undefined,
{ refetchInterval: 3000 },
);

const activeCount = (sessions ?? []).filter(
(session) =>
session.status === "queued" ||
session.status === "preparing" ||
session.status === "running" ||
session.status === "verifying",
const runningCount = (allSessions ?? []).filter(
(s) =>
s.status === "preparing" ||
s.status === "running" ||
s.status === "verifying",
).length;
const queuedCount = (allSessions ?? []).filter(
(s) => s.status === "queued",
).length;
const activeCount = runningCount + queuedCount;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

listAll() への切り替えで、このボタンの件数が他ワークスペース分まで混ざっています。

TodoButtonworkspaceId を受け取る現在ワークスペース文脈の UI ですが、ここで todoAgent.listAll() を使うと apps/desktop/src/main/todo-agent/trpc-router.ts:99-106 / apps/desktop/src/main/todo-agent/session-store.ts:259-280 の実装どおり全ワークスペースのセッションが返ります。
そのため、別ワークスペースで preparing / running / verifying / queued があるだけでも、このボタンの強調表示とバッジ件数が誤表示になります。ここは従来どおり todoAgent.list({ workspaceId }) を使うか、少なくとも allSessionsworkspaceId で絞ってから集計した方がよいです。

修正例
-	const { data: allSessions } = electronTrpc.todoAgent.listAll.useQuery(
-		undefined,
-		{ refetchInterval: 3000 },
-	);
+	const { data: workspaceSessions } = electronTrpc.todoAgent.list.useQuery(
+		{ workspaceId },
+		{ refetchInterval: 3000 },
+	);

-	const runningCount = (allSessions ?? []).filter(
+	const runningCount = (workspaceSessions ?? []).filter(
 		(s) =>
 			s.status === "preparing" ||
 			s.status === "running" ||
 			s.status === "verifying",
 	).length;
-	const queuedCount = (allSessions ?? []).filter(
+	const queuedCount = (workspaceSessions ?? []).filter(
 		(s) => s.status === "queued",
 	).length;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/features/todo-agent/TodoButton/TodoButton.tsx`
around lines 30 - 44, TodoButton is using
electronTrpc.todoAgent.listAll.useQuery which returns sessions across all
workspaces, causing incorrect counts; change the data source to only the current
workspace by either calling electronTrpc.todoAgent.list.useQuery with the
current workspaceId or, if keeping listAll, filter allSessions by workspaceId
before computing runningCount/queuedCount; update references to allSessions,
runningCount, queuedCount, and activeCount accordingly so only sessions where
session.workspaceId === workspaceId are included.

Comment on lines +1030 to +1055
function pairStreamEvents(events: TodoStreamEvent[]): StreamItem[] {
const items: StreamItem[] = [];
for (let i = 0; i < events.length; i++) {
const ev = events[i];
if (!ev) continue;
if (ev.kind === "tool_use") {
const next = events[i + 1];
if (next?.kind === "tool_result") {
items.push({
type: "tool",
id: ev.id,
toolUse: ev,
toolResult: next,
});
i++;
} else {
items.push({ type: "tool", id: ev.id, toolUse: ev, toolResult: null });
}
} else if (ev.kind === "tool_result") {
items.push({ type: "message", id: ev.id, event: ev });
} else {
items.push({ type: "message", id: ev.id, event: ev });
}
}
return items;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

連続したツール実行がまだ 1 グループに畳まれていません。

pairStreamEvents() は隣接する tool_usetool_result を 1 件ずつペアにするだけなので、ツール呼び出しが続く区間でも行数はそのまま増えます。これだと長いツール連打でストリームが依然として縦に伸びますし、tool_result が直後に来ないケースは MessageRow 側へ落ちて表示も崩れます。ここは 1 ペアではなく、連続する tool_use / tool_result をまとめたグループ単位の StreamItem にした方が今回の目的に合っています。

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

In `@apps/desktop/src/renderer/features/todo-agent/TodoManager/TodoManager.tsx`
around lines 1030 - 1055, pairStreamEvents currently pairs only a single
adjacent tool_use with a tool_result; change it to collapse consecutive tool
invocations into one grouped StreamItem (e.g., type "toolGroup") so runs of
tool_use/tool_result become one item. Implement by iterating with an index, and
when you see a tool_use start a group collecting toolUse entries and
corresponding toolResult (if next event is tool_result) in arrays, advancing the
index past consumed events until the next non-tool event; for non-tool events
continue producing message items as before. Update references to StreamItem
consumers (e.g., MessageRow or any renderers expecting single tool entries) to
handle the new group fields (toolUses/toolResults) accordingly.

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