fix(codex): recover and read bridged sessions safely#8288
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds support for discovering, reading, parsing, and caching plain and zstd-compressed Codex rollouts, including paginated turn items. Adds filesystem markers, preservation, replacement, migration, refresh, rollback, and WSL bridging for Codex sessions. Updates native chat transcript resolution, decoding limits, caching, and watching for compressed and paginated files, deduplicates skill suggestions by source priority, and expands Codex slash-command suggestions. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main/ai-vault/session-scanner-parse-cache.ts (1)
53-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
isCodexCompressedRolloutPathinstead of inline.endsWith.The inline
.endsWith('.jsonl.zst')duplicatesisCodexCompressedRolloutPathfromsession-scanner-codex-paths.ts, which is already used consistently insession-scanner-codex-rollout-read.tsandtranscript-reader.ts. Using the shared helper keeps the compressed-path detection logic in one place.♻️ Proposed refactor
case 'codex': // Why: byte offsets only describe plain append-only JSONL. Cold zstd // sessions must be decompressed from the beginning after each change. - return candidate.file.path.endsWith('.jsonl.zst') + return isCodexCompressedRolloutPath(candidate.file.path) ? null : () => createCodexSessionResumeState(candidate.file, candidate.codexHome)
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 82c4dfbe-81cb-4445-a9ac-a118675dfdec
📒 Files selected for processing (29)
src/main/ai-vault/session-scanner-codex-discovery.tssrc/main/ai-vault/session-scanner-codex-parser.test.tssrc/main/ai-vault/session-scanner-codex-parser.tssrc/main/ai-vault/session-scanner-codex-paths.tssrc/main/ai-vault/session-scanner-codex-record-consume.tssrc/main/ai-vault/session-scanner-codex-rollout-read.tssrc/main/ai-vault/session-scanner-codex-zstd.test.tssrc/main/ai-vault/session-scanner-discovery.tssrc/main/ai-vault/session-scanner-parse-cache.tssrc/main/ai-vault/session-scanner-source-discovery.tssrc/main/codex/codex-session-bridge-link.tssrc/main/codex/codex-session-bridge.test.tssrc/main/codex/codex-session-bridge.tssrc/main/codex/codex-session-copy-markers.tssrc/main/codex/codex-session-file-listing.tssrc/main/codex/codex-session-preserved-copies.tssrc/main/codex/codex-session-preserved-install.tssrc/main/codex/wsl-codex-session-bridge.test.tssrc/main/codex/wsl-codex-session-bridge.tssrc/main/native-chat/session-file-resolver.test.tssrc/main/native-chat/session-file-resolver.tssrc/main/native-chat/transcript-codex-turn-items.tssrc/main/native-chat/transcript-line-decoders-codex.tssrc/main/native-chat/transcript-reader.test.tssrc/main/native-chat/transcript-reader.tssrc/renderer/src/components/native-chat/native-chat-composer-state.test.tssrc/renderer/src/components/native-chat/native-chat-composer-state.tssrc/shared/native-chat-slash-commands.test.tssrc/shared/native-chat-slash-commands.ts
22db924 to
598ce95
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1fb7c96b-1c75-48d8-ab0b-40450ddd132f
📒 Files selected for processing (36)
src/main/ai-vault/session-scanner-codex-discovery.tssrc/main/ai-vault/session-scanner-codex-parser.test.tssrc/main/ai-vault/session-scanner-codex-parser.tssrc/main/ai-vault/session-scanner-codex-paths.tssrc/main/ai-vault/session-scanner-codex-record-consume.tssrc/main/ai-vault/session-scanner-codex-rollout-read.tssrc/main/ai-vault/session-scanner-codex-zstd.test.tssrc/main/ai-vault/session-scanner-discovery.tssrc/main/ai-vault/session-scanner-parse-cache.tssrc/main/ai-vault/session-scanner-source-discovery.tssrc/main/codex/codex-session-bridge-link.tssrc/main/codex/codex-session-bridge.test.tssrc/main/codex/codex-session-bridge.tssrc/main/codex/codex-session-copy-markers.tssrc/main/codex/codex-session-file-listing.tssrc/main/codex/codex-session-preserved-copies.tssrc/main/codex/codex-session-preserved-install.tssrc/main/codex/wsl-codex-session-bridge.test.tssrc/main/codex/wsl-codex-session-bridge.tssrc/main/native-chat/session-file-resolver.test.tssrc/main/native-chat/session-file-resolver.tssrc/main/native-chat/transcript-codex-turn-items.tssrc/main/native-chat/transcript-line-decoders-codex.tssrc/main/native-chat/transcript-read-cache.test.tssrc/main/native-chat/transcript-read-cache.tssrc/main/native-chat/transcript-reader.test.tssrc/main/native-chat/transcript-reader.tssrc/main/native-chat/transcript-stream-lines.tssrc/main/native-chat/transcript-watch.test.tssrc/main/native-chat/transcript-watch.tssrc/main/runtime/rpc/methods/native-chat.test.tssrc/main/runtime/rpc/methods/native-chat.tssrc/renderer/src/components/native-chat/native-chat-composer-state.test.tssrc/renderer/src/components/native-chat/native-chat-composer-state.tssrc/shared/native-chat-slash-commands.test.tssrc/shared/native-chat-slash-commands.ts
🚧 Files skipped from review as they are similar to previous changes (23)
- src/main/ai-vault/session-scanner-codex-discovery.ts
- src/main/ai-vault/session-scanner-discovery.ts
- src/shared/native-chat-slash-commands.test.ts
- src/main/ai-vault/session-scanner-codex-parser.test.ts
- src/main/native-chat/transcript-line-decoders-codex.ts
- src/renderer/src/components/native-chat/native-chat-composer-state.ts
- src/main/codex/wsl-codex-session-bridge.test.ts
- src/main/ai-vault/session-scanner-codex-rollout-read.ts
- src/main/ai-vault/session-scanner-parse-cache.ts
- src/main/codex/codex-session-preserved-copies.ts
- src/shared/native-chat-slash-commands.ts
- src/renderer/src/components/native-chat/native-chat-composer-state.test.ts
- src/main/ai-vault/session-scanner-codex-zstd.test.ts
- src/main/ai-vault/session-scanner-codex-paths.ts
- src/main/codex/codex-session-file-listing.ts
- src/main/codex/codex-session-preserved-install.ts
- src/main/codex/codex-session-bridge.ts
- src/main/codex/codex-session-copy-markers.ts
- src/main/ai-vault/session-scanner-source-discovery.ts
- src/main/codex/codex-session-bridge-link.ts
- src/main/ai-vault/session-scanner-codex-record-consume.ts
- src/main/native-chat/transcript-codex-turn-items.ts
- src/main/codex/codex-session-bridge.test.ts
598ce95 to
1c0082b
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 703ebb5e-f963-4a93-980d-0b0ab4f8a791
📒 Files selected for processing (37)
src/main/ai-vault/session-scanner-codex-discovery.tssrc/main/ai-vault/session-scanner-codex-parser.test.tssrc/main/ai-vault/session-scanner-codex-parser.tssrc/main/ai-vault/session-scanner-codex-paths.tssrc/main/ai-vault/session-scanner-codex-record-consume.tssrc/main/ai-vault/session-scanner-codex-rollout-read.tssrc/main/ai-vault/session-scanner-codex-zstd.test.tssrc/main/ai-vault/session-scanner-discovery.tssrc/main/ai-vault/session-scanner-parse-cache.tssrc/main/ai-vault/session-scanner-source-discovery.tssrc/main/codex/codex-session-bridge-link.tssrc/main/codex/codex-session-bridge.test.tssrc/main/codex/codex-session-bridge.tssrc/main/codex/codex-session-copy-markers.tssrc/main/codex/codex-session-file-listing.tssrc/main/codex/codex-session-preserved-copies.tssrc/main/codex/codex-session-preserved-install.tssrc/main/codex/wsl-codex-session-bridge.test.tssrc/main/codex/wsl-codex-session-bridge.tssrc/main/native-chat/session-file-resolver.test.tssrc/main/native-chat/session-file-resolver.tssrc/main/native-chat/transcript-codex-turn-items.tssrc/main/native-chat/transcript-line-decoders-codex.tssrc/main/native-chat/transcript-read-cache.test.tssrc/main/native-chat/transcript-read-cache.tssrc/main/native-chat/transcript-reader.test.tssrc/main/native-chat/transcript-reader.tssrc/main/native-chat/transcript-stream-lines.test.tssrc/main/native-chat/transcript-stream-lines.tssrc/main/native-chat/transcript-watch.test.tssrc/main/native-chat/transcript-watch.tssrc/main/runtime/rpc/methods/native-chat.test.tssrc/main/runtime/rpc/methods/native-chat.tssrc/renderer/src/components/native-chat/native-chat-composer-state.test.tssrc/renderer/src/components/native-chat/native-chat-composer-state.tssrc/shared/native-chat-slash-commands.test.tssrc/shared/native-chat-slash-commands.ts
✅ Files skipped from review due to trivial changes (1)
- src/shared/native-chat-slash-commands.ts
🚧 Files skipped from review as they are similar to previous changes (32)
- src/shared/native-chat-slash-commands.test.ts
- src/main/codex/codex-session-file-listing.ts
- src/main/ai-vault/session-scanner-discovery.ts
- src/main/ai-vault/session-scanner-codex-parser.test.ts
- src/main/ai-vault/session-scanner-codex-zstd.test.ts
- src/main/ai-vault/session-scanner-parse-cache.ts
- src/renderer/src/components/native-chat/native-chat-composer-state.ts
- src/renderer/src/components/native-chat/native-chat-composer-state.test.ts
- src/main/native-chat/transcript-line-decoders-codex.ts
- src/main/ai-vault/session-scanner-codex-paths.ts
- src/main/native-chat/transcript-reader.ts
- src/main/native-chat/transcript-read-cache.test.ts
- src/main/ai-vault/session-scanner-source-discovery.ts
- src/main/ai-vault/session-scanner-codex-rollout-read.ts
- src/main/native-chat/transcript-codex-turn-items.ts
- src/main/codex/codex-session-copy-markers.ts
- src/main/codex/codex-session-preserved-copies.ts
- src/main/runtime/rpc/methods/native-chat.test.ts
- src/main/ai-vault/session-scanner-codex-discovery.ts
- src/main/codex/wsl-codex-session-bridge.test.ts
- src/main/native-chat/session-file-resolver.test.ts
- src/main/native-chat/transcript-watch.ts
- src/main/ai-vault/session-scanner-codex-record-consume.ts
- src/main/native-chat/transcript-reader.test.ts
- src/main/runtime/rpc/methods/native-chat.ts
- src/main/native-chat/transcript-stream-lines.ts
- src/main/codex/codex-session-preserved-install.ts
- src/main/native-chat/transcript-read-cache.ts
- src/main/codex/codex-session-bridge-link.ts
- src/main/native-chat/session-file-resolver.ts
- src/main/codex/codex-session-bridge.test.ts
- src/main/ai-vault/session-scanner-codex-parser.ts
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/main/codex/wsl-codex-session-bridge.ts (2)
108-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the sourceSize-clamping semantics.
write_copy_markerdeliberately overwritessource_sizewithtarget_size(line 116) before recording it as"sourceSize"in the marker, andcopy_marker_matchesmirrors this by buildingmarker_prefixwith the currenttarget_sizein place of the real source size (line 132). This means the marker's"sourceSize"field is not the literal size of the source file but the amount actually verified-copied — a non-obvious design choice (accounting for sources that may still be growing) that isn't explained anywhere nearby.As per coding guidelines, "When code is driven by a design document or non-obvious constraint, add a brief one- or two-line comment explaining why it behaves that way, without restating the implementation."
📝 Suggested addition
+ // Why: "sourceSize" records the verified-copied byte count, not the + // literal source size, since the source may still be growing. 'write_copy_marker() {',Source: Coding guidelines
92-208: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoffConsider deduplicating repeated full-file SHA-256 hashing.
A single
refresh_copycall for an existing target can hash the same file content up to ~4 times: once insidecopy_marker_matches, thentarget_fingerprint_before,target_fingerprint_after, andpreserved_fingerprint. For larger rollout files and a periodic sync, this is avoidable I/O — e.g.copy_marker_matchescould surface its computed fingerprint for reuse astarget_fingerprint_beforeinstead of recomputing.Given the safety-critical, already heavily-reviewed nature of this script and that rollout files are typically modest-sized JSONL logs, this is a nice-to-have rather than urgent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 5b123b28-9eff-433f-8168-e72b1465d2da
📒 Files selected for processing (1)
src/main/codex/wsl-codex-session-bridge.ts
a33d90c to
bd57186
Compare
|
Followed up on the current-head review in amended commit I did not deduplicate the repeated SHA-256 calls. That suggestion is explicitly a poor-tradeoff nitpick: these rollout files are normally modest, while threading a cached fingerprint through Focused bridge tests: 9/9; targeted lint/format, max-lines, and diff checks pass under Node 24. |
Summary
Replacement slice for #7950, limited to Codex session discovery, bridge safety, and Native Chat decoding.
.jsonl.zstrollouts; prefer a plain sibling before applying result limitsitem_completedTurnItems for AI Vault and Native ChatWhy this is separate
#7950 mixed startup, hooks, auth/rate limits, and session behavior. This PR contains only the session lifecycle/read path so its data-safety policy can be reviewed independently.
Data-safety tradeoff
An open POSIX file descriptor can continue writing to an old inode after rename. A repeated “check then replace” loop therefore cannot prove that a resumed Codex append was retained.
This slice performs at most one automatic copy refresh/migration and permanently retains:
Once a preserved record exists, automatic refresh stops and logs the path for manual reconciliation. This uses more disk and may require manual cleanup, but it avoids silently overwriting divergent session history.
Transcript byte-safety
StringDecodernow carries incomplete UTF-8 across Buffer chunks and flushes once at EOF. Raw0x0Aboundaries drive JSONL line settlement, so malformed bytes that decode toU+FFFDcannot inflate a persisted watch offset or weaken byte limits. Plain file readers and incremental watches keep data as Buffers until this boundary; the string-stream compatibility path carries a trailing high surrogate across string chunks.Cross-platform notes
Testing
pnpm typecheck:nodeoxlintandoxfmt --checkpnpm check:max-lines-ratchetbash -ngit diff --check origin/main...HEADThe branch is rebased onto current
origin/mainat667e04e72; range-diff preserved the prior two commits and added only the reviewed transcript-byte fix. Full lint is not claimed for the final head because current main has an unrelated localization-catalog failure; all changed files pass targeted lint.Environment note
Final verification used the repository-declared Node 24 toolchain. CI remains the authoritative full-platform verifier.