fix(coding-agent): preserved prompted paused plan commands
Kept /plan <prompt> from paused plan mode on the prompted entry path while retaining the no-arg third-toggle exit. Added regression coverage for paused plan mode resuming and submitting the prompt. Fixes #2510
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `/plan` cycling between `plan` and `plan_paused` with no path back to mode `none`. `handlePlanModeCommand` had branches for entering and pausing but fell through to `#enterPlanMode()` when invoked from the paused state, so once a session entered plan mode the only operator-visible toggle re-entered it. The handler now matches `planModePaused` and fully exits — clearing `planModeHasEntered` and appending a `mode_change` to `"none"` — so `/goal` (and any other mode gated on `planModeEnabled || planModePaused`) can run again after a third `/plan` ([#2510](https://github.com/can1357/oh-my-pi/issues/2510)).
|
||||
- Fixed `/plan` cycling between `plan` and `plan_paused` with no path back to mode `none`, while preserving prompted paused-mode requests. The no-arg third toggle now fully exits — clearing `planModeHasEntered` and appending a `mode_change` to `"none"` — and `/plan <prompt>` from `plan_paused` re-enters plan mode and submits the prompt as the first turn ([#2510](https://github.com/can1357/oh-my-pi/issues/2510)).
|
||||
|
||||
## [15.12.5] - 2026-06-13
|
||||
### Changed
|
||||
|
||||
@@ -2237,12 +2237,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
||||
await this.#exitPlanMode({ paused: true });
|
||||
return;
|
||||
}
|
||||
if (this.planModePaused) {
|
||||
// Third toggle: paused → off. Tools, model, and plan state were already
|
||||
// restored by the prior #exitPlanMode({ paused: true }); only the
|
||||
if (this.planModePaused && !initialPrompt) {
|
||||
// No-arg third toggle: paused → off. Tools, model, and plan state were
|
||||
// already restored by the prior #exitPlanMode({ paused: true }); only the
|
||||
// paused flag, the reentry marker, and the session mode entry remain.
|
||||
// Without this branch the handler fell through to #enterPlanMode and
|
||||
// the session was stuck cycling plan ↔ plan_paused (issue #2510).
|
||||
// Prompted /plan invocations fall through to #enterPlanMode below so the
|
||||
// supplied prompt is still submitted as the first plan-mode turn.
|
||||
this.planModePaused = false;
|
||||
this.#planModeHasEntered = false;
|
||||
this.#updatePlanModeStatus();
|
||||
|
||||
@@ -212,6 +212,26 @@ describe("InteractiveMode resume mode restoration", () => {
|
||||
expect(created.session.getActiveToolNames()).toContain("resolve");
|
||||
});
|
||||
|
||||
it("submits prompted plan commands when plan mode is paused", async () => {
|
||||
const created = await createHarness();
|
||||
await created.mode.handlePlanModeCommand();
|
||||
await created.mode.handlePlanModeCommand();
|
||||
expect(created.mode.planModeEnabled).toBe(false);
|
||||
expect(created.mode.planModePaused).toBe(true);
|
||||
|
||||
let submittedText: string | undefined;
|
||||
created.mode.onInputCallback = input => {
|
||||
submittedText = input.text;
|
||||
};
|
||||
|
||||
await created.mode.handlePlanModeCommand("write the plan");
|
||||
|
||||
expect(created.mode.planModeEnabled).toBe(true);
|
||||
expect(created.mode.planModePaused).toBe(false);
|
||||
expect(created.session.getPlanModeState()).toMatchObject({ enabled: true });
|
||||
expect(submittedText).toBe("write the plan");
|
||||
});
|
||||
|
||||
it("clears stale plan mode state when switching to a non-plan session", async () => {
|
||||
const registry = modelRegistry();
|
||||
const defaultModel = modelOrThrow(registry, "claude-sonnet-4-5");
|
||||
|
||||
Reference in New Issue
Block a user