fix(compaction): recover prepared suffix after summary timeout - #646
fix(compaction): recover prepared suffix after summary timeout#646madgegja wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
1 issue found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/coding-agent/src/core/extensions/builtin/compaction/deterministic-fallback.ts">
<violation number="1" location="packages/coding-agent/src/core/extensions/builtin/compaction/deterministic-fallback.ts:89">
P2: The retained-fit gate now sums content-based estimateTokens instead of using the provider-recorded assistant usage. This is the right fix for the stale-usage overcount, but estimateTokens undercounts base64-heavy assistant content: for assistant role it adds raw text/thinking length, whereas for user/toolResult/bashExecution it applies the 4x base64 weighting (weightedChars). A retained suffix whose bulk is a base64-laden assistant message (inline screenshot or dense payload in assistant text/thinking) can therefore pass the `retainedTokens <= contextWindow - reserveTokens` check even though the provider would overflow, contradicting the fail-closed 'preserves the latest request' guarantee this recovery is meant to enforce. Consider weighting base64 runs in the assistant text/thinking arms of estimateTokens (or applying weightedChars there) before relying on it for this safety boundary.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| buildSessionContext([...branchEntries, syntheticCompaction]).messages, | ||
| ).tokens; | ||
| const retainedTokens = buildSessionContext([...branchEntries, syntheticCompaction]).messages.reduce( | ||
| (tokens, message) => tokens + estimateTokens(message), |
There was a problem hiding this comment.
P2: The retained-fit gate now sums content-based estimateTokens instead of using the provider-recorded assistant usage. This is the right fix for the stale-usage overcount, but estimateTokens undercounts base64-heavy assistant content: for assistant role it adds raw text/thinking length, whereas for user/toolResult/bashExecution it applies the 4x base64 weighting (weightedChars). A retained suffix whose bulk is a base64-laden assistant message (inline screenshot or dense payload in assistant text/thinking) can therefore pass the retainedTokens <= contextWindow - reserveTokens check even though the provider would overflow, contradicting the fail-closed 'preserves the latest request' guarantee this recovery is meant to enforce. Consider weighting base64 runs in the assistant text/thinking arms of estimateTokens (or applying weightedChars there) before relying on it for this safety boundary.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/coding-agent/src/core/extensions/builtin/compaction/deterministic-fallback.ts, line 89:
<comment>The retained-fit gate now sums content-based estimateTokens instead of using the provider-recorded assistant usage. This is the right fix for the stale-usage overcount, but estimateTokens undercounts base64-heavy assistant content: for assistant role it adds raw text/thinking length, whereas for user/toolResult/bashExecution it applies the 4x base64 weighting (weightedChars). A retained suffix whose bulk is a base64-laden assistant message (inline screenshot or dense payload in assistant text/thinking) can therefore pass the `retainedTokens <= contextWindow - reserveTokens` check even though the provider would overflow, contradicting the fail-closed 'preserves the latest request' guarantee this recovery is meant to enforce. Consider weighting base64 runs in the assistant text/thinking arms of estimateTokens (or applying weightedChars there) before relying on it for this safety boundary.</comment>
<file context>
@@ -85,9 +85,10 @@ export function createRequiredCompactionFallback(
- buildSessionContext([...branchEntries, syntheticCompaction]).messages,
- ).tokens;
+ const retainedTokens = buildSessionContext([...branchEntries, syntheticCompaction]).messages.reduce(
+ (tokens, message) => tokens + estimateTokens(message),
+ 0,
+ );
</file context>
Summary
Regression evidence
The affected 272K-context session retained about 20,105 tokens after compaction, but the previous estimator reused a 252,607-token assistant usage snapshot and reported 255,699 tokens, 83 above the 255,616 threshold. The rebuilt branch accepts that real prepared suffix and still rejects a genuinely unsafe 20,000-token window.
Validation
vitest --runon the three changed compaction suites: 39 tests passedtsgo --noEmitnpm run build019fbd3f-bb18-7329-81f8-6baf64c25bdaagainst the built artifactSummary by cubic
Recover prepared suffix after summarization timeout by computing fit from retained message content instead of stale assistant usage. Keep final provider result settlement inside the 120s watchdog and apply the same budget across the full extension compaction operation to prevent stuck sessions.
pre_promptrecovery only at the hard input cap; optional pre-prompt below the cap stays fail-closed; latest request is preserved.responseStream.result()in the 120s budget; abort withStreamDurationBudgetErrorwhen the final result lingers; extension compaction now bounds credential resolution and stream setup under the same budget, with caller abort taking precedence.Written for commit d33c1cb. Summary will update on new commits.