Skip to content

Add AI Vault subagent display#7423

Merged
brennanb2025 merged 14 commits into
stablyai:mainfrom
hmrserver:feat/ai-vault-subagents-and-edge-peek
Jul 10, 2026
Merged

Add AI Vault subagent display#7423
brennanb2025 merged 14 commits into
stablyai:mainfrom
hmrserver:feat/ai-vault-subagents-and-edge-peek

Conversation

@hmrserver

@hmrserver hmrserver commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

AI Vault now shows Claude Task subagents under their parent session. Session rows show N subagents when available, live status dots for active sessions, and the model label; expanding a session shows each subagent's status, type, message count, and log link.

Scope note: this PR originally also added a right-edge hover peek for the right sidebar. That feature was split out unchanged to #8015 so this PR lands the subagent display only.

Screenshots

Visual change — media attached in the PR conversation:

  • AI Vault session row with the live status dot and working spinner, subagent count, and model label:
ai-vault-row-live-dot-subagents-model
  • Expanded Subagents section with per-subagent status dots (running, completed, failed), agent type, message count, and log button:
ai-vault-subagents-section

Testing

  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • pnpm build
  • Added or updated high-quality tests that would catch regressions, or explained why tests were not needed

Latest state (after the review-hardening commits): full pnpm typecheck and pnpm lint pass on the branch merged with current main. Targeted Vitest: all src/main/ai-vault/ + src/main/ipc/ai-vault.test.ts (131 tests) and all src/renderer/src/components/right-sidebar/ tests (1190 tests) green. The subagent display was also validated live in a dev build against real Claude session data: row badge, model label, and working dot render; the expanded Subagents section lists live/finished subagents with statuses; the badge recounts on refresh and exactly matches the transcripts on disk; refetches keep prior rows (no flicker); zero renderer console errors.

AI Review Report

The review focused on scan correctness, IPC path handling, SSH/local behavior, UI interaction edge cases, performance in scanner hot paths, and supported-agent compatibility. It flagged and this PR now fixes:

  • Claude subagent transcripts were originally discoverable as phantom top-level sessions; local and SSH discovery now prune subagents/.
  • Subagent IPC now resolves and contains-checks renderer-supplied paths.
  • Subagent transcript parsing is batched at concurrency 8.
  • Subagent rows keep prior data during refetches so active sessions do not flicker.

Review hardening on top (maintainer review pass):

  • The row-badge count, recoverable-empty signal, on-demand lister, and remote walk regex now share one agent-*.jsonl predicate, so the N subagents badge can never disagree with the expanded list; a fails-on-revert exclusion test pins the predicate.
  • The parent-transcript status pass prefilters toolUseResult lines on "agentId", so it no longer JSON-parses the large tool-output records that dominate a transcript's bytes on every on-demand fetch (measured ~192ms of main-process CPU per expand on a 65MB transcript before the fix; behavior-preserving, notification lines stay ungated).
  • Formatting-only churn on 8 files untouched by the feature (cloud-profile, IME/xterm tests) was reverted to main's content, so the diff is AI Vault-scoped only.

Cross-platform coverage was explicitly checked for macOS, Linux, and Windows. There are no shortcut or label changes. Path handling uses Node path utilities, WSL Claude roots are included, and remote path segments handle / and \.

Security Audit

The new IPC surface is aiVault:listSubagentSessions. It is provider-gated to Claude, host-gated to local sessions, and resolves parentFilePath before checking containment under known Claude project roots, including WSL roots. Dot-segment traversal is covered by a test.

The endpoint only lists */subagents/agent-*.jsonl transcripts and their meta sidecars. It does not execute commands, read arbitrary paths after containment, add network access, touch secrets, or add dependencies. Symlink escape would require prior write access inside ~/.claude/projects; realpath is intentionally not required because those roots may not exist on fresh installs or WSL setups.

Notes

SSH sessions show the subagent count (derived from the remote walk listing, no extra round-trips), but listSubagentSessions intentionally returns empty for non-local hosts — their transcripts live on the remote disk. Remote subagent listing is future work; this is honest UI behavior instead of trying to read local paths for remote sessions.

Known limitations, documented in code: depth-2+ nested subagents fall back to a 5-minute recency heuristic for their status dot (their terminal status lives in the spawner's transcript, not the parent's), and a user message that literally begins with a pasted task-notification can forge a cosmetic status dot on a view-only row.

