Skip to content

Commit 608323f

Browse files
Gucc111cursoragent
andcommitted
fix: pass basePermissionMode through protocol to correctly restore permission after plan mode exit
The frontend computes effectivePermissionMode='plan' when in UI Plan mode, erasing the user's actual permission preference. The backend saved the stale config value (often 'default') as the restore target, causing unexpected permission prompts after exit_plan_mode. Now the frontend sends the raw user preference as basePermissionMode alongside the effective mode. applyPermissionOverrides uses it to correctly populate permissionModeBeforePlan, ensuring Full Access is properly restored on exit. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 13d4dcc commit 608323f

10 files changed

Lines changed: 24 additions & 2 deletions

File tree

src/agent/loop/AgentLoop.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export type AgentLoopInput = {
6060
messages: CanonicalMessage[];
6161
maxTurns?: number;
6262
permissionMode?: PermissionMode;
63+
/** The user's actual permission preference before plan-mode override. */
64+
basePermissionMode?: PermissionMode;
6365
permissionRules?: Partial<PermissionRuleSet>;
6466
abortSignal?: AbortSignal;
6567
onDurableMessage?: (message: CanonicalMessage) => void | Promise<void>;
@@ -96,7 +98,7 @@ export class AgentLoop {
9698
}
9799

98100
async *run(input: AgentLoopInput): AsyncGenerator<AgentEvent, AgentLoopRunResult, unknown> {
99-
this.applyPermissionOverrides(input.permissionMode, input.permissionRules);
101+
this.applyPermissionOverrides(input.permissionMode, input.permissionRules, input.basePermissionMode);
100102
const startedAt = this.now().toISOString();
101103
let messages = [...input.messages];
102104
let turnCount = 1;
@@ -1351,10 +1353,11 @@ export class AgentLoop {
13511353
private applyPermissionOverrides(
13521354
permissionMode?: PermissionMode,
13531355
permissionRules?: Partial<PermissionRuleSet>,
1356+
basePermissionMode?: PermissionMode,
13541357
): void {
13551358
if (permissionMode) {
13561359
if (permissionMode === "plan" && this.config.permissionMode !== "plan") {
1357-
this.config.permissionModeBeforePlan = this.config.permissionMode;
1360+
this.config.permissionModeBeforePlan = basePermissionMode ?? this.config.permissionMode;
13581361
}
13591362
this.config.permissionMode = permissionMode;
13601363
this.config.permissionContext.mode = permissionMode;

src/agent/protocol/input.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ export type AgentSubmitOptions = {
1010
maxTurns?: number;
1111
metadata?: Record<string, unknown>;
1212
permissionMode?: PermissionMode;
13+
/** The user's actual permission preference before plan-mode override. */
14+
basePermissionMode?: PermissionMode;
1315
permissionRules?: Partial<PermissionRuleSet>;
1416
};

src/agent/session/AgentSession.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export class AgentSession {
7878
input,
7979
maxTurns: submitOptions.maxTurns,
8080
permissionMode: submitOptions.permissionMode,
81+
basePermissionMode: submitOptions.basePermissionMode,
8182
permissionRules: submitOptions.permissionRules,
8283
abortSignal: this.state.abortController.signal,
8384
});

src/agent/turn/TurnRunner.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type TurnRunnerOptions = {
1717
input: AgentInput;
1818
maxTurns?: number;
1919
permissionMode?: PermissionMode;
20+
/** The user's actual permission preference before plan-mode override. */
21+
basePermissionMode?: PermissionMode;
2022
permissionRules?: Partial<PermissionRuleSet>;
2123
abortSignal?: AbortSignal;
2224
};
@@ -105,6 +107,7 @@ export class TurnRunner {
105107
messages,
106108
maxTurns: options.maxTurns,
107109
permissionMode: options.permissionMode,
110+
basePermissionMode: options.basePermissionMode,
108111
permissionRules: options.permissionRules,
109112
abortSignal: options.abortSignal,
110113
onDurableMessage: (msg) => this.transcript.recordDurableMessage(options.sessionId, options.turnId, msg),

src/gateway/client/InProcessGateway.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ export class InProcessGateway implements Gateway {
315315
turnId: runId,
316316
maxTurns: input.maxTurns,
317317
permissionMode,
318+
basePermissionMode: input.basePermissionMode,
318319
permissionRules: {
319320
...persistedRules,
320321
allow: [...sessionAllowRules, ...persistedRules.allow],

src/gateway/protocol/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ export type GatewaySubmitTurnInput = {
7575
workspaceCwd?: string;
7676
attachments?: ChannelAttachment[];
7777
mode?: GatewayMode;
78+
/** The user's actual permission preference before plan-mode override. */
79+
basePermissionMode?: GatewayMode;
7880
runId?: string;
7981
maxTurns?: number;
8082
telemetry?: {

ui/server/pilotdeck-bridge.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ export async function runChatViaGateway(
736736
...(uiFilesToAttachments(options?.attachments) || []),
737737
];
738738
const resolvedMode = resolvePermissionMode(options);
739+
const basePermissionMode = options?.basePermissionMode || undefined;
739740
console.log(`[pilotdeck-bridge] submitTurn mode=${resolvedMode} (options.permissionMode=${options?.permissionMode}, options.mode=${options?.mode})`);
740741

741742
try {
@@ -746,6 +747,7 @@ export async function runChatViaGateway(
746747
message: command ?? '',
747748
mode: resolvedMode,
748749
runId,
750+
...(basePermissionMode ? { basePermissionMode } : {}),
749751
...(attachments.length > 0 ? { attachments } : {}),
750752
...(options.workspaceCwd ? { workspaceCwd: options.workspaceCwd } : {}),
751753
});

ui/src/components/chat-v2/ChatInterfaceV2.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ function ChatInterfaceV2({
202202
currentSessionId,
203203
model,
204204
permissionMode: effectivePermissionMode,
205+
basePermissionMode: permissionMode,
205206
cycleRunMode,
206207
isLoading,
207208
canAbortSession,

ui/src/components/chat/hooks/useChatComposerState.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ interface UseChatComposerStateArgs {
4545
currentSessionId: string | null;
4646
model: string;
4747
permissionMode: PermissionMode | string;
48+
basePermissionMode?: PermissionMode | string;
4849
cycleRunMode: () => void;
4950
isLoading: boolean;
5051
canAbortSession: boolean;
@@ -141,6 +142,7 @@ export function useChatComposerState({
141142
currentSessionId,
142143
model,
143144
permissionMode,
145+
basePermissionMode,
144146
cycleRunMode,
145147
isLoading,
146148
canAbortSession,
@@ -788,6 +790,7 @@ export function useChatComposerState({
788790
temporarySessionId: sessionToActivate,
789791
toolsSettings,
790792
permissionMode,
793+
basePermissionMode,
791794
model,
792795
sessionSummary,
793796
images: uploadedImages,
@@ -820,6 +823,7 @@ export function useChatComposerState({
820823
onSessionProcessing,
821824
pendingViewSessionRef,
822825
permissionMode,
826+
basePermissionMode,
823827
resetCommandMenuState,
824828
scrollToBottom,
825829
selectedProject,

ui/src/components/chat/utils/sessionLauncher.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type StartSessionOptions = {
99
sessionId?: string | null;
1010
temporarySessionId?: string;
1111
permissionMode?: PermissionMode | string;
12+
basePermissionMode?: PermissionMode | string;
1213
model?: string;
1314
sessionSummary?: string | null;
1415
toolsSettings?: PilotDeckSettings;
@@ -82,6 +83,7 @@ export function startSessionCommand({
8283
sessionId,
8384
temporarySessionId,
8485
permissionMode = 'default',
86+
basePermissionMode,
8587
model,
8688
sessionSummary,
8789
toolsSettings = getPilotDeckSettings(),
@@ -104,6 +106,7 @@ export function startSessionCommand({
104106
cwd: resolvedProjectPath,
105107
toolsSettings,
106108
permissionMode,
109+
...(basePermissionMode ? { basePermissionMode } : {}),
107110
...(model ? { model } : {}),
108111
sessionSummary,
109112
...(alwaysOnPlanId ? { alwaysOnPlanId } : {}),

0 commit comments

Comments
 (0)