Files
ArkhAngelLifeJiggyandGitHub 353e306064 feat: add conversation cache and session persistence (#705)
* feat: add conversation cache and session persistence

- ConversationCache: LRU cache for conversation history with TTL
- Session persistence with encrypted save/load
- Cross-device sync support
- Integrated into sessionHistory

* fix: address PR review feedback

- Remove broken XOR encryption - store sessions as plain JSON
- Fix key not being persisted issue
- Integrate cacheSession into fetchLatestEvents for actual use
- Remove dead code: no more unused integration functions
- Use proper config directory path

* test: add unit tests for conversationCache and sessionPersistence

- conversationCache.test.ts: 8 tests (LRU, TTL, get/set, delete/clear)
- sessionPersistence.test.ts: 7 tests (create, save/load, list, delete)

* fix: use getClaudeConfigHomeDir for consistent config path

- Replace custom path logic with getClaudeConfigHomeDir() from envUtils
- Ensures consistency with rest of codebase (122 other usages)

* fix: address PR #705 blockers

* fix: fully address PR #705 blockers

1. Remove dead listPersistedSessions (no consumer)
2. Integrate loadCachedSession + cacheSession into fetchLatestEvents
   - fetchLatestEvents now checks cache first (loadCachedSession)
   - fetchLatestEvents now saves to cache + disk (cacheSession)
3. Add extractSessionId() function for session ID extraction
4. Proper serialization/deserialization with CacheMessage type

* fix: address all non-blocking issues for PR #705

1. Fix O(n) accessOrder - use Map instead of array filtering (O(1))
2. Remove maxMemoryMb - add deprecated function, memory limit not enforced
3. Add test override for session dir - OPENCLAUDE_TEST_SESSIONS_DIR env var

All blockers and non-blockers now addressed.

* fix: address PR #705 remaining blocker

- Add timestamp to CacheMessage for SessionMessage compatibility
- Replace as any with explicit cast for SessionMessage compatibility
- Use serializeToCacheMessage consistently for both cache and persist

* chore: remove PR705 review comment file

* fix: preserve full SDKMessage fields in cache round-trip

- Extend CacheMessage interface with id, type, model, created_at, stop_reason, usage, is_development, index
- serializeToCacheMessage: preserve all relevant fields with type guards
- deserializeFromCacheMessage: restore all preserved fields
- Prevents data corruption on structured message history

* fix: resolve PR 705 blocking issues

- Fix cache-hit returns hasMore:true/firstId:null - now always fetch latest
- Fix deserialize reconstructs structured content from JSON
- Fix extractSessionId uses regex for robustness
- Fix debounce saveSession - only persist on meaningful change (new count)

Fixes reviewer feedback from gnanam1990 and Vasanthdev2004

* fix: use temp test directory in sessionPersistence test

Non-blocking fix: use /tmp/openclaude-test-sessions instead of default
to avoid touching real local state outside CI

* fix: resolve PR 705 remaining blockers

- fetchLatestEvents returns cached immediately for offline/restart support
- Background fetch after returning cached
- cacheSession checks message IDs not just count
- Test uses temp directory

* fix: resolve PR 705 remaining blockers - fetchLatestEvents returns fresh data, fixes firstId

* fix: PR 705 - round-trip content type safety and pagination metadata

Blocking:
- Add contentIsArray flag to track whether content was originally string vs array
- Serializer stores the flag; deserializer uses it instead of heuristic (startsWith '[')
- Prevents corruption of string content like '[]' or '[1,2]' being parsed as JSON

Non-blocking:
- Wire OPENCLAUDE_TEST_SESSIONS_DIR in sessionPersistence.test.ts beforeEach
- Add afterEach to clean up env var
- Store hasMore/lastId metadata in cache, use real values on fallback instead of fabricating hasMore: true

* fix: PR 705 - persist pagination metadata across restarts

- Add pagination field to Session interface for hasMore/lastId
- cacheSession() now saves pagination to persisted session
- loadCachedSession() reconstructs sessionMetadataCache from persisted session
- After restart/offline resume, fetchLatestEvents() returns correct hasMore from saved metadata

* fix: preserve full SDKMessage shape in cache serializer

Add missing type-specific payload fields to serialization/deserialization:
- message (assistant/user/system payload)
- uuid, session_id, parent_tool_use_id, tool_use_result (user messages)
- subtype, result (result/system messages)
- event (stream events)

Previously only role/content were stored, dropping type-specific
payloads needed by convertSDKMessage().

* fix: add error handling to PR intent scan entry point

* fix: persist all SDKMessage variant fields through cache round-trip

- Add error field for SDKAssistantMessage errors (was silently dropping)
- Add errors field for SDKResultMessage error variant (was degrading to 'Unknown error')
- Add status field for SDKStatusMessage ('compacting' was being dropped)
- Add compact_metadata field for SDKCompactBoundaryMessage
- Add tool_name and elapsed_time_seconds fields for SDKToolProgressMessage (was rendering undefined)
- Add 11 regression tests verifying every variant round-trips correctly

Fixes P1: Persisted history still does not round-trip the full SDKMessage union

* fix: persist pagination cursor and use uuid for cache-dirty detection (PR review)
2026-06-01 19:00:25 +08:00
..