🤖 refactor: move auto-compaction & auto-retry from frontend to backend#2469
🤖 refactor: move auto-compaction & auto-retry from frontend to backend#2469
Conversation
|
@codex review |
d6c510e to
dd1f8f9
Compare
|
@codex review Rebased onto main and resolved conflicts. All static checks (typecheck, lint, fmt-check) and targeted tests pass. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd1f8f9f16
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Fixed the failing integration tests for backend-driven compaction. All static checks pass locally. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3967369f54
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed P1 feedback:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 136e8a4c4f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
6b5cd3d to
d979a7e
Compare
|
@codex review Addressed P1: non-retryable errors now cancel pending retry timer via Added test coverage for the non-retryable-supersedes-retryable scenario. |
|
@codex review Addressed all 3 review comments:
Also fixed mock AI stream player's abort event ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc6bee46f1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
fc6bee4 to
f4cb628
Compare
|
@codex review Addressed the P1 feedback: Seed usage state before backend pre-send compaction checks — Added Also rebased onto latest |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4cb6281ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed both review comments: P1: Skip duplicate user turn emission — When on-send compaction triggers, the original user message is no longer emitted to the frontend immediately. The follow-up path dispatches it (persisted + emitted) after compaction completes, preventing duplicate prompts. P2: Clear retry status on in-flight disable — |
There was a problem hiding this comment.
💡 Codex Review
mux/src/node/services/agentSession.ts
Lines 1116 to 1120 in 1cfa7ba
sendMessage() persists file/skill snapshot messages before running the on-send compaction check, so when shouldForceCompact is true the deferred user prompt is not written yet but the new snapshots are already part of history. That means the compaction request is executed against a larger context than intended, and large @file snapshots can push an already-near-limit turn over the context window so compaction fails before the follow-up can run. Persist these snapshots only after deciding not to force compact (or attach them with the deferred follow-up path).
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed the latest P1 feedback about snapshot timing during on-send auto-compaction. What changed
Regression coverage
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edffb64901
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed the latest providersConfig feedback:
Also reran local validation ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0de6005353
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed the latest P1:
Local validation rerun and passing: |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f03489c6ca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed the latest two comments:
Local validation rerun and passing: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 288dddecbe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed the idle-compaction synthetic-send feedback.
Local validation rerun and passing: |
|
@codex review Addressed startup auto-retry rerun behavior on transient history-read failures:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 673f68420e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Implemented startup auto-retry history-read backoff to avoid tight rerun loops on persistent I/O failures:
|
|
Codex Review: Didn't find any major issues. Hooray! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66010e13de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 331cec7828
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 05acab326f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79b22c22f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Bravo. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
1 similar comment
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
This function was only used by the frontend RetryBarrier component's manual retry flow. PR #2469 moved retry management to the backend RetryManager, which uses createFreshRetryState and createFailedRetryState instead. The function definition and its tests were left behind as dead code.
This function was only used by the frontend RetryBarrier component's manual retry flow. PR #2469 moved retry management to the backend RetryManager, which uses createFreshRetryState and createFailedRetryState instead. The function definition and its tests were left behind as dead code.
Summary
Moves three autonomous behaviors from the frontend (React hooks + localStorage) to the backend (
AgentSession), making the backend the single source of truth for "what happens next in a turn":RetryManagerinAgentSessionhandles exponential backoff retry of failed streamsIdleCompactionServiceexecutes compaction directly instead of asking the frontendCompactionMonitorinAgentSessionchecks context usage before sending and during streaming, triggering compaction automaticallyBackground
The frontend previously acted as the orchestrator for auto-retry (via
useResumeManager) and auto-compaction (viauseForceCompaction,useIdleCompactionHandler, and pre-send checks inChatInput). This created fragility: retries and compactions didn't survive page reloads, race conditions existed between frontend and backend state, and the frontend contained complex state machines that belong in the backend.Implementation
Phase 1 — Auto-Retry → Backend:
retryEligibility.tsandretryState.tsfrombrowser/tocommon/RetryManagerclass with backoff scheduling, non-retryable error detection, cancel/disposeAgentSession: stream-end →handleStreamSuccess(), stream-error →handleStreamFailure()setAutoRetryEnabledIPC routeauto-retry-scheduled,auto-retry-starting,auto-retry-abandoneduseResumeManagerhook andautoRetryPreferencelocalStorage wrapperPhase 2 — Idle Compaction → Backend:
buildCompactionMessageText()tocommon/utils/compaction/compactionPrompt.tsIdleCompactionServicenow callsworkspaceService.executeIdleCompaction()directlyidle-compaction-started(replacesidle-compaction-needed)useIdleCompactionHandlerhookPhase 3 — On-Send & Mid-Stream Compaction → Backend:
autoCompactionCheck.tsandcontextLimit.tsfrombrowser/tocommon/CompactionMonitorclass with threshold checks and single-trigger-per-stream guardAgentSession: pre-send check synthesizes compaction request with follow-up, mid-stream check interrupts and chains compactionsetAutoCompactionThresholdIPC routeauto-compaction-triggered,auto-compaction-completeduseForceCompactionhook andshouldTriggerAutoCompactionpre-send checkPhase 4 — Cleanup:
getAutoRetryKey,getRetryStateKey)MuxFrontendMetadata→MuxMessageMetadata(no longer frontend-specific)Risks
AgentSessioncrashes mid-retry, state is lost (acceptable: fresh start on restart, same as before)nullin session-side monitor checks (existingSendMessageOptionsdoesn't carry providers config map). A follow-up can thread this for full parity.CompactionWarningis now informational-only (backend handles the action)📋 Implementation Plan
Move Auto-Compaction & Auto-Retry from Frontend to Backend
Context & Why
Today, the frontend (React hooks + localStorage) acts as the orchestrator for two critical autonomous behaviors:
/compactturns (on-send, mid-stream, and idle).This creates fragility: retries and compactions don't survive page reloads, race conditions exist between frontend state and backend state, and the frontend contains complex state machines that belong in the backend. Moving this logic into
AgentSessionmakes the backend the single source of truth for "what happens next in a turn."Evidence
src/browser/hooks/useResumeManager.tsRetryStateto localStoragesrc/browser/utils/messages/retryEligibility.tsisEligibleForAutoRetry()src/browser/utils/messages/retryState.tsmin(1000 * 2^attempt, 60000)src/browser/utils/compaction/autoCompactionCheck.tssrc/browser/hooks/useForceCompaction.tsshouldForceCompact, interrupts stream, triggers compactionsrc/browser/hooks/useIdleCompactionHandler.tsidle-compaction-needed, serializes queue, callssendMessagesrc/browser/utils/chatCommands.tsexecuteCompaction()builds metadata withtype: "compaction-request"and callssendMessagesrc/node/services/agentSession.tsTurnPhase(IDLE→PREPARING→STREAMING→COMPLETING→IDLE),streamWithHistory, and compaction follow-up dispatchsrc/node/services/compactionHandler.tshandleCompletion()already processes compaction results and returnsbooleanto signal follow-up dispatchsrc/node/services/idleCompactionService.tssrc/node/services/streamManager.tsprevious_response_not_foundsrc/common/types/message.tsCompactionRequestData,MuxFrontendMetadata,CompactionFollowUpRequestalready definedsrc/common/orpc/schemas/stream.tsWorkspaceChatMessageunion already includesidle-compaction-neededvariantsrc/browser/stores/WorkspaceStore.tsstream-endArchitecture Overview
The core change:
AgentSessiongains a post-turn decision loop that can chain compaction or retry turns without frontend involvement.The frontend becomes a thin display layer that shows status events ("retrying attempt 2/5…", "auto-compacting…") rather than driving the state machine.
Phase 1: Move Auto-Retry into Backend
Goal:
AgentSessionautomatically retries failed streams with exponential backoff. FrontenduseResumeManageris removed.~350 LoC added (backend), ~400 LoC removed (frontend). Net: −50 LoC.
1.1 Extract shared retry utilities to
src/common/Move pure logic from frontend to common (no DOM/React dependencies):
src/browser/utils/messages/retryEligibility.ts→src/common/utils/messages/retryEligibility.tsisEligibleForAutoRetry()— adapt signature to work withMuxMessage[]instead ofDisplayedMessage[]NON_RETRYABLE_STREAM_ERRORS,NON_RETRYABLE_SEND_ERRORS— export as-isisNonRetryableSendError(),isNonRetryableStreamError()— export as-issrc/browser/utils/messages/retryState.ts→src/common/utils/messages/retryState.tsRetryStateinterface,calculateBackoffDelay(),createFreshRetryState(),createFailedRetryState()createManualRetryState()(manual retries become just anotherresumeStreamcall)Update all existing frontend imports to point to
src/common/....1.2 Add
RetryManagertoAgentSessionNew file:
src/node/services/retryManager.ts1.3 Integrate
RetryManagerintoAgentSessionIn
src/node/services/agentSession.ts:RetryManagerin constructor, wireonRetryto callthis.resumeStream()onStatusChangeto emit new chat events (see §1.4)stream-endhandler (line ~1772): callretryManager.handleStreamSuccess()retryManager.handleStreamFailure(error)interrupt()method: callretryManager.cancel()(user explicitly stopped)setAutoRetryEnabled(enabled: boolean)method, called from IPC when user toggles preference1.4 New chat event types
In
src/common/orpc/schemas/stream.ts, add to theWorkspaceChatMessageunion:1.5 New IPC method for retry preference
In
src/node/orpc/router.ts, add:This replaces the current localStorage-based
autoRetryPreference.ts.1.6 Frontend cleanup
Remove:
src/browser/hooks/useResumeManager.ts— entire filesrc/browser/utils/messages/autoRetryPreference.ts— replaced by IPC callsrc/browser/utils/messages/retryState.ts— moved to common (update imports)src/browser/utils/messages/retryEligibility.ts— moved to common (update imports)WorkspaceStore.ts(line ~438)Update:
src/browser/stores/WorkspaceStore.ts— handle newauto-retry-scheduled/starting/abandonedevents, update workspace state for UI displaysrc/browser/components/Messages/ChatBarrier/RetryBarrier.tsx— show "Backend retrying (attempt N)…" status from store state instead of driving retriesChatPane.tsxor whereveruseResumeManageris mounted — remove the hook callPhase 2: Move Idle Compaction Execution to Backend
Goal: When
IdleCompactionServicedetects an idle workspace, the backend executes the compaction itself instead of asking the frontend.~100 LoC added (backend), ~150 LoC removed (frontend). Net: −50 LoC.
2.1 Backend:
IdleCompactionServiceexecutes directlyIn
src/node/services/idleCompactionService.ts(orworkspaceService.ts):idle-compaction-needed, callagentSession.sendMessage()directly with the compaction metadataexecuteCompactiondoes in the frontend — constructmuxMetadatawithtype: "compaction-request"andsource: "idle-compaction"AsyncMutexinStreamManager(natural serialization — only one stream per workspace at a time)IdleCompactionServiceitself (mirrors thequeueReflogic fromuseIdleCompactionHandler)2.2 New chat event for status
Add to
WorkspaceChatMessage:The existing
idle-compaction-neededevent can be kept for backwards compatibility (frontend shows a notification) or removed if the frontend no longer needs to know about idle compaction until it's happening. Recommend: replace withidle-compaction-startedso the frontend can show "Compacting idle workspace…" status.2.3 Build compaction prompt in backend
Extract from
src/browser/utils/chatCommands.ts(prepareCompactionMessage):buildCompactionPrompt) — move tosrc/common/utils/compaction/orsrc/node/services/muxMetadatawithtype: "compaction-request",source,displayStatus) — this is already defined insrc/common/types/message.ts, so the backend can construct it directly2.4 Frontend cleanup
Remove:
src/browser/hooks/useIdleCompactionHandler.ts— entire fileuseIdleCompactionHandlerfrom the app root componentidle-compaction-neededevent handling fromWorkspaceStore.ts(or replace withidle-compaction-startedhandler)Phase 3: Move On-Send & Mid-Stream Compaction to Backend
Goal: The backend automatically compacts when context usage exceeds thresholds, without frontend involvement. This is the largest change.
~250 LoC added (backend), ~300 LoC removed (frontend). Net: −50 LoC.
3.1 Extract compaction threshold logic to
src/common/Move from
src/browser/utils/compaction/autoCompactionCheck.tstosrc/common/utils/compaction/autoCompactionCheck.ts:checkAutoCompaction()— adapt to take raw token counts + model info rather than frontend store typesgetContextTokens()— pure math, moves as-isgetEffectiveContextLimit()fromsrc/browser/utils/compaction/contextLimit.ts— already only depends on model stats, move to common3.2 Add
CompactionMonitortoAgentSessionNew file:
src/node/services/compactionMonitor.ts3.3 Integrate into
AgentSessionIn
src/node/services/agentSession.ts:On-send compaction:
sendMessage(), before callingstreamWithHistory(), callcompactionMonitor.checkBeforeSend()followUpContent(same as the frontend'sexecuteCompactiondoes today), then callstreamWithHistory()with the compaction requestcompactionHandler.handleCompletion()→dispatchPendingFollowUp()flow handles sending the original message after compaction finishes — this already works todayMid-stream compaction:
usage-deltaevent handler, callcompactionMonitor.handleUsageDelta()this.interrupt()), then chain a compaction turn followed by a[CONTINUE]resume — reusing the existing compaction-then-follow-up infrastructure3.4 Threshold preference via IPC
In
src/node/orpc/router.ts, add:The frontend's
ContextUsageIndicatorButtonslider calls this instead of persisting locally. The backend stores the threshold onAgentSession(or a workspace config object).3.5 New chat events
3.6 Frontend cleanup
Remove:
src/browser/hooks/useForceCompaction.ts— entire filesrc/browser/components/ChatInput/index.tsx— theshouldTriggerAutoCompactionpre-send checkautoCompactionResultcomputation inChatPane.tsx— no longer needed for driving compaction (may still be needed for the warning banner UI)src/browser/utils/compaction/autoCompactionCheck.ts— moved to common (update imports)Keep (modified):
CompactionWarning.tsx— can still show "context usage is high" based onusage-deltaevents from the store, but it becomes informational rather than actionable (the backend handles it automatically)ContextUsageIndicatorButton— slider still exists, but calls IPC to set threshold on backend instead of persisting locallyPhase 4: Clean Up & Polish
~Net −100 LoC (removing dead code, consolidating types).
4.1 Remove dead frontend-to-backend compaction path
sendMessagewithtype: "compaction-request"metadata — all compaction is backend-initiatedexecuteCompactionandprepareCompactionMessagefromsrc/browser/utils/chatCommands.tsformatCompactionCommandLineif no longer used4.2 Consolidate types
MuxFrontendMetadatainsrc/common/types/message.ts— the"compaction-request"variant is now backend-internal; consider renaming toMuxMessageMetadatasince it's no longer frontend-specificCompactionFollowUpRequest— now constructed entirely by the backend4.3 Update tests
RetryManager(backoff, non-retryable errors, cancel) andCompactionMonitor(threshold checks, mid-stream trigger, reset)AgentSessionauto-retries on transient errors and auto-compacts on threshold breachuseResumeManager,useForceCompaction, oruseIdleCompactionHandler4.4 Manual user-triggered
/compactstill worksThe user can still type
/compactin chat input → the frontend sends it as a regularsendMessagewithtype: "compaction-request"metadata → the backend processes it as before. This path doesn't change.Execution Order & Dependencies
graph TD A["Phase 1: Auto-Retry → Backend"] --> C["Phase 3: On-Send + Mid-Stream Compaction → Backend"] B["Phase 2: Idle Compaction → Backend"] --> C C --> D["Phase 4: Cleanup & Polish"] style A fill:#4CAF50,color:#fff style B fill:#4CAF50,color:#fff style C fill:#FF9800,color:#fff style D fill:#2196F3,color:#fffRisk Mitigation
interrupt()already cancels active streams;RetryManager.cancel()clears pending timers. Frontend sends interrupt IPC as before.RetryManagerstate is in-memory only; on crash,AgentSessionrestarts fresh. The existingresumeStreampath on app restart handles recovery.StreamManagermutex ensures one stream per workspace. Queued user messages are held until the compaction turn completes (existingsendQueuedMessagesbehavior).Total Estimated Impact
Generated with
mux• Model:anthropic:claude-opus-4-6• Thinking:xhigh• Cost:$4.01