What happened?
sentMessages() hashes every user and toolResult message. A user message whose content array is
empty carries nothing to transmit and is transient — present for one provider call, gone by the next.
Because it is hashed, its removal shifts every later index, commonPrefixLength stops short of
sentCount, and the turn resolves to sent_stream_diverged: the session forks and re-sends the whole
history even though the conversation never changed.
Captured on 2026.8.9-2 from two consecutive provider calls of one session, both 203 messages:
call N [200] toolResult bash len=159 sha b518dd86
[201] user len=0 sha 12ae32cb <- content: []
[202] user len=8194 sha 5c1ea814
call N+1 [200] toolResult bash len=159 sha b518dd86
[201] user len=8194 sha 5c1ea814 <- shifted up one slot
[202] toolResult bash len=3191 sha aec978fe
Index 200 is byte-identical. Only the empty message disappears, and the next decision is
flatten / sent_stream_diverged at 203 messages.
Cost on one affected turn: cacheRead collapsed 582,409 -> 61,594 with cacheWrite 524,339, roughly 8x
its neighbouring turns. That session logged 12 sent_stream_diverged events in 12 minutes.
This is independent of #691 — it reproduces on 5275da1f5, after #751 merged.
Steps to reproduce
#791 carries the regression. To see the failure on unpatched main, check the PR out and revert only
the production change:
gh pr checkout 791
git checkout main -- packages/coding-agent/src/core/extensions/builtin/claude-sdk-oauth/session-sync.ts
cd packages/coding-agent
npx vitest run test/suite/regressions/790-claude-sdk-oauth-empty-user-continuity.test.ts
Observed:
× excludes a content-less user message from the transmitted set
expected length 2, received 3
× stays a delta when a transient content-less user message disappears
- "kind": "delta", "reason": null
+ "kind": "fork", "reason": "sent_stream_diverged"
Tests 2 failed | 2 passed (4)
Expected: Tests 4 passed (4).
Restore the fix with git checkout - -- packages/coding-agent/src/core/extensions/builtin/claude-sdk-oauth/session-sync.ts.
Expected behavior
A transient message carrying nothing to transmit should not participate in the continuity hash, so an
unchanged conversation stays a delta.
Genuine divergence must keep failing closed: a real rewrite of already-sent history should still report
sent_stream_diverged. The two passing controls in that test file cover both directions.
Version
@code-yeongyu/senpi 2026.8.9-2 (observed) · reproduced on main @ 5275da1f5
@anthropic-ai/claude-agent-sdk 0.3.220
provider claude-sdk-oauth · model claude-opus-5 · thinking high
I have a fix in #791. Written with an LLM; the reproduction, measurements, and verification are mine.
What happened?
sentMessages()hashes everyuserandtoolResultmessage. A user message whosecontentarray isempty carries nothing to transmit and is transient — present for one provider call, gone by the next.
Because it is hashed, its removal shifts every later index,
commonPrefixLengthstops short ofsentCount, and the turn resolves tosent_stream_diverged: the session forks and re-sends the wholehistory even though the conversation never changed.
Captured on
2026.8.9-2from two consecutive provider calls of one session, both 203 messages:Index 200 is byte-identical. Only the empty message disappears, and the next decision is
flatten / sent_stream_divergedat 203 messages.Cost on one affected turn:
cacheReadcollapsed 582,409 -> 61,594 withcacheWrite524,339, roughly 8xits neighbouring turns. That session logged 12
sent_stream_divergedevents in 12 minutes.This is independent of #691 — it reproduces on
5275da1f5, after #751 merged.Steps to reproduce
#791 carries the regression. To see the failure on unpatched
main, check the PR out and revert onlythe production change:
gh pr checkout 791 git checkout main -- packages/coding-agent/src/core/extensions/builtin/claude-sdk-oauth/session-sync.ts cd packages/coding-agent npx vitest run test/suite/regressions/790-claude-sdk-oauth-empty-user-continuity.test.tsObserved:
Expected:
Tests 4 passed (4).Restore the fix with
git checkout - -- packages/coding-agent/src/core/extensions/builtin/claude-sdk-oauth/session-sync.ts.Expected behavior
A transient message carrying nothing to transmit should not participate in the continuity hash, so an
unchanged conversation stays a
delta.Genuine divergence must keep failing closed: a real rewrite of already-sent history should still report
sent_stream_diverged. The two passing controls in that test file cover both directions.Version
I have a fix in #791. Written with an LLM; the reproduction, measurements, and verification are mine.