mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 07:17:54 +08:00
cli: leave a directory-access request to the human under YOLO
The access hook short-circuited on YOLO mode and answered its own question with a process-only grant, so `--yes` (and `/yolo on`) reached any path outside the workspace without asking. That grant is a filesystem capability, not one risky call: the command denylist and a skill write are asked either way, and so is this one now. `yolo_allow` stays as the process-scoped store a host fills when it approves without persisting; nothing auto-approves it any more. A non-interactive run denies, like the other policy prompts.
This commit is contained in:
@@ -297,12 +297,15 @@ class ReplState:
|
||||
reason: str,
|
||||
timeout_seconds: float,
|
||||
) -> AccessDecision | str | None:
|
||||
"""Directory-access confirm: YOLO/``--yes`` auto-approves, else ask."""
|
||||
from plyngent.cli.limits import prompt_directory_access_confirm
|
||||
from plyngent.tools.access import AccessDecision as Decision
|
||||
"""Directory-access confirm: always ask, whatever YOLO mode says.
|
||||
|
||||
An out-of-workspace path is a grant on the filesystem rather than one
|
||||
risky call, so ``--yes`` / ``/yolo`` do not answer it — the command
|
||||
denylist and a skill write are not skipped either. A non-interactive run
|
||||
denies.
|
||||
"""
|
||||
from plyngent.cli.limits import prompt_directory_access_confirm
|
||||
|
||||
if self.effective_yolo() != "off":
|
||||
return Decision(mode, persist=False)
|
||||
return prompt_directory_access_confirm(path, mode, reason, timeout_seconds)
|
||||
|
||||
def _install_config_access(self) -> None:
|
||||
|
||||
@@ -4,7 +4,8 @@ A grant is one ``path → mode`` pair; its *source* is structural:
|
||||
|
||||
- :attr:`WorkspacePolicy.config_allow` — static TOML pre-allow (never prompts),
|
||||
- :attr:`SessionState.access_grants` — human-approved for this session,
|
||||
- :attr:`WorkspacePolicy.yolo_allow` — ``--yes`` / YOLO auto-approval, process only.
|
||||
- :attr:`WorkspacePolicy.yolo_allow` — process only: a host that approved
|
||||
without keeping the grant (``--yes`` does **not** answer this prompt).
|
||||
|
||||
``resolve_path`` consults all three. Grants are a policy gate for path-resolving
|
||||
tools, **not** a sandbox: an ``exec`` grant (or any command) can reach the rest
|
||||
@@ -257,8 +258,10 @@ async def request_directory_access(path: str, reason: str = "", mode: str = "rea
|
||||
workspace root``, passing the ``mode`` that error names. ``mode`` is ``read``
|
||||
(inspect files), ``write`` (also modify files) or ``exec`` (also use it as
|
||||
``run_argv`` / PTY cwd); the human may approve a different level. Approval
|
||||
lasts for the session (or for this process when ``--yes`` auto-approves).
|
||||
Paths matching the configured denylist can never be granted.
|
||||
lasts for the session (or for this process when the host answers without
|
||||
persisting); ``--yes`` / YOLO mode does not answer this prompt, and a
|
||||
non-interactive run denies. Paths matching the configured denylist can never
|
||||
be granted.
|
||||
"""
|
||||
requested = parse_access_mode(mode)
|
||||
if requested is None:
|
||||
|
||||
@@ -114,7 +114,8 @@ class WorkspacePolicy:
|
||||
policy_confirm_timeout_seconds: float = DEFAULT_POLICY_CONFIRM_TIMEOUT_SECONDS
|
||||
# Static TOML pre-allow (resolved path → mode); never prompts.
|
||||
config_allow: dict[Path, AccessMode] = field(default_factory=dict)
|
||||
# Process-scoped YOLO / --yes grants (resolved path → mode); never persisted.
|
||||
# Process-scoped grants (resolved path → mode): a host answer kept only for
|
||||
# this process. YOLO mode never fills it; nothing is auto-approved here.
|
||||
yolo_allow: dict[Path, AccessMode] = field(default_factory=dict)
|
||||
# Host-contributed read-only roots (skill directories): every path tool may
|
||||
# read them, nothing may write them. Not a grant, so they stay out of
|
||||
|
||||
Reference in New Issue
Block a user