feat: add custom HeaderPanel widget and switchable agent modes
Replace built-in Header with a custom HeaderPanel showing model name, mode badge, and live token usage. Add AgentMode enum (normal/plan/auto) with mode-aware permission gating — Plan mode restricts to read-only tools, Auto mode auto-approves everything. Includes /mode slash command and Ctrl+P keybinding to cycle modes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import re
|
||||
import shlex
|
||||
from collections.abc import Awaitable, Callable
|
||||
|
||||
from app.models.config import PermissionsConfig, ToolsConfig
|
||||
from app.models.config import AgentMode, PermissionsConfig, ToolsConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,6 +30,10 @@ class PermissionsService:
|
||||
shows a modal dialog. Without a callback, unlisted tools are denied.
|
||||
"""
|
||||
|
||||
READ_ONLY_TOOLS: frozenset[str] = frozenset({
|
||||
"read_file", "list_dir", "grep_files", "find_files", "finish",
|
||||
})
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: PermissionsConfig,
|
||||
@@ -38,6 +42,16 @@ class PermissionsService:
|
||||
self.config = config
|
||||
self._tools_config = tools_config
|
||||
self._prompt_callback: PromptCallback | None = None
|
||||
self._mode: AgentMode = AgentMode.NORMAL
|
||||
|
||||
@property
|
||||
def mode(self) -> AgentMode:
|
||||
"""Current agent mode."""
|
||||
return self._mode
|
||||
|
||||
@mode.setter
|
||||
def mode(self, value: AgentMode) -> None:
|
||||
self._mode = value
|
||||
|
||||
def set_prompt_callback(self, callback: PromptCallback) -> None:
|
||||
"""Set the async callback used to prompt the user for permission.
|
||||
@@ -67,6 +81,16 @@ class PermissionsService:
|
||||
logger.info("Tool '%s' is in deny list — blocked", tool_name)
|
||||
return False
|
||||
|
||||
if self._mode == AgentMode.AUTO:
|
||||
logger.debug("Tool '%s' auto-approved (AUTO mode)", tool_name)
|
||||
return True
|
||||
|
||||
if self._mode == AgentMode.PLAN:
|
||||
if tool_name not in self.READ_ONLY_TOOLS:
|
||||
logger.info("Tool '%s' blocked in Plan mode (read-only tools only)", tool_name)
|
||||
return False
|
||||
return True
|
||||
|
||||
if tool_name in self.config.auto_approve:
|
||||
logger.debug("Tool '%s' is auto-approved", tool_name)
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user