fix: paginate thread history when forking and loading sessions - #481
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Cursor-cycle detection is required to prevent malformed pagination responses from causing an infinite loop.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds cursor-based thread-history pagination while preserving replay and fork-point matching.
Changes:
- Loads full turns chronologically via paginated
thread/turns/list. - Excludes turns from resume, fork, and audit-fork responses.
- Adds pagination, failure, and fingerprint-matching tests.
File summaries
| File | Description |
|---|---|
src/SessionFork.ts |
Resolves fork points from paginated history. |
src/CodexAppServerClient.ts |
Implements pagination, but repeated or cyclic cursors can cause an infinite request loop. |
src/CodexAcpClient.ts |
Uses metadata-only resume and fork responses. |
src/__tests__/CodexACPAgent/thread-history.test.ts |
Tests pagination and failure handling. |
src/__tests__/CodexACPAgent/load-session.test.ts |
Updates session-loading mocks and assertions. |
src/__tests__/CodexACPAgent/data/paginated-thread-history.json |
Provides expected paginated-history data. |
src/__tests__/CodexACPAgent/CodexAcpClient.test.ts |
Tests resume, fork, and fingerprint behavior. |
src/__tests__/CodexACPAgent/agent-file-change-report.test.ts |
Verifies metadata-only audit forks. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const turns: ThreadReadResponse["thread"]["turns"] = []; | ||
| let cursor: string | null = null; | ||
| do { | ||
| const page = await this.threadTurnsList({ | ||
| threadId, | ||
| cursor, | ||
| limit: 50, | ||
| sortDirection: "asc", | ||
| itemsView: "full", | ||
| }); | ||
| turns.push(...page.data); | ||
| cursor = page.nextCursor; | ||
| } while (cursor !== null); |
There was a problem hiding this comment.
🟡 Changes recommended
Two moderate issues prevent history pagination from providing a consistent snapshot.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 2
- Review effort level: Balanced
| threadId: response.thread.id, | ||
| includeTurns: true, | ||
| }); | ||
| const historyResponse = await this.codexClient.threadReadWithHistory(response.thread.id); |
| threadId, | ||
| cursor, | ||
| limit: 50, | ||
| sortDirection: "asc", |
There was a problem hiding this comment.
🟡 Changes recommended
Legacy session history must retain its required full-read path before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Balanced
| async threadReadWithHistory(threadId: string): Promise<ThreadReadResponse> { | ||
| const response = await this.threadRead({threadId}); | ||
| const turns = await this.threadReadHistory(threadId); | ||
| return {...response, thread: {...response.thread, turns}}; | ||
| } |
Forking and loading paginated threads emitted repeated “Full-history hydration is deprecated” warnings because the adapter requested complete history through
thread/read,thread/fork, andthread/resume.Load history through descending
thread/turns/listpages withitemsView: "full", then reverse the turns for chronological replay. Paginated session loads use the resume response'sturnsBackwardsCursoras the upper boundary; a null resume cursor means empty durable history. Standalone reads and AIR fork-point resolution start at the newest page so newly appended turns do not enter subsequent pages. Legacy histories usethread/read(includeTurns: true)instead of pagination, avoiding repeated rollout reconstruction and dependence on the paging API. Repeated or cyclic cursors fail explicitly.Use
excludeTurns: truefor session resume, session fork, and temporary audit forks. Preserve complete items, their order within each turn, and AIR fork-point matching across pages.Validation:
npm run typecheckandnpm run buildpassed.npm test: 552 passed, 26 skipped.