fix(coding-agent): preserve plan exit rollback state

This commit is contained in:
Victor Araújo
2026-07-18 16:33:45 -03:00
parent 39c95e5e29
commit 8a8ff498b3
2 changed files with 84 additions and 12 deletions
@@ -2458,26 +2458,50 @@ export class InteractiveMode implements InteractiveModeContext {
}
const planModeState = this.session.getPlanModeState();
const planModeTools = this.session.getEnabledToolNames();
const planModeModelState = this.session.model
? { model: this.session.model, thinkingLevel: this.session.configuredThinkingLevel() }
: undefined;
this.session.setPlanModeState(undefined);
try {
if (this.#planModePreviousTools !== undefined) {
await this.session.setActiveToolsByName(this.#planModePreviousTools);
}
if (this.#planModePreviousModelState) {
if (!options?.deferModelRestore) {
await this.#restorePlanPreviousModel(this.#planModePreviousModelState);
}
// If #applyPlanModeModel queued a deferred switch to the plan-role model
// (because the session was streaming on entry), drop it now: we are
// leaving plan mode, so flushing it on the next agent_end would land the
// session on the plan-role model after the user has exited plan mode
// (issue #816). This runs even when deferModelRestore is set
// (compact-approval path): otherwise the stale plan switch survives and
// flushPendingModelSwitch() later clobbers the restored/execution model.
this.#clearPendingPlanModelSwitch();
if (this.#planModePreviousModelState && !options?.deferModelRestore) {
await this.#restorePlanPreviousModel(this.#planModePreviousModelState);
}
// If #applyPlanModeModel queued a deferred switch to the plan-role model
// (because the session was streaming on entry), drop it now: we are
// leaving plan mode, so flushing it on the next agent_end would land the
// session on the plan-role model after the user has exited plan mode
// (issue #816). This runs even when deferModelRestore is set
// (compact-approval path): otherwise the stale plan switch survives and
// flushPendingModelSwitch() later clobbers the restored/execution model.
if (this.#planModePreviousModelState) this.#clearPendingPlanModelSwitch();
} catch (error) {
this.session.setPlanModeState(planModeState);
if (
planModeModelState &&
(!modelsAreEqual(this.session.model, planModeModelState.model) ||
this.session.configuredThinkingLevel() !== planModeModelState.thinkingLevel)
) {
try {
await this.#restorePlanPreviousModel(planModeModelState);
} catch (rollbackError) {
logger.warn("Failed to restore plan model after plan exit failure", { error: String(rollbackError) });
}
}
const enabledTools = this.session.getEnabledToolNames();
if (
enabledTools.length !== planModeTools.length ||
enabledTools.some((name, index) => name !== planModeTools[index])
) {
try {
await this.session.setActiveToolsByName(planModeTools);
} catch (rollbackError) {
logger.warn("Failed to restore plan tools after plan exit failure", { error: String(rollbackError) });
}
}
throw error;
}
this.session.setPlanProposalHandler?.(null);