( fromMode: string, toMode: string, context: ToolPermissionContext, )
| 595 | * @param context The current tool permission context |
| 596 | */ |
| 597 | export function transitionPermissionMode( |
| 598 | fromMode: string, |
| 599 | toMode: string, |
| 600 | context: ToolPermissionContext, |
| 601 | ): ToolPermissionContext { |
| 602 | // plan→plan (SDK set_permission_mode) would wrongly hit the leave branch below |
| 603 | if (fromMode === toMode) return context |
| 604 | |
| 605 | handlePlanModeTransition(fromMode, toMode) |
| 606 | handleAutoModeTransition(fromMode, toMode) |
| 607 | |
| 608 | if (fromMode === 'plan' && toMode !== 'plan') { |
| 609 | setHasExitedPlanMode(true) |
| 610 | } |
| 611 | |
| 612 | if (feature('TRANSCRIPT_CLASSIFIER')) { |
| 613 | if (toMode === 'plan' && fromMode !== 'plan') { |
| 614 | return prepareContextForPlanMode(context) |
| 615 | } |
| 616 | |
| 617 | // Plan with auto active counts as using the classifier (for the leaving side). |
| 618 | // isAutoModeActive() is the authoritative signal — prePlanMode/strippedDangerousRules |
| 619 | // are unreliable proxies because auto can be deactivated mid-plan (non-opt-in |
| 620 | // entry, transitionPlanAutoMode) while those fields remain set/unset. |
| 621 | const fromUsesClassifier = |
| 622 | fromMode === 'auto' || |
| 623 | (fromMode === 'plan' && |
| 624 | (autoModeStateModule?.isAutoModeActive() ?? false)) |
| 625 | const toUsesClassifier = toMode === 'auto' // plan entry handled above |
| 626 | |
| 627 | if (toUsesClassifier && !fromUsesClassifier) { |
| 628 | if (!isAutoModeGateEnabled()) { |
| 629 | throw new Error('Cannot transition to auto mode: gate is not enabled') |
| 630 | } |
| 631 | autoModeStateModule?.setAutoModeActive(true) |
| 632 | context = stripDangerousPermissionsForAutoMode(context) |
| 633 | } else if (fromUsesClassifier && !toUsesClassifier) { |
| 634 | autoModeStateModule?.setAutoModeActive(false) |
| 635 | setNeedsAutoModeExitAttachment(true) |
| 636 | context = restoreDangerousPermissions(context) |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | // Only spread if there's something to clear (preserves ref equality) |
| 641 | if (fromMode === 'plan' && toMode !== 'plan' && context.prePlanMode) { |
| 642 | return { ...context, prePlanMode: undefined } |
| 643 | } |
| 644 | |
| 645 | return context |
| 646 | } |
| 647 | |
| 648 | /** |
| 649 | * Parse base tools specification from CLI |
no test coverage detected