Skip to content

fix(sessions): prune stale agent session rows - #375

Open
DerrickBarra wants to merge 3 commits into
daggerhashimoto:masterfrom
GambitGamesLLC:fix/issue-372-prune-stale-agent-sessions
Open

DerrickBarra wants to merge 3 commits into
daggerhashimoto:masterfrom
GambitGamesLLC:fix/issue-372-prune-stale-agent-sessions

Conversation

@DerrickBarra

@DerrickBarra DerrickBarra commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

In Plain English

The Agents panel can keep showing old spawned-session rows after the gateway no longer reports them as current children. This PR makes the full session list converge with current spawnedBy truth while still keeping live children visible during gateway lag or transient spawnedBy lookup failures.

What

  • Adds session reconciliation logic for authoritative full-list plus spawnedBy session results.
  • Keeps live supplemental sessions when the full list lags behind a recent spawn.
  • Prunes terminal or stale child rows that are no longer confirmed by the current spawnedBy list.
  • Adds targeted regression tests for stale supplemental and stale base-list child sessions.

Why

Closes #372.

Without this, the Agents panel can show stale subagent/session rows after a spawned session is deleted, archived, or no longer returned by the gateway's current child lookup.

How

  • Introduces mergeAuthoritativeSessions in src/features/sessions/sessionReconciliation.ts.
  • Updates SessionContext to fetch spawnedBy child lists with success/failure metadata and reconcile them against the full list.
  • Leaves base sessions untouched when all spawnedBy lookups fail, avoiding sidebar data loss during RPC outages.
  • Covers the behavior in sessionReconciliation.test.ts and SessionContext.test.tsx.

Pre-patch proof:

  • On unpatched upstream master 312e27333e14f841b95bf4f2b205a856b4a4c370, adding only the stale-child regression test and running npm test -- --run src/contexts/SessionContext.test.tsx failed because Old cached child remained in the document.

Validation:

  • npm test -- --run src/features/sessions/sessionReconciliation.test.ts src/contexts/SessionContext.test.tsx passed: 2 files, 27 tests.
  • npm run lint passed.
  • npm run build passed. Vite reported existing chunk-size/dynamic-import warnings.
  • npm run build:server passed.
  • npm test -- --run passed: 143 files, 1878 tests. The run emitted existing React act(...) and nested-button warning noise from unrelated tests.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 📝 Documentation update
  • 🔧 Refactor / chore (no functional change)

Checklist

  • npm run lint passes
  • npm run build && npm run build:server succeeds
  • npm test -- --run passes
  • New features include tests
  • UI changes include a screenshot or screen recording

Screenshots

Not applicable; this is a session reconciliation behavior fix covered by tests.

Summary by CodeRabbit

  • Bug Fixes
    • Improved session list accuracy by removing stale or completed spawned sessions when authoritative updates confirm they are no longer active.
    • Preserved active sessions and live child sessions during reconciliation, including when some session lookups fail.
    • Prevented successful empty responses from being confused with failed requests, reducing unintended session removal.
    • Improved handling of duplicate and supplemental session records for a more consistent session view.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b3ad9186-43c2-4ff5-8855-cb0c6fab3b4d

📥 Commits

Reviewing files that changed from the base of the PR and between 08cd488 and 28e7b01.

📒 Files selected for processing (4)
  • src/contexts/SessionContext.test.tsx
  • src/contexts/SessionContext.tsx
  • src/features/sessions/sessionReconciliation.test.ts
  • src/features/sessions/sessionReconciliation.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/contexts/SessionContext.tsx
  • src/features/sessions/sessionReconciliation.ts

📝 Walkthrough

Walkthrough

Session loading now reconciles full gateway sessions with spawnedBy results. It removes stale child sessions, preserves eligible live sessions, and avoids pruning base sessions when spawned-session requests fail.

Changes

Session reconciliation

Layer / File(s) Summary
Reconciliation merge rules
src/features/sessions/sessionReconciliation.ts, src/features/sessions/sessionReconciliation.test.ts
Added live and terminal state handling, authoritative merge options, stale-child pruning, duplicate handling, and coverage for live, terminal, missing, and failed lookup cases.
Session loading integration
src/contexts/SessionContext.tsx, src/contexts/SessionContext.test.tsx
Session loading now tracks success for each spawnedBy request and passes the authority state to reconciliation. Context tests verify current child retention, stale child removal, and failed-lookup preservation.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: pruning stale agent session rows.
Description check ✅ Passed The description covers the change, rationale, implementation, validation, type, checklist, and screenshot applicability.
Linked Issues check ✅ Passed The implementation addresses issue #372 by pruning stale children, preserving roots and live sessions, and handling lookup failures safely.
Out of Scope Changes check ✅ Passed The changes and tests remain focused on session reconciliation and the stale agent-session behavior described in issue #372.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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

