Skip to content

fix(history): resume cross-project sessions & find Codex sessions by payload.id#1058

Open
pedramamini wants to merge 1 commit into
mainfrom
fix/251-history-resume-cross-project-and-codex-payload-id
Open

fix(history): resume cross-project sessions & find Codex sessions by payload.id#1058
pedramamini wants to merge 1 commit into
mainfrom
fix/251-history-resume-cross-project-and-codex-payload-id

Conversation

@pedramamini

@pedramamini pedramamini commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Closes #251

Problem

Two distinct bugs in History → "Open session … as new tab":

Repro A (local Claude Code): Resuming a history entry that belongs to a different local project than the active session failed with [AgentSessions] read error and no tab opened. handleResumeSession always read the stored session from activeSession.projectRoot, so a cross-project entry pointed at the wrong storage path.

Repro B (local Codex): Resuming a Codex history entry opened an empty tab. findSessionFile() only matched the rollout filename UUID or the legacy top-level metadata.id, but new-format Codex sessions record their id under session_metapayload.id. When the session's thread id differed from the filename UUID, the file was never found and the read returned no messages.

Fix

Repro A — Thread the history entry's projectPath from the click site down to the reader:
HistoryEntryItem / HistoryDetailModalHistoryPanelRightPaneluseRightPanelPropshandleResumeSession. The resolved path (projectPath ?? activeSession.projectRoot) is now used for both agentSessions.read and the Claude getSessionOrigins lookup. Both resume affordances (the session pill and the detail-modal Resume button) are covered.

Repro BfindSessionFile() (and findSessionFileRemote() for SSH) now also match metadata.payload?.id, consistent with how the rest of codex-session-storage.ts already extracts session IDs (metadata?.payload?.id || metadata?.id).

Notes

  • The "Extra" suggestion about the {} in the error log is already addressed on current main: withIpcErrorLogging runs the error through serializeError, which extracts name/message/stack for Error instances. The bare {} was from the 0.14.5 reported build.
  • SSH-remote history resume still falls back to local behavior — HistoryEntry does not currently persist an sshRemoteId, so threading that is out of scope here and would need a data-model change.

Tests

  • useAgentSessionManagement: reads a cross-project session from the provided projectPath; falls back to the active project root when none is provided.
  • useRightPanelProps: onOpenSessionAsTab forwards the project path to handleResumeSession's trailing parameter.
  • codex-session-storage: finds a session by session_meta payload.id when the filename UUID differs.
  • Updated existing onOpenSessionAsTab call-signature assertions in HistoryEntryItem / HistoryPanel (unit + integration).

All affected unit + integration tests pass locally; ESLint and Prettier clean on changed files.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved session lookup to correctly match sessions when file metadata differs from filename identifiers
  • New Features

    • Added support for resuming and opening sessions across different projects by preserving project context during session navigation

Review Change Stack

