You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Every pass with a thinking part deep-clones the whole message array for a retry that almost never runs #798
What happens (main a5c029921):transform.ts:821-829 checks whether any message has a reasoning, thinking or redacted_thinking part. If one does, it calls cloneKeepingCoveredInstructionMarks(messages), which is structuredClone of the whole message array (host-served-rows.ts:159). On a Claude session with thinking enabled, that is every pass. The copy is used only by the signed-thinking fit retry at transform.ts:3295-3317. That retry needs all three:
an active signed-thinking prefix hold (activeThinkingPrefixHold, set at :1830);
a final-wire estimate above the boundary limit;
no retry already running for the session.
Otherwise the copy is dropped.
Measurements:
Host profile: one OpenCode 2 server process serving about 20 sessions, 0.49.22, 120 s CPU profile. The main thread was about 66% busy, with 81 event-loop lags over 250 ms (max 998 ms). structuredClone under cloneKeepingCoveredInstructionMarks was 8.0% of the JS thread, the largest single Magic Context entry.
Cost per pass: Bun structuredClone of real message tails (the rows after the newest compaction row, parsed from the store), 7 runs each:
Tail rows
JSON size
Median clone
808
3.6 MB
13 ms
1,384
4.8 MB
18 ms
1,537
7.6 MB
31 ms
This runs synchronously on every LLM step of every such session.
Benefit: about 10,400 transform passes over four days of logs on that host contain 0 fit_retry lines; the retry's rebuild logs origin=fit_retry. So in that window every copy was thrown away.
Possible cheaper paths:
Take the copy only when the retry is possible. The hold is known at :1830. If no stage mutates message content between :829 and :1830, the snapshot can move there and be gated on activeThinkingPrefixHold. It could also be gated on pre-pass usage being near the limit. (We haven't checked whether anything mutates in that range.)
Copy on write. Shallow-copy the array and the message objects, and clone a part only when a stage changes it in place.
No snapshot on OpenCode 2. Rebuild the retry input from the host store instead.
Same class as #703 (a deep clone on every pass), at a different site.
Thanks for the careful measurements. Confirmed on current master: when any message carries a reasoning, thinking or redacted_thinking part, transform.ts deep-clones the whole message array up front (cloneKeepingCoveredInstructionMarks, transform.ts:821-829). The only reader of that copy is the signed-thinking fit retry (transform.ts:3295-3317), which needs an active signed-thinking hold, a final-wire estimate over the limit, and no retry already running. So on a Claude session with thinking enabled, every pass pays for the copy, and nearly every copy is thrown away.
The retry restores those exact input messages, so the fix has to keep that input byte-identical whenever the retry can run. Of your options, taking the snapshot only once a retry is possible, or copying on write, both fit. We'll check which stages change messages before the hold is decided before choosing between them, and I'll update this issue when the fix is scheduled.
Activity
magic-alfonso commentedon Oct 27, 2026
Thanks for the careful measurements. Confirmed on current master: when any message carries a
reasoning,thinkingorredacted_thinkingpart,transform.tsdeep-clones the whole message array up front (cloneKeepingCoveredInstructionMarks,transform.ts:821-829). The only reader of that copy is the signed-thinking fit retry (transform.ts:3295-3317), which needs an active signed-thinking hold, a final-wire estimate over the limit, and no retry already running. So on a Claude session with thinking enabled, every pass pays for the copy, and nearly every copy is thrown away.The retry restores those exact input messages, so the fix has to keep that input byte-identical whenever the retry can run. Of your options, taking the snapshot only once a retry is possible, or copying on write, both fit. We'll check which stages change messages before the hold is decided before choosing between them, and I'll update this issue when the fix is scheduled.