This overlaps with #7369 in Claude discovery wiring, so a small merge conflict is likely whichever lands second.

Non-English locale entries are synced with English placeholder text for the new strings, matching the existing translation-pass workflow.

AI Vault now lists Claude Task subagents under their parent session with
live run-status dots, shows subagent counts and the model label in session
rows, and drops the inconsistent Codex-only effort display. The right
sidebar gains an Arc-style edge peek: hovering the window's right edge
reveals it as a floating overlay without reflowing the editor.

Subagent transcripts are pruned from local and SSH scan discovery and read
lazily through a provider- and host-gated IPC endpoint with path
containment. The edge peek uses geometry-based arm/dismiss listeners (no
intercepting DOM strip) and a transient, unpersisted store flag.
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

This PR adds Claude subagent transcript tracking: discovery, counting, and status-resolved listing of "Task subagent" transcripts colocated under a session's subagents/ directory, pruned from normal session enumeration and exposed via a new IPC endpoint and preload API. AiVaultSession gains subagent/subagentCount fields. The renderer adds a subagent list UI section, session live-state resolution wired into session rows, a new "failed" agent dot state, and a sessionModelLabel helper. Separately, a right-sidebar hover "edge peek" feature is introduced with new store state, dismiss logic, and layout wiring. Locale files gain related translation keys, and tests cover the new behaviors.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: AI Vault subagent display.
Description check ✅ Passed The description follows the template with summary, screenshots, testing, AI review, security audit, and notes.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/renderer/src/components/right-sidebar/ai-vault-session-display.ts (1)

83-85: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Minor: prefer ?? over || for the null check.

session.model || null also nulls out an empty-string model. Using ?? matches the declared string | null type more precisely.

♻️ Proposed tweak
 export function sessionModelLabel(session: AiVaultSession): string | null {
-  return session.model || null
+  return session.model ?? null
 }
src/renderer/src/components/AgentStateDot.tsx (1)

105-119: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a distinct glyph for failed, similar to done's check icon.

failed currently renders as a plain red dot (same visual weight as blocked/interrupted), while done gets a dedicated CircleCheck icon for scannability. Since subagent failures are meant to be an attention-worthy outcome, a distinct icon (e.g. an X/alert glyph) could make failures easier to spot at a glance in a long session list.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 37ce0884-6d18-409c-9952-2b7386544fad

📥 Commits

Reviewing files that changed from the base of the PR and between 9981c3b and 7b9816f.

📒 Files selected for processing (46)
  • src/main/ai-vault/remote-session-scanner-sources.ts
  • src/main/ai-vault/remote-session-scanner.test.ts
  • src/main/ai-vault/session-scanner-accumulator.ts
  • src/main/ai-vault/session-scanner-agent-parser.ts
  • src/main/ai-vault/session-scanner-claude-subagent-prune.test.ts
  • src/main/ai-vault/session-scanner-claude-subagents.test.ts
  • src/main/ai-vault/session-scanner-claude-subagents.ts
  • src/main/ai-vault/session-scanner-codex-parser.test.ts
  • src/main/ai-vault/session-scanner-codex-parser.ts
  • src/main/ai-vault/session-scanner-discovery.ts
  • src/main/ai-vault/session-scanner-scan-roots-fixture.ts
  • src/main/ai-vault/session-scanner-source-discovery.ts
  • src/main/ai-vault/session-scanner.test.ts
  • src/main/ipc/ai-vault.test.ts
  • src/main/ipc/ai-vault.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/App.tsx
  • src/renderer/src/components/AgentStateDot.tsx
  • src/renderer/src/components/right-sidebar/AiVaultPanel.tsx
  • src/renderer/src/components/right-sidebar/AiVaultSessionDetails.tsx
  • src/renderer/src/components/right-sidebar/AiVaultSessionRow.tsx
  • src/renderer/src/components/right-sidebar/AiVaultSessionSubagents.test.tsx
  • src/renderer/src/components/right-sidebar/AiVaultSessionSubagents.tsx
  • src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsx
  • src/renderer/src/components/right-sidebar/ai-vault-original-pane-actions.ts
  • src/renderer/src/components/right-sidebar/ai-vault-original-pane.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-original-pane.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-display.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-display.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-filters.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-projects.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-row-display.tsx
  • src/renderer/src/components/right-sidebar/ai-vault-session-worktree.test.ts
  • src/renderer/src/components/right-sidebar/index.tsx
  • src/renderer/src/components/right-sidebar/right-sidebar-edge-peek.test.tsx
  • src/renderer/src/components/right-sidebar/right-sidebar-edge-peek.tsx
  • src/renderer/src/components/sidebar/worktree-card-agent-summary.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/slices/editor.ts
  • src/renderer/src/web/web-preload-api.ts
  • src/shared/ai-vault-types.ts