…y payload.id (#251)

Repro A — "Open session … as new tab" (and the detail-modal Resume button)
read the stored session using the active session's projectRoot, so resuming a
history entry that belongs to a different local project read the wrong path and
threw an AgentSessions read error. Thread the history entry's projectPath
through HistoryEntryItem / HistoryDetailModal → RightPanel → useRightPanelProps
→ handleResumeSession and use it for both the agentSessions.read and
getSessionOrigins lookups (falling back to the active project root).

Repro B — Codex resume opened an empty tab because findSessionFile only matched
the rollout filename UUID or the legacy top-level metadata.id, never the
new-format session_meta payload.id. Match payload.id as well (locally and over
SSH), consistent with how the rest of the storage extracts session IDs.

Closes #251
@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b21ad953-4be8-4948-9fca-64674234cc38

📥 Commits

Reviewing files that changed from the base of the PR and between 575efd0 and eb3eed1.

📒 Files selected for processing (13)
  • src/__tests__/integration/HistoryPanel.integration.test.tsx
  • src/__tests__/main/storage/codex-session-storage.test.ts
  • src/__tests__/renderer/components/History/HistoryEntryItem.test.tsx
  • src/__tests__/renderer/components/HistoryPanel.test.tsx
  • src/__tests__/renderer/hooks/useAgentSessionManagement.test.ts
  • src/__tests__/renderer/hooks/useRightPanelProps.test.ts
  • src/main/storage/codex-session-storage.ts
  • src/renderer/components/History/HistoryEntryItem.tsx
  • src/renderer/components/HistoryDetailModal.tsx
  • src/renderer/components/HistoryPanel.tsx
  • src/renderer/components/RightPanel.tsx
  • src/renderer/hooks/agent/useAgentSessionManagement.ts
  • src/renderer/hooks/props/useRightPanelProps.ts

📝 Walkthrough

Walkthrough

This PR extends session resumption to support cross-project scenarios by threading project path through the history flow and updating storage matching to recognize both legacy and newer session-meta payload IDs, resolving failures when opening history entries from different projects.

Changes

Cross-project session resumption

Layer / File(s) Summary
Storage-level dual ID matching
src/main/storage/codex-session-storage.ts, src/__tests__/main/storage/codex-session-storage.test.ts
Local and remote session file discovery now match session_meta.payload.id (newer format) in addition to metadata.id (legacy), enabling Codex sessions identified by thread_id to be correctly resolved.
Hook signature and resolver
src/renderer/hooks/agent/useAgentSessionManagement.ts, src/__tests__/renderer/hooks/useAgentSessionManagement.test.ts
handleResumeSession accepts optional projectPath and usageStats; introduces readProjectPath resolver that uses the provided path or falls back to active session root, applied to both agent session reads and Claude origin lookups.
Component callback signatures
src/renderer/components/History/HistoryEntryItem.tsx, src/renderer/components/HistoryDetailModal.tsx, src/renderer/components/HistoryPanel.tsx, src/renderer/components/RightPanel.tsx
Callback props for onResumeSession and onOpenSessionAsTab updated across all history-related components to accept optional projectPath parameter.
Button wiring and history tests
src/renderer/components/History/HistoryEntryItem.tsx, src/renderer/components/HistoryDetailModal.tsx, src/__tests__/integration/HistoryPanel.integration.test.tsx, src/__tests__/renderer/components/History/HistoryEntryItem.test.tsx, src/__tests__/renderer/components/HistoryPanel.test.tsx
Session entry's projectPath is now passed in resume and open-as-tab button handlers; integration and component tests updated to verify both session ID and project path arguments are forwarded.
Hook adapter bridge
src/renderer/hooks/props/useRightPanelProps.ts, src/__tests__/renderer/hooks/useRightPanelProps.test.ts
Type imports expanded and dependency interface for handleResumeSession extended; adapter wrappers created for onResumeSession and onOpenSessionAsTab that forward session ID and project path to the underlying hook while supplying undefined for intermediate optional parameters.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • RunMaestro/Maestro#699: Related through session_meta.payload.id handling in codex-session-storage.ts around Codex session storage and resume behavior.

Suggested labels

ready to merge

Suggested reviewers

  • chr1syy

Poem

🐰 A session lost across project bounds,
Now finds its path through project grounds.
With dual IDs and paths so clear,
History resumes without a fear!
Cross-project hops, the flow is tight,
Threading project paths just right. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main fixes: cross-project session resume and Codex session lookup by payload.id.
Linked Issues check ✅ Passed The PR addresses both objectives from #251: threading projectPath for cross-project Claude Code resume, and updating findSessionFile to match metadata.payload?.id for Codex sessions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the two issues: projectPath threading through components, session storage lookup updates, and corresponding test updates.

✏️ 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/251-history-resume-cross-project-and-codex-payload-id

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.

@greptile-apps

greptile-apps Bot commented May 30, 2026

Copy link
Copy Markdown

Greptile Summary

Fixes two independent History → "Open session as new tab" bugs: cross-project sessions were read from the wrong storage path (Repro A), and Codex sessions with a thread id that differed from the rollout filename UUID were never found (Repro B).

  • Repro A: projectPath is now threaded from HistoryEntryItem / HistoryDetailModal through HistoryPanelRightPaneluseRightPanelPropshandleResumeSession, which resolves the storage path as projectPath ?? activeSession.projectRoot before calling both agentSessions.read and getSessionOrigins.
  • Repro B: findSessionFile (local) and findSessionFileRemote (SSH) now additionally match metadata.payload?.id, consistent with the existing ID-extraction pattern in codex-session-storage.ts.
  • New unit and integration tests cover the cross-project fallback, the payload-id matching, and the updated call signatures for both resume affordances.

Confidence Score: 4/5

Both bugs are fixed correctly and the changed paths are well tested; the only open gap is the agentId derivation in handleResumeSession, which would silently fail for cross-project sessions resumed from a unified history view containing entries from a different agent type — a scenario not yet reachable in the current UI.

The cross-project path fix and the Codex payload.id fix are straightforward, targeted, and backed by new tests covering the happy path and the fallback. The one concern is that agentId is still read from the active session's tool type rather than the history entry, meaning a future unified-history view resuming a cross-tool entry would silently read from the wrong storage. This is pre-existing behaviour not worsened by the PR, but worth tracking before the unified view ships.

src/renderer/hooks/agent/useAgentSessionManagement.ts — the agentId derivation from activeSession.toolType is the only remaining gap; src/renderer/hooks/props/useRightPanelProps.ts — the two identical handler lambdas are minor but worth cleaning up.

Important Files Changed

Filename Overview
src/renderer/hooks/agent/useAgentSessionManagement.ts Adds optional projectPath parameter to handleResumeSession; resolves read path via projectPath ?? activeSession.projectRoot for both agentSessions.read and getSessionOrigins — fixes Repro A cleanly. agentId still derived from activeSession.toolType, which works for same-tool cross-project but could mismatch in a unified-history-view scenario.
src/main/storage/codex-session-storage.ts findSessionFile and findSessionFileRemote now also match metadata.payload?.id, consistent with the rest of the file's ID extraction pattern — correctly fixes Repro B. Both local and SSH code paths updated symmetrically.
src/renderer/hooks/props/useRightPanelProps.ts onResumeSession and onOpenSessionAsTab are now separate inline lambdas that both forward projectPath as the 6th argument to handleResumeSession. The two lambdas are functionally identical; they could be a single shared helper.
src/renderer/components/History/HistoryEntryItem.tsx Session button now passes entry.projectPath to onOpenSessionAsTab. Safe — the button only renders when entry.agentSessionId is truthy, and projectPath is a required field on HistoryEntry.
src/renderer/components/HistoryDetailModal.tsx Resume button now passes entry.projectPath to onResumeSession, completing the cross-project fix for the detail modal path.
src/tests/renderer/hooks/useAgentSessionManagement.test.ts New tests validate cross-project path threading and fallback to activeSession.projectRoot; both agentSessions.read assertions confirm the correct path is used.
src/tests/main/storage/codex-session-storage.test.ts New test creates a session file whose filename UUID differs from the payload.id thread id, then asserts readSessionMessages returns the correct messages — directly exercises Repro B.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant HEI as HistoryEntryItem
    participant HP as HistoryPanel
    participant RP as RightPanel
    participant URPP as useRightPanelProps
    participant UASM as useAgentSessionManagement
    participant API as agentSessions.read

    U->>HEI: click session pill
    HEI->>HP: onOpenSessionAsTab(sessionId, entry.projectPath)
    HP->>RP: onOpenSessionAsTab(sessionId, projectPath)
    RP->>URPP: onOpenSessionAsTab(sessionId, projectPath)
    URPP->>UASM: handleResumeSession(sessionId, ···, projectPath)
    Note over UASM: readProjectPath = projectPath ?? activeSession.projectRoot
    UASM->>API: read(agentId, readProjectPath, sessionId, opts)
    API-->>UASM: messages
    UASM-->>U: new AI tab opened
Loading

Reviews (1): Last reviewed commit: "fix(history): resume cross-project sessi..." | Re-trigger Greptile

// Use projectRoot (not cwd) for consistent session storage access
// Use the resolved project path so cross-project history entries
// read from their own storage location instead of the active one.
const agentId = activeSession.toolType || 'claude-code';

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 agentId always mirrors the active session's tool type

HistoryEntry carries no toolType field, so the agent type used for agentSessions.read is always derived from activeSession.toolType. For the scenarios this PR targets (same-tool cross-project) that is fine. However, HistoryEntryItem already accepts a showAgentName prop described as "used in unified history view," implying a view where entries from different agent types may coexist. If a Codex entry from a different project is resumed inside a Claude Code agent's panel, agentId will be 'claude-code' and the read will silently fail to find the Codex session. Adding toolType?: ToolType to HistoryEntry and threading it here would close this gap when the unified view lands.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +150 to +170
// History entries can belong to a different local project than the active
// session, so forward the entry's project path to handleResumeSession (its
// trailing parameter) and let it read the stored session from there.
onResumeSession: (agentSessionId: string, projectPath?: string) =>
deps.handleResumeSession(
agentSessionId,
undefined,
undefined,
undefined,
undefined,
projectPath
),
onOpenSessionAsTab: (agentSessionId: string, projectPath?: string) =>
deps.handleResumeSession(
agentSessionId,
undefined,
undefined,
undefined,
undefined,
projectPath
),

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 Duplicated onResumeSession/onOpenSessionAsTab lambdas

Both inline functions have an identical body — they differ only in the key name. A single shared adapter function assigned to both keys would make a future signature change a one-line edit instead of two, and eliminates the risk of the two implementations drifting.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

History “Open session … as new tab” fails for local Claude Code (AgentSessions read error) and opens empty tab for local Codex

1 participant