Skip to content

fix(session): avoid repeated full-history summary loads during multi-step loops#21762

Open
sjawhar wants to merge 1 commit intoanomalyco:devfrom
sjawhar:fix/session-summary-hot-loop
Open

fix(session): avoid repeated full-history summary loads during multi-step loops#21762
sjawhar wants to merge 1 commit intoanomalyco:devfrom
sjawhar:fix/session-summary-hot-loop

Conversation

@sjawhar
Copy link
Copy Markdown

@sjawhar sjawhar commented Apr 9, 2026

Issue for this PR

Closes #21761

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Fixes a high-impact session performance bug where tool-using sessions repeatedly reloaded the full session history during a single user prompt.

Root cause:

  • SessionProcessor called SessionSummary.summarize(...) inside the finish-step handler
  • finish-step runs once per assistant model turn, not once per overall user prompt
  • SessionSummary.summarize(...) loads the full session history with sessions.messages({ sessionID })
  • Tool-using agents can take many model turns in one prompt, so this caused repeated full-history hydrations and multi-GB RSS growth

Fix:

  • remove the processor-side SessionSummary.summarize(...) call from session/processor.ts
  • keep summary responsibility at the prompt-loop level instead of per-step
  • add a cheap guard in session/summary.ts to return early when the target user message already has summary.diffs

How did you verify your code works?

  • cd packages/opencode && bun test test/session/prompt-effect.test.ts --test-name-pattern 'loop continues when finish is tool-calls|loop continues when finish is stop but assistant has tool parts|loop summarizes once across a multi-step tool run'
    • 3 pass, 0 fail
  • cd packages/opencode && bun test test/session/processor-effect.test.ts
    • 12 pass, 0 fail
  • cd packages/opencode && bun typecheck
    • pass

Regression evidence

New regression test went red → green:

  • before fix: summaries = 3
  • after fix: summaries = 1

That reproduces the bug directly in a two-step tool loop and proves the processor-side summary call was the repeated trigger.

Live diagnostic evidence

Labeled diagnostic binary showed the bad sequence in a real frozen session:

  • prompt loop entry: 4
  • summary start: 4
  • summary loaded messages: 4
  • prompt loop summary trigger: 1
  • prune start: 0
  • session messages full: 10
  • message hydrate: 45

This proved:

  • prompt loop re-entry was happening during multi-step tool execution
  • repeated full-history loads were coming from summary, not prune
  • the processor-side summary caller explained the extra repeated loads beyond the one prompt-loop trigger

Notes

  • This bug likely contributed to both standalone TUI freezes and shared serve memory drift on long-running tool sessions
  • Windows e2e remains flaky on dev; unrelated to this change

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

fairyhunter13 added a commit to fairyhunter13/opencode that referenced this pull request Apr 10, 2026
Port anomalyco/sst upstream dev's proven sync architecture to permanently
fix the flaky "blank subagent view" bug where ctrl+x down into a running
subagent showed an empty content area until completion.

sync.tsx:
- Remove `force` parameter from session.sync()
- Always mark fullSyncedSessions after fetch (drop time.completed gate)
- Remove redundant session.created handler (session.updated upserts)
- Match upstream/dev byte-for-byte in sync-relevant sections

routes/session/index.tsx:
- Task component: createEffect -> onMount with empty-messages guard
- Route-level sync: drop force=true argument

Preserved defensively (ahead of upstream):
- worker.ts + server/routes/event.ts still use GlobalBus.on("event", ...)
  as defense-in-depth on top of InstanceState shared PubSub
- worker.ts WASM stdout/stderr capture to suppress Aborted() TUI corruption
- worker.ts per-directory filter in GlobalBus.on handler

provider.ts:
- Raise FLOOR_CEILINGS.critical from 4 to 10 for non-Anthropic providers
- Expand isAnthropicParent to also match parentModelID containing "claude"
  (covers claude-via-proxy cases)

transform.ts:
- Preserve assistant messages with reasoning blocks verbatim to avoid
  Anthropic 400 "thinking blocks cannot be modified" (ref upstream PR anomalyco#21370)
- Enable 4-BP cache strategy for GitHub Copilot Claude models

llm.ts:
- Use 5m TTL for short-lived subagent BP2 cache breakpoint; 1h for long-lived

processor.ts:
- Remove duplicate SessionSummary.summarize() call that triggered full-history
  DB hydration on every LLM step in multi-step tool loops (ref upstream anomalyco#21762)

Tests:
- task-escalation.test.ts: update FLOOR_CEILINGS critical assertions to
  match new ceiling (critical > opus, toBe(10)); expand coverage
- cache-max-efficiency-e2e.test.ts: new E2E test validating Anthropic prompt
  cache hit rate across multi-turn conversations

Skill:
- Add opencode-prompt-cache-maximization skill (CLAUDE.md)

Submodule:
- Bump cmd/opencode-search-engine to include idle CPU perf improvements

Root cause: our prior fullSyncedSessions + time.completed guard raced
with Task.createEffect's fire-and-forget sync(id, force=false) -- first
fetch at T=0 returned 0 messages, session was never marked synced,
and no re-sync trigger existed, leaving the view blank until the
subagent finished.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: SessionSummary runs on every processor finish-step and repeatedly loads full session history

1 participant