🤖 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".
f3275cd to
04d9feb
Compare
|
@codex review Added a follow-up stabilization for CI parity:
Local validation still passes ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 04d9feb6e4
ℹ️ 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 two latest review threads:
Also added regression tests for both paths and reran Generated version.ts: v0.17.1-nightly.76-46-ge51161dc2 (e51161d) at 2026-02-19T07:46:17Z Checking npm package CLI completeness... Checking CLI subcommand imports (bun, lockfile-free)... |
|
@codex review Follow-up: previous comment had shell-escaped formatting issues. This push addresses both latest threads (full-replay retry-state reset + boundary-aware usage seeding) with regression tests and green static-check. |
|
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". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e51161dc28
ℹ️ 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 in AskUserQuestionToolCall:
Validation: targeted browser/node tests + |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ef78550273
ℹ️ 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 Follow-up fix for the latest P1:
Validation: targeted browser tests + |
|
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 |
|
Codex Review: Didn't find any major issues. What shall we delve into next? ℹ️ 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: 80d64f1f04
ℹ️ 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 latest P1 on on-send compaction semantics:
Added regression test coverage in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f37a3d06aa
ℹ️ 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 latest P1 in RetryBarrier manual retry flow:
Validation: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9720b9214
ℹ️ 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 latest P2 on teardown safety for temporary ask-user auto-retry enablement:
Validation: new ask-user test + retry barrier test + auto-compaction test + |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 96750fe692
ℹ️ 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 latest P2 follow-ups:
Validation: targeted ask-user + retry-barrier + auto-compaction tests, and |
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