claude: accepting ExitPlanMode exits plan mode in the panel (T-337)
ExitPlanMode arrives as a can_use_tool permission prompt and was approved like any other tool — the control_response was sent but the tracked SessionStatus.permissionMode never changed, so the mode indicator and composer stayed on "plan" after the plan was accepted. On approving an ExitPlanMode prompt, sync the tracked mode to 'default' (the CLI performs the transition itself, so no set_permission_mode control request is sent — we only mirror it). The change rides the existing statusStream → claude_pane._status plumbing, so the permission-mode control and status indicator update with no extra wiring. Deny, and any non-ExitPlanMode tool, leave the mode untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -478,6 +478,43 @@ void main() {
|
||||
expect(session.toolUseOutcomes['toolu_1'], isFalse);
|
||||
});
|
||||
|
||||
String planInit() => jsonEncode({'type': 'system', 'subtype': 'init', 'model': 'claude-opus-4-7', 'permissionMode': 'plan'});
|
||||
|
||||
test('approving ExitPlanMode leaves plan mode (T-337)', () async {
|
||||
proc.emit(planInit());
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(statuses.last.permissionMode, 'plan');
|
||||
|
||||
proc.emit(canUseTool('exit-1', tool: 'ExitPlanMode', input: {'plan': 'do the thing'}));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
final p = session.pendingPrompt!;
|
||||
expect(p.toolName, 'ExitPlanMode');
|
||||
|
||||
session.resolvePrompt(p.promptId, AllowTool(p.input));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(statuses.last.permissionMode, 'default', reason: 'approving ExitPlanMode must exit plan mode');
|
||||
});
|
||||
|
||||
test('denying ExitPlanMode stays in plan mode (T-337)', () async {
|
||||
proc.emit(planInit());
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
proc.emit(canUseTool('exit-2', tool: 'ExitPlanMode', input: {'plan': 'x'}));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
session.resolvePrompt(session.pendingPrompt!.promptId, const DenyTool('keep planning'));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(statuses.last.permissionMode, 'plan', reason: 'a denied plan-exit keeps plan mode');
|
||||
});
|
||||
|
||||
test('approving a non-ExitPlanMode tool does not change plan mode (T-337)', () async {
|
||||
proc.emit(planInit());
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
proc.emit(canUseTool('w1')); // a Write
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
session.resolvePrompt(session.pendingPrompt!.promptId, AllowTool(const {}));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(statuses.last.permissionMode, 'plan', reason: 'only ExitPlanMode exits plan mode');
|
||||
});
|
||||
|
||||
test('resolvePrompt(deny) writes a deny decision with a message', () async {
|
||||
proc.emit(canUseTool('req-3'));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
Reference in New Issue
Block a user