Comment thread src/main/ipc/ai-vault.ts Outdated
Comment thread src/renderer/src/components/right-sidebar/right-sidebar-edge-peek.tsx Outdated
Comment thread src/renderer/src/i18n/locales/en.json Outdated
Comment thread src/renderer/src/web/web-preload-api.ts Outdated
…, guard subagent IPC args

The edge-peek flag now resets when the zone unmounts so a view change can't
leave a ghost peek behind; the row badge picks singular/plural subagent keys
per the catalog convention; and malformed aiVault:listSubagentSessions
payloads resolve empty instead of throwing, matching the sibling handler.
@hmrserver
hmrserver force-pushed the feat/ai-vault-subagents-and-edge-peek branch from ff3c45b to 0129b65 Compare July 5, 2026 18:58
hmrserver added 3 commits July 6, 2026 13:36
…ts-and-edge-peek

# Conflicts:
#	src/main/ai-vault/session-scanner-discovery.ts
#	src/main/ai-vault/session-scanner-source-discovery.ts
#	src/main/ai-vault/session-scanner.test.ts
…ts-and-edge-peek

# Conflicts:
#	src/main/ai-vault/session-scanner-codex-parser.ts
…vault-subagents-and-edge-peek

# Conflicts:
#	src/main/ai-vault/remote-session-scanner-sources.ts
#	src/main/ai-vault/remote-session-scanner.test.ts
#	src/main/ipc/ai-vault.test.ts
#	src/main/ipc/ai-vault.ts
#	src/renderer/src/components/right-sidebar/AiVaultSessionDetails.tsx
#	src/renderer/src/components/right-sidebar/ai-vault-original-pane.test.ts
#	src/renderer/src/components/right-sidebar/ai-vault-session-display.test.ts
#	src/renderer/src/components/right-sidebar/ai-vault-session-filters.test.ts
#	src/renderer/src/components/right-sidebar/ai-vault-session-projects.test.ts
#	src/renderer/src/components/right-sidebar/ai-vault-session-row-display.tsx
#	src/renderer/src/components/right-sidebar/ai-vault-session-worktree.test.ts
#	src/renderer/src/i18n/locales/es.json
#	src/renderer/src/i18n/locales/ja.json
#	src/renderer/src/i18n/locales/ko.json
#	src/renderer/src/i18n/locales/zh.json
#	src/renderer/src/web/web-preload-api.ts
@hmrserver

Copy link
Copy Markdown
Contributor Author

@brennanb2025 can we review this?

brennanb2025 and others added 5 commits July 9, 2026 12:44
The edge peek will be reviewed as its own PR (preserved on
feat/right-sidebar-edge-peek-extracted). Also restores main's formatting
on files this PR never meant to touch and removes the superseded
AiVaultSessionRow.subagentCount locale key (the singular/plural pair
replaced it).

Co-authored-by: Orca <help@stably.ai>
Main already counts sibling subagent transcripts (stablyai#7889) for zero-turn
recovery; this PR was written before that landed and added a parallel
subagentCount doing the same readdir. Keep one field: count every local
session's transcripts in the shared Claude finalize (one readdir per
parse instead of two extra call sites), attach remote counts from the
walk listing for all sessions, and gate the on-demand subagent listing
UI to local sessions. Net effect vs the PR: SSH rows now show a real
subagent count badge; listing stays local-only.

Co-authored-by: Orca <help@stably.ai>
The status marker sits after <tool-use-id>/<output-file> lines, past the
96-char title cap, so user-message-delivered notifications lost their
<status> and fell back to transcript recency. Read the notification text
untruncated and only honor records whose text is the notification itself,
so a user prompt quoting one can't overwrite a real terminal status.

Co-authored-by: Orca <help@stably.ai>
…cation

A sync-Task completion carries its status in toolUseResult, but when the
subagent's report text mentions <task-notification> the raw-line prefilter
routed the record into the notification branch, whose guard rejected the
non-notification text and then unconditionally continued — dropping the real
'completed' status to the recency fallback. Only consume the record in the
notification branch when its text actually is the notification.