Copy link
Copy Markdown

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: 08cd488ada

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

? baseSessions.filter((session) => {
const key = getSessionKey(session);
if (!key) return false;
if (isSessionEligibleWithoutSpawnedConfirmation(session)) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve non-spawned channel and direct sessions

When a spawnedBy lookup succeeds, this filter removes every inactive base session that is not a root or cron session, including ordinary keys such as agent:main:telegram:direct:123 and agent:reviewer:discord:channel:456 that sessionTree.ts deliberately renders beneath agent roots. Those sessions do not appear in spawned-child results, so successful refreshes now make existing DM/channel conversations disappear from the sidebar; restrict stale pruning to session types that can actually be governed by spawnedBy.

Useful? React with 👍 / 👎.

Comment thread src/contexts/SessionContext.tsx Outdated
return mergeAuthoritativeSessions(
baseSessions,
spawnedResults.map((result) => result.sessions),
{ spawnedByAuthoritative: spawnedResults.some((result) => result.ok) },

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 Preserve sessions for roots whose lookup failed

When there are multiple agent roots and only one sessions.list({ spawnedBy }) call fails, some(result.ok) still marks the combined data authoritative. The failed root contributes an empty child list, causing its inactive children from the full list to be pruned as stale merely because another root's request succeeded; track authority per root or retain sessions belonging to failed roots.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/contexts/SessionContext.tsx`:
- Around line 328-343: Scope spawned-session authority per root instead of using
aggregate success: in src/contexts/SessionContext.tsx lines 328-343, retain each
rootSessionKey with its lookup result and pass per-root success data to
mergeAuthoritativeSessions; in src/features/sessions/sessionReconciliation.ts
lines 73-107, prune base children only when their root lookup succeeded,
preserving children for failed roots. Add unit coverage for mixed
successful/failed roots in src/features/sessions/sessionReconciliation.test.ts
lines 66-80 and integration coverage confirming a child under a failed root
remains visible in src/contexts/SessionContext.test.tsx lines 524-555.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 414e1dc6-debc-450a-b7d2-211c9614ad39

📥 Commits

Reviewing files that changed from the base of the PR and between 312e273 and 08cd488.

📒 Files selected for processing (4)
  • src/contexts/SessionContext.test.tsx
  • src/contexts/SessionContext.tsx
  • src/features/sessions/sessionReconciliation.test.ts
  • src/features/sessions/sessionReconciliation.ts

Comment thread src/contexts/SessionContext.tsx Outdated

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

Copy link
Copy Markdown

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: 28e7b01b87

ℹ️ 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 on lines +117 to +120
const key = getSessionKey(session);
if (!key || seen.has(key)) continue;
if (!isLiveSupplementalSession(session)) continue;
seen.add(key);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve spawned sessions without explicit state fields

When the full list lags after a spawn and the spawnedBy response contains only the session key/label—the API-compatible shape used throughout this repository—isLiveSupplementalSession returns false because all state fields are optional, so this line drops the newly spawned child even though the successful spawnedBy lookup confirms it exists. This regresses the preceding refresh behavior and can make a newly created child disappear from the sidebar until the full list catches up; confirmed spawned rows with unknown state need to remain eligible.

Useful? React with 👍 / 👎.

Comment on lines +106 to +108
const rootKey = getRootAgentSessionKey(key);
if (rootKey && authoritativeSpawnedByRoots && !authoritativeSpawnedByRoots.has(rootKey)) return true;
return Boolean(rootKey && spawnedKeys.has(rootKey));

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 Scope opaque children by their explicit parent

When one root lookup succeeds and another fails, an inactive child with a supported opaque key such as custom-subagent-key and parentSessionKey: 'agent:reviewer:main' reaches this branch with rootKey === null, so it is pruned despite belonging to the failed, non-authoritative root. Fresh evidence in the final tree is that sessionTree.ts explicitly supports opaque keys through parentSessionKey/parentId, while the new preservation logic derives scope only from the key; use the explicit parent candidates to determine the authoritative root as well.

Useful? React with 👍 / 👎.

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.

[Bug] Agents panel keeps stale spawned-session rows after sessions disappear

1 participant