perf(opencode): load session history once per turn, append the id-tail per step
Issue for this PR
Issues are disabled on this fork; motivation below. Stacked on #25 (phase telemetry); re-target after it merges.
Type of change
- Refactor / code improvement
What does this PR do?
runLoop reloaded and re-hydrated the entire session history from SQLite on every step (filterCompactedEffect inside the while loop) ā O(history Ć steps) I/O per turn, the largest per-step prep cost on long sessions. The runner lock makes a turn the session's single writer, so the loop can reuse what it already loaded.
Now the first iteration loads history as before; each later iteration fetches only rows with id greater than the last-seen id (new MessageV2.after, one indexed query ā ids are monotonic per session) and appends. Two correctness guards, both traced against real behaviors:
- A queued follow-up prompt writes its user message mid-turn (
createUserMessageruns before the runner parks); today's per-step reload picks it up, and the id-tail query preserves exactly that. - If the fresh tail contains any compaction part or summary assistant, the retained-window computation (
filterCompacted) is no longer append-stable, so the loop recomputes from scratch.
The cache is captured after SessionReminders.apply so reminder transforms persist into it, and it lives only for one runLoop invocation ā cross-turn behavior is untouched.
How did you verify your code works?
The existing multi-step, subtask, question, and compaction suites all depend on correct cross-step history threading and are the golden tests here: prompt.test.ts + compaction.test.ts + message-v2.test.ts 148/148, full packages/opencode suite 3129 pass with the only failures being the pre-existing Truncate date bug (fixed in #24, not in this branch's ancestry) and a pty test that passes 9/9 in isolation (load-contention flake). bun run typecheck clean, bun run lint 0 errors. Fleet-wide before/after comes free from #25's history_ms field.
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
š¤ Generated with Claude Code
Summary by cubic
Loads session history once per turn and appends newly created messages by id on subsequent steps, replacing per-step full reloads. This removes O(history Ć steps) I/O in long turns while preserving correctness and cross-turn behavior.
- Adds
MessageV2.after(sessionID, id)to fetch rows with id > last seen (ids are monotonic per session). - Caches
msgsacross steps in the turnrunLoop; if fresh rows include any compaction part or an assistant summary, recomputes the retained window from scratch. - Captures the cache after
SessionReminders.apply, so reminder transforms persist; cache lives for a singlerunLoop. - Preserves mid-turn queued user messages: the id-tail fetch picks them up.
- No schema or API changes; no migration required. Monitor
history_msto validate the perf win.