perf(opencode): load session history once per turn, append the id-tail per step

Closed
PI
2mo ago
+36-3

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 (createUserMessage runs 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 msgs across steps in the turn runLoop; 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 single runLoop.
  • Preserves mid-turn queued user messages: the id-tail fetch picks them up.
  • No schema or API changes; no migration required. Monitor history_ms to validate the perf win.