core/cli+config: confirm destructive tools and path denylist

Wire [agent] confirm_destructive / path_denylist into CLI; default
confirm prompt denies; document Phase C safety hooks.
This commit is contained in:
2026-07-14 23:30:17 +08:00
parent 35e89c4dcc
commit bcfd1098f8
8 changed files with 56 additions and 3 deletions
+3
View File
@@ -67,6 +67,9 @@ async def _run_chat(
raise click.ClickException(str(exc)) from exc
_ = set_workspace_root(workspace)
from plyngent.tools import set_path_denylist
set_path_denylist(store.agent_config.path_denylist or None)
install_cli_limit_hooks()
memory = await MemoryStore.open(_database_config(store))
try:
+2
View File
@@ -26,6 +26,8 @@ _MINIMAL_CONFIG = """\
# system_prompt = "You are a careful coding assistant."
# max_tool_result_chars = 32000
# parallel_tools = true
# confirm_destructive = true
# path_denylist = ["/secrets/", ".ssh/"]
# [providers.example]
# preset = "openai-compatible"
+16
View File
@@ -1,9 +1,14 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import click
from plyngent.tools.process.pty_session import PtyManager
if TYPE_CHECKING:
from collections.abc import Mapping
def prompt_continue_limit(reason: str) -> bool:
"""Ask the user whether to raise a limit and continue (TTY)."""
@@ -15,6 +20,17 @@ def prompt_continue_limit(reason: str) -> bool:
return False
def prompt_confirm_tool(name: str, args: Mapping[str, object], reason: str) -> bool:
"""Ask whether to allow a destructive tool call (TTY). Default is deny."""
del args
click.echo()
click.secho(f"[confirm] tool {name!r}: {reason}", fg="yellow")
try:
return bool(click.confirm("Allow this tool call?", default=False))
except click.Abort:
return False
def install_cli_limit_hooks() -> None:
"""Register interactive continue hooks for process-global tool limits."""
PtyManager.set_limit_continue_hook(prompt_continue_limit)
+10
View File
@@ -40,6 +40,16 @@ class ReplState:
def _tool_registry(self) -> ToolRegistry | None:
if not self.tools_enabled:
return None
from plyngent.cli.limits import prompt_confirm_tool
from plyngent.tools.danger import classify_danger
agent_cfg = self.config.agent_config
if agent_cfg.confirm_destructive:
return ToolRegistry(
list(DEFAULT_TOOLS),
danger=classify_danger,
on_confirm=prompt_confirm_tool,
)
return ToolRegistry(list(DEFAULT_TOOLS))
def _make_agent(self) -> ChatAgent:
+2
View File
@@ -16,6 +16,8 @@ class AgentConfig(Struct, omit_defaults=True):
system_prompt: str = ""
max_tool_result_chars: int = 32_000
parallel_tools: bool = True
confirm_destructive: bool = True
path_denylist: list[str] = field(default_factory=list)
class ModelConfig(Struct, omit_defaults=True):