diff --git a/plugins/provider-claude-code/src/bridge/bridge.ts b/plugins/provider-claude-code/src/bridge/bridge.ts index efdd25192..2bbb6a4dd 100644 --- a/plugins/provider-claude-code/src/bridge/bridge.ts +++ b/plugins/provider-claude-code/src/bridge/bridge.ts @@ -1701,6 +1701,29 @@ function createForwardUserQuestionRequest( }); } +/** + * `/plan` on a later turn of a live session. Session construction used to be + * the only place the permission mode was applied, so a mid-conversation + * `/plan` stripped the mention and pushed the prompt into a session that was + * still in the user's preset mode: no plan-mode reminder, no ExitPlanMode + * proposal. Switch the live SDK session into Plan mode before the prompt is + * pushed; the preset stays in `approvedPlanPermissionMode` for the approval + * to restore. + */ +async function enterPlanModeIfRequested( + threadSession: ThreadSession, + params: TurnStartParams | TurnSteerParams, +): Promise { + if ( + params.claudeCodePermissionMode !== "plan" || + threadSession.permissionMode === "plan" + ) { + return; + } + threadSession.permissionMode = "plan"; + await threadSession.session.setPermissionMode("plan"); +} + /** * Leave Plan mode once the user approves a plan. * @@ -2281,6 +2304,7 @@ async function runTurnStart( params.threadId, withTurnLiveSessionSettings(threadSession.liveSettings, params), ); + await enterPlanModeIfRequested(threadSession, params); } catch (error) { const message = error instanceof Error ? error.message : String(error); sendError(id, -32000, message); @@ -2346,6 +2370,7 @@ async function runTurnSteer( params.threadId, withTurnLiveSessionSettings(threadSession.liveSettings, params), ); + await enterPlanModeIfRequested(threadSession, params); } catch (error) { const message = error instanceof Error ? error.message : String(error); sendError(id, -32000, message); diff --git a/plugins/provider-claude-code/src/bridge/commands.ts b/plugins/provider-claude-code/src/bridge/commands.ts index 4b9874c44..5e6efeab4 100644 --- a/plugins/provider-claude-code/src/bridge/commands.ts +++ b/plugins/provider-claude-code/src/bridge/commands.ts @@ -94,6 +94,9 @@ export const claudeTurnStartParamsSchema = z.object({ providerSubagentsEnabled: z.boolean().optional(), config: z.record(z.string(), z.unknown()).optional(), permissionEscalation: bridgePermissionEscalationSchema, + // `/plan` on a later turn: the live session must switch into Plan mode + // before the prompt is pushed. Undefined keeps the session's current mode. + claudeCodePermissionMode: z.literal("plan").optional(), }); export const claudeTurnSteerParamsSchema = z.object({ @@ -107,6 +110,7 @@ export const claudeTurnSteerParamsSchema = z.object({ memoryEnabled: z.boolean().optional(), providerSubagentsEnabled: z.boolean().optional(), permissionEscalation: bridgePermissionEscalationSchema, + claudeCodePermissionMode: z.literal("plan").optional(), }); /** The canonical Provider Bridge Protocol params, per method. */ diff --git a/plugins/provider-claude-code/src/session-params.ts b/plugins/provider-claude-code/src/session-params.ts index 97f935904..d781e63aa 100644 --- a/plugins/provider-claude-code/src/session-params.ts +++ b/plugins/provider-claude-code/src/session-params.ts @@ -328,5 +328,8 @@ export function buildClaudeTurnParams( memoryEnabled: providerOptions.memoryEnabled, providerSubagentsEnabled: providerOptions.providerSubagentsEnabled, permissionEscalation: args.options.permissionEscalation, + ...(providerOptions.claudeCodePermissionMode !== undefined + ? { claudeCodePermissionMode: providerOptions.claudeCodePermissionMode } + : {}), }; }