Co-authored-by: Orca <help@stably.ai>
@brennanb2025 brennanb2025 changed the title Add AI Vault subagents and right-sidebar edge peek Add AI Vault subagent display Jul 10, 2026
@brennanb2025

Copy link
Copy Markdown
Contributor

Thanks @hmrserver — the subagents feature is in good shape and I'm landing it after some reworking on the branch. What changed:

  1. Split out the edge peek. The right-edge hover peek is now its own PR so we can discuss that UX separately — this PR is subagents-only (retitled accordingly; the peek section of the description is superseded).
  2. Converged the counting. Main recently gained subagent-transcript counting (#7889), so the new subagentCount field became a near-duplicate. Rows now reuse the existing subagentTranscriptCount, populated for every local Claude session with one readdir in the shared finalize path — and SSH sessions get real counts from the remote walk listing instead of the hardcoded 0.
  3. Fixed two status-parsing bugs (with regression tests that fail on the old code):
    • <task-notification> text was read through a title helper that truncates at 96 chars, so a <status> after a long output path was silently dropped and the dot fell back to recency.
    • A sync-Task toolUseResult whose report text merely quoted a notification was misrouted into the notification branch and its real terminal status discarded.
  4. Also removed a superseded locale key and restored main's formatting on a few files the merge commits had churned.

Nice work on the on-demand listing design and the IPC containment checks — both held up well under review.

brennanb2025 and others added 4 commits July 9, 2026 21:44
The merge from main left formatting-only reflows on cloud-profile and
IME/xterm test files. Revert them to main's exact content so unrelated
surfaces have zero diff.

Co-authored-by: Orca <help@stably.ai>
…tus pass

- Share one agent-*.jsonl predicate (isSubagentTranscriptFileName) across the
  row-badge count, the recoverable-empty signal, and the on-demand lister (and
  the remote SUBAGENT_DIRECT_CHILD_PATTERN) so the "N subagents" badge can't
  disagree with the expanded list.
- Gate collectSubagentTaskStatuses on a second "agentId" marker so the status
  pass no longer JSON-parses the large tool-output records that dominate a
  transcript's bytes on every on-demand fetch. Behavior-preserving: the
  toolUseResult branch only sets a status when result.agentId && result.status,
  so a status-setting line always contains "agentId"; notification lines stay
  ungated via hasNotification.
- Do NOT add a runtime arm to ownsTranscriptDisk: a 'runtime:' host never
  reaches finalizeClaudeSessionParseState. The local scan (used for local AND
  runtime-host scans) finalizes without an executionHostId and stamps the host
  after parse (withSessionExecutionHost), so runtime already counts via the
  undefined branch; only the SSH parse path threads an executionHostId and is
  correctly skipped. A runtime arm would be dead code (recorded in a comment on
  the guard). SSH-skip is covered by session-scanner-recoverable-empty.test.ts.
- Document the accepted forged-status limitation (a user message beginning with
  a verbatim task-notification).

Co-authored-by: Orca <help@stably.ai>
A stray non-agent .jsonl or a directory named like a transcript must not
inflate the row badge past what the on-demand lister shows.

Co-authored-by: Orca <help@stably.ai>
@brennanb2025

Copy link
Copy Markdown
Contributor

Pushed the final review-hardening commits on top of the rework:

  • e656db8a0 — the row-badge count, recoverable-empty signal, on-demand lister, and remote walk regex now share one agent-*.jsonl predicate (badge can't disagree with the expanded list), and the parent-transcript status pass prefilters toolUseResult lines on "agentId" so it stops JSON-parsing the huge tool-output records on every expand (~192ms of main-process CPU on a 65MB transcript before the fix).
  • c84536a90 — fails-on-revert test pinning the count predicate.
  • 5cca6eef0 — reverts formatting-only churn on 8 files untouched by the feature, so the diff is AI Vault-scoped only.

Full typecheck, lint, 131 main-process + 1190 renderer tests green on the branch merged with current main, plus live validation in a dev build against real Claude session data (badge/model/live dot, expanded subagent list with statuses, recount-on-refresh matching disk exactly, no refetch flicker). PR description updated to match the subagents-only scope (edge peek lives in #8015). Waiting on CI, then this is ready to land.

@brennanb2025
brennanb2025 merged commit 67447fe into stablyai:main Jul 10, 2026
3 checks passed
brennanb2025 pushed a commit that referenced this pull request Jul 10, 2026
AiVaultSession.subagent became required on main; the five fixtures added on
this branch predate it. Top-level scanned sessions carry null.
brennanb2025 added a commit that referenced this pull request Jul 10, 2026
* Show agent session history on mobile

Bring the desktop "Agent Session History" panel to Orca Mobile as a
per-worktree screen: browse past agent transcript sessions across the
host with scope tabs (Workspace/Project/All), search, grouping, session
cards, and tap-to-read message previews.

The transcript scan previously ran only over Electron IPC, so mobile
could not reach it. Expose it over the runtime RPC protocol mobile
already speaks (aiVault.listSessions) so the scan runs on whichever host
owns the transcripts — correct for local and SSH/remote hosts. Both the
desktop IPC handler and the new RPC method share one cache, so opening
the desktop panel and the mobile screen never double-scan.

The pure filter/group/display logic is lifted into /shared (the renderer
re-exports it) so the standalone mobile package can reuse it. Mobile
narrows scoped tabs client-side by cwd path-prefix because the host scan
treats scope paths as a widening union.

Resume-from-mobile is intentionally a follow-up.

* Fix mobile agent history list rendering and RPC authorization

- Authorize aiVault.listSessions in the mobile RPC allowlist so the
  mobile client's call is not rejected before dispatch (without this the
  screen could never load sessions at runtime).
- Name each SectionList section's rows `data` (the field React Native
  reads) instead of `cards`, fixing a type error and silent empty-section
  rendering.

* Address review feedback on agent session history

- Match quoted repo:/path: search operator values so labels and paths
  with spaces match (e.g. path:"/Users/ada/My Project").
- Hold a scoped tab in loading until the worktree list resolves instead
  of firing an unscoped fetch that briefly shows unrelated host history;
  proceed once loaded even if the worktree is absent (no stuck spinner).
- Clear cached host capabilities on disconnect/host-switch and failed
  status.get so a capability-gated action can't linger for a host that
  doesn't support it.
- Cover the real OrcaRuntimeService codex-home forwarding path and the
  quoted-operator parser with tests.

* Hide redundant mobile current worktree badges

Co-authored-by: Orca <help@stably.ai>

* Resume agent sessions from mobile history (#6969)

Co-authored-by: Orca <help@stably.ai>

* Adapt merged seams to main's lint and reply-sender hardening

Co-authored-by: Orca <help@stably.ai>

* Cap mobile project-scope paths to the aiVault RPC bound

Co-authored-by: Orca <help@stably.ai>

* Share the aiVault scopePaths bound between the RPC schema and mobile

Co-authored-by: Orca <help@stably.ai>

* Guard shared AI Vault inflight cleanup against concurrent key replacement

The extracted cache module's .finally() cleared inflight tracking
unconditionally, dropping the if (inflightKey === key) guard its sibling
outer cache kept: an older scan resolving after a different-key scan
replaced the tracking would null the newer scan's dedup slot, so a
re-request started a duplicate transcript rescan. Mirrors the sibling
guard; the regression test flushes a macrotask so a reverted guard fails
fast on the call count instead of hanging.

Co-authored-by: Orca <help@stably.ai>

* Harden aiVault.listSessions contract and gate mobile header entry on capability

- Clamp scopePaths (64) instead of rejecting, cap limit at 2000, and make
  executionHostId optional so mobile can omit it; restamp per caller.
- Retain successful mobile terminal-create mutation ids for 60s so resume
  retries dedupe after transient socket drops.
- Gate the session-header Agent History action on the aiVault.v1 capability
  (mirrors the host-list action) so old hosts never show a dead-end entry.
- Fix stale contract comments (scopePaths clamp semantics; filters move
  includes quoted repo:/path: operator parsing).

* Add subagent field to session test fixtures after #7423 merge

AiVaultSession.subagent became required on main; the five fixtures added on
this branch predate it. Top-level scanned sessions carry null.

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
brennanb2025 added a commit that referenced this pull request Jul 10, 2026
PR #7423 resized the working spinner ring from the dot size to the
icon size (sm: 6px -> 10px, md: 8px -> 12px) as a side effect of the
AI Vault subagent display work. The size change was unintentional in
the sidebar agent rows, so restore the original dot sizing.